test_crypto.js (25813B)
1 "use strict"; 2 3 const { getCryptoParamsFromHeaders, PushCrypto } = ChromeUtils.importESModule( 4 "resource://gre/modules/PushCrypto.sys.mjs" 5 ); 6 7 const REJECT_PADDING = { padding: "reject" }; 8 9 // A common key to decrypt some aesgcm and aesgcm128 messages. Other decryption 10 // tests have their own keys. 11 const LEGACY_PRIVATE_KEY = { 12 kty: "EC", 13 crv: "P-256", 14 d: "4h23G_KkXC9TvBSK2v0Q7ImpS2YAuRd8hQyN0rFAwBg", 15 x: "sd85ZCbEG6dEkGMCmDyGBIt454Qy-Yo-1xhbaT2Jlk4", 16 y: "vr3cKpQ-Sp1kpZ9HipNjUCwSA55yy0uM8N9byE8dmLs", 17 ext: true, 18 }; 19 20 const LEGACY_PUBLIC_KEY = 21 "BLHfOWQmxBunRJBjApg8hgSLeOeEMvmKPtcYW2k9iZZOvr3cKpQ-Sp1kpZ9HipNjUCwSA55yy0uM8N9byE8dmLs"; 22 23 async function assertDecrypts(test, headers) { 24 let privateKey = test.privateKey; 25 let publicKey = ChromeUtils.base64URLDecode(test.publicKey, REJECT_PADDING); 26 let authSecret = null; 27 if (test.authSecret) { 28 authSecret = ChromeUtils.base64URLDecode(test.authSecret, REJECT_PADDING); 29 } 30 let payload = ChromeUtils.base64URLDecode(test.data, REJECT_PADDING); 31 let result = await PushCrypto.decrypt( 32 privateKey, 33 publicKey, 34 authSecret, 35 headers, 36 payload 37 ); 38 let decoder = new TextDecoder("utf-8"); 39 equal(decoder.decode(new Uint8Array(result)), test.result, test.desc); 40 } 41 42 async function assertNotDecrypts(test, headers) { 43 let authSecret = null; 44 if (test.authSecret) { 45 authSecret = ChromeUtils.base64URLDecode(test.authSecret, REJECT_PADDING); 46 } 47 let data = ChromeUtils.base64URLDecode(test.data, REJECT_PADDING); 48 let publicKey = ChromeUtils.base64URLDecode(test.publicKey, REJECT_PADDING); 49 let promise = PushCrypto.decrypt( 50 test.privateKey, 51 publicKey, 52 authSecret, 53 headers, 54 data 55 ); 56 await Assert.rejects(promise, test.expected, test.desc); 57 } 58 59 add_setup(async function () { 60 // Per https://firefox-source-docs.mozilla.org/toolkit/components/glean/user/instrumentation_tests.html#xpcshell-tests 61 do_get_profile(); 62 Services.fog.initializeFOG(); 63 }); 64 65 add_task(async function test_crypto_getCryptoParamsFromHeaders() { 66 // These headers should parse correctly. 67 let shouldParse = [ 68 { 69 desc: "aesgcm with multiple keys", 70 headers: { 71 encoding: "aesgcm", 72 crypto_key: "keyid=p256dh;dh=Iy1Je2Kv11A,p256ecdsa=o2M8QfiEKuI", 73 encryption: "keyid=p256dh;salt=upk1yFkp1xI", 74 }, 75 params: { 76 senderKey: "Iy1Je2Kv11A", 77 salt: "upk1yFkp1xI", 78 rs: 4096, 79 }, 80 }, 81 { 82 desc: "aesgcm with quoted key param", 83 headers: { 84 encoding: "aesgcm", 85 crypto_key: 'dh="byfHbUffc-k"', 86 encryption: "salt=C11AvAsp6Gc", 87 }, 88 params: { 89 senderKey: "byfHbUffc-k", 90 salt: "C11AvAsp6Gc", 91 rs: 4096, 92 }, 93 }, 94 { 95 desc: "aesgcm with Crypto-Key and rs = 24", 96 headers: { 97 encoding: "aesgcm", 98 crypto_key: 'dh="ybuT4VDz-Bg"', 99 encryption: "salt=H7U7wcIoIKs; rs=24", 100 }, 101 params: { 102 senderKey: "ybuT4VDz-Bg", 103 salt: "H7U7wcIoIKs", 104 rs: 24, 105 }, 106 }, 107 { 108 desc: "aesgcm with Crypto-Key and rs = 2", 109 headers: { 110 encoding: "aesgcm", 111 crypto_key: "dh=LqrDQuVl9lY", 112 encryption: "salt=YngI8B7YapM; rs=2", 113 }, 114 params: { 115 senderKey: "LqrDQuVl9lY", 116 salt: "YngI8B7YapM", 117 rs: 2, 118 }, 119 }, 120 ]; 121 for (let test of shouldParse) { 122 let params = getCryptoParamsFromHeaders(test.headers); 123 let senderKey = ChromeUtils.base64URLDecode( 124 test.params.senderKey, 125 REJECT_PADDING 126 ); 127 let salt = ChromeUtils.base64URLDecode(test.params.salt, REJECT_PADDING); 128 deepEqual( 129 new Uint8Array(params.senderKey), 130 new Uint8Array(senderKey), 131 "Sender key should match for " + test.desc 132 ); 133 deepEqual( 134 new Uint8Array(params.salt), 135 new Uint8Array(salt), 136 "Salt should match for " + test.desc 137 ); 138 equal( 139 params.rs, 140 test.params.rs, 141 "Record size should match for " + test.desc 142 ); 143 } 144 145 // These headers should be rejected. 146 let shouldThrow = [ 147 { 148 desc: "aesgcm128 with Crypto-Key", 149 headers: { 150 encoding: "aesgcm128", 151 crypto_key: "keyid=v2; dh=VA6wmY1IpiE", 152 encryption: "keyid=v2; salt=F0Im7RtGgNY", 153 }, 154 exception: /Unexpected encoding/, 155 }, 156 { 157 desc: "aesgcm128 with Encryption-Key and rs = 2", 158 headers: { 159 encoding: "aesgcm128", 160 encryption_key: "keyid=legacy; dh=LqrDQuVl9lY", 161 encryption: "keyid=legacy; salt=YngI8B7YapM; rs=2", 162 }, 163 exception: /Unexpected encoding/, 164 }, 165 { 166 desc: "aesgcm128 with Encryption-Key", 167 headers: { 168 encoding: "aesgcm128", 169 encryption_key: "keyid=v2; dh=VA6wmY1IpiE", 170 encryption: "keyid=v2; salt=khtpyXhpDKM", 171 }, 172 exception: /Unexpected encoding/, 173 }, 174 { 175 desc: "Invalid encoding", 176 headers: { 177 encoding: "nonexistent", 178 }, 179 exception: /Unexpected encoding/, 180 }, 181 { 182 desc: "Missing salt header", 183 headers: { 184 encoding: "aesgcm", 185 crypto_key: "dh=pbmv1QkcEDY", 186 encryption: "dh=Esao8aTBfIk;rs=32", 187 }, 188 exception: /Invalid salt parameter/, 189 }, 190 { 191 desc: "Invalid record size", 192 headers: { 193 encoding: "aesgcm", 194 crypto_key: "dh=pbmv1QkcEDY", 195 encryption: "salt=Esao8aTBfIk;rs=bad", 196 }, 197 exception: /rs parameter must be/, 198 }, 199 { 200 desc: "Zero record size", 201 headers: { 202 encoding: "aesgcm", 203 crypto_key: "dh=pbmv1QkcEDY", 204 encryption: "salt=Esao8aTBfIk;rs=0", 205 }, 206 exception: /rs parameter must be/, 207 }, 208 { 209 desc: "Too big record size", 210 headers: { 211 encoding: "aesgcm", 212 crypto_key: "dh=pbmv1QkcEDY", 213 encryption: "salt=Esao8aTBfIk;rs=68719476736", 214 }, 215 exception: /rs parameter must be/, 216 }, 217 { 218 desc: "aesgcm with Encryption-Key", 219 headers: { 220 encoding: "aesgcm", 221 encryption_key: "dh=FplK5KkvUF0", 222 encryption: "salt=p6YHhFF3BQY", 223 }, 224 exception: /Missing Crypto-Key header/, 225 }, 226 ]; 227 for (let test of shouldThrow) { 228 throws( 229 () => getCryptoParamsFromHeaders(test.headers), 230 test.exception, 231 test.desc 232 ); 233 } 234 }); 235 236 add_task(async function test_aes128gcm_ok() { 237 Services.fog.testResetFOG(); 238 let expectedSuccesses = [ 239 { 240 desc: "Example from draft-ietf-webpush-encryption-latest", 241 result: "When I grow up, I want to be a watermelon", 242 data: "DGv6ra1nlYgDCS1FRnbzlwAAEABBBP4z9KsN6nGRTbVYI_c7VJSPQTBtkgcy27mlmlMoZIIgDll6e3vCYLocInmYWAmS6TlzAC8wEqKK6PBru3jl7A_yl95bQpu6cVPTpK4Mqgkf1CXztLVBSt2Ks3oZwbuwXPXLWyouBWLVWGNWQexSgSxsj_Qulcy4a-fN", 243 authSecret: "BTBZMqHH6r4Tts7J_aSIgg", 244 privateKey: { 245 kty: "EC", 246 crv: "P-256", 247 d: "q1dXpw3UpT5VOmu_cf_v6ih07Aems3njxI-JWgLcM94", 248 x: "JXGyvs3942BVGq8e0PTNNmwRzr5VX4m8t7GGpTM5FzE", 249 y: "aOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", 250 ext: true, 251 }, 252 publicKey: 253 "BCVxsr7N_eNgVRqvHtD0zTZsEc6-VV-JvLexhqUzORcxaOzi6-AYWXvTBHm4bjyPjs7Vd8pZGH6SRpkNtoIAiw4", 254 }, 255 { 256 desc: "rs = 24, pad = 0", 257 result: 258 "I am the very model of a modern Major-General; I've information vegetable, animal, and mineral", 259 data: "goagSH7PP0ZGwUsgShmdkwAAABhBBDJVyIuUJbOSVMeWHP8VNPnxY-dZSw86doqOkEzZZZY1ALBWVXTVf0rUDH3oi68I9Hrp-01zA-mr8XKWl5kcH8cX0KiV2PtCwdkEyaQ73YF5fsDxgoWDiaTA3wPqMvuLDqGsZWHnE9Psnfoy7UMEqKlh2a1nE7ZOXiXcOBHLNj260jYzSJnEPV2eXixSXfyWpaSJHAwfj4wVdAAocmViIg6ywk8wFB1hgJpnX2UVEU_qIOcaP6AOIOr1UUQPfosQqC2MEHe5u9gHXF5pi-E267LAlkoYefq01KV_xK_vjbxpw8GAYfSjQEm0L8FG-CN37c8pnQ2Yf61MkihaXac9OctfNeWq_22cN6hn4qsOq0F7QoWIiZqWhB1vS9cJ3KUlyPQvKI9cvevDxw0fJHWeTFzhuwT9BjdILjjb2Vkqc0-qTDOawqD4c8WXsvdGDQCec5Y1x3UhdQXdjR_mhXypxFM37OZTvKJBr1vPCpRXl-bI6iOd7KScgtMM1x5luKhGzZyz25HyuFyj1ec82A", 260 authSecret: "_tK2LDGoIt6be6agJ_nvGA", 261 privateKey: { 262 kty: "EC", 263 crv: "P-256", 264 d: "bGViEe3PvjjFJg8lcnLsqu71b2yqWGnZN9J2MTed-9s", 265 x: "auB0GHF0AZ2LAocFnvOXDS7EeCMopnzbg-tS21FMHrU", 266 y: "GpbhrW-_xKj3XhhXA-kDZSicKZ0kn0BuVhqzhLOB-Cc", 267 ext: true, 268 }, 269 publicKey: 270 "BGrgdBhxdAGdiwKHBZ7zlw0uxHgjKKZ824PrUttRTB61GpbhrW-_xKj3XhhXA-kDZSicKZ0kn0BuVhqzhLOB-Cc", 271 }, 272 { 273 desc: "rs = 49, pad = 84; ciphertext length falls on record boundary", 274 result: "Hello, world", 275 data: "-yiDzsHE_K3W0TcfbqSR4AAAADFBBC1EHuf5_2oDKaZJJ9BST9vnsixvtl4Qq0_cA4-UQgoMo_oo2tNshOyRoWLq4Hj6rSwc7XjegRPhlgKyDolPSXa5c-L89oL6DIzNmvPVv_Ht4W-tWjHOGdOLXh_h94pPrYQrvBAlTCxs3ZaitVKE2XLFPK2MO6yxD19X6w1KQzO2BBAroRfK4pEI-9n2Kai6aWDdAZRbOe03unBsQ0oQ_SvSCU_5JJvNrUUTX1_kX804Bx-LLTlBr9pDmBDXeqyvfOULVDJb9YyVAzN9BzeFoyPfo0M", 276 authSecret: "lfF1cOUI72orKtG09creMw", 277 privateKey: { 278 kty: "EC", 279 crv: "P-256", 280 d: "ZwBKTqgg3u2OSdtelIDmPT6jzOGujhpgYJcT1SfQAe8", 281 x: "AU6PFLktoHzgg7k_ljZ-h7IXpH9-8u6TqdNDqgY-V1o", 282 y: "nzDVnGkMajmz_IFbFQyn3RSWAXQTN7U1B6UfQbFzpyE", 283 ext: true, 284 }, 285 publicKey: 286 "BAFOjxS5LaB84IO5P5Y2foeyF6R_fvLuk6nTQ6oGPldanzDVnGkMajmz_IFbFQyn3RSWAXQTN7U1B6UfQbFzpyE", 287 }, 288 { 289 desc: "rs = 18, pad = 0", 290 result: "1", 291 data: "fK69vCCTjuNAqUbxvU9o8QAAABJBBDfP21Ij2fleqgL27ZQP8i6vBbNiLpSdw86fM15u-bJq6qzKD3QICos2RZLyzMbV7d1DAEtwuRiH0UTZ-pPxbDvH6mj0_VR6lOyoSxbhOKYIAXc", 292 authSecret: "1loE35Xy215gSDn3F9zeeQ", 293 privateKey: { 294 kty: "EC", 295 crv: "P-256", 296 d: "J0M_q4lws8tShLYRg--0YoZWLNKnMw2MrpYJEaVXHQw", 297 x: "UV1DJjVWUjmdoksr6SQeYztc8U-vDPOm_WAxe5VMCi8", 298 y: "SEhUgASyewz3SAvIEMa-wDqPt5yOoA_IsF4A-INFY-8", 299 ext: true, 300 }, 301 publicKey: 302 "BFFdQyY1VlI5naJLK-kkHmM7XPFPrwzzpv1gMXuVTAovSEhUgASyewz3SAvIEMa-wDqPt5yOoA_IsF4A-INFY-8", 303 }, 304 ]; 305 for (let test of expectedSuccesses) { 306 let privateKey = test.privateKey; 307 let publicKey = ChromeUtils.base64URLDecode(test.publicKey, { 308 padding: "reject", 309 }); 310 let authSecret = ChromeUtils.base64URLDecode(test.authSecret, { 311 padding: "reject", 312 }); 313 let payload = ChromeUtils.base64URLDecode(test.data, { 314 padding: "reject", 315 }); 316 let result = await PushCrypto.decrypt( 317 privateKey, 318 publicKey, 319 authSecret, 320 { 321 encoding: "aes128gcm", 322 }, 323 payload 324 ); 325 let decoder = new TextDecoder("utf-8"); 326 equal(decoder.decode(new Uint8Array(result)), test.result, test.desc); 327 } 328 Assert.equal( 329 Glean.webPush.contentEncoding.aes128gcm.testGetValue(), 330 4, 331 "Glean counter should be increased for each decrypt" 332 ); 333 }); 334 335 add_task(async function test_aes128gcm_err() { 336 let expectedFailures = [ 337 { 338 // Just the payload; no header at all. 339 desc: "Missing header block", 340 data: "RbdNK2m-mwdN47NaqH58FWEd", 341 privateKey: { 342 kty: "EC", 343 crv: "P-256", 344 d: "G-g_ODMu8JaB-vPzB7H_LhDKt4zHzatoOsDukqw_buE", 345 x: "26mRyiFTQ_Nr3T6FfK_ePRi_V_GDWygzutQU8IhBYgU", 346 y: "GslqCyRJADfQfPUo5OGOEAoaZOt0R0hUS_HiINq6zyw", 347 ext: true, 348 }, 349 publicKey: 350 "BNupkcohU0Pza90-hXyv3j0Yv1fxg1soM7rUFPCIQWIFGslqCyRJADfQfPUo5OGOEAoaZOt0R0hUS_HiINq6zyw", 351 authSecret: "NHG7mEgeAlM785VCvPPbpA", 352 expected: /Truncated header/, 353 }, 354 { 355 // The sender key should be 65 bytes; this header contains an invalid key 356 // that's only 1 byte. 357 desc: "Truncated sender key", 358 data: "3ltpa4fxoVy2revdedb5ngAAABIBALa8GCbDfJ9z3WtIWcK1BRgZUg", 359 privateKey: { 360 kty: "EC", 361 crv: "P-256", 362 d: "zojo4LMFekdS60yPqTHrYhwwLaWtA7ga9FnPZzVWDK4", 363 x: "oyXZkITEDeDOcioELESNlKMmkXIcp54890XnjGmIYZQ", 364 y: "sCzqGSJBdnlanU27sgc68szW-m8KTHxJaFVr5QKjuoE", 365 ext: true, 366 }, 367 publicKey: 368 "BKMl2ZCExA3gznIqBCxEjZSjJpFyHKeePPdF54xpiGGUsCzqGSJBdnlanU27sgc68szW-m8KTHxJaFVr5QKjuoE", 369 authSecret: "XDHg2W2aE5iZrAlp01n3QA", 370 expected: /Invalid sender public key/, 371 }, 372 { 373 // The message is encrypted with only the first 12 bytes of the 16-byte 374 // auth secret, so the derived decryption key and nonce won't match. 375 desc: "Encrypted with mismatched auth secret", 376 data: "gRX0mIuMOSp7rLQ8jxrFZQAAABJBBBmUSDxUHpvDmmrwP_cTqndFwoThOKQqJDW3l7IMS2mM9RGLT4VVMXwZDqvr-rdJwWTT9r3r4NRBcZExo1fYiQoTxNvUsW_z3VqD98ka1uBArEJzCn8LPNMkXp-Nb_McdR1BDP0", 377 privateKey: { 378 kty: "EC", 379 crv: "P-256", 380 d: "YMdjalF95wOaCsLQ4wZEAHlMeOfgSTmBKaInzuD5qAE", 381 x: "_dBBKKhcBYltf4H-EYvcuIe588H_QYOtxMgk0ShgcwA", 382 y: "6Yay37WmEOWvQ-QIoAcwWE-T49_d_ERzfV8I-y1viRY", 383 ext: true, 384 }, 385 publicKey: 386 "BP3QQSioXAWJbX-B_hGL3LiHufPB_0GDrcTIJNEoYHMA6Yay37WmEOWvQ-QIoAcwWE-T49_d_ERzfV8I-y1viRY", 387 authSecret: "NVo4zW2b7xWZDi0zCNvWAA", 388 expected: /Bad encryption/, 389 }, 390 { 391 // Multiple records; the first has padding delimiter = 2, but should be 1. 392 desc: "Early final record", 393 data: "2-IVUH0a09Lq6r6ubodNjwAAABJBBHvEND80qDSM3E5GL_x8QKpqjGGnOcTEHUUSVQX3Dp_F-e-oaFLdSI3Pjo6iyvt14Hq9XufJ1cA4uv7weVcbC9opRBHOmMdt0DHA5YBXekmAo3XkXtMEKb4OLunafm34aW0BuOw", 394 privateKey: { 395 kty: "EC", 396 crv: "P-256", 397 d: "XdodkYvEB7o82hLLgBTUmqfgJpACggMERmvIADTKkkA", 398 x: "yVxlINrRHo9qG_gDGkDCpO4QRcGQO-BqHfp_gpzOst4", 399 y: "Akga5r0EdhIbEsVTLQsjF4gHfvoGg6W_4NYjObJRyzU", 400 ext: true, 401 }, 402 publicKey: 403 "BMlcZSDa0R6Pahv4AxpAwqTuEEXBkDvgah36f4KczrLeAkga5r0EdhIbEsVTLQsjF4gHfvoGg6W_4NYjObJRyzU", 404 authSecret: "QMJB_eQmnuHm1yVZLZgnGA", 405 expected: /Padding is wrong!/, 406 }, 407 ]; 408 for (let test of expectedFailures) { 409 await assertNotDecrypts(test, { encoding: "aes128gcm" }); 410 } 411 }); 412 413 add_task(async function test_aesgcm_ok() { 414 Services.fog.testResetFOG(); 415 let expectedSuccesses = [ 416 { 417 desc: "padSize = 2, rs = 24, pad = 0", 418 result: "Some message", 419 data: "Oo34w2F9VVnTMFfKtdx48AZWQ9Li9M6DauWJVgXU", 420 authSecret: "aTDc6JebzR6eScy2oLo4RQ", 421 privateKey: LEGACY_PRIVATE_KEY, 422 publicKey: LEGACY_PUBLIC_KEY, 423 headers: { 424 crypto_key: 425 "dh=BCHFVrflyxibGLlgztLwKelsRZp4gqX3tNfAKFaxAcBhpvYeN1yIUMrxaDKiLh4LNKPtj0BOXGdr-IQ-QP82Wjo", 426 encryption: "salt=zCU18Rw3A5aB_Xi-vfixmA; rs=24", 427 encoding: "aesgcm", 428 }, 429 }, 430 { 431 desc: "padSize = 2, rs = 8, pad = 16", 432 result: "Yet another message", 433 data: "uEC5B_tR-fuQ3delQcrzrDCp40W6ipMZjGZ78USDJ5sMj-6bAOVG3AK6JqFl9E6AoWiBYYvMZfwThVxmDnw6RHtVeLKFM5DWgl1EwkOohwH2EhiDD0gM3io-d79WKzOPZE9rDWUSv64JstImSfX_ADQfABrvbZkeaWxh53EG59QMOElFJqHue4dMURpsMXg", 434 authSecret: "6plwZnSpVUbF7APDXus3UQ", 435 privateKey: LEGACY_PRIVATE_KEY, 436 publicKey: LEGACY_PUBLIC_KEY, 437 headers: { 438 crypto_key: 439 "dh=BEaA4gzA3i0JDuirGhiLgymS4hfFX7TNTdEhSk_HBlLpkjgCpjPL5c-GL9uBGIfa_fhGNKKFhXz1k9Kyens2ZpQ", 440 encryption: "salt=ZFhzj0S-n29g9P2p4-I7tA; rs=8", 441 encoding: "aesgcm", 442 }, 443 }, 444 { 445 desc: "padSize = 2, rs = 3, pad = 0", 446 result: "Small record size", 447 data: "oY4e5eDatDVt2fpQylxbPJM-3vrfhDasfPc8Q1PWt4tPfMVbz_sDNL_cvr0DXXkdFzS1lxsJsj550USx4MMl01ihjImXCjrw9R5xFgFrCAqJD3GwXA1vzS4T5yvGVbUp3SndMDdT1OCcEofTn7VC6xZ-zP8rzSQfDCBBxmPU7OISzr8Z4HyzFCGJeBfqiZ7yUfNlKF1x5UaZ4X6iU_TXx5KlQy_toV1dXZ2eEAMHJUcSdArvB6zRpFdEIxdcHcJyo1BIYgAYTDdAIy__IJVCPY_b2CE5W_6ohlYKB7xDyH8giNuWWXAgBozUfScLUVjPC38yJTpAUi6w6pXgXUWffende5FreQpnMFL1L4G-38wsI_-ISIOzdO8QIrXHxmtc1S5xzYu8bMqSgCinvCEwdeGFCmighRjj8t1zRWo0D14rHbQLPR_b1P5SvEeJTtS9Nm3iibM", 448 authSecret: "g2rWVHUCpUxgcL9Tz7vyeQ", 449 privateKey: LEGACY_PRIVATE_KEY, 450 publicKey: LEGACY_PUBLIC_KEY, 451 headers: { 452 crypto_key: 453 "dh=BCg6ZIGuE2ZNm2ti6Arf4CDVD_8--aLXAGLYhpghwjl1xxVjTLLpb7zihuEOGGbyt8Qj0_fYHBP4ObxwJNl56bk", 454 encryption: "salt=5LIDBXbvkBvvb7ZdD-T4PQ; rs=3", 455 encoding: "aesgcm", 456 }, 457 }, 458 { 459 desc: "Example from draft-ietf-httpbis-encryption-encoding-02", 460 result: "I am the walrus", 461 data: "6nqAQUME8hNqw5J3kl8cpVVJylXKYqZOeseZG8UueKpA", 462 authSecret: "R29vIGdvbyBnJyBqb29iIQ", 463 privateKey: { 464 kty: "EC", 465 crv: "P-256", 466 d: "9FWl15_QUQAWDaD3k3l50ZBZQJ4au27F1V4F0uLSD_M", 467 x: "ISQGPMvxncL6iLZDugTm3Y2n6nuiyMYuD3epQ_TC-pE", 468 y: "T21EEWyf0cQDQcakQMqz4hQKYOQ3il2nNZct4HgAUQU", 469 ext: true, 470 }, 471 publicKey: 472 "BCEkBjzL8Z3C-oi2Q7oE5t2Np-p7osjGLg93qUP0wvqRT21EEWyf0cQDQcakQMqz4hQKYOQ3il2nNZct4HgAUQU", 473 headers: { 474 crypto_key: 475 'keyid="dhkey"; dh="BNoRDbb84JGm8g5Z5CFxurSqsXWJ11ItfXEWYVLE85Y7CYkDjXsIEc4aqxYaQ1G8BqkXCJ6DPpDrWtdWj_mugHU"', 476 encryption: 'keyid="dhkey"; salt="lngarbyKfMoi9Z75xYXmkg"', 477 encoding: "aesgcm", 478 }, 479 }, 480 ]; 481 for (let test of expectedSuccesses) { 482 await assertDecrypts(test, test.headers); 483 } 484 Assert.equal( 485 Glean.webPush.contentEncoding.aesgcm.testGetValue(), 486 4, 487 "Glean counter should be increased for each decrypt" 488 ); 489 }); 490 491 add_task(async function test_aesgcm_err() { 492 let expectedFailures = [ 493 { 494 desc: "aesgcm128 message decrypted as aesgcm", 495 data: "fwkuwTTChcLnrzsbDI78Y2EoQzfnbMI8Ax9Z27_rwX8", 496 authSecret: "BhbpNTWyO5wVJmVKTV6XaA", 497 privateKey: LEGACY_PRIVATE_KEY, 498 publicKey: LEGACY_PUBLIC_KEY, 499 headers: { 500 crypto_key: 501 "dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0", 502 encryption: "salt=c6JQl9eJ0VvwrUVCQDxY7Q", 503 encoding: "aesgcm", 504 }, 505 expected: /Bad encryption/, 506 }, 507 { 508 // The plaintext is "O hai". The ciphertext is exactly `rs + 16` bytes, 509 // but we didn't include the empty trailing block that aesgcm requires for 510 // exact multiples. 511 desc: "rs = 7, no trailing block", 512 data: "YG4F-b06y590hRlnSsw_vuOw62V9Iz8", 513 authSecret: "QoDi0u6vcslIVJKiouXMXw", 514 privateKey: { 515 kty: "EC", 516 crv: "P-256", 517 d: "2bu4paOAZbL2ef1u-wTzONuTIcDPc00o0zUJgg46XTc", 518 x: "uEvLZUMVn1my0cwnLdcFT0mj1gSU5uzI3HeGwXC7jX8", 519 y: "SfNVLGL-FurydsuzciDfw8K1cUHyoDWnJJ_16UG6Dbo", 520 ext: true, 521 }, 522 publicKey: 523 "BLhLy2VDFZ9ZstHMJy3XBU9Jo9YElObsyNx3hsFwu41_SfNVLGL-FurydsuzciDfw8K1cUHyoDWnJJ_16UG6Dbo", 524 headers: { 525 crypto_key: 526 "dh=BD_bsTUpxBMvSv8eksith3vijMLj44D4jhJjO51y7wK1ytbUlsyYBBYYyB5AAe5bnREA_WipTgemDVz00LiWcfM", 527 encryption: "salt=xKWvs_jWWeg4KOsot_uBhA; rs=7", 528 encoding: "aesgcm", 529 }, 530 expected: /Encrypted data truncated/, 531 }, 532 { 533 // The last block is only 1 byte, but valid blocks must be at least 2 bytes. 534 desc: "Pad size > last block length", 535 data: "JvX9HsJ4lL5gzP8_uCKc6s15iRIaNhD4pFCgq5-dfwbUqEcNUkqv", 536 authSecret: "QtGZeY8MQfCaq-XwKOVGBQ", 537 privateKey: { 538 kty: "EC", 539 crv: "P-256", 540 d: "CosERAVXgvTvoh7UkrRC2V-iXoNs0bXle9I68qzkles", 541 x: "_D0YqEwirvTJQJdjG6xXrjstMVpeAzf221cUqZz6hgY", 542 y: "9MnFbM7U14uiYMDI5e2I4jN29tYmsM9F66QodhKmA-c", 543 ext: true, 544 }, 545 publicKey: 546 "BPw9GKhMIq70yUCXYxusV647LTFaXgM39ttXFKmc-oYG9MnFbM7U14uiYMDI5e2I4jN29tYmsM9F66QodhKmA-c", 547 headers: { 548 crypto_key: 549 "dh=BBNZNEi5Ew_ID5S4Y9jWBi1NeVDje6Mjs7SDLViUn6A8VAZj-6X3QAuYQ3j20BblqjwTgYst7PRnY6UGrKyLbmU", 550 encryption: "salt=ot8hzbwOo6CYe6ZhdlwKtg; rs=6", 551 encoding: "aesgcm", 552 }, 553 expected: /Decoded array is too short/, 554 }, 555 { 556 // The last block is 3 bytes (2 bytes for the pad length; 1 byte of data), 557 // but claims its pad length is 2. 558 desc: "Padding length > last block length", 559 data: "oWSOFA-UO5oWq-kI79RHaFfwAejLiQJ4C7eTmrSTBl4gArLXfx7lZ-Y", 560 authSecret: "gKG_P6-de5pyzS8hyH_NyQ", 561 privateKey: { 562 kty: "EC", 563 crv: "P-256", 564 d: "9l-ahcBM-I0ykwbWiDS9KRrPdhyvTZ0SxKiPpj2aeaI", 565 x: "qx0tU4EDaQv6ayFA3xvLLBdMmn4mLxjn7SK6mIeIxeg", 566 y: "ymbMcmUOEyh_-rLrBsi26NG4UFCis2MTDs5FG2VdDPI", 567 ext: true, 568 }, 569 publicKey: 570 "BKsdLVOBA2kL-mshQN8byywXTJp-Ji8Y5-0iupiHiMXoymbMcmUOEyh_-rLrBsi26NG4UFCis2MTDs5FG2VdDPI", 571 headers: { 572 crypto_key: 573 "dh=BKe2IBO_cwmEzQyTVscSbQcj0Y3uBSzGZ_mHlANMciS8uGpb7U8_Bw7TNdlYfpwWDLd0cxM8YYWNDbNJ_p2Rp4o", 574 encryption: "salt=z7QJ6UR89SiFRkd4RsC4Vg; rs=6", 575 encoding: "aesgcm", 576 }, 577 expected: /Padding is wrong/, 578 }, 579 { 580 // The first block has no padding, but claims its pad length is 1. 581 desc: "Non-zero padding", 582 data: "Qdvjh0LkZXKu_1Hvv56D0rOSF6Mww3y0F8xkxUNlwVu2U1iakOUUGRs", 583 authSecret: "cMpWQW58BrpDbJ8KqbS9ig", 584 privateKey: { 585 kty: "EC", 586 crv: "P-256", 587 d: "IzuaxLqFJmjSu8GjLCo2oEaDZjDButW4m4T0qx02XsM", 588 x: "Xy7vt_TJTynxwWsQyY069BcKmrhkRjhKPFuTi-AphoY", 589 y: "0M10IVM1ourR7Q5AUX2b2fgdmGyTWcYsdHcdFK_b4Hk", 590 ext: true, 591 }, 592 publicKey: 593 "BF8u77f0yU8p8cFrEMmNOvQXCpq4ZEY4Sjxbk4vgKYaG0M10IVM1ourR7Q5AUX2b2fgdmGyTWcYsdHcdFK_b4Hk", 594 headers: { 595 crypto_key: 596 "dh=BBicj01QI0ryiFzAaty9VpW_crgq9XbU1bOCtEZI9UNE6tuOgp4lyN_UN0N905ECnLWK5v_sCPUIxnQgOuCseSo", 597 encryption: "salt=SbkGHONbQBBsBcj9dLyIUw; rs=6", 598 encoding: "aesgcm", 599 }, 600 expected: /Padding is wrong/, 601 }, 602 { 603 // The first record is 22 bytes: 2 bytes for the pad length, 4 bytes of 604 // data, and a 16-byte auth tag. The second "record" is missing the pad 605 // and data, and contains just the auth tag. 606 desc: "rs = 6, second record truncated to only auth tag", 607 data: "C7u3j5AL4Yzh2yYB_umN6tzrVHxrt7D5baTEW9DE1Bk3up9fY4w", 608 authSecret: "3rWhsRCU_KdaqfKPbd3zBQ", 609 privateKey: { 610 kty: "EC", 611 crv: "P-256", 612 d: "nhOT9171xuoQBJGkiZ3aqT5qw_ILJ94_PPiVNu1LFSY", 613 x: "lCj7ctQTmRfwzTMcODlNfHjFMAHmgdI44OhTQXX_xpE", 614 y: "WBdgz4GWGtGAisC63O9DtP5l--hnCzPZiV-YZ-a6Lcw", 615 ext: true, 616 }, 617 publicKey: 618 "BJQo-3LUE5kX8M0zHDg5TXx4xTAB5oHSOODoU0F1_8aRWBdgz4GWGtGAisC63O9DtP5l--hnCzPZiV-YZ-a6Lcw", 619 headers: { 620 crypto_key: 621 "dh=BI38Qs_OhDmQIxbszc6Nako-MrX3FzAE_8HzxM1wgoEIG4ocxyF-YAAVhfkpJUvDpRyKW2LDHIaoylaZuxQfRhE", 622 encryption: "salt=QClh48OlvGpSjZ0Mg0e8rg; rs=6", 623 encoding: "aesgcm", 624 }, 625 expected: /Decoded array is too short/, 626 }, 627 ]; 628 for (let test of expectedFailures) { 629 await assertNotDecrypts(test, test.headers); 630 } 631 }); 632 633 add_task(async function test_aesgcm128_err() { 634 let expectedFailures = [ 635 { 636 desc: "padSize = 1, rs = 4096, pad = 2", 637 result: "aesgcm128 encrypted message", 638 data: "ljBJ44NPzJFH9EuyT5xWMU4vpZ90MdAqaq1TC1kOLRoPNHtNFXeJ0GtuSaE", 639 privateKey: LEGACY_PRIVATE_KEY, 640 publicKey: LEGACY_PUBLIC_KEY, 641 headers: { 642 encryption_key: 643 "dh=BOmnfg02vNd6RZ7kXWWrCGFF92bI-rQ-bV0Pku3-KmlHwbGv4ejWqgasEdLGle5Rhmp6SKJunZw2l2HxKvrIjfI", 644 encryption: "salt=btxxUtclbmgcc30b9rT3Bg; rs=4096", 645 encoding: "aesgcm128", 646 }, 647 expected: /Unsupported Content-Encoding/, 648 }, 649 { 650 // aesgcm128 doesn't use an auth secret, but we've mixed one in during 651 // encryption, so the decryption key and nonce won't match. 652 desc: "padSize = 1, rs = 4096, auth secret, pad = 8", 653 data: "h0FmyldY8aT5EQ6CJrbfRn_IdDvytoLeHb9_q5CjtdFRfgDRknxLmOzavLaVG4oOiS0r", 654 authSecret: "Sxb6u0gJIhGEogyLawjmCw", 655 privateKey: LEGACY_PRIVATE_KEY, 656 publicKey: LEGACY_PUBLIC_KEY, 657 headers: { 658 crypto_key: 659 "dh=BCXHk7O8CE-9AOp6xx7g7c-NCaNpns1PyyHpdcmDaijLbT6IdGq0ezGatBwtFc34BBfscFxdk4Tjksa2Mx5rRCM", 660 encryption: "salt=aGBpoKklLtrLcAUCcCr7JQ", 661 encoding: "aesgcm128", 662 }, 663 expected: /Unsupported Content-Encoding/, 664 }, 665 { 666 // The first byte of each record must be the pad length. 667 desc: "Missing padding", 668 data: "anvsHj7oBQTPMhv7XSJEsvyMS4-8EtbC7HgFZsKaTg", 669 privateKey: LEGACY_PRIVATE_KEY, 670 publicKey: LEGACY_PUBLIC_KEY, 671 headers: { 672 crypto_key: 673 "dh=BMSqfc3ohqw2DDgu3nsMESagYGWubswQPGxrW1bAbYKD18dIHQBUmD3ul_lu7MyQiT5gNdzn5JTXQvCcpf-oZE4", 674 encryption: "salt=Czx2i18rar8XWOXAVDnUuw", 675 encoding: "aesgcm128", 676 }, 677 expected: /Unsupported Content-Encoding/, 678 }, 679 { 680 desc: "Truncated input", 681 data: "AlDjj6NvT5HGyrHbT8M5D6XBFSra6xrWS9B2ROaCIjwSu3RyZ1iyuv0", 682 privateKey: LEGACY_PRIVATE_KEY, 683 publicKey: LEGACY_PUBLIC_KEY, 684 headers: { 685 crypto_key: 686 "dh=BCHn-I-J3dfPRLJBlNZ3xFoAqaBLZ6qdhpaz9W7Q00JW1oD-hTxyEECn6KYJNK8AxKUyIDwn6Icx_PYWJiEYjQ0", 687 encryption: "salt=c6JQl9eJ0VvwrUVCQDxY7Q; rs=25", 688 encoding: "aesgcm128", 689 }, 690 expected: /Unsupported Content-Encoding/, 691 }, 692 { 693 desc: "Padding length > rs", 694 data: "Ct_h1g7O55e6GvuhmpjLsGnv8Rmwvxgw8iDESNKGxk_8E99iHKDzdV8wJPyHA-6b2E6kzuVa5UWiQ7s4Zms1xzJ4FKgoxvBObXkc_r_d4mnb-j245z3AcvRmcYGk5_HZ0ci26SfhAN3lCgxGzTHS4nuHBRkGwOb4Tj4SFyBRlLoTh2jyVK2jYugNjH9tTrGOBg7lP5lajLTQlxOi91-RYZSfFhsLX3LrAkXuRoN7G1CdiI7Y3_eTgbPIPabDcLCnGzmFBTvoJSaQF17huMl_UnWoCj2WovA4BwK_TvWSbdgElNnQ4CbArJ1h9OqhDOphVu5GUGr94iitXRQR-fqKPMad0ULLjKQWZOnjuIdV1RYEZ873r62Yyd31HoveJcSDb1T8l_QK2zVF8V4k0xmK9hGuC0rF5YJPYPHgl5__usknzxMBnRrfV5_MOL5uPZwUEFsu", 695 privateKey: LEGACY_PRIVATE_KEY, 696 publicKey: LEGACY_PUBLIC_KEY, 697 headers: { 698 crypto_key: 699 "dh=BAcMdWLJRGx-kPpeFtwqR3GE1LWzd1TYh2rg6CEFu53O-y3DNLkNe_BtGtKRR4f7ZqpBMVS6NgfE2NwNPm3Ndls", 700 encryption: "salt=NQVTKhB0rpL7ZzKkotTGlA; rs=1", 701 encoding: "aesgcm128", 702 }, 703 expected: /Unsupported Content-Encoding/, 704 }, 705 ]; 706 for (let test of expectedFailures) { 707 await assertNotDecrypts(test, test.headers); 708 } 709 });