mirror of
https://repo.dactyloidae.xyz/Dactyloidae/UXP.git
synced 2026-09-20 23:37:33 +09:00
306 lines
13 KiB
C
306 lines
13 KiB
C
#include "test.h"
|
|
|
|
#include <string.h>
|
|
|
|
typedef struct webpush_aesgcm_decrypt_ok_test_s {
|
|
const char* desc;
|
|
const char* plaintext;
|
|
const char* recvPrivKey;
|
|
const char* authSecret;
|
|
const char* ciphertext;
|
|
const char* cryptoKey;
|
|
const char* encryption;
|
|
size_t ciphertextLen;
|
|
size_t maxPlaintextLen;
|
|
size_t plaintextLen;
|
|
} webpush_aesgcm_decrypt_ok_test_t;
|
|
|
|
typedef struct webpush_aesgcm_decrypt_err_test_s {
|
|
const char* desc;
|
|
const char* recvPrivKey;
|
|
const char* authSecret;
|
|
const char* ciphertext;
|
|
const char* cryptoKey;
|
|
const char* encryption;
|
|
size_t maxPlaintextLen;
|
|
size_t ciphertextLen;
|
|
int err;
|
|
} webpush_aesgcm_decrypt_err_test_t;
|
|
|
|
static webpush_aesgcm_decrypt_ok_test_t webpush_aesgcm_decrypt_ok_tests[] = {
|
|
{
|
|
.desc = "rs = 24, pad = 0",
|
|
.plaintext = "Some message",
|
|
.recvPrivKey = "\xe2\x1d\xb7\x1b\xf2\xa4\x5c\x2f\x53\xbc\x14\x8a\xda\xfd"
|
|
"\x10\xec\x89\xa9\x4b\x66\x00\xb9\x17\x7c\x85\x0c\x8d\xd2"
|
|
"\xb1\x40\xc0\x18",
|
|
.authSecret =
|
|
"\x69\x30\xdc\xe8\x97\x9b\xcd\x1e\x9e\x49\xcc\xb6\xa0\xba\x38\x45",
|
|
.ciphertext = "\x3a\x8d\xf8\xc3\x61\x7d\x55\x59\xd3\x30\x57\xca\xb5\xdc"
|
|
"\x78\xf0\x06\x56\x43\xd2\xe2\xf4\xce\x83\x6a\xe5\x89\x56"
|
|
"\x05\xd4",
|
|
.cryptoKey = "dh="
|
|
"BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxa"
|
|
"DKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo",
|
|
.encryption = "salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24",
|
|
.ciphertextLen = 30,
|
|
.maxPlaintextLen = 14,
|
|
.plaintextLen = 12,
|
|
},
|
|
{
|
|
.desc = "rs = 8, pad = 16",
|
|
.plaintext = "Yet another message",
|
|
.recvPrivKey = "\xe2\x1d\xb7\x1b\xf2\xa4\x5c\x2f\x53\xbc\x14\x8a\xda\xfd"
|
|
"\x10\xec\x89\xa9\x4b\x66\x00\xb9\x17\x7c\x85\x0c\x8d\xd2"
|
|
"\xb1\x40\xc0\x18",
|
|
.authSecret =
|
|
"\xea\x99\x70\x66\x74\xa9\x55\x46\xc5\xec\x03\xc3\x5e\xeb\x37\x51",
|
|
.ciphertext =
|
|
"\xb8\x40\xb9\x07\xfb\x51\xf9\xfb\x90\xdd\xd7\xa5\x41\xca\xf3\xac\x30"
|
|
"\xa9\xe3\x45\xba\x8a\x93\x19\x8c\x66\x7b\xf1\x44\x83\x27\x9b\x0c\x8f"
|
|
"\xee\x9b\x00\xe5\x46\xdc\x02\xba\x26\xa1\x65\xf4\x4e\x80\xa1\x68\x81"
|
|
"\x61\x8b\xcc\x65\xfc\x13\x85\x5c\x66\x0e\x7c\x3a\x44\x7b\x55\x78\xb2"
|
|
"\x85\x33\x90\xd6\x82\x5d\x44\xc2\x43\xa8\x87\x01\xf6\x12\x18\x83\x0f"
|
|
"\x48\x0c\xde\x2a\x3e\x77\xbf\x56\x2b\x33\x8f\x64\x4f\x6b\x0d\x65\x12"
|
|
"\xbf\xae\x09\xb2\xd2\x26\x49\xf5\xff\x00\x34\x1f\x00\x1a\xef\x6d\x99"
|
|
"\x1e\x69\x6c\x61\xe7\x71\x06\xe7\xd4\x0c\x38\x49\x45\x26\xa1\xee\x7b"
|
|
"\x87\x4c\x51\x1a\x6c\x31\x78",
|
|
.cryptoKey = "dh=BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_"
|
|
"HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ",
|
|
.encryption = "salt=ZFhzj0S-n29g9P2p4-I7tA; rs=8",
|
|
.ciphertextLen = 143,
|
|
.maxPlaintextLen = 47,
|
|
.plaintextLen = 19,
|
|
},
|
|
{
|
|
.desc = "rs = 3, pad = 0",
|
|
.plaintext = "Small record size",
|
|
.recvPrivKey = "\xe2\x1d\xb7\x1b\xf2\xa4\x5c\x2f\x53\xbc\x14\x8a\xda\xfd"
|
|
"\x10\xec\x89\xa9\x4b\x66\x00\xb9\x17\x7c\x85\x0c\x8d\xd2"
|
|
"\xb1\x40\xc0\x18",
|
|
.authSecret =
|
|
"\x83\x6a\xd6\x54\x75\x02\xa5\x4c\x60\x70\xbf\x53\xcf\xbb\xf2\x79",
|
|
.ciphertext =
|
|
"\xa1\x8e\x1e\xe5\xe0\xda\xb4\x35\x6d\xd9\xfa\x50\xca\x5c\x5b\x3c\x93"
|
|
"\x3e\xde\xfa\xdf\x84\x36\xac\x7c\xf7\x3c\x43\x53\xd6\xb7\x8b\x4f\x7c"
|
|
"\xc5\x5b\xcf\xfb\x03\x34\xbf\xdc\xbe\xbd\x03\x5d\x79\x1d\x17\x34\xb5"
|
|
"\x97\x1b\x09\xb2\x3e\x79\xd1\x44\xb1\xe0\xc3\x25\xd3\x58\xa1\x8c\x89"
|
|
"\x97\x0a\x3a\xf0\xf5\x1e\x71\x16\x01\x6b\x08\x0a\x89\x0f\x71\xb0\x5c"
|
|
"\x0d\x6f\xcd\x2e\x13\xe7\x2b\xc6\x55\xb5\x29\xdd\x29\xdd\x30\x37\x53"
|
|
"\xd4\xe0\x9c\x12\x87\xd3\x9f\xb5\x42\xeb\x16\x7e\xcc\xff\x2b\xcd\x24"
|
|
"\x1f\x0c\x20\x41\xc6\x63\xd4\xec\xe2\x12\xce\xbf\x19\xe0\x7c\xb3\x14"
|
|
"\x21\x89\x78\x17\xea\x89\x9e\xf2\x51\xf3\x65\x28\x5d\x71\xe5\x46\x99"
|
|
"\xe1\x7e\xa2\x53\xf4\xd7\xc7\x92\xa5\x43\x2f\xed\xa1\x5d\x5d\x5d\x9d"
|
|
"\x9e\x10\x03\x07\x25\x47\x12\x74\x0a\xef\x07\xac\xd1\xa4\x57\x44\x23"
|
|
"\x17\x5c\x1d\xc2\x72\xa3\x50\x48\x62\x00\x18\x4c\x37\x40\x23\x2f\xff"
|
|
"\x20\x95\x42\x3d\x8f\xdb\xd8\x21\x39\x5b\xfe\xa8\x86\x56\x0a\x07\xbc"
|
|
"\x43\xc8\x7f\x20\x88\xdb\x96\x59\x70\x20\x06\x8c\xd4\x7d\x27\x0b\x51"
|
|
"\x58\xcf\x0b\x7f\x32\x25\x3a\x40\x52\x2e\xb0\xea\x95\xe0\x5d\x45\x9f"
|
|
"\x7d\xe9\xdd\x7b\x91\x6b\x79\x0a\x67\x30\x52\xf5\x2f\x81\xbe\xdf\xcc"
|
|
"\x2c\x23\xff\x88\x48\x83\xb3\x74\xef\x10\x22\xb5\xc7\xc6\x6b\x5c\xd5"
|
|
"\x2e\x71\xcd\x8b\xbc\x6c\xca\x92\x80\x28\xa7\xbc\x21\x30\x75\xe1\x85"
|
|
"\x0a\x68\xa0\x85\x18\xe3\xf2\xdd\x73\x45\x6a\x34\x0f\x5e\x2b\x1d\xb4"
|
|
"\x0b\x3d\x1f\xdb\xd4\xfe\x52\xbc\x47\x89\x4e\xd4\xbd\x36\x6d\xe2\x89"
|
|
"\xb3",
|
|
.cryptoKey = "dh=BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--"
|
|
"aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_"
|
|
"fYHBP4ObxwJNl56bk",
|
|
.encryption = "salt=5LIDBXbvkBvvb7ZdD-T4PQ; rs=3",
|
|
.ciphertextLen = 341,
|
|
.maxPlaintextLen = 53,
|
|
.plaintextLen = 17,
|
|
},
|
|
{
|
|
.desc = "Example from draft-ietf-httpbis-encryption-encoding-02",
|
|
.plaintext = "I am the walrus",
|
|
.recvPrivKey = "\xf4\x55\xa5\xd7\x9f\xd0\x51\x00\x16\x0d\xa0\xf7\x93\x79"
|
|
"\x79\xd1\x90\x59\x40\x9e\x1a\xbb\x6e\xc5\xd5\x5e\x05\xd2"
|
|
"\xe2\xd2\x0f\xf3",
|
|
.authSecret =
|
|
"\x47\x6f\x6f\x20\x67\x6f\x6f\x20\x67\x27\x20\x6a\x6f\x6f\x62\x21",
|
|
.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",
|
|
.cryptoKey = "keyid=\"dhkey\"; "
|
|
"dh="
|
|
"\"BNoRDbb84JGm8g5Z5CFxurSqsXWJ11ItfXEWYVLE85Y7CYkDjXsIEc4"
|
|
"aqxYaQ1G8BqkXCJ6DPpDrWtdWj_mugHU\"",
|
|
.encryption = "keyid=\"dhkey\"; salt=\"lngarbyKfMoi9Z75xYXmkg\"",
|
|
.ciphertextLen = 33,
|
|
.maxPlaintextLen = 17,
|
|
.plaintextLen = 15,
|
|
},
|
|
};
|
|
|
|
static webpush_aesgcm_decrypt_err_test_t webpush_aesgcm_decrypt_err_tests[] = {
|
|
{
|
|
.desc = "rs = 7, no trailer",
|
|
.recvPrivKey = "\xd9\xbb\xb8\xa5\xa3\x80\x65\xb2\xf6\x79\xfd\x6e\xfb\x04"
|
|
"\xf3\x38\xdb\x93\x21\xc0\xcf\x73\x4d\x28\xd3\x35\x09\x82"
|
|
"\x0e\x3a\x5d\x37",
|
|
.authSecret =
|
|
"\x42\x80\xe2\xd2\xee\xaf\x72\xc9\x48\x54\x92\xa2\xa2\xe5\xcc\x5f",
|
|
// "O hai". The ciphertext is exactly `rs + 16`, without a trailer.
|
|
.ciphertext = "\x60\x6e\x05\xf9\xbd\x3a\xcb\x9f\x74\x85\x19\x67\x4a\xcc\x3f"
|
|
"\xbe\xe3\xb0\xeb\x65\x7d\x23\x3f",
|
|
.cryptoKey = "dh=BD_"
|
|
"bsTUpxBMvSv8eksith3vijMLj44D4jhJjO51y7wK1ytbUlsyYBBYYyB5AAe5b"
|
|
"nREA_WipTgemDVz00LiWcfM",
|
|
.encryption = "salt=xKWvs_jWWeg4KOsot_uBhA; rs=7",
|
|
.ciphertextLen = 23,
|
|
.maxPlaintextLen = 7,
|
|
.err = ECE_ERROR_DECRYPT_TRUNCATED,
|
|
},
|
|
{
|
|
// Last block is only 1 byte; pad length prefix is 2 bytes.
|
|
.desc = "Pad size > last block length",
|
|
.recvPrivKey = "\x0a\x8b\x04\x44\x05\x57\x82\xf4\xef\xa2\x1e\xd4\x92\xb4"
|
|
"\x42\xd9\x5f\xa2\x5e\x83\x6c\xd1\xb5\xe5\x7b\xd2\x3a\xf2"
|
|
"\xac\xe4\x95\xeb",
|
|
.authSecret =
|
|
"\x42\xd1\x99\x79\x8f\x0c\x41\xf0\x9a\xab\xe5\xf0\x28\xe5\x46\x05",
|
|
.ciphertext = "\x26\xf5\xfd\x1e\xc2\x78\x94\xbe\x60\xcc\xff\x3f\xb8\x22\x9c"
|
|
"\xea\xcd\x79\x89\x12\x1a\x36\x10\xf8\xa4\x50\xa0\xab\x9f\x9d"
|
|
"\x7f\x06\xd4\xa8\x47\x0d\x52\x4a\xaf",
|
|
.cryptoKey = "dh=BBNZNEi5Ew_ID5S4Y9jWBi1NeVDje6Mjs7SDLViUn6A8VAZj-"
|
|
"6X3QAuYQ3j20BblqjwTgYst7PRnY6UGrKyLbmU",
|
|
.encryption = "salt=ot8hzbwOo6CYe6ZhdlwKtg; rs=6",
|
|
.ciphertextLen = 39,
|
|
.maxPlaintextLen = 7,
|
|
.err = ECE_ERROR_DECRYPT_PADDING,
|
|
},
|
|
{
|
|
// Last block is 1 byte, but claims its pad length is 2.
|
|
.desc = "Padding length > last block length",
|
|
.recvPrivKey = "\xf6\x5f\x9a\x85\xc0\x4c\xf8\x8d\x32\x93\x06\xd6\x88\x34"
|
|
"\xbd\x29\x1a\xcf\x76\x1c\xaf\x4d\x9d\x12\xc4\xa8\x8f\xa6"
|
|
"\x3d\x9a\x79\xa2",
|
|
.authSecret =
|
|
"\x80\xa1\xbf\x3f\xaf\x9d\x7b\x9a\x72\xcd\x2f\x21\xc8\x7f\xcd\xc9",
|
|
.ciphertext = "\xa1\x64\x8e\x14\x0f\x94\x3b\x9a\x16\xab\xe9\x08\xef\xd4\x47"
|
|
"\x68\x57\xf0\x01\xe8\xcb\x89\x02\x78\x0b\xb7\x93\x9a\xb4\x93"
|
|
"\x06\x5e\x20\x02\xb2\xd7\x7f\x1e\xe5\x67\xe6",
|
|
.cryptoKey = "dh=BKe2IBO_cwmEzQyTVscSbQcj0Y3uBSzGZ_mHlANMciS8uGpb7U8_"
|
|
"Bw7TNdlYfpwWDLd0cxM8YYWNDbNJ_p2Rp4o",
|
|
.encryption = "salt=z7QJ6UR89SiFRkd4RsC4Vg; rs=6",
|
|
.ciphertextLen = 41,
|
|
.maxPlaintextLen = 9,
|
|
.err = ECE_ERROR_DECRYPT_PADDING,
|
|
},
|
|
{
|
|
// First block has no padding, but claims its pad length is 1.
|
|
.desc = "Non-zero padding",
|
|
.recvPrivKey = "\x23\x3b\x9a\xc4\xba\x85\x26\x68\xd2\xbb\xc1\xa3\x2c\x2a"
|
|
"\x36\xa0\x46\x83\x66\x30\xc1\xba\xd5\xb8\x9b\x84\xf4\xab"
|
|
"\x1d\x36\x5e\xc3",
|
|
.authSecret =
|
|
"\x70\xca\x56\x41\x6e\x7c\x06\xba\x43\x6c\x9f\x0a\xa9\xb4\xbd\x8a",
|
|
.ciphertext = "\x41\xdb\xe3\x87\x42\xe4\x65\x72\xae\xff\x51\xef\xbf\x9e\x83"
|
|
"\xd2\xb3\x92\x17\xa3\x30\xc3\x7c\xb4\x17\xcc\x64\xc5\x43\x65"
|
|
"\xc1\x5b\xb6\x53\x58\x9a\x90\xe5\x14\x19\x1b",
|
|
.cryptoKey = "dh=BBicj01QI0ryiFzAaty9VpW_crgq9XbU1bOCtEZI9UNE6tuOgp4lyN_"
|
|
"UN0N905ECnLWK5v_sCPUIxnQgOuCseSo",
|
|
.encryption = "salt=SbkGHONbQBBsBcj9dLyIUw; rs=6",
|
|
.ciphertextLen = 41,
|
|
.maxPlaintextLen = 9,
|
|
.err = ECE_ERROR_DECRYPT_PADDING,
|
|
},
|
|
{
|
|
.desc = "rs = 6, auth tag for last record",
|
|
.recvPrivKey = "\x9e\x13\x93\xf7\x5e\xf5\xc6\xea\x10\x04\x91\xa4\x89\x9d"
|
|
"\xda\xa9\x3e\x6a\xc3\xf2\x0b\x27\xde\x3f\x3c\xf8\x95\x36"
|
|
"\xed\x4b\x15\x26",
|
|
.authSecret =
|
|
"\xde\xb5\xa1\xb1\x10\x94\xfc\xa7\x5a\xa9\xf2\x8f\x6d\xdd\xf3\x05",
|
|
.ciphertext = "\x0b\xbb\xb7\x8f\x90\x0b\xe1\x8c\xe1\xdb\x26\x01\xfe\xe9\x8d"
|
|
"\xea\xdc\xeb\x54\x7c\x6b\xb7\xb0\xf9\x6d\xa4\xc4\x5b\xd0\xc4"
|
|
"\xd4\x19\x37\xba\x9f\x5f\x63\x8c",
|
|
.cryptoKey = "dh=BI38Qs_OhDmQIxbszc6Nako-MrX3FzAE_8HzxM1wgoEIG4ocxyF-"
|
|
"YAAVhfkpJUvDpRyKW2LDHIaoylaZuxQfRhE",
|
|
.encryption = "salt=QClh48OlvGpSjZ0Mg0e8rg; rs=6",
|
|
.ciphertextLen = 38,
|
|
.maxPlaintextLen = 6,
|
|
.err = ECE_ERROR_SHORT_BLOCK,
|
|
},
|
|
};
|
|
|
|
void
|
|
test_webpush_aesgcm_decrypt_ok(void) {
|
|
size_t length = sizeof(webpush_aesgcm_decrypt_ok_tests) /
|
|
sizeof(webpush_aesgcm_decrypt_ok_test_t);
|
|
for (size_t i = 0; i < length; i++) {
|
|
webpush_aesgcm_decrypt_ok_test_t t = webpush_aesgcm_decrypt_ok_tests[i];
|
|
|
|
const void* recvPrivKey = t.recvPrivKey;
|
|
const void* authSecret = t.authSecret;
|
|
const void* ciphertext = t.ciphertext;
|
|
|
|
uint8_t salt[ECE_SALT_LENGTH];
|
|
uint8_t rawSenderPubKey[ECE_WEBPUSH_PUBLIC_KEY_LENGTH];
|
|
uint32_t rs;
|
|
int err = ece_webpush_aesgcm_headers_extract_params(
|
|
t.cryptoKey, t.encryption, salt, ECE_SALT_LENGTH, rawSenderPubKey,
|
|
ECE_WEBPUSH_PUBLIC_KEY_LENGTH, &rs);
|
|
ece_assert(!err, "Got %d parsing crypto headers", err);
|
|
|
|
size_t plaintextLen = ece_aesgcm_plaintext_max_length(rs, t.ciphertextLen);
|
|
ece_assert(plaintextLen == t.maxPlaintextLen,
|
|
"Got plaintext max length %zu for `%s`; want %zu", plaintextLen,
|
|
t.desc, t.maxPlaintextLen);
|
|
|
|
uint8_t* plaintext = calloc(plaintextLen, sizeof(uint8_t));
|
|
|
|
err = ece_webpush_aesgcm_decrypt(
|
|
recvPrivKey, ECE_WEBPUSH_PRIVATE_KEY_LENGTH, authSecret,
|
|
ECE_WEBPUSH_AUTH_SECRET_LENGTH, salt, ECE_SALT_LENGTH, rawSenderPubKey,
|
|
ECE_WEBPUSH_PUBLIC_KEY_LENGTH, rs, ciphertext, t.ciphertextLen, plaintext,
|
|
&plaintextLen);
|
|
ece_assert(!err, "Got %d decrypting ciphertext for `%s`", err, t.desc);
|
|
|
|
ece_assert(plaintextLen == t.plaintextLen,
|
|
"Got plaintext length %zu for `%s`; want %zu", plaintextLen,
|
|
t.desc, t.plaintextLen);
|
|
ece_assert(!memcmp(plaintext, t.plaintext, plaintextLen),
|
|
"Wrong plaintext for `%s`", t.desc);
|
|
|
|
free(plaintext);
|
|
}
|
|
}
|
|
|
|
void
|
|
test_webpush_aesgcm_decrypt_err(void) {
|
|
size_t tests = sizeof(webpush_aesgcm_decrypt_err_tests) /
|
|
sizeof(webpush_aesgcm_decrypt_err_test_t);
|
|
for (size_t i = 0; i < tests; i++) {
|
|
webpush_aesgcm_decrypt_err_test_t t = webpush_aesgcm_decrypt_err_tests[i];
|
|
|
|
const void* recvPrivKey = t.recvPrivKey;
|
|
const void* authSecret = t.authSecret;
|
|
const void* ciphertext = t.ciphertext;
|
|
|
|
uint8_t salt[ECE_SALT_LENGTH];
|
|
uint8_t rawSenderPubKey[ECE_WEBPUSH_PUBLIC_KEY_LENGTH];
|
|
uint32_t rs;
|
|
int err = ece_webpush_aesgcm_headers_extract_params(
|
|
t.cryptoKey, t.encryption, salt, ECE_SALT_LENGTH, rawSenderPubKey,
|
|
ECE_WEBPUSH_PUBLIC_KEY_LENGTH, &rs);
|
|
ece_assert(!err, "Got %d parsing crypto headers", err);
|
|
|
|
size_t plaintextLen = ece_aesgcm_plaintext_max_length(rs, t.ciphertextLen);
|
|
ece_assert(plaintextLen == t.maxPlaintextLen,
|
|
"Got plaintext max length %zu for `%s`; want %zu", plaintextLen,
|
|
t.desc, t.maxPlaintextLen);
|
|
|
|
uint8_t* plaintext = calloc(plaintextLen, sizeof(uint8_t));
|
|
|
|
err = ece_webpush_aesgcm_decrypt(
|
|
recvPrivKey, ECE_WEBPUSH_PRIVATE_KEY_LENGTH, authSecret,
|
|
ECE_WEBPUSH_AUTH_SECRET_LENGTH, salt, ECE_SALT_LENGTH, rawSenderPubKey,
|
|
ECE_WEBPUSH_PUBLIC_KEY_LENGTH, rs, ciphertext, t.ciphertextLen, plaintext,
|
|
&plaintextLen);
|
|
ece_assert(err == t.err, "Got %d decrypting ciphertext for `%s`; want %d",
|
|
err, t.desc, t.err);
|
|
|
|
free(plaintext);
|
|
}
|
|
}
|