diff --git a/common/common_defs.h b/common/common_defs.h
index 279385a..871db2c 100644
--- a/common/common_defs.h
+++ b/common/common_defs.h
@@ -90,4 +90,13 @@ static inline int ilog2(unsigned long n)
 #  define le64_to_cpu(v)	(__builtin_bswap64((__force u64)(v)))
 #endif
 
+/* The hash algorithm that 'fsverity' assumes when none is specified */
+#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
+
+/*
+ * Largest digest size among all hash algorithms supported by fs-verity.
+ * This can be increased if needed.
+ */
+#define FS_VERITY_MAX_DIGEST_SIZE	64
+
 #endif /* COMMON_COMMON_DEFS_H */
diff --git a/include/libfsverity.h b/include/libfsverity.h
index 8f78a13..8d1f93b 100644
--- a/include/libfsverity.h
+++ b/include/libfsverity.h
@@ -112,6 +112,27 @@ libfsverity_sign_digest(const struct libfsverity_digest *digest,
 			const struct libfsverity_signature_params *sig_params,
 			uint8_t **sig_ret, size_t *sig_size_ret);
 
+/**
+ * libfsverity_enable() - Enable fs-verity on a file
+ *          An fsverity_digest (also called a "file measurement") is the root of
+ *          a file's Merkle tree.  Not to be confused with a traditional file
+ *          digest computed over the entire file.
+ * @file: path to the file to enable
+ * @signature: (optional) path to signature for @file
+ * @params: struct libfsverity_merkle_tree_params specifying the fs-verity
+ *	    version, the hash algorithm, the block size, and
+ *	    optionally a salt.  Reserved fields must be zero.
+ *      All fields bar the version are optional, and defaults will be used
+ *      if set to zero.
+ *
+ * Returns:
+ * * 0 for success, -EINVAL for invalid input arguments, or a generic error
+ *   if the FS_IOC_ENABLE_VERITY ioctl fails.
+ */
+int
+libfsverity_enable(const char *file, const char *signature,
+			struct libfsverity_merkle_tree_params *params);
+
 /**
  * libfsverity_find_hash_alg_by_name() - Find hash algorithm by name
  * @name: Pointer to name of hash algorithm
diff --git a/lib/enable.c b/lib/enable.c
new file mode 100644
index 0000000..ad86cc5
--- /dev/null
+++ b/lib/enable.c
@@ -0,0 +1,191 @@
+// SPDX-License-Identifier: MIT
+/*
+ * Implementation of libfsverity_enable().
+ *
+ * Copyright 2020 Microsoft
+ *
+ * Use of this source code is governed by an MIT-style
+ * license that can be found in the LICENSE file or at
+ * https://opensource.org/licenses/MIT.
+ */
+
+#include "lib_private.h"
+
+#include <fcntl.h>
+#include <getopt.h>
+#include <limits.h>
+#include <sys/ioctl.h>
+#include <sys/stat.h>
+#include <sys/types.h>
+#include <stdlib.h>
+#include <unistd.h>
+#include <stdio.h>
+#include <string.h>
+
+
+struct filedes {
+	int fd;
+	char *name;		/* filename, for logging or error messages */
+};
+
+static u32 get_default_block_size(void)
+{
+	long n = sysconf(_SC_PAGESIZE);
+
+	if (n <= 0 || n >= INT_MAX || !is_power_of_2(n)) {
+		libfsverity_error_msg("Warning: invalid _SC_PAGESIZE (%ld).  Assuming 4K blocks.\n",
+			n);
+		return 4096;
+	}
+	return n;
+}
+
+static bool open_file(struct filedes *file, const char *filename, int flags, int mode)
+{
+	file->fd = open(filename, flags, mode);
+	if (file->fd < 0) {
+		libfsverity_error_msg("can't open '%s' for %s", filename,
+				(flags & O_ACCMODE) == O_RDONLY ? "reading" :
+				(flags & O_ACCMODE) == O_WRONLY ? "writing" :
+				"reading and writing");
+		return false;
+	}
+	file->name = strdup(filename);
+	return true;
+}
+
+static bool get_file_size(struct filedes *file, u64 *size_ret)
+{
+	struct stat stbuf;
+
+	if (fstat(file->fd, &stbuf) != 0) {
+		libfsverity_error_msg("can't stat file '%s'", file->name);
+		return false;
+	}
+	*size_ret = stbuf.st_size;
+	return true;
+}
+
+static bool full_read(struct filedes *file, void *buf, size_t count)
+{
+	while (count) {
+		int n = read(file->fd, buf, min(count, INT_MAX));
+
+		if (n < 0) {
+			libfsverity_error_msg("reading from '%s'", file->name);
+			return false;
+		}
+		if (n == 0) {
+			libfsverity_error_msg("unexpected end-of-file on '%s'", file->name);
+			return false;
+		}
+		buf += n;
+		count -= n;
+	}
+	return true;
+}
+
+static bool filedes_close(struct filedes *file)
+{
+	int res;
+
+	if (file->fd < 0)
+		return true;
+	res = close(file->fd);
+	if (res != 0)
+		libfsverity_error_msg("closing '%s'", file->name);
+	file->fd = -1;
+	free(file->name);
+	file->name = NULL;
+	return res == 0;
+}
+
+static bool read_signature(const char *filename, u8 **sig_ret,
+			   u32 *sig_size_ret)
+{
+	struct filedes file = { .fd = -1 };
+	u64 file_size;
+	u8 *sig = NULL;
+	bool ok = false;
+
+	if (!open_file(&file, filename, O_RDONLY, 0))
+		goto out;
+	if (!get_file_size(&file, &file_size))
+		goto out;
+	if (file_size <= 0) {
+		libfsverity_error_msg("signature file '%s' is empty", filename);
+		goto out;
+	}
+	if (file_size > 1000000) {
+		libfsverity_error_msg("signature file '%s' is too large", filename);
+		goto out;
+	}
+	sig = libfsverity_zalloc(file_size);
+	if (!full_read(&file, sig, file_size))
+		goto out;
+	*sig_ret = sig;
+	*sig_size_ret = file_size;
+	sig = NULL;
+	ok = true;
+out:
+	filedes_close(&file);
+	free(sig);
+	return ok;
+}
+
+LIBEXPORT int
+libfsverity_enable(const char *file, const char *signature,
+			struct libfsverity_merkle_tree_params *params)
+{
+	struct fsverity_enable_arg arg;
+	u8 *sig = NULL;
+	struct filedes f;
+	int status;
+
+	if (!file || !params)  {
+		libfsverity_error_msg("missing required parameters for enable");
+		return -EINVAL;
+	}
+
+	arg = (struct fsverity_enable_arg) {
+			.version = params->version,
+			.hash_algorithm = params->hash_algorithm,
+			.block_size = params->block_size,
+			.salt_size = params->salt_size,
+			.salt_ptr = (uintptr_t)params->salt,
+	};
+
+	if (signature && !arg.sig_ptr) {
+		if (!read_signature(signature, &sig, &arg.sig_size))
+			return -EINVAL;
+		arg.sig_ptr = (uintptr_t)sig;
+	}
+
+	if (arg.hash_algorithm == 0)
+		arg.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
+
+	if (arg.block_size == 0)
+		arg.block_size = get_default_block_size();
+
+	if (!open_file(&f, file, O_RDONLY, 0)) {
+		status = -EINVAL;
+		goto out;
+	}
+	if (ioctl(f.fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
+		libfsverity_error_msg("FS_IOC_ENABLE_VERITY failed on '%s'",
+				f.name);
+		filedes_close(&f);
+		goto out_err;
+	}
+	if (!filedes_close(&f))
+		goto out_err;
+
+	status = 0;
+out:
+	free(sig);
+	return status;
+
+out_err:
+	status = 1;
+	goto out;
+}
diff --git a/programs/cmd_enable.c b/programs/cmd_enable.c
index d90d208..085b6a3 100644
--- a/programs/cmd_enable.c
+++ b/programs/cmd_enable.c
@@ -11,43 +11,7 @@
 
 #include "fsverity.h"
 
-#include <fcntl.h>
 #include <getopt.h>
-#include <limits.h>
-#include <sys/ioctl.h>
-
-static bool read_signature(const char *filename, u8 **sig_ret,
-			   u32 *sig_size_ret)
-{
-	struct filedes file = { .fd = -1 };
-	u64 file_size;
-	u8 *sig = NULL;
-	bool ok = false;
-
-	if (!open_file(&file, filename, O_RDONLY, 0))
-		goto out;
-	if (!get_file_size(&file, &file_size))
-		goto out;
-	if (file_size <= 0) {
-		error_msg("signature file '%s' is empty", filename);
-		goto out;
-	}
-	if (file_size > 1000000) {
-		error_msg("signature file '%s' is too large", filename);
-		goto out;
-	}
-	sig = xmalloc(file_size);
-	if (!full_read(&file, sig, file_size))
-		goto out;
-	*sig_ret = sig;
-	*sig_size_ret = file_size;
-	sig = NULL;
-	ok = true;
-out:
-	filedes_close(&file);
-	free(sig);
-	return ok;
-}
 
 enum {
 	OPT_HASH_ALG,
@@ -68,10 +32,8 @@ static const struct option longopts[] = {
 int fsverity_cmd_enable(const struct fsverity_command *cmd,
 			int argc, char *argv[])
 {
-	struct fsverity_enable_arg arg = { .version = 1 };
-	u8 *salt = NULL;
-	u8 *sig = NULL;
-	struct filedes file;
+	struct libfsverity_merkle_tree_params arg = { .version = 1 };
+	char *sig = NULL;
 	int status;
 	int c;
 
@@ -86,18 +48,17 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd,
 				goto out_usage;
 			break;
 		case OPT_SALT:
-			if (!parse_salt_option(optarg, &salt, &arg.salt_size))
+			if (!parse_salt_option(optarg, (uint8_t **)&arg.salt, &arg.salt_size))
 				goto out_usage;
-			arg.salt_ptr = (uintptr_t)salt;
 			break;
 		case OPT_SIGNATURE:
 			if (sig != NULL) {
 				error_msg("--signature can only be specified once");
 				goto out_usage;
 			}
-			if (!read_signature(optarg, &sig, &arg.sig_size))
+			sig = strdup(optarg);
+			if (!sig)
 				goto out_err;
-			arg.sig_ptr = (uintptr_t)sig;
 			break;
 		default:
 			goto out_usage;
@@ -110,26 +71,11 @@ int fsverity_cmd_enable(const struct fsverity_command *cmd,
 	if (argc != 1)
 		goto out_usage;
 
-	if (arg.hash_algorithm == 0)
-		arg.hash_algorithm = FS_VERITY_HASH_ALG_DEFAULT;
-
-	if (arg.block_size == 0)
-		arg.block_size = get_default_block_size();
-
-	if (!open_file(&file, argv[0], O_RDONLY, 0))
-		goto out_err;
-	if (ioctl(file.fd, FS_IOC_ENABLE_VERITY, &arg) != 0) {
-		error_msg_errno("FS_IOC_ENABLE_VERITY failed on '%s'",
-				file.name);
-		filedes_close(&file);
-		goto out_err;
-	}
-	if (!filedes_close(&file))
+	if (libfsverity_enable(argv[0], sig, &arg))
 		goto out_err;
 
 	status = 0;
 out:
-	free(salt);
 	free(sig);
 	return status;
 
diff --git a/programs/fsverity.h b/programs/fsverity.h
index 669fef2..5e33eee 100644
--- a/programs/fsverity.h
+++ b/programs/fsverity.h
@@ -14,15 +14,6 @@
 #include "utils.h"
 #include "../common/fsverity_uapi.h"
 
-/* The hash algorithm that 'fsverity' assumes when none is specified */
-#define FS_VERITY_HASH_ALG_DEFAULT	FS_VERITY_HASH_ALG_SHA256
-
-/*
- * Largest digest size among all hash algorithms supported by fs-verity.
- * This can be increased if needed.
- */
-#define FS_VERITY_MAX_DIGEST_SIZE	64
-
 struct fsverity_command;
 
 /* cmd_digest.c */
