Dactyloidae/mobile/ios/ThirdParty/ecec/test/encrypt/aesgcm.c
2026-06-26 21:04:09 -07:00

219 lines
10 KiB
C

#include "test.h"
#include <stdlib.h>
#include <string.h>
#include <openssl/rand.h>
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);
}
}