change all calls of getRandomBytes and getRandomBN to be async

This commit is contained in:
Bart Butler 2018-03-05 16:31:56 -08:00
parent b088f005da
commit f57888fe55
26 changed files with 498 additions and 277 deletions

View File

@ -107,9 +107,12 @@ module.exports = function(grunt) {
transform: [
["babelify", {
global: true,
// Only babelify chai-as-promised in node_modules
only: /^(?:.*\/node_modules\/chai-as-promised\/|(?!.*\/node_modules\/)).*$/,
plugins: ["transform-async-to-generator",
"syntax-async-functions",
"transform-regenerator",
"transform-runtime",
"transform-remove-strict-mode"],
ignore: ['*.min.js'],
presets: ["env"]

View File

@ -50,13 +50,15 @@ hash_headers[11] = [0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48, 0x01,
* @param {Integer} length Length of the padding in bytes
* @return {String} Padding as string
*/
function getPkcs1Padding(length) {
async function getPkcs1Padding(length) {
let result = '';
let randomByte;
while (result.length < length) {
randomByte = random.getRandomBytes(1)[0];
if (randomByte !== 0) {
result += String.fromCharCode(randomByte);
// eslint-disable-next-line no-await-in-loop
const randomBytes = await random.getRandomBytes(length - result.length);
for (let i = 0; i < randomBytes.length; i++) {
if (randomBytes[i] !== 0) {
result += String.fromCharCode(randomBytes[i]);
}
}
}
return result;
@ -69,9 +71,9 @@ export default {
* create a EME-PKCS1-v1_5 padding (See {@link https://tools.ietf.org/html/rfc4880#section-13.1.1|RFC 4880 13.1.1})
* @param {String} M message to be encoded
* @param {Integer} k the length in octets of the key modulus
* @return {String} EME-PKCS1 padded message
* @return {Promise<String>} EME-PKCS1 padded message
*/
encode: function(M, k) {
encode: async function(M, k) {
const mLen = M.length;
// length checking
if (mLen > k - 11) {
@ -79,15 +81,14 @@ export default {
}
// Generate an octet string PS of length k - mLen - 3 consisting of
// pseudo-randomly generated nonzero octets
const PS = getPkcs1Padding(k - mLen - 3);
const PS = await getPkcs1Padding(k - mLen - 3);
// Concatenate PS, the message M, and other padding to form an
// encoded message EM of length k octets as EM = 0x00 || 0x02 || PS || 0x00 || M.
const EM = String.fromCharCode(0) +
String.fromCharCode(2) +
PS +
String.fromCharCode(0) +
M;
return EM;
return String.fromCharCode(0) +
String.fromCharCode(2) +
PS +
String.fromCharCode(0) +
M;
},
/**
* decodes a EME-PKCS1-v1_5 padding (See {@link https://tools.ietf.org/html/rfc4880#section-13.1.2|RFC 4880 13.1.2})

View File

@ -48,7 +48,7 @@ export default {
* g, p, q, x are all BN
* returns { r: BN, s: BN }
*/
sign: function(hash_algo, m, g, p, q, x) {
sign: async function(hash_algo, m, g, p, q, x) {
let k;
let r;
let s;
@ -73,7 +73,8 @@ export default {
// or s = 0 if signatures are generated properly.
while (true) {
// See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
k = random.getRandomBN(one, q); // returns in [1, q-1]
// eslint-disable-next-line no-await-in-loop
k = await random.getRandomBN(one, q); // returns in [1, q-1]
r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q
if (zero.cmp(r) === 0) {
continue;
@ -96,7 +97,7 @@ export default {
* p, q, g, y are all BN
* returns BN
*/
verify: function(hash_algo, r, s, m, p, q, g, y) {
verify: async function(hash_algo, r, s, m, p, q, g, y) {
if (zero.ucmp(r) >= 0 || r.ucmp(q) >= 0 ||
zero.ucmp(s) >= 0 || s.ucmp(q) >= 0) {
util.print_debug("invalid DSA Signature");

View File

@ -33,13 +33,13 @@ export default {
* m, p, g, y are all BN
* returns { c1: BN, c2: BN }
*/
encrypt: function(m, p, g, y) {
encrypt: async function(m, p, g, y) {
const redp = new BN.red(p);
const mred = m.toRed(redp);
const gred = g.toRed(redp);
const yred = y.toRed(redp);
// See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
const k = random.getRandomBN(zero, p); // returns in [0, p-1]
const k = await random.getRandomBN(zero, p); // returns in [0, p-1]
return {
c1: gred.redPow(k).fromRed(),
c2: yred.redPow(k).redMul(mred).fromRed()
@ -50,7 +50,7 @@ export default {
* c1, c2, p, x are all BN
* returns BN
*/
decrypt: function(c1, c2, p, x) {
decrypt: async function(c1, c2, p, x) {
const redp = new BN.red(p);
const c1red = c1.toRed(redp);
const c2red = c2.toRed(redp);

View File

@ -178,7 +178,7 @@ Curve.prototype.genKeyPair = async function () {
if (!keyPair || !keyPair.priv) {
// elliptic fallback
const r = await this.curve.genKeyPair({
entropy: util.Uint8Array_to_str(random.getRandomBytes(32))
entropy: util.Uint8Array_to_str(await random.getRandomBytes(32))
});
const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
if (this.keyType === enums.publicKey.eddsa) {

View File

@ -37,15 +37,16 @@ export default {
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return BN
*/
function randomProbablePrime(bits, e, k) {
async function randomProbablePrime(bits, e, k) {
const min = new BN(1).shln(bits - 1);
let n = random.getRandomBN(min, min.shln(1));
let n = await random.getRandomBN(min, min.shln(1));
if (n.isEven()) {
n.iaddn(1); // force odd
}
while (!isProbablePrime(n, e, k)) {
// eslint-disable-next-line no-await-in-loop
while (!await isProbablePrime(n, e, k)) {
n.iaddn(2);
// If reached the maximum, go back to the minimum.
if (n.bitLength() > bits) {
@ -62,17 +63,17 @@ function randomProbablePrime(bits, e, k) {
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return {boolean}
*/
function isProbablePrime(n, e, k) {
async function isProbablePrime(n, e, k) {
if (e && !n.subn(1).gcd(e).eqn(1)) {
return false;
}
if (!fermat(n)) {
return false;
}
if (!millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) {
if (!await millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) {
return false;
}
if (!millerRabin(n, k)) {
if (!await millerRabin(n, k)) {
return false;
}
// TODO implement the Lucas test
@ -138,7 +139,7 @@ const lowprimes = [
* @param {Function} rand Optional function to generate potential witnesses
* @return {boolean}
*/
function millerRabin(n, k, rand) {
async function millerRabin(n, k, rand) {
const len = n.bitLength();
const red = BN.mont(n);
const rone = new BN(1).toRed(red);
@ -155,7 +156,8 @@ function millerRabin(n, k, rand) {
const d = n.shrn(s);
for (; k > 0; k--) {
let a = rand ? rand() : random.getRandomBN(new BN(2), n1);
// eslint-disable-next-line no-await-in-loop
let a = rand ? rand() : await random.getRandomBN(new BN(2), n1);
let x = a.toRed(red).redPow(d);
if (x.eq(rone) || x.eq(rn1))

View File

@ -55,7 +55,7 @@ export default {
* @param d private MPI part as BN
* @return BN
*/
sign: function(m, n, e, d) {
sign: async function(m, n, e, d) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
@ -70,7 +70,7 @@ export default {
* @param e public MPI part as BN
* @return BN
*/
verify: function(s, n, e) {
verify: async function(s, n, e) {
if (n.cmp(s) <= 0) {
throw new Error('Data too large.');
}
@ -85,7 +85,7 @@ export default {
* @param e public MPI part as BN
* @return BN
*/
encrypt: function(m, n, e) {
encrypt: async function(m, n, e) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
@ -104,7 +104,7 @@ export default {
* @param u RSA u as BN
* @return {BN} The decrypted value of the message
*/
decrypt: function(m, n, e, d, p, q, u) {
decrypt: async function(m, n, e, d, p, q, u) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
@ -117,7 +117,7 @@ export default {
let blinder;
let unblinder;
if (config.rsa_blinding) {
unblinder = random.getRandomBN(new BN(2), n).toRed(nred);
unblinder = (await random.getRandomBN(new BN(2), n)).toRed(nred);
blinder = unblinder.redInvm().redPow(e);
m = m.toRed(nred).redMul(blinder).fromRed();
}
@ -204,8 +204,8 @@ export default {
// RSA keygen fallback using 40 iterations of the Miller-Rabin test
// See https://stackoverflow.com/a/6330138 for justification
// Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
let p = prime.randomProbablePrime(B - (B >> 1), E, 40);
let q = prime.randomProbablePrime(B >> 1, E, 40);
let p = await prime.randomProbablePrime(B - (B >> 1), E, 40);
let q = await prime.randomProbablePrime(B >> 1, E, 40);
if (p.cmp(q) < 0) {
[p, q] = [q, p];

View File

@ -34,7 +34,7 @@ export default {
const m = msg_MPIs[0].toBN();
const n = pub_MPIs[0].toBN();
const e = pub_MPIs[1].toBN();
const EM = publicKey.rsa.verify(m, n, e);
const EM = await publicKey.rsa.verify(m, n, e);
const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array_to_str(data), n.byteLength());
return util.Uint8Array_to_hex(EM) === EM2;
}
@ -88,7 +88,7 @@ export default {
const d = key_params[2].toBN();
data = util.Uint8Array_to_str(data);
const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16);
const signature = publicKey.rsa.sign(m, n, e, d);
const signature = await publicKey.rsa.sign(m, n, e, d);
return util.Uint8Array_to_MPI(signature);
}
case enums.publicKey.dsa: {
@ -96,7 +96,7 @@ export default {
const q = key_params[1].toBN();
const g = key_params[2].toBN();
const x = key_params[4].toBN();
const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
const signature = await publicKey.dsa.sign(hash_algo, data, g, p, q, x);
return util.concatUint8Array([
util.Uint8Array_to_MPI(signature.r),
util.Uint8Array_to_MPI(signature.s)

View File

@ -380,35 +380,32 @@ Key.prototype.getEncryptionKeyPacket = function(keyId, date=new Date()) {
* Encrypts all secret key and subkey packets
* @param {String} passphrase
*/
Key.prototype.encrypt = function(passphrase) {
Key.prototype.encrypt = async function(passphrase) {
if (!this.isPrivate()) {
throw new Error("Nothing to encrypt in a public key");
}
const keys = this.getAllKeyPackets();
for (let i = 0; i < keys.length; i++) {
keys[i].encrypt(passphrase);
keys[i].clearPrivateParams();
}
await Promise.all(keys.map(async function(packet) {
await packet.encrypt(passphrase);
await packet.clearPrivateParams();
return packet;
}));
return true;
};
/**
* Decrypts all secret key and subkey packets
* @param {String} passphrase
* @return {Boolean} true if all key and subkey packets decrypted successfully
* @return {Promise<Boolean>} true if all key and subkey packets decrypted successfully
*/
Key.prototype.decrypt = function(passphrase) {
if (this.isPrivate()) {
const keys = this.getAllKeyPackets();
for (let i = 0; i < keys.length; i++) {
const success = keys[i].decrypt(passphrase);
if (!success) {
return false;
}
}
} else {
Key.prototype.decrypt = async function(passphrase) {
if (!this.isPrivate()) {
throw new Error("Nothing to decrypt in a public key");
}
const keys = this.getAllKeyPackets();
await Promise.all(keys.map(packet => packet.decrypt(passphrase)));
return true;
};
@ -1254,46 +1251,48 @@ export function generate(options) {
* @return {module:key~Key}
* @static
*/
export function reformat(options) {
export async function reformat(options) {
let secretKeyPacket;
let secretSubkeyPacket;
return Promise.resolve().then(() => {
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
throw new Error('Only RSA Encrypt or Sign supported');
}
if (!options.privateKey.decrypt()) {
throw new Error('Key not decrypted');
}
options.keyType = options.keyType || enums.publicKey.rsa_encrypt_sign;
if (options.keyType !== enums.publicKey.rsa_encrypt_sign) { // RSA Encrypt-Only and RSA Sign-Only are deprecated and SHOULD NOT be generated
throw new Error('Only RSA Encrypt or Sign supported');
}
if (!options.passphrase) { // Key without passphrase is unlocked by definition
options.unlocked = true;
try {
await options.privateKey.decrypt();
}
catch(err) {
throw new Error('Key not decrypted');
}
if (!options.passphrase) { // Key without passphrase is unlocked by definition
options.unlocked = true;
}
if (util.isString(options.userIds)) {
options.userIds = [options.userIds];
}
const packetlist = options.privateKey.toPacketlist();
for (let i = 0; i < packetlist.length; i++) {
if (packetlist[i].tag === enums.packet.secretKey) {
secretKeyPacket = packetlist[i];
options.keyType = secretKeyPacket.algorithm;
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
secretSubkeyPacket = packetlist[i];
options.subkeyType = secretSubkeyPacket.algorithm;
}
if (util.isString(options.userIds)) {
options.userIds = [options.userIds];
}
const packetlist = options.privateKey.toPacketlist();
for (let i = 0; i < packetlist.length; i++) {
if (packetlist[i].tag === enums.packet.secretKey) {
secretKeyPacket = packetlist[i];
options.keyType = secretKeyPacket.algorithm;
} else if (packetlist[i].tag === enums.packet.secretSubkey) {
secretSubkeyPacket = packetlist[i];
options.subkeyType = secretSubkeyPacket.algorithm;
}
}
if (!secretKeyPacket) {
throw new Error('Key does not contain a secret key packet');
}
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
});
}
if (!secretKeyPacket) {
throw new Error('Key does not contain a secret key packet');
}
return wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options);
}
async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
// set passphrase protection
if (options.passphrase) {
secretKeyPacket.encrypt(options.passphrase);
await secretKeyPacket.encrypt(options.passphrase);
if (secretSubkeyPacket) {
secretSubkeyPacket.encrypt(options.passphrase);
}

View File

@ -262,7 +262,7 @@ Message.prototype.encrypt = function(keys, passwords, sessionKey, wildcard=false
}
if (!sessionKey) {
sessionKey = crypto.generateSessionKey(symAlgo);
sessionKey = await crypto.generateSessionKey(symAlgo);
}
msg = await encryptSessionKey(sessionKey, symAlgo, keys, passwords, wildcard, date);

View File

@ -179,6 +179,26 @@ export function decryptKey({ privateKey, passphrase }) {
}).catch(onError.bind(null, 'Error decrypting private key'));
}
/**
* Lock a private key with your passphrase.
* @param {Key} privateKey the private key that is to be decrypted
* @param {String} passphrase the user's passphrase chosen during key generation
* @return {Key} the locked private key
*/
export function encryptKey({ privateKey, passphrase }) {
if (asyncProxy) { // use web worker if available
return asyncProxy.delegate('encryptKey', { privateKey, passphrase });
}
return Promise.resolve().then(async function() {
await privateKey.encrypt(passphrase);
return {
key: privateKey
};
}).catch(onError.bind(null, 'Error decrypting private key'));
}
///////////////////////////////////////////
// //

View File

@ -112,7 +112,7 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
if (algo === enums.publicKey.ecdh) {
toEncrypt = new type_mpi(crypto.pkcs5.encode(data));
} else {
toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
toEncrypt = new type_mpi(await crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
}
this.encrypted = await crypto.publicKeyEncrypt(

View File

@ -81,7 +81,7 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
const hash = util.Uint8Array_to_str(hashfn(cleartext));
if (hash !== hashtext) {
return new Error("Hash mismatch.");
return new Error("Incorrect key passphrase");
}
const algo = enums.write(enums.publicKey, algorithm);
@ -173,7 +173,7 @@ SecretKey.prototype.write = function () {
* This can be used to remove passphrase protection after calling decrypt().
* @param {String} passphrase
*/
SecretKey.prototype.encrypt = function (passphrase) {
SecretKey.prototype.encrypt = async function (passphrase) {
if (this.isDecrypted && !passphrase) {
this.encrypted = null;
return;
@ -182,11 +182,12 @@ SecretKey.prototype.encrypt = function (passphrase) {
}
const s2k = new type_s2k();
s2k.salt = await crypto.random.getRandomBytes(8);
const symmetric = 'aes256';
const cleartext = write_cleartext_params('sha1', this.algorithm, this.params);
const key = produceEncryptionKey(s2k, passphrase, symmetric);
const blockLen = crypto.cipher[symmetric].blockSize;
const iv = crypto.random.getRandomBytes(blockLen);
const iv = await crypto.random.getRandomBytes(blockLen);
const arr = [new Uint8Array([254, enums.write(enums.symmetric, symmetric)])];
arr.push(s2k.write());
@ -213,7 +214,7 @@ function produceEncryptionKey(s2k, passphrase, algorithm) {
* @return {Boolean} True if the passphrase was correct or param already
* decrypted; false if not
*/
SecretKey.prototype.decrypt = function (passphrase) {
SecretKey.prototype.decrypt = async function (passphrase) {
if (this.isDecrypted) {
return true;
}
@ -261,12 +262,11 @@ SecretKey.prototype.decrypt = function (passphrase) {
const privParams = parse_cleartext_params(hash, cleartext, this.algorithm);
if (privParams instanceof Error) {
return false;
throw privParams;
}
this.params = this.params.concat(privParams);
this.isDecrypted = true;
this.encrypted = null;
return true;
};
SecretKey.prototype.generate = function (bits, curve) {

View File

@ -66,10 +66,8 @@ SymEncryptedAEADProtected.prototype.write = function () {
* @param {Uint8Array} key The session key used to encrypt the payload
* @return {Promise<undefined>} Nothing is returned
*/
SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) {
return crypto.gcm.decrypt(sessionKeyAlgorithm, this.encrypted, key, this.iv).then(decrypted => {
this.packets.read(decrypted);
});
SymEncryptedAEADProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
this.packets.read(await crypto.gcm.decrypt(sessionKeyAlgorithm, this.encrypted, key, this.iv));
};
/**
@ -78,9 +76,7 @@ SymEncryptedAEADProtected.prototype.decrypt = function (sessionKeyAlgorithm, key
* @param {Uint8Array} key The session key used to encrypt the payload
* @return {Promise<undefined>} Nothing is returned
*/
SymEncryptedAEADProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) {
this.iv = crypto.random.getRandomBytes(IV_LEN); // generate new random IV
return crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv).then(encrypted => {
this.encrypted = encrypted;
});
SymEncryptedAEADProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
this.iv = await crypto.random.getRandomBytes(IV_LEN); // generate new random IV
this.encrypted = await crypto.gcm.encrypt(sessionKeyAlgorithm, this.packets.write(), key, this.iv);
};

View File

@ -81,9 +81,9 @@ SymEncryptedIntegrityProtected.prototype.write = function () {
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @return {Promise}
*/
SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm, key) {
SymEncryptedIntegrityProtected.prototype.encrypt = async function (sessionKeyAlgorithm, key) {
const bytes = this.packets.write();
const prefixrandom = crypto.getPrefixRandom(sessionKeyAlgorithm);
const prefixrandom = await crypto.getPrefixRandom(sessionKeyAlgorithm);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
const prefix = util.concatUint8Array([prefixrandom, repeat]);
const mdc = new Uint8Array([0xD3, 0x14]); // modification detection code packet
@ -98,8 +98,6 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
this.encrypted = crypto.cfb.encrypt(prefixrandom, sessionKeyAlgorithm, tohash, key, false);
this.encrypted = this.encrypted.subarray(0, prefix.length + tohash.length);
}
return Promise.resolve();
};
/**
@ -108,7 +106,7 @@ SymEncryptedIntegrityProtected.prototype.encrypt = function (sessionKeyAlgorithm
* @param {Uint8Array} key The key of cipher blocksize length to be used
* @return {Promise}
*/
SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm, key) {
SymEncryptedIntegrityProtected.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
let decrypted;
if (sessionKeyAlgorithm.substr(0, 3) === 'aes') { // AES optimizations. Native code for node, asmCrypto for browser.
decrypted = aesDecrypt(sessionKeyAlgorithm, this.encrypted, key);

View File

@ -52,7 +52,7 @@ export default function SymEncryptedSessionKey() {
this.sessionKeyEncryptionAlgorithm = null;
this.sessionKeyAlgorithm = 'aes256';
this.encrypted = null;
this.s2k = new type_s2k();
this.s2k = null;
}
/**
@ -73,6 +73,7 @@ SymEncryptedSessionKey.prototype.read = function(bytes) {
const algo = enums.read(enums.symmetric, bytes[1]);
// A string-to-key (S2K) specifier, length as defined above.
this.s2k = new type_s2k();
const s2klength = this.s2k.read(bytes.subarray(2, bytes.length));
// Optionally, the encrypted session key itself, which is decrypted
@ -106,7 +107,7 @@ SymEncryptedSessionKey.prototype.write = function() {
*
* @return {Uint8Array} The unencrypted session key
*/
SymEncryptedSessionKey.prototype.decrypt = function(passphrase) {
SymEncryptedSessionKey.prototype.decrypt = async function(passphrase) {
const algo = this.sessionKeyEncryptionAlgorithm !== null ?
this.sessionKeyEncryptionAlgorithm :
this.sessionKeyAlgorithm;
@ -124,20 +125,23 @@ SymEncryptedSessionKey.prototype.decrypt = function(passphrase) {
}
};
SymEncryptedSessionKey.prototype.encrypt = function(passphrase) {
SymEncryptedSessionKey.prototype.encrypt = async function(passphrase) {
const algo = this.sessionKeyEncryptionAlgorithm !== null ?
this.sessionKeyEncryptionAlgorithm :
this.sessionKeyAlgorithm;
this.sessionKeyEncryptionAlgorithm = algo;
this.s2k = new type_s2k();
this.s2k.salt = await crypto.random.getRandomBytes(8);
const length = crypto.cipher[algo].keySize;
const key = this.s2k.produce_key(passphrase, length);
const algo_enum = new Uint8Array([enums.write(enums.symmetric, this.sessionKeyAlgorithm)]);
if (this.sessionKey === null) {
this.sessionKey = crypto.getRandomBytes(crypto.cipher[this.sessionKeyAlgorithm].keySize);
this.sessionKey = await crypto.generateSessionKey(this.sessionKeyAlgorithm);
}
const private_key = util.concatUint8Array([algo_enum, this.sessionKey]);

View File

@ -62,7 +62,7 @@ SymmetricallyEncrypted.prototype.write = function () {
* Key as string with the corresponding length to the
* algorithm
*/
SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
SymmetricallyEncrypted.prototype.decrypt = async function (sessionKeyAlgorithm, key) {
const decrypted = crypto.cfb.decrypt(sessionKeyAlgorithm, key, this.encrypted, true);
// for modern cipher (blocklength != 64 bit, except for Twofish) MDC is required
if (!this.ignore_mdc_error &&
@ -76,10 +76,10 @@ SymmetricallyEncrypted.prototype.decrypt = function (sessionKeyAlgorithm, key) {
return Promise.resolve();
};
SymmetricallyEncrypted.prototype.encrypt = function (algo, key) {
SymmetricallyEncrypted.prototype.encrypt = async function (algo, key) {
const data = this.packets.write();
this.encrypted = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, data, key, true);
this.encrypted = crypto.cfb.encrypt(await crypto.getPrefixRandom(algo), algo, data, key, true);
return Promise.resolve();
};

View File

@ -46,7 +46,7 @@ export default function S2K() {
/** Eight bytes of salt in a binary string.
* @type {String}
*/
this.salt = crypto.random.getRandomBytes(8);
this.salt = null;
}
S2K.prototype.get_count = function () {

View File

@ -85,8 +85,8 @@ AsyncProxy.prototype.onMessage = function(event) {
* Send message to worker with random data
* @param {Integer} size Number of bytes to send
*/
AsyncProxy.prototype.seedRandom = function(size) {
const buf = crypto.random.getRandomBytes(size);
AsyncProxy.prototype.seedRandom = async function(size) {
const buf = await crypto.random.getRandomBytes(size);
this.worker.postMessage({ event:'seed-random', buf }, util.getTransferables(buf));
};

View File

@ -373,11 +373,10 @@ describe('API functional testing', function() {
it('Asymmetric using RSA with eme_pkcs1 padding', function () {
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
const RSAUnencryptedData = crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())
const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData);
return crypto.publicKeyEncrypt(
1, RSApubMPIs, RSAUnencryptedMPI
).then(RSAEncryptedData => {
return crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()).then(RSAUnencryptedData => {
const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData);
return crypto.publicKeyEncrypt(1, RSApubMPIs, RSAUnencryptedMPI);
}).then(RSAEncryptedData => {
return crypto.publicKeyDecrypt(
1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
@ -393,12 +392,10 @@ describe('API functional testing', function() {
it('Asymmetric using Elgamal with eme_pkcs1 padding', function () {
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
const ElgamalUnencryptedData = crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength());
const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData);
return crypto.publicKeyEncrypt(
16, ElgamalpubMPIs, ElgamalUnencryptedMPI
).then(ElgamalEncryptedData => {
return crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()).then(ElgamalUnencryptedData => {
const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData);
return crypto.publicKeyEncrypt(16, ElgamalpubMPIs, ElgamalUnencryptedMPI);
}).then(ElgamalEncryptedData => {
return crypto.publicKeyDecrypt(
16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData

View File

@ -142,7 +142,7 @@ describe('Elliptic Curve Cryptography', function () {
data[name].pub_key = pub.keys[0];
return data[name].pub_key;
}
function load_priv_key(name) {
async function load_priv_key(name) {
if (data[name].priv_key) {
return data[name].priv_key;
}
@ -151,7 +151,7 @@ describe('Elliptic Curve Cryptography', function () {
expect(pk.err).to.not.exist;
expect(pk.keys).to.have.length(1);
expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id);
expect(pk.keys[0].decrypt(data[name].pass)).to.be.true;
expect(await pk.keys[0].decrypt(data[name].pass)).to.be.true;
data[name].priv_key = pk.keys[0];
return data[name].priv_key;
}
@ -160,10 +160,10 @@ describe('Elliptic Curve Cryptography', function () {
load_pub_key('juliet');
done();
});
it('Load private key', function (done) {
load_priv_key('romeo');
load_priv_key('juliet');
done();
it('Load private key', async function () {
await load_priv_key('romeo');
await load_priv_key('juliet');
return true;
});
it('Verify clear signed message', function () {
const pub = load_pub_key('juliet');
@ -175,52 +175,45 @@ describe('Elliptic Curve Cryptography', function () {
expect(result.signatures[0].valid).to.be.true;
});
});
it('Sign message', function () {
const romeo = load_priv_key('romeo');
return openpgp.sign({privateKeys: [romeo], data: data.romeo.message + "\n"}).then(function (signed) {
const romeo = load_pub_key('romeo');
const msg = openpgp.cleartext.readArmored(signed.data);
return openpgp.verify({publicKeys: [romeo], message: msg}).then(function (result) {
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.romeo.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
});
it('Sign message', async function () {
const romeoPrivate = await load_priv_key('romeo');
const signed = await openpgp.sign({privateKeys: [romeoPrivate], data: data.romeo.message + "\n"});
const romeoPublic = load_pub_key('romeo');
const msg = openpgp.cleartext.readArmored(signed.data);
const result = await openpgp.verify({publicKeys: [romeoPublic], message: msg});
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.romeo.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
it('Decrypt and verify message', function () {
it('Decrypt and verify message', async function () {
const juliet = load_pub_key('juliet');
const romeo = load_priv_key('romeo');
const romeo = await load_priv_key('romeo');
const msg = openpgp.message.readArmored(data.juliet.message_encrypted);
return openpgp.decrypt(
{privateKeys: romeo, publicKeys: [juliet], message: msg}
).then(function (result) {
expect(result).to.exist;
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
expect(result.data.trim()).to.equal(data.juliet.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
const result = await openpgp.decrypt({privateKeys: romeo, publicKeys: [juliet], message: msg});
expect(result).to.exist;
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
expect(result.data.trim()).to.equal(data.juliet.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
it('Encrypt and sign message', function () {
const romeo = load_priv_key('romeo');
const juliet = load_pub_key('juliet');
expect(romeo.decrypt(data.romeo.pass)).to.be.true;
return openpgp.encrypt(
{publicKeys: [juliet], privateKeys: [romeo], data: data.romeo.message + "\n"}
).then(function (encrypted) {
const message = openpgp.message.readArmored(encrypted.data);
const romeo = load_pub_key('romeo');
const juliet = load_priv_key('juliet');
return openpgp.decrypt(
{privateKeys: juliet, publicKeys: [romeo], message: message}
).then(function (result) {
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.romeo.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
});
it('Encrypt and sign message', async function () {
const romeoPrivate = await load_priv_key('romeo');
const julietPublic = load_pub_key('juliet');
expect(await romeoPrivate.decrypt(data.romeo.pass)).to.be.true;
const encrypted = await openpgp.encrypt({publicKeys: [julietPublic], privateKeys: [romeoPrivate], data: data.romeo.message + "\n"});
const message = openpgp.message.readArmored(encrypted.data);
const romeoPublic = load_pub_key('romeo');
const julietPrivate = await load_priv_key('juliet');
const result = await openpgp.decrypt({privateKeys: julietPrivate, publicKeys: [romeoPublic], message: message});
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.romeo.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
it('Generate key', function () {
const options = {

View File

@ -1037,26 +1037,24 @@ describe('Key', function() {
});
});
it('Encrypt key with new passphrase', function() {
it('Encrypt key with new passphrase', async function() {
const userId = 'test <a@b.com>';
const opt = {numBits: 512, userIds: userId, passphrase: 'passphrase'};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
return openpgp.generateKey(opt).then(function(key) {
key = key.key;
const armor1 = key.armor();
const armor2 = key.armor();
expect(armor1).to.equal(armor2);
expect(key.decrypt('passphrase')).to.be.true;
expect(key.primaryKey.isDecrypted).to.be.true;
key.encrypt('new_passphrase');
expect(key.primaryKey.isDecrypted).to.be.false;
expect(key.decrypt('passphrase')).to.be.false;
expect(key.primaryKey.isDecrypted).to.be.false;
expect(key.decrypt('new_passphrase')).to.be.true;
expect(key.primaryKey.isDecrypted).to.be.true;
const armor3 = key.armor();
expect(armor3).to.not.equal(armor1);
});
const key = (await openpgp.generateKey(opt)).key;
const armor1 = key.armor();
const armor2 = key.armor();
expect(armor1).to.equal(armor2);
expect(await key.decrypt('passphrase')).to.be.true;
expect(key.primaryKey.isDecrypted).to.be.true;
await key.encrypt('new_passphrase');
expect(key.primaryKey.isDecrypted).to.be.false;
await expect(key.decrypt('passphrase')).to.eventually.be.rejectedWith('Incorrect key passphrase');
expect(key.primaryKey.isDecrypted).to.be.false;
expect(await key.decrypt('new_passphrase')).to.be.true;
expect(key.primaryKey.isDecrypted).to.be.true;
const armor3 = key.armor();
expect(armor3).to.not.equal(armor1);
});
it('Generate key - ensure keyExpirationTime works', function() {
@ -1268,7 +1266,7 @@ describe('Key', function() {
});
});
it('Throw user friendly error when reformatting encrypted key', function() {
it('Reject with user-friendly error when reformatting encrypted key', function() {
const opt = {numBits: 512, userIds: 'test1 <a@b.com>', passphrase: '1234'};
if (openpgp.util.getWebCryptoAll()) { opt.numBits = 2048; } // webkit webcrypto accepts minimum 2048 bit keys
return openpgp.generateKey(opt).then(function(original) {

View File

@ -635,12 +635,12 @@ describe('OpenPGP.js public api tests', function() {
openpgp.config.aead_protect = aead_protectVal;
});
it('Decrypting key with wrong passphrase returns false', function () {
expect(privateKey.keys[0].decrypt('wrong passphrase')).to.be.false;
it('Decrypting key with wrong passphrase rejected', function () {
expect(privateKey.keys[0].decrypt('wrong passphrase')).to.eventually.be.rejectedWith('Incorrect key passphrase');
});
it('Decrypting key with correct passphrase returns true', function () {
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
it('Decrypting key with correct passphrase returns true', async function () {
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
});
tryTests('CFB mode (asm.js)', tests, {
@ -719,7 +719,7 @@ describe('OpenPGP.js public api tests', function() {
privateKey: privateKey.keys[0],
passphrase: 'incorrect'
}).catch(function(error){
expect(error.message).to.match(/Invalid passphrase/);
expect(error.message).to.match(/Incorrect key passphrase/);
});
});
});
@ -727,9 +727,10 @@ describe('OpenPGP.js public api tests', function() {
describe('encryptSessionKey, decryptSessionKeys', function() {
const sk = new Uint8Array([0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01,0x01]);
beforeEach(function(done) {
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
privateKey.keys[0].verifyPrimaryUser().then(() => done());
beforeEach(async function() {
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
await privateKey.keys[0].verifyPrimaryUser();
return true;
});
it('should encrypt with public key', function() {
@ -867,14 +868,13 @@ describe('OpenPGP.js public api tests', function() {
'=6XMW\r\n' +
'-----END PGP PUBLIC KEY BLOCK-----\r\n\r\n';
beforeEach(function (done) {
expect(privateKey.keys[0].decrypt(passphrase)).to.be.true;
Promise.all([
privateKey.keys[0].verifyPrimaryUser(),
privateKey_2000_2008.keys[0].verifyPrimaryUser(),
privateKey_1337.keys[0].verifyPrimaryUser(),
privateKey_2038_2045.keys[0].verifyPrimaryUser()
]).then(() => done());
beforeEach( async function () {
expect(await privateKey.keys[0].decrypt(passphrase)).to.be.true;
await privateKey.keys[0].verifyPrimaryUser();
await privateKey_2000_2008.keys[0].verifyPrimaryUser();
await privateKey_1337.keys[0].verifyPrimaryUser();
await privateKey_2038_2045.keys[0].verifyPrimaryUser();
return true;
});
it('should encrypt then decrypt', function () {

219
test/general/openpgp2.js Normal file
View File

@ -0,0 +1,219 @@
/* globals tryTests: true */
'use strict';
var openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
var sinon = require('sinon'),
chai = require('chai');
chai.use(require('chai-as-promised'));
var expect = chai.expect;
var pub_key =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'mI0EUmEvTgEEANyWtQQMOybQ9JltDqmaX0WnNPJeLILIM36sw6zL0nfTQ5zXSS3+',
'fIF6P29lJFxpblWk02PSID5zX/DYU9/zjM2xPO8Oa4xo0cVTOTLj++Ri5mtr//f5',
'GLsIXxFrBJhD/ghFsL3Op0GXOeLJ9A5bsOn8th7x6JucNKuaRB6bQbSPABEBAAG0',
'JFRlc3QgTWNUZXN0aW5ndG9uIDx0ZXN0QGV4YW1wbGUuY29tPoi5BBMBAgAjBQJS',
'YS9OAhsvBwsJCAcDAgEGFQgCCQoLBBYCAwECHgECF4AACgkQSmNhOk1uQJQwDAP6',
'AgrTyqkRlJVqz2pb46TfbDM2TDF7o9CBnBzIGoxBhlRwpqALz7z2kxBDmwpQa+ki',
'Bq3jZN/UosY9y8bhwMAlnrDY9jP1gdCo+H0sD48CdXybblNwaYpwqC8VSpDdTndf',
'9j2wE/weihGp/DAdy/2kyBCaiOY1sjhUfJ1GogF49rC4jQRSYS9OAQQA6R/PtBFa',
'JaT4jq10yqASk4sqwVMsc6HcifM5lSdxzExFP74naUMMyEsKHP53QxTF0Grqusag',
'Qg/ZtgT0CN1HUM152y7ACOdp1giKjpMzOTQClqCoclyvWOFB+L/SwGEIJf7LSCEr',
'woBuJifJc8xAVr0XX0JthoW+uP91eTQ3XpsAEQEAAYkBPQQYAQIACQUCUmEvTgIb',
'LgCoCRBKY2E6TW5AlJ0gBBkBAgAGBQJSYS9OAAoJEOCE90RsICyXuqIEANmmiRCA',
'SF7YK7PvFkieJNwzeK0V3F2lGX+uu6Y3Q/Zxdtwc4xR+me/CSBmsURyXTO29OWhP',
'GLszPH9zSJU9BdDi6v0yNprmFPX/1Ng0Abn/sCkwetvjxC1YIvTLFwtUL/7v6NS2',
'bZpsUxRTg9+cSrMWWSNjiY9qUKajm1tuzPDZXAUEAMNmAN3xXN/Kjyvj2OK2ck0X',
'W748sl/tc3qiKPMJ+0AkMF7Pjhmh9nxqE9+QCEl7qinFqqBLjuzgUhBU4QlwX1GD',
'AtNTq6ihLMD5v1d82ZC7tNatdlDMGWnIdvEMCv2GZcuIqDQ9rXWs49e7tq1NncLY',
'hz3tYjKhoFTKEIq3y3Pp',
'=h/aX',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var priv_key =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: GnuPG v2.0.19 (GNU/Linux)',
'',
'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',
'=lw5e',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
var pub_key_de =
['-----BEGIN PGP PUBLIC KEY BLOCK-----',
'Version: GnuPG v2.0.22 (GNU/Linux)',
'',
'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',
'og2umGfGng==',
'=v3+L',
'-----END PGP PUBLIC KEY BLOCK-----'].join('\n');
var priv_key_de =
['-----BEGIN PGP PRIVATE KEY BLOCK-----',
'Version: GnuPG v2.0.22 (GNU/Linux)',
'',
'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',
'DHTORVqAOdoOcu5Qh7AQg1GdSmfFAsx2',
'=kyeP',
'-----END PGP PRIVATE KEY BLOCK-----'].join('\n');
var passphrase = 'hello world';
var plaintext = 'short message\nnext line\n한국어/조선말';
var password1 = 'I am a password';
var password2 = 'I am another password';
var fuckedUp = ['-----BEGIN PGP MESSAGE-----',
'Version: OpenPGP.js v3.0.0',
'Comment: https://openpgpjs.org',
'',
'wy4ECQMIWjj3WEfWxGpgrfb3vXu0TS9L8UNTBvNZFIjltGjMVkLFD+/afgs5',
'aXt0wy4ECQMIrFo3TFN5xqtgtB+AaAjBcWJrA4bvIPBpJ38PbMWeF0JQgrqg',
'j3uehxXy0mUB5i7B61g0ho+YplyFGM0s9XayJCnu40tWmr5LqqsRxuwrhJKR',
'migslOF/l6Y9F0F9xGIZWGhxp3ugQPjVKjj8fOH7ap14mLm60C8q8AOxiSmL',
'ubsd/hL7FPZatUYAAZVA0a6hmQ==',
'=cHCV',
'-----END PGP MESSAGE-----'].join('\n');
describe('encrypt, decrypt, sign, verify - integration tests', function() {
var privateKey, publicKey, zero_copyVal, use_nativeVal, aead_protectVal;
beforeEach(function(done) {
publicKey = openpgp.key.readArmored(pub_key);
expect(publicKey.keys).to.have.length(1);
expect(publicKey.err).to.not.exist;
privateKey = openpgp.key.readArmored(priv_key);
expect(privateKey.keys).to.have.length(1);
expect(privateKey.err).to.not.exist;
zero_copyVal = openpgp.config.zero_copy;
use_nativeVal = openpgp.config.use_native;
aead_protectVal = openpgp.config.aead_protect;
privateKey.keys[0].verifyPrimaryUser().then(() => done());
});
afterEach(function() {
openpgp.config.zero_copy = zero_copyVal;
openpgp.config.use_native = use_nativeVal;
openpgp.config.aead_protect = aead_protectVal;
});
it('should encrypt and decrypt with two passwords', function() {
var encOpt = {
data: plaintext,
passwords: [password1, password2]
};
var decOpt = {
password: password2
};
return openpgp.encrypt(encOpt).then(function(encrypted) {
encrypted.data = fuckedUp;
//console.log(encrypted.data);
decOpt.message = openpgp.message.readArmored(encrypted.data);
return openpgp.decrypt(decOpt);
}).then(function(decrypted) {
expect(decrypted.data).to.equal(plaintext);
expect(decrypted.signatures.length).to.equal(0);
});
});
});

View File

@ -55,7 +55,7 @@ describe("Packet", function() {
'=KXkj\n' +
'-----END PGP PRIVATE KEY BLOCK-----';
it('Symmetrically encrypted packet', function(done) {
it('Symmetrically encrypted packet', async function() {
const message = new openpgp.packet.List();
const literal = new openpgp.packet.Literal();
@ -68,18 +68,17 @@ describe("Packet", function() {
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
const algo = 'aes256';
enc.encrypt(algo, key);
await enc.encrypt(algo, key);
const msg2 = new openpgp.packet.List();
msg2.read(message.write());
msg2[0].ignore_mdc_error = true;
msg2[0].decrypt(algo, key);
await msg2[0].decrypt(algo, key);
expect(stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
done();
});
it('Symmetrically encrypted packet - MDC error for modern cipher', function() {
it('Symmetrically encrypted packet - MDC error for modern cipher', async function() {
const message = new openpgp.packet.List();
const literal = new openpgp.packet.Literal();
@ -87,19 +86,19 @@ describe("Packet", function() {
const enc = new openpgp.packet.SymmetricallyEncrypted();
message.push(enc);
enc.packets.push(literal);
await enc.packets.push(literal);
const key = '12345678901234567890123456789012';
const algo = 'aes256';
enc.encrypt(algo, key);
await enc.encrypt(algo, key);
const msg2 = new openpgp.packet.List();
msg2.read(message.write());
expect(msg2[0].decrypt.bind(msg2[0], algo, key)).to.throw('Decryption failed due to missing MDC in combination with modern cipher.');
expect(msg2[0].decrypt(algo, key)).to.eventually.be.rejectedWith('Decryption failed due to missing MDC in combination with modern cipher.');
});
it('Sym. encrypted integrity protected packet', function(done) {
it('Sym. encrypted integrity protected packet', async function() {
const key = new Uint8Array([1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2,3,4,5,6,7,8,9,0,1,2]);
const algo = 'aes256';
@ -110,15 +109,14 @@ describe("Packet", function() {
msg.push(enc);
literal.setText('Hello world!');
enc.packets.push(literal);
enc.encrypt(algo, key);
await enc.encrypt(algo, key);
const msg2 = new openpgp.packet.List();
msg2.read(msg.write());
msg2[0].decrypt(algo, key);
await msg2[0].decrypt(algo, key);
expect(stringify(msg2[0].packets[0].data)).to.equal(stringify(literal.data));
done();
});
it('Sym. encrypted AEAD protected packet', function() {
@ -310,7 +308,7 @@ describe("Packet", function() {
});
});
it('Sym. encrypted session key reading/writing', function(done) {
it('Sym. encrypted session key reading/writing', async function() {
const passphrase = 'hello';
const algo = 'aes256';
@ -323,23 +321,22 @@ describe("Packet", function() {
msg.push(enc);
key_enc.sessionKeyAlgorithm = algo;
key_enc.decrypt(passphrase);
await key_enc.encrypt(passphrase);
const key = key_enc.sessionKey;
literal.setText('Hello world!');
enc.packets.push(literal);
enc.encrypt(algo, key);
await enc.encrypt(algo, key);
const msg2 = new openpgp.packet.List();
msg2.read(msg.write());
msg2[0].decrypt(passphrase);
await msg2[0].decrypt(passphrase);
const key2 = msg2[0].sessionKey;
msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2);
await msg2[1].decrypt(msg2[0].sessionKeyAlgorithm, key2);
expect(stringify(msg2[1].packets[0].data)).to.equal(stringify(literal.data));
done();
});
it('Secret key encryption/decryption test', function() {

View File

@ -127,7 +127,7 @@ describe('X25519 Cryptography', function () {
return data[name].pub_key;
}
function load_priv_key(name) {
async function load_priv_key(name) {
if (data[name].priv_key) {
return data[name].priv_key;
}
@ -136,7 +136,7 @@ describe('X25519 Cryptography', function () {
expect(pk.err).to.not.exist;
expect(pk.keys).to.have.length(1);
expect(pk.keys[0].primaryKey.getKeyId().toHex()).to.equal(data[name].id);
expect(pk.keys[0].decrypt(data[name].pass)).to.be.true;
expect(await pk.keys[0].decrypt(data[name].pass)).to.be.true;
data[name].priv_key = pk.keys[0];
return data[name].priv_key;
}
@ -149,10 +149,10 @@ describe('X25519 Cryptography', function () {
// This test is slow because the keys are generated by GPG2, which
// by default chooses a larger number for S2K iterations than we do.
it('Load private key', function (done) {
load_priv_key('light');
load_priv_key('night');
done();
it('Load private key', async function () {
await load_priv_key('light');
await load_priv_key('night');
return true;
});
it('Verify clear signed message', function () {
@ -167,56 +167,49 @@ describe('X25519 Cryptography', function () {
});
});
it('Sign message', function () {
it('Sign message', async function () {
const name = 'light';
const priv = load_priv_key(name);
return openpgp.sign({ privateKeys: [priv], data: data[name].message + "\n" }).then(function (signed) {
const pub = load_pub_key(name);
const msg = openpgp.cleartext.readArmored(signed.data);
return openpgp.verify({ publicKeys: [pub], message: msg}).then(function (result) {
expect(result).to.exist;
expect(result.data.trim()).to.equal(data[name].message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
});
const priv = await load_priv_key(name);
const signed = await openpgp.sign({ privateKeys: [priv], data: data[name].message + "\n" });
const pub = load_pub_key(name);
const msg = openpgp.cleartext.readArmored(signed.data);
const result = await openpgp.verify({ publicKeys: [pub], message: msg});
expect(result).to.exist;
expect(result.data.trim()).to.equal(data[name].message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
it('Decrypt and verify message', function () {
it('Decrypt and verify message', async function () {
const light = load_pub_key('light');
const night = load_priv_key('night');
expect(night.decrypt(data.night.pass)).to.be.true;
const night = await load_priv_key('night');
expect(await night.decrypt(data.night.pass)).to.be.true;
const msg = openpgp.message.readArmored(data.night.message_encrypted);
return openpgp.decrypt(
{ privateKeys: night, publicKeys: [light], message: msg }
).then(function (result) {
expect(result).to.exist;
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
expect(result.data.trim()).to.equal(data.night.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
const result = await openpgp.decrypt({ privateKeys: night, publicKeys: [light], message: msg });
expect(result).to.exist;
// trim required because https://github.com/openpgpjs/openpgpjs/issues/311
expect(result.data.trim()).to.equal(data.night.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
it('Encrypt and sign message', function () {
const night = load_pub_key('night');
const light = load_priv_key('light');
expect(light.decrypt(data.light.pass)).to.be.true;
openpgp.encrypt(
{ publicKeys: [night], privateKeys: [light], data: data.light.message + "\n" }
).then(function (encrypted) {
const message = openpgp.message.readArmored(encrypted.data);
const light = load_pub_key('light');
const night = load_priv_key('night');
return openpgp.decrypt(
{ privateKeys: night, publicKeys: [light], message: message }
).then(function (result) {
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.light.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
});
it('Encrypt and sign message', async function () {
const nightPublic = load_pub_key('night');
const lightPrivate = await load_priv_key('light');
expect(await lightPrivate.decrypt(data.light.pass)).to.be.true;
const encrypted = await openpgp.encrypt({ publicKeys: [nightPublic], privateKeys: [lightPrivate], data: data.light.message + "\n" });
const message = openpgp.message.readArmored(encrypted.data);
const lightPublic = load_pub_key('light');
const nightPrivate = await load_priv_key('night');
const result = await openpgp.decrypt({ privateKeys: nightPrivate, publicKeys: [lightPublic], message: message });
expect(result).to.exist;
expect(result.data.trim()).to.equal(data.light.message);
expect(result.signatures).to.have.length(1);
expect(result.signatures[0].valid).to.be.true;
});
// TODO export, then reimport key and validate