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); |
+} |