mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-10-14 00:59:29 +00:00
Merge pull request #1811
This commit is contained in:
commit
bf85deedb8
2
openpgp.d.ts
vendored
2
openpgp.d.ts
vendored
@ -916,6 +916,8 @@ export namespace enums {
|
||||
export enum aead {
|
||||
eax = 1,
|
||||
ocb = 2,
|
||||
gcm = 3,
|
||||
/** @deprecated use `gcm` instead */
|
||||
experimentalGCM = 100 // Private algorithm
|
||||
}
|
||||
|
||||
|
@ -468,12 +468,26 @@ export function generateSessionKey(algo) {
|
||||
/**
|
||||
* Get implementation of the given AEAD mode
|
||||
* @param {enums.aead} algo
|
||||
* @param {Boolean} [acceptExperimentalGCM] - whether to allow the non-standard, legacy `experimentalGCM` algo
|
||||
* @returns {Object}
|
||||
* @throws {Error} on invalid algo
|
||||
*/
|
||||
export function getAEADMode(algo) {
|
||||
const algoName = enums.read(enums.aead, algo);
|
||||
return mode[algoName];
|
||||
export function getAEADMode(algo, acceptExperimentalGCM = false) {
|
||||
switch (algo) {
|
||||
case enums.aead.eax:
|
||||
return mode.eax;
|
||||
case enums.aead.ocb:
|
||||
return mode.ocb;
|
||||
case enums.aead.gcm:
|
||||
return mode.gcm;
|
||||
case enums.aead.experimentalGCM:
|
||||
if (!acceptExperimentalGCM) {
|
||||
throw new Error('Unexpected non-standard `experimentalGCM` AEAD algorithm provided in `config.preferredAEADAlgorithm`: use `gcm` instead');
|
||||
}
|
||||
return mode.gcm;
|
||||
default:
|
||||
throw new Error('Unsupported AEAD mode');
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -154,7 +154,7 @@ export default {
|
||||
'SHA-512': 10
|
||||
},
|
||||
|
||||
/** {@link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-04#section-9.6|RFC4880bis-04, section 9.6}
|
||||
/** {@link https://www.rfc-editor.org/rfc/rfc9580.html#name-aead-algorithms}
|
||||
* @enum {Integer}
|
||||
* @readonly
|
||||
*/
|
||||
@ -162,6 +162,7 @@ export default {
|
||||
eax: 1,
|
||||
ocb: 2,
|
||||
gcm: 3,
|
||||
/** @deprecated used by OpenPGP.js v5 for legacy AEAD support; use `gcm` instead for the RFC9580-standardized ID */
|
||||
experimentalGCM: 100 // Private algorithm
|
||||
},
|
||||
|
||||
|
@ -78,7 +78,7 @@ class AEADEncryptedDataPacket {
|
||||
this.aeadAlgorithm = await reader.readByte();
|
||||
this.chunkSizeByte = await reader.readByte();
|
||||
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm);
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm, true);
|
||||
this.iv = await reader.readBytes(mode.ivLength);
|
||||
this.encrypted = reader.remainder();
|
||||
});
|
||||
@ -119,7 +119,7 @@ class AEADEncryptedDataPacket {
|
||||
async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
|
||||
this.cipherAlgorithm = sessionKeyAlgorithm;
|
||||
|
||||
const { ivLength } = crypto.getAEADMode(this.aeadAlgorithm);
|
||||
const { ivLength } = crypto.getAEADMode(this.aeadAlgorithm, true);
|
||||
this.iv = crypto.random.getRandomBytes(ivLength); // generate new random IV
|
||||
this.chunkSizeByte = config.aeadChunkSizeByte;
|
||||
const data = this.packets.write();
|
||||
|
@ -454,7 +454,7 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
|
||||
let cleartext;
|
||||
if (this.s2kUsage === 253) {
|
||||
const mode = crypto.getAEADMode(this.aead);
|
||||
const mode = crypto.getAEADMode(this.aead, true);
|
||||
const modeInstance = await mode(this.symmetric, key);
|
||||
try {
|
||||
const associateData = this.isLegacyAEAD ?
|
||||
|
@ -234,7 +234,10 @@ export async function runAEAD(packet, fn, key, data) {
|
||||
const isAEADP = !isSEIPDv2 && packet.constructor.tag === enums.packet.aeadEncryptedData; // no `instanceof` to avoid importing the corresponding class (circular import)
|
||||
if (!isSEIPDv2 && !isAEADP) throw new Error('Unexpected packet type');
|
||||
|
||||
const mode = crypto.getAEADMode(packet.aeadAlgorithm);
|
||||
// we allow `experimentalGCM` for AEADP for backwards compatibility, since v5 keys from OpenPGP.js v5 might be declaring
|
||||
// that preference, as the `gcm` ID had not been standardized at the time.
|
||||
// NB: AEADP are never automatically generate as part of message encryption by OpenPGP.js, the packet must be manually created.
|
||||
const mode = crypto.getAEADMode(packet.aeadAlgorithm, isAEADP);
|
||||
const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0;
|
||||
const tagLengthIfEncrypting = fn === 'encrypt' ? mode.tagLength : 0;
|
||||
const chunkSize = 2 ** (packet.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6))
|
||||
|
@ -105,7 +105,7 @@ class SymEncryptedSessionKeyPacket {
|
||||
offset += this.s2k.read(bytes.subarray(offset, bytes.length));
|
||||
|
||||
if (this.version >= 5) {
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm);
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm, true);
|
||||
|
||||
// A starting initialization vector of size specified by the AEAD
|
||||
// algorithm.
|
||||
@ -167,7 +167,7 @@ class SymEncryptedSessionKeyPacket {
|
||||
const key = await this.s2k.produceKey(passphrase, keySize);
|
||||
|
||||
if (this.version >= 5) {
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm);
|
||||
const mode = crypto.getAEADMode(this.aeadAlgorithm, true);
|
||||
const adata = new Uint8Array([0xC0 | SymEncryptedSessionKeyPacket.tag, this.version, this.sessionKeyEncryptionAlgorithm, this.aeadAlgorithm]);
|
||||
const encryptionKey = this.version === 6 ? await computeHKDF(enums.hash.sha256, key, new Uint8Array(), adata, keySize) : key;
|
||||
const modeInstance = await mode(algo, encryptionKey);
|
||||
|
@ -2286,7 +2286,7 @@ function versionSpecificTests() {
|
||||
openpgp.config.preferredSymmetricAlgorithm = openpgp.enums.symmetric.aes192;
|
||||
openpgp.config.preferredHashAlgorithm = openpgp.enums.hash.sha224;
|
||||
openpgp.config.preferredCompressionAlgorithm = openpgp.enums.compression.zlib;
|
||||
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM;
|
||||
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.eax;
|
||||
|
||||
const testPref = function(key) {
|
||||
const selfSignature = openpgp.config.v6Keys ? key.directSignatures[0] : key.users[0].selfCertifications[0];
|
||||
@ -2301,15 +2301,12 @@ function versionSpecificTests() {
|
||||
if (openpgp.config.aeadProtect) {
|
||||
const aead = openpgp.enums.aead;
|
||||
expect(selfSignature.preferredCipherSuites).to.eql([
|
||||
[sym.aes192, aead.experimentalGCM],
|
||||
[sym.aes256, aead.experimentalGCM],
|
||||
[sym.aes128, aead.experimentalGCM],
|
||||
[sym.aes192, aead.gcm],
|
||||
[sym.aes256, aead.gcm],
|
||||
[sym.aes128, aead.gcm],
|
||||
[sym.aes192, aead.eax],
|
||||
[sym.aes256, aead.eax],
|
||||
[sym.aes128, aead.eax],
|
||||
[sym.aes192, aead.gcm],
|
||||
[sym.aes256, aead.gcm],
|
||||
[sym.aes128, aead.gcm],
|
||||
[sym.aes192, aead.ocb],
|
||||
[sym.aes256, aead.ocb],
|
||||
[sym.aes128, aead.ocb]
|
||||
|
@ -1716,6 +1716,29 @@ aOU=
|
||||
}
|
||||
});
|
||||
|
||||
it('supports decrypting a legacy AEAD message encrypted by OpenPGP.js v5 with `experimentalGCM` (AEADEncryptedDataPacket)', async () => {
|
||||
const plaintext = 'test';
|
||||
const passphrase = 'passphrase';
|
||||
const messageLegacyAEAD = await openpgp.readMessage({
|
||||
armoredMessage: `-----BEGIN PGP MESSAGE-----
|
||||
|
||||
w0oFCWQDCMbFipDX5vyLAFXhzn5i6iGJY/4BhPed85Yl62F1j8JWGT/8Mw3/
|
||||
s7f058pohmXCztkTnrSo5+LUmRX8YwlGC5+5LbczD9Q8AQlkDHfOCyGb8NSF
|
||||
mnk1YJIgLeTgPF4F1TK1ead1VfPqvUHK2Z/FzlaY94wK9f8QcA9RUSvjoKGH
|
||||
BdPq
|
||||
=+vdf
|
||||
-----END PGP MESSAGE-----`,
|
||||
config: { enableParsingV5Entities: true }
|
||||
});
|
||||
|
||||
const { data: decryptedData } = await openpgp.decrypt({
|
||||
message: messageLegacyAEAD,
|
||||
passwords: passphrase
|
||||
});
|
||||
|
||||
expect(decryptedData).to.equal(plaintext);
|
||||
});
|
||||
|
||||
it('decrypt with `config.constantTimePKCS1Decryption` option should succeed', async function () {
|
||||
const publicKey = await openpgp.readKey({ armoredKey: pub_key });
|
||||
const publicKey2 = await openpgp.readKey({ armoredKey: eccPrivateKey });
|
||||
|
Loading…
x
Reference in New Issue
Block a user