Commit 23a3dae7 authored by Geoff Simmons's avatar Geoff Simmons

Initial commit

Initially no VMOD, VFP or the like is defined, but code applying
libcrypto to implement RFC8188 is committed, as well as a test
program that checks the results against the two examples in
chapters 3.1 and 3.2 of the RFC.

The 'make' target currently doesn't do anything, but 'make check'
runs the test.
parents
Makefile
Makefile.in
.deps/
.libs/
*.o
*.lo
*.la
*~
*.[1-9]
.dirstamp
/aclocal.m4
/ar-lib
/autom4te.cache/
/compile
/config.guess
/config.h
/config.h.in
/config.log
/config.status
/config.sub
/configure
/depcomp
/install-sh
/libtool
/ltmain.sh
/missing
/stamp-h1
/test-driver
/m4/
/src/vcc_if.c
/src/vcc_if.h
/src/vmod_*rst
/src/rfc8188_test*
/src/VSC_ece.*
/src/tests/*.log
/src/tests/*.trs
/src/test-suite.log
ACLOCAL_AMFLAGS = -I m4 -I ${VARNISHAPI_DATAROOTDIR}/aclocal
SUBDIRS = src
DISTCHECK_CONFIGURE_FLAGS = \
VMOD_DIR='$${libdir}/varnish/vmods'
#!/bin/sh
warn() {
echo "WARNING: $@" 1>&2
}
case `uname -s` in
Darwin)
LIBTOOLIZE=glibtoolize
;;
FreeBSD)
LIBTOOLIZE=libtoolize
;;
Linux)
LIBTOOLIZE=libtoolize
;;
SunOS)
LIBTOOLIZE=libtoolize
;;
*)
warn "unrecognized platform:" `uname -s`
LIBTOOLIZE=libtoolize
esac
automake_version=`automake --version | tr ' ' '\n' | egrep '^[0-9]\.[0-9a-z.-]+'`
if [ -z "$automake_version" ] ; then
warn "unable to determine automake version"
else
case $automake_version in
0.*|1.[0-8]|1.[0-8][.-]*)
warn "automake ($automake_version) detected; 1.9 or newer recommended"
;;
*)
;;
esac
fi
# check for varnishapi.m4 in custom paths
dataroot=$(pkg-config --variable=datarootdir varnishapi 2>/dev/null)
if [ -z "$dataroot" ] ; then
cat >&2 <<'EOF'
Package varnishapi was not found in the pkg-config search path.
Perhaps you should add the directory containing `varnishapi.pc'
to the PKG_CONFIG_PATH environment variable
EOF
exit 1
fi
set -ex
aclocal -I m4 -I ${dataroot}/aclocal
$LIBTOOLIZE --copy --force
autoheader
automake --add-missing --copy --foreign
autoconf
AC_PREREQ(2.68)
AC_COPYRIGHT([Copyright (c) 2019 UPLEX - Nils Goroll Systemoptimierung])
AC_INIT([libvmod-ece], [trunk], [varnish-support@uplex.de], [vmod-ece])
AC_CONFIG_MACRO_DIR([m4])
AC_CONFIG_SRCDIR(src/vmod_ece.vcc)
AM_CONFIG_HEADER(config.h)
AC_CANONICAL_SYSTEM
AC_LANG(C)
AM_INIT_AUTOMAKE([1.12 -Wall -Werror foreign parallel-tests])
AM_SILENT_RULES([yes])
AM_PROG_AR
LT_PREREQ([2.2.6])
LT_INIT([dlopen disable-static])
AC_PROG_CC
AC_PROG_CC_STDC
if test "x$ac_cv_prog_cc_c99" = xno; then
AC_MSG_ERROR([Could not find a C99 compatible compiler])
fi
AC_PROG_CPP
AX_PTHREAD(,[AC_MSG_ERROR([Could not configure pthreads support])])
LIBS="$PTHREAD_LIBS $LIBS"
CFLAGS="$CFLAGS $PTHREAD_CFLAGS"
CC="$PTHREAD_CC"
AC_ARG_WITH([rst2man],
AS_HELP_STRING(
[--with-rst2man=PATH],
[Location of rst2man (auto)]),
[RST2MAN="$withval"],
AC_CHECK_PROGS(RST2MAN, [rst2man rst2man.py], []))
AM_CONDITIONAL(HAVE_RST2MAN, [test "x$RST2MAN" != "xno"])
m4_ifndef([VARNISH_PREREQ], AC_MSG_ERROR([Need varnish.m4 -- see README.rst]))
PKG_CHECK_MODULES([CRYPTO], [libcrypto])
VARNISH_PREREQ([trunk])
VARNISH_VMODS([ece])
VMOD_TESTS="$(cd $srcdir/src && echo tests/*.vtc)"
AC_SUBST(VMOD_TESTS)
PKG_CHECK_VAR([LIBVARNISHAPI_LIBDIR], [varnishapi], [libdir])
AC_SUBST([VARNISH_LIBRARY_PATH],
[$LIBVARNISHAPI_LIBDIR:$LIBVARNISHAPI_LIBDIR/varnish])
# Checks for C sources
# --enable-stack-protector
AC_ARG_ENABLE(stack-protector,
AS_HELP_STRING([--enable-stack-protector],[enable stack protector (default is YES)]),
[],
[enable_stack_protector=yes])
if test "x$enable_stack_protector" != "xno"; then
AX_CHECK_COMPILE_FLAG([-fstack-protector],
AX_CHECK_LINK_FLAG([-fstack-protector],
[CFLAGS="${CFLAGS} -fstack-protector"], [], []),
[], [])
fi
# --enable-debugging
AC_ARG_ENABLE(debugging,
AS_HELP_STRING([--enable-debugging],[enable debugging (default is NO)]),
[],
[enable_debugging=no])
# AC_PROG_CC sets CFLAGS to '-g -O2' unless already set, so there's no
# need to add -g. Disable or change by explicitly setting CFLAGS. If
# this option is enabled, then -Og or -O0 becomes the last
# optimization option, and hence takes precedence.
if test "x$enable_debugging" != "xno"; then
CFLAGS="${CFLAGS} -fno-inline"
AX_CHECK_COMPILE_FLAG([-Og],
[CFLAGS="${CFLAGS} -O0 -Og"],
[CFLAGS="${CFLAGS} -O0"],
[])
fi
AC_CONFIG_FILES([
Makefile
src/Makefile
])
AC_OUTPUT
((nil . ((indent-tabs-mode . t)))
(c-mode . ((c-file-style . "BSD"))))
AUTOMAKE_OPTIONS = subdir-objects
AM_CFLAGS = $(VARNISHAPI_CFLAGS) @CRYPTO_CFLAGS@ -Wall -Werror -Wextra -std=c99
AM_LDFLAGS = $(VARNISHAPI_LIBS) @CRYPTO_LIBS@ -ldl
EXTRA_DIST = \
vmod_ece.vcc
TESTS = rfc8188_test
check_PROGRAMS = rfc8188_test
rfc8188_test_SOURCES = rfc8188_test.c rfc8188.c rfc8188.h
/*-
* Copyright (c) 2019 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Chapter references are to RFC 8188 */
#include <openssl/hmac.h>
#include <openssl/err.h>
#include "rfc8188.h"
#ifndef VDEF_H_INCLUDED
# include "vdef.h"
#endif
#include "vas.h"
/* ch 2.2 */
static const unsigned char cek_info[] = "Content-Encoding: aes128gcm\0\1";
static const int cek_info_len = sizeof(cek_info) - 1;
/* ch 2.3 */
static const unsigned char nonce_info[] = "Content-Encoding: nonce\0\1";
static const int nonce_info_len = sizeof(nonce_info) - 1;
/*
* ch 2: "The additional data passed to each invocation of
* AEAD_AES_128_GCM is a zero-length octet sequence."
*/
static const unsigned char *aad = (const void *)&aad;
static const int aad_len = 0;
static inline void
mk_error(char *buf)
{
snprintf(buf, ERRMSG_LEN, "%s",
ERR_error_string(ERR_get_error(), NULL));
}
/* ch 2.2 pseudorandom key */
int
derive_prk(uint8_t *salt, uint8_t *key, unsigned char *prk, char *errmsg)
{
unsigned len;
AN(salt);
AN(key);
AN(prk);
AN(errmsg);
if (HMAC(EVP_sha256(), salt, SALT_LEN, key, AES128_KEYLEN, prk, &len)
== NULL) {
mk_error(errmsg);
return (-1);
}
assert(len == SHA256_LEN);
return 0;
}
/* ch 2.2 content encryption key */
int
derive_cek(unsigned char *prk, unsigned char *cek, char *errmsg)
{
unsigned len;
AN(prk);
AN(cek);
AN(errmsg);
if (HMAC(EVP_sha256(), prk, SHA256_LEN, cek_info, cek_info_len, cek,
&len) == NULL) {
mk_error(errmsg);
return (-1);
}
assert(len == SHA256_LEN);
return 0;
}
/* ch 2.3 prenonce to be XOR'd with each record sequence number */
int
derive_prenonce(unsigned char *prk, unsigned char *prenonce, char *errmsg)
{
unsigned len;
if (HMAC(EVP_sha256(), prk, SHA256_LEN, nonce_info, nonce_info_len,
prenonce, &len) == NULL) {
mk_error(errmsg);
return (-1);
}
assert(len == SHA256_LEN);
return 0;
}
static EVP_CIPHER_CTX *
cipher_ctx_params(EVP_CIPHER_CTX *ctx, int enc, char *errmsg)
{
AN(ctx);
AN(errmsg);
(void)EVP_CIPHER_CTX_set_padding(ctx, 0);
if (EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, NULL, NULL, enc)
!= 1) {
mk_error(errmsg);
return (NULL);
}
return ctx;
}
EVP_CIPHER_CTX *
cipher_ctx_init(int enc, char *errmsg)
{
EVP_CIPHER_CTX *ctx;
AN(errmsg);
if ((ctx = EVP_CIPHER_CTX_new()) == NULL) {
mk_error(errmsg);
return (NULL);
}
return cipher_ctx_params(ctx, enc, errmsg);
}
EVP_CIPHER_CTX *
cipher_ctx_reset(EVP_CIPHER_CTX *ctx, int enc, char *errmsg)
{
AN(ctx);
AN(errmsg);
if (EVP_CIPHER_CTX_reset(ctx) != 1) {
mk_error(errmsg);
return (NULL);
}
return cipher_ctx_params(ctx, enc, errmsg);
}
ssize_t
decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
int ciphertext_len, unsigned char *tag, uint8_t *cek, unsigned char *nonce,
unsigned char *plaintext, int *last, char *errmsg)
{
int len, plaintext_len;
unsigned char *end = NULL;
AN(ctx);
AN(ciphertext);
AN(cek);
AN(nonce);
AN(plaintext);
AN(last);
AN(errmsg);
if (EVP_CipherInit_ex(ctx, EVP_aes_128_gcm(), NULL, cek, nonce, 0)
!= 1) {
mk_error(errmsg);
return (-1);
}
if (!EVP_CIPHER_CTX_ctrl(ctx, EVP_CTRL_GCM_SET_TAG, TAG_LEN, tag)) {
mk_error(errmsg);
return (-1);
}
if (EVP_CipherUpdate(ctx, NULL, &len, aad, aad_len) != 1) {
mk_error(errmsg);
return (-1);
}
if (EVP_CipherUpdate(ctx, plaintext, &len, ciphertext,
ciphertext_len) != 1) {
mk_error(errmsg);
return (-1);
}
assert(ciphertext_len == len);
plaintext_len = len;
if ((EVP_CipherFinal_ex(ctx, plaintext + plaintext_len, &len)) != 1) {
mk_error(errmsg);
return (-1);
}
plaintext_len += len;
*last = -1;
for (unsigned char *b = plaintext + plaintext_len - 1;
*last == -1 && b != plaintext; b--) {
switch (*b) {
case '\0':
continue;
case '\1':
case '\2':
end = b;
*last = (*b == '\2');
break;
default:
snprintf(errmsg, ERRMSG_LEN, "Ill-formed padding");
return (-1);
}
}
if (*last == -1) {
snprintf(errmsg, ERRMSG_LEN, "No delimiter found");
return (-1);
}
assert(end != NULL);
assert(end >= plaintext);
return (end - plaintext);
}
/*-
* Copyright (c) 2019 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
/* Chapter references are to RFC 8188 */
#include <stdint.h>
#include <sys/types.h>
#include <openssl/evp.h>
#define AES128_KEYLEN 16
#define SALT_LEN 16
#define SHA256_LEN 32
#define NONCE_LEN 12
#define TAG_LEN AES128_KEYLEN
/* ERR_error_string(3): "buf must be at least 256 bytes long." */
#define ERRMSG_LEN 256
/*
* Functions returning int return 0 on success, non-zero on failure.
* Every function writes an error message to the errmsg buffer on failure.
*
* Buffer parameters in many of the function declarations are expressed
* here as arrays with sizes, for documentation purposes. Those buffers
* MUST be allocated with the given size.
*/
/* copied from vend.h */
static inline uint32_t
be32dec(const void *pp)
{
uint8_t const *p = (uint8_t const *)pp;
return (((unsigned)p[0] << 24) | (p[1] << 16) | (p[2] << 8) | p[3]);
}
/*
* ch 2.1
* Input is hdr, outputs are rs and idlen. The salt is the first SALT_LEN
* bytes of hdr. The keyid is *idlen bytes long, starting at hdr[ID_OFF].
*/
#define RS_OFF 16
#define IDLEN_OFF 20
#define ID_OFF (IDLEN_OFF + 1)
static inline void
decode_header(uint8_t *hdr, uint32_t *rs, uint8_t *idlen)
{
*rs = be32dec(&hdr[RS_OFF]);
*idlen = hdr[IDLEN_OFF];
}
/*
* ch. 2.2 pseudorandom key
* Inputs are salt and key, output is prk. prk is re-used for both
* derive_cek() and derive_prenonce() below.
*/
int derive_prk(uint8_t salt[SALT_LEN], uint8_t key[AES128_KEYLEN],
unsigned char prk[SHA256_LEN], char errmsg[ERRMSG_LEN]);
/*
* ch. 2.2 content encryption key
* Input is prk, output is cek. The first AES128_KEYLEN bytes of cek
* are used as the key.
*/
int derive_cek(unsigned char prk[SHA256_LEN], unsigned char cek[SHA256_LEN],
char errmsg[ERRMSG_LEN]);
/*
* ch. 2.3, except this derives a "prenonce", which is constant throughout
* the en-/decryption of a message. The prenonce is XOR'd with the
* sequence number to derive the nonce for each record.
*
* Input is prk, output is prenonce. The first NONCE_LEN bytes of the
* per-record nonce is used to en-/crypt each record.
*/
int derive_prenonce(unsigned char prk[SHA256_LEN],
unsigned char prenonce[SHA256_LEN], char errmsg[ERRMSG_LEN]);
/*
* Returns an EVP_CIPHER_CTX that may be re-used to encrypt or decrypt the
* entire sequence of records in a message. enc is 1 for encryption, 0 for
* decryption.
*
* Returns NULL on failure.
*/
EVP_CIPHER_CTX * cipher_ctx_init(int enc, char errmsg[ERRMSG_LEN]);
/*
* Returns ctx reset for en-/decrypting a new record. ctx MUST have been
* previously initialized with cipher_ctx_init().
*
* Returns NULL on failure.
*/
EVP_CIPHER_CTX * cipher_ctx_reset(EVP_CIPHER_CTX *ctx, int enc,
char errmsg[ERRMSG_LEN]);
/* Finalize an EVP_CIPHER_CTX. */
static inline void
cipher_ctx_fini(EVP_CIPHER_CTX *ctx)
{
EVP_CIPHER_CTX_free(ctx);
}
/*
* Decrypt a record. Inputs are:
* ciphertext, ciphertext_len, tag, cek, nonce
* Outputs are:
* plaintext, last
*
* 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.
*
* On return, *last is non-zero if this was the last record of the
* message, 0 otherwise.
*
* Returns -1 on error, otherwise the number of plaintext bytes.
*/
ssize_t decrypt_record(EVP_CIPHER_CTX *ctx, unsigned char *ciphertext,
int ciphertext_len, unsigned char tag[TAG_LEN], uint8_t cek[AES128_KEYLEN],
unsigned char nonce[NONCE_LEN], unsigned char *plaintext, int *last,
char errmsg[ERRMSG_LEN]);
/*
* Encrypt a record. Inputs are:
* plaintext, plaintext_len, rs, cek, nonce, last
* Outputs is:
* ciphertext
*
* plaintext_len MAY NOT be > rs - 17.
*
* If last is non-zero, then this is the last record in the message.
*
* At least rs bytes must be allocated for the buffer at ciphertext.
*
* Returns -1 on error, otherwise the number of ciphertext bytes. That
* number will be equal to rs for every record but the last, which may be
* smaller.
*/
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]);
/*-
* Copyright (c) 2019 UPLEX Nils Goroll Systemoptimierung
* All rights reserved
*
* Author: Geoffrey Simmons <geoffrey.simmons@uplex.de>
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY THE AUTHOR AND CONTRIBUTORS ``AS IS'' AND
* ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
* ARE DISCLAIMED. IN NO EVENT SHALL AUTHOR OR CONTRIBUTORS BE LIABLE
* FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL
* DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS
* OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION)
* HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT
* LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY
* OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF
* SUCH DAMAGE.
*/
#include "config.h"
#include <string.h>
#ifndef VDEF_H_INCLUDED
# include "vdef.h"
#endif
#include "vas.h"
#include "rfc8188.h"
#define HDR_PREFIX_LEN (SALT_LEN + 4 + 1)
/*
* Input data and expected values from the two examples in RFC8188 ch 3.1
* and 3.2
*
* base64 encodings using libcrypto EVP_En/Decode differ from the examples
* in RFC8188.
*
* The RFC uses base64url, libcrypto does not, so:
* '-' => '+'
* '_' => '/'
*
* libcrypto requires '=' padding, the RFC doesn't use them.
*
* The output buffers for libcrypto decodings are larger than the actual
* binary lengths, because libcrypto pads '\0's so that the output size is
* exactly input_size*(3/4).
*/
static const unsigned char exp_plaintext[] = "I am the walrus";
static const int exp_plaintext_len = 15;
/* 1st example in ch 3.1 */
static const unsigned char body1_b64[] =
"I1BsxtFttlv3u/Oo94xnmwAAEAAA+NAVub2qFgBEuQKRapoZu+IxkIva3MEB1PD+ly8Thjg=",
key1_b64[] = "yqdlZ+tYemfogSmv7Ws5PQ==",
salt_b64[] = "I1BsxtFttlv3u/Oo94xnmw==",
prk1_b64[] = "zyeH5phsIsgUyd4oiSEIy35x+gIi4aM7y0hCF8mwn9g=",
cek1_b64[] = "/wniytB+ofscZDh4tbSjHw==",
nonce1_b64[] = "Bcs8gkIRKLI8GeI8";
static const int bodylen1 = 53;
static const uint32_t exp_rs1 = 4096;
static const uint8_t exp_idlen1 = 0;
/* 2nd example in ch 3.2 */
const unsigned char body2_b64[] =
"uNCkWiNYzKTnBN9ji3+qWAAAABkCYTHOG8chz/gnvgOqdGYovxyjuqRyJFjEDyoF1Fvkj6hQPdPHI51OEUKEpgz3SsLWIqS/uA==",
key2_b64[] = "BO3ZVPxUlnLORbVGMpbT1Q==",
exp_keyid2[] = "a1";
static const uint32_t exp_rs2 = 25;
static const uint8_t exp_idlen2 = 2;
int
main(int argc, char *argv[])
{
EVP_CIPHER_CTX *ctx;
unsigned char prk_b64[45], cek_b64[25], nonce_b64[17];
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;
char errmsg[ERRMSG_LEN];
uint32_t rs;
uint8_t idlen;
int len, last, plaintext_len;
(void)argc;
(void)argv;
/* example 1, ch 3.1 */
len = EVP_DecodeBlock(key1, key1_b64, sizeof(key1_b64) - 1);
assert(len == AES128_KEYLEN + 2);
len = EVP_DecodeBlock(salt, salt_b64, sizeof(salt_b64) - 1);
assert(len == SALT_LEN + 2);
if (derive_prk(salt, key1, prk, errmsg) != 0) {
fprintf(stderr, "ex1 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 CEK: %s\n", errmsg);
exit(-1);
}
len = EVP_EncodeBlock(cek_b64, cek, AES128_KEYLEN);
assert(len == 24);
AZ(memcmp(cek_b64, cek1_b64, len));
memset(seq, 0, NONCE_LEN);
if (derive_prenonce(prk, nonce, errmsg) != 0) {
fprintf(stderr, "ex1 NONCE: %s\n", errmsg);
exit(-1);
}
len = EVP_EncodeBlock(nonce_b64, nonce, NONCE_LEN);
assert(len == 16);
AZ(memcmp(nonce_b64, nonce1_b64, len));
len = EVP_DecodeBlock(body1, body1_b64, sizeof(body1_b64) - 1);
assert(len == 54);
AZ(memcmp(body1, salt, SALT_LEN));
decode_header(body1, &rs, &idlen);
assert(rs == exp_rs1);
assert(idlen == exp_idlen1);
if ((ctx = cipher_ctx_init(0, errmsg)) == NULL) {
fprintf(stderr, "ex1: cipher_ctx_init: %s\n", errmsg);
exit(-1);
}
/* bodylen < rs, so we compute ciphertext_len and tag specially */
ciphertext = body1 + HDR_PREFIX_LEN + idlen;
len = decrypt_record(ctx, ciphertext,
bodylen1 - (HDR_PREFIX_LEN + idlen) - TAG_LEN,
body1 + (bodylen1 - TAG_LEN), cek, nonce, plaintext, &last, errmsg);
if (len < 0) {
fprintf(stderr, "ex1 decrypt_record: %s\n", errmsg);
exit(-1);
}
assert(len == exp_plaintext_len);
AN(last);
AZ(memcmp(plaintext, exp_plaintext, len));
/* example 2, ch 3.2 */
if ((ctx = cipher_ctx_reset(ctx, 0, errmsg)) == NULL) {
fprintf(stderr, "ex2: cipher_ctx_reset: %s\n", errmsg);
exit(-1);
}
len = EVP_DecodeBlock(key2, key2_b64, sizeof(key2_b64) - 1);
assert(len == AES128_KEYLEN + 2);
len = EVP_DecodeBlock(body2, body2_b64, sizeof(body2_b64) - 1);
assert(len == 75);
decode_header(body2, &rs, &idlen);
assert(rs == exp_rs2);
assert(idlen = exp_idlen2);
AZ(memcmp(&body2[HDR_PREFIX_LEN], exp_keyid2, idlen));
if (derive_prk(body2, key2, prk, errmsg) != 0) {
fprintf(stderr, "ex2 PRK: %s\n", errmsg);
exit(-1);
}
if (derive_cek(prk, cek, errmsg) != 0) {
fprintf(stderr, "ex2 CEK: %s\n", errmsg);
exit(-1);
}
if (derive_prenonce(prk, nonce, errmsg) != 0) {
fprintf(stderr, "ex2 NONCE: %s\n", errmsg);
exit(-1);
}
/* First record */
ciphertext = body2 + HDR_PREFIX_LEN + idlen;
len = decrypt_record(ctx, ciphertext, rs - TAG_LEN,
ciphertext + (rs - TAG_LEN), cek, nonce, plaintext, &last, errmsg);
if (len < 0) {
fprintf(stderr, "ex2 1st record decrypt_record: %s\n", errmsg);
exit(-1);
}
AZ(last);
plaintext_len = len;
/* Second record */
seq[NONCE_LEN - 1] = 1; // simulates increment
for (int i = 0; i < NONCE_LEN; i++)
nonce[i] ^= seq[i];
ciphertext += rs;
len = decrypt_record(ctx, ciphertext, rs - TAG_LEN,
ciphertext + (rs - TAG_LEN), cek, nonce, plaintext + plaintext_len,
&last, errmsg);
if (len < 0) {
fprintf(stderr, "ex2 2st record decrypt_record: %s\n", errmsg);
exit(-1);
}
AN(last);
plaintext_len += len;
AZ(memcmp(plaintext, exp_plaintext, 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