larabr 3cdaab7894 Check session key size on v3 SKESK and PKESK packet decryption
For v3 SKESK and PKESK packets, the session key algorithm is part of the payload,
so we can check the session key size on packet decryption.
This is helpful to catch errors early, when using e.g. `decryptSessionKeys`.

In v6 packets, the session key size check can only be done on SEIPDv2 decryption.
2024-10-22 12:40:15 +02:00

175 lines
5.9 KiB
JavaScript

// Modified by ProtonTech AG
// Adapted from https://github.com/artjomb/cryptojs-extension/blob/8c61d159/test/eax.js
import sinon from 'sinon';
import { use as chaiUse, expect } from 'chai';
import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import
chaiUse(chaiAsPromised);
import openpgp from '../initOpenpgp.js';
import EAX from '../../src/crypto/mode/eax.js';
import util from '../../src/util.js';
function testAESEAX() {
it('Passes all test vectors', async function() {
const vectors = [
// From http://www.cs.ucdavis.edu/~rogaway/papers/eax.pdf ...
{
msg: '',
key: '233952DEE4D5ED5F9B9C6D6FF80FF478',
nonce: '62EC67F9C3A4A407FCB2A8C49031A8B3',
header: '6BFB914FD07EAE6B',
ct: 'E037830E8389F27B025A2D6527E79D01'
},
{
msg: 'F7FB',
key: '91945D3F4DCBEE0BF45EF52255F095A4',
nonce: 'BECAF043B0A23D843194BA972C66DEBD',
header: 'FA3BFD4806EB53FA',
ct: '19DD5C4C9331049D0BDAB0277408F67967E5'
},
{
msg: '1A47CB4933',
key: '01F74AD64077F2E704C0F60ADA3DD523',
nonce: '70C3DB4F0D26368400A10ED05D2BFF5E',
header: '234A3463C1264AC6',
ct: 'D851D5BAE03A59F238A23E39199DC9266626C40F80'
},
{
msg: '481C9E39B1',
key: 'D07CF6CBB7F313BDDE66B727AFD3C5E8',
nonce: '8408DFFF3C1A2B1292DC199E46B7D617',
header: '33CCE2EABFF5A79D',
ct: '632A9D131AD4C168A4225D8E1FF755939974A7BEDE'
},
{
msg: '40D0C07DA5E4',
key: '35B6D0580005BBC12B0587124557D2C2',
nonce: 'FDB6B06676EEDC5C61D74276E1F8E816',
header: 'AEB96EAEBE2970E9',
ct: '071DFE16C675CB0677E536F73AFE6A14B74EE49844DD'
},
{
msg: '4DE3B35C3FC039245BD1FB7D',
key: 'BD8E6E11475E60B268784C38C62FEB22',
nonce: '6EAC5C93072D8E8513F750935E46DA1B',
header: 'D4482D1CA78DCE0F',
ct: '835BB4F15D743E350E728414ABB8644FD6CCB86947C5E10590210A4F'
},
{
msg: '8B0A79306C9CE7ED99DAE4F87F8DD61636',
key: '7C77D6E813BED5AC98BAA417477A2E7D',
nonce: '1A8C98DCD73D38393B2BF1569DEEFC19',
header: '65D2017990D62528',
ct: '02083E3979DA014812F59F11D52630DA30137327D10649B0AA6E1C181DB617D7F2'
},
{
msg: '1BDA122BCE8A8DBAF1877D962B8592DD2D56',
key: '5FFF20CAFAB119CA2FC73549E20F5B0D',
nonce: 'DDE59B97D722156D4D9AFF2BC7559826',
header: '54B9F04E6A09189A',
ct: '2EC47B2C4954A489AFC7BA4897EDCDAE8CC33B60450599BD02C96382902AEF7F832A'
},
{
msg: '6CF36720872B8513F6EAB1A8A44438D5EF11',
key: 'A4A4782BCFFD3EC5E7EF6D8C34A56123',
nonce: 'B781FCF2F75FA5A8DE97A9CA48E522EC',
header: '899A175897561D7E',
ct: '0DE18FD0FDD91E7AF19F1D8EE8733938B1E8E7F6D2231618102FDB7FE55FF1991700'
},
{
msg: 'CA40D7446E545FFAED3BD12A740A659FFBBB3CEAB7',
key: '8395FCF1E95BEBD697BD010BC766AAC3',
nonce: '22E7ADD93CFC6393C57EC0B3C17D6B44',
header: '126735FCC320D25A',
ct: 'CB8920F87A6C75CFF39627B56E3ED197C552D295A7CFC46AFC253B4652B1AF3795B124AB6E'
}
];
const cipher = openpgp.enums.symmetric.aes128;
await Promise.all(vectors.map(async vec => {
const keyBytes = util.hexToUint8Array(vec.key);
const msgBytes = util.hexToUint8Array(vec.msg);
const nonceBytes = util.hexToUint8Array(vec.nonce);
const headerBytes = util.hexToUint8Array(vec.header);
const ctBytes = util.hexToUint8Array(vec.ct);
const eax = await EAX(cipher, keyBytes);
// encryption test
let ct = await eax.encrypt(msgBytes, nonceBytes, headerBytes);
expect(util.uint8ArrayToHex(ct)).to.equal(vec.ct.toLowerCase());
// decryption test with verification
let pt = await eax.decrypt(ctBytes, nonceBytes, headerBytes);
expect(util.uint8ArrayToHex(pt)).to.equal(vec.msg.toLowerCase());
// tampering detection test
ct = await eax.encrypt(msgBytes, nonceBytes, headerBytes);
ct[2] ^= 8;
pt = eax.decrypt(ct, nonceBytes, headerBytes);
await expect(pt).to.eventually.be.rejectedWith('Authentication tag mismatch');
// testing without additional data
ct = await eax.encrypt(msgBytes, nonceBytes, new Uint8Array());
pt = await eax.decrypt(ct, nonceBytes, new Uint8Array());
expect(util.uint8ArrayToHex(pt)).to.equal(vec.msg.toLowerCase());
// testing with multiple additional data
ct = await eax.encrypt(msgBytes, nonceBytes, util.concatUint8Array([headerBytes, headerBytes, headerBytes]));
pt = await eax.decrypt(ct, nonceBytes, util.concatUint8Array([headerBytes, headerBytes, headerBytes]));
expect(util.uint8ArrayToHex(pt)).to.equal(vec.msg.toLowerCase());
}));
});
}
/* eslint-disable no-invalid-this */
export default () => describe('Symmetric AES-EAX', function() {
let sinonSandbox;
let getWebCryptoStub;
let getNodeCryptoStub;
const disableNative = () => {
enableNative();
// stubbed functions return undefined
getWebCryptoStub = sinonSandbox.stub(util, 'getWebCrypto');
getNodeCryptoStub = sinonSandbox.stub(util, 'getNodeCrypto');
};
const enableNative = () => {
getWebCryptoStub && getWebCryptoStub.restore();
getNodeCryptoStub && getNodeCryptoStub.restore();
};
describe('Symmetric AES-EAX (native)', function() {
before(function () {
const detectNative = () => !!(util.getWebCrypto() || util.getNodeCrypto());
if (!detectNative()) { this.skip(); }
});
beforeEach(function () {
sinonSandbox = sinon.createSandbox();
enableNative();
});
afterEach(function () {
sinonSandbox.restore();
});
testAESEAX();
});
describe('Symmetric AES-EAX (non-native fallback)', function() {
beforeEach(function () {
sinonSandbox = sinon.createSandbox();
disableNative();
});
afterEach(function () {
sinonSandbox.restore();
});
testAESEAX();
});
});