/* Initializes the nonce level random generator. * * the @new_key must be provided. * * @init must be non zero on first initialization, and * zero on any subsequent reinitializations. */ static int single_prng_init(struct prng_ctx_st *ctx, uint8_t new_key[PRNG_KEY_SIZE], unsigned new_key_size, unsigned init) { uint8_t nonce[CHACHA_NONCE_SIZE]; memset(nonce, 0, sizeof(nonce)); /* to prevent valgrind from whinning */ if (init == 0) { /* use the previous key to generate IV as well */ chacha_crypt(&ctx->ctx, sizeof(nonce), nonce, nonce); /* Add key continuity by XORing the new key with data generated * from the old key */ chacha_crypt(&ctx->ctx, new_key_size, new_key, new_key); } else { struct timespec now; /* current time */ ctx->forkid = _gnutls_get_forkid(); gettime(&now); memcpy(nonce, &now, MIN(sizeof(nonce), sizeof(now))); ctx->last_reseed = now.tv_sec; } chacha_set_key(&ctx->ctx, new_key); chacha_set_nonce(&ctx->ctx, nonce); zeroize_key(new_key, new_key_size); ctx->counter = 0; return 0; }
/** Generate a stream of random bytes via ChaCha @param st The ChaCha20 state @param out [out] The output buffer @param outlen The output length @return CRYPT_OK on success */ int chacha_keystream(chacha_state *st, unsigned char *out, unsigned long outlen) { if (outlen == 0) return CRYPT_OK; /* nothing to do */ LTC_ARGCHK(out != NULL); XMEMSET(out, 0, outlen); return chacha_crypt(st, out, outlen, out); }
int chacha_test(void) { #ifndef LTC_TEST return CRYPT_NOP; #else unsigned long len; unsigned char out[1000]; /* https://tools.ietf.org/html/rfc7539#section-2.4.2 */ unsigned char k[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f }; unsigned char n[] = { 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x4a, 0x00, 0x00, 0x00, 0x00 }; unsigned char ct[] = { 0x6E, 0x2E, 0x35, 0x9A, 0x25, 0x68, 0xF9, 0x80, 0x41, 0xBA, 0x07, 0x28, 0xDD, 0x0D, 0x69, 0x81, 0xE9, 0x7E, 0x7A, 0xEC, 0x1D, 0x43, 0x60, 0xC2, 0x0A, 0x27, 0xAF, 0xCC, 0xFD, 0x9F, 0xAE, 0x0B, 0xF9, 0x1B, 0x65, 0xC5, 0x52, 0x47, 0x33, 0xAB, 0x8F, 0x59, 0x3D, 0xAB, 0xCD, 0x62, 0xB3, 0x57, 0x16, 0x39, 0xD6, 0x24, 0xE6, 0x51, 0x52, 0xAB, 0x8F, 0x53, 0x0C, 0x35, 0x9F, 0x08, 0x61, 0xD8, 0x07, 0xCA, 0x0D, 0xBF, 0x50, 0x0D, 0x6A, 0x61, 0x56, 0xA3, 0x8E, 0x08, 0x8A, 0x22, 0xB6, 0x5E, 0x52, 0xBC, 0x51, 0x4D, 0x16, 0xCC, 0xF8, 0x06, 0x81, 0x8C, 0xE9, 0x1A, 0xB7, 0x79, 0x37, 0x36, 0x5A, 0xF9, 0x0B, 0xBF, 0x74, 0xA3, 0x5B, 0xE6, 0xB4, 0x0B, 0x8E, 0xED, 0xF2, 0x78, 0x5E, 0x42, 0x87, 0x4D }; char pt[] = "Ladies and Gentlemen of the class of '99: If I could offer you only one tip for the future, sunscreen would be it."; chacha_state st; int err; len = strlen(pt); /* crypt piece by piece - using chacha_ivctr32() */ if ((err = chacha_setup(&st, k, sizeof(k), 20)) != CRYPT_OK) return err; if ((err = chacha_ivctr32(&st, n, sizeof(n), 1)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt, 35, out )) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt + 35, 35, out + 35)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt + 70, 5, out + 70)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt + 75, 5, out + 75)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt + 80, len - 80, out + 80)) != CRYPT_OK) return err; if (compare_testvector(out, len, ct, sizeof(ct), "CHACHA-TV1", 1)) return CRYPT_FAIL_TESTVECTOR; /* crypt in one go - using chacha_ivctr32() */ if ((err = chacha_setup(&st, k, sizeof(k), 20)) != CRYPT_OK) return err; if ((err = chacha_ivctr32(&st, n, sizeof(n), 1)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt, len, out)) != CRYPT_OK) return err; if (compare_testvector(out, len, ct, sizeof(ct), "CHACHA-TV2", 1)) return CRYPT_FAIL_TESTVECTOR; /* crypt in one go - using chacha_ivctr64() */ if ((err = chacha_setup(&st, k, sizeof(k), 20)) != CRYPT_OK) return err; if ((err = chacha_ivctr64(&st, n + 4, sizeof(n) - 4, 1)) != CRYPT_OK) return err; if ((err = chacha_crypt(&st, (unsigned char*)pt, len, out)) != CRYPT_OK) return err; if (compare_testvector(out, len, ct, sizeof(ct), "CHACHA-TV3", 1)) return CRYPT_FAIL_TESTVECTOR; /* crypt in a single call using 32-bit counter with a value of 1 */ if ((err = chacha_memory(k, sizeof(k), 20, n, sizeof(n), 1, (unsigned char*)pt, len, out)) != CRYPT_OK) return err; if (compare_testvector(out, len, ct, sizeof(ct), "CHACHA-TV4", 1)) return CRYPT_FAIL_TESTVECTOR; /* crypt in a single call using 64-bit counter with a value of 1 */ if ((err = chacha_memory(k, sizeof(k), 20, n + 4, sizeof(n) - 4, 1, (unsigned char*)pt, len, out)) != CRYPT_OK) return err; if (compare_testvector(out, len, ct, sizeof(ct), "CHACHA-TV5", 1)) return CRYPT_FAIL_TESTVECTOR; return CRYPT_OK; #endif }
void chacha_poly1305_decrypt (struct chacha_poly1305_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) { if (!length) return; assert (ctx->data_size % CHACHA_POLY1305_BLOCK_SIZE == 0); poly1305_pad (ctx); poly1305_update (ctx, length, src); chacha_crypt (&ctx->chacha, length, dst, src); ctx->data_size += length; }
void test_chacha(const uint8_t *key, const uint8_t *iv, uint8_t *expected, uint8_t keylength, uint8_t rounds) { uint8_t cipher_data[64] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; uint8_t cipher_result[64] = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; struct chacha_ctx cipher_ctx; uint8_t errors; chacha_set_key(&cipher_ctx, keylength, key); chacha_set_iv(&cipher_ctx, iv); chacha_crypt(&cipher_ctx, 64, rounds, &cipher_result[0], &cipher_data[0]); if (DEBUG) { printf("Result after encryption:\n"); print_block(cipher_result); } errors = 0; for (uint8_t i = 0 ; i < 64 ; i++) { if (cipher_result[i] != expected[i]) { errors++; } } if (errors > 0) { printf("Error, expected:\n"); print_block(&expected[0]); printf("Got:\n"); print_block(cipher_result); } else { printf("Success, result matched expected.\n"); } }
void chacha_poly1305_encrypt (struct chacha_poly1305_ctx *ctx, size_t length, uint8_t *dst, const uint8_t *src) { if (!length) return; assert (ctx->data_size % CHACHA_POLY1305_BLOCK_SIZE == 0); if (!ctx->data_size) { uint8_t buf[8]; LE_WRITE_UINT64 (buf, ctx->auth_size); poly1305_update (ctx, sizeof(buf), buf); } chacha_crypt (&ctx->chacha, length, dst, src); poly1305_update (ctx, length, dst); ctx->data_size += length; }
/** Encrypt bytes of ciphertext with ChaCha20Poly1305 @param st The ChaCha20Poly1305 state @param in The plaintext @param inlen The length of the input (octets) @param out [out] The ciphertext (length inlen) @return CRYPT_OK if successful */ int chacha20poly1305_encrypt(chacha20poly1305_state *st, const unsigned char *in, unsigned long inlen, unsigned char *out) { unsigned char padzero[16] = { 0 }; unsigned long padlen; int err; if (inlen == 0) return CRYPT_OK; /* nothing to do */ LTC_ARGCHK(st != NULL); if ((err = chacha_crypt(&st->chacha, in, inlen, out)) != CRYPT_OK) return err; if (st->aadflg) { padlen = 16 - (unsigned long)(st->aadlen % 16); if (padlen < 16) { if ((err = poly1305_process(&st->poly, padzero, padlen)) != CRYPT_OK) return err; } st->aadflg = 0; /* no more AAD */ } if ((err = poly1305_process(&st->poly, out, inlen)) != CRYPT_OK) return err; st->ctlen += (ulong64)inlen; return CRYPT_OK; }
static int wrap_nettle_rnd(void *_ctx, int level, void *data, size_t datasize) { struct generators_ctx_st *ctx = _ctx; struct prng_ctx_st *prng_ctx; int ret, reseed = 0; uint8_t new_key[PRNG_KEY_SIZE]; time_t now; if (level == GNUTLS_RND_RANDOM || level == GNUTLS_RND_KEY) prng_ctx = &ctx->normal; else if (level == GNUTLS_RND_NONCE) prng_ctx = &ctx->nonce; else return gnutls_assert_val(GNUTLS_E_RANDOM_FAILED); /* Two reasons for this memset(): * 1. avoid getting filled with valgrind warnings * 2. avoid a cipher/PRNG failure to expose stack data */ memset(data, 0, datasize); now = gnutls_time(0); /* We re-seed based on time in addition to output data. That is, * to prevent a temporal state compromise to become permanent for low * traffic sites */ if (unlikely(_gnutls_detect_fork(prng_ctx->forkid))) { reseed = 1; } else { if (now > prng_ctx->last_reseed + prng_reseed_time[level]) reseed = 1; } if (reseed != 0 || prng_ctx->counter > prng_reseed_limits[level]) { if (level == GNUTLS_RND_NONCE) { ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key)); } else { /* we also use the system entropy to reduce the impact * of a temporal state compromise for these two levels. */ ret = _rnd_get_system_entropy(new_key, sizeof(new_key)); } if (ret < 0) { gnutls_assert(); goto cleanup; } ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0); if (ret < 0) { gnutls_assert(); goto cleanup; } prng_ctx->last_reseed = now; prng_ctx->forkid = _gnutls_get_forkid(); } chacha_crypt(&prng_ctx->ctx, datasize, data, data); prng_ctx->counter += datasize; if (level == GNUTLS_RND_KEY) { /* prevent backtracking */ ret = wrap_nettle_rnd(_ctx, GNUTLS_RND_RANDOM, new_key, sizeof(new_key)); if (ret < 0) { gnutls_assert(); goto cleanup; } ret = single_prng_init(prng_ctx, new_key, sizeof(new_key), 0); if (ret < 0) { gnutls_assert(); goto cleanup; } } ret = 0; cleanup: return ret; }
static void test_chacha_stream(const struct tstring *key, const struct tstring *iv, const struct tstring *ciphertext, const struct tstring *xor_ref) { struct chacha_ctx ctx; uint8_t data[STREAM_LENGTH + 1]; uint8_t stream[STREAM_LENGTH + 1]; uint8_t xor[CHACHA_BLOCK_SIZE]; size_t j; ASSERT (iv->length == CHACHA_IV_SIZE); ASSERT (ciphertext->length == 4*CHACHA_BLOCK_SIZE); ASSERT (xor_ref->length == CHACHA_BLOCK_SIZE); chacha_set_key(&ctx, key->length, key->data); chacha_set_iv(&ctx, iv->data); memset(stream, 0, STREAM_LENGTH + 1); chacha_crypt(&ctx, STREAM_LENGTH, stream, stream); if (stream[STREAM_LENGTH]) { fprintf(stderr, "Stream of %d bytes wrote too much!\n", STREAM_LENGTH); FAIL(); } if (!MEMEQ (64, stream, ciphertext->data)) { fprintf(stderr, "Error failed, offset 0:\n"); fprintf(stderr, "\nOutput: "); print_hex(64, stream); fprintf(stderr, "\nExpected:"); print_hex(64, ciphertext->data); fprintf(stderr, "\n"); FAIL(); } if (!MEMEQ (128, stream + 192, ciphertext->data + 64)) { fprintf(stderr, "Error failed, offset 192:\n"); fprintf(stderr, "\nOutput: "); print_hex(128, stream + 192); fprintf(stderr, "\nExpected:"); print_hex(64, ciphertext->data + 64); fprintf(stderr, "\n"); FAIL(); } if (!MEMEQ (64, stream + 448, ciphertext->data + 192)) { fprintf(stderr, "Error failed, offset 448:\n"); fprintf(stderr, "\nOutput: "); print_hex(64, stream + 448); fprintf(stderr, "\nExpected:"); print_hex(64, ciphertext->data + 192); fprintf(stderr, "\n"); FAIL(); } memxor3 (xor, stream, stream + CHACHA_BLOCK_SIZE, CHACHA_BLOCK_SIZE); for (j = 2*CHACHA_BLOCK_SIZE; j < STREAM_LENGTH; j += CHACHA_BLOCK_SIZE) memxor (xor, stream + j, CHACHA_BLOCK_SIZE); if (!MEMEQ (CHACHA_BLOCK_SIZE, xor, xor_ref->data)) { fprintf(stderr, "Error failed, bad xor 448:\n"); fprintf(stderr, "\nOutput: "); print_hex(CHACHA_BLOCK_SIZE, xor); fprintf(stderr, "\nExpected:"); print_hex(CHACHA_BLOCK_SIZE, xor_ref->data); fprintf(stderr, "\n"); FAIL(); } for (j = 1; j <= STREAM_LENGTH; j++) { memset(data, 0, STREAM_LENGTH + 1); chacha_set_iv(&ctx, iv->data); chacha_crypt(&ctx, j, data, data); if (!MEMEQ(j, data, stream)) { fprintf(stderr, "Encrypt failed for length %lu:\n", (unsigned long) j); fprintf(stderr, "\nOutput: "); print_hex(j, data); fprintf(stderr, "\nExpected:"); print_hex(j, stream); fprintf(stderr, "\n"); FAIL(); } if (!memzero_p (data + j, STREAM_LENGTH + 1 - j)) { fprintf(stderr, "Encrypt failed for length %lu, wrote too much:\n", (unsigned long) j); fprintf(stderr, "\nOutput: "); print_hex(STREAM_LENGTH + 1 - j, data + j); fprintf(stderr, "\n"); FAIL(); } } }