#include "test.h" #include #include #include typedef struct webpush_aesgcm_encrypt_ok_test_s { const char* desc; const char* ciphertext; const char* senderPrivKey; const char* recvPubKey; const char* authSecret; const char* salt; const char* plaintext; size_t plaintextLen; size_t padLen; size_t maxCiphertextLen; size_t ciphertextLen; uint32_t rs; } webpush_aesgcm_encrypt_ok_test_t; static webpush_aesgcm_encrypt_ok_test_t webpush_aesgcm_encrypt_ok_tests[] = { { .desc = "Example from draft-ietf-webpush-encryption-04", .ciphertext = "\xea\x7a\x80\x41\x43\x04\xf2\x13\x6a\xc3\x92\x77\x92\x5f" "\x1c\xa5\x55\x49\xca\x55\xca\x62\xa6\x4e\x7a\xc7\x99\x1b" "\xc5\x2e\x78\xaa\x40", .senderPrivKey = "\x9c\x24\x9c\x7a\x4f\x90\xa4\x48\xe6\x38\xe9\x53\xfa" "\xb4\x37\xf2\x76\x73\xbd\xd3\xe5\xa9\xad\x34\x67\x2d" "\x22\xea\x6d\x8e\x26\xf6", .recvPubKey = "\x04\x21\x24\x06\x3c\xcb\xf1\x9d\xc2\xfa\x88\xb6\x43\xba" "\x04\xe6\xdd\x8d\xa7\xea\x7b\xa2\xc8\xc6\x2e\x0f\x77\xa9" "\x43\xf4\xc2\xfa\x91\x4f\x6d\x44\x11\x6c\x9f\xd1\xc4\x03" "\x41\xc6\xa4\x40\xca\xb3\xe2\x14\x0a\x60\xe4\x37\x8a\x5d" "\xa7\x35\x97\x2d\xe0\x78\x00\x51\x05", .authSecret = "\x47\x6f\x6f\x20\x67\x6f\x6f\x20\x67\x27\x20\x6a\x6f\x6f\x62\x21", .salt = "\x96\x78\x1a\xad\xbc\x8a\x7c\xca\x22\xf5\x9e\xf9\xc5\x85\xe6\x92", .plaintext = "I am the walrus", .plaintextLen = 15, .padLen = 0, .maxCiphertextLen = 33, .ciphertextLen = 33, .rs = 4096, }, { .desc = "rs = 4, trailer", .ciphertext = "\xe8\x7d\x81\x05\x8d\x25\xd1\xdb\xfc\xf3\x1c\x5d\x52\xd4\x39\x51\xb4" "\x7c\x63\x07\x4d\x6d\xb3\xf0\xaf\x89\xe4\xb3\xae\x64\x3d\x00\xb5\xce" "\xda\x84\x4c\xf7\x9b\xce\xb7\xf9\x4e\xdc\x95\xa9\xf8\x78\x4d\xcf\xf0" "\xb7\xeb\xdc\x27\x7e\x9a\x17\xdb\xd4\x62\x78\xaa\x8d\x7e\xe9\x65\x24" "\x81\x22\xe8\x40\x81\x37\x4c\xab\xd0\x3d\xb8\x3b\x98\x39\xdb\x34\x28" "\x88\xf9\x68\x58\x35\x26\x58\x34\xbb\x11\xe6\xdb\x1e\xb0\x95\xcd\x17" "\x5e\x12\xc5\xf8\x3f\x1f\xb6\xed\x3a\x4a\x00\x2b\x05\x7c\x10\xeb\x97" "\x94\x16\x6a\xc6\x1d\xc0\x00\x74\xf8\xd0\x95\xbb\xb6\x7f\xa8\x18\x35" "\xac\x1b\xa7\x16\xc8\xde\x94\xda\x49\xfb\xdb\x0a\x74\xf6\xac\x68\x89" "\x7b\xb3\x8d\x3f\xba\x88\x7a\x8b\xc9\x44\xa4\xc9\xdd\x83\x61\x20\x8b" "\xc3\x82\x57\x65\xe6\xb8\xc7\x69\xd5\x4d\xa7\xa4\x6f\x26\xf0\x50\xd0" "\xad\xc9\x5a\x16\xde\xa1\xa7\x50\xb0\xf7\xcf\x4d\x3b\xf4\xf5\x1f\xad" "\x32\xaf\x7a\x0a\x4a\x6a\x4c\x33\x2b\xa8\x49\xe5\xcf\x78\xa3\xf7\x8b" "\x38\x98\xdc\x3a\x8c\x95\x3c\xde\xb1\xc9\x79\x9b\x0e\xb4\x6e\xad\x37" "\xe0\xfa\xf4\xba\xc2\xbe\xd6\x56\x58\xb4\x86\x1c\xf9\x42\xfd\x77\xf2" "\x36\x33\xc5\xe5\xa1\x2e\xe1\x3c\xba\xd7\x64\xa2\x1a\x92\x5c\x79\x47" "\xf1\xd3\x18\x34\x9a\x9d\x96\x10\x71\xb8\x74\xc6\xb0\xc1\x06\x66\x62" "\x34\x97\x27\x84\x36\xa9\x3c\xd1\xa9\xa4\xe0\x32\xa9\x64\x9f\x29\x7a" "\x4d\x8b\xa4\x59\x44\xf6\x3b\xdb\x0b\xce\x45\xce\x0d\x75\xd3\x7c\x24" "\xa0\x69\x0f\xa0\x96\x0d\x86\x6e\x99\x2d\x61\x63\x15\xba\x01\x3b\x80" "\xc6\xe3\xac\xfd\x97\x26\x07\x42\x4b\xec\x10\x88\x8d\xd5\x0a\x15\x13" "\x42\xf1\x5d\x48\x18\x36\xfd\xf3\x65\x8c\x86\xe8\x58\xac\xc0\xf2\x5f" "\x3f\xe0\xce\xe5\xc2\x29\x5f\x04\x4e\x1e\x82\x89\xbc\x52\x50\x9d\x4f" "\x5c\x4e\xb5\x56\x5d\x4e\x62\x0a\xe5\x9c\xdb\x26\xa5\x02\x09\x63\xfc" "\x6e\x43\xca\x0e\x7c\x45\x29\xb3\x22\x38\xfd\x3a\xf6\x0e\xda\xf6\xc5" "\x6c\x9f\xb4\x08\x95\xb1\xe6\x01\x4a\xd7\x3b\x69\xb3\xcb\x55\x2c\xe8" "\xc0\xa8\xbb\x14\xff\xe9\x66\xc7\x4d\x40\xfc\x28\xd2\x05\xab\xee\x5f" "\x73\xed\x9a\x0d\xb9\x64\xe0\x69\xe4\x86\xb3\x02\xef\x58\xcc\xf6\xa3" "\x4b\x4e", .senderPrivKey = "\xf1\xfe\xd7\x17\xb0\xd8\x35\xd2\x9b\x9e\x4e\x4f\x4c" "\xf8\x7d\x0c\xb9\x85\xe2\xbc\x44\xff\xd6\xd4\x37\xa4" "\xe7\x4a\x18\x04\x55\x87", .recvPubKey = "\x04\x09\x22\xf1\x7c\xed\x7d\x09\xd4\x55\x10\x2d\xd9\x0c" "\x31\x46\xce\x33\x2f\x45\xd2\x34\x96\x46\xfe\x64\xca\xbb" "\x7f\x41\x23\x03\xbe\x5c\x23\x53\x9a\xff\x5c\x30\x72\xdd" "\x85\xa9\x0b\x76\x92\xe1\x96\x22\x09\x39\xee\xb7\x3e\x0e" "\xf2\x47\x59\x32\xb1\xcd\xa9\x16\xc7", .authSecret = "\xd3\xf8\x22\xf4\x8c\x74\x74\x19\x5d\xa6\x4d\x78\x5f\x40\x8f\x26", .salt = "\x77\x10\x67\x4b\x1e\xcd\x0b\xf7\xa7\xd3\x25\x88\xea\xe9\x53\x9c", .plaintext = "I am the very model of a modern Major-General.", .plaintextLen = 46, .padLen = 0, .maxCiphertextLen = 478, .ciphertextLen = 478, .rs = 4, }, { .desc = "rs = 6, pad = 4", .ciphertext = "\x54\x6d\x13\x0b\x1e\xf0\xc2\x3e\xff\x49\x87\xe5\xc6\x57\xf1" "\x94\xb1\xb8\xad\x5a\xda\xb7\x02\x32\x3c\xf9\xc0\x7b\xd8\x28" "\x20\x9e\x5d\xe7\x5e\x8b\xf6\x6a\xa5\xf8\xf6\x3b\x0a\x66\xd3" "\x99\xeb\x8b\x98\x70\x6c\xfc\xa5\xa5\x3f\x8f\x50\x8c\x26\x56" "\x5a\x34\xe4", .senderPrivKey = "\xe3\x31\x8e\xc3\x99\xa9\xc4\x71\x7a\xa9\x4b\xa4\xed\xb3" "\xaa\x1d\x90\x96\x5c\xe7\x6a\x57\x6b\x52\xa0\x7c\x27\x44" "\x0c\x6d\x9b\xd1", .recvPubKey = "\x04\xaa\xec\x79\x05\x22\xad\x2b\x70\x56\x05\x58\x99\xda\xa6" "\x47\x7d\x55\xab\x3f\x16\x5d\x76\x00\x56\x19\xf3\xdf\xa9\x72" "\x19\xf5\x59\x02\x5d\x09\xc0\x80\x83\x70\xc6\x06\x8a\x66\x51" "\x81\xd4\x3d\x74\xac\x3d\x0a\xca\x28\x67\x5e\x81\x55\x9a\x20" "\x4a\x6d\x44\xb5\x91", .authSecret = "\x54\x9d\xe2\xfd\x40\x52\xef\x4e\x05\x28\x45\x13\x3e\x37\x61\x3d", .salt = "\x3a\x4b\xd3\x72\x75\x09\x5d\x71\xc9\x81\xe0\x9c\xde\x0d\xa6\x2a", .plaintext = "Hello", .plaintextLen = 5, .padLen = 4, .maxCiphertextLen = 63, .ciphertextLen = 63, .rs = 6, }, }; void test_webpush_aesgcm_encrypt_ok(void) { size_t tests = sizeof(webpush_aesgcm_encrypt_ok_tests) / sizeof(webpush_aesgcm_encrypt_ok_test_t); for (size_t i = 0; i < tests; i++) { webpush_aesgcm_encrypt_ok_test_t t = webpush_aesgcm_encrypt_ok_tests[i]; const void* senderPrivKey = t.senderPrivKey; const void* authSecret = t.authSecret; const void* salt = t.salt; const void* recvPubKey = t.recvPubKey; const void* plaintext = t.plaintext; size_t ciphertextLen = ece_aesgcm_ciphertext_max_length(t.rs, t.padLen, t.plaintextLen); ece_assert(ciphertextLen == t.maxCiphertextLen, "Got ciphertext max length %zu for `%s`; want %zu", ciphertextLen, t.desc, t.maxCiphertextLen); uint8_t* ciphertext = calloc(ciphertextLen, sizeof(uint8_t)); int err = ece_webpush_aesgcm_encrypt_with_keys( senderPrivKey, ECE_WEBPUSH_PRIVATE_KEY_LENGTH, authSecret, ECE_WEBPUSH_AUTH_SECRET_LENGTH, salt, ECE_SALT_LENGTH, recvPubKey, ECE_WEBPUSH_PUBLIC_KEY_LENGTH, t.rs, t.padLen, plaintext, t.plaintextLen, ciphertext, &ciphertextLen); ece_assert(!err, "Got %d encrypting ciphertext for `%s`", err, t.desc); ece_assert(ciphertextLen == t.ciphertextLen, "Got actual ciphertext length %zu for `%s`; want %zu", ciphertextLen, t.desc, t.ciphertextLen); ece_assert(!memcmp(ciphertext, t.ciphertext, ciphertextLen), "Wrong ciphertext for `%s`", t.desc); free(ciphertext); } } void test_webpush_aesgcm_encrypt_pad(void) { static const uint32_t maxRs = 128; const void* senderPrivKey = "\xac\xae\xc1\xc3\x7c\x30\x7c\xb9\x02\x8f\xbb\xd9" "\xc7\xf3\xc6\x89\x26\x60\x08\x95\x9a\x5e\xd4\x03" "\x42\x21\xb2\xda\x72\x01\x82\x8f"; const void* authSecret = "\x44\x29\x81\x2d\x53\x5f\xbf\xdb\xea\xc8\x6d\xb7\x14\x5c\x6a\xf2"; const void* salt = "\x45\x2b\xfb\xea\x8c\xc7\xa7\x57\x14\xd2\x03\xcf\xf1\x02\xe8\x76"; const void* recvPubKey = "\x04\x2d\x78\x8d\x3e\x8e\x82\xf2\xd7\xea\xef\xbd\xe3\xa1\xbe\xde\xa2\x1f" "\x3b\xc9\x60\x33\x15\x73\x22\xa0\x9e\x14\x46\x55\xa3\xdf\x78\xfd\xca\xc8" "\x10\xe3\x02\x2a\xb5\x6a\x0e\xa9\xb8\xec\x06\x73\x8a\xce\x41\x1f\x49\x54" "\x7b\xc0\x0d\x1a\x1c\xde\x97\xce\x7b\xdd\x26"; for (uint32_t rs = ECE_AESGCM_MIN_RS + 1; rs <= maxRs; rs++) { // Generate a random plaintext. size_t plaintextLen = (size_t)(rand() % (int) (maxRs - 1) + 1); uint8_t* plaintext = calloc(plaintextLen, sizeof(uint8_t)); int ok = RAND_bytes(plaintext, (int) plaintextLen); ece_assert(ok == 1, "Got %d generating plaintext for rs = %d", ok, rs); size_t maxPadLen = (rs - ECE_AESGCM_MIN_RS) * (plaintextLen + 1); // Encrypting with the maximum padding length should succeed. size_t ciphertextLen = ece_aesgcm_ciphertext_max_length(rs, maxPadLen, plaintextLen); uint8_t* ciphertext = calloc(ciphertextLen, sizeof(uint8_t)); int err = ece_webpush_aesgcm_encrypt_with_keys( senderPrivKey, ECE_WEBPUSH_PRIVATE_KEY_LENGTH, authSecret, ECE_WEBPUSH_AUTH_SECRET_LENGTH, salt, ECE_SALT_LENGTH, recvPubKey, ECE_WEBPUSH_PUBLIC_KEY_LENGTH, rs, maxPadLen, plaintext, plaintextLen, ciphertext, &ciphertextLen); ece_assert(!err, "Got %d encrypting with rs = %d, padLen = %zu", err, rs, maxPadLen); // Adding more padding should fail. size_t badPadLen = maxPadLen + 1; ciphertextLen = ece_aesgcm_ciphertext_max_length(rs, badPadLen, plaintextLen); ciphertext = realloc(ciphertext, ciphertextLen); err = ece_webpush_aesgcm_encrypt_with_keys( senderPrivKey, ECE_WEBPUSH_PRIVATE_KEY_LENGTH, authSecret, ECE_WEBPUSH_AUTH_SECRET_LENGTH, salt, ECE_SALT_LENGTH, recvPubKey, ECE_WEBPUSH_PUBLIC_KEY_LENGTH, rs, badPadLen, plaintext, plaintextLen, ciphertext, &ciphertextLen); ece_assert(err == ECE_ERROR_ENCRYPT_PADDING, "Want error encrypting with rs = %d, padLen = %zu", rs, badPadLen); free(plaintext); free(ciphertext); } }