Commit 68bdc698 authored by Geoff Simmons's avatar Geoff Simmons

Add an implementation of encrypt_record().

Test passes for the example in RFC8188 ch 3.1.
parent 7b322013
......@@ -31,6 +31,8 @@
#include <openssl/hmac.h>
#include <openssl/err.h>
#include <string.h>
#include "rfc8188.h"
#ifndef VDEF_H_INCLUDED
......@@ -60,6 +62,26 @@ mk_error(char *buf)
ERR_error_string(ERR_get_error(), NULL));
}
/* copied from vend.h */
static inline void
be32enc(void *pp, uint32_t u)
{
uint8_t *p = (uint8_t *)pp;
p[0] = (u >> 24) & 0xff;
p[1] = (u >> 16) & 0xff;
p[2] = (u >> 8) & 0xff;
p[3] = u & 0xff;
}
void
encode_header(uint8_t *hdr, uint32_t rs, uint8_t idlen, uint8_t *keyid)
{
be32enc(hdr + RS_OFF, rs);
hdr[IDLEN_OFF] = idlen;
memcpy(hdr + ID_OFF, keyid, idlen);
}
/* ch 2.2 pseudorandom key */
int
derive_prk(uint8_t *salt, uint8_t *key, unsigned char *prk, char *errmsg)
......@@ -230,3 +252,62 @@ decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
assert(end >= plaintext);
return (end - plaintext);
}
ssize_t
encrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *plaintext,
int plaintext_len, uint32_t rs, uint8_t cek[AES128_KEYLEN],
unsigned char nonce[NONCE_LEN], int last, unsigned char *ciphertext,
uint8_t *tag, char errmsg[ERRMSG_LEN])
{
int delim_idx, len, ciphertext_len;
AN(ctx);
AN(plaintext);
AN(cek);
AN(nonce);
AN(ciphertext);
AN(errmsg);
assert(plaintext_len >= 0);
assert(rs >= 18);
assert((unsigned)plaintext_len <= rs - (TAG_LEN + 1));
delim_idx = rs - (TAG_LEN + 1);
if (!last)
plaintext[delim_idx] = '\1';
else
plaintext[delim_idx] = '\2';
for (int i = delim_idx - 1; i > plaintext_len; i--)
plaintext[i] = '\0';
if (EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, cek, nonce, -1)
!= 1) {
mk_error(errmsg);
return (-1);
}
if (EVP_CipherUpdate(ctx, NULL, &len, aad, aad_len) != 1) {
mk_error(errmsg);
return (-1);
}
if (EVP_CipherUpdate(ctx, ciphertext, &len, plaintext, rs - TAG_LEN)
!= 1) {
mk_error(errmsg);
return (-1);
}
assert(rs - TAG_LEN == (unsigned)len);
ciphertext_len = len;
if ((EVP_CipherFinal_ex(ctx, ciphertext + ciphertext_len, &len)) != 1) {
mk_error(errmsg);
return (-1);
}
ciphertext_len += len;
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_GET_TAG, TAG_LEN, tag)) {
mk_error(errmsg);
return (-1);
}
return (ciphertext_len);
}
......@@ -76,6 +76,15 @@ decode_header(uint8_t *hdr, uint32_t *rs, uint8_t *idlen)
*idlen = hdr[IDLEN_OFF];
}
/*
* Output is hdr, inputs are rs, idlen and keyid. Salt is added
* separately.
*/
void encode_header(uint8_t *hdr, uint32_t rs, uint8_t idlen, uint8_t *keyid);
/* XXX implement me */
int add_salt(unsigned char *hdr);
/*
* ch. 2.2 pseudorandom key
* Inputs are salt and key, output is prk. prk is re-used for both
......@@ -137,7 +146,7 @@ cipher_ctx_fini(EVP_CIPHER_CTX *ctx)
* nonce results from XOR-ing the prenonce with the record sequence
* number.
*
* At least rs - 16 bytes must be allocated for the buffer at plaintext.
* At least rs - TAG_LEN bytes must be allocated for the buffer at plaintext.
*
* On return, *last is non-zero if this was the last record of the
* message, 0 otherwise.
......@@ -153,9 +162,11 @@ ssize_t decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
* Encrypt a record. Inputs are:
* plaintext, plaintext_len, rs, cek, nonce, last
* Outputs is:
* ciphertext
* ciphertext, tag
*
* The buffer at plaintext MUST have at least rs bytes allocated.
*
* plaintext_len MAY NOT be > rs - 17.
* plaintext_len MAY NOT be > rs - (TAG_LEN + 1).
*
* If last is non-zero, then this is the last record in the message.
*
......@@ -168,4 +179,4 @@ ssize_t decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
ssize_t encrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *plaintext,
int plaintext_len, uint32_t rs, uint8_t cek[AES128_KEYLEN],
unsigned char nonce[NONCE_LEN], int last, unsigned char *ciphertext,
char errmsg[ERRMSG_LEN]);
uint8_t tag[TAG_LEN], char errmsg[ERRMSG_LEN]);
......@@ -84,11 +84,12 @@ int
main(int argc, char *argv[])
{
EVP_CIPHER_CTX *ctx;
unsigned char prk_b64[45], cek_b64[25], nonce_b64[17];
unsigned char prk_b64[45], cek_b64[25], nonce_b64[17],
body1_test_b64[73];
unsigned char key1[AES128_KEYLEN + 2], key2[AES128_KEYLEN + 2],
salt[SALT_LEN + 2], prk[SHA256_LEN], cek[SHA256_LEN],
nonce[SHA256_LEN], seq[NONCE_LEN];
unsigned char body1[54], body2[75], plaintext[64], *ciphertext;
nonce[SHA256_LEN], seq[NONCE_LEN], tag[TAG_LEN],
body1[54], body2[75], plaintext[64], *ciphertext;
char errmsg[ERRMSG_LEN];
uint32_t rs;
uint8_t idlen;
......@@ -105,7 +106,7 @@ main(int argc, char *argv[])
assert(len == SALT_LEN + 2);
if (derive_prk(salt, key1, prk, errmsg) != 0) {
fprintf(stderr, "ex1 PRK: %s\n", errmsg);
fprintf(stderr, "ex1 decrypt PRK: %s\n", errmsg);
exit(-1);
}
......@@ -114,7 +115,7 @@ main(int argc, char *argv[])
AZ(memcmp(prk_b64, prk1_b64, len));
if (derive_cek(prk, cek, errmsg) != 0) {
fprintf(stderr, "ex1 CEK: %s\n", errmsg);
fprintf(stderr, "ex1 decrypt CEK: %s\n", errmsg);
exit(-1);
}
......@@ -124,7 +125,7 @@ main(int argc, char *argv[])
memset(seq, 0, NONCE_LEN);
if (derive_prenonce(prk, nonce, errmsg) != 0) {
fprintf(stderr, "ex1 NONCE: %s\n", errmsg);
fprintf(stderr, "ex1 decrypt NONCE: %s\n", errmsg);
exit(-1);
}
......@@ -142,7 +143,7 @@ main(int argc, char *argv[])
assert(idlen == exp_idlen1);
if ((ctx = cipher_ctx_init(0, errmsg)) == NULL) {
fprintf(stderr, "ex1: cipher_ctx_init: %s\n", errmsg);
fprintf(stderr, "ex1 decrypt: cipher_ctx_init: %s\n", errmsg);
exit(-1);
}
......@@ -217,6 +218,68 @@ main(int argc, char *argv[])
plaintext_len += len;
AZ(memcmp(plaintext, exp_plaintext, len));
cipher_ctx_fini(ctx);
/* example 1, ch 3.1, encryption */
memset(plaintext, 0xff, 64);
memcpy(plaintext, exp_plaintext, exp_plaintext_len);
/* Ordinarily call add_salt() to get random salt. */
memcpy(body1, salt, SALT_LEN);
encode_header(body1, exp_rs1, exp_idlen1, (unsigned char *)"");
if (derive_prk(body1, key1, prk, errmsg) != 0) {
fprintf(stderr, "ex1 encrypt PRK: %s\n", errmsg);
exit(-1);
}
len = EVP_EncodeBlock(prk_b64, prk, SHA256_LEN);
assert(len == 44);
AZ(memcmp(prk_b64, prk1_b64, len));
if (derive_cek(prk, cek, errmsg) != 0) {
fprintf(stderr, "ex1 encrypt CEK: %s\n", errmsg);
exit(-1);
}
len = EVP_EncodeBlock(cek_b64, cek, AES128_KEYLEN);
assert(len == 24);
AZ(memcmp(cek_b64, cek1_b64, len));
seq[NONCE_LEN - 1] = 0; // simulates reset to 0
if (derive_prenonce(prk, nonce, errmsg) != 0) {
fprintf(stderr, "ex1 encrypt NONCE: %s\n", errmsg);
exit(-1);
}
len = EVP_EncodeBlock(nonce_b64, nonce, NONCE_LEN);
assert(len == 16);
AZ(memcmp(nonce_b64, nonce1_b64, len));
if ((ctx = cipher_ctx_init(1, errmsg)) == NULL) {
fprintf(stderr, "ex1 encrypt: cipher_ctx_init: %s\n", errmsg);
exit(-1);
}
/* Using a shorter record size */
rs = 32;
assert(rs + HDR_PREFIX_LEN + exp_idlen1 == bodylen1);
last = 1;
ciphertext = body1 + HDR_PREFIX_LEN + exp_idlen1;
len = encrypt_record(ctx, plaintext, exp_plaintext_len, rs, cek,
nonce, last, ciphertext, tag, errmsg);
if (len < 0) {
fprintf(stderr, "ex1 encrypt_record: %s\n", errmsg);
exit(-1);
}
assert((unsigned)len == rs - TAG_LEN);
memcpy(ciphertext + (rs - TAG_LEN), tag, TAG_LEN);
len = EVP_EncodeBlock(body1_test_b64, body1, bodylen1);
assert(len == 72);
AZ(memcmp(body1_test_b64, body1_b64, len));
cipher_ctx_fini(ctx);
exit(0);
}
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment