commit
b1b19946d8
|
@ -181,7 +181,7 @@ export default {
|
||||||
cipherfn = new cipher[cipherfn](key);
|
cipherfn = new cipher[cipherfn](key);
|
||||||
const block_size = cipherfn.blockSize;
|
const block_size = cipherfn.blockSize;
|
||||||
|
|
||||||
let iblock = new Uint8Array(block_size);
|
const iblock = new Uint8Array(block_size);
|
||||||
let ablock = new Uint8Array(block_size);
|
let ablock = new Uint8Array(block_size);
|
||||||
|
|
||||||
let i;
|
let i;
|
||||||
|
@ -189,30 +189,10 @@ export default {
|
||||||
let n;
|
let n;
|
||||||
let text = new Uint8Array(ciphertext.length - block_size);
|
let text = new Uint8Array(ciphertext.length - block_size);
|
||||||
|
|
||||||
// initialisation vector
|
|
||||||
for (i = 0; i < block_size; i++) {
|
|
||||||
iblock[i] = 0;
|
|
||||||
}
|
|
||||||
|
|
||||||
iblock = cipherfn.encrypt(iblock);
|
|
||||||
for (i = 0; i < block_size; i++) {
|
|
||||||
ablock[i] = ciphertext[i];
|
|
||||||
iblock[i] ^= ablock[i];
|
|
||||||
}
|
|
||||||
|
|
||||||
ablock = cipherfn.encrypt(ablock);
|
|
||||||
|
|
||||||
// test check octets
|
|
||||||
if (iblock[block_size - 2] !== (ablock[0] ^ ciphertext[block_size]) ||
|
|
||||||
iblock[block_size - 1] !== (ablock[1] ^ ciphertext[block_size + 1])) {
|
|
||||||
throw new Error('CFB decrypt: invalid key');
|
|
||||||
}
|
|
||||||
|
|
||||||
/* RFC4880: Tag 18 and Resync:
|
/* RFC4880: Tag 18 and Resync:
|
||||||
* [...] Unlike the Symmetrically Encrypted Data Packet, no
|
* [...] Unlike the Symmetrically Encrypted Data Packet, no
|
||||||
* special CFB resynchronization is done after encrypting this prefix
|
* special CFB resynchronization is done after encrypting this prefix
|
||||||
* data. See "OpenPGP CFB Mode" below for more details.
|
* data. See "OpenPGP CFB Mode" below for more details.
|
||||||
|
|
||||||
*/
|
*/
|
||||||
|
|
||||||
j = 0;
|
j = 0;
|
||||||
|
|
|
@ -48,7 +48,7 @@ function rightXorMut(data, padding) {
|
||||||
|
|
||||||
function pad(data, padding, padding2) {
|
function pad(data, padding, padding2) {
|
||||||
// if |M| in {n, 2n, 3n, ...}
|
// if |M| in {n, 2n, 3n, ...}
|
||||||
if (data.length % blockLength === 0) {
|
if (data.length && data.length % blockLength === 0) {
|
||||||
// then return M xor→ B,
|
// then return M xor→ B,
|
||||||
return rightXorMut(data, padding);
|
return rightXorMut(data, padding);
|
||||||
}
|
}
|
||||||
|
|
|
@ -177,7 +177,14 @@ Curve.prototype.keyFromSecret = function (secret) { // Only for ed25519
|
||||||
};
|
};
|
||||||
|
|
||||||
Curve.prototype.keyFromPublic = function (pub) {
|
Curve.prototype.keyFromPublic = function (pub) {
|
||||||
return new KeyPair(this, { pub: pub });
|
const keyPair = new KeyPair(this, { pub: pub });
|
||||||
|
if (
|
||||||
|
this.keyType === enums.publicKey.ecdsa &&
|
||||||
|
keyPair.keyPair.validate().result !== true
|
||||||
|
) {
|
||||||
|
throw new Error('Invalid elliptic public key');
|
||||||
|
}
|
||||||
|
return keyPair;
|
||||||
};
|
};
|
||||||
|
|
||||||
Curve.prototype.genKeyPair = async function () {
|
Curve.prototype.genKeyPair = async function () {
|
||||||
|
|
51
src/key.js
51
src/key.js
|
@ -1451,6 +1451,19 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
|
||||||
packetlist.push(secretKeyPacket);
|
packetlist.push(secretKeyPacket);
|
||||||
|
|
||||||
await Promise.all(options.userIds.map(async function(userId, index) {
|
await Promise.all(options.userIds.map(async function(userId, index) {
|
||||||
|
function createdPreferredAlgos(algos, configAlgo) {
|
||||||
|
if (configAlgo) { // Not `uncompressed` / `plaintext`
|
||||||
|
const configIndex = algos.indexOf(configAlgo);
|
||||||
|
if (configIndex >= 1) { // If it is included and not in first place,
|
||||||
|
algos.splice(configIndex, 1); // remove it.
|
||||||
|
}
|
||||||
|
if (configIndex !== 0) { // If it was included and not in first place, or wasn't included,
|
||||||
|
algos.unshift(configAlgo); // add it to the front.
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return algos;
|
||||||
|
}
|
||||||
|
|
||||||
const userIdPacket = new packet.Userid();
|
const userIdPacket = new packet.Userid();
|
||||||
userIdPacket.format(userId);
|
userIdPacket.format(userId);
|
||||||
|
|
||||||
|
@ -1462,26 +1475,30 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPackets, options) {
|
||||||
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
signaturePacket.publicKeyAlgorithm = secretKeyPacket.algorithm;
|
||||||
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, secretKeyPacket);
|
signaturePacket.hashAlgorithm = await getPreferredHashAlgo(null, secretKeyPacket);
|
||||||
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
signaturePacket.keyFlags = [enums.keyFlags.certify_keys | enums.keyFlags.sign_data];
|
||||||
signaturePacket.preferredSymmetricAlgorithms = [];
|
signaturePacket.preferredSymmetricAlgorithms = createdPreferredAlgos([
|
||||||
// prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support)
|
// prefer aes256, aes128, then aes192 (no WebCrypto support: https://www.chromium.org/blink/webcrypto#TOC-AES-support)
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes256);
|
enums.symmetric.aes256,
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes128);
|
enums.symmetric.aes128,
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.aes192);
|
enums.symmetric.aes192,
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.cast5);
|
enums.symmetric.cast5,
|
||||||
signaturePacket.preferredSymmetricAlgorithms.push(enums.symmetric.tripledes);
|
enums.symmetric.tripledes
|
||||||
|
], config.encryption_cipher);
|
||||||
if (config.aead_protect && config.aead_protect_version === 4) {
|
if (config.aead_protect && config.aead_protect_version === 4) {
|
||||||
signaturePacket.preferredAeadAlgorithms = [];
|
signaturePacket.preferredAeadAlgorithms = createdPreferredAlgos([
|
||||||
signaturePacket.preferredAeadAlgorithms.push(enums.aead.eax);
|
enums.aead.eax,
|
||||||
signaturePacket.preferredAeadAlgorithms.push(enums.aead.ocb);
|
enums.aead.ocb
|
||||||
|
], config.aead_mode);
|
||||||
}
|
}
|
||||||
signaturePacket.preferredHashAlgorithms = [];
|
signaturePacket.preferredHashAlgorithms = createdPreferredAlgos([
|
||||||
// prefer fast asm.js implementations (SHA-256). SHA-1 will not be secure much longer...move to bottom of list
|
// prefer fast asm.js implementations (SHA-256). SHA-1 will not be secure much longer...move to bottom of list
|
||||||
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha256);
|
enums.hash.sha256,
|
||||||
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha512);
|
enums.hash.sha512,
|
||||||
signaturePacket.preferredHashAlgorithms.push(enums.hash.sha1);
|
enums.hash.sha1
|
||||||
signaturePacket.preferredCompressionAlgorithms = [];
|
], config.prefer_hash_algorithm);
|
||||||
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zlib);
|
signaturePacket.preferredCompressionAlgorithms = createdPreferredAlgos([
|
||||||
signaturePacket.preferredCompressionAlgorithms.push(enums.compression.zip);
|
enums.compression.zlib,
|
||||||
|
enums.compression.zip
|
||||||
|
], config.compression);
|
||||||
if (index === 0) {
|
if (index === 0) {
|
||||||
signaturePacket.isPrimaryUserID = true;
|
signaturePacket.isPrimaryUserID = true;
|
||||||
}
|
}
|
||||||
|
@ -1690,7 +1707,7 @@ export async function getPreferredHashAlgo(key, keyPacket, date=new Date(), user
|
||||||
*/
|
*/
|
||||||
export async function getPreferredAlgo(type, keys, date=new Date(), userId={}) {
|
export async function getPreferredAlgo(type, keys, date=new Date(), userId={}) {
|
||||||
const prefProperty = type === 'symmetric' ? 'preferredSymmetricAlgorithms' : 'preferredAeadAlgorithms';
|
const prefProperty = type === 'symmetric' ? 'preferredSymmetricAlgorithms' : 'preferredAeadAlgorithms';
|
||||||
const defaultAlgo = type === 'symmetric' ? config.encryption_cipher : config.aead_mode;
|
const defaultAlgo = type === 'symmetric' ? enums.symmetric.aes128 : enums.aead.eax;
|
||||||
const prioMap = {};
|
const prioMap = {};
|
||||||
await Promise.all(keys.map(async function(key) {
|
await Promise.all(keys.map(async function(key) {
|
||||||
const primaryUser = await key.getPrimaryUser(date, userId);
|
const primaryUser = await key.getPrimaryUser(date, userId);
|
||||||
|
|
|
@ -153,6 +153,7 @@ Message.prototype.decrypt = async function(privateKeys, passwords, sessionKeys,
|
||||||
Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||||
let keyPackets = [];
|
let keyPackets = [];
|
||||||
|
|
||||||
|
let exception;
|
||||||
if (passwords) {
|
if (passwords) {
|
||||||
const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
|
const symESKeyPacketlist = this.packets.filterByTag(enums.packet.symEncryptedSessionKey);
|
||||||
if (!symESKeyPacketlist) {
|
if (!symESKeyPacketlist) {
|
||||||
|
@ -181,10 +182,18 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||||
throw new Error('No public key encrypted session key packet found.');
|
throw new Error('No public key encrypted session key packet found.');
|
||||||
}
|
}
|
||||||
await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) {
|
await Promise.all(pkESKeyPacketlist.map(async function(keyPacket) {
|
||||||
const privateKeyPackets = new packet.List();
|
await Promise.all(privateKeys.map(async function(privateKey) {
|
||||||
privateKeys.forEach(privateKey => {
|
const primaryUser = await privateKey.getPrimaryUser(); // TODO: Pass userId from somewhere.
|
||||||
privateKeyPackets.concat(privateKey.getKeys(keyPacket.publicKeyId).map(key => key.keyPacket));
|
let algos = [
|
||||||
});
|
enums.symmetric.aes256, // Old OpenPGP.js default fallback
|
||||||
|
enums.symmetric.aes128, // RFC4880bis fallback
|
||||||
|
enums.symmetric.tripledes // RFC4880 fallback
|
||||||
|
];
|
||||||
|
if (primaryUser && primaryUser.selfCertification.preferredSymmetricAlgorithms) {
|
||||||
|
algos = algos.concat(primaryUser.selfCertification.preferredSymmetricAlgorithms);
|
||||||
|
}
|
||||||
|
|
||||||
|
const privateKeyPackets = privateKey.getKeys(keyPacket.publicKeyId).map(key => key.keyPacket);
|
||||||
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
|
await Promise.all(privateKeyPackets.map(async function(privateKeyPacket) {
|
||||||
if (!privateKeyPacket) {
|
if (!privateKeyPacket) {
|
||||||
return;
|
return;
|
||||||
|
@ -194,11 +203,16 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
await keyPacket.decrypt(privateKeyPacket);
|
await keyPacket.decrypt(privateKeyPacket);
|
||||||
|
if (!algos.includes(enums.write(enums.symmetric, keyPacket.sessionKeyAlgorithm))) {
|
||||||
|
throw new Error('A non-preferred symmetric algorithm was used.');
|
||||||
|
}
|
||||||
keyPackets.push(keyPacket);
|
keyPackets.push(keyPacket);
|
||||||
} catch (err) {
|
} catch (err) {
|
||||||
util.print_debug_error(err);
|
util.print_debug_error(err);
|
||||||
|
exception = err;
|
||||||
}
|
}
|
||||||
}));
|
}));
|
||||||
|
}));
|
||||||
stream.cancel(keyPacket.encrypted); // Don't keep copy of encrypted data in memory.
|
stream.cancel(keyPacket.encrypted); // Don't keep copy of encrypted data in memory.
|
||||||
keyPacket.encrypted = null;
|
keyPacket.encrypted = null;
|
||||||
}));
|
}));
|
||||||
|
@ -222,7 +236,7 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||||
|
|
||||||
return keyPackets.map(packet => ({ data: packet.sessionKey, algorithm: packet.sessionKeyAlgorithm }));
|
return keyPackets.map(packet => ({ data: packet.sessionKey, algorithm: packet.sessionKeyAlgorithm }));
|
||||||
}
|
}
|
||||||
throw new Error('Session key decryption failed.');
|
throw exception || new Error('Session key decryption failed.');
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -230,7 +244,8 @@ Message.prototype.decryptSessionKeys = async function(privateKeys, passwords) {
|
||||||
* @returns {(Uint8Array|null)} literal body of the message as Uint8Array
|
* @returns {(Uint8Array|null)} literal body of the message as Uint8Array
|
||||||
*/
|
*/
|
||||||
Message.prototype.getLiteralData = function() {
|
Message.prototype.getLiteralData = function() {
|
||||||
const literal = this.packets.findPacket(enums.packet.literal);
|
const msg = this.unwrapCompressed();
|
||||||
|
const literal = msg.packets.findPacket(enums.packet.literal);
|
||||||
return (literal && literal.getBytes()) || null;
|
return (literal && literal.getBytes()) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -239,7 +254,8 @@ Message.prototype.getLiteralData = function() {
|
||||||
* @returns {(String|null)} filename of literal data packet as string
|
* @returns {(String|null)} filename of literal data packet as string
|
||||||
*/
|
*/
|
||||||
Message.prototype.getFilename = function() {
|
Message.prototype.getFilename = function() {
|
||||||
const literal = this.packets.findPacket(enums.packet.literal);
|
const msg = this.unwrapCompressed();
|
||||||
|
const literal = msg.packets.findPacket(enums.packet.literal);
|
||||||
return (literal && literal.getFilename()) || null;
|
return (literal && literal.getFilename()) || null;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -248,7 +264,8 @@ Message.prototype.getFilename = function() {
|
||||||
* @returns {(String|null)} literal body of the message interpreted as text
|
* @returns {(String|null)} literal body of the message interpreted as text
|
||||||
*/
|
*/
|
||||||
Message.prototype.getText = function() {
|
Message.prototype.getText = function() {
|
||||||
const literal = this.packets.findPacket(enums.packet.literal);
|
const msg = this.unwrapCompressed();
|
||||||
|
const literal = msg.packets.findPacket(enums.packet.literal);
|
||||||
if (literal) {
|
if (literal) {
|
||||||
return literal.getText();
|
return literal.getText();
|
||||||
}
|
}
|
||||||
|
|
|
@ -168,24 +168,10 @@ List.prototype.filterByTag = function (...args) {
|
||||||
/**
|
/**
|
||||||
* Traverses packet tree and returns first matching packet
|
* Traverses packet tree and returns first matching packet
|
||||||
* @param {module:enums.packet} type The packet type
|
* @param {module:enums.packet} type The packet type
|
||||||
* @returns {module:packet/packet|null}
|
* @returns {module:packet/packet|undefined}
|
||||||
*/
|
*/
|
||||||
List.prototype.findPacket = function (type) {
|
List.prototype.findPacket = function (type) {
|
||||||
const packetlist = this.filterByTag(type);
|
return this.find(packet => packet.tag === type);
|
||||||
if (packetlist.length) {
|
|
||||||
return packetlist[0];
|
|
||||||
}
|
|
||||||
let found = null;
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
|
||||||
if (this[i].packets.length) {
|
|
||||||
found = this[i].packets.findPacket(type);
|
|
||||||
if (found) {
|
|
||||||
return found;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return null;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -62,6 +62,11 @@ function PublicKey(date=new Date()) {
|
||||||
* @type {Date}
|
* @type {Date}
|
||||||
*/
|
*/
|
||||||
this.created = util.normalizeDate(date);
|
this.created = util.normalizeDate(date);
|
||||||
|
/**
|
||||||
|
* Public key algorithm.
|
||||||
|
* @type {String}
|
||||||
|
*/
|
||||||
|
this.algorithm = null;
|
||||||
/**
|
/**
|
||||||
* Algorithm specific params
|
* Algorithm specific params
|
||||||
* @type {Array<Object>}
|
* @type {Array<Object>}
|
||||||
|
|
|
@ -52,7 +52,10 @@ function PublicKeyEncryptedSessionKey() {
|
||||||
this.version = 3;
|
this.version = 3;
|
||||||
|
|
||||||
this.publicKeyId = new type_keyid();
|
this.publicKeyId = new type_keyid();
|
||||||
|
this.publicKeyAlgorithm = null;
|
||||||
|
|
||||||
this.sessionKey = null;
|
this.sessionKey = null;
|
||||||
|
this.sessionKeyAlgorithm = null;
|
||||||
|
|
||||||
/** @type {Array<module:type/mpi>} */
|
/** @type {Array<module:type/mpi>} */
|
||||||
this.encrypted = [];
|
this.encrypted = [];
|
||||||
|
@ -150,7 +153,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
|
||||||
key = util.str_to_Uint8Array(decoded.substring(1, decoded.length - 2));
|
key = util.str_to_Uint8Array(decoded.substring(1, decoded.length - 2));
|
||||||
|
|
||||||
if (!util.equalsUint8Array(checksum, util.write_checksum(key))) {
|
if (!util.equalsUint8Array(checksum, util.write_checksum(key))) {
|
||||||
throw new Error('Checksum mismatch');
|
throw new Error('Decryption error');
|
||||||
} else {
|
} else {
|
||||||
this.sessionKey = key;
|
this.sessionKey = key;
|
||||||
this.sessionKeyAlgorithm = enums.read(enums.symmetric, decoded.charCodeAt(0));
|
this.sessionKeyAlgorithm = enums.read(enums.symmetric, decoded.charCodeAt(0));
|
||||||
|
|
|
@ -666,6 +666,10 @@ Signature.prototype.verify = async function (key, signatureType, data) {
|
||||||
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
const publicKeyAlgorithm = enums.write(enums.publicKey, this.publicKeyAlgorithm);
|
||||||
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
const hashAlgorithm = enums.write(enums.hash, this.hashAlgorithm);
|
||||||
|
|
||||||
|
if (publicKeyAlgorithm !== enums.write(enums.publicKey, key.algorithm)) {
|
||||||
|
throw new Error('Public key algorithm used to sign signature does not match issuer key algorithm.');
|
||||||
|
}
|
||||||
|
|
||||||
let toHash;
|
let toHash;
|
||||||
let hash;
|
let hash;
|
||||||
if (this.hashed) {
|
if (this.hashed) {
|
||||||
|
|
|
@ -223,7 +223,17 @@ describe('Elliptic Curve Cryptography', async function () {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00
|
||||||
]);
|
]);
|
||||||
const secp256k1_dummy_point = new Uint8Array([
|
const secp256k1_point = new Uint8Array([
|
||||||
|
0x04,
|
||||||
|
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC,
|
||||||
|
0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07,
|
||||||
|
0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9,
|
||||||
|
0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98,
|
||||||
|
0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65,
|
||||||
|
0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8,
|
||||||
|
0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19,
|
||||||
|
0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8]);
|
||||||
|
const secp256k1_invalid_point = new Uint8Array([
|
||||||
0x04,
|
0x04,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -233,7 +243,7 @@ describe('Elliptic Curve Cryptography', async function () {
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
const secp256k1_invalid_point = new Uint8Array([
|
const secp256k1_invalid_point_format = new Uint8Array([
|
||||||
0x04,
|
0x04,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
@ -255,13 +265,18 @@ describe('Elliptic Curve Cryptography', async function () {
|
||||||
'secp256k1', 8, [], [], [], []
|
'secp256k1', 8, [], [], [], []
|
||||||
)).to.be.rejectedWith(Error, /Unknown point format/),
|
)).to.be.rejectedWith(Error, /Unknown point format/),
|
||||||
expect(verify_signature(
|
expect(verify_signature(
|
||||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
'secp256k1', 8, [], [], [], secp256k1_invalid_point_format
|
||||||
)).to.be.rejectedWith(Error, /Unknown point format/)
|
)).to.be.rejectedWith(Error, /Unknown point format/)
|
||||||
]);
|
]);
|
||||||
});
|
});
|
||||||
|
it('Invalid point', function (done) {
|
||||||
|
expect(verify_signature(
|
||||||
|
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||||
|
)).to.be.rejectedWith(Error, /Invalid elliptic public key/).notify(done);
|
||||||
|
});
|
||||||
it('Invalid signature', function (done) {
|
it('Invalid signature', function (done) {
|
||||||
expect(verify_signature(
|
expect(verify_signature(
|
||||||
'secp256k1', 8, [], [], [], secp256k1_dummy_point
|
'secp256k1', 8, [], [], [], secp256k1_point
|
||||||
)).to.eventually.be.false.notify(done);
|
)).to.eventually.be.false.notify(done);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -331,11 +346,21 @@ describe('Elliptic Curve Cryptography', async function () {
|
||||||
]);
|
]);
|
||||||
const secp256k1_point = new Uint8Array([
|
const secp256k1_point = new Uint8Array([
|
||||||
0x04,
|
0x04,
|
||||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x79, 0xBE, 0x66, 0x7E, 0xF9, 0xDC, 0xBB, 0xAC,
|
||||||
|
0x55, 0xA0, 0x62, 0x95, 0xCE, 0x87, 0x0B, 0x07,
|
||||||
|
0x02, 0x9B, 0xFC, 0xDB, 0x2D, 0xCE, 0x28, 0xD9,
|
||||||
|
0x59, 0xF2, 0x81, 0x5B, 0x16, 0xF8, 0x17, 0x98,
|
||||||
|
0x48, 0x3A, 0xDA, 0x77, 0x26, 0xA3, 0xC4, 0x65,
|
||||||
|
0x5D, 0xA4, 0xFB, 0xFC, 0x0E, 0x11, 0x08, 0xA8,
|
||||||
|
0xFD, 0x17, 0xB4, 0x48, 0xA6, 0x85, 0x54, 0x19,
|
||||||
|
0x9C, 0x47, 0xD0, 0x8F, 0xFB, 0x10, 0xD4, 0xB8]);
|
||||||
|
const secp256k1_invalid_point = new Uint8Array([
|
||||||
|
0x04,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x10, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
|
||||||
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00]);
|
||||||
|
@ -354,6 +379,11 @@ describe('Elliptic Curve Cryptography', async function () {
|
||||||
'secp256k1', 2, 7, [], [], [], []
|
'secp256k1', 2, 7, [], [], [], []
|
||||||
)).to.be.rejectedWith(Error, /Unknown point format/).notify(done);
|
)).to.be.rejectedWith(Error, /Unknown point format/).notify(done);
|
||||||
});
|
});
|
||||||
|
it('Invalid elliptic public key', function (done) {
|
||||||
|
expect(decrypt_message(
|
||||||
|
'secp256k1', 2, 7, secp256k1_value, secp256k1_invalid_point, secp256k1_data, []
|
||||||
|
)).to.be.rejectedWith(Error, /Invalid elliptic public key/).notify(done);
|
||||||
|
});
|
||||||
it('Invalid key data integrity', function (done) {
|
it('Invalid key data integrity', function (done) {
|
||||||
expect(decrypt_message(
|
expect(decrypt_message(
|
||||||
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_data, []
|
'secp256k1', 2, 7, secp256k1_value, secp256k1_point, secp256k1_data, []
|
||||||
|
|
|
@ -1357,6 +1357,49 @@ function versionSpecificTests() {
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
it('Preferences of generated key - with config values', async function() {
|
||||||
|
const encryption_cipherVal = openpgp.config.encryption_cipher;
|
||||||
|
const prefer_hash_algorithmVal = openpgp.config.prefer_hash_algorithm;
|
||||||
|
const compressionVal = openpgp.config.compression;
|
||||||
|
const aead_modeVal = openpgp.config.aead_mode;
|
||||||
|
openpgp.config.encryption_cipher = openpgp.enums.symmetric.aes192;
|
||||||
|
openpgp.config.prefer_hash_algorithm = openpgp.enums.hash.sha384;
|
||||||
|
openpgp.config.compression = openpgp.enums.compression.zlib;
|
||||||
|
openpgp.config.aead_mode = openpgp.enums.aead.experimental_gcm;
|
||||||
|
|
||||||
|
const testPref = function(key) {
|
||||||
|
// key flags
|
||||||
|
const keyFlags = openpgp.enums.keyFlags;
|
||||||
|
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.certify_keys).to.equal(keyFlags.certify_keys);
|
||||||
|
expect(key.users[0].selfCertifications[0].keyFlags[0] & keyFlags.sign_data).to.equal(keyFlags.sign_data);
|
||||||
|
expect(key.subKeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encrypt_communication).to.equal(keyFlags.encrypt_communication);
|
||||||
|
expect(key.subKeys[0].bindingSignatures[0].keyFlags[0] & keyFlags.encrypt_storage).to.equal(keyFlags.encrypt_storage);
|
||||||
|
const sym = openpgp.enums.symmetric;
|
||||||
|
expect(key.users[0].selfCertifications[0].preferredSymmetricAlgorithms).to.eql([sym.aes192, sym.aes256, sym.aes128, sym.cast5, sym.tripledes]);
|
||||||
|
if (openpgp.config.aead_protect && openpgp.config.aead_protect_version === 4) {
|
||||||
|
const aead = openpgp.enums.aead;
|
||||||
|
expect(key.users[0].selfCertifications[0].preferredAeadAlgorithms).to.eql([aead.experimental_gcm, aead.eax, aead.ocb]);
|
||||||
|
}
|
||||||
|
const hash = openpgp.enums.hash;
|
||||||
|
expect(key.users[0].selfCertifications[0].preferredHashAlgorithms).to.eql([hash.sha384, hash.sha256, hash.sha512, hash.sha1]);
|
||||||
|
const compr = openpgp.enums.compression;
|
||||||
|
expect(key.users[0].selfCertifications[0].preferredCompressionAlgorithms).to.eql([compr.zlib, compr.zip]);
|
||||||
|
expect(key.users[0].selfCertifications[0].features).to.eql(openpgp.config.aead_protect && openpgp.config.aead_protect_version === 4 ? [7] : [1]);
|
||||||
|
};
|
||||||
|
const opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: 'hello'};
|
||||||
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
try {
|
||||||
|
const key = await openpgp.generateKey(opt);
|
||||||
|
testPref(key.key);
|
||||||
|
testPref((await openpgp.key.readArmored(key.publicKeyArmored)).keys[0]);
|
||||||
|
} finally {
|
||||||
|
openpgp.config.encryption_cipher = encryption_cipherVal;
|
||||||
|
openpgp.config.prefer_hash_algorithm = prefer_hash_algorithmVal;
|
||||||
|
openpgp.config.compression = compressionVal;
|
||||||
|
openpgp.config.aead_mode = aead_modeVal;
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
it('Generated key is not unlocked by default', function() {
|
it('Generated key is not unlocked by default', function() {
|
||||||
const opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: '123'};
|
const opt = {numBits: 512, userIds: 'test <a@b.com>', passphrase: '123'};
|
||||||
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
|
||||||
|
@ -2147,14 +2190,14 @@ describe('Key', function() {
|
||||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes256);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getPreferredAlgo('symmetric') - two key - AES128", async function() {
|
it("getPreferredAlgo('symmetric') - two key - AES192", async function() {
|
||||||
const keys = (await openpgp.key.readArmored(twoKeys)).keys;
|
const keys = (await openpgp.key.readArmored(twoKeys)).keys;
|
||||||
const key1 = keys[0];
|
const key1 = keys[0];
|
||||||
const key2 = keys[1];
|
const key2 = keys[1];
|
||||||
const primaryUser = await key2.getPrimaryUser();
|
const primaryUser = await key2.getPrimaryUser();
|
||||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = [6,7,3];
|
primaryUser.selfCertification.preferredSymmetricAlgorithms = [6,8,3];
|
||||||
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1, key2]);
|
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1, key2]);
|
||||||
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes192);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getPreferredAlgo('symmetric') - two key - one without pref", async function() {
|
it("getPreferredAlgo('symmetric') - two key - one without pref", async function() {
|
||||||
|
@ -2164,7 +2207,7 @@ describe('Key', function() {
|
||||||
const primaryUser = await key2.getPrimaryUser();
|
const primaryUser = await key2.getPrimaryUser();
|
||||||
primaryUser.selfCertification.preferredSymmetricAlgorithms = null;
|
primaryUser.selfCertification.preferredSymmetricAlgorithms = null;
|
||||||
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1, key2]);
|
const prefAlgo = await openpgp.key.getPreferredAlgo('symmetric', [key1, key2]);
|
||||||
expect(prefAlgo).to.equal(openpgp.config.encryption_cipher);
|
expect(prefAlgo).to.equal(openpgp.enums.symmetric.aes128);
|
||||||
});
|
});
|
||||||
|
|
||||||
it("getPreferredAlgo('aead') - one key - OCB", async function() {
|
it("getPreferredAlgo('aead') - one key - OCB", async function() {
|
||||||
|
@ -2188,7 +2231,7 @@ describe('Key', function() {
|
||||||
const primaryUser2 = await key2.getPrimaryUser();
|
const primaryUser2 = await key2.getPrimaryUser();
|
||||||
primaryUser2.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
primaryUser2.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||||
const prefAlgo = await openpgp.key.getPreferredAlgo('aead', [key1, key2]);
|
const prefAlgo = await openpgp.key.getPreferredAlgo('aead', [key1, key2]);
|
||||||
expect(prefAlgo).to.equal(openpgp.config.aead_mode);
|
expect(prefAlgo).to.equal(openpgp.enums.aead.eax);
|
||||||
const supported = await openpgp.key.isAeadSupported([key1, key2]);
|
const supported = await openpgp.key.isAeadSupported([key1, key2]);
|
||||||
expect(supported).to.be.true;
|
expect(supported).to.be.true;
|
||||||
});
|
});
|
||||||
|
@ -2201,7 +2244,7 @@ describe('Key', function() {
|
||||||
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
primaryUser.selfCertification.features = [7]; // Monkey-patch AEAD feature flag
|
||||||
primaryUser.selfCertification.preferredAeadAlgorithms = [2,1];
|
primaryUser.selfCertification.preferredAeadAlgorithms = [2,1];
|
||||||
const prefAlgo = await openpgp.key.getPreferredAlgo('aead', [key1, key2]);
|
const prefAlgo = await openpgp.key.getPreferredAlgo('aead', [key1, key2]);
|
||||||
expect(prefAlgo).to.equal(openpgp.config.aead_mode);
|
expect(prefAlgo).to.equal(openpgp.enums.aead.eax);
|
||||||
const supported = await openpgp.key.isAeadSupported([key1, key2]);
|
const supported = await openpgp.key.isAeadSupported([key1, key2]);
|
||||||
expect(supported).to.be.false;
|
expect(supported).to.be.false;
|
||||||
});
|
});
|
||||||
|
|
|
@ -2,4 +2,5 @@ describe('Security', function () {
|
||||||
require('./message_signature_bypass');
|
require('./message_signature_bypass');
|
||||||
require('./unsigned_subpackets');
|
require('./unsigned_subpackets');
|
||||||
require('./subkey_trust');
|
require('./subkey_trust');
|
||||||
|
require('./preferred_algo_mismatch');
|
||||||
});
|
});
|
||||||
|
|
49
test/security/preferred_algo_mismatch.js
Normal file
49
test/security/preferred_algo_mismatch.js
Normal file
|
@ -0,0 +1,49 @@
|
||||||
|
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||||
|
|
||||||
|
const { key, cleartext, enums, packet: { List, Signature } } = openpgp;
|
||||||
|
|
||||||
|
const chai = require('chai');
|
||||||
|
chai.use(require('chai-as-promised'));
|
||||||
|
|
||||||
|
const expect = chai.expect;
|
||||||
|
|
||||||
|
const messageArmor = `-----BEGIN PGP MESSAGE-----
|
||||||
|
Version: OpenPGP.js VERSION
|
||||||
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
wYwD3eCUoDfD5yoBA/4rhxaaw+E2ma+LdmLVDBRqxglhIgnM6EgNxzf8J5Ty
|
||||||
|
ecQBLOf3BjjC72mJ9RqMmvQ16aG4EXXDAUmCP1sBLj+b7V1t4keeyTn+2nXu
|
||||||
|
7Wgu2yq9CvZahRLsayt3y8VodZwTi3K/+gmx1f8EhdLPONQgGkYAqZ3Tyyd0
|
||||||
|
KF3pknplvdI+AXqRs0n2vVr89oIdmQPJFSHEoJtltbSNxhwShdzDvOor2FKJ
|
||||||
|
vhGWNysion2aBg0fIbgDUKeXKp8YN44LDTk=
|
||||||
|
=RYrv
|
||||||
|
-----END PGP MESSAGE-----`;
|
||||||
|
|
||||||
|
const privateKeyArmor = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||||
|
Version: OpenPGP.js VERSION
|
||||||
|
Comment: https://openpgpjs.org
|
||||||
|
|
||||||
|
xcEYBFvbA08BBACl8U5VEY7TNq1PAzwU0f3soqNfFpKtNFt+LY3q5sasouJ7
|
||||||
|
zE4/TPYrAaAoM5/yOjfvbfJP5myBUCtkdtIRIY2iP2uOPhfaly8U+zH25Qnq
|
||||||
|
bmgLfvu4ytPAPrKZF8f98cIeJmHD81SPRgDMuB2U9wwgN6stgVBBCUS+lu/L
|
||||||
|
/4pyuwARAQABAAP+Jz6BIvcrCuJ0bCo8rEPZRHxWHKfO+m1Wcem+FV6Mf8lp
|
||||||
|
vJNdsfS2hwc0ZC2JVxTTo6kh1CmPYamfCXxcQ7bmsqWkkq/6d17zKE6BqE/n
|
||||||
|
spW7qTnZ14VPC0iPrBetAWRlCk+m0cEkRnBxqPOVBNd6VPcZyM7GUOGf/kiw
|
||||||
|
AsHf+nECANkN1tsqLJ3+pH2MRouF7yHevQ9OGg+rwetBO2a8avvcsAuoFjVw
|
||||||
|
hERpkHv/PQjKAE7KcBzqLLad0QbrQW+sUcMCAMO3to0tSBJrNA9YkrViT76I
|
||||||
|
siiahSB/FC9JlO+T46xncRleZeBHc0zoVAP+W/PjRo2CR4ydtwjjalrxcKX9
|
||||||
|
E6kCALfDyhkRNzZLxg2XOGDWyeXqe80VWnMBqTZK73nZlACRcUoXuvjRc15Q
|
||||||
|
K2c3/nZ7LMyQidj8XsTq4sz1zfWz4Cejj80cVGVzdCBVc2VyIDx0ZXN0QGV4
|
||||||
|
YW1wbGUuY29tPsK1BBABCAApBQJb2wNPAgsJCRDd4JSgN8PnKgQVCAoCAxYC
|
||||||
|
AQIZAQIbDwIeBwMiAQIAABGjA/4y6HjthMU03AC3bIUyYPv6EJc9czS5wysa
|
||||||
|
5rKuNhzka0Klb0INcX1YZ8usPIIl1rtr8f8xxCdSiqhJpn+uqIPVROHi0XLG
|
||||||
|
ej3gSJM5i1lIt1jxyJlvVI/7W0vzuE85KDzGXQFNFyO/T9D7T1SDHnS8KbBh
|
||||||
|
EnxUPL95HuMKoVkf4w==
|
||||||
|
=oopr
|
||||||
|
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||||
|
|
||||||
|
it('Does not accept message encrypted with algo not mentioned in preferred algorithms', async function() {
|
||||||
|
const message = await openpgp.message.readArmored(messageArmor);
|
||||||
|
const privKey = (await openpgp.key.readArmored(privateKeyArmor)).keys[0];
|
||||||
|
await expect(openpgp.decrypt({ message, privateKeys: [privKey] })).to.be.rejectedWith('A non-preferred symmetric algorithm was used.');
|
||||||
|
});
|
Loading…
Reference in New Issue
Block a user