#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;
if (memtouse(maxmem, maxmemfrac, &memlimit))
return (1);
if ((rc = scryptenc_cpuperf(&opps)) != 0)
return (rc);
opslimit = opps * maxtime;
if (opslimit < 32768)
opslimit = 32768;
*r = 8;
#ifdef DEBUG
fprintf(stderr, "Requiring 128Nr <= %zu, 4Nrp <= %f\n",
memlimit, opslimit);
#endif
if (opslimit < memlimit/32) {
*p = 1;
maxN = opslimit / (*r * 4);
for (*logN = 1; *logN < 63; *logN += 1) {
if ((uint64_t)(1) << *logN > maxN / 2)
break;
}
} else {
maxN = memlimit / (*r * 128);
for (*logN = 1; *logN < 63; *logN += 1) {
if ((uint64_t)(1) << *logN > maxN / 2)
break;
}
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
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;
if (memtouse(maxmem, maxmemfrac, &memlimit))
return (1);
if ((rc = scryptenc_cpuperf(&opps)) != 0)
return (rc);
opslimit = opps * maxtime;
if ((logN < 1) || (logN > 63))
return (7);
if ((uint64_t)(r) * (uint64_t)(p) >= 0x40000000)
return (7);
N = (uint64_t)(1) << logN;
if ((memlimit / N) / r < 128)
return (9);
if ((opslimit / N) / (r * p) < 4)
return (10);
return (0);
}
static int
getsalt(uint8_t salt[32])
{
int fd;
ssize_t lenread;
uint8_t * buf = salt;
size_t buflen = 32;
if ((fd = open("/dev/urandom", O_RDONLY)) == -1)
goto err0;
while (buflen > 0) {
if ((lenread = read(fd, buf, buflen)) == -1)
goto err1;
if (lenread == 0)
goto err1;
buf += lenread;
buflen -= lenread;
}
while (close(fd) == -1) {
if (errno != EINTR)
goto err0;
}
return (0);
err1:
close(fd);
err0:
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;
if ((rc = pickparams(maxmem, maxmemfrac, maxtime,
&logN, &r, &p)) != 0)
return (rc);
N = (uint64_t)(1) << logN;
if ((rc = getsalt(salt)) != 0)
return (rc);
if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64))
return (3);
memcpy(header, "scrypt", 6);
header[6] = 0;
header[7] = logN;
be32enc(&header[8], r);
be32enc(&header[12], p);
memcpy(&header[16], salt, 32);
SHA256_Init(&ctx);
SHA256_Update(&ctx, header, 48);
SHA256_Final(hbuf, &ctx);
memcpy(&header[48], hbuf, 16);
HMAC_SHA256_Init(&hctx, key_hmac, 32);
HMAC_SHA256_Update(&hctx, header, 64);
HMAC_SHA256_Final(hbuf, &hctx);
memcpy(&header[64], hbuf, 32);
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;
logN = header[7];
r = be32dec(&header[8]);
p = be32dec(&header[12]);
memcpy(salt, &header[16], 32);
SHA256_Init(&ctx);
SHA256_Update(&ctx, header, 48);
SHA256_Final(hbuf, &ctx);
if (memcmp(&header[48], hbuf, 16))
return (7);
if ((rc = checkparams(maxmem, maxmemfrac, maxtime, logN, r, p)) != 0)
return (rc);
N = (uint64_t)(1) << logN;
if (crypto_scrypt(passwd, passwdlen, salt, 32, N, r, p, dk, 64))
return (3);
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);
return (0);
}
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;
if ((rc = scryptenc_setup(header, dk, passwd, passwdlen,
maxmem, maxmemfrac, maxtime)) != 0)
return (rc);
memcpy(outbuf, header, 96);
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);
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);
memset(dk, 0, 64);
memset(&key_enc_exp, 0, sizeof(AES_KEY));
return (0);
}
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;
if ((inbuflen < 7) || (memcmp(inbuf, "scrypt", 6) != 0))
return (7);
if (inbuf[6] != 0)
return (8);
if (inbuflen < 128)
return (7);
if ((rc = scryptdec_setup(inbuf, dk, passwd, passwdlen,
maxmem, maxmemfrac, maxtime)) != 0)
return (rc);
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;
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);
memset(dk, 0, 64);
memset(&key_enc_exp, 0, sizeof(AES_KEY));
return (0);
}
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;
if ((rc = scryptenc_setup(header, dk, passwd, passwdlen,
maxmem, maxmemfrac, maxtime)) != 0)
return (rc);
HMAC_SHA256_Init(&hctx, key_hmac, 32);
HMAC_SHA256_Update(&hctx, header, 96);
if (fwrite(header, 96, 1, outfile) != 1)
return (12);
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);
if (ferror(infile))
return (13);
HMAC_SHA256_Final(hbuf, &hctx);
if (fwrite(hbuf, 32, 1, outfile) != 1)
return (12);
memset(dk, 0, 64);
memset(&key_enc_exp, 0, sizeof(AES_KEY));
return (0);
}
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;
if (fread(header, 7, 1, infile) < 1) {
if (ferror(infile))
return (13);
else
return (7);
}
if (memcmp(header, "scrypt", 6))
return (7);
if (header[6] != 0)
return (8);
if (fread(&header[7], 89, 1, infile) < 1) {
if (ferror(infile))
return (13);
else
return (7);
}
if ((rc = scryptdec_setup(header, dk, passwd, passwdlen,
maxmem, maxmemfrac, maxtime)) != 0)
return (rc);
HMAC_SHA256_Init(&hctx, key_hmac, 32);
HMAC_SHA256_Update(&hctx, header, 96);
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[buflen], 1,
ENCBLOCK + 32 - buflen, infile)) == 0)
break;
buflen += readlen;
if (buflen <= 32)
continue;
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);
memmove(buf, &buf[buflen - 32], 32);
buflen = 32;
} while (1);
crypto_aesctr_free(AES);
if (ferror(infile))
return (13);
if (buflen < 32)
return (7);
HMAC_SHA256_Final(hbuf, &hctx);
if (memcmp(hbuf, buf, 32))
return (7);
memset(dk, 0, 64);
memset(&key_enc_exp, 0, sizeof(AES_KEY));
return (0);
}