Implement SEIPD v2

This commit is contained in:
Daniel Huigens 2022-12-13 17:30:29 +01:00 committed by larabr
parent 939622e827
commit 9d85938ed7
9 changed files with 238 additions and 150 deletions

View File

@ -9,7 +9,7 @@ import util from '../util';
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
export default async function HKDF(hashAlgo, inputKey, salt, info, outLen) {
export default async function computeHKDF(hashAlgo, inputKey, salt, info, outLen) {
const hash = enums.read(enums.webHash, hashAlgo);
if (!hash) throw new Error('Hash algo not supported with HKDF');

View File

@ -308,7 +308,7 @@ export async function isAEADSupported(keys, date = new Date(), userIDs = [], con
await Promise.all(keys.map(async function(key, i) {
const selfCertification = await key.getPrimarySelfSignature(date, userIDs[i], config);
if (!selfCertification.features ||
!(selfCertification.features[0] & enums.features.aead)) {
!(selfCertification.features[0] & enums.features.seipdv2)) {
supported = false;
}
}));

View File

@ -392,13 +392,12 @@ export class Message {
const msg = await Message.encryptSessionKey(sessionKeyData, algorithmName, aeadAlgorithmName, encryptionKeys, passwords, wildcard, encryptionKeyIDs, date, userIDs, config);
let symEncryptedPacket;
const symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket();
if (aeadAlgorithmName) {
symEncryptedPacket = new AEADEncryptedDataPacket();
symEncryptedPacket.version = 2;
symEncryptedPacket.aeadAlgorithm = enums.write(enums.aead, aeadAlgorithmName);
} else {
symEncryptedPacket = new SymEncryptedIntegrityProtectedDataPacket();
}
symEncryptedPacket.packets = this.packets;
const algorithm = enums.write(enums.symmetric, algorithmName);

View File

@ -21,6 +21,7 @@ import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';
import { UnsupportedError } from './packet';
import { runAEAD } from './sym_encrypted_integrity_protected_data';
import LiteralDataPacket from './literal_data';
import CompressedDataPacket from './compressed_data';
@ -101,7 +102,7 @@ class AEADEncryptedDataPacket {
*/
async decrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
this.packets = await PacketList.fromBinary(
await this.crypt('decrypt', key, stream.clone(this.encrypted)),
await runAEAD(this, 'decrypt', key, stream.clone(this.encrypted)),
allowedPackets,
config
);
@ -122,86 +123,7 @@ class AEADEncryptedDataPacket {
this.iv = crypto.random.getRandomBytes(ivLength); // generate new random IV
this.chunkSizeByte = config.aeadChunkSizeByte;
const data = this.packets.write();
this.encrypted = await this.crypt('encrypt', key, data);
}
/**
* En/decrypt the payload.
* @param {encrypt|decrypt} fn - Whether to encrypt or decrypt
* @param {Uint8Array} key - The session key used to en/decrypt the payload
* @param {Uint8Array | ReadableStream<Uint8Array>} data - The data to en/decrypt
* @returns {Promise<Uint8Array | ReadableStream<Uint8Array>>}
* @async
*/
async crypt(fn, key, data) {
const mode = crypto.getAEADMode(this.aeadAlgorithm);
const modeInstance = await mode(this.cipherAlgorithm, key);
const tagLengthIfDecrypting = fn === 'decrypt' ? mode.tagLength : 0;
const tagLengthIfEncrypting = fn === 'encrypt' ? mode.tagLength : 0;
const chunkSize = 2 ** (this.chunkSizeByte + 6) + tagLengthIfDecrypting; // ((uint64_t)1 << (c + 6))
const adataBuffer = new ArrayBuffer(21);
const adataArray = new Uint8Array(adataBuffer, 0, 13);
const adataTagArray = new Uint8Array(adataBuffer);
const adataView = new DataView(adataBuffer);
const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8);
adataArray.set([0xC0 | AEADEncryptedDataPacket.tag, this.version, this.cipherAlgorithm, this.aeadAlgorithm, this.chunkSizeByte], 0);
let chunkIndex = 0;
let latestPromise = Promise.resolve();
let cryptedBytes = 0;
let queuedBytes = 0;
const iv = this.iv;
return stream.transformPair(data, async (readable, writable) => {
if (util.isStream(readable) !== 'array') {
const buffer = new TransformStream({}, {
highWaterMark: util.getHardwareConcurrency() * 2 ** (this.chunkSizeByte + 6),
size: array => array.length
});
stream.pipe(buffer.readable, writable);
writable = buffer.writable;
}
const reader = stream.getReader(readable);
const writer = stream.getWriter(writable);
try {
while (true) {
let chunk = await reader.readBytes(chunkSize + tagLengthIfDecrypting) || new Uint8Array();
const finalChunk = chunk.subarray(chunk.length - tagLengthIfDecrypting);
chunk = chunk.subarray(0, chunk.length - tagLengthIfDecrypting);
let cryptedPromise;
let done;
if (!chunkIndex || chunk.length) {
reader.unshift(finalChunk);
cryptedPromise = modeInstance[fn](chunk, mode.getNonce(iv, chunkIndexArray), adataArray);
queuedBytes += chunk.length - tagLengthIfDecrypting + tagLengthIfEncrypting;
} else {
// After the last chunk, we either encrypt a final, empty
// data chunk to get the final authentication tag or
// validate that final authentication tag.
adataView.setInt32(13 + 4, cryptedBytes); // Should be setInt64(13, ...)
cryptedPromise = modeInstance[fn](finalChunk, mode.getNonce(iv, chunkIndexArray), adataTagArray);
queuedBytes += tagLengthIfEncrypting;
done = true;
}
cryptedBytes += chunk.length - tagLengthIfDecrypting;
// eslint-disable-next-line no-loop-func
latestPromise = latestPromise.then(() => cryptedPromise).then(async crypted => {
await writer.ready;
await writer.write(crypted);
queuedBytes -= crypted.length;
}).catch(err => writer.abort(err));
if (done || queuedBytes > writer.desiredSize) {
await latestPromise; // Respect backpressure
}
if (!done) {
adataView.setInt32(5 + 4, ++chunkIndex); // Should be setInt64(5, ...)
} else {
await writer.close();
break;
}
}
} catch (e) {
await writer.abort(e);
}
});
this.encrypted = await runAEAD(this, 'encrypt', key, data);
}
}

View File

@ -17,6 +17,7 @@
import * as stream from '@openpgp/web-stream-tools';
import crypto from '../crypto';
import computeHKDF from '../crypto/hkdf';
import enums from '../enums';
import util from '../util';
import defaultConfig from '../config';
@ -36,8 +37,6 @@ const allowedPackets = /*#__PURE__*/ util.constructAllowedPackets([
SignaturePacket
]);
const VERSION = 1; // A one-octet version number of the data packet.
/**
* Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18)
*
@ -54,28 +53,55 @@ class SymEncryptedIntegrityProtectedDataPacket {
}
constructor() {
this.version = VERSION;
this.version = 1;
// The following 4 fields are for V2 only.
/** @type {enums.symmetric} */
this.cipherAlgorithm = null;
/** @type {enums.aead} */
this.aeadAlgorithm = null;
this.chunkSizeByte = null;
this.salt = null;
this.encrypted = null;
this.packets = null;
}
async read(bytes) {
await stream.parse(bytes, async reader => {
const version = await reader.readByte();
// - A one-octet version number. The only currently defined value is 1.
if (version !== VERSION) {
this.version = await reader.readByte();
// - A one-octet version number with value 1 or 2.
if (this.version !== 1 && this.version !== 2) {
throw new UnsupportedError(`Version ${version} of the SEIP packet is unsupported.`);
}
if (this.version === 2) {
// - A one-octet cipher algorithm.
this.cipherAlgorithm = await reader.readByte();
// - A one-octet AEAD algorithm.
this.aeadAlgorithm = await reader.readByte();
// - A one-octet chunk size.
this.chunkSizeByte = await reader.readByte();
// - Thirty-two octets of salt. The salt is used to derive the message key and must be unique.
this.salt = await reader.readBytes(32);
}
// For V1:
// - Encrypted data, the output of the selected symmetric-key cipher
// operating in Cipher Feedback mode with shift amount equal to the
// block size of the cipher (CFB-n where n is the block size).
// For V2:
// - Encrypted data, the output of the selected symmetric-key cipher operating in the given AEAD mode.
// - A final, summary authentication tag for the AEAD mode.
this.encrypted = reader.remainder();
});
}
write() {
return util.concat([new Uint8Array([VERSION]), this.encrypted]);
if (this.version === 2) {
return util.concat([new Uint8Array([this.version, this.cipherAlgorithm, this.aeadAlgorithm, this.chunkSizeByte]), this.salt, this.encrypted]);
}
return util.concat([new Uint8Array([this.version]), this.encrypted]);
}
/**
@ -88,18 +114,27 @@ class SymEncryptedIntegrityProtectedDataPacket {
* @async
*/
async encrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);
let bytes = this.packets.write();
if (stream.isArrayStream(bytes)) bytes = await stream.readToEnd(bytes);
const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
const tohash = util.concat([prefix, bytes, mdc]);
const hash = await crypto.hash.sha1(stream.passiveClone(tohash));
const plaintext = util.concat([tohash, hash]);
if (this.version === 2) {
this.cipherAlgorithm = sessionKeyAlgorithm;
this.encrypted = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(blockSize), config);
this.salt = crypto.random.getRandomBytes(32);
this.chunkSizeByte = config.aeadChunkSizeByte;
this.encrypted = await runAEAD(this, 'encrypt', key, bytes);
} else {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);
const prefix = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
const tohash = util.concat([prefix, bytes, mdc]);
const hash = await crypto.hash.sha1(stream.passiveClone(tohash));
const plaintext = util.concat([tohash, hash]);
this.encrypted = await crypto.mode.cfb.encrypt(sessionKeyAlgorithm, key, plaintext, new Uint8Array(blockSize), config);
}
return true;
}
@ -113,33 +148,152 @@ class SymEncryptedIntegrityProtectedDataPacket {
* @async
*/
async decrypt(sessionKeyAlgorithm, key, config = defaultConfig) {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);
let encrypted = stream.clone(this.encrypted);
if (stream.isArrayStream(encrypted)) encrypted = await stream.readToEnd(encrypted);
const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize));
// there must be a modification detection code packet as the
// last packet and everything gets hashed except the hash itself
const realHash = stream.slice(stream.passiveClone(decrypted), -20);
const tohash = stream.slice(decrypted, 0, -20);
const verifyHash = Promise.all([
stream.readToEnd(await crypto.hash.sha1(stream.passiveClone(tohash))),
stream.readToEnd(realHash)
]).then(([hash, mdc]) => {
if (!util.equalsUint8Array(hash, mdc)) {
throw new Error('Modification detected.');
let packetbytes;
if (this.version === 2) {
packetbytes = await runAEAD(this, 'decrypt', key, encrypted);
} else {
const { blockSize } = crypto.getCipher(sessionKeyAlgorithm);
const decrypted = await crypto.mode.cfb.decrypt(sessionKeyAlgorithm, key, encrypted, new Uint8Array(blockSize));
// there must be a modification detection code packet as the
// last packet and everything gets hashed except the hash itself
const realHash = stream.slice(stream.passiveClone(decrypted), -20);
const tohash = stream.slice(decrypted, 0, -20);
const verifyHash = Promise.all([
stream.readToEnd(await crypto.hash.sha1(stream.passiveClone(tohash))),
stream.readToEnd(realHash)
]).then(([hash, mdc]) => {
if (!util.equalsUint8Array(hash, mdc)) {
throw new Error('Modification detected.');
}
return new Uint8Array();
});
const bytes = stream.slice(tohash, blockSize + 2); // Remove random prefix
packetbytes = stream.slice(bytes, 0, -2); // Remove MDC packet
packetbytes = stream.concat([packetbytes, stream.fromAsync(() => verifyHash)]);
if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) {
packetbytes = await stream.readToEnd(packetbytes);
}
return new Uint8Array();
});
const bytes = stream.slice(tohash, blockSize + 2); // Remove random prefix
let packetbytes = stream.slice(bytes, 0, -2); // Remove MDC packet
packetbytes = stream.concat([packetbytes, stream.fromAsync(() => verifyHash)]);
if (!util.isStream(encrypted) || !config.allowUnauthenticatedStream) {
packetbytes = await stream.readToEnd(packetbytes);
}
this.packets = await PacketList.fromBinary(packetbytes, allowedPackets, config);
return true;
}
}
export default SymEncryptedIntegrityProtectedDataPacket;
/**
* En/decrypt the payload.
* @param {encrypt|decrypt} fn - Whether to encrypt or decrypt
* @param {Uint8Array} key - The session key used to en/decrypt the payload
* @param {Uint8Array | ReadableStream<Uint8Array>} data - The data to en/decrypt
* @returns {Promise<Uint8Array | ReadableStream<Uint8Array>>}
* @async
*/
export async function runAEAD(packet, fn, key, data) {
const isSEIPDv2 = packet instanceof SymEncryptedIntegrityProtectedDataPacket && packet.version === 2;
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);
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))
const chunkIndexSizeIfAEADEP = isAEADP ? 8 : 0;
const adataBuffer = new ArrayBuffer(13 + chunkIndexSizeIfAEADEP);
const adataArray = new Uint8Array(adataBuffer, 0, 5 + chunkIndexSizeIfAEADEP);
const adataTagArray = new Uint8Array(adataBuffer);
const adataView = new DataView(adataBuffer);
const chunkIndexArray = new Uint8Array(adataBuffer, 5, 8);
adataArray.set([0xC0 | packet.constructor.tag, packet.version, packet.cipherAlgorithm, packet.aeadAlgorithm, packet.chunkSizeByte], 0);
let chunkIndex = 0;
let latestPromise = Promise.resolve();
let cryptedBytes = 0;
let queuedBytes = 0;
let iv;
let ivView;
if (isSEIPDv2) {
const { keySize } = crypto.getCipher(packet.cipherAlgorithm);
const { ivLength } = mode;
const info = new Uint8Array(adataBuffer, 0, 5);
const derived = await computeHKDF(enums.hash.sha256, key, packet.salt, info, keySize + ivLength);
key = derived.subarray(0, keySize);
iv = derived.subarray(keySize); // The last 8 bytes of HKDF output are unneeded, but this avoids one copy.
iv.fill(0, iv.length - 8);
ivView = new DataView(iv.buffer, iv.byteOffset, iv.byteLength);
} else { // AEADEncryptedDataPacket
iv = packet.iv;
// ivView is unused in this case
}
const modeInstance = await mode(packet.cipherAlgorithm, key);
return stream.transformPair(data, async (readable, writable) => {
if (util.isStream(readable) !== 'array') {
const buffer = new TransformStream({}, {
highWaterMark: util.getHardwareConcurrency() * 2 ** (packet.chunkSizeByte + 6),
size: array => array.length
});
stream.pipe(buffer.readable, writable);
writable = buffer.writable;
}
const reader = stream.getReader(readable);
const writer = stream.getWriter(writable);
try {
while (true) {
let chunk = await reader.readBytes(chunkSize + tagLengthIfDecrypting) || new Uint8Array();
const finalChunk = chunk.subarray(chunk.length - tagLengthIfDecrypting);
chunk = chunk.subarray(0, chunk.length - tagLengthIfDecrypting);
let cryptedPromise;
let done;
let nonce;
if (isSEIPDv2) { // SEIPD V2
nonce = iv;
} else { // AEADEncryptedDataPacket
nonce = iv.slice();
for (let i = 0; i < 8; i++) {
nonce[iv.length - 8 + i] ^= chunkIndexArray[i];
}
}
if (!chunkIndex || chunk.length) {
reader.unshift(finalChunk);
cryptedPromise = modeInstance[fn](chunk, nonce, adataArray);
queuedBytes += chunk.length - tagLengthIfDecrypting + tagLengthIfEncrypting;
} else {
// After the last chunk, we either encrypt a final, empty
// data chunk to get the final authentication tag or
// validate that final authentication tag.
adataView.setInt32(5 + chunkIndexSizeIfAEADEP + 4, cryptedBytes); // Should be setInt64(5 + chunkIndexSizeIfAEADEP, ...)
cryptedPromise = modeInstance[fn](finalChunk, nonce, adataTagArray);
queuedBytes += tagLengthIfEncrypting;
done = true;
}
cryptedBytes += chunk.length - tagLengthIfDecrypting;
// eslint-disable-next-line no-loop-func
latestPromise = latestPromise.then(() => cryptedPromise).then(async crypted => {
await writer.ready;
await writer.write(crypted);
queuedBytes -= crypted.length;
}).catch(err => writer.abort(err));
if (done || queuedBytes > writer.desiredSize) {
await latestPromise; // Respect backpressure
}
if (!done) {
if (isSEIPDv2) { // SEIPD V2
ivView.setInt32(iv.length - 4, ++chunkIndex); // Should be setInt64(iv.length - 8, ...)
} else { // AEADEncryptedDataPacket
adataView.setInt32(5 + 4, ++chunkIndex); // Should be setInt64(5, ...)
}
} else {
await writer.close();
break;
}
}
} catch (e) {
await writer.ready.catch(() => {});
await writer.abort(e);
}
});
}

View File

@ -119,6 +119,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
await openpgp.decrypt({ message: encryptedMessage, passwords, config });
});
@ -131,6 +132,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
await openpgp.decrypt({ message: encryptedMessage, passwords, config });
});
@ -142,7 +144,8 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket);
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 2);
await openpgp.decrypt({ message: encryptedMessage, passwords, config });
});
@ -154,7 +157,8 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket);
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 2);
await openpgp.decrypt({ message: encryptedMessage, passwords, config });
});
@ -176,6 +180,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {
@ -201,6 +206,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {
@ -225,7 +231,8 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket);
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 2);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {
@ -250,7 +257,8 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket);
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 2);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {
@ -276,6 +284,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {
@ -301,6 +310,7 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 1);
const { data: decryptedData } = await openpgp.decrypt({
message: encryptedMessage,
passwords,
@ -329,7 +339,8 @@ class MemoryBenchamrkSuite {
const armoredEncryptedMessage = await openpgp.encrypt({ message: plaintextMessage, passwords, config });
const encryptedMessage = await openpgp.readMessage({ armoredMessage: armoredEncryptedMessage });
assert.ok(encryptedMessage.packets[1] instanceof openpgp.AEADEncryptedDataPacket);
assert.ok(encryptedMessage.packets[1] instanceof openpgp.SymEncryptedIntegrityProtectedDataPacket);
assert.ok(encryptedMessage.packets[1].version === 2);
const { data: decryptedData } = await openpgp.decrypt({ message: encryptedMessage, passwords, config });
// read out output stream to trigger decryption
await new Promise(resolve => {

View File

@ -272,6 +272,7 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI
const { packets: [skesk, encData] } = encrypted;
expect(skesk.version).to.equal(4); // cfb
expect(encData.constructor.tag).to.equal(openpgp.enums.packet.symEncryptedIntegrityProtectedData);
expect(encData.version).to.equal(1);
const { packets: [literal] } = await encrypted.decrypt(null, passwords, null, encrypted.fromStream, openpgp.config);
expect(literal.constructor.tag).to.equal(openpgp.enums.packet.literalData);
@ -284,7 +285,8 @@ n9/quqtmyOtYOA6gXNCw0Fal3iANKBmsPmYI
const encrypted2 = await openpgp.readMessage({ armoredMessage: armored2 });
const { packets: [skesk2, encData2] } = encrypted2;
expect(skesk2.version).to.equal(5);
expect(encData2.constructor.tag).to.equal(openpgp.enums.packet.aeadEncryptedData);
expect(encData2.constructor.tag).to.equal(openpgp.enums.packet.symEncryptedIntegrityProtectedData);
expect(encData2.version).to.equal(2);
const { packets: [compressed] } = await encrypted2.decrypt(null, passwords, null, encrypted2.fromStream, openpgp.config);
expect(compressed.constructor.tag).to.equal(openpgp.enums.packet.compressedData);
expect(compressed.algorithm).to.equal(openpgp.enums.compression.zip);

View File

@ -2300,10 +2300,10 @@ XfA3pqV4mTzF
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.experimentalGCM;
openpgp.config.v6Keys = true;
// Monkey-patch AEAD feature flag
publicKey.users[0].selfCertifications[0].features = [7];
publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
@ -2313,10 +2313,10 @@ XfA3pqV4mTzF
openpgp.config.aeadProtect = true;
openpgp.config.aeadChunkSizeByte = 0;
// Monkey-patch AEAD feature flag
publicKey.users[0].selfCertifications[0].features = [7];
publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
@ -2326,10 +2326,10 @@ XfA3pqV4mTzF
openpgp.config.aeadProtect = true;
openpgp.config.preferredAEADAlgorithm = openpgp.enums.aead.ocb;
// Monkey-patch AEAD feature flag
publicKey.users[0].selfCertifications[0].features = [7];
publicKey_2000_2008.users[0].selfCertifications[0].features = [7];
publicKey_2038_2045.users[0].selfCertifications[0].features = [7];
// Monkey-patch SEIPD V2 feature flag
publicKey.users[0].selfCertifications[0].features = [9];
publicKey_2000_2008.users[0].selfCertifications[0].features = [9];
publicKey_2038_2045.users[0].selfCertifications[0].features = [9];
}
});
@ -2626,7 +2626,7 @@ XfA3pqV4mTzF
return openpgp.encrypt(encOpt).then(async function (encrypted) {
expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false);
return openpgp.decrypt(decOpt);
}).then(function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2649,7 +2649,7 @@ XfA3pqV4mTzF
return openpgp.encrypt(encOpt).then(async function (encrypted) {
expect(encrypted).to.match(/^-----BEGIN PGP MESSAGE/);
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false);
return openpgp.decrypt(decOpt);
}).then(function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2668,7 +2668,7 @@ XfA3pqV4mTzF
};
return openpgp.encrypt(encOpt).then(async function (encrypted) {
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(openpgp.config.aeadProtect);
return openpgp.decrypt(decOpt);
}).then(async function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2692,7 +2692,7 @@ XfA3pqV4mTzF
};
return openpgp.encrypt(encOpt).then(async function (encrypted) {
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(openpgp.config.aeadProtect);
return openpgp.decrypt(decOpt);
}).then(async function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2715,7 +2715,7 @@ XfA3pqV4mTzF
};
return openpgp.encrypt(encOpt).then(async function (encrypted) {
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(false);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(false);
return openpgp.decrypt(decOpt);
}).then(async function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2746,7 +2746,7 @@ XfA3pqV4mTzF
};
return openpgp.encrypt(encOpt).then(async function (encrypted) {
decOpt.message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!decOpt.message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
expect(decOpt.message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(openpgp.config.aeadProtect);
return openpgp.decrypt(decOpt);
}).then(async function (decrypted) {
expect(decrypted.data).to.equal(plaintext);
@ -2775,7 +2775,7 @@ XfA3pqV4mTzF
detached: true
});
const message = await openpgp.readMessage({ armoredMessage: encrypted });
expect(!!message.packets.findPacket(openpgp.enums.packet.aeadEncryptedData)).to.equal(openpgp.config.aeadProtect);
expect(message.packets.findPacket(openpgp.enums.packet.symEncryptedIntegrityProtectedData).version === 2).to.equal(openpgp.config.aeadProtect);
const decrypted = await openpgp.decrypt({
message,
signature: await openpgp.readSignature({ armoredSignature: signed }),

View File

@ -152,7 +152,7 @@ export default () => describe('Packet', function() {
expect(await stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
});
it('Sym. encrypted AEAD protected packet', function() {
it('Sym. encrypted AEAD protected packet (AEADP)', function() {
const aeadProtectVal = openpgp.config.aeadProtect;
openpgp.config.aeadProtect = false;
@ -201,7 +201,7 @@ export default () => describe('Packet', function() {
return cryptStub;
}
it('Sym. encrypted AEAD protected packet is encrypted in parallel (AEAD, GCM)', async function() {
it('Sym. encrypted AEAD protected packet is encrypted in parallel (AEADP, GCM)', async function() {
const webCrypto = util.getWebCrypto();
if (!webCrypto || util.getNodeCrypto()) return;
const encryptStub = cryptStub(webCrypto, 'encrypt');
@ -236,7 +236,7 @@ export default () => describe('Packet', function() {
}
});
it('Sym. encrypted AEAD protected packet test vector (AEAD)', async function() {
it('AEAD Encrypted Data packet test vector (AEADP)', async function() {
// From https://gitlab.com/openpgp-wg/rfc4880bis/commit/00b20923e6233fb6ff1666ecd5acfefceb32907d
const nodeCrypto = util.getNodeCrypto();
@ -516,7 +516,7 @@ export default () => describe('Packet', function() {
}
});
it('Sym. encrypted session key reading/writing test vector (EAX, AEAD)', async function() {
it('Sym. encrypted session key reading/writing test vector (AEAD, EAX)', async function() {
// From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-eax-encryption-and-decryption
const nodeCrypto = util.getNodeCrypto();