Chromium Code Reviews
chromiumcodereview-hr@appspot.gserviceaccount.com (chromiumcodereview-hr) | Please choose your nickname with Settings | Help | Chromium Project | Gerrit Changes | Sign out
(439)

Unified Diff: src/lib/scryptenc/scryptenc.c

Issue 2847081: Initial code. (Closed) Base URL: ssh://git@chromiumos-git/libscrypt.git
Patch Set: Use scrypt distro and patch with changes instead. Created 10 years, 5 months ago
Use n/p to move between diff chunks; N/P to move between comments. Draft comments are only viewable by you.
Jump to:
View side-by-side diff with in-line comments
Download patch
« no previous file with comments | « src/lib/scryptenc/scryptenc.h ('k') | src/lib/scryptenc/scryptenc_cpuperf.h » ('j') | no next file with comments »
Expand Comments ('e') | Collapse Comments ('c') | Show Comments Hide Comments ('s')
Index: src/lib/scryptenc/scryptenc.c
diff --git a/src/lib/scryptenc/scryptenc.c b/src/lib/scryptenc/scryptenc.c
new file mode 100644
index 0000000000000000000000000000000000000000..3b7fd0f3f0854b753929bdcd6e5d793c9b99067b
--- /dev/null
+++ b/src/lib/scryptenc/scryptenc.c
@@ -0,0 +1,606 @@
+/*-
+ * Copyright 2009 Colin Percival
+ * All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions
+ * are met:
+ * 1. Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * 2. Redistributions in binary form must reproduce the above copyright
+ * notice, this list of conditions and the following disclaimer in the
+ * documentation and/or other materials provided with the distribution.
+ *
+ * THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
+ * ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
+ * IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE AUTHOR OR CONTRIBUTORS BE LIABLE
+ * FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
+ * DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
+ * OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
+ * HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
+ * LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
+ * OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
+ * SUCH DAMAGE.
+ *
+ * This file was originally written by Colin Percival as part of the Tarsnap
+ * online backup system.
+ */
+#include "scrypt_platform.h"
+
+#include <errno.h>
+#include <fcntl.h>
+#include <stdint.h>
+#include <stdio.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <openssl/aes.h>
+
+#include "crypto_aesctr.h"
+#include "crypto_scrypt.h"
+#include "memlimit.h"
+#include "scryptenc_cpuperf.h"
+#include "sha256.h"
+#include "sysendian.h"
+
+#include "scryptenc.h"
+
+#define ENCBLOCK 65536
+
+static int pickparams(size_t, double, double,
+ int *, uint32_t *, uint32_t *);
+static int checkparams(size_t, double, double, int, uint32_t, uint32_t);
+static int getsalt(uint8_t[32]);
+
+static int
+pickparams(size_t maxmem, double maxmemfrac, double maxtime,
+ int * logN, uint32_t * r, uint32_t * p)
+{
+ size_t memlimit;
+ double opps;
+ double opslimit;
+ double maxN, maxrp;
+ int rc;
+
+ /* Figure out how much memory to use. */
+ if (memtouse(maxmem, maxmemfrac, &memlimit))
+ return (1);
+
+ /* Figure out how fast the CPU is. */
+ if ((rc = scryptenc_cpuperf(&opps)) != 0)
+ return (rc);
+ opslimit = opps * maxtime;
+
+ /* Allow a minimum of 2^15 salsa20/8 cores. */
+ if (opslimit < 32768)
+ opslimit = 32768;
+
+ /* Fix r = 8 for now. */
+ *r = 8;
+
+ /*
+ * The memory limit requires that 128Nr <= memlimit, while the CPU
+ * limit requires that 4Nrp <= opslimit. If opslimit < memlimit/32,
+ * opslimit imposes the stronger limit on N.
+ */
+#ifdef DEBUG
+ fprintf(stderr, "Requiring 128Nr <= %zu, 4Nrp <= %f\n",
+ memlimit, opslimit);
+#endif
+ if (opslimit < memlimit/32) {
+ /* Set p = 1 and choose N based on the CPU limit. */
+ *p = 1;
+ maxN = opslimit / (*r * 4);
+ for (*logN = 1; *logN < 63; *logN += 1) {
+ if ((uint64_t)(1) << *logN > maxN / 2)
+ break;
+ }
+ } else {
+ /* Set N based on the memory limit. */
+ maxN = memlimit / (*r * 128);
+ for (*logN = 1; *logN < 63; *logN += 1) {
+ if ((uint64_t)(1) << *logN > maxN / 2)
+ break;
+ }
+
+ /* Choose p based on the CPU limit. */
+ maxrp = (opslimit / 4) / ((uint64_t)(1) << *logN);
+ if (maxrp > 0x3fffffff)
+ maxrp = 0x3fffffff;
+ *p = (uint32_t)(maxrp) / *r;
+ }
+
+#ifdef DEBUG
+ fprintf(stderr, "N = %zu r = %d p = %d\n",
+ (size_t)(1) << *logN, (int)(*r), (int)(*p));
+#endif
+
+ /* Success! */
+ return (0);
+}
+
+static int
+checkparams(size_t maxmem, double maxmemfrac, double maxtime,
+ int logN, uint32_t r, uint32_t p)
+{
+ size_t memlimit;
+ double opps;
+ double opslimit;
+ uint64_t N;
+ int rc;
+
+ /* Figure out the maximum amount of memory we can use. */
+ if (memtouse(maxmem, maxmemfrac, &memlimit))
+ return (1);
+
+ /* Figure out how fast the CPU is. */
+ if ((rc = scryptenc_cpuperf(&opps)) != 0)
+ return (rc);
+ opslimit = opps * maxtime;
+
+ /* Sanity-check values. */
+ if ((logN < 1) || (logN > 63))
+ return (7);
+ if ((uint64_t)(r) * (uint64_t)(p) >= 0x40000000)
+ return (7);
+
+ /* Check limits. */
+ N = (uint64_t)(1) << logN;
+ if ((memlimit / N) / r < 128)
+ return (9);
+ if ((opslimit / N) / (r * p) < 4)
+ return (10);
+
+ /* Success! */
+ return (0);
+}
+
+static int
+getsalt(uint8_t salt[32])
+{
+ int fd;
+ ssize_t lenread;
+ uint8_t * buf = salt;
+ size_t buflen = 32;
+
+ /* Open /dev/urandom. */
+ if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
+ goto err0;
+
+ /* Read bytes until we have filled the buffer. */
+ while (buflen > 0) {
+ if ((lenread = read(fd, buf, buflen)) == -1)
+ goto err1;
+
+ /* The random device should never EOF. */
+ if (lenread == 0)
+ goto err1;
+
+ /* We're partly done. */
+ buf += lenread;
+ buflen -= lenread;
+ }
+
+ /* Close the device. */
+ while (close(fd) == -1) {
+ if (errno != EINTR)
+ goto err0;
+ }
+
+ /* Success! */
+ return (0);
+
+err1:
+ close(fd);
+err0:
+ /* Failure! */
+ return (4);
+}
+
+static int
+scryptenc_setup(uint8_t header[96], uint8_t dk[64],
+ const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t salt[32];
+ uint8_t hbuf[32];
+ int logN;
+ uint64_t N;
+ uint32_t r;
+ uint32_t p;
+ SHA256_CTX ctx;
+ uint8_t * key_hmac = &dk[32];
+ HMAC_SHA256_CTX hctx;
+ int rc;
+
+ /* Pick values for N, r, p. */
+ if ((rc = pickparams(maxmem, maxmemfrac, maxtime,
+ &logN, &r, &p)) != 0)
+ return (rc);
+ N = (uint64_t)(1) << logN;
+
+ /* Get some salt. */
+ if ((rc = getsalt(salt)) != 0)
+ return (rc);
+
+ /* Generate the derived keys. */
+ if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64))
+ return (3);
+
+ /* Construct the file header. */
+ memcpy(header, "scrypt", 6);
+ header[6] = 0;
+ header[7] = logN;
+ be32enc(&header[8], r);
+ be32enc(&header[12], p);
+ memcpy(&header[16], salt, 32);
+
+ /* Add header checksum. */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, header, 48);
+ SHA256_Final(hbuf, &ctx);
+ memcpy(&header[48], hbuf, 16);
+
+ /* Add header signature (used for verifying password). */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, header, 64);
+ HMAC_SHA256_Final(hbuf, &hctx);
+ memcpy(&header[64], hbuf, 32);
+
+ /* Success! */
+ return (0);
+}
+
+static int
+scryptdec_setup(const uint8_t header[96], uint8_t dk[64],
+ const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t salt[32];
+ uint8_t hbuf[32];
+ int logN;
+ uint32_t r;
+ uint32_t p;
+ uint64_t N;
+ SHA256_CTX ctx;
+ uint8_t * key_hmac = &dk[32];
+ HMAC_SHA256_CTX hctx;
+ int rc;
+
+ /* Parse N, r, p, salt. */
+ logN = header[7];
+ r = be32dec(&header[8]);
+ p = be32dec(&header[12]);
+ memcpy(salt, &header[16], 32);
+
+ /* Verify header checksum. */
+ SHA256_Init(&ctx);
+ SHA256_Update(&ctx, header, 48);
+ SHA256_Final(hbuf, &ctx);
+ if (memcmp(&header[48], hbuf, 16))
+ return (7);
+
+ /*
+ * Check whether the provided parameters are valid and whether the
+ * key derivation function can be computed within the allowed memory
+ * and CPU time.
+ */
+ if ((rc = checkparams(maxmem, maxmemfrac, maxtime, logN, r, p)) != 0)
+ return (rc);
+
+ /* Compute the derived keys. */
+ N = (uint64_t)(1) << logN;
+ if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64))
+ return (3);
+
+ /* Check header signature (i.e., verify password). */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, header, 64);
+ HMAC_SHA256_Final(hbuf, &hctx);
+ if (memcmp(hbuf, &header[64], 32))
+ return (11);
+
+ /* Success! */
+ return (0);
+}
+
+/**
+ * scryptenc_buf(inbuf, inbuflen, outbuf, passwd, passwdlen,
+ * maxmem, maxmemfrac, maxtime):
+ * Encrypt inbuflen bytes from inbuf, writing the resulting inbuflen + 128
+ * bytes to outbuf.
+ */
+int
+scryptenc_buf(const uint8_t * inbuf, size_t inbuflen, uint8_t * outbuf,
+ const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t dk[64];
+ uint8_t hbuf[32];
+ uint8_t header[96];
+ uint8_t * key_enc = dk;
+ uint8_t * key_hmac = &dk[32];
+ int rc;
+ HMAC_SHA256_CTX hctx;
+ AES_KEY key_enc_exp;
+ struct crypto_aesctr * AES;
+
+ /* Generate the header and derived key. */
+ if ((rc = scryptenc_setup(header, dk, passwd, passwdlen,
+ maxmem, maxmemfrac, maxtime)) != 0)
+ return (rc);
+
+ /* Copy header into output buffer. */
+ memcpy(outbuf, header, 96);
+
+ /* Encrypt data. */
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
+ return (5);
+ if ((AES = crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
+ return (6);
+ crypto_aesctr_stream(AES, inbuf, &outbuf[96], inbuflen);
+ crypto_aesctr_free(AES);
+
+ /* Add signature. */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, outbuf, 96 + inbuflen);
+ HMAC_SHA256_Final(hbuf, &hctx);
+ memcpy(&outbuf[96 + inbuflen], hbuf, 32);
+
+ /* Zero sensitive data. */
+ memset(dk, 0, 64);
+ memset(&key_enc_exp, 0, sizeof(AES_KEY));
+
+ /* Success! */
+ return (0);
+}
+
+/**
+ * scryptdec_buf(inbuf, inbuflen, outbuf, outlen, passwd, passwdlen,
+ * maxmem, maxmemfrac, maxtime):
+ * Decrypt inbuflen bytes fro inbuf, writing the result into outbuf and the
+ * decrypted data length to outlen. The allocated length of outbuf must
+ * be at least inbuflen.
+ */
+int
+scryptdec_buf(const uint8_t * inbuf, size_t inbuflen, uint8_t * outbuf,
+ size_t * outlen, const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t hbuf[32];
+ uint8_t dk[64];
+ uint8_t * key_enc = dk;
+ uint8_t * key_hmac = &dk[32];
+ int rc;
+ HMAC_SHA256_CTX hctx;
+ AES_KEY key_enc_exp;
+ struct crypto_aesctr * AES;
+
+ /*
+ * All versions of the scrypt format will start with "scrypt" and
+ * have at least 7 bytes of header.
+ */
+ if ((inbuflen < 7) || (memcmp(inbuf, "scrypt", 6) != 0))
+ return (7);
+
+ /* Check the format. */
+ if (inbuf[6] != 0)
+ return (8);
+
+ /* We must have at least 128 bytes. */
+ if (inbuflen < 128)
+ return (7);
+
+ /* Parse the header and generate derived keys. */
+ if ((rc = scryptdec_setup(inbuf, dk, passwd, passwdlen,
+ maxmem, maxmemfrac, maxtime)) != 0)
+ return (rc);
+
+ /* Decrypt data. */
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
+ return (5);
+ if ((AES = crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
+ return (6);
+ crypto_aesctr_stream(AES, &inbuf[96], outbuf, inbuflen - 128);
+ crypto_aesctr_free(AES);
+ *outlen = inbuflen - 128;
+
+ /* Verify signature. */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, inbuf, inbuflen - 32);
+ HMAC_SHA256_Final(hbuf, &hctx);
+ if (memcmp(hbuf, &inbuf[inbuflen - 32], 32))
+ return (7);
+
+ /* Zero sensitive data. */
+ memset(dk, 0, 64);
+ memset(&key_enc_exp, 0, sizeof(AES_KEY));
+
+ /* Success! */
+ return (0);
+}
+
+/**
+ * scryptenc_file(infile, outfile, passwd, passwdlen,
+ * maxmem, maxmemfrac, maxtime):
+ * Read a stream from infile and encrypt it, writing the resulting stream to
+ * outfile.
+ */
+int
+scryptenc_file(FILE * infile, FILE * outfile,
+ const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t buf[ENCBLOCK];
+ uint8_t dk[64];
+ uint8_t hbuf[32];
+ uint8_t header[96];
+ uint8_t * key_enc = dk;
+ uint8_t * key_hmac = &dk[32];
+ size_t readlen;
+ HMAC_SHA256_CTX hctx;
+ AES_KEY key_enc_exp;
+ struct crypto_aesctr * AES;
+ int rc;
+
+ /* Generate the header and derived key. */
+ if ((rc = scryptenc_setup(header, dk, passwd, passwdlen,
+ maxmem, maxmemfrac, maxtime)) != 0)
+ return (rc);
+
+ /* Hash and write the header. */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, header, 96);
+ if (fwrite(header, 96, 1, outfile) != 1)
+ return (12);
+
+ /*
+ * Read blocks of data, encrypt them, and write them out; hash the
+ * data as it is produced.
+ */
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
+ return (5);
+ if ((AES = crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
+ return (6);
+ do {
+ if ((readlen = fread(buf, 1, ENCBLOCK, infile)) == 0)
+ break;
+ crypto_aesctr_stream(AES, buf, buf, readlen);
+ HMAC_SHA256_Update(&hctx, buf, readlen);
+ if (fwrite(buf, 1, readlen, outfile) < readlen)
+ return (12);
+ } while (1);
+ crypto_aesctr_free(AES);
+
+ /* Did we exit the loop due to a read error? */
+ if (ferror(infile))
+ return (13);
+
+ /* Compute the final HMAC and output it. */
+ HMAC_SHA256_Final(hbuf, &hctx);
+ if (fwrite(hbuf, 32, 1, outfile) != 1)
+ return (12);
+
+ /* Zero sensitive data. */
+ memset(dk, 0, 64);
+ memset(&key_enc_exp, 0, sizeof(AES_KEY));
+
+ /* Success! */
+ return (0);
+}
+
+/**
+ * scryptdec_file(infile, outfile, passwd, passwdlen,
+ * maxmem, maxmemfrac, maxtime):
+ * Read a stream from infile and decrypt it, writing the resulting stream to
+ * outfile.
+ */
+int
+scryptdec_file(FILE * infile, FILE * outfile,
+ const uint8_t * passwd, size_t passwdlen,
+ size_t maxmem, double maxmemfrac, double maxtime)
+{
+ uint8_t buf[ENCBLOCK + 32];
+ uint8_t header[96];
+ uint8_t hbuf[32];
+ uint8_t dk[64];
+ uint8_t * key_enc = dk;
+ uint8_t * key_hmac = &dk[32];
+ size_t buflen = 0;
+ size_t readlen;
+ HMAC_SHA256_CTX hctx;
+ AES_KEY key_enc_exp;
+ struct crypto_aesctr * AES;
+ int rc;
+
+ /*
+ * Read the first 7 bytes of the file; all future version of scrypt
+ * are guaranteed to have at least 7 bytes of header.
+ */
+ if (fread(header, 7, 1, infile) < 1) {
+ if (ferror(infile))
+ return (13);
+ else
+ return (7);
+ }
+
+ /* Do we have the right magic? */
+ if (memcmp(header, "scrypt", 6))
+ return (7);
+ if (header[6] != 0)
+ return (8);
+
+ /*
+ * Read another 89 bytes of the file; version 0 of the srypt file
+ * format has a 96-byte header.
+ */
+ if (fread(&header[7], 89, 1, infile) < 1) {
+ if (ferror(infile))
+ return (13);
+ else
+ return (7);
+ }
+
+ /* Parse the header and generate derived keys. */
+ if ((rc = scryptdec_setup(header, dk, passwd, passwdlen,
+ maxmem, maxmemfrac, maxtime)) != 0)
+ return (rc);
+
+ /* Start hashing with the header. */
+ HMAC_SHA256_Init(&hctx, key_hmac, 32);
+ HMAC_SHA256_Update(&hctx, header, 96);
+
+ /*
+ * We don't know how long the encrypted data block is (we can't know,
+ * since data can be streamed into 'scrypt enc') so we need to read
+ * data and decrypt all of it except the final 32 bytes, then check
+ * if that final 32 bytes is the correct signature.
+ */
+ if (AES_set_encrypt_key(key_enc, 256, &key_enc_exp))
+ return (5);
+ if ((AES = crypto_aesctr_init(&key_enc_exp, 0)) == NULL)
+ return (6);
+ do {
+ /* Read data until we have more than 32 bytes of it. */
+ if ((readlen = fread(&buf[buflen], 1,
+ ENCBLOCK + 32 - buflen, infile)) == 0)
+ break;
+ buflen += readlen;
+ if (buflen <= 32)
+ continue;
+
+ /*
+ * Decrypt, hash, and output everything except the last 32
+ * bytes out of what we have in our buffer.
+ */
+ HMAC_SHA256_Update(&hctx, buf, buflen - 32);
+ crypto_aesctr_stream(AES, buf, buf, buflen - 32);
+ if (fwrite(buf, 1, buflen - 32, outfile) < buflen - 32)
+ return (12);
+
+ /* Move the last 32 bytes to the start of the buffer. */
+ memmove(buf, &buf[buflen - 32], 32);
+ buflen = 32;
+ } while (1);
+ crypto_aesctr_free(AES);
+
+ /* Did we exit the loop due to a read error? */
+ if (ferror(infile))
+ return (13);
+
+ /* Did we read enough data that we *might* have a valid signature? */
+ if (buflen < 32)
+ return (7);
+
+ /* Verify signature. */
+ HMAC_SHA256_Final(hbuf, &hctx);
+ if (memcmp(hbuf, buf, 32))
+ return (7);
+
+ /* Zero sensitive data. */
+ memset(dk, 0, 64);
+ memset(&key_enc_exp, 0, sizeof(AES_KEY));
+
+ return (0);
+}
« no previous file with comments | « src/lib/scryptenc/scryptenc.h ('k') | src/lib/scryptenc/scryptenc_cpuperf.h » ('j') | no next file with comments »

Powered by Google App Engine
This is Rietveld 408576698