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 @@ ...@@ -31,6 +31,8 @@
#include <openssl/hmac.h> #include <openssl/hmac.h>
#include <openssl/err.h> #include <openssl/err.h>
#include <string.h>
#include "rfc8188.h" #include "rfc8188.h"
#ifndef VDEF_H_INCLUDED #ifndef VDEF_H_INCLUDED
...@@ -60,6 +62,26 @@ mk_error(char *buf) ...@@ -60,6 +62,26 @@ mk_error(char *buf)
ERR_error_string(ERR_get_error(), NULL)); 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 */ /* ch 2.2 pseudorandom key */
int int
derive_prk(uint8_t *salt, uint8_t *key, unsigned char *prk, char *errmsg) 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, ...@@ -230,3 +252,62 @@ decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
assert(end >= plaintext); assert(end >= plaintext);
return (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) ...@@ -76,6 +76,15 @@ decode_header(uint8_t *hdr, uint32_t *rs, uint8_t *idlen)
*idlen = hdr[IDLEN_OFF]; *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 * ch. 2.2 pseudorandom key
* Inputs are salt and key, output is prk. prk is re-used for both * 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) ...@@ -137,7 +146,7 @@ cipher_ctx_fini(EVP_CIPHER_CTX *ctx)
* nonce results from XOR-ing the prenonce with the record sequence * nonce results from XOR-ing the prenonce with the record sequence
* number. * 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 * On return, *last is non-zero if this was the last record of the
* message, 0 otherwise. * message, 0 otherwise.
...@@ -153,9 +162,11 @@ ssize_t decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext, ...@@ -153,9 +162,11 @@ ssize_t decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
* Encrypt a record. Inputs are: * Encrypt a record. Inputs are:
* plaintext, plaintext_len, rs, cek, nonce, last * plaintext, plaintext_len, rs, cek, nonce, last
* Outputs is: * 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. * 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, ...@@ -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, ssize_t encrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *plaintext,
int plaintext_len, uint32_t rs, uint8_t cek[AES128_KEYLEN], int plaintext_len, uint32_t rs, uint8_t cek[AES128_KEYLEN],
unsigned char nonce[NONCE_LEN], int last, unsigned char *ciphertext, 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 ...@@ -84,11 +84,12 @@ int
main(int argc, char *argv[]) main(int argc, char *argv[])
{ {
EVP_CIPHER_CTX *ctx; 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], unsigned char key1[AES128_KEYLEN + 2], key2[AES128_KEYLEN + 2],
salt[SALT_LEN + 2], prk[SHA256_LEN], cek[SHA256_LEN], salt[SALT_LEN + 2], prk[SHA256_LEN], cek[SHA256_LEN],
nonce[SHA256_LEN], seq[NONCE_LEN]; nonce[SHA256_LEN], seq[NONCE_LEN], tag[TAG_LEN],
unsigned char body1[54], body2[75], plaintext[64], *ciphertext; body1[54], body2[75], plaintext[64], *ciphertext;
char errmsg[ERRMSG_LEN]; char errmsg[ERRMSG_LEN];
uint32_t rs; uint32_t rs;
uint8_t idlen; uint8_t idlen;
...@@ -105,7 +106,7 @@ main(int argc, char *argv[]) ...@@ -105,7 +106,7 @@ main(int argc, char *argv[])
assert(len == SALT_LEN + 2); assert(len == SALT_LEN + 2);
if (derive_prk(salt, key1, prk, errmsg) != 0) { 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); exit(-1);
} }
...@@ -114,7 +115,7 @@ main(int argc, char *argv[]) ...@@ -114,7 +115,7 @@ main(int argc, char *argv[])
AZ(memcmp(prk_b64, prk1_b64, len)); AZ(memcmp(prk_b64, prk1_b64, len));
if (derive_cek(prk, cek, errmsg) != 0) { if (derive_cek(prk, cek, errmsg) != 0) {
fprintf(stderr, "ex1 CEK: %s\n", errmsg); fprintf(stderr, "ex1 decrypt CEK: %s\n", errmsg);
exit(-1); exit(-1);
} }
...@@ -124,7 +125,7 @@ main(int argc, char *argv[]) ...@@ -124,7 +125,7 @@ main(int argc, char *argv[])
memset(seq, 0, NONCE_LEN); memset(seq, 0, NONCE_LEN);
if (derive_prenonce(prk, nonce, errmsg) != 0) { if (derive_prenonce(prk, nonce, errmsg) != 0) {
fprintf(stderr, "ex1 NONCE: %s\n", errmsg); fprintf(stderr, "ex1 decrypt NONCE: %s\n", errmsg);
exit(-1); exit(-1);
} }
...@@ -142,7 +143,7 @@ main(int argc, char *argv[]) ...@@ -142,7 +143,7 @@ main(int argc, char *argv[])
assert(idlen == exp_idlen1); assert(idlen == exp_idlen1);
if ((ctx = cipher_ctx_init(0, errmsg)) == NULL) { 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); exit(-1);
} }
...@@ -217,6 +218,68 @@ main(int argc, char *argv[]) ...@@ -217,6 +218,68 @@ main(int argc, char *argv[])
plaintext_len += len; plaintext_len += len;
AZ(memcmp(plaintext, exp_plaintext, 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); cipher_ctx_fini(ctx);
exit(0); 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