/** * scryptdec_buf(inbuf, inbuflen, outbuf, outlen, passwd, passwdlen, * maxmem, maxmemfrac, maxtime): * Decrypt inbuflen bytes from 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; struct crypto_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 ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) 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); crypto_aes_key_free(key_enc_exp); *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. */ insecure_memzero(dk, 64); /* Success! */ return (0); }
/** * crypto_aesctr_free(stream): * Free the provided stream object. */ void crypto_aesctr_free(struct crypto_aesctr * stream) { /* Behave consistently with free(NULL). */ if (stream == NULL) return; /* Zero potentially sensitive information. */ insecure_memzero(stream, sizeof(struct crypto_aesctr)); /* Free the stream. */ free(stream); }
/** * crypto_aes_key_free_aesni(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free_aesni(void * key) { /* Behave consistently with free(NULL). */ if (key == NULL) return; /* Attempt to zero the expanded key. */ insecure_memzero(key, sizeof(struct crypto_aes_key_aesni)); /* Free the key. */ free(key); }
/** * crypto_aesctr_buf(key, nonce, inbuf, outbuf, buflen): * Equivalent to init(key, nonce); stream(inbuf, outbuf, buflen); free. */ void crypto_aesctr_buf(const struct crypto_aes_key * key, uint64_t nonce, const uint8_t * inbuf, uint8_t * outbuf, size_t buflen) { struct crypto_aesctr stream_rec; struct crypto_aesctr * stream = &stream_rec; /* Initialize values. */ stream->key = key; stream->nonce = nonce; stream->bytectr = 0; /* Perform the encryption. */ crypto_aesctr_stream(stream, inbuf, outbuf, buflen); /* Zero potentially sensitive information. */ insecure_memzero(stream, sizeof(struct crypto_aesctr)); }
/** * 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; struct crypto_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 ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) 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); crypto_aes_key_free(key_enc_exp); /* 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. */ insecure_memzero(dk, 64); /* Success! */ return (0); }
/** * crypto_aes_key_free(key): * Free the expanded AES key ${key}. */ void crypto_aes_key_free(struct crypto_aes_key * key) { #ifdef CPUSUPPORT_X86_AESNI if (useaesni()) { crypto_aes_key_free_aesni((void *)key); return; } #endif /* Behave consistently with free(NULL). */ if (key == NULL) return; /* Attempt to zero the expanded key. */ insecure_memzero(key, sizeof(AES_KEY)); /* Free the key. */ free(key); }
/** * 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; struct crypto_aes_key * key_enc_exp; struct crypto_aesctr * AES; int rc; /* * Read the first 7 bytes of the file; all future versions 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 scrypt 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 ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) 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) { crypto_aesctr_free(AES); 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); crypto_aes_key_free(key_enc_exp); /* 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. */ insecure_memzero(dk, 64); 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; struct crypto_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 ((key_enc_exp = crypto_aes_key_expand(key_enc, 32)) == NULL) 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) { crypto_aesctr_free(AES); return (12); } } while (1); crypto_aesctr_free(AES); crypto_aes_key_free(key_enc_exp); /* 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. */ insecure_memzero(dk, 64); /* Success! */ return (0); }
int main(int argc, char *argv[]) { FILE * infile; FILE * outfile; int dec = 0; size_t maxmem = 0; double maxmemfrac = 0.5; double maxtime = 300.0; const char * ch; char * passwd; int rc; int verbose = 0; WARNP_INIT; /* We should have "enc" or "dec" first. */ if (argc < 2) usage(); if (strcmp(argv[1], "enc") == 0) { maxmem = 0; maxmemfrac = 0.125; maxtime = 5.0; } else if (strcmp(argv[1], "dec") == 0) { dec = 1; } else usage(); argc--; argv++; /* Parse arguments. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("-M"): maxmem = strtoumax(optarg, NULL, 0); break; GETOPT_OPTARG("-m"): maxmemfrac = strtod(optarg, NULL); break; GETOPT_OPTARG("-t"): maxtime = strtod(optarg, NULL); break; GETOPT_OPT("-v"): verbose = 1; break; GETOPT_MISSING_ARG: warn0("Missing argument to %s\n", ch); /* FALLTHROUGH */ GETOPT_DEFAULT: usage(); } } argc -= optind; argv += optind; /* We must have one or two parameters left. */ if ((argc < 1) || (argc > 2)) usage(); /* If the input isn't stdin, open the file. */ if (strcmp(argv[0], "-")) { if ((infile = fopen(argv[0], "rb")) == NULL) { warnp("Cannot open input file: %s", argv[0]); exit(1); } } else { infile = stdin; } /* If we have an output file, open it. */ if (argc > 1) { if ((outfile = fopen(argv[1], "wb")) == NULL) { warnp("Cannot open output file: %s", argv[1]); exit(1); } } else { outfile = stdout; } /* Prompt for a password. */ if (readpass(&passwd, "Please enter passphrase", dec ? NULL : "Please confirm passphrase", 1)) exit(1); /* Encrypt or decrypt. */ if (dec) rc = scryptdec_file(infile, outfile, (uint8_t *)passwd, strlen(passwd), maxmem, maxmemfrac, maxtime, verbose); else rc = scryptenc_file(infile, outfile, (uint8_t *)passwd, strlen(passwd), maxmem, maxmemfrac, maxtime, verbose); /* Zero and free the password. */ insecure_memzero(passwd, strlen(passwd)); free(passwd); /* Close any files we opened. */ if (infile != stdin) fclose(infile); if (outfile != stdout) fclose(outfile); /* If we failed, print the right error message and exit. */ if (rc != 0) { switch (rc) { case 1: warnp("Error determining amount of available memory"); break; case 2: warnp("Error reading clocks"); break; case 3: warnp("Error computing derived key"); break; case 4: warnp("Error reading salt"); break; case 5: warnp("OpenSSL error"); break; case 6: warnp("Error allocating memory"); break; case 7: warn0("Input is not valid scrypt-encrypted block"); break; case 8: warn0("Unrecognized scrypt format version"); break; case 9: warn0("Decrypting file would require too much memory"); break; case 10: warn0("Decrypting file would take too much CPU time"); break; case 11: warn0("Passphrase is incorrect"); break; case 12: warnp("Error writing file: %s", (argc > 1) ? argv[1] : "standard output"); break; case 13: warnp("Error reading file: %s", argv[0]); break; } exit(1); } return (0); }
int main(int argc, char * argv[]) { /* State variables. */ struct serverpool * SP; struct dynamodb_request_queue * QW; struct dynamodb_request_queue * QR; struct dispatch_state * D; int s; /* Command-line parameters. */ char * opt_k = NULL; char * opt_l = NULL; char * opt_p = NULL; char * opt_r = NULL; char * opt_s = NULL; char * opt_t = NULL; int opt_1 = 0; /* Working variable. */ char * dynamodb_host; char * key_id; char * key_secret; struct sock_addr ** sas; struct logging_file * logfile; struct capacity_reader * M; const char * ch; WARNP_INIT; /* Parse the command line. */ while ((ch = GETOPT(argc, argv)) != NULL) { GETOPT_SWITCH(ch) { GETOPT_OPTARG("-k"): if (opt_k != NULL) usage(); if ((opt_k = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-l"): if (opt_l != NULL) usage(); if ((opt_l = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-p"): if (opt_p != NULL) usage(); if ((opt_p = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-r"): if (opt_r != NULL) usage(); if ((opt_r = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-s"): if (opt_s != NULL) usage(); if ((opt_s = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPTARG("-t"): if (opt_t != NULL) usage(); if ((opt_t = strdup(optarg)) == NULL) OPT_EPARSE(ch, optarg); break; GETOPT_OPT("--version"): fprintf(stderr, "dynamodb-kv @VERSION@\n"); exit(0); GETOPT_OPT("-1"): if (opt_1 != 0) usage(); opt_1 = 1; break; GETOPT_MISSING_ARG: warn0("Missing argument to %s\n", ch); usage(); GETOPT_DEFAULT: warn0("illegal option -- %s\n", ch); usage(); } } argc -= optind; argv += optind; /* We should have processed all the arguments. */ if (argc != 0) usage(); /* Verify that we have mandatory options. */ if (opt_k == NULL) usage(); if (opt_r == NULL) usage(); if (opt_s == NULL) usage(); if (opt_t == NULL) usage(); /* Construct the DynamoDB endpoint host name. */ if (asprintf(&dynamodb_host, "dynamodb.%s.amazonaws.com:80", opt_r) == -1) { warnp("asprintf"); exit(1); } /* Start looking up addresses for DynamoDB endpoints. */ if ((SP = serverpool_create(dynamodb_host, 15, 120)) == NULL) { warnp("Error starting DNS lookups for %s", dynamodb_host); exit(1); } /* Read the key file. */ if (aws_readkeys(opt_k, &key_id, &key_secret)) { warnp("Error reading AWS keys from %s", opt_k); exit(1); } /* Create DynamoDB request queues for writes and reads. */ if ((QW = dynamodb_request_queue_init(key_id, key_secret, opt_r, SP)) == NULL) { warnp("Error creating DynamoDB request queue"); exit(1); } if ((QR = dynamodb_request_queue_init(key_id, key_secret, opt_r, SP)) == NULL) { warnp("Error creating DynamoDB request queue"); exit(1); } /* Start reading table throughput parameters. */ if ((M = capacity_init(key_id, key_secret, opt_t, opt_r, SP, QW, QR)) == NULL) { warnp("Error reading DynamoDB table metadata"); exit(1); } /* Resolve the listening address. */ if ((sas = sock_resolve(opt_s)) == NULL) { warnp("Error resolving socket address: %s", opt_s); exit(1); } if (sas[0] == NULL) { warn0("No addresses found for %s", opt_s); exit(1); } /* Create and bind a socket, and mark it as listening. */ if (sas[1] != NULL) warn0("Listening on first of multiple addresses found for %s", opt_s); if ((s = sock_listener(sas[0])) == -1) exit(1); /* If requested, create a log file. */ if (opt_l != NULL) { if ((logfile = logging_open(opt_l)) == NULL) { warnp("Cannot open log file"); exit(1); } dynamodb_request_queue_log(QW, logfile); dynamodb_request_queue_log(QR, logfile); } else { logfile = NULL; } /* Daemonize and write pid. */ if (opt_p == NULL) { if (asprintf(&opt_p, "%s.pid", opt_s) == -1) { warnp("asprintf"); exit(1); } } if (daemonize(opt_p)) { warnp("Failed to daemonize"); exit(1); } /* Handle connections, one at once. */ do { /* accept a connection. */ if ((D = dispatch_accept(QW, QR, opt_t, s)) == NULL) { warnp("Error accepting new connection"); exit(1); } /* Loop until the connection dies. */ do { if (events_run()) { warnp("Error running event loop"); exit(1); } } while (dispatch_alive(D)); /* Clean up the connection. */ if (dispatch_done(D)) exit(1); } while (opt_1 == 0); /* Close the log file, if we have one. */ if (logfile != NULL) logging_close(logfile); /* Close the listening socket. */ close(s); /* Free the address structures. */ sock_addr_freelist(sas); /* Stop performing DescribeTable requests. */ capacity_free(M); /* Free DynamoDB request queues. */ dynamodb_request_queue_free(QR); dynamodb_request_queue_free(QW); /* Stop DNS lookups. */ serverpool_free(SP); /* Shut down the event subsystem. */ events_shutdown(); /* Free string allocated by asprintf. */ free(dynamodb_host); /* Free key strings. */ free(key_id); insecure_memzero(key_secret, strlen(key_secret)); free(key_secret); /* Free option strings. */ free(opt_k); free(opt_l); free(opt_p); free(opt_r); free(opt_s); free(opt_t); /* Success! */ exit(0); }
/** * readpass(passwd, prompt, confirmprompt, devtty) * If ${devtty} is non-zero, read a password from /dev/tty if possible; if * not, read from stdin. If reading from a tty (either /dev/tty or stdin), * disable echo and prompt the user by printing ${prompt} to stderr. If * ${confirmprompt} is non-NULL, read a second password (prompting if a * terminal is being used) and repeat until the user enters the same password * twice. Return the password as a malloced NUL-terminated string via * ${passwd}. */ int readpass(char ** passwd, const char * prompt, const char * confirmprompt, int devtty) { FILE * readfrom; char passbuf[MAXPASSLEN]; char confpassbuf[MAXPASSLEN]; struct sigaction sa, savedsa[NSIGS]; struct termios term, term_old; size_t i; int usingtty; /* * If devtty != 0, try to open /dev/tty; if that fails, or if devtty * is zero, we'll read the password from stdin instead. */ if ((devtty == 0) || ((readfrom = fopen("/dev/tty", "r")) == NULL)) readfrom = stdin; /* We have not received any signals yet. */ for (i = 0; i <= MAXSIG; i++) gotsig[i] = 0; /* * If we receive a signal while we're reading the password, we might * end up with echo disabled; to prevent this, we catch the signals * here, and we'll re-send them to ourselves later after we re-enable * terminal echo. */ sa.sa_handler = handle; sa.sa_flags = 0; sigemptyset(&sa.sa_mask); for (i = 0; i < NSIGS; i++) sigaction(badsigs[i], &sa, &savedsa[i]); /* If we're reading from a terminal, try to disable echo. */ if ((usingtty = isatty(fileno(readfrom))) != 0) { if (tcgetattr(fileno(readfrom), &term_old)) { warnp("Cannot read terminal settings"); goto err2; } memcpy(&term, &term_old, sizeof(struct termios)); term.c_lflag = (term.c_lflag & ~ECHO) | ECHONL; if (tcsetattr(fileno(readfrom), TCSANOW, &term)) { warnp("Cannot set terminal settings"); goto err2; } } retry: /* If we have a terminal, prompt the user to enter the password. */ if (usingtty) fprintf(stderr, "%s: ", prompt); /* Read the password. */ if (fgets(passbuf, MAXPASSLEN, readfrom) == NULL) { warnp("Cannot read password"); goto err3; } /* Confirm the password if necessary. */ if (confirmprompt != NULL) { if (usingtty) fprintf(stderr, "%s: ", confirmprompt); if (fgets(confpassbuf, MAXPASSLEN, readfrom) == NULL) { warnp("Cannot read password"); goto err3; } if (strcmp(passbuf, confpassbuf)) { fprintf(stderr, "Passwords mismatch, please try again\n"); goto retry; } } /* Terminate the string at the first "\r" or "\n" (if any). */ passbuf[strcspn(passbuf, "\r\n")] = '\0'; /* If we changed terminal settings, reset them. */ if (usingtty) tcsetattr(fileno(readfrom), TCSANOW, &term_old); /* Restore old signals. */ for (i = 0; i < NSIGS; i++) sigaction(badsigs[i], &savedsa[i], NULL); /* If we intercepted a signal, re-issue it. */ for (i = 0; i < NSIGS; i++) { if (gotsig[badsigs[i]]) raise(badsigs[i]); } /* Close /dev/tty if we opened it. */ if (readfrom != stdin) fclose(readfrom); /* Copy the password out. */ if ((*passwd = strdup(passbuf)) == NULL) { warnp("Cannot allocate memory"); goto err1; } /* * Zero any stored passwords. This is not guaranteed to work, since a * "sufficiently intelligent" compiler can optimize these out due to * the values not being accessed again; and even if we outwitted the * compiler, all we can do is ensure that *a* buffer is zeroed but * not that it is the only buffer containing the data in question. * Unfortunately the C standard does not provide any way to mark data * as "sensitive" in order to prevent extra copies being sprinkled * around the implementation address space. */ insecure_memzero(passbuf, MAXPASSLEN); insecure_memzero(confpassbuf, MAXPASSLEN); /* Success! */ return (0); err3: /* Reset terminal settings if necessary. */ if (usingtty) tcsetattr(fileno(readfrom), TCSAFLUSH, &term_old); err2: /* Close /dev/tty if we opened it. */ if (readfrom != stdin) fclose(readfrom); err1: /* Zero any stored passwords. */ insecure_memzero(passbuf, MAXPASSLEN); insecure_memzero(confpassbuf, MAXPASSLEN); /* Failure! */ return (-1); }
/** * aws_readkeys(fname, key_id, key_secret): * Read an AWS key id and secret from the file ${fname}, returning malloced * strings via ${key_id} and ${key_secret}. */ int aws_readkeys(const char * fname, char ** key_id, char ** key_secret) { FILE * f; char buf[1024]; char * p; /* No keys yet. */ *key_id = *key_secret = NULL; /* Open the key file. */ if ((f = fopen(fname, "r")) == NULL) { warnp("fopen(%s)", fname); goto err0; } /* Read lines of up to 1024 characters. */ while (fgets(buf, sizeof(buf), f) != NULL) { /* Find the first EOL character and truncate. */ p = buf + strcspn(buf, "\r\n"); if (*p == '\0') { warn0("Missing EOL in %s", fname); break; } else *p = '\0'; /* Look for the first = character. */ p = strchr(buf, '='); /* Missing separator? */ if (p == NULL) goto err3; /* Replace separator with NUL and point p at the value. */ *p++ = '\0'; /* We should have ACCESS_KEY_ID or ACCESS_KEY_SECRET. */ if (strcmp(buf, "ACCESS_KEY_ID") == 0) { /* Copy key ID string. */ if (*key_id != NULL) { warn0("ACCESS_KEY_ID specified twice"); goto err2; } if ((*key_id = strdup(p)) == NULL) goto err2; } else if (strcmp(buf, "ACCESS_KEY_SECRET") == 0) { /* Copy key secret string. */ if (*key_secret != NULL) { warn0("ACCESS_KEY_SECRET specified twice"); goto err2; } if ((*key_secret = strdup(p)) == NULL) goto err2; } else goto err3; } /* Check for error. */ if (ferror(f)) { warnp("Error reading %s", fname); goto err2; } /* Close the file. */ if (fclose(f)) { warnp("fclose"); goto err1; } /* Check that we got the necessary keys. */ if ((*key_id == NULL) || (*key_secret == NULL)) { warn0("Need ACCESS_KEY_ID and ACCESS_KEY_SECRET"); goto err1; } /* Success! */ return (0); err3: warn0("Lines in %s must be ACCESS_KEY_(ID|SECRET)=...", fname); err2: fclose(f); err1: free(*key_id); if (*key_secret) { insecure_memzero(*key_secret, strlen(*key_secret)); free(*key_secret); } err0: /* Failure! */ return (-1); }
int keygen_actual(struct register_internal * C, const char * keyfilename, const int passphrased, const uint64_t maxmem, const double maxtime, const char *oldkeyfilename) { FILE * keyfile; char * passphrase = NULL; int keymask = CRYPTO_KEYMASK_USER; uint64_t dummy; /* Sanity-check the user name. */ if (strlen(C->user) > 255) { fprintf(stderr, "User name too long: %s\n", C->user); goto err0; } if (strlen(C->user) == 0) { fprintf(stderr, "User name must be non-empty\n"); goto err0; } /* Sanity-check the machine name. */ if (strlen(C->name) > 255) { fprintf(stderr, "Machine name too long: %s\n", C->name); goto err0; } if (strlen(C->name) == 0) { fprintf(stderr, "Machine name must be non-empty\n"); goto err0; } /* Get a password. */ if (readpass(&C->passwd, "Enter tarsnap account password", NULL, 0)) { warnp("Error reading password"); goto err0; } /* * Create key file -- we do this now rather than later so that we * avoid registering with the server if we won't be able to create * the key file later. */ if ((keyfile = keyfile_write_open(keyfilename)) == NULL) { warnp("Cannot create %s", keyfilename); goto err1; } /* Initialize key cache. */ if (crypto_keys_init()) { warnp("Key cache initialization failed"); goto err3; } /* keyregen (with oldkeyfilename) only regenerates certain keys. */ if (oldkeyfilename != NULL) { /* * Load the keys CRYPTO_KEY_HMAC_{CHUNK, NAME, CPARAMS} * from the old key file, since these are the keys which need * to be consistent in order for two key sets to be * compatible. (CHUNK and NAME are used to compute the * 32-byte keys for blocks; CPARAMS is used to compute * parameters used to split a stream of bytes into chunks.) */ if (keyfile_read(oldkeyfilename, &dummy, CRYPTO_KEYMASK_HMAC_CHUNK | CRYPTO_KEYMASK_HMAC_NAME | CRYPTO_KEYMASK_HMAC_CPARAMS)) { warnp("Error reading old key file"); goto err3; } /* * Adjust the keymask to avoid regenerating keys we read from * the old keyfile. */ keymask &= ~CRYPTO_KEYMASK_HMAC_CHUNK & ~CRYPTO_KEYMASK_HMAC_NAME & ~CRYPTO_KEYMASK_HMAC_CPARAMS; } /* Generate keys. */ if (crypto_keys_generate(keymask)) { warnp("Error generating keys"); goto err3; } /* Register the keys with the server. */ if (keygen_network_register(C) != 0) goto err3; /* Exit with a code of 1 if we couldn't register. */ if (C->machinenum == (uint64_t)(-1)) goto err3; /* If the user wants to passphrase the keyfile, get the passphrase. */ if (passphrased != 0) { if (readpass(&passphrase, "Please enter passphrase for keyfile encryption", "Please confirm passphrase for keyfile encryption", 1)) { warnp("Error reading password"); goto err3; } } /* Write keys to file. */ if (keyfile_write_file(keyfile, C->machinenum, CRYPTO_KEYMASK_USER, passphrase, maxmem, maxtime)) goto err3; /* Close the key file. */ if (fclose(keyfile)) { warnp("Error closing key file"); goto err2; } /* Free allocated memory. C->passwd is a NUL-terminated string. */ insecure_memzero(C->passwd, strlen(C->passwd)); free(C->passwd); /* Free passphrase, if used. passphrase is a NUL-terminated string. */ if (passphrase != NULL) { insecure_memzero(passphrase, strlen(passphrase)); free(passphrase); } /* Success! */ return (0); err3: fclose(keyfile); err2: unlink(keyfilename); err1: insecure_memzero(C->passwd, strlen(C->passwd)); free(C->passwd); if (passphrase != NULL) { insecure_memzero(passphrase, strlen(passphrase)); free(passphrase); } err0: /* Failure! */ return (-1); }