diff --git a/configure.ac b/configure.ac
index e73f7d92e..e002c248e 100644
--- a/configure.ac
+++ b/configure.ac
@@ -40,6 +40,7 @@ AC_CHECK_HEADERS([ \
     linux/cryptouser.h \
     linux/genetlink.h \
     linux/keyctl.h \
+    linux/if_alg.h \
     linux/if_packet.h \
     linux/if_ether.h \
     linux/mempolicy.h \
diff --git a/include/lapi/if_alg.h b/include/lapi/if_alg.h
new file mode 100644
index 000000000..5a74df99b
--- /dev/null
+++ b/include/lapi/if_alg.h
@@ -0,0 +1,38 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#ifndef IF_ALG_H__
+#define IF_ALG_H__
+
+#ifdef HAVE_LINUX_IF_ALG_H
+#  include <linux/if_alg.h>
+#else
+#  include <stdint.h>
+
+struct sockaddr_alg {
+	uint16_t	salg_family;
+	uint8_t		salg_type[14];
+	uint32_t	salg_feat;
+	uint32_t	salg_mask;
+	uint8_t		salg_name[64];
+};
+
+struct af_alg_iv {
+	uint32_t	ivlen;
+	uint8_t		iv[0];
+};
+
+#define ALG_SET_KEY		1
+#define ALG_SET_IV		2
+#define ALG_SET_OP		3
+#define ALG_SET_AEAD_ASSOCLEN	4
+#define ALG_SET_AEAD_AUTHSIZE	5
+
+#define ALG_OP_DECRYPT		0
+#define ALG_OP_ENCRYPT		1
+
+#endif /* !HAVE_LINUX_IF_ALG_H */
+
+#endif /* IF_ALG_H__ */
diff --git a/include/lapi/socket.h b/include/lapi/socket.h
index 4e856dfb3..2605443e8 100644
--- a/include/lapi/socket.h
+++ b/include/lapi/socket.h
@@ -49,6 +49,10 @@
 # define SOCK_CLOEXEC 02000000
 #endif
 
+#ifndef AF_ALG
+# define AF_ALG		38
+#endif
+
 #ifndef SOL_SCTP
 # define SOL_SCTP	132
 #endif
@@ -61,4 +65,8 @@
 # define SOL_DCCP		269
 #endif
 
+#ifndef SOL_ALG
+# define SOL_ALG		279
+#endif
+
 #endif /* __LAPI_SOCKET_H__ */
diff --git a/include/tst_af_alg.h b/include/tst_af_alg.h
new file mode 100644
index 000000000..fc4b1989a
--- /dev/null
+++ b/include/tst_af_alg.h
@@ -0,0 +1,136 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Google LLC
+ */
+/**
+ * @file tst_af_alg.h
+ *
+ * Library for accessing kernel crypto algorithms via AF_ALG.
+ *
+ * See https://www.kernel.org/doc/html/latest/crypto/userspace-if.html
+ * for more information about AF_ALG.
+ */
+
+#ifndef TST_AF_ALG_H
+#define TST_AF_ALG_H
+
+#include "lapi/if_alg.h"
+#include <stdbool.h>
+
+/**
+ * Create an AF_ALG algorithm socket.
+ *
+ * This creates an AF_ALG algorithm socket that is initially not bound to any
+ * particular algorithm.  On failure, tst_brk() is called with TCONF if the
+ * kernel doesn't support AF_ALG, otherwise TBROK.
+ *
+ * @return a new AF_ALG algorithm socket
+ */
+int tst_alg_create(void);
+
+/**
+ * Bind an AF_ALG algorithm socket to an algorithm.
+ *
+ * @param algfd An AF_ALG algorithm socket
+ * @param addr A structure which specifies the algorithm to use
+ *
+ * On failure, tst_brk() is called with TCONF if the kernel doesn't support the
+ * specified algorithm, otherwise TBROK.
+ */
+void tst_alg_bind_addr(int algfd, const struct sockaddr_alg *addr);
+
+/**
+ * Bind an AF_ALG algorithm socket to an algorithm.
+ *
+ * @param algfd An AF_ALG algorithm socket
+ * @param algtype The type of algorithm, such as "hash" or "skcipher"
+ * @param algname The name of the algorithm, such as "sha256" or "xts(aes)"
+ *
+ * Like tst_alg_bind_addr(), except this just takes in the algorithm type and
+ * name.  The 'feat' and 'mask' fields are left 0.
+ *
+ * On failure, tst_brk() is called with TCONF if the kernel doesn't support the
+ * specified algorithm, otherwise TBROK.
+ */
+void tst_alg_bind(int algfd, const char *algtype, const char *algname);
+
+/**
+ * Check for the availability of an algorithm.
+ *
+ * @param algtype The type of algorithm, such as "hash" or "skcipher"
+ * @param algname The name of the algorithm, such as "sha256" or "xts(aes)"
+ *
+ * Return true if the algorithm is available, or false if unavailable.
+ * If another error occurs, tst_brk() is called with TBROK.
+ */
+bool tst_have_alg(const char *algtype, const char *algname);
+
+/**
+ * Require the availability of an algorithm.
+ *
+ * @param algtype The type of algorithm, such as "hash" or "skcipher"
+ * @param algname The name of the algorithm, such as "sha256" or "xts(aes)"
+ *
+ * If the algorithm is unavailable, tst_brk() is called with TCONF.
+ * If another error occurs, tst_brk() is called with TBROK.
+ */
+void tst_require_alg(const char *algtype, const char *algname);
+
+/**
+ * Assign a cryptographic key to an AF_ALG algorithm socket.
+ *
+ * @param algfd An AF_ALG algorithm socket
+ * @param key Pointer to the key.  If NULL, a random key is generated.
+ * @param keylen Length of the key in bytes
+ *
+ * On failure, tst_brk() is called with TBROK.
+ */
+void tst_alg_setkey(int algfd, const uint8_t *key, unsigned int keylen);
+
+/**
+ * Create an AF_ALG request socket for the given algorithm socket.
+ *
+ * @param algfd An AF_ALG algorithm socket
+ *
+ * This creates a request socket for the given algorithm socket, which must be
+ * bound to an algorithm.  The same algorithm socket can have many request
+ * sockets used concurrently to perform independent cryptographic operations,
+ * e.g. hashing or encryption/decryption.  But the key, if any, that has been
+ * assigned to the algorithm is shared by all request sockets.
+ *
+ * On failure, tst_brk() is called with TBROK.
+ *
+ * @return a new AF_ALG request socket
+ */
+int tst_alg_accept(int algfd);
+
+/**
+ * Set up an AF_ALG algorithm socket for the given algorithm w/ given key.
+ *
+ * @param algtype The type of algorithm, such as "hash" or "skcipher"
+ * @param algname The name of the algorithm, such as "sha256" or "xts(aes)"
+ * @param key The key to use (optional)
+ * @param keylen The length of the key in bytes (optional)
+ *
+ * This is a helper function which creates an AF_ALG algorithm socket, binds it
+ * to the specified algorithm, and optionally sets a key.  If keylen is 0 then
+ * no key is set; otherwise if key is NULL a key of the given length is randomly
+ * generated and set; otherwise the given key is set.
+ *
+ * @return the AF_ALG algorithm socket that was set up
+ */
+int tst_alg_setup(const char *algtype, const char *algname,
+		  const uint8_t *key, unsigned int keylen);
+
+/**
+ * Set up an AF_ALG request socket for the given algorithm w/ given key.
+ *
+ * This is like tst_alg_setup(), except this returns a request fd instead of the
+ * alg fd.  The alg fd is closed, so it doesn't need to be kept track of.
+ *
+ * @return the AF_ALG request socket that was set up
+ */
+int tst_alg_setup_reqfd(const char *algtype, const char *algname,
+			const uint8_t *key, unsigned int keylen);
+
+#endif /* TST_AF_ALG_H */
diff --git a/lib/tst_af_alg.c b/lib/tst_af_alg.c
new file mode 100644
index 000000000..5cae85721
--- /dev/null
+++ b/lib/tst_af_alg.c
@@ -0,0 +1,147 @@
+// SPDX-License-Identifier: GPL-2.0-or-later
+/*
+ * Copyright 2019 Google LLC
+ */
+
+#include <errno.h>
+#include <stdlib.h>
+
+#define TST_NO_DEFAULT_MAIN
+#include "tst_test.h"
+#include "tst_af_alg.h"
+
+int tst_alg_create(void)
+{
+	TEST(socket(AF_ALG, SOCK_SEQPACKET, 0));
+	if (TST_RET >= 0)
+		return TST_RET;
+	if (TST_ERR == EPROTONOSUPPORT)
+		tst_brk(TCONF, "kernel doesn't support AF_ALG");
+	tst_brk(TBROK | TTERRNO, "unexpected error creating AF_ALG socket");
+	return -1;
+}
+
+void tst_alg_bind_addr(int algfd, const struct sockaddr_alg *addr)
+{
+	TEST(bind(algfd, (const struct sockaddr *)addr, sizeof(*addr)));
+	if (TST_RET == 0)
+		return;
+	if (TST_ERR == ENOENT) {
+		tst_brk(TCONF, "kernel doesn't support %s algorithm '%s'",
+			addr->salg_type, addr->salg_name);
+	}
+	tst_brk(TBROK | TTERRNO,
+		"unexpected error binding AF_ALG socket to %s algorithm '%s'",
+		addr->salg_type, addr->salg_name);
+}
+
+static void init_sockaddr_alg(struct sockaddr_alg *addr,
+			      const char *algtype, const char *algname)
+{
+	memset(addr, 0, sizeof(*addr));
+
+	addr->salg_family = AF_ALG;
+
+	strncpy((char *)addr->salg_type, algtype, sizeof(addr->salg_type));
+	if (addr->salg_type[sizeof(addr->salg_type) - 1] != '\0')
+		tst_brk(TBROK, "algorithm type too long: '%s'", algtype);
+
+	strncpy((char *)addr->salg_name, algname, sizeof(addr->salg_name));
+	if (addr->salg_name[sizeof(addr->salg_name) - 1] != '\0')
+		tst_brk(TBROK, "algorithm name too long: '%s'", algname);
+}
+
+void tst_alg_bind(int algfd, const char *algtype, const char *algname)
+{
+	struct sockaddr_alg addr;
+
+	init_sockaddr_alg(&addr, algtype, algname);
+
+	tst_alg_bind_addr(algfd, &addr);
+}
+
+bool tst_have_alg(const char *algtype, const char *algname)
+{
+	int algfd;
+	struct sockaddr_alg addr;
+	bool have_alg = true;
+
+	algfd = tst_alg_create();
+
+	init_sockaddr_alg(&addr, algtype, algname);
+
+	TEST(bind(algfd, (const struct sockaddr *)&addr, sizeof(addr)));
+	if (TST_RET != 0) {
+		if (TST_ERR != ENOENT) {
+			tst_brk(TBROK | TTERRNO,
+				"unexpected error binding AF_ALG socket to %s algorithm '%s'",
+				algtype, algname);
+		}
+		have_alg = false;
+	}
+
+	close(algfd);
+	return have_alg;
+}
+
+void tst_require_alg(const char *algtype, const char *algname)
+{
+	int algfd = tst_alg_create();
+
+	tst_alg_bind(algfd, algtype, algname);
+
+	close(algfd);
+}
+
+void tst_alg_setkey(int algfd, const uint8_t *key, unsigned int keylen)
+{
+	uint8_t *keybuf = NULL;
+	unsigned int i;
+
+	if (key == NULL) {
+		/* generate a random key */
+		keybuf = SAFE_MALLOC(keylen);
+		for (i = 0; i < keylen; i++)
+			keybuf[i] = rand();
+		key = keybuf;
+	}
+	TEST(setsockopt(algfd, SOL_ALG, ALG_SET_KEY, key, keylen));
+	if (TST_RET != 0) {
+		tst_brk(TBROK | TTERRNO,
+			"unexpected error setting key (len=%u)", keylen);
+	}
+	free(keybuf);
+}
+
+int tst_alg_accept(int algfd)
+{
+	TEST(accept(algfd, NULL, NULL));
+	if (TST_RET < 0) {
+		tst_brk(TBROK | TTERRNO,
+			"unexpected error accept()ing AF_ALG request socket");
+	}
+	return TST_RET;
+}
+
+int tst_alg_setup(const char *algtype, const char *algname,
+		  const uint8_t *key, unsigned int keylen)
+{
+	int algfd = tst_alg_create();
+
+	tst_alg_bind(algfd, algtype, algname);
+
+	if (keylen != 0)
+		tst_alg_setkey(algfd, key, keylen);
+
+	return algfd;
+}
+
+int tst_alg_setup_reqfd(const char *algtype, const char *algname,
+			const uint8_t *key, unsigned int keylen)
+{
+	int algfd = tst_alg_setup(algtype, algname, key, keylen);
+	int reqfd = tst_alg_accept(algfd);
+
+	close(algfd);
+	return reqfd;
+}
