From 067d7d8752d4d8a98f8e0e5e9b1a5b13e1b7ff9c Mon Sep 17 00:00:00 2001 From: Werner Koch Date: Fri, 16 Jan 2015 14:55:03 +0100 Subject: Add OCB cipher mode * cipher/cipher-ocb.c: New. * cipher/Makefile.am (libcipher_la_SOURCES): Add cipher-ocb.c * cipher/cipher-internal.h (OCB_BLOCK_LEN, OCB_L_TABLE_SIZE): New. (gcry_cipher_handle): Add fields marks.finalize and u_mode.ocb. * cipher/cipher.c (_gcry_cipher_open_internal): Add OCB mode. (_gcry_cipher_open_internal): Setup default taglen of OCB. (cipher_reset): Clear OCB specific data. (cipher_encrypt, cipher_decrypt, _gcry_cipher_authenticate) (_gcry_cipher_gettag, _gcry_cipher_checktag): Call OCB functions. (_gcry_cipher_setiv): Add OCB specific nonce setting. (_gcry_cipher_ctl): Add GCRYCTL_FINALIZE and GCRYCTL_SET_TAGLEN * src/gcrypt.h.in (GCRYCTL_SET_TAGLEN): New. (gcry_cipher_final): New. * cipher/bufhelp.h (buf_xor_1): New. * tests/basic.c (hex2buffer): New. (check_ocb_cipher): New. (main): Call it here. Add option --cipher-modes. * tests/bench-slope.c (bench_aead_encrypt_do_bench): Call gcry_cipher_final. (bench_aead_decrypt_do_bench): Ditto. (bench_aead_authenticate_do_bench): Ditto. Check error code. (bench_ocb_encrypt_do_bench): New. (bench_ocb_decrypt_do_bench): New. (bench_ocb_authenticate_do_bench): New. (ocb_encrypt_ops): New. (ocb_decrypt_ops): New. (ocb_authenticate_ops): New. (cipher_modes): Add them. (cipher_bench_one): Skip wrong block length for OCB. * tests/benchmark.c (cipher_bench): Add field noncelen to MODES. Add OCB support. -- See the comments on top of cipher/cipher-ocb.c for the patent status of the OCB mode. The implementation has not yet been optimized and as such is not faster that the other AEAD modes. A first candidate for optimization is the double_block function. Large improvements can be expected by writing an AES ECB function to work on multiple blocks. Signed-off-by: Werner Koch --- tests/basic.c | 401 +++++++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 399 insertions(+), 2 deletions(-) (limited to 'tests/basic.c') diff --git a/tests/basic.c b/tests/basic.c index ef8260f0..869b3818 100644 --- a/tests/basic.c +++ b/tests/basic.c @@ -61,6 +61,22 @@ static int error_count; static int in_fips_mode; static int die_on_error; +#define MAX_DATA_LEN 128 + +#define digitp(p) (*(p) >= '0' && *(p) <= '9') +#define hexdigitp(a) (digitp (a) \ + || (*(a) >= 'A' && *(a) <= 'F') \ + || (*(a) >= 'a' && *(a) <= 'f')) +#define xtoi_1(p) (*(p) <= '9'? (*(p)- '0'): \ + *(p) <= 'F'? (*(p)-'A'+10):(*(p)-'a'+10)) +#define xtoi_2(p) ((xtoi_1(p) * 16) + xtoi_1((p)+1)) +#define xmalloc(a) gcry_xmalloc ((a)) +#define xcalloc(a,b) gcry_xcalloc ((a),(b)) +#define xstrdup(a) gcry_xstrdup ((a)) +#define xfree(a) gcry_free ((a)) + + + static void fail (const char *format, ...) { @@ -74,6 +90,7 @@ fail (const char *format, ...) exit (1); } + static void mismatch (const void *expected, size_t expectedlen, const void *computed, size_t computedlen) @@ -102,6 +119,30 @@ die (const char *format, ...) } +/* Convert STRING consisting of hex characters into its binary + representation and return it as an allocated buffer. The valid + length of the buffer is returned at R_LENGTH. The string is + delimited by end of string. The function terminates on error. */ +static void * +hex2buffer (const char *string, size_t *r_length) +{ + const char *s; + unsigned char *buffer; + size_t length; + + buffer = xmalloc (strlen(string)/2+1); + length = 0; + for (s=string; *s; s +=2 ) + { + if (!hexdigitp (s) || !hexdigitp (s+1)) + die ("invalid hex digits in \"%s\"\n", string); + ((unsigned char*)buffer)[length++] = xtoi_2 (s); + } + *r_length = length; + return buffer; +} + + static void show_sexp (const char *prefix, gcry_sexp_t a) { @@ -195,8 +236,6 @@ show_mac_not_available (int algo) -#define MAX_DATA_LEN 128 - void progress_handler (void *cb_data, const char *what, int printchar, int current, int total) @@ -2741,6 +2780,355 @@ check_ccm_cipher (void) } +static void +check_ocb_cipher (void) +{ + /* Note that we use hex strings and not binary strings in TV. That + makes it easier to maintain the test vectors. */ + static const struct + { + int algo; + int taglen; /* 16, 12, or 8 bytes */ + const char *key; /* NULL means "000102030405060708090A0B0C0D0E0F" */ + const char *nonce; + const char *aad; + const char *plain; + const char *ciph; + } tv[] = { + /* The RFC-7253 test vectos*/ + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221100", + "", + "", + "785407BFFFC8AD9EDCC5520AC9111EE6" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221101", + "0001020304050607", + "0001020304050607", + "6820B3657B6F615A5725BDA0D3B4EB3A257C9AF1F8F03009" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221102", + "0001020304050607", + "", + "81017F8203F081277152FADE694A0A00" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221103", + "", + "0001020304050607", + "45DD69F8F5AAE72414054CD1F35D82760B2CD00D2F99BFA9" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221104", + "000102030405060708090A0B0C0D0E0F", + "000102030405060708090A0B0C0D0E0F", + "571D535B60B277188BE5147170A9A22C3AD7A4FF3835B8C5" + "701C1CCEC8FC3358" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221105", + "000102030405060708090A0B0C0D0E0F", + "", + "8CF761B6902EF764462AD86498CA6B97" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221106", + "", + "000102030405060708090A0B0C0D0E0F", + "5CE88EC2E0692706A915C00AEB8B2396F40E1C743F52436B" + "DF06D8FA1ECA343D" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221107", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "1CA2207308C87C010756104D8840CE1952F09673A448A122" + "C92C62241051F57356D7F3C90BB0E07F" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221108", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "", + "6DC225A071FC1B9F7C69F93B0F1E10DE" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA99887766554433221109", + "", + "000102030405060708090A0B0C0D0E0F1011121314151617", + "221BD0DE7FA6FE993ECCD769460A0AF2D6CDED0C395B1C3C" + "E725F32494B9F914D85C0B1EB38357FF" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110A", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F", + "BD6F6C496201C69296C11EFD138A467ABD3C707924B964DE" + "AFFC40319AF5A48540FBBA186C5553C68AD9F592A79A4240" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110B", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F", + "", + "FE80690BEE8A485D11F32965BC9D2A32" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110C", + "", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F", + "2942BFC773BDA23CABC6ACFD9BFD5835BD300F0973792EF4" + "6040C53F1432BCDFB5E1DDE3BC18A5F840B52E653444D5DF" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110D", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "D5CA91748410C1751FF8A2F618255B68A0A12E093FF45460" + "6E59F9C1D0DDC54B65E8628E568BAD7AED07BA06A4A69483" + "A7035490C5769E60" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110E", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "", + "C5CD9D1850C141E358649994EE701B68" + }, + { GCRY_CIPHER_AES, 16, NULL, + "BBAA9988776655443322110F", + "", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "4412923493C57D5DE0D700F753CCE0D1D2D95060122E9F15" + "A5DDBFC5787E50B5CC55EE507BCB084E479AD363AC366B95" + "A98CA5F3000B1479" + }, + { GCRY_CIPHER_AES, 12, "0F0E0D0C0B0A09080706050403020100", + "BBAA9988776655443322110D", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "000102030405060708090A0B0C0D0E0F1011121314151617" + "18191A1B1C1D1E1F2021222324252627", + "1792A4E31E0755FB03E31B22116E6C2DDF9EFD6E33D536F1" + "A0124B0A55BAE884ED93481529C76B6AD0C515F4D1CDD4FD" + "AC4F02AA" + } + }; + gpg_error_t err = 0; + gcry_cipher_hd_t hde, hdd; + unsigned char out[MAX_DATA_LEN]; + unsigned char tag[16]; + int tidx; + + if (verbose) + fprintf (stderr, " Starting OCB checks.\n"); + + for (tidx = 0; tidx < DIM (tv); tidx++) + { + char *key, *nonce, *aad, *ciph, *plain; + size_t keylen, noncelen, aadlen, ciphlen, plainlen; + int taglen; + + if (verbose) + fprintf (stderr, " checking OCB mode for %s [%i] (tv %d)\n", + gcry_cipher_algo_name (tv[tidx].algo), tv[tidx].algo, tidx); + + /* Convert to hex strings to binary. */ + key = hex2buffer (tv[tidx].key? tv[tidx].key + /* */: "000102030405060708090A0B0C0D0E0F", + &keylen); + nonce = hex2buffer (tv[tidx].nonce, &noncelen); + aad = hex2buffer (tv[tidx].aad, &aadlen); + plain = hex2buffer (tv[tidx].plain, &plainlen); + ciph = hex2buffer (tv[tidx].ciph, &ciphlen); + + /* Check that our test vectors are sane. */ + assert (plainlen <= sizeof out); + assert (tv[tidx].taglen <= ciphlen); + assert (tv[tidx].taglen <= sizeof tag); + + err = gcry_cipher_open (&hde, tv[tidx].algo, GCRY_CIPHER_MODE_OCB, 0); + if (!err) + err = gcry_cipher_open (&hdd, tv[tidx].algo, GCRY_CIPHER_MODE_OCB, 0); + if (err) + { + fail ("cipher-ocb, gcry_cipher_open failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + return; + } + + /* Set the taglen. For the first handle we do this only for a + non-default taglen. For the second handle we check that we + can also set to the default taglen. */ + taglen = tv[tidx].taglen; + if (taglen != 16) + { + err = gcry_cipher_ctl (hde, GCRYCTL_SET_TAGLEN, + &taglen, sizeof taglen); + if (err) + { + fail ("cipher-ocb, gcryctl_set_taglen failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + } + err = gcry_cipher_ctl (hdd, GCRYCTL_SET_TAGLEN, + &taglen, sizeof taglen); + if (err) + { + fail ("cipher-ocb, gcryctl_set_taglen failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + err = gcry_cipher_setkey (hde, key, keylen); + if (!err) + err = gcry_cipher_setkey (hdd, key, keylen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_setkey failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + err = gcry_cipher_setiv (hde, nonce, noncelen); + if (!err) + err = gcry_cipher_setiv (hdd, nonce, noncelen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_setiv failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + err = gcry_cipher_authenticate (hde, aad, aadlen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_authenticate failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + err = gcry_cipher_final (hde); + if (!err) + err = gcry_cipher_encrypt (hde, out, MAX_DATA_LEN, plain, plainlen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_encrypt failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + /* Check that the encrypt output matches the expected cipher + text without the tag (i.e. at the length of plaintext). */ + if (memcmp (ciph, out, plainlen)) + { + mismatch (ciph, plainlen, out, plainlen); + fail ("cipher-ocb, encrypt data mismatch (tv %d)\n", tidx); + } + + /* Check that the tag matches TAGLEN bytes from the end of the + expected ciphertext. */ + err = gcry_cipher_gettag (hde, tag, tv[tidx].taglen); + if (err) + { + fail ("cipher_ocb, gcry_cipher_gettag failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + } + if (memcmp (ciph + ciphlen - tv[tidx].taglen, tag, tv[tidx].taglen)) + { + mismatch (ciph + ciphlen - tv[tidx].taglen, tv[tidx].taglen, + tag, tv[tidx].taglen); + fail ("cipher-ocb, encrypt tag mismatch (tv %d)\n", tidx); + } + + + err = gcry_cipher_authenticate (hdd, aad, aadlen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_authenticate failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + /* Now for the decryption. */ + err = gcry_cipher_final (hdd); + if (!err) + err = gcry_cipher_decrypt (hdd, out, plainlen, NULL, 0); + if (err) + { + fail ("cipher-ocb, gcry_cipher_decrypt (tv %d) failed: %s\n", + tidx, gpg_strerror (err)); + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + return; + } + + /* We still have TAG from the encryption. */ + err = gcry_cipher_checktag (hdd, tag, tv[tidx].taglen); + if (err) + { + fail ("cipher-ocb, gcry_cipher_checktag failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + } + + /* Check that the decrypt output matches the original plaintext. */ + if (memcmp (plain, out, plainlen)) + { + mismatch (plain, plainlen, out, plainlen); + fail ("cipher-ocb, decrypt data mismatch (tv %d)\n", tidx); + } + + /* Check that gettag also works for decryption. */ + err = gcry_cipher_gettag (hdd, tag, tv[tidx].taglen); + if (err) + { + fail ("cipher_ocb, decrypt gettag failed (tv %d): %s\n", + tidx, gpg_strerror (err)); + } + if (memcmp (ciph + ciphlen - tv[tidx].taglen, tag, tv[tidx].taglen)) + { + mismatch (ciph + ciphlen - tv[tidx].taglen, tv[tidx].taglen, + tag, tv[tidx].taglen); + fail ("cipher-ocb, decrypt tag mismatch (tv %d)\n", tidx); + } + + gcry_cipher_close (hde); + gcry_cipher_close (hdd); + + xfree (nonce); + xfree (aad); + xfree (ciph); + xfree (plain); + xfree (key); + } + + if (verbose) + fprintf (stderr, " Completed OCB checks.\n"); +} + + static void check_stream_cipher (void) { @@ -4391,6 +4779,7 @@ check_cipher_modes(void) check_ccm_cipher (); check_gcm_cipher (); check_poly1305_cipher (); + check_ocb_cipher (); check_stream_cipher (); check_stream_cipher_large_block (); @@ -7143,6 +7532,7 @@ main (int argc, char **argv) int use_fips = 0; int selftest_only = 0; int pubkey_only = 0; + int cipher_modes_only = 0; int loop = 0; unsigned int loopcount = 0; @@ -7183,6 +7573,11 @@ main (int argc, char **argv) pubkey_only = 1; argc--; argv++; } + else if (!strcmp (*argv, "--cipher-modes")) + { + cipher_modes_only = 1; + argc--; argv++; + } else if (!strcmp (*argv, "--die")) { die_on_error = 1; @@ -7228,6 +7623,8 @@ main (int argc, char **argv) { if (pubkey_only) check_pubkey (); + else if (cipher_modes_only) + check_ciphers (); else if (!selftest_only) { check_ciphers (); -- cgit v1.2.1