mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-06-10 08:06:43 +00:00
Support AEAD encryption for v4 and v6 private keys
This commit is contained in:
parent
21343f2bb8
commit
95fd04db8e
@ -21,7 +21,8 @@ import crypto from '../crypto';
|
||||
import enums from '../enums';
|
||||
import util from '../util';
|
||||
import defaultConfig from '../config';
|
||||
import { UnsupportedError } from './packet';
|
||||
import { UnsupportedError, writeTag } from './packet';
|
||||
import computeHKDF from '../crypto/hkdf';
|
||||
|
||||
/**
|
||||
* A Secret-Key packet contains all the information that is found in a
|
||||
@ -140,14 +141,27 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
this.symmetric = this.s2kUsage;
|
||||
}
|
||||
|
||||
// - [Optional] If secret data is encrypted (string-to-key usage octet
|
||||
// not zero), an Initial Vector (IV) of the same length as the
|
||||
// cipher's block size.
|
||||
|
||||
if (this.s2kUsage) {
|
||||
this.iv = bytes.subarray(
|
||||
i,
|
||||
i + crypto.getCipher(this.symmetric).blockSize
|
||||
);
|
||||
// - crypto-refresh: If string-to-key usage octet was 255, 254 [..], an initialization vector (IV)
|
||||
// of the same length as the cipher's block size.
|
||||
// - RFC4880bis (v5 keys, regardless of AEAD): an Initial Vector (IV) of the same length as the
|
||||
// cipher's block size. If string-to-key usage octet was 253 the IV is used as the nonce for the AEAD algorithm.
|
||||
// If the AEAD algorithm requires a shorter nonce, the high-order bits of the IV are used and the remaining bits MUST be zero
|
||||
if (this.s2kUsage !== 253 || this.version === 5) {
|
||||
this.iv = bytes.subarray(
|
||||
i,
|
||||
i + crypto.getCipher(this.symmetric).blockSize
|
||||
);
|
||||
} else {
|
||||
// crypto-refresh: If string-to-key usage octet was 253 (that is, the secret data is AEAD-encrypted),
|
||||
// an initialization vector (IV) of size specified by the AEAD algorithm (see Section 5.13.2), which
|
||||
// is used as the nonce for the AEAD algorithm.
|
||||
this.iv = bytes.subarray(
|
||||
i,
|
||||
i + crypto.getAEADMode(this.aead).ivLength
|
||||
);
|
||||
}
|
||||
|
||||
i += this.iv.length;
|
||||
}
|
||||
@ -342,19 +356,28 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
this.s2k.generateSalt();
|
||||
const cleartext = crypto.serializeParams(this.algorithm, this.privateParams);
|
||||
this.symmetric = enums.symmetric.aes256;
|
||||
const key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric);
|
||||
|
||||
const { blockSize } = crypto.getCipher(this.symmetric);
|
||||
this.iv = crypto.random.getRandomBytes(blockSize);
|
||||
|
||||
if (config.aeadProtect) {
|
||||
this.s2kUsage = 253;
|
||||
this.aead = enums.aead.eax;
|
||||
const mode = crypto.getAEADMode(this.aead);
|
||||
|
||||
const serializedPacketTag = writeTag(this.constructor.tag);
|
||||
const key = await produceEncryptionKey(this.version, this.s2k, passphrase, this.symmetric, this.aead, serializedPacketTag);
|
||||
|
||||
const modeInstance = await mode(this.symmetric, key);
|
||||
this.keyMaterial = await modeInstance.encrypt(cleartext, this.iv.subarray(0, mode.ivLength), new Uint8Array());
|
||||
this.iv = (this.version === 5) ? crypto.random.getRandomBytes(blockSize) : crypto.random.getRandomBytes(mode.ivLength);
|
||||
const associateData = this.version === 5 ?
|
||||
new Uint8Array() :
|
||||
util.concatUint8Array([serializedPacketTag, this.writePublicKey()]);
|
||||
|
||||
this.keyMaterial = await modeInstance.encrypt(cleartext, this.iv.subarray(0, mode.ivLength), associateData);
|
||||
} else {
|
||||
this.s2kUsage = 254;
|
||||
const key = await produceEncryptionKey(this.version, this.s2k, passphrase, this.symmetric);
|
||||
this.iv = crypto.random.getRandomBytes(blockSize);
|
||||
this.keyMaterial = await crypto.mode.cfb.encrypt(this.symmetric, key, util.concatUint8Array([
|
||||
cleartext,
|
||||
await crypto.hash.sha1(cleartext, config)
|
||||
@ -385,8 +408,10 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
}
|
||||
|
||||
let key;
|
||||
const serializedPacketTag = writeTag(this.constructor.tag); // relevant for AEAD only
|
||||
if (this.s2kUsage === 254 || this.s2kUsage === 253) {
|
||||
key = await produceEncryptionKey(this.s2k, passphrase, this.symmetric);
|
||||
key = await produceEncryptionKey(
|
||||
this.version, this.s2k, passphrase, this.symmetric, this.aead, serializedPacketTag);
|
||||
} else if (this.s2kUsage === 255) {
|
||||
throw new Error('Encrypted private key is authenticated using an insecure two-byte hash');
|
||||
} else {
|
||||
@ -398,7 +423,10 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
const mode = crypto.getAEADMode(this.aead);
|
||||
const modeInstance = await mode(this.symmetric, key);
|
||||
try {
|
||||
cleartext = await modeInstance.decrypt(this.keyMaterial, this.iv.subarray(0, mode.ivLength), new Uint8Array());
|
||||
const associateData = this.version === 5 ?
|
||||
new Uint8Array() :
|
||||
util.concatUint8Array([serializedPacketTag, this.writePublicKey()]);
|
||||
cleartext = await modeInstance.decrypt(this.keyMaterial, this.iv.subarray(0, mode.ivLength), associateData);
|
||||
} catch (err) {
|
||||
if (err.message === 'Authentication tag mismatch') {
|
||||
throw new Error('Incorrect key passphrase: ' + err.message);
|
||||
@ -425,6 +453,8 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
this.isEncrypted = false;
|
||||
this.keyMaterial = null;
|
||||
this.s2kUsage = 0;
|
||||
this.aead = null;
|
||||
this.symmetric = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -478,9 +508,27 @@ class SecretKeyPacket extends PublicKeyPacket {
|
||||
}
|
||||
}
|
||||
|
||||
async function produceEncryptionKey(s2k, passphrase, algorithm) {
|
||||
const { keySize } = crypto.getCipher(algorithm);
|
||||
return s2k.produceKey(passphrase, keySize);
|
||||
/**
|
||||
* Derive encryption key
|
||||
* @param {Number} keyVersion - key derivation differs for v5 keys
|
||||
* @param {module:type/s2k} s2k
|
||||
* @param {String} passphrase
|
||||
* @param {module:enums.symmetric} cipherAlgo
|
||||
* @param {module:enums.aead} [aeadMode] - for AEAD-encrypted keys only (excluding v5)
|
||||
* @param {Uint8Array} serializedPacketTag - for AEAD-encrypted keys only (excluding v5)
|
||||
* @returns encryption key
|
||||
*/
|
||||
async function produceEncryptionKey(keyVersion, s2k, passphrase, cipherAlgo, aeadMode, serializedPacketTag) {
|
||||
const { keySize } = crypto.getCipher(cipherAlgo);
|
||||
const derivedKey = await s2k.produceKey(passphrase, keySize);
|
||||
if (!aeadMode || keyVersion === 5) {
|
||||
return derivedKey;
|
||||
}
|
||||
const info = util.concatUint8Array([
|
||||
serializedPacketTag,
|
||||
new Uint8Array([keyVersion, cipherAlgo, aeadMode])
|
||||
]);
|
||||
return computeHKDF(enums.hash.sha256, derivedKey, new Uint8Array(), info, keySize);
|
||||
}
|
||||
|
||||
export default SecretKeyPacket;
|
||||
|
@ -4,6 +4,7 @@ import { use as chaiUse, expect } from 'chai';
|
||||
import chaiAsPromised from 'chai-as-promised'; // eslint-disable-line import/newline-after-import
|
||||
chaiUse(chaiAsPromised);
|
||||
|
||||
import sinon from 'sinon';
|
||||
import openpgp from '../initOpenpgp.js';
|
||||
import util from '../../src/util.js';
|
||||
import { getPreferredCipherSuite } from '../../src/key';
|
||||
@ -2912,7 +2913,7 @@ export default () => describe('Key', function() {
|
||||
let aeadProtectVal;
|
||||
|
||||
tryTests('V4', versionSpecificTests, {
|
||||
if: !openpgp.config.ci,
|
||||
if: true,
|
||||
beforeEach: function() {
|
||||
v6KeysVal = openpgp.config.v6Keys;
|
||||
openpgp.config.v6Keys = false;
|
||||
@ -2923,7 +2924,7 @@ export default () => describe('Key', function() {
|
||||
});
|
||||
|
||||
tryTests('V6', versionSpecificTests, {
|
||||
if: !openpgp.config.ci,
|
||||
if: true,
|
||||
beforeEach: function() {
|
||||
v6KeysVal = openpgp.config.v6Keys;
|
||||
aeadProtectVal = openpgp.config.aeadProtect;
|
||||
@ -2987,6 +2988,172 @@ export default () => describe('Key', function() {
|
||||
expect(key).to.exist;
|
||||
});
|
||||
|
||||
it('Parsing, decrypting, encrypting and serializing V5 key (AEAD-encrypted)', async function() {
|
||||
const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xYwFZC7tvxYAAAAtCSsGAQQB2kcPAQEHQP/d1oBAqCKZYxb6k8foyX2Aa/VK
|
||||
dHFymZPGvHRk1ncs/R0JAQMIrDnS3Bany9EAF6dwQSfPSdObc4ROYIMAnwAA
|
||||
ADKV1OhGzwANnapimvODI6fK5F7/V0GxETY9WmnipnBzr4Fe9GZw4QD4Q4hd
|
||||
IJMawjUBrs0MdjVAYWVhZC50ZXN0wpIFEBYKAEQFgmQu7b8ECwkHCAMVCAoE
|
||||
FgACAQIZAQKbAwIeByKhBQ/Y89PNwfdXUdI/td5Q9rNrYP9mb7Dg6k/3nxTg
|
||||
ugQ5AyIBAgAAf0kBAJv0OQvd4u8R0f3HAsmQeqMnwNA4or75BOn/ieApNZUt
|
||||
AP9kQVmYEk4+MV57Us15l2kQEslLDr3qiH5+VCICdEprB8eRBWQu7b8SAAAA
|
||||
MgorBgEEAZdVAQUBAQdA4IgEkfze3eNKRz6DgzGSJxw/CV/5Rp5u4Imn47h7
|
||||
pyADAQgH/R0JAQMIwayD3R4E0ugAyszSmOIpaLJ40YGBp5uU7wAAADKmSv4W
|
||||
tio7GfZCVl8eJ7xX3J1b0iMvEm876tUeHANQlYYCWz+2ahmPVe79zzZA9OhN
|
||||
FcJ6BRgWCAAsBYJkLu2/ApsMIqEFD9jz083B91dR0j+13lD2s2tg/2ZvsODq
|
||||
T/efFOC6BDkAAHcjAPwIPNHnR9bKmkVop6cE05dCIpZ/W8zXDGnjKYrrC4Hb
|
||||
4gEAmISD1GRkNOmCV8aHwN5svO6HuwXR4cR3o3l7HlYeag8=
|
||||
=wpkQ
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
const passphrase = 'password';
|
||||
const encryptedKey = await openpgp.readKey({ armoredKey });
|
||||
const decryptedKey = await openpgp.decryptKey({
|
||||
privateKey: encryptedKey,
|
||||
passphrase
|
||||
});
|
||||
const reecryptedKey = await openpgp.encryptKey({
|
||||
privateKey: decryptedKey,
|
||||
passphrase,
|
||||
config: { aeadProtect: true }
|
||||
});
|
||||
expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253);
|
||||
const redecryptedKey = await openpgp.decryptKey({
|
||||
privateKey: reecryptedKey,
|
||||
passphrase
|
||||
});
|
||||
expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write());
|
||||
});
|
||||
|
||||
it('Parsing, decrypting, encrypting and serializing V4 key (AEAD-encrypted)', async function() {
|
||||
// key from gopenpgp
|
||||
const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
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
|
||||
oZIvKIVq9Vqf8XJVjMDbRMNTmh3a5A==
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
const passphrase = 'password';
|
||||
const encryptedKey = await openpgp.readKey({ armoredKey });
|
||||
const decryptedKey = await openpgp.decryptKey({
|
||||
privateKey: encryptedKey,
|
||||
passphrase
|
||||
});
|
||||
const reecryptedKey = await openpgp.encryptKey({
|
||||
privateKey: decryptedKey,
|
||||
passphrase,
|
||||
config: { aeadProtect: true }
|
||||
});
|
||||
expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253);
|
||||
const redecryptedKey = await openpgp.decryptKey({
|
||||
privateKey: reecryptedKey,
|
||||
passphrase
|
||||
});
|
||||
expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write());
|
||||
});
|
||||
|
||||
it('Parsing, decrypting, encrypting and serializing V6 key (AEAD-encrypted)', async function() {
|
||||
// official test vector from https://www.ietf.org/archive/id/draft-ietf-openpgp-crypto-refresh-10.html#appendix-A.5
|
||||
const armoredKey = `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
||||
xYIGY4d/4xsAAAAg+U2nu0jWCmHlZ3BqZYfQMxmZu52JGggkLq2EVD34laP9JgkC
|
||||
FARdb9ccngltHraRe25uHuyuAQQVtKipJ0+r5jL4dacGWSAheCWPpITYiyfyIOPS
|
||||
3gIDyg8f7strd1OB4+LZsUhcIjOMpVHgmiY/IutJkulneoBYwrEGHxsKAAAAQgWC
|
||||
Y4d/4wMLCQcFFQoOCAwCFgACmwMCHgkiIQbLGGxPBgmml+TVLfpscisMHx4nwYpW
|
||||
cI9lJewnutmsyQUnCQIHAgAAAACtKCAQPi19In7A5tfORHHbNr/JcIMlNpAnFJin
|
||||
7wV2wH+q4UWFs7kDsBJ+xP2i8CMEWi7Ha8tPlXGpZR4UruETeh1mhELIj5UeM8T/
|
||||
0z+5oX1RHu11j8bZzFDLX9eTsgOdWATHggZjh3/jGQAAACCGkySDZ/nlAV25Ivj0
|
||||
gJXdp4SYfy1ZhbEvutFsr15ENf0mCQIUBA5hhGgp2oaavg6mFUXcFMwBBBUuE8qf
|
||||
9Ock+xwusd+GAglBr5LVyr/lup3xxQvHXFSjjA2haXfoN6xUGRdDEHI6+uevKjVR
|
||||
v5oAxgu7eJpaXNjCmwYYGwoAAAAsBYJjh3/jApsMIiEGyxhsTwYJppfk1S36bHIr
|
||||
DB8eJ8GKVnCPZSXsJ7rZrMkAAAAABAEgpukYbZ1ZNfyP5WMUzbUnSGpaUSD5t2Ki
|
||||
Nacp8DkBClZRa2c3AMQzSDXa9jGhYzxjzVb5scHDzTkjyRZWRdTq8U6L4da+/+Kt
|
||||
ruh8m7Xo2ehSSFyWRSuTSZe5tm/KXgYG
|
||||
-----END PGP PRIVATE KEY BLOCK-----`;
|
||||
const passphrase = 'correct horse battery staple';
|
||||
const encryptedKey = await openpgp.readKey({ armoredKey });
|
||||
|
||||
// avoid argon2's expensive computation
|
||||
const stubArgon2PrimaryKey = sinon.stub(encryptedKey.keyPacket.s2k, 'produceKey').returns(
|
||||
util.hexToUint8Array('832bd2662a5c2b251ee3fc82aec349a766ca539015880133002e5a21960b3bcf'));
|
||||
const stubArgon2Subkey = sinon.stub(encryptedKey.subkeys[0].keyPacket.s2k, 'produceKey').returns(
|
||||
util.hexToUint8Array('f74a6ce873a089ef13a3da9ac059777bb22340d15eaa6c9dc0f8ef09035c67cd'));
|
||||
|
||||
try {
|
||||
const decryptedKey = await openpgp.decryptKey({
|
||||
privateKey: encryptedKey,
|
||||
passphrase
|
||||
});
|
||||
|
||||
const reecryptedKey = await openpgp.encryptKey({
|
||||
privateKey: decryptedKey,
|
||||
passphrase,
|
||||
config: { aeadProtect: true }
|
||||
});
|
||||
expect(reecryptedKey.keyPacket.s2kUsage).to.equal(253);
|
||||
const redecryptedKey = await openpgp.decryptKey({
|
||||
privateKey: reecryptedKey,
|
||||
passphrase
|
||||
});
|
||||
expect(redecryptedKey.write()).to.deep.equal(decryptedKey.write());
|
||||
} finally {
|
||||
stubArgon2PrimaryKey.restore();
|
||||
stubArgon2Subkey.restore();
|
||||
}
|
||||
});
|
||||
|
||||
it('Parsing ECDH key with unknown kdf param version', async function() {
|
||||
// subkey with unknown kdfParam version 255. Parsing should not fail, the subkey should simply dropped
|
||||
const key = await openpgp.readKey({ armoredKey: `-----BEGIN PGP PRIVATE KEY BLOCK-----
|
||||
|
Loading…
x
Reference in New Issue
Block a user