Don't export default objects / namespaces

Import individual functions, instead.
This commit is contained in:
Daniel Huigens 2020-06-26 19:46:06 +02:00
parent f276e1ef51
commit b3e08fdc26
40 changed files with 1746 additions and 1845 deletions

View File

@ -24,7 +24,7 @@
* @module cleartext
*/
import armor from './encoding/armor';
import { armor, unarmor } from './encoding/armor';
import enums from './enums';
import util from './util';
import { PacketList, LiteralDataPacket, SignaturePacket } from './packet';
@ -140,7 +140,7 @@ export class CleartextMessage {
text: this.text,
data: this.signature.packets.write()
};
return armor.encode(enums.armor.signed, body);
return armor(enums.armor.signed, body);
}
/**
@ -162,7 +162,7 @@ export class CleartextMessage {
* @static
*/
export async function readArmoredCleartextMessage(armoredText) {
const input = await armor.decode(armoredText);
const input = await unarmor(armoredText);
if (input.type !== enums.armor.signed) {
throw new Error('No cleartext signed message.');
}

View File

@ -23,10 +23,17 @@
* @module crypto/aes_kw
*/
import cipher from './cipher';
import * as cipher from './cipher';
import util from '../util';
function wrap(key, data) {
/**
* AES key wrap
* @function
* @param {String} key
* @param {String} data
* @returns {Uint8Array}
*/
export function wrap(key, data) {
const aes = new cipher["aes" + (key.length * 8)](key);
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
const P = unpack(data);
@ -58,7 +65,15 @@ function wrap(key, data) {
return pack(A, R);
}
function unwrap(key, data) {
/**
* AES key unwrap
* @function
* @param {String} key
* @param {String} data
* @returns {Uint8Array}
* @throws {Error}
*/
export function unwrap(key, data) {
const aes = new cipher["aes" + (key.length * 8)](key);
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
const C = unpack(data);
@ -131,23 +146,3 @@ function pack() {
}
return new Uint8Array(buffer);
}
export default {
/**
* AES key wrap
* @function
* @param {String} key
* @param {String} data
* @returns {Uint8Array}
*/
wrap,
/**
* AES key unwrap
* @function
* @param {String} key
* @param {String} data
* @returns {Uint8Array}
* @throws {Error}
*/
unwrap
};

View File

@ -27,7 +27,7 @@
import { AES_CFB } from 'asmcrypto.js/dist_es8/aes/cfb';
import stream from 'web-stream-tools';
import cipher from './cipher';
import * as cipher from './cipher';
import config from '../config';
import util from '../util';
@ -38,7 +38,6 @@ const Buffer = util.getNodeBuffer();
const knownAlgos = nodeCrypto ? nodeCrypto.getCiphers() : [];
const nodeAlgos = {
idea: knownAlgos.includes('idea-cfb') ? 'idea-cfb' : undefined, /* Unused, not implemented */
'3des': knownAlgos.includes('des-ede3-cfb') ? 'des-ede3-cfb' : undefined,
tripledes: knownAlgos.includes('des-ede3-cfb') ? 'des-ede3-cfb' : undefined,
cast5: knownAlgos.includes('cast5-cfb') ? 'cast5-cfb' : undefined,
blowfish: knownAlgos.includes('bf-cfb') ? 'bf-cfb' : undefined,
@ -48,73 +47,71 @@ const nodeAlgos = {
/* twofish is not implemented in OpenSSL */
};
export default {
encrypt: function(algo, key, plaintext, iv) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
return nodeEncrypt(algo, key, plaintext, iv);
}
if (algo.substr(0, 3) === 'aes') {
return aesEncrypt(algo, key, plaintext, iv);
}
const cipherfn = new cipher[algo](key);
const block_size = cipherfn.blockSize;
const blockc = iv.slice();
let pt = new Uint8Array();
const process = chunk => {
if (chunk) {
pt = util.concatUint8Array([pt, chunk]);
}
const ciphertext = new Uint8Array(pt.length);
let i;
let j = 0;
while (chunk ? pt.length >= block_size : pt.length) {
const encblock = cipherfn.encrypt(blockc);
for (i = 0; i < block_size; i++) {
blockc[i] = pt[i] ^ encblock[i];
ciphertext[j++] = blockc[i];
}
pt = pt.subarray(block_size);
}
return ciphertext.subarray(0, j);
};
return stream.transform(plaintext, process, process);
},
decrypt: async function(algo, key, ciphertext, iv) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
return nodeDecrypt(algo, key, ciphertext, iv);
}
if (algo.substr(0, 3) === 'aes') {
return aesDecrypt(algo, key, ciphertext, iv);
}
const cipherfn = new cipher[algo](key);
const block_size = cipherfn.blockSize;
let blockp = iv;
let ct = new Uint8Array();
const process = chunk => {
if (chunk) {
ct = util.concatUint8Array([ct, chunk]);
}
const plaintext = new Uint8Array(ct.length);
let i;
let j = 0;
while (chunk ? ct.length >= block_size : ct.length) {
const decblock = cipherfn.encrypt(blockp);
blockp = ct;
for (i = 0; i < block_size; i++) {
plaintext[j++] = blockp[i] ^ decblock[i];
}
ct = ct.subarray(block_size);
}
return plaintext.subarray(0, j);
};
return stream.transform(ciphertext, process, process);
export async function encrypt(algo, key, plaintext, iv) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
return nodeEncrypt(algo, key, plaintext, iv);
}
};
if (algo.substr(0, 3) === 'aes') {
return aesEncrypt(algo, key, plaintext, iv);
}
const cipherfn = new cipher[algo](key);
const block_size = cipherfn.blockSize;
const blockc = iv.slice();
let pt = new Uint8Array();
const process = chunk => {
if (chunk) {
pt = util.concatUint8Array([pt, chunk]);
}
const ciphertext = new Uint8Array(pt.length);
let i;
let j = 0;
while (chunk ? pt.length >= block_size : pt.length) {
const encblock = cipherfn.encrypt(blockc);
for (i = 0; i < block_size; i++) {
blockc[i] = pt[i] ^ encblock[i];
ciphertext[j++] = blockc[i];
}
pt = pt.subarray(block_size);
}
return ciphertext.subarray(0, j);
};
return stream.transform(plaintext, process, process);
}
export async function decrypt(algo, key, ciphertext, iv) {
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
return nodeDecrypt(algo, key, ciphertext, iv);
}
if (algo.substr(0, 3) === 'aes') {
return aesDecrypt(algo, key, ciphertext, iv);
}
const cipherfn = new cipher[algo](key);
const block_size = cipherfn.blockSize;
let blockp = iv;
let ct = new Uint8Array();
const process = chunk => {
if (chunk) {
ct = util.concatUint8Array([ct, chunk]);
}
const plaintext = new Uint8Array(ct.length);
let i;
let j = 0;
while (chunk ? ct.length >= block_size : ct.length) {
const decblock = cipherfn.encrypt(blockp);
blockp = ct;
for (i = 0; i < block_size; i++) {
plaintext[j++] = blockp[i] ^ decblock[i];
}
ct = ct.subarray(block_size);
}
return plaintext.subarray(0, j);
};
return stream.transform(ciphertext, process, process);
}
function aesEncrypt(algo, key, pt, iv) {
if (

View File

@ -432,7 +432,7 @@ function des_removePadding(message, padding) {
// added by Recurity Labs
function TripleDES(key) {
export function TripleDES(key) {
this.key = [];
for (let i = 0; i < 3; i++) {
@ -459,7 +459,7 @@ TripleDES.blockSize = TripleDES.prototype.blockSize = 8;
// This is "original" DES
function DES(key) {
export function DES(key) {
this.key = key;
this.encrypt = function(block, padding) {
@ -472,5 +472,3 @@ function DES(key) {
return des(keys, block, false, 0, null, padding);
};
}
export default { DES, TripleDES };

View File

@ -9,83 +9,80 @@
*/
import aes from './aes';
import des from './des.js';
import cast5 from './cast5';
import twofish from './twofish';
import blowfish from './blowfish';
import { DES, TripleDES } from './des.js';
import Cast5 from './cast5';
import TF from './twofish';
import BF from './blowfish';
export default {
/**
* AES-128 encryption and decryption (ID 7)
* @function
* @param {String} key 128-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
aes128: aes(128),
/**
* AES-128 Block Cipher (ID 8)
* @function
* @param {String} key 192-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
aes192: aes(192),
/**
* AES-128 Block Cipher (ID 9)
* @function
* @param {String} key 256-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
aes256: aes(256),
// Not in OpenPGP specifications
des: des.DES,
/**
* Triple DES Block Cipher (ID 2)
* @function
* @param {String} key 192-bit key
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
* @returns {Object}
*/
tripledes: des.TripleDES,
'3des': des.TripleDES,
/**
* CAST-128 Block Cipher (ID 3)
* @function
* @param {String} key 128-bit key
* @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
* @returns {Object}
*/
cast5: cast5,
/**
* Twofish Block Cipher (ID 10)
* @function
* @param {String} key 256-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
* @returns {Object}
*/
twofish: twofish,
/**
* Blowfish Block Cipher (ID 4)
* @function
* @param {String} key 128-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
* @returns {Object}
*/
blowfish: blowfish,
/**
* Not implemented
* @function
* @throws {Error}
*/
idea: function() {
throw new Error('IDEA symmetric-key algorithm not implemented');
}
/**
* AES-128 encryption and decryption (ID 7)
* @function
* @param {String} key 128-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
export const aes128 = aes(128);
/**
* AES-128 Block Cipher (ID 8)
* @function
* @param {String} key 192-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
export const aes192 = aes(192);
/**
* AES-128 Block Cipher (ID 9)
* @function
* @param {String} key 256-bit key
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
* @returns {Object}
* @requires asmcrypto.js
*/
export const aes256 = aes(256);
// Not in OpenPGP specifications
export const des = DES;
/**
* Triple DES Block Cipher (ID 2)
* @function
* @param {String} key 192-bit key
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
* @returns {Object}
*/
export const tripledes = TripleDES;
/**
* CAST-128 Block Cipher (ID 3)
* @function
* @param {String} key 128-bit key
* @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
* @returns {Object}
*/
export const cast5 = Cast5;
/**
* Twofish Block Cipher (ID 10)
* @function
* @param {String} key 256-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
* @returns {Object}
*/
export const twofish = TF;
/**
* Blowfish Block Cipher (ID 4)
* @function
* @param {String} key 128-bit key
* @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
* @returns {Object}
*/
export const blowfish = BF;
/**
* Not implemented
* @function
* @throws {Error}
*/
export const idea = function() {
throw new Error('IDEA symmetric-key algorithm not implemented');
};

View File

@ -33,17 +33,17 @@
*/
import publicKey from './public_key';
import cipher from './cipher';
import random from './random';
import * as cipher from './cipher';
import { getRandomBytes } from './random';
import type_ecdh_symkey from '../type/ecdh_symkey';
import KDFParams from '../type/kdf_params';
import type_mpi from '../type/mpi';
import enums from '../enums';
import util from '../util';
import OID from '../type/oid';
import Curve from './public_key/elliptic/curves';
import { Curve } from './public_key/elliptic/curves';
function constructParams(types, data) {
export function constructParams(types, data) {
return types.map(function(type, i) {
if (data && data[i]) {
return new type(data[i]);
@ -52,335 +52,331 @@ function constructParams(types, data) {
});
}
export default {
/**
* Encrypts data using specified algorithm and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Object} pubParams Algorithm-specific public key parameters
* @param {Uint8Array} data Data to be encrypted
* @param {Uint8Array} fingerprint Recipient fingerprint
* @returns {Array<module:type/mpi|
* module:type/ecdh_symkey>} encrypted session key parameters
* @async
*/
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
const types = this.getEncSessionKeyParamTypes(algo);
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign: {
const { n, e } = publicParams;
const res = await publicKey.rsa.encrypt(data, n, e);
return constructParams(types, [res]);
}
case enums.publicKey.elgamal: {
const { p, g, y } = publicParams;
const res = await publicKey.elgamal.encrypt(data, p, g, y);
return constructParams(types, [res.c1, res.c2]);
}
case enums.publicKey.ecdh: {
const { oid, Q, kdfParams } = publicParams;
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
oid, kdfParams, data, Q, fingerprint);
return constructParams(types, [V, C]);
}
default:
return [];
/**
* Encrypts data using specified algorithm and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Object} pubParams Algorithm-specific public key parameters
* @param {Uint8Array} data Data to be encrypted
* @param {Uint8Array} fingerprint Recipient fingerprint
* @returns {Array<module:type/mpi|
* module:type/ecdh_symkey>} encrypted session key parameters
* @async
*/
export async function publicKeyEncrypt(algo, publicParams, data, fingerprint) {
const types = getEncSessionKeyParamTypes(algo);
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign: {
const { n, e } = publicParams;
const res = await publicKey.rsa.encrypt(data, n, e);
return constructParams(types, [res]);
}
},
/**
* Decrypts data using specified algorithm and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Object} publicKeyParams Algorithm-specific public key parameters
* @param {Object} privateKeyParams Algorithm-specific private key parameters
* @param {Array<module:type/mpi|
module:type/ecdh_symkey>}
data_params encrypted session key parameters
* @param {Uint8Array} fingerprint Recipient fingerprint
* @returns {Uint8Array} decrypted data
* @async
*/
publicKeyDecrypt: async function(algo, publicKeyParams, privateKeyParams, data_params, fingerprint) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt: {
const c = data_params[0].toUint8Array();
const { n, e } = publicKeyParams;
const { d, p, q, u } = privateKeyParams;
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
}
case enums.publicKey.elgamal: {
const c1 = data_params[0].toUint8Array();
const c2 = data_params[1].toUint8Array();
const p = publicKeyParams.p;
const x = privateKeyParams.x;
return publicKey.elgamal.decrypt(c1, c2, p, x);
}
case enums.publicKey.ecdh: {
const { oid, Q, kdfParams } = publicKeyParams;
const { d } = privateKeyParams;
const V = data_params[0].toUint8Array();
const C = data_params[1].data;
return publicKey.elliptic.ecdh.decrypt(
oid, kdfParams, V, C, Q, d, fingerprint);
}
default:
throw new Error('Invalid public key encryption algorithm.');
case enums.publicKey.elgamal: {
const { p, g, y } = publicParams;
const res = await publicKey.elgamal.encrypt(data, p, g, y);
return constructParams(types, [res.c1, res.c2]);
}
},
/**
* Parse public key material in binary form to get the key parameters
* @param {module:enums.publicKey} algo The key algorithm
* @param {Uint8Array} bytes The key material to parse
* @returns {Object} key parameters referenced by name
* @returns { read: Number, publicParams: Object } number of read bytes plus key parameters referenced by name
*/
parsePublicKeyParams: function(algo, bytes) {
let read = 0;
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
let read = 0;
const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
return { read, publicParams: { n, e } };
}
case enums.publicKey.dsa: {
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
return { read, publicParams: { p, q, g, y } };
}
case enums.publicKey.elgamal: {
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
return { read, publicParams: { p, g, y } };
}
case enums.publicKey.ecdsa: {
const oid = new OID(); read += oid.read(bytes);
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
return { read: read, publicParams: { oid, Q } };
}
case enums.publicKey.eddsa: {
const oid = new OID(); read += oid.read(bytes);
let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
Q = util.padToLength(Q, 33);
return { read: read, publicParams: { oid, Q } };
}
case enums.publicKey.ecdh: {
const oid = new OID(); read += oid.read(bytes);
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
return { read: read, publicParams: { oid, Q, kdfParams } };
}
default:
throw new Error('Invalid public key encryption algorithm.');
case enums.publicKey.ecdh: {
const { oid, Q, kdfParams } = publicParams;
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
oid, kdfParams, data, Q, fingerprint);
return constructParams(types, [V, C]);
}
},
default:
return [];
}
}
/**
* Parse private key material in binary form to get the key parameters
* @param {module:enums.publicKey} algo The key algorithm
* @param {Uint8Array} bytes The key material to parse
* @param {Object} publicParams (ECC only) public params, needed to format some private params
* @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
*/
parsePrivateKeyParams: function(algo, bytes, publicParams) {
let read = 0;
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
return { read, privateParams: { d, p, q, u } };
}
case enums.publicKey.dsa:
case enums.publicKey.elgamal: {
const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
return { read, privateParams: { x } };
}
case enums.publicKey.ecdsa:
case enums.publicKey.ecdh: {
const curve = new Curve(publicParams.oid);
let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
d = util.padToLength(d, curve.payloadSize);
return { read, privateParams: { d } };
}
case enums.publicKey.eddsa: {
let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
seed = util.padToLength(seed, 32);
return { read, privateParams: { seed } };
}
default:
throw new Error('Invalid public key encryption algorithm.');
/**
* Decrypts data using specified algorithm and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Object} publicKeyParams Algorithm-specific public key parameters
* @param {Object} privateKeyParams Algorithm-specific private key parameters
* @param {Array<module:type/mpi|
module:type/ecdh_symkey>}
data_params encrypted session key parameters
* @param {Uint8Array} fingerprint Recipient fingerprint
* @returns {Uint8Array} decrypted data
* @async
*/
export async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, data_params, fingerprint) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt: {
const c = data_params[0].toUint8Array();
const { n, e } = publicKeyParams;
const { d, p, q, u } = privateKeyParams;
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
}
},
/** Returns the types comprising the encrypted session key of an algorithm
* @param {module:enums.publicKey} algo The public key algorithm
* @returns {Array<Object>} The array of types
*/
getEncSessionKeyParamTypes: function(algo) {
switch (algo) {
// Algorithm-Specific Fields for RSA encrypted session keys:
// - MPI of RSA encrypted value m**e mod n.
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
return [type_mpi];
// Algorithm-Specific Fields for Elgamal encrypted session keys:
// - MPI of Elgamal value g**k mod p
// - MPI of Elgamal value m * y**k mod p
case enums.publicKey.elgamal:
return [type_mpi, type_mpi];
// Algorithm-Specific Fields for ECDH encrypted session keys:
// - MPI containing the ephemeral key used to establish the shared secret
// - ECDH Symmetric Key
case enums.publicKey.ecdh:
return [type_mpi, type_ecdh_symkey];
default:
throw new Error('Invalid public key encryption algorithm.');
case enums.publicKey.elgamal: {
const c1 = data_params[0].toUint8Array();
const c2 = data_params[1].toUint8Array();
const p = publicKeyParams.p;
const x = privateKeyParams.x;
return publicKey.elgamal.decrypt(c1, c2, p, x);
}
},
/**
* Convert params to MPI and serializes them in the proper order
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Object} params The key parameters indexed by name
* @returns {Uint8Array} The array containing the MPIs
*/
serializeKeyParams: function(algo, params) {
const orderedParams = Object.keys(params).map(name => {
const param = params[name];
return util.isUint8Array(param) ? util.uint8ArrayToMpi(param) : param.write();
});
return util.concatUint8Array(orderedParams);
},
/**
* Generate algorithm-specific key parameters
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Integer} bits Bit length for RSA keys
* @param {module:type/oid} oid Object identifier for ECC keys
* @returns { publicParams, privateParams: {Object} } The parameters referenced by name
* @async
*/
generateParams: function(algo, bits, oid) {
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
privateParams: { d, p, q, u },
publicParams: { n, e }
}));
}
case enums.publicKey.ecdsa:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
privateParams: { d: secret },
publicParams: { oid: new OID(oid), Q }
}));
case enums.publicKey.eddsa:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
privateParams: { seed: secret },
publicParams: { oid: new OID(oid), Q }
}));
case enums.publicKey.ecdh:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
privateParams: { d: secret },
publicParams: {
oid: new OID(oid),
Q,
kdfParams: new KDFParams({ hash, cipher })
}
}));
case enums.publicKey.dsa:
case enums.publicKey.elgamal:
throw new Error('Unsupported algorithm for key generation.');
default:
throw new Error('Invalid public key algorithm.');
case enums.publicKey.ecdh: {
const { oid, Q, kdfParams } = publicKeyParams;
const { d } = privateKeyParams;
const V = data_params[0].toUint8Array();
const C = data_params[1].data;
return publicKey.elliptic.ecdh.decrypt(
oid, kdfParams, V, C, Q, d, fingerprint);
}
},
default:
throw new Error('Invalid public key encryption algorithm.');
}
}
/**
* Validate algorithm-specific key parameters
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Object} publicParams Algorithm-specific public key parameters
* @param {Object} privateParams Algorithm-specific private key parameters
* @returns {Promise<Boolean>} whether the parameters are valid
* @async
*/
validateParams: async function(algo, publicParams, privateParams) {
if (!publicParams || !privateParams) {
throw new Error('Missing key parameters');
/**
* Parse public key material in binary form to get the key parameters
* @param {module:enums.publicKey} algo The key algorithm
* @param {Uint8Array} bytes The key material to parse
* @returns {Object} key parameters referenced by name
* @returns { read: Number, publicParams: Object } number of read bytes plus key parameters referenced by name
*/
export function parsePublicKeyParams(algo, bytes) {
let read = 0;
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
let read = 0;
const n = util.readMPI(bytes.subarray(read)); read += n.length + 2;
const e = util.readMPI(bytes.subarray(read)); read += e.length + 2;
return { read, publicParams: { n, e } };
}
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
const { n, e } = publicParams;
const { d, p, q, u } = privateParams;
return publicKey.rsa.validateParams(n, e, d, p, q, u);
}
case enums.publicKey.dsa: {
const { p, q, g, y } = publicParams;
const { x } = privateParams;
return publicKey.dsa.validateParams(p, q, g, y, x);
}
case enums.publicKey.elgamal: {
const { p, g, y } = publicParams;
const { x } = privateParams;
return publicKey.elgamal.validateParams(p, g, y, x);
}
case enums.publicKey.ecdsa:
case enums.publicKey.ecdh: {
const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
const { oid, Q } = publicParams;
const { d } = privateParams;
return algoModule.validateParams(oid, Q, d);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicParams;
const { seed } = privateParams;
return publicKey.elliptic.eddsa.validateParams(oid, Q, seed);
}
default:
throw new Error('Invalid public key algorithm.');
case enums.publicKey.dsa: {
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
return { read, publicParams: { p, q, g, y } };
}
},
case enums.publicKey.elgamal: {
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
return { read, publicParams: { p, g, y } };
}
case enums.publicKey.ecdsa: {
const oid = new OID(); read += oid.read(bytes);
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
return { read: read, publicParams: { oid, Q } };
}
case enums.publicKey.eddsa: {
const oid = new OID(); read += oid.read(bytes);
let Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
Q = util.padToLength(Q, 33);
return { read: read, publicParams: { oid, Q } };
}
case enums.publicKey.ecdh: {
const oid = new OID(); read += oid.read(bytes);
const Q = util.readMPI(bytes.subarray(read)); read += Q.length + 2;
const kdfParams = new KDFParams(); read += kdfParams.read(bytes.subarray(read));
return { read: read, publicParams: { oid, Q, kdfParams } };
}
default:
throw new Error('Invalid public key encryption algorithm.');
}
}
/**
* Generates a random byte prefix for the specified algorithm
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
* @returns {Uint8Array} Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.
* @async
*/
getPrefixRandom: async function(algo) {
const prefixrandom = await random.getRandomBytes(cipher[algo].blockSize);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
return util.concat([prefixrandom, repeat]);
},
/**
* Parse private key material in binary form to get the key parameters
* @param {module:enums.publicKey} algo The key algorithm
* @param {Uint8Array} bytes The key material to parse
* @param {Object} publicParams (ECC only) public params, needed to format some private params
* @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
*/
export function parsePrivateKeyParams(algo, bytes, publicParams) {
let read = 0;
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
return { read, privateParams: { d, p, q, u } };
}
case enums.publicKey.dsa:
case enums.publicKey.elgamal: {
const x = util.readMPI(bytes.subarray(read)); read += x.length + 2;
return { read, privateParams: { x } };
}
case enums.publicKey.ecdsa:
case enums.publicKey.ecdh: {
const curve = new Curve(publicParams.oid);
let d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
d = util.padToLength(d, curve.payloadSize);
return { read, privateParams: { d } };
}
case enums.publicKey.eddsa: {
let seed = util.readMPI(bytes.subarray(read)); read += seed.length + 2;
seed = util.padToLength(seed, 32);
return { read, privateParams: { seed } };
}
default:
throw new Error('Invalid public key encryption algorithm.');
}
}
/**
* Generating a session key for the specified symmetric algorithm
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
* @returns {Uint8Array} Random bytes as a string to be used as a key
* @async
*/
generateSessionKey: function(algo) {
return random.getRandomBytes(cipher[algo].keySize);
},
/** Returns the types comprising the encrypted session key of an algorithm
* @param {module:enums.publicKey} algo The public key algorithm
* @returns {Array<Object>} The array of types
*/
export function getEncSessionKeyParamTypes(algo) {
switch (algo) {
// Algorithm-Specific Fields for RSA encrypted session keys:
// - MPI of RSA encrypted value m**e mod n.
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
return [type_mpi];
constructParams: constructParams
};
// Algorithm-Specific Fields for Elgamal encrypted session keys:
// - MPI of Elgamal value g**k mod p
// - MPI of Elgamal value m * y**k mod p
case enums.publicKey.elgamal:
return [type_mpi, type_mpi];
// Algorithm-Specific Fields for ECDH encrypted session keys:
// - MPI containing the ephemeral key used to establish the shared secret
// - ECDH Symmetric Key
case enums.publicKey.ecdh:
return [type_mpi, type_ecdh_symkey];
default:
throw new Error('Invalid public key encryption algorithm.');
}
}
/**
* Convert params to MPI and serializes them in the proper order
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Object} params The key parameters indexed by name
* @returns {Uint8Array} The array containing the MPIs
*/
export function serializeKeyParams(algo, params) {
const orderedParams = Object.keys(params).map(name => {
const param = params[name];
return util.isUint8Array(param) ? util.uint8ArrayToMpi(param) : param.write();
});
return util.concatUint8Array(orderedParams);
}
/**
* Generate algorithm-specific key parameters
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Integer} bits Bit length for RSA keys
* @param {module:type/oid} oid Object identifier for ECC keys
* @returns { publicParams, privateParams: {Object} } The parameters referenced by name
* @async
*/
export function generateParams(algo, bits, oid) {
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
return publicKey.rsa.generate(bits, 65537).then(({ n, e, d, p, q, u }) => ({
privateParams: { d, p, q, u },
publicParams: { n, e }
}));
}
case enums.publicKey.ecdsa:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
privateParams: { d: secret },
publicParams: { oid: new OID(oid), Q }
}));
case enums.publicKey.eddsa:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret }) => ({
privateParams: { seed: secret },
publicParams: { oid: new OID(oid), Q }
}));
case enums.publicKey.ecdh:
return publicKey.elliptic.generate(oid).then(({ oid, Q, secret, hash, cipher }) => ({
privateParams: { d: secret },
publicParams: {
oid: new OID(oid),
Q,
kdfParams: new KDFParams({ hash, cipher })
}
}));
case enums.publicKey.dsa:
case enums.publicKey.elgamal:
throw new Error('Unsupported algorithm for key generation.');
default:
throw new Error('Invalid public key algorithm.');
}
}
/**
* Validate algorithm-specific key parameters
* @param {module:enums.publicKey} algo The public key algorithm
* @param {Object} publicParams Algorithm-specific public key parameters
* @param {Object} privateParams Algorithm-specific private key parameters
* @returns {Promise<Boolean>} whether the parameters are valid
* @async
*/
export async function validateParams(algo, publicParams, privateParams) {
if (!publicParams || !privateParams) {
throw new Error('Missing key parameters');
}
switch (algo) {
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaSign: {
const { n, e } = publicParams;
const { d, p, q, u } = privateParams;
return publicKey.rsa.validateParams(n, e, d, p, q, u);
}
case enums.publicKey.dsa: {
const { p, q, g, y } = publicParams;
const { x } = privateParams;
return publicKey.dsa.validateParams(p, q, g, y, x);
}
case enums.publicKey.elgamal: {
const { p, g, y } = publicParams;
const { x } = privateParams;
return publicKey.elgamal.validateParams(p, g, y, x);
}
case enums.publicKey.ecdsa:
case enums.publicKey.ecdh: {
const algoModule = publicKey.elliptic[enums.read(enums.publicKey, algo)];
const { oid, Q } = publicParams;
const { d } = privateParams;
return algoModule.validateParams(oid, Q, d);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicParams;
const { seed } = privateParams;
return publicKey.elliptic.eddsa.validateParams(oid, Q, seed);
}
default:
throw new Error('Invalid public key algorithm.');
}
}
/**
* Generates a random byte prefix for the specified algorithm
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
* @returns {Uint8Array} Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.
* @async
*/
export async function getPrefixRandom(algo) {
const prefixrandom = await getRandomBytes(cipher[algo].blockSize);
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
return util.concat([prefixrandom, repeat]);
}
/**
* Generating a session key for the specified symmetric algorithm
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
* @returns {Uint8Array} Random bytes as a string to be used as a key
* @async
*/
export function generateSessionKey(algo) {
return getRandomBytes(cipher[algo].keySize);
}

View File

@ -9,19 +9,19 @@
* @module crypto
*/
import cipher from './cipher';
import * as cipher from './cipher';
import hash from './hash';
import cfb from './cfb';
import * as cfb from './cfb';
import gcm from './gcm';
import eax from './eax';
import ocb from './ocb';
import publicKey from './public_key';
import signature from './signature';
import random from './random';
import pkcs1 from './pkcs1';
import pkcs5 from './pkcs5';
import crypto from './crypto';
import aes_kw from './aes_kw';
import * as signature from './signature';
import * as random from './random';
import * as pkcs1 from './pkcs1';
import * as pkcs5 from './pkcs5';
import * as crypto from './crypto';
import * as aes_kw from './aes_kw';
// TODO move cfb and gcm to cipher
const mod = {

View File

@ -22,7 +22,7 @@
* @module crypto/ocb
*/
import ciphers from './cipher';
import * as ciphers from './cipher';
import util from '../util';

View File

@ -25,14 +25,9 @@
* @module crypto/pkcs1
*/
import random from './random';
import { getRandomBytes } from './random';
import hash from './hash';
/** @namespace */
const eme = {};
/** @namespace */
const emsa = {};
/**
* ASN1 object identifiers for hashes
* @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}
@ -62,7 +57,7 @@ async function getPkcs1Padding(length) {
const result = new Uint8Array(length);
let count = 0;
while (count < length) {
const randomBytes = await random.getRandomBytes(length - count);
const randomBytes = await getRandomBytes(length - count);
for (let i = 0; i < randomBytes.length; i++) {
if (randomBytes[i] !== 0) {
result[count++] = randomBytes[i];
@ -80,7 +75,7 @@ async function getPkcs1Padding(length) {
* @returns {Promise<Uint8Array>} EME-PKCS1 padded message
* @async
*/
eme.encode = async function(message, keyLength) {
export async function emeEncode(message, keyLength) {
const mLength = message.length;
// length checking
if (mLength > keyLength - 11) {
@ -98,7 +93,7 @@ eme.encode = async function(message, keyLength) {
// 0x00 bytes
encoded.set(message, keyLength - mLength);
return encoded;
};
}
/**
* Decode a EME-PKCS1-v1_5 padded message
@ -106,7 +101,7 @@ eme.encode = async function(message, keyLength) {
* @param {Uint8Array} encoded encoded message bytes
* @returns {Uint8Array} message
*/
eme.decode = function(encoded) {
export function emeDecode(encoded) {
let i = 2;
while (encoded[i] !== 0 && i < encoded.length) {
i++;
@ -117,7 +112,7 @@ eme.decode = function(encoded) {
return encoded.subarray(i);
}
throw new Error('Decryption error');
};
}
/**
* Create a EMSA-PKCS1-v1_5 padded message
@ -127,7 +122,7 @@ eme.decode = function(encoded) {
* @param {Integer} emLen intended length in octets of the encoded message
* @returns {Uint8Array} encoded message
*/
emsa.encode = async function(algo, hashed, emLen) {
export async function emsaEncode(algo, hashed, emLen) {
let i;
if (hashed.length !== hash.getHashByteLength(algo)) {
throw new Error('Invalid hash length');
@ -155,6 +150,4 @@ emsa.encode = async function(algo, hashed, emLen) {
EM.set(hashPrefix, emLen - tLen);
EM.set(hashed, emLen - hashed.length);
return EM;
};
export default { eme, emsa };
}

View File

@ -28,7 +28,7 @@ import util from '../util';
* @param {Uint8Array} message message to pad
* @returns {Uint8Array} padded message
*/
function encode(message) {
export function encode(message) {
const c = 8 - (message.length % 8);
const padded = new Uint8Array(message.length + c).fill(c);
padded.set(message);
@ -40,7 +40,7 @@ function encode(message) {
* @param {Uint8Array} message message to remove padding from
* @returns {Uint8Array} message without padding
*/
function decode(message) {
export function decode(message) {
const len = message.length;
if (len > 0) {
const c = message[len - 1];
@ -54,5 +54,3 @@ function decode(message) {
}
throw new Error('Invalid padding');
}
export default { encode, decode };

View File

@ -21,9 +21,9 @@
* @requires util
* @module crypto/public_key/dsa
*/
import random from '../random';
import { getRandomBigInteger } from '../random';
import util from '../../util';
import prime from './prime';
import { isProbablePrime } from './prime';
/*
TODO regarding the hash function, read:
@ -31,170 +31,168 @@ import prime from './prime';
https://tools.ietf.org/html/rfc4880#section-14
*/
export default {
/**
* DSA Sign function
* @param {Integer} hash_algo
* @param {Uint8Array} hashed
* @param {Uint8Array} g
* @param {Uint8Array} p
* @param {Uint8Array} q
* @param {Uint8Array} x
* @returns {{ r: Uint8Array, s: Uint8Array }}
* @async
*/
sign: async function(hash_algo, hashed, g, p, q, x) {
const BigInteger = await util.getBigInteger();
const one = new BigInteger(1);
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
x = new BigInteger(x);
/**
* DSA Sign function
* @param {Integer} hash_algo
* @param {Uint8Array} hashed
* @param {Uint8Array} g
* @param {Uint8Array} p
* @param {Uint8Array} q
* @param {Uint8Array} x
* @returns {{ r: Uint8Array, s: Uint8Array }}
* @async
*/
export async function sign(hash_algo, hashed, g, p, q, x) {
const BigInteger = await util.getBigInteger();
const one = new BigInteger(1);
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
x = new BigInteger(x);
let k;
let r;
let s;
let t;
g = g.mod(p);
x = x.mod(q);
// If the output size of the chosen hash is larger than the number of
// bits of q, the hash result is truncated to fit by taking the number
// of leftmost bits equal to the number of bits of q. This (possibly
// truncated) hash function result is treated as a number and used
// directly in the DSA signature algorithm.
const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q);
// FIPS-186-4, section 4.6:
// The values of r and s shall be checked to determine if r = 0 or s = 0.
// If either r = 0 or s = 0, a new value of k shall be generated, and the
// signature shall be recalculated. It is extremely unlikely that r = 0
// 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 = await random.getRandomBigInteger(one, q); // returns in [1, q-1]
r = g.modExp(k, p).imod(q); // (g**k mod p) mod q
if (r.isZero()) {
continue;
}
const xr = x.mul(r).imod(q);
t = h.add(xr).imod(q); // H(m) + x*r mod q
s = k.modInv(q).imul(t).imod(q); // k**-1 * (H(m) + x*r) mod q
if (s.isZero()) {
continue;
}
break;
let k;
let r;
let s;
let t;
g = g.mod(p);
x = x.mod(q);
// If the output size of the chosen hash is larger than the number of
// bits of q, the hash result is truncated to fit by taking the number
// of leftmost bits equal to the number of bits of q. This (possibly
// truncated) hash function result is treated as a number and used
// directly in the DSA signature algorithm.
const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q);
// FIPS-186-4, section 4.6:
// The values of r and s shall be checked to determine if r = 0 or s = 0.
// If either r = 0 or s = 0, a new value of k shall be generated, and the
// signature shall be recalculated. It is extremely unlikely that r = 0
// 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 = await getRandomBigInteger(one, q); // returns in [1, q-1]
r = g.modExp(k, p).imod(q); // (g**k mod p) mod q
if (r.isZero()) {
continue;
}
return {
r: r.toUint8Array('be', q.byteLength()),
s: s.toUint8Array('be', q.byteLength())
};
},
/**
* DSA Verify function
* @param {Integer} hash_algo
* @param {Uint8Array} r
* @param {Uint8Array} s
* @param {Uint8Array} hashed
* @param {Uint8Array} g
* @param {Uint8Array} p
* @param {Uint8Array} q
* @param {Uint8Array} y
* @returns {boolean}
* @async
*/
verify: async function(hash_algo, r, s, hashed, g, p, q, y) {
const BigInteger = await util.getBigInteger();
const zero = new BigInteger(0);
r = new BigInteger(r);
s = new BigInteger(s);
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
y = new BigInteger(y);
if (r.lte(zero) || r.gte(q) ||
s.lte(zero) || s.gte(q)) {
util.printDebug("invalid DSA Signature");
return false;
const xr = x.mul(r).imod(q);
t = h.add(xr).imod(q); // H(m) + x*r mod q
s = k.modInv(q).imul(t).imod(q); // k**-1 * (H(m) + x*r) mod q
if (s.isZero()) {
continue;
}
const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
const w = s.modInv(q); // s**-1 mod q
if (w.isZero()) {
util.printDebug("invalid DSA Signature");
return false;
}
g = g.mod(p);
y = y.mod(p);
const u1 = h.mul(w).imod(q); // H(m) * w mod q
const u2 = r.mul(w).imod(q); // r * w mod q
const t1 = g.modExp(u1, p); // g**u1 mod p
const t2 = y.modExp(u2, p); // y**u2 mod p
const v = t1.mul(t2).imod(p).imod(q); // (g**u1 * y**u2 mod p) mod q
return v.equal(r);
},
/**
* Validate DSA parameters
* @param {Uint8Array} p DSA prime
* @param {Uint8Array} q DSA group order
* @param {Uint8Array} g DSA sub-group generator
* @param {Uint8Array} y DSA public key
* @param {Uint8Array} x DSA private key
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
validateParams: async function (p, q, g, y, x) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
y = new BigInteger(y);
const one = new BigInteger(1);
// Check that 1 < g < p
if (g.lte(one) || g.gte(p)) {
return false;
}
/**
* Check that subgroup order q divides p-1
*/
if (!p.dec().mod(q).isZero()) {
return false;
}
/**
* g has order q
* Check that g ** q = 1 mod p
*/
if (!g.modExp(q, p).isOne()) {
return false;
}
/**
* Check q is large and probably prime (we mainly want to avoid small factors)
*/
const qSize = new BigInteger(q.bitLength());
const n150 = new BigInteger(150);
if (qSize.lt(n150) || !(await prime.isProbablePrime(q, null, 32))) {
return false;
}
/**
* Re-derive public key y' = g ** x mod p
* Expect y == y'
*
* Blinded exponentiation computes g**{rq + x} to compare to y
*/
x = new BigInteger(x);
const two = new BigInteger(2);
const r = await random.getRandomBigInteger(two.leftShift(qSize.dec()), two.leftShift(qSize)); // draw r of same size as q
const rqx = q.mul(r).add(x);
if (!y.equal(g.modExp(rqx, p))) {
return false;
}
return true;
break;
}
};
return {
r: r.toUint8Array('be', q.byteLength()),
s: s.toUint8Array('be', q.byteLength())
};
}
/**
* DSA Verify function
* @param {Integer} hash_algo
* @param {Uint8Array} r
* @param {Uint8Array} s
* @param {Uint8Array} hashed
* @param {Uint8Array} g
* @param {Uint8Array} p
* @param {Uint8Array} q
* @param {Uint8Array} y
* @returns {boolean}
* @async
*/
export async function verify(hash_algo, r, s, hashed, g, p, q, y) {
const BigInteger = await util.getBigInteger();
const zero = new BigInteger(0);
r = new BigInteger(r);
s = new BigInteger(s);
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
y = new BigInteger(y);
if (r.lte(zero) || r.gte(q) ||
s.lte(zero) || s.gte(q)) {
util.printDebug("invalid DSA Signature");
return false;
}
const h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
const w = s.modInv(q); // s**-1 mod q
if (w.isZero()) {
util.printDebug("invalid DSA Signature");
return false;
}
g = g.mod(p);
y = y.mod(p);
const u1 = h.mul(w).imod(q); // H(m) * w mod q
const u2 = r.mul(w).imod(q); // r * w mod q
const t1 = g.modExp(u1, p); // g**u1 mod p
const t2 = y.modExp(u2, p); // y**u2 mod p
const v = t1.mul(t2).imod(p).imod(q); // (g**u1 * y**u2 mod p) mod q
return v.equal(r);
}
/**
* Validate DSA parameters
* @param {Uint8Array} p DSA prime
* @param {Uint8Array} q DSA group order
* @param {Uint8Array} g DSA sub-group generator
* @param {Uint8Array} y DSA public key
* @param {Uint8Array} x DSA private key
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
export async function validateParams(p, q, g, y, x) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
q = new BigInteger(q);
g = new BigInteger(g);
y = new BigInteger(y);
const one = new BigInteger(1);
// Check that 1 < g < p
if (g.lte(one) || g.gte(p)) {
return false;
}
/**
* Check that subgroup order q divides p-1
*/
if (!p.dec().mod(q).isZero()) {
return false;
}
/**
* g has order q
* Check that g ** q = 1 mod p
*/
if (!g.modExp(q, p).isOne()) {
return false;
}
/**
* Check q is large and probably prime (we mainly want to avoid small factors)
*/
const qSize = new BigInteger(q.bitLength());
const n150 = new BigInteger(150);
if (qSize.lt(n150) || !(await isProbablePrime(q, null, 32))) {
return false;
}
/**
* Re-derive public key y' = g ** x mod p
* Expect y == y'
*
* Blinded exponentiation computes g**{rq + x} to compare to y
*/
x = new BigInteger(x);
const two = new BigInteger(2);
const r = await getRandomBigInteger(two.leftShift(qSize.dec()), two.leftShift(qSize)); // draw r of same size as q
const rqx = q.mul(r).add(x);
if (!y.equal(g.modExp(rqx, p))) {
return false;
}
return true;
}

View File

@ -23,125 +23,123 @@
*/
import util from '../../util';
import random from '../random';
import pkcs1 from '../pkcs1';
import { getRandomBigInteger } from '../random';
import { emeEncode, emeDecode } from '../pkcs1';
export default {
/**
* ElGamal Encryption function
* Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
* @param {Uint8Array} data to be padded and encrypted
* @param {Uint8Array} p
* @param {Uint8Array} g
* @param {Uint8Array} y
* @returns {{ c1: Uint8Array, c2: Uint8Array }}
* @async
*/
encrypt: async function(data, p, g, y) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
g = new BigInteger(g);
y = new BigInteger(y);
/**
* ElGamal Encryption function
* Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
* @param {Uint8Array} data to be padded and encrypted
* @param {Uint8Array} p
* @param {Uint8Array} g
* @param {Uint8Array} y
* @returns {{ c1: Uint8Array, c2: Uint8Array }}
* @async
*/
export async function encrypt(data, p, g, y) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
g = new BigInteger(g);
y = new BigInteger(y);
const padded = await pkcs1.eme.encode(data, p.byteLength());
const m = new BigInteger(padded);
const padded = await emeEncode(data, p.byteLength());
const m = new BigInteger(padded);
// OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
// hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
const k = await random.getRandomBigInteger(new BigInteger(1), p.dec());
return {
c1: g.modExp(k, p).toUint8Array(),
c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
};
},
// OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
// hence g has order p-1, and to avoid that k = 0 mod p-1, we need to pick k in [1, p-2]
const k = await getRandomBigInteger(new BigInteger(1), p.dec());
return {
c1: g.modExp(k, p).toUint8Array(),
c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
};
}
/**
* ElGamal Encryption function
* @param {Uint8Array} c1
* @param {Uint8Array} c2
* @param {Uint8Array} p
* @param {Uint8Array} x
* @returns {Uint8Array} unpadded message
* @async
*/
decrypt: async function(c1, c2, p, x) {
const BigInteger = await util.getBigInteger();
c1 = new BigInteger(c1);
c2 = new BigInteger(c2);
p = new BigInteger(p);
x = new BigInteger(x);
/**
* ElGamal Encryption function
* @param {Uint8Array} c1
* @param {Uint8Array} c2
* @param {Uint8Array} p
* @param {Uint8Array} x
* @returns {Uint8Array} unpadded message
* @async
*/
export async function decrypt(c1, c2, p, x) {
const BigInteger = await util.getBigInteger();
c1 = new BigInteger(c1);
c2 = new BigInteger(c2);
p = new BigInteger(p);
x = new BigInteger(x);
const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
return pkcs1.eme.decode(padded.toUint8Array('be', p.byteLength()));
},
const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
return emeDecode(padded.toUint8Array('be', p.byteLength()));
}
/**
* Validate ElGamal parameters
* @param {Uint8Array} p ElGamal prime
* @param {Uint8Array} g ElGamal group generator
* @param {Uint8Array} y ElGamal public key
* @param {Uint8Array} x ElGamal private exponent
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
validateParams: async function (p, g, y, x) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
g = new BigInteger(g);
y = new BigInteger(y);
/**
* Validate ElGamal parameters
* @param {Uint8Array} p ElGamal prime
* @param {Uint8Array} g ElGamal group generator
* @param {Uint8Array} y ElGamal public key
* @param {Uint8Array} x ElGamal private exponent
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
export async function validateParams(p, g, y, x) {
const BigInteger = await util.getBigInteger();
p = new BigInteger(p);
g = new BigInteger(g);
y = new BigInteger(y);
const one = new BigInteger(1);
// Check that 1 < g < p
if (g.lte(one) || g.gte(p)) {
return false;
}
// Expect p-1 to be large
const pSize = new BigInteger(p.bitLength());
const n1023 = new BigInteger(1023);
if (pSize.lt(n1023)) {
return false;
}
/**
* g should have order p-1
* Check that g ** (p-1) = 1 mod p
*/
if (!g.modExp(p.dec(), p).isOne()) {
return false;
}
/**
* Since p-1 is not prime, g might have a smaller order that divides p-1
* We want to make sure that the order is large enough to hinder a small subgroup attack
*
* We just check g**i != 1 for all i up to a threshold
*/
let res = g;
const i = new BigInteger(1);
const threshold = new BigInteger(2).leftShift(new BigInteger(17)); // we want order > threshold
while (i.lt(threshold)) {
res = res.mul(g).imod(p);
if (res.isOne()) {
return false;
}
i.iinc();
}
/**
* Re-derive public key y' = g ** x mod p
* Expect y == y'
*
* Blinded exponentiation computes g**{r(p-1) + x} to compare to y
*/
x = new BigInteger(x);
const two = new BigInteger(2);
const r = await random.getRandomBigInteger(two.leftShift(pSize.dec()), two.leftShift(pSize)); // draw r of same size as p-1
const rqx = p.dec().imul(r).iadd(x);
if (!y.equal(g.modExp(rqx, p))) {
return false;
}
return true;
const one = new BigInteger(1);
// Check that 1 < g < p
if (g.lte(one) || g.gte(p)) {
return false;
}
};
// Expect p-1 to be large
const pSize = new BigInteger(p.bitLength());
const n1023 = new BigInteger(1023);
if (pSize.lt(n1023)) {
return false;
}
/**
* g should have order p-1
* Check that g ** (p-1) = 1 mod p
*/
if (!g.modExp(p.dec(), p).isOne()) {
return false;
}
/**
* Since p-1 is not prime, g might have a smaller order that divides p-1
* We want to make sure that the order is large enough to hinder a small subgroup attack
*
* We just check g**i != 1 for all i up to a threshold
*/
let res = g;
const i = new BigInteger(1);
const threshold = new BigInteger(2).leftShift(new BigInteger(17)); // we want order > threshold
while (i.lt(threshold)) {
res = res.mul(g).imod(p);
if (res.isOne()) {
return false;
}
i.iinc();
}
/**
* Re-derive public key y' = g ** x mod p
* Expect y == y'
*
* Blinded exponentiation computes g**{r(p-1) + x} to compare to y
*/
x = new BigInteger(x);
const two = new BigInteger(2);
const r = await getRandomBigInteger(two.leftShift(pSize.dec()), two.leftShift(pSize)); // draw r of same size as p-1
const rqx = p.dec().imul(r).iadd(x);
if (!y.equal(g.modExp(rqx, p))) {
return false;
}
return true;
}

View File

@ -28,7 +28,7 @@
*/
import nacl from 'tweetnacl/nacl-fast-light.js';
import random from '../../random';
import { getRandomBytes } from '../../random';
import enums from '../../../enums';
import util from '../../../util';
import OID from '../../../type/oid';
@ -186,7 +186,7 @@ class Curve {
case 'node':
return nodeGenKeyPair(this.name);
case 'curve25519': {
const privateKey = await random.getRandomBytes(32);
const privateKey = await getRandomBytes(32);
privateKey[0] = (privateKey[0] & 127) | 64;
privateKey[31] &= 248;
const secretKey = privateKey.slice().reverse();
@ -195,7 +195,7 @@ class Curve {
return { publicKey, privateKey };
}
case 'ed25519': {
const privateKey = await random.getRandomBytes(32);
const privateKey = await getRandomBytes(32);
const keyPair = nacl.sign.keyPair.fromSeed(privateKey);
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
return { publicKey, privateKey };
@ -203,7 +203,7 @@ class Curve {
}
const indutnyCurve = await getIndutnyCurve(this.name);
keyPair = await indutnyCurve.genKeyPair({
entropy: util.uint8ArrayToStr(await random.getRandomBytes(32))
entropy: util.uint8ArrayToStr(await getRandomBytes(32))
});
return { publicKey: new Uint8Array(keyPair.getPublic('array', false)), privateKey: keyPair.getPrivate().toArrayLike(Uint8Array) };
}
@ -291,10 +291,8 @@ async function validateStandardParams(algo, oid, Q, d) {
return true;
}
export default Curve;
export {
curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams
Curve, curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams
};
//////////////////////////

View File

@ -30,14 +30,14 @@
*/
import nacl from 'tweetnacl/nacl-fast-light.js';
import Curve, { jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams } from './curves';
import aes_kw from '../../aes_kw';
import cipher from '../../cipher';
import random from '../../random';
import { Curve, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams } from './curves';
import * as aes_kw from '../../aes_kw';
import * as cipher from '../../cipher';
import { getRandomBytes } from '../../random';
import hash from '../../hash';
import enums from '../../../enums';
import util from '../../../util';
import pkcs5 from '../../pkcs5';
import * as pkcs5 from '../../pkcs5';
import MPI from '../../../type/mpi';
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
@ -52,7 +52,7 @@ const nodeCrypto = util.getNodeCrypto();
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
async function validateParams(oid, Q, d) {
export async function validateParams(oid, Q, d) {
return validateStandardParams(enums.publicKey.ecdh, oid, Q, d);
}
@ -102,7 +102,7 @@ async function kdf(hash_algo, X, length, param, stripLeading = false, stripTrail
async function genPublicEphemeralKey(curve, Q) {
switch (curve.type) {
case 'curve25519': {
const d = await random.getRandomBytes(32);
const d = await getRandomBytes(32);
const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d);
let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
@ -134,7 +134,7 @@ async function genPublicEphemeralKey(curve, Q) {
* @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
* @async
*/
async function encrypt(oid, kdfParams, data, Q, fingerprint) {
export async function encrypt(oid, kdfParams, data, Q, fingerprint) {
const m = new MPI(pkcs5.encode(data));
const curve = new Curve(oid);
@ -196,7 +196,7 @@ async function genPrivateEphemeralKey(curve, V, Q, d) {
* @returns {Promise<Uint8Array>} Value derived from session key
* @async
*/
async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
export async function decrypt(oid, kdfParams, V, C, Q, d, fingerprint) {
const curve = new Curve(oid);
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
@ -387,5 +387,3 @@ async function nodePublicEphemeralKey(curve, Q) {
const publicKey = new Uint8Array(sender.getPublicKey());
return { publicKey, sharedKey };
}
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams };

View File

@ -26,9 +26,9 @@
import enums from '../../../enums';
import util from '../../../util';
import random from '../../random';
import { getRandomBytes } from '../../random';
import hash from '../../hash';
import Curve, { webCurves, privateToJwk, rawPublicToJwk, validateStandardParams } from './curves';
import { Curve, webCurves, privateToJwk, rawPublicToJwk, validateStandardParams } from './curves';
import { getIndutnyCurve, keyFromPrivate, keyFromPublic } from './indutnyKey';
const webCrypto = util.getWebCrypto();
@ -46,7 +46,7 @@ const nodeCrypto = util.getNodeCrypto();
* s: Uint8Array}} Signature of the message
* @async
*/
async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
export async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
const curve = new Curve(oid);
if (message && !util.isStream(message)) {
const keyPair = { publicKey, privateKey };
@ -91,7 +91,7 @@ async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
* @returns {Boolean}
* @async
*/
async function verify(oid, hash_algo, signature, message, publicKey, hashed) {
export async function verify(oid, hash_algo, signature, message, publicKey, hashed) {
const curve = new Curve(oid);
if (message && !util.isStream(message)) {
switch (curve.type) {
@ -125,7 +125,7 @@ async function verify(oid, hash_algo, signature, message, publicKey, hashed) {
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
async function validateParams(oid, Q, d) {
export async function validateParams(oid, Q, d) {
const curve = new Curve(oid);
// Reject curves x25519 and ed25519
if (curve.keyType !== enums.publicKey.ecdsa) {
@ -137,7 +137,7 @@ async function validateParams(oid, Q, d) {
switch (curve.type) {
case 'web':
case 'node': {
const message = await random.getRandomBytes(8);
const message = await getRandomBytes(8);
const hashAlgo = enums.hash.sha256;
const hashed = await hash.digest(hashAlgo, message);
try {
@ -152,8 +152,6 @@ async function validateParams(oid, Q, d) {
}
}
export default { sign, verify, ellipticVerify, ellipticSign, validateParams };
//////////////////////////
// //

View File

@ -42,7 +42,7 @@ nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest());
* S: Uint8Array}} Signature of the message
* @async
*/
async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
export async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
const secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]);
const signature = nacl.sign.detached(hashed, secretKey);
// EdDSA signature params are returned in little-endian format
@ -64,11 +64,10 @@ async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
* @returns {Boolean}
* @async
*/
async function verify(oid, hash_algo, { R, S }, m, publicKey, hashed) {
export async function verify(oid, hash_algo, { R, S }, m, publicKey, hashed) {
const signature = util.concatUint8Array([R, S]);
return nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1));
}
/**
* Validate EdDSA parameters
* @param {module:type/oid} oid Elliptic curve object identifier
@ -77,7 +76,7 @@ async function verify(oid, hash_algo, { R, S }, m, publicKey, hashed) {
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
async function validateParams(oid, Q, k) {
export async function validateParams(oid, Q, k) {
// Check whether the given curve is supported
if (oid.getName() !== 'ed25519') {
return false;
@ -91,5 +90,3 @@ async function validateParams(oid, Q, k) {
const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
return util.equalsUint8Array(Q, dG);
}
export default { sign, verify, validateParams };

View File

@ -24,11 +24,11 @@
* @module crypto/public_key/elliptic
*/
import Curve, { generate, getPreferredHashAlgo } from './curves';
import ecdsa from './ecdsa';
import eddsa from './eddsa';
import ecdh from './ecdh';
import { Curve, generate, getPreferredHashAlgo } from './curves';
import * as ecdsa from './ecdsa';
import * as eddsa from './eddsa';
import * as ecdh from './ecdh';
export default {
export {
Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo
};

View File

@ -9,10 +9,10 @@
*/
import nacl from 'tweetnacl/nacl-fast-light.js';
import rsa from './rsa';
import elgamal from './elgamal';
import elliptic from './elliptic';
import dsa from './dsa';
import * as rsa from './rsa';
import * as elgamal from './elgamal';
import * as elliptic from './elliptic';
import * as dsa from './dsa';
export default {
/** @see module:crypto/public_key/rsa */

View File

@ -22,11 +22,7 @@
*/
import util from '../../util';
import random from '../random';
export default {
randomProbablePrime, isProbablePrime, fermat, millerRabin, divisionTest
};
import { getRandomBigInteger } from '../random';
/**
* Probabilistic random number generator
@ -36,7 +32,7 @@ export default {
* @returns BigInteger
* @async
*/
async function randomProbablePrime(bits, e, k) {
export async function randomProbablePrime(bits, e, k) {
const BigInteger = await util.getBigInteger();
const one = new BigInteger(1);
const min = one.leftShift(new BigInteger(bits - 1));
@ -49,7 +45,7 @@ async function randomProbablePrime(bits, e, k) {
*/
const adds = [1, 6, 5, 4, 3, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 2, 1, 4, 3, 2, 1, 6, 5, 4, 3, 2, 1, 2];
const n = await random.getRandomBigInteger(min, min.leftShift(one));
const n = await getRandomBigInteger(min, min.leftShift(one));
let i = n.mod(thirty).toNumber();
do {
@ -72,7 +68,7 @@ async function randomProbablePrime(bits, e, k) {
* @returns {boolean}
* @async
*/
async function isProbablePrime(n, e, k) {
export async function isProbablePrime(n, e, k) {
if (e && !n.dec().gcd(e).isOne()) {
return false;
}
@ -97,13 +93,13 @@ async function isProbablePrime(n, e, k) {
* @param {BigInteger} b Optional Fermat test base
* @returns {boolean}
*/
async function fermat(n, b) {
export async function fermat(n, b) {
const BigInteger = await util.getBigInteger();
b = b || new BigInteger(2);
return b.modExp(n.dec(), n).isOne();
}
async function divisionTest(n) {
export async function divisionTest(n) {
const BigInteger = await util.getBigInteger();
return smallPrimes.every(m => {
return n.mod(new BigInteger(m)) !== 0;
@ -232,7 +228,7 @@ const smallPrimes = [
* @returns {boolean}
* @async
*/
async function millerRabin(n, k, rand) {
export async function millerRabin(n, k, rand) {
const BigInteger = await util.getBigInteger();
const len = n.bitLength();
@ -248,7 +244,7 @@ async function millerRabin(n, k, rand) {
const d = n.rightShift(new BigInteger(s));
for (; k > 0; k--) {
const a = rand ? rand() : await random.getRandomBigInteger(new BigInteger(2), n1);
const a = rand ? rand() : await getRandomBigInteger(new BigInteger(2), n1);
let x = a.modExp(d, n);
if (x.isOne() || x.equal(n1)) {

View File

@ -24,11 +24,11 @@
* @module crypto/public_key/rsa
*/
import prime from './prime';
import random from '../random';
import { randomProbablePrime } from './prime';
import { getRandomBigInteger } from '../random';
import config from '../../config';
import util from '../../util';
import pkcs1 from '../pkcs1';
import { emsaEncode, emeEncode, emeDecode } from '../pkcs1';
import enums from '../../enums';
const webCrypto = util.getWebCrypto();
@ -73,484 +73,480 @@ const RSAPublicKey = util.detectNode() ? asn1.define('RSAPubliceKey', function (
}) : undefined;
/* eslint-enable no-invalid-this */
export default {
/** Create signature
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Uint8Array} data message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA private coefficient
* @param {Uint8Array} hashed hashed message
* @returns {Uint8Array} RSA Signature
* @async
*/
sign: async function(hash_algo, data, n, e, d, p, q, u, hashed) {
if (data && !util.isStream(data)) {
if (util.getWebCrypto()) {
try {
return await this.webSign(enums.read(enums.webHash, hash_algo), data, n, e, d, p, q, u);
} catch (err) {
util.printDebugError(err);
}
} else if (util.getNodeCrypto()) {
return this.nodeSign(hash_algo, data, n, e, d, p, q, u);
}
}
return this.bnSign(hash_algo, n, d, hashed);
},
/**
* Verify signature
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Uint8Array} data message
* @param {Uint8Array} s signature
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} hashed hashed message
* @returns {Boolean}
* @async
*/
verify: async function(hash_algo, data, s, n, e, hashed) {
if (data && !util.isStream(data)) {
if (util.getWebCrypto()) {
try {
return await this.webVerify(enums.read(enums.webHash, hash_algo), data, s, n, e);
} catch (err) {
util.printDebugError(err);
}
} else if (util.getNodeCrypto()) {
return this.nodeVerify(hash_algo, data, s, n, e);
}
}
return this.bnVerify(hash_algo, s, n, e, hashed);
},
/**
* Encrypt message
* @param {Uint8Array} data message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @returns {Uint8Array} RSA Ciphertext
* @async
*/
encrypt: async function(data, n, e) {
if (util.getNodeCrypto()) {
return this.nodeEncrypt(data, n, e);
}
return this.bnEncrypt(data, n, e);
},
/**
* Decrypt RSA message
* @param {Uint8Array} m message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA private coefficient
* @returns {String} RSA Plaintext
* @async
*/
decrypt: async function(data, n, e, d, p, q, u) {
if (util.getNodeCrypto()) {
return this.nodeDecrypt(data, n, e, d, p, q, u);
}
return this.bnDecrypt(data, n, e, d, p, q, u);
},
/**
* Generate a new random private key B bits long with public exponent E.
*
* When possible, webCrypto or nodeCrypto is used. Otherwise, primes are generated using
* 40 rounds of the Miller-Rabin probabilistic random prime generation algorithm.
* @see module:crypto/public_key/prime
* @param {Integer} bits RSA bit length
* @param {Integer} e RSA public exponent
* @returns {{n, e, d,
* p, q ,u: Uint8Array}} RSA public modulus, RSA public exponent, RSA private exponent,
* RSA private prime p, RSA private prime q, u = p ** -1 mod q
* @async
*/
generate: async function(bits, e) {
const BigInteger = await util.getBigInteger();
e = new BigInteger(e);
// Native RSA keygen using Web Crypto
/** Create signature
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Uint8Array} data message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA private coefficient
* @param {Uint8Array} hashed hashed message
* @returns {Uint8Array} RSA Signature
* @async
*/
export async function sign(hash_algo, data, n, e, d, p, q, u, hashed) {
if (data && !util.isStream(data)) {
if (util.getWebCrypto()) {
let keyPair;
let keyGenOpt;
if ((globalThis.crypto && globalThis.crypto.subtle) || globalThis.msCrypto) {
// current standard spec
keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits, // the specified keysize in bits
publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keyPair = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
keyPair = await promisifyIE11Op(keyPair, 'Error generating RSA key pair.');
} else if (globalThis.crypto && globalThis.crypto.webkitSubtle) {
// outdated spec implemented by old Webkit
keyGenOpt = {
name: 'RSA-OAEP',
modulusLength: bits, // the specified keysize in bits
publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keyPair = await webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']);
} else {
throw new Error('Unknown WebCrypto implementation');
try {
return await webSign(enums.read(enums.webHash, hash_algo), data, n, e, d, p, q, u);
} catch (err) {
util.printDebugError(err);
}
} else if (util.getNodeCrypto()) {
return nodeSign(hash_algo, data, n, e, d, p, q, u);
}
}
return bnSign(hash_algo, n, d, hashed);
}
// export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
let jwk = webCrypto.exportKey('jwk', keyPair.privateKey);
jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.');
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
if (jwk instanceof ArrayBuffer) {
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
/**
* Verify signature
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Uint8Array} data message
* @param {Uint8Array} s signature
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} hashed hashed message
* @returns {Boolean}
* @async
*/
export async function verify(hash_algo, data, s, n, e, hashed) {
if (data && !util.isStream(data)) {
if (util.getWebCrypto()) {
try {
return await webVerify(enums.read(enums.webHash, hash_algo), data, s, n, e);
} catch (err) {
util.printDebugError(err);
}
// map JWK parameters to corresponding OpenPGP names
return {
n: util.b64ToUint8Array(jwk.n),
e: e.toUint8Array(),
d: util.b64ToUint8Array(jwk.d),
// switch p and q
p: util.b64ToUint8Array(jwk.q),
q: util.b64ToUint8Array(jwk.p),
// Since p and q are switched in places, u is the inverse of jwk.q
u: util.b64ToUint8Array(jwk.qi)
};
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
const opts = {
modulusLength: bits,
publicExponent: e.toNumber(),
publicKeyEncoding: { type: 'pkcs1', format: 'der' },
privateKeyEncoding: { type: 'pkcs1', format: 'der' }
};
const prv = await new Promise((resolve, reject) => nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => {
if (err) {
reject(err);
} else {
resolve(RSAPrivateKey.decode(der, 'der'));
} else if (util.getNodeCrypto()) {
return nodeVerify(hash_algo, data, s, n, e);
}
}
return bnVerify(hash_algo, s, n, e, hashed);
}
/**
* Encrypt message
* @param {Uint8Array} data message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @returns {Uint8Array} RSA Ciphertext
* @async
*/
export async function encrypt(data, n, e) {
if (util.getNodeCrypto()) {
return nodeEncrypt(data, n, e);
}
return bnEncrypt(data, n, e);
}
/**
* Decrypt RSA message
* @param {Uint8Array} m message
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA private coefficient
* @returns {String} RSA Plaintext
* @async
*/
export async function decrypt(data, n, e, d, p, q, u) {
if (util.getNodeCrypto()) {
return nodeDecrypt(data, n, e, d, p, q, u);
}
return bnDecrypt(data, n, e, d, p, q, u);
}
/**
* Generate a new random private key B bits long with public exponent E.
*
* When possible, webCrypto or nodeCrypto is used. Otherwise, primes are generated using
* 40 rounds of the Miller-Rabin probabilistic random prime generation algorithm.
* @see module:crypto/public_key/prime
* @param {Integer} bits RSA bit length
* @param {Integer} e RSA public exponent
* @returns {{n, e, d,
* p, q ,u: Uint8Array}} RSA public modulus, RSA public exponent, RSA private exponent,
* RSA private prime p, RSA private prime q, u = p ** -1 mod q
* @async
*/
export async function generate(bits, e) {
const BigInteger = await util.getBigInteger();
e = new BigInteger(e);
// Native RSA keygen using Web Crypto
if (util.getWebCrypto()) {
let keyPair;
let keyGenOpt;
if ((globalThis.crypto && globalThis.crypto.subtle) || globalThis.msCrypto) {
// current standard spec
keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: bits, // the specified keysize in bits
publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
}));
/**
* OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`.
* @link https://tools.ietf.org/html/rfc3447#section-3.2
* @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
*/
return {
n: prv.modulus.toArrayLike(Uint8Array),
e: prv.publicExponent.toArrayLike(Uint8Array),
d: prv.privateExponent.toArrayLike(Uint8Array),
// switch p and q
p: prv.prime2.toArrayLike(Uint8Array),
q: prv.prime1.toArrayLike(Uint8Array),
// Since p and q are switched in places, we can keep u as defined by DER
u: prv.coefficient.toArrayLike(Uint8Array)
};
keyPair = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
keyPair = await promisifyIE11Op(keyPair, 'Error generating RSA key pair.');
} else if (globalThis.crypto && globalThis.crypto.webkitSubtle) {
// outdated spec implemented by old Webkit
keyGenOpt = {
name: 'RSA-OAEP',
modulusLength: bits, // the specified keysize in bits
publicExponent: e.toUint8Array(), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keyPair = await webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']);
} else {
throw new Error('Unknown WebCrypto implementation');
}
// 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 q = await prime.randomProbablePrime(bits - (bits >> 1), e, 40);
let p = await prime.randomProbablePrime(bits >> 1, e, 40);
// export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
let jwk = webCrypto.exportKey('jwk', keyPair.privateKey);
jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.');
if (q.lt(p)) {
[p, q] = [q, p];
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
if (jwk instanceof ArrayBuffer) {
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
}
const phi = p.dec().imul(q.dec());
// map JWK parameters to corresponding OpenPGP names
return {
n: p.mul(q).toUint8Array(),
n: util.b64ToUint8Array(jwk.n),
e: e.toUint8Array(),
d: e.modInv(phi).toUint8Array(),
p: p.toUint8Array(),
q: q.toUint8Array(),
// dp: d.mod(p.subn(1)),
// dq: d.mod(q.subn(1)),
u: p.modInv(q).toUint8Array()
};
},
/**
* Validate RSA parameters
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA inverse of p w.r.t. q
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
validateParams: async function (n, e, d, p, q, u) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
p = new BigInteger(p);
q = new BigInteger(q);
// expect pq = n
if (!p.mul(q).equal(n)) {
return false;
}
const two = new BigInteger(2);
// expect p*u = 1 mod q
u = new BigInteger(u);
if (!p.mul(u).mod(q).isOne()) {
return false;
}
e = new BigInteger(e);
d = new BigInteger(d);
/**
* In RSA pkcs#1 the exponents (d, e) are inverses modulo lcm(p-1, q-1)
* We check that [de = 1 mod (p-1)] and [de = 1 mod (q-1)]
* By CRT on coprime factors of (p-1, q-1) it follows that [de = 1 mod lcm(p-1, q-1)]
*
* We blind the multiplication with r, and check that rde = r mod lcm(p-1, q-1)
*/
const nSizeOver3 = new BigInteger(Math.floor(n.bitLength() / 3));
const r = await random.getRandomBigInteger(two, two.leftShift(nSizeOver3)); // r in [ 2, 2^{|n|/3} ) < p and q
const rde = r.mul(d).mul(e);
const areInverses = rde.mod(p.dec()).equal(r) && rde.mod(q.dec()).equal(r);
if (!areInverses) {
return false;
}
return true;
},
bnSign: async function (hash_algo, n, d, hashed) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
const m = new BigInteger(await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength()));
d = new BigInteger(d);
if (m.gte(n)) {
throw new Error('Message size cannot exceed modulus size');
}
return m.modExp(d, n).toUint8Array('be', n.byteLength());
},
webSign: async function (hash_name, data, n, e, d, p, q, u) {
/** OpenPGP keys require that p < q, and Safari Web Crypto requires that p > q.
* We swap them in privateToJwk, so it usually works out, but nevertheless,
* not all OpenPGP keys are compatible with this requirement.
* OpenPGP.js used to generate RSA keys the wrong way around (p > q), and still
* does if the underlying Web Crypto does so (e.g. old MS Edge 50% of the time).
*/
const jwk = await privateToJwk(n, e, d, p, q, u);
const algo = {
name: "RSASSA-PKCS1-v1_5",
hash: { name: hash_name }
};
const key = await webCrypto.importKey("jwk", jwk, algo, false, ["sign"]);
// add hash field for ms edge support
return new Uint8Array(await webCrypto.sign({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, data));
},
nodeSign: async function (hash_algo, data, n, e, d, p, q, u) {
const { default: BN } = await import('bn.js');
const pBNum = new BN(p);
const qBNum = new BN(q);
const dBNum = new BN(d);
const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo));
sign.write(data);
sign.end();
const keyObject = {
version: 0,
modulus: new BN(n),
publicExponent: new BN(e),
privateExponent: new BN(d),
d: util.b64ToUint8Array(jwk.d),
// switch p and q
prime1: new BN(q),
prime2: new BN(p),
// switch dp and dq
exponent1: dq,
exponent2: dp,
coefficient: new BN(u)
p: util.b64ToUint8Array(jwk.q),
q: util.b64ToUint8Array(jwk.p),
// Since p and q are switched in places, u is the inverse of jwk.q
u: util.b64ToUint8Array(jwk.qi)
};
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
const der = RSAPrivateKey.encode(keyObject, 'der');
return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' }));
}
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
const opts = {
modulusLength: bits,
publicExponent: e.toNumber(),
publicKeyEncoding: { type: 'pkcs1', format: 'der' },
privateKeyEncoding: { type: 'pkcs1', format: 'der' }
};
const prv = await new Promise((resolve, reject) => nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => {
if (err) {
reject(err);
} else {
resolve(RSAPrivateKey.decode(der, 'der'));
}
}));
/**
* OpenPGP spec differs from DER spec, DER: `u = (inverse of q) mod p`, OpenPGP: `u = (inverse of p) mod q`.
* @link https://tools.ietf.org/html/rfc3447#section-3.2
* @link https://tools.ietf.org/html/draft-ietf-openpgp-rfc4880bis-08#section-5.6.1
*/
return {
n: prv.modulus.toArrayLike(Uint8Array),
e: prv.publicExponent.toArrayLike(Uint8Array),
d: prv.privateExponent.toArrayLike(Uint8Array),
// switch p and q
p: prv.prime2.toArrayLike(Uint8Array),
q: prv.prime1.toArrayLike(Uint8Array),
// Since p and q are switched in places, we can keep u as defined by DER
u: prv.coefficient.toArrayLike(Uint8Array)
};
}
// 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 q = await randomProbablePrime(bits - (bits >> 1), e, 40);
let p = await randomProbablePrime(bits >> 1, e, 40);
if (q.lt(p)) {
[p, q] = [q, p];
}
const phi = p.dec().imul(q.dec());
return {
n: p.mul(q).toUint8Array(),
e: e.toUint8Array(),
d: e.modInv(phi).toUint8Array(),
p: p.toUint8Array(),
q: q.toUint8Array(),
// dp: d.mod(p.subn(1)),
// dq: d.mod(q.subn(1)),
u: p.modInv(q).toUint8Array()
};
}
/**
* Validate RSA parameters
* @param {Uint8Array} n RSA public modulus
* @param {Uint8Array} e RSA public exponent
* @param {Uint8Array} d RSA private exponent
* @param {Uint8Array} p RSA private prime p
* @param {Uint8Array} q RSA private prime q
* @param {Uint8Array} u RSA inverse of p w.r.t. q
* @returns {Promise<Boolean>} whether params are valid
* @async
*/
export async function validateParams(n, e, d, p, q, u) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
p = new BigInteger(p);
q = new BigInteger(q);
// expect pq = n
if (!p.mul(q).equal(n)) {
return false;
}
const two = new BigInteger(2);
// expect p*u = 1 mod q
u = new BigInteger(u);
if (!p.mul(u).mod(q).isOne()) {
return false;
}
e = new BigInteger(e);
d = new BigInteger(d);
/**
* In RSA pkcs#1 the exponents (d, e) are inverses modulo lcm(p-1, q-1)
* We check that [de = 1 mod (p-1)] and [de = 1 mod (q-1)]
* By CRT on coprime factors of (p-1, q-1) it follows that [de = 1 mod lcm(p-1, q-1)]
*
* We blind the multiplication with r, and check that rde = r mod lcm(p-1, q-1)
*/
const nSizeOver3 = new BigInteger(Math.floor(n.bitLength() / 3));
const r = await getRandomBigInteger(two, two.leftShift(nSizeOver3)); // r in [ 2, 2^{|n|/3} ) < p and q
const rde = r.mul(d).mul(e);
const areInverses = rde.mod(p.dec()).equal(r) && rde.mod(q.dec()).equal(r);
if (!areInverses) {
return false;
}
return true;
}
async function bnSign(hash_algo, n, d, hashed) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
const m = new BigInteger(await emsaEncode(hash_algo, hashed, n.byteLength()));
d = new BigInteger(d);
if (m.gte(n)) {
throw new Error('Message size cannot exceed modulus size');
}
return m.modExp(d, n).toUint8Array('be', n.byteLength());
}
async function webSign(hash_name, data, n, e, d, p, q, u) {
/** OpenPGP keys require that p < q, and Safari Web Crypto requires that p > q.
* We swap them in privateToJwk, so it usually works out, but nevertheless,
* not all OpenPGP keys are compatible with this requirement.
* OpenPGP.js used to generate RSA keys the wrong way around (p > q), and still
* does if the underlying Web Crypto does so (e.g. old MS Edge 50% of the time).
*/
const jwk = await privateToJwk(n, e, d, p, q, u);
const algo = {
name: "RSASSA-PKCS1-v1_5",
hash: { name: hash_name }
};
const key = await webCrypto.importKey("jwk", jwk, algo, false, ["sign"]);
// add hash field for ms edge support
return new Uint8Array(await webCrypto.sign({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, data));
}
async function nodeSign(hash_algo, data, n, e, d, p, q, u) {
const { default: BN } = await import('bn.js');
const pBNum = new BN(p);
const qBNum = new BN(q);
const dBNum = new BN(d);
const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
const sign = nodeCrypto.createSign(enums.read(enums.hash, hash_algo));
sign.write(data);
sign.end();
const keyObject = {
version: 0,
modulus: new BN(n),
publicExponent: new BN(e),
privateExponent: new BN(d),
// switch p and q
prime1: new BN(q),
prime2: new BN(p),
// switch dp and dq
exponent1: dq,
exponent2: dp,
coefficient: new BN(u)
};
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
const der = RSAPrivateKey.encode(keyObject, 'der');
return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' }));
}
const pem = RSAPrivateKey.encode(keyObject, 'pem', {
label: 'RSA PRIVATE KEY'
});
return new Uint8Array(sign.sign(pem));
}
async function bnVerify(hash_algo, s, n, e, hashed) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
s = new BigInteger(s);
e = new BigInteger(e);
if (s.gte(n)) {
throw new Error('Signature size cannot exceed modulus size');
}
const EM1 = s.modExp(e, n).toUint8Array('be', n.byteLength());
const EM2 = await emsaEncode(hash_algo, hashed, n.byteLength());
return util.equalsUint8Array(EM1, EM2);
}
async function webVerify(hash_name, data, s, n, e) {
const jwk = publicToJwk(n, e);
const key = await webCrypto.importKey("jwk", jwk, {
name: "RSASSA-PKCS1-v1_5",
hash: { name: hash_name }
}, false, ["verify"]);
// add hash field for ms edge support
return webCrypto.verify({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, s, data);
}
async function nodeVerify(hash_algo, data, s, n, e) {
const { default: BN } = await import('bn.js');
const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo));
verify.write(data);
verify.end();
const keyObject = {
modulus: new BN(n),
publicExponent: new BN(e)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
const der = RSAPublicKey.encode(keyObject, 'der');
key = { key: der, format: 'der', type: 'pkcs1' };
} else {
key = RSAPublicKey.encode(keyObject, 'pem', {
label: 'RSA PUBLIC KEY'
});
}
try {
return await verify.verify(key, s);
} catch (err) {
return false;
}
}
async function nodeEncrypt(data, n, e) {
const { default: BN } = await import('bn.js');
const keyObject = {
modulus: new BN(n),
publicExponent: new BN(e)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') {
const der = RSAPublicKey.encode(keyObject, 'der');
key = { key: der, format: 'der', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
} else {
const pem = RSAPublicKey.encode(keyObject, 'pem', {
label: 'RSA PUBLIC KEY'
});
key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
}
return new Uint8Array(nodeCrypto.publicEncrypt(key, data));
}
async function bnEncrypt(data, n, e) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
data = new BigInteger(await emeEncode(data, n.byteLength()));
e = new BigInteger(e);
if (data.gte(n)) {
throw new Error('Message size cannot exceed modulus size');
}
return data.modExp(e, n).toUint8Array('be', n.byteLength());
}
async function nodeDecrypt(data, n, e, d, p, q, u) {
const { default: BN } = await import('bn.js');
const pBNum = new BN(p);
const qBNum = new BN(q);
const dBNum = new BN(d);
const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
const keyObject = {
version: 0,
modulus: new BN(n),
publicExponent: new BN(e),
privateExponent: new BN(d),
// switch p and q
prime1: new BN(q),
prime2: new BN(p),
// switch dp and dq
exponent1: dq,
exponent2: dp,
coefficient: new BN(u)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') {
const der = RSAPrivateKey.encode(keyObject, 'der');
key = { key: der, format: 'der' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
} else {
const pem = RSAPrivateKey.encode(keyObject, 'pem', {
label: 'RSA PRIVATE KEY'
});
return new Uint8Array(sign.sign(pem));
},
key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
}
try {
return new Uint8Array(nodeCrypto.privateDecrypt(key, data));
} catch (err) {
throw new Error('Decryption error');
}
}
bnVerify: async function (hash_algo, s, n, e, hashed) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
s = new BigInteger(s);
e = new BigInteger(e);
if (s.gte(n)) {
throw new Error('Signature size cannot exceed modulus size');
}
const EM1 = s.modExp(e, n).toUint8Array('be', n.byteLength());
const EM2 = await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength());
return util.equalsUint8Array(EM1, EM2);
},
async function bnDecrypt(data, n, e, d, p, q, u) {
const BigInteger = await util.getBigInteger();
data = new BigInteger(data);
n = new BigInteger(n);
e = new BigInteger(e);
d = new BigInteger(d);
p = new BigInteger(p);
q = new BigInteger(q);
u = new BigInteger(u);
if (data.gte(n)) {
throw new Error('Data too large.');
}
const dq = d.mod(q.dec()); // d mod (q-1)
const dp = d.mod(p.dec()); // d mod (p-1)
webVerify: async function (hash_name, data, s, n, e) {
const jwk = publicToJwk(n, e);
const key = await webCrypto.importKey("jwk", jwk, {
name: "RSASSA-PKCS1-v1_5",
hash: { name: hash_name }
}, false, ["verify"]);
// add hash field for ms edge support
return webCrypto.verify({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, s, data);
},
let blinder;
let unblinder;
if (config.rsaBlinding) {
unblinder = (await getRandomBigInteger(new BigInteger(2), n)).mod(n);
blinder = unblinder.modInv(n).modExp(e, n);
data = data.mul(blinder).mod(n);
}
nodeVerify: async function (hash_algo, data, s, n, e) {
const { default: BN } = await import('bn.js');
const mp = data.modExp(dp, p); // data**{d mod (q-1)} mod p
const mq = data.modExp(dq, q); // data**{d mod (p-1)} mod q
const h = u.mul(mq.sub(mp)).mod(q); // u * (mq-mp) mod q (operands already < q)
const verify = nodeCrypto.createVerify(enums.read(enums.hash, hash_algo));
verify.write(data);
verify.end();
const keyObject = {
modulus: new BN(n),
publicExponent: new BN(e)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
const der = RSAPublicKey.encode(keyObject, 'der');
key = { key: der, format: 'der', type: 'pkcs1' };
} else {
key = RSAPublicKey.encode(keyObject, 'pem', {
label: 'RSA PUBLIC KEY'
});
}
try {
return await verify.verify(key, s);
} catch (err) {
return false;
}
},
let result = h.mul(p).add(mp); // result < n due to relations above
nodeEncrypt: async function (data, n, e) {
const { default: BN } = await import('bn.js');
if (config.rsaBlinding) {
result = result.mul(unblinder).mod(n);
}
const keyObject = {
modulus: new BN(n),
publicExponent: new BN(e)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') {
const der = RSAPublicKey.encode(keyObject, 'der');
key = { key: der, format: 'der', type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
} else {
const pem = RSAPublicKey.encode(keyObject, 'pem', {
label: 'RSA PUBLIC KEY'
});
key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
}
return new Uint8Array(nodeCrypto.publicEncrypt(key, data));
},
bnEncrypt: async function (data, n, e) {
const BigInteger = await util.getBigInteger();
n = new BigInteger(n);
data = new BigInteger(await pkcs1.eme.encode(data, n.byteLength()));
e = new BigInteger(e);
if (data.gte(n)) {
throw new Error('Message size cannot exceed modulus size');
}
return data.modExp(e, n).toUint8Array('be', n.byteLength());
},
nodeDecrypt: async function (data, n, e, d, p, q, u) {
const { default: BN } = await import('bn.js');
const pBNum = new BN(p);
const qBNum = new BN(q);
const dBNum = new BN(d);
const dq = dBNum.mod(qBNum.subn(1)); // d mod (q-1)
const dp = dBNum.mod(pBNum.subn(1)); // d mod (p-1)
const keyObject = {
version: 0,
modulus: new BN(n),
publicExponent: new BN(e),
privateExponent: new BN(d),
// switch p and q
prime1: new BN(q),
prime2: new BN(p),
// switch dp and dq
exponent1: dq,
exponent2: dp,
coefficient: new BN(u)
};
let key;
if (typeof nodeCrypto.createPrivateKey !== 'undefined') {
const der = RSAPrivateKey.encode(keyObject, 'der');
key = { key: der, format: 'der' , type: 'pkcs1', padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
} else {
const pem = RSAPrivateKey.encode(keyObject, 'pem', {
label: 'RSA PRIVATE KEY'
});
key = { key: pem, padding: nodeCrypto.constants.RSA_PKCS1_PADDING };
}
try {
return new Uint8Array(nodeCrypto.privateDecrypt(key, data));
} catch (err) {
throw new Error('Decryption error');
}
},
bnDecrypt: async function(data, n, e, d, p, q, u) {
const BigInteger = await util.getBigInteger();
data = new BigInteger(data);
n = new BigInteger(n);
e = new BigInteger(e);
d = new BigInteger(d);
p = new BigInteger(p);
q = new BigInteger(q);
u = new BigInteger(u);
if (data.gte(n)) {
throw new Error('Data too large.');
}
const dq = d.mod(q.dec()); // d mod (q-1)
const dp = d.mod(p.dec()); // d mod (p-1)
let blinder;
let unblinder;
if (config.rsaBlinding) {
unblinder = (await random.getRandomBigInteger(new BigInteger(2), n)).mod(n);
blinder = unblinder.modInv(n).modExp(e, n);
data = data.mul(blinder).mod(n);
}
const mp = data.modExp(dp, p); // data**{d mod (q-1)} mod p
const mq = data.modExp(dq, q); // data**{d mod (p-1)} mod q
const h = u.mul(mq.sub(mp)).mod(q); // u * (mq-mp) mod q (operands already < q)
let result = h.mul(p).add(mp); // result < n due to relations above
if (config.rsaBlinding) {
result = result.mul(unblinder).mod(n);
}
return pkcs1.eme.decode(result.toUint8Array('be', n.byteLength()));
},
prime: prime
};
return emeDecode(result.toUint8Array('be', n.byteLength()));
}
/** Convert Openpgp private key params to jwk key according to
* @link https://tools.ietf.org/html/rfc7517

View File

@ -94,53 +94,51 @@ class RandomBuffer {
}
}
export default {
/**
* Retrieve secure random byte array of the specified length
* @param {Integer} length Length in bytes to generate
* @returns {Uint8Array} Random byte array
* @async
*/
getRandomBytes: async function(length) {
const buf = new Uint8Array(length);
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
crypto.getRandomValues(buf);
} else if (typeof globalThis !== 'undefined' && typeof globalThis.msCrypto === 'object' && typeof globalThis.msCrypto.getRandomValues === 'function') {
globalThis.msCrypto.getRandomValues(buf);
} else if (nodeCrypto) {
const bytes = nodeCrypto.randomBytes(buf.length);
buf.set(bytes);
} else if (this.randomBuffer.buffer) {
await this.randomBuffer.get(buf);
} else {
throw new Error('No secure random number generator available.');
}
return buf;
},
/**
* Retrieve secure random byte array of the specified length
* @param {Integer} length Length in bytes to generate
* @returns {Uint8Array} Random byte array
* @async
*/
export async function getRandomBytes(length) {
const buf = new Uint8Array(length);
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
crypto.getRandomValues(buf);
} else if (typeof globalThis !== 'undefined' && typeof globalThis.msCrypto === 'object' && typeof globalThis.msCrypto.getRandomValues === 'function') {
globalThis.msCrypto.getRandomValues(buf);
} else if (nodeCrypto) {
const bytes = nodeCrypto.randomBytes(buf.length);
buf.set(bytes);
} else if (randomBuffer.buffer) {
await randomBuffer.get(buf);
} else {
throw new Error('No secure random number generator available.');
}
return buf;
}
/**
* Create a secure random BigInteger that is greater than or equal to min and less than max.
* @param {module:BigInteger} min Lower bound, included
* @param {module:BigInteger} max Upper bound, excluded
* @returns {module:BigInteger} Random BigInteger
* @async
*/
getRandomBigInteger: async function(min, max) {
const BigInteger = await util.getBigInteger();
/**
* Create a secure random BigInteger that is greater than or equal to min and less than max.
* @param {module:BigInteger} min Lower bound, included
* @param {module:BigInteger} max Upper bound, excluded
* @returns {module:BigInteger} Random BigInteger
* @async
*/
export async function getRandomBigInteger(min, max) {
const BigInteger = await util.getBigInteger();
if (max.lt(min)) {
throw new Error('Illegal parameter value: max <= min');
}
if (max.lt(min)) {
throw new Error('Illegal parameter value: max <= min');
}
const modulus = max.sub(min);
const bytes = modulus.byteLength();
const modulus = max.sub(min);
const bytes = modulus.byteLength();
// Using a while loop is necessary to avoid bias introduced by the mod operation.
// However, we request 64 extra random bits so that the bias is negligible.
// Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
const r = new BigInteger(await this.getRandomBytes(bytes + 8));
return r.mod(modulus).add(min);
},
// Using a while loop is necessary to avoid bias introduced by the mod operation.
// However, we request 64 extra random bits so that the bias is negligible.
// Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
const r = new BigInteger(await getRandomBytes(bytes + 8));
return r.mod(modulus).add(min);
}
randomBuffer: new RandomBuffer()
};
export const randomBuffer = new RandomBuffer();

View File

@ -1,125 +1,121 @@
/**
* @fileoverview Provides functions for asymmetric signing and signature verification
* @requires crypto/crypto
* @requires crypto/public_key
* @requires enums
* @requires util
* @module crypto/signature
*/
import crypto from './crypto';
import publicKey from './public_key';
import enums from '../enums';
import util from '../util';
export default {
/**
* Verifies the signature provided for data using specified algorithms and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
* @param {Object} publicParams Algorithm-specific public key parameters
* @param {Uint8Array} data Data for which the signature was created
* @param {Uint8Array} hashed The hashed data
* @returns {Boolean} True if signature is valid
* @async
*/
verify: async function(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicParams;
const m = msg_MPIs[0].toUint8Array('be', n.length);
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
}
case enums.publicKey.dsa: {
const r = await msg_MPIs[0].toUint8Array();
const s = await msg_MPIs[1].toUint8Array();
const { g, p, q, y } = publicParams;
return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
}
case enums.publicKey.ecdsa: {
const { oid, Q } = publicParams;
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicParams;
// EdDSA signature params are expected in little-endian format
const signature = {
R: msg_MPIs[0].toUint8Array('le', 32),
S: msg_MPIs[1].toUint8Array('le', 32)
};
return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q, hashed);
}
default:
throw new Error('Invalid signature algorithm.');
/**
* Verifies the signature provided for data using specified algorithms and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
* @param {Object} publicParams Algorithm-specific public key parameters
* @param {Uint8Array} data Data for which the signature was created
* @param {Uint8Array} hashed The hashed data
* @returns {Boolean} True if signature is valid
* @async
*/
export async function verify(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicParams;
const m = msg_MPIs[0].toUint8Array('be', n.length);
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
}
},
/**
* Creates a signature on data using specified algorithms and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Object} publicKeyParams Algorithm-specific public and private key parameters
* @param {Object} privateKeyParams Algorithm-specific public and private key parameters
* @param {Uint8Array} data Data to be signed
* @param {Uint8Array} hashed The hashed data
* @returns {Uint8Array} Signature
* @async
*/
sign: async function(algo, hash_algo, publicKeyParams, privateKeyParams, data, hashed) {
if (!publicKeyParams || !privateKeyParams) {
throw new Error('Missing key parameters');
case enums.publicKey.dsa: {
const r = await msg_MPIs[0].toUint8Array();
const s = await msg_MPIs[1].toUint8Array();
const { g, p, q, y } = publicParams;
return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
}
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicKeyParams;
const { d, p, q, u } = privateKeyParams;
const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
return util.uint8ArrayToMpi(signature);
}
case enums.publicKey.dsa: {
const { g, p, q } = publicKeyParams;
const { x } = privateKeyParams;
const signature = await publicKey.dsa.sign(hash_algo, hashed, g, p, q, x);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.r),
util.uint8ArrayToMpi(signature.s)
]);
}
case enums.publicKey.elgamal: {
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
}
case enums.publicKey.ecdsa: {
const { oid, Q } = publicKeyParams;
const { d } = privateKeyParams;
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, Q, d, hashed);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.r),
util.uint8ArrayToMpi(signature.s)
]);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicKeyParams;
const { seed } = privateKeyParams;
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, Q, seed, hashed);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.R),
util.uint8ArrayToMpi(signature.S)
]);
}
default:
throw new Error('Invalid signature algorithm.');
case enums.publicKey.ecdsa: {
const { oid, Q } = publicParams;
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicParams;
// EdDSA signature params are expected in little-endian format
const signature = {
R: msg_MPIs[0].toUint8Array('le', 32),
S: msg_MPIs[1].toUint8Array('le', 32)
};
return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q, hashed);
}
default:
throw new Error('Invalid signature algorithm.');
}
};
}
/**
* Creates a signature on data using specified algorithms and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Object} publicKeyParams Algorithm-specific public and private key parameters
* @param {Object} privateKeyParams Algorithm-specific public and private key parameters
* @param {Uint8Array} data Data to be signed
* @param {Uint8Array} hashed The hashed data
* @returns {Uint8Array} Signature
* @async
*/
export async function sign(algo, hash_algo, publicKeyParams, privateKeyParams, data, hashed) {
if (!publicKeyParams || !privateKeyParams) {
throw new Error('Missing key parameters');
}
switch (algo) {
case enums.publicKey.rsaEncryptSign:
case enums.publicKey.rsaEncrypt:
case enums.publicKey.rsaSign: {
const { n, e } = publicKeyParams;
const { d, p, q, u } = privateKeyParams;
const signature = await publicKey.rsa.sign(hash_algo, data, n, e, d, p, q, u, hashed);
return util.uint8ArrayToMpi(signature);
}
case enums.publicKey.dsa: {
const { g, p, q } = publicKeyParams;
const { x } = privateKeyParams;
const signature = await publicKey.dsa.sign(hash_algo, hashed, g, p, q, x);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.r),
util.uint8ArrayToMpi(signature.s)
]);
}
case enums.publicKey.elgamal: {
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
}
case enums.publicKey.ecdsa: {
const { oid, Q } = publicKeyParams;
const { d } = privateKeyParams;
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, Q, d, hashed);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.r),
util.uint8ArrayToMpi(signature.s)
]);
}
case enums.publicKey.eddsa: {
const { oid, Q } = publicKeyParams;
const { seed } = privateKeyParams;
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, Q, seed, hashed);
return util.concatUint8Array([
util.uint8ArrayToMpi(signature.R),
util.uint8ArrayToMpi(signature.S)
]);
}
default:
throw new Error('Invalid signature algorithm.');
}
}

View File

@ -25,7 +25,7 @@
*/
import stream from 'web-stream-tools';
import base64 from './base64.js';
import * as base64 from './base64.js';
import enums from '../enums.js';
import config from '../config';
import util from '../util';
@ -233,7 +233,7 @@ function splitChecksum(text) {
* @async
* @static
*/
function dearmor(input) {
export function unarmor(input) {
return new Promise(async (resolve, reject) => {
try {
const reSplit = /^-----[^-]+-----$/m;
@ -359,7 +359,7 @@ function dearmor(input) {
* @returns {String | ReadableStream<String>} Armored text
* @static
*/
function armor(messagetype, body, partindex, parttotal, customComment) {
export function armor(messagetype, body, partindex, parttotal, customComment) {
let text;
let hash;
if (messagetype === enums.armor.signed) {
@ -426,8 +426,3 @@ function armor(messagetype, body, partindex, parttotal, customComment) {
return util.concat(result);
}
export default {
encode: armor,
decode: dearmor
};

View File

@ -41,7 +41,7 @@ if (Buffer) {
* @returns {String | ReadableStream<String>} radix-64 version of input string
* @static
*/
function encode(data) {
export function encode(data) {
let buf = new Uint8Array();
return stream.transform(data, value => {
buf = util.concatUint8Array([buf, value]);
@ -65,7 +65,7 @@ function encode(data) {
* @returns {Uint8Array | ReadableStream<Uint8Array>} binary array version of input string
* @static
*/
function decode(data) {
export function decode(data) {
let buf = '';
return stream.transform(data, value => {
buf += value;
@ -92,5 +92,3 @@ function decode(data) {
return decoded;
}, () => decodeChunk(buf));
}
export default { encode, decode };

View File

@ -126,7 +126,6 @@ export default {
plaintext: 0,
/** Not implemented! */
idea: 1,
'3des': 2,
tripledes: 2,
cast5: 3,
blowfish: 4,

View File

@ -98,7 +98,7 @@ export { default as stream } from 'web-stream-tools';
* @see module:encoding/armor
* @name module:openpgp.armor
*/
export { default as armor } from './encoding/armor';
export * from './encoding/armor';
/**
* @see module:enums

View File

@ -33,7 +33,7 @@ import * as helper from './helper';
import enums from '../enums';
import util from '../util';
import config from '../config';
import armor from '../encoding/armor';
import { unarmor } from '../encoding/armor';
/**
* Generates a new OpenPGP key. Supports RSA and ECC keys.
@ -283,7 +283,7 @@ export async function readKey(data) {
* @static
*/
export async function readArmoredKey(armoredKey) {
const input = await armor.decode(armoredKey);
const input = await unarmor(armoredKey);
if (!(input.type === enums.armor.publicKey || input.type === enums.armor.privateKey)) {
throw new Error('Armored text not of type key');
}
@ -321,7 +321,7 @@ export async function readKeys(data) {
* @static
*/
export async function readArmoredKeys(armoredKey) {
const input = await armor.decode(armoredKey);
const input = await unarmor(armoredKey);
if (!(input.type === enums.armor.publicKey || input.type === enums.armor.privateKey)) {
throw new Error('Armored text not of type key');
}

View File

@ -25,7 +25,7 @@
* @module key/Key
*/
import armor from '../encoding/armor';
import { armor, unarmor } from '../encoding/armor';
import {
PacketList,
PublicKeyPacket,
@ -280,7 +280,7 @@ class Key {
*/
armor() {
const type = this.isPublic() ? enums.armor.publicKey : enums.armor.privateKey;
return armor.encode(type, this.toPacketlist().write());
return armor(type, this.toPacketlist().write());
}
/**
@ -749,7 +749,7 @@ class Key {
const revocationSignature = await helper.getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.keyRevocation, dataToVerify, date);
const packetlist = new PacketList();
packetlist.push(revocationSignature);
return armor.encode(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate');
return armor(enums.armor.publicKey, packetlist.write(), null, null, 'This is a revocation certificate');
}
/**
@ -761,7 +761,7 @@ class Key {
* @async
*/
async applyRevocationCertificate(revocationCertificate) {
const input = await armor.decode(revocationCertificate);
const input = await unarmor(revocationCertificate);
const packetlist = new PacketList();
await packetlist.read(input.data, { SignaturePacket });
const revocationSignature = packetlist.findPacket(enums.packet.signature);

View File

@ -30,7 +30,7 @@
*/
import stream from 'web-stream-tools';
import armor from './encoding/armor';
import { armor, unarmor } from './encoding/armor';
import type_keyid from './type/keyid';
import config from './config';
import crypto from './crypto';
@ -607,7 +607,7 @@ export class Message {
* @param {String|Uint8Array} detachedSignature The detached ASCII-armored or Uint8Array PGP signature
*/
async appendSignature(detachedSignature) {
await this.packets.read(util.isUint8Array(detachedSignature) ? detachedSignature : (await armor.decode(detachedSignature)).data, { SignaturePacket });
await this.packets.read(util.isUint8Array(detachedSignature) ? detachedSignature : (await unarmor(detachedSignature)).data, { SignaturePacket });
}
/**
@ -623,7 +623,7 @@ export class Message {
* @returns {ReadableStream<String>} ASCII armor
*/
armor() {
return armor.encode(enums.armor.message, this.write());
return armor(enums.armor.message, this.write());
}
/**
@ -816,7 +816,7 @@ export async function readArmoredMessage(armoredText) {
if (streamType === 'node') {
armoredText = stream.nodeToWeb(armoredText);
}
const input = await armor.decode(armoredText);
const input = await unarmor(armoredText);
return readMessage(input.data, streamType);
}

View File

@ -29,268 +29,266 @@ import stream from 'web-stream-tools';
import enums from '../enums';
import util from '../util';
export default {
readSimpleLength: function(bytes) {
let len = 0;
let offset;
const type = bytes[0];
export function readSimpleLength(bytes) {
let len = 0;
let offset;
const type = bytes[0];
if (type < 192) {
[len] = bytes;
offset = 1;
} else if (type < 255) {
len = ((bytes[0] - 192) << 8) + (bytes[1]) + 192;
offset = 2;
} else if (type === 255) {
len = util.readNumber(bytes.subarray(1, 1 + 4));
offset = 5;
if (type < 192) {
[len] = bytes;
offset = 1;
} else if (type < 255) {
len = ((bytes[0] - 192) << 8) + (bytes[1]) + 192;
offset = 2;
} else if (type === 255) {
len = util.readNumber(bytes.subarray(1, 1 + 4));
offset = 5;
}
return {
len: len,
offset: offset
};
}
/**
* Encodes a given integer of length to the openpgp length specifier to a
* string
*
* @param {Integer} length The length to encode
* @returns {Uint8Array} String with openpgp length representation
*/
export function writeSimpleLength(length) {
if (length < 192) {
return new Uint8Array([length]);
} else if (length > 191 && length < 8384) {
/*
* let a = (total data packet length) - 192 let bc = two octet
* representation of a let d = b + 192
*/
return new Uint8Array([((length - 192) >> 8) + 192, (length - 192) & 0xFF]);
}
return util.concatUint8Array([new Uint8Array([255]), util.writeNumber(length, 4)]);
}
export function writePartialLength(power) {
if (power < 0 || power > 30) {
throw new Error('Partial Length power must be between 1 and 30');
}
return new Uint8Array([224 + power]);
}
export function writeTag(tag_type) {
/* we're only generating v4 packet headers here */
return new Uint8Array([0xC0 | tag_type]);
}
/**
* Writes a packet header version 4 with the given tag_type and length to a
* string
*
* @param {Integer} tag_type Tag type
* @param {Integer} length Length of the payload
* @returns {String} String of the header
*/
export function writeHeader(tag_type, length) {
/* we're only generating v4 packet headers here */
return util.concatUint8Array([writeTag(tag_type), writeSimpleLength(length)]);
}
/**
* Whether the packet type supports partial lengths per RFC4880
* @param {Integer} tag_type Tag type
* @returns {Boolean} String of the header
*/
export function supportsStreaming(tag_type) {
return [
enums.packet.literalData,
enums.packet.compressedData,
enums.packet.symmetricallyEncryptedData,
enums.packet.symEncryptedIntegrityProtectedData,
enums.packet.AEADEncryptedData
].includes(tag_type);
}
/**
* Generic static Packet Parser function
*
* @param {Uint8Array | ReadableStream<Uint8Array>} input Input stream as string
* @param {Function} callback Function to call with the parsed packet
* @returns {Boolean} Returns false if the stream was empty and parsing is done, and true otherwise.
*/
export async function readPackets(input, streaming, callback) {
const reader = stream.getReader(input);
let writer;
let callbackReturned;
try {
const peekedBytes = await reader.peekBytes(2);
// some sanity checks
if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) {
throw new Error("Error during parsing. This message / key probably does not conform to a valid OpenPGP format.");
}
const headerByte = await reader.readByte();
let tag = -1;
let format = -1;
let packet_length;
format = 0; // 0 = old format; 1 = new format
if ((headerByte & 0x40) !== 0) {
format = 1;
}
return {
len: len,
offset: offset
};
},
/**
* Encodes a given integer of length to the openpgp length specifier to a
* string
*
* @param {Integer} length The length to encode
* @returns {Uint8Array} String with openpgp length representation
*/
writeSimpleLength: function(length) {
if (length < 192) {
return new Uint8Array([length]);
} else if (length > 191 && length < 8384) {
/*
* let a = (total data packet length) - 192 let bc = two octet
* representation of a let d = b + 192
*/
return new Uint8Array([((length - 192) >> 8) + 192, (length - 192) & 0xFF]);
let packet_length_type;
if (format) {
// new format header
tag = headerByte & 0x3F; // bit 5-0
} else {
// old format header
tag = (headerByte & 0x3F) >> 2; // bit 5-2
packet_length_type = headerByte & 0x03; // bit 1-0
}
return util.concatUint8Array([new Uint8Array([255]), util.writeNumber(length, 4)]);
},
writePartialLength: function(power) {
if (power < 0 || power > 30) {
throw new Error('Partial Length power must be between 1 and 30');
const packetSupportsStreaming = supportsStreaming(tag);
let packet = null;
if (streaming && packetSupportsStreaming) {
const transform = new stream.TransformStream();
writer = stream.getWriter(transform.writable);
packet = transform.readable;
callbackReturned = callback({ tag, packet });
} else {
packet = [];
}
return new Uint8Array([224 + power]);
},
writeTag: function(tag_type) {
/* we're only generating v4 packet headers here */
return new Uint8Array([0xC0 | tag_type]);
},
/**
* Writes a packet header version 4 with the given tag_type and length to a
* string
*
* @param {Integer} tag_type Tag type
* @param {Integer} length Length of the payload
* @returns {String} String of the header
*/
writeHeader: function(tag_type, length) {
/* we're only generating v4 packet headers here */
return util.concatUint8Array([this.writeTag(tag_type), this.writeSimpleLength(length)]);
},
/**
* Whether the packet type supports partial lengths per RFC4880
* @param {Integer} tag_type Tag type
* @returns {Boolean} String of the header
*/
supportsStreaming: function(tag_type) {
return [
enums.packet.literalData,
enums.packet.compressedData,
enums.packet.symmetricallyEncryptedData,
enums.packet.symEncryptedIntegrityProtectedData,
enums.packet.AEADEncryptedData
].includes(tag_type);
},
/**
* Generic static Packet Parser function
*
* @param {Uint8Array | ReadableStream<Uint8Array>} input Input stream as string
* @param {Function} callback Function to call with the parsed packet
* @returns {Boolean} Returns false if the stream was empty and parsing is done, and true otherwise.
*/
read: async function(input, streaming, callback) {
const reader = stream.getReader(input);
let writer;
let callbackReturned;
try {
const peekedBytes = await reader.peekBytes(2);
// some sanity checks
if (!peekedBytes || peekedBytes.length < 2 || (peekedBytes[0] & 0x80) === 0) {
throw new Error("Error during parsing. This message / key probably does not conform to a valid OpenPGP format.");
}
const headerByte = await reader.readByte();
let tag = -1;
let format = -1;
let packet_length;
format = 0; // 0 = old format; 1 = new format
if ((headerByte & 0x40) !== 0) {
format = 1;
}
let packet_length_type;
if (format) {
// new format header
tag = headerByte & 0x3F; // bit 5-0
} else {
// old format header
tag = (headerByte & 0x3F) >> 2; // bit 5-2
packet_length_type = headerByte & 0x03; // bit 1-0
}
const supportsStreaming = this.supportsStreaming(tag);
let packet = null;
if (streaming && supportsStreaming) {
const transform = new stream.TransformStream();
writer = stream.getWriter(transform.writable);
packet = transform.readable;
callbackReturned = callback({ tag, packet });
} else {
packet = [];
}
let wasPartialLength;
do {
if (!format) {
// 4.2.1. Old Format Packet Lengths
switch (packet_length_type) {
case 0:
// The packet has a one-octet length. The header is 2 octets
// long.
packet_length = await reader.readByte();
break;
case 1:
// The packet has a two-octet length. The header is 3 octets
// long.
packet_length = (await reader.readByte() << 8) | await reader.readByte();
break;
case 2:
// The packet has a four-octet length. The header is 5
// octets long.
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
8) | await reader.readByte();
break;
default:
// 3 - The packet is of indeterminate length. The header is 1
// octet long, and the implementation must determine how long
// the packet is. If the packet is in a file, this means that
// the packet extends until the end of the file. In general,
// an implementation SHOULD NOT use indeterminate-length
// packets except where the end of the data will be clear
// from the context, and even then it is better to use a
// definite length, or a new format header. The new format
// headers described below have a mechanism for precisely
// encoding data of indeterminate length.
packet_length = Infinity;
break;
}
} else { // 4.2.2. New Format Packet Lengths
// 4.2.2.1. One-Octet Lengths
const lengthByte = await reader.readByte();
wasPartialLength = false;
if (lengthByte < 192) {
packet_length = lengthByte;
// 4.2.2.2. Two-Octet Lengths
} else if (lengthByte >= 192 && lengthByte < 224) {
packet_length = ((lengthByte - 192) << 8) + (await reader.readByte()) + 192;
// 4.2.2.4. Partial Body Lengths
} else if (lengthByte > 223 && lengthByte < 255) {
packet_length = 1 << (lengthByte & 0x1F);
wasPartialLength = true;
if (!supportsStreaming) {
throw new TypeError('This packet type does not support partial lengths.');
}
// 4.2.2.3. Five-Octet Lengths
} else {
let wasPartialLength;
do {
if (!format) {
// 4.2.1. Old Format Packet Lengths
switch (packet_length_type) {
case 0:
// The packet has a one-octet length. The header is 2 octets
// long.
packet_length = await reader.readByte();
break;
case 1:
// The packet has a two-octet length. The header is 3 octets
// long.
packet_length = (await reader.readByte() << 8) | await reader.readByte();
break;
case 2:
// The packet has a four-octet length. The header is 5
// octets long.
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
8) | await reader.readByte();
break;
default:
// 3 - The packet is of indeterminate length. The header is 1
// octet long, and the implementation must determine how long
// the packet is. If the packet is in a file, this means that
// the packet extends until the end of the file. In general,
// an implementation SHOULD NOT use indeterminate-length
// packets except where the end of the data will be clear
// from the context, and even then it is better to use a
// definite length, or a new format header. The new format
// headers described below have a mechanism for precisely
// encoding data of indeterminate length.
packet_length = Infinity;
break;
}
} else { // 4.2.2. New Format Packet Lengths
// 4.2.2.1. One-Octet Lengths
const lengthByte = await reader.readByte();
wasPartialLength = false;
if (lengthByte < 192) {
packet_length = lengthByte;
// 4.2.2.2. Two-Octet Lengths
} else if (lengthByte >= 192 && lengthByte < 224) {
packet_length = ((lengthByte - 192) << 8) + (await reader.readByte()) + 192;
// 4.2.2.4. Partial Body Lengths
} else if (lengthByte > 223 && lengthByte < 255) {
packet_length = 1 << (lengthByte & 0x1F);
wasPartialLength = true;
if (!packetSupportsStreaming) {
throw new TypeError('This packet type does not support partial lengths.');
}
// 4.2.2.3. Five-Octet Lengths
} else {
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
8) | await reader.readByte();
}
}
if (packet_length > 0) {
let bytesRead = 0;
while (true) {
if (writer) await writer.ready;
const { done, value } = await reader.read();
if (done) {
if (packet_length === Infinity) break;
throw new Error('Unexpected end of packet');
}
const chunk = packet_length === Infinity ? value : value.subarray(0, packet_length - bytesRead);
if (writer) await writer.write(chunk);
else packet.push(chunk);
bytesRead += value.length;
if (bytesRead >= packet_length) {
reader.unshift(value.subarray(packet_length - bytesRead + value.length));
break;
}
}
if (packet_length > 0) {
let bytesRead = 0;
while (true) {
if (writer) await writer.ready;
const { done, value } = await reader.read();
if (done) {
if (packet_length === Infinity) break;
throw new Error('Unexpected end of packet');
}
const chunk = packet_length === Infinity ? value : value.subarray(0, packet_length - bytesRead);
if (writer) await writer.write(chunk);
else packet.push(chunk);
bytesRead += value.length;
if (bytesRead >= packet_length) {
reader.unshift(value.subarray(packet_length - bytesRead + value.length));
break;
}
}
}
} while (wasPartialLength);
}
} while (wasPartialLength);
// If this was not a packet that "supports streaming", we peek to check
// whether it is the last packet in the message. We peek 2 bytes instead
// of 1 because the beginning of this function also peeks 2 bytes, and we
// want to cut a `subarray` of the correct length into `web-stream-tools`'
// `externalBuffer` as a tiny optimization here.
//
// If it *was* a streaming packet (i.e. the data packets), we peek at the
// entire remainder of the stream, in order to forward errors in the
// remainder of the stream to the packet data. (Note that this means we
// read/peek at all signature packets before closing the literal data
// packet, for example.) This forwards MDC errors to the literal data
// stream, for example, so that they don't get lost / forgotten on
// decryptedMessage.packets.stream, which we never look at.
//
// An example of what we do when stream-parsing a message containing
// [ one-pass signature packet, literal data packet, signature packet ]:
// 1. Read the one-pass signature packet
// 2. Peek 2 bytes of the literal data packet
// 3. Parse the one-pass signature packet
//
// 4. Read the literal data packet, simultaneously stream-parsing it
// 5. Peek until the end of the message
// 6. Finish parsing the literal data packet
//
// 7. Read the signature packet again (we already peeked at it in step 5)
// 8. Peek at the end of the stream again (`peekBytes` returns undefined)
// 9. Parse the signature packet
//
// Note that this means that if there's an error in the very end of the
// stream, such as an MDC error, we throw in step 5 instead of in step 8
// (or never), which is the point of this exercise.
const nextPacket = await reader.peekBytes(supportsStreaming ? Infinity : 2);
if (writer) {
await writer.ready;
await writer.close();
} else {
packet = util.concatUint8Array(packet);
await callback({ tag, packet });
}
return !nextPacket || !nextPacket.length;
} catch (e) {
if (writer) {
await writer.abort(e);
return true;
} else {
throw e;
}
} finally {
if (writer) {
await callbackReturned;
}
reader.releaseLock();
// If this was not a packet that "supports streaming", we peek to check
// whether it is the last packet in the message. We peek 2 bytes instead
// of 1 because the beginning of this function also peeks 2 bytes, and we
// want to cut a `subarray` of the correct length into `web-stream-tools`'
// `externalBuffer` as a tiny optimization here.
//
// If it *was* a streaming packet (i.e. the data packets), we peek at the
// entire remainder of the stream, in order to forward errors in the
// remainder of the stream to the packet data. (Note that this means we
// read/peek at all signature packets before closing the literal data
// packet, for example.) This forwards MDC errors to the literal data
// stream, for example, so that they don't get lost / forgotten on
// decryptedMessage.packets.stream, which we never look at.
//
// An example of what we do when stream-parsing a message containing
// [ one-pass signature packet, literal data packet, signature packet ]:
// 1. Read the one-pass signature packet
// 2. Peek 2 bytes of the literal data packet
// 3. Parse the one-pass signature packet
//
// 4. Read the literal data packet, simultaneously stream-parsing it
// 5. Peek until the end of the message
// 6. Finish parsing the literal data packet
//
// 7. Read the signature packet again (we already peeked at it in step 5)
// 8. Peek at the end of the stream again (`peekBytes` returns undefined)
// 9. Parse the signature packet
//
// Note that this means that if there's an error in the very end of the
// stream, such as an MDC error, we throw in step 5 instead of in step 8
// (or never), which is the point of this exercise.
const nextPacket = await reader.peekBytes(packetSupportsStreaming ? Infinity : 2);
if (writer) {
await writer.ready;
await writer.close();
} else {
packet = util.concatUint8Array(packet);
await callback({ tag, packet });
}
return !nextPacket || !nextPacket.length;
} catch (e) {
if (writer) {
await writer.abort(e);
return true;
} else {
throw e;
}
} finally {
if (writer) {
await callbackReturned;
}
reader.releaseLock();
}
};
}

View File

@ -10,7 +10,11 @@
import stream from 'web-stream-tools';
import * as packets from './all_packets';
import packetParser from './packet';
import {
readPackets, supportsStreaming,
writeTag, writeHeader,
writePartialLength, writeSimpleLength
} from './packet';
import config from '../config';
import enums from '../enums';
import util from '../util';
@ -33,7 +37,7 @@ class PacketList extends Array {
try {
while (true) {
await writer.ready;
const done = await packetParser.read(readable, streaming, async parsed => {
const done = await readPackets(readable, streaming, async parsed => {
try {
const tag = enums.read(enums.packet, parsed.tag);
const packet = packets.newPacketFromTag(tag, allowedPackets);
@ -42,7 +46,7 @@ class PacketList extends Array {
await packet.read(parsed.packet, streaming);
await writer.write(packet);
} catch (e) {
if (!config.tolerant || packetParser.supportsStreaming(parsed.tag)) {
if (!config.tolerant || supportsStreaming(parsed.tag)) {
// The packets that support streaming are the ones that contain
// message data. Those are also the ones we want to be more strict
// about and throw on parse errors for.
@ -71,7 +75,7 @@ class PacketList extends Array {
} else {
this.stream = null;
}
if (done || packetParser.supportsStreaming(value.tag)) {
if (done || supportsStreaming(value.tag)) {
break;
}
}
@ -88,31 +92,31 @@ class PacketList extends Array {
for (let i = 0; i < this.length; i++) {
const packetbytes = this[i].write();
if (util.isStream(packetbytes) && packetParser.supportsStreaming(this[i].tag)) {
if (util.isStream(packetbytes) && supportsStreaming(this[i].tag)) {
let buffer = [];
let bufferLength = 0;
const minLength = 512;
arr.push(packetParser.writeTag(this[i].tag));
arr.push(writeTag(this[i].tag));
arr.push(stream.transform(packetbytes, value => {
buffer.push(value);
bufferLength += value.length;
if (bufferLength >= minLength) {
const powerOf2 = Math.min(Math.log(bufferLength) / Math.LN2 | 0, 30);
const chunkSize = 2 ** powerOf2;
const bufferConcat = util.concat([packetParser.writePartialLength(powerOf2)].concat(buffer));
const bufferConcat = util.concat([writePartialLength(powerOf2)].concat(buffer));
buffer = [bufferConcat.subarray(1 + chunkSize)];
bufferLength = buffer[0].length;
return bufferConcat.subarray(0, 1 + chunkSize);
}
}, () => util.concat([packetParser.writeSimpleLength(bufferLength)].concat(buffer))));
}, () => util.concat([writeSimpleLength(bufferLength)].concat(buffer))));
} else {
if (util.isStream(packetbytes)) {
let length = 0;
arr.push(stream.transform(stream.clone(packetbytes), value => {
length += value.length;
}, () => packetParser.writeHeader(this[i].tag, length)));
}, () => writeHeader(this[i].tag, length)));
} else {
arr.push(packetParser.writeHeader(this[i].tag, packetbytes.length));
arr.push(writeHeader(this[i].tag, packetbytes.length));
}
arr.push(packetbytes);
}

View File

@ -26,7 +26,7 @@
*/
import stream from 'web-stream-tools';
import packet from './packet';
import { readSimpleLength, writeSimpleLength } from './packet';
import type_keyid from '../type/keyid.js';
import type_mpi from '../type/mpi.js';
import crypto from '../crypto';
@ -319,7 +319,7 @@ class SignaturePacket {
arr.push(write_sub_packet(sub.issuerFingerprint, bytes));
}
this.unhashedSubpackets.forEach(data => {
arr.push(packet.writeSimpleLength(data.length));
arr.push(writeSimpleLength(data.length));
arr.push(data);
});
@ -535,7 +535,7 @@ class SignaturePacket {
// subpacket data set (zero or more subpackets)
while (i < 2 + subpacket_length) {
const len = packet.readSimpleLength(bytes.subarray(i, bytes.length));
const len = readSimpleLength(bytes.subarray(i, bytes.length));
i += len.offset;
this.read_sub_packet(bytes.subarray(i, i + len.len), trusted);
@ -767,7 +767,7 @@ class SignaturePacket {
*/
function write_sub_packet(type, data) {
const arr = [];
arr.push(packet.writeSimpleLength(data.length + 1));
arr.push(writeSimpleLength(data.length + 1));
arr.push(new Uint8Array([type]));
arr.push(data);
return util.concat(arr);

View File

@ -21,7 +21,7 @@
* @requires util
*/
import packet from './packet';
import { readSimpleLength, writeSimpleLength } from './packet';
import enums from '../enums';
import util from '../util';
@ -56,7 +56,7 @@ class UserAttributePacket {
read(bytes) {
let i = 0;
while (i < bytes.length) {
const len = packet.readSimpleLength(bytes.subarray(i, bytes.length));
const len = readSimpleLength(bytes.subarray(i, bytes.length));
i += len.offset;
this.attributes.push(util.uint8ArrayToStr(bytes.subarray(i, i + len.len)));
@ -71,7 +71,7 @@ class UserAttributePacket {
write() {
const arr = [];
for (let i = 0; i < this.attributes.length; i++) {
arr.push(packet.writeSimpleLength(this.attributes[i].length));
arr.push(writeSimpleLength(this.attributes[i].length));
arr.push(util.strToUint8Array(this.attributes[i]));
}
return util.concatUint8Array(arr);

View File

@ -22,7 +22,7 @@
* @module signature
*/
import armor from './encoding/armor';
import { armor, unarmor } from './encoding/armor';
import { PacketList, SignaturePacket } from './packet';
import enums from './enums';
@ -50,7 +50,7 @@ export class Signature {
* @returns {ReadableStream<String>} ASCII armor
*/
armor() {
return armor.encode(enums.armor.signature, this.write());
return armor(enums.armor.signature, this.write());
}
}
@ -62,7 +62,7 @@ export class Signature {
* @static
*/
export async function readArmoredSignature(armoredText) {
const input = await armor.decode(armoredText);
const input = await unarmor(armoredText);
return readSignature(input.data);
}

View File

@ -30,7 +30,7 @@ import emailAddresses from 'email-addresses';
import stream from 'web-stream-tools';
import config from './config';
import util from './util'; // re-import module to access util functions
import b64 from './encoding/base64';
import * as b64 from './encoding/base64';
import { getBigInteger } from './biginteger';
export default {

View File

@ -186,17 +186,17 @@ module.exports = () => describe('Elliptic Curve Cryptography @lightweight', func
]);
}
});
it('Invalid point', function () {
it('Invalid point', async function () {
if (!openpgp.config.useIndutnyElliptic && !openpgp.util.getNodeCrypto()) {
this.skip();
}
if (openpgp.util.getNodeCrypto()) {
expect(verify_signature(
await expect(verify_signature(
'secp256k1', 8, [], [], [], secp256k1_invalid_point
)).to.eventually.be.false;
}
if (openpgp.config.useIndutnyElliptic) {
expect(verify_signature_elliptic(
await expect(verify_signature_elliptic(
'secp256k1', 8, [], [], [], secp256k1_invalid_point
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
}

View File

@ -377,8 +377,8 @@ NJCB6+LWtabSoVIjNVgKwyKqyTLaESNwC2ogZwkdE8qPGiDFEHo4Gg9zuRof
-----END PGP PUBLIC KEY BLOCK-----
`;
const { type, data } = await openpgp.armor.decode(pubKey);
const armor = await openpgp.stream.readToEnd(openpgp.armor.encode(type, data));
const { type, data } = await openpgp.unarmor(pubKey);
const armor = await openpgp.stream.readToEnd(openpgp.armor(type, data));
expect(
armor
.replace(/^(Version|Comment): .*$\r\n/mg, '')

View File

@ -2584,35 +2584,9 @@ function versionSpecificTests() {
}
module.exports = () => describe('Key', function() {
async function deepCopyKeyParams(params) {
const paramsCopy = {};
Object.keys(params).forEach(name => {
const param = params[name];
const copy = new Uint8Array(param.length);
copy.set(param);
paramsCopy[name] = copy;
});
return paramsCopy;
}
let rsaGenStub;
let v5KeysVal;
let aeadProtectVal;
const rsaGenValue = {
512: openpgp.crypto.publicKey.rsa.generate(512, 65537),
1024: openpgp.crypto.publicKey.rsa.generate(1024, 65537),
2048: openpgp.crypto.publicKey.rsa.generate(2048, 65537)
};
beforeEach(function() {
// We fake the generation function to speed up the tests
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
rsaGenStub.callsFake(async N => deepCopyKeyParams(await rsaGenValue[N]));
});
afterEach(function() {
rsaGenStub.restore();
});
tryTests('V4', versionSpecificTests, {
if: !openpgp.config.ci,
@ -2706,7 +2680,7 @@ module.exports = () => describe('Key', function() {
const packetlist = new openpgp.PacketList();
await packetlist.read((await openpgp.armor.decode(pub_sig_test)).data, openpgp);
await packetlist.read((await openpgp.unarmor(pub_sig_test)).data, openpgp);
const subkeys = pubKey.getSubkeys();
expect(subkeys).to.exist;
@ -3132,10 +3106,10 @@ module.exports = () => describe('Key', function() {
const revKey = await openpgp.readArmoredKey(revoked_key_arm4);
const revocationCertificate = await revKey.getRevocationCertificate();
const input = await openpgp.armor.decode(revocation_certificate_arm4);
const input = await openpgp.unarmor(revocation_certificate_arm4);
const packetlist = new openpgp.PacketList();
await packetlist.read(input.data, { SignaturePacket: openpgp.SignaturePacket });
const armored = openpgp.armor.encode(openpgp.enums.armor.publicKey, packetlist.write());
const armored = openpgp.armor(openpgp.enums.armor.publicKey, packetlist.write());
expect(revocationCertificate.replace(/^Comment: .*$\r\n/mg, '')).to.equal(armored.replace(/^Comment: .*$\r\n/mg, ''));
});

View File

@ -549,29 +549,6 @@ function withCompression(tests) {
}
module.exports = () => describe('OpenPGP.js public api tests', function() {
async function deepCopyKeyParams(params) {
const paramsCopy = {};
Object.keys(params).forEach(name => {
const param = params[name];
const copy = new Uint8Array(param.length);
copy.set(param);
paramsCopy[name] = copy;
});
return paramsCopy;
}
let rsaGenStub;
const rsaGenValue = openpgp.crypto.publicKey.rsa.generate(openpgp.util.getWebCryptoAll() ? 2048 : 512, 65537);
beforeEach(function() {
// We fake the generation function to speed up the tests
rsaGenStub = stub(openpgp.crypto.publicKey.rsa, 'generate');
rsaGenStub.returns(async () => deepCopyKeyParams(await rsaGenValue()));
});
afterEach(function() {
rsaGenStub.restore();
});
describe('generateKey - validate user ids', function() {
it('should fail for invalid user name', async function() {

View File

@ -243,6 +243,9 @@ module.exports = () => describe("Packet", function() {
it('Sym. encrypted AEAD protected packet test vector (AEAD)', async function() {
// From https://gitlab.com/openpgp-wg/rfc4880bis/commit/00b20923e6233fb6ff1666ecd5acfefceb32907d
const nodeCrypto = openpgp.util.getNodeCrypto();
if (!nodeCrypto) return;
let packetBytes = openpgp.util.hexToUint8Array(`
d4 4a 01 07 01 0e b7 32 37 9f 73 c4 92 8d e2 5f
ac fe 65 17 ec 10 5d c1 1a 81 dc 0c b8 a2 f6 f3
@ -271,8 +274,8 @@ module.exports = () => describe("Packet", function() {
const msg2 = new openpgp.PacketList();
let randomBytesStub = stub(openpgp.crypto.random, 'getRandomBytes');
randomBytesStub.returns(resolves(iv));
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
randomBytesStub.returns(iv);
try {
await enc.encrypt(algo, key);
@ -298,7 +301,7 @@ module.exports = () => describe("Packet", function() {
'=VZ0/\n' +
'-----END PGP MESSAGE-----';
const msgbytes = (await openpgp.armor.decode(msg)).data;
const msgbytes = (await openpgp.unarmor(msg)).data;
const parsed = new openpgp.PacketList();
await parsed.read(msgbytes, openpgp);
@ -365,7 +368,7 @@ module.exports = () => describe("Packet", function() {
'-----END PGP PRIVATE KEY BLOCK-----';
let key = new openpgp.PacketList();
await key.read((await openpgp.armor.decode(armored_key)).data, openpgp);
await key.read((await openpgp.unarmor(armored_key)).data, openpgp);
key = key[0];
const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
@ -432,11 +435,11 @@ module.exports = () => describe("Packet", function() {
'-----END PGP MESSAGE-----';
let key = new openpgp.PacketList();
await key.read((await openpgp.armor.decode(armored_key)).data, openpgp);
await key.read((await openpgp.unarmor(armored_key)).data, openpgp);
key = key[3];
const msg = new openpgp.PacketList();
await msg.read((await openpgp.armor.decode(armored_msg)).data, openpgp);
await msg.read((await openpgp.unarmor(armored_msg)).data, openpgp);
return msg[0].decrypt(key).then(async () => {
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
@ -521,6 +524,9 @@ module.exports = () => describe("Packet", function() {
it('Sym. encrypted session key reading/writing test vector (EAX, AEAD)', async function() {
// From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-eax-encryption-and-decryption
const nodeCrypto = openpgp.util.getNodeCrypto();
if (!nodeCrypto) return;
let aeadProtectVal = openpgp.config.aeadProtect;
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
let s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte;
@ -533,11 +539,11 @@ module.exports = () => describe("Packet", function() {
let sessionIV = openpgp.util.hexToUint8Array(`bc 66 9e 34 e5 00 dc ae dc 5b 32 aa 2d ab 02 35`.replace(/\s+/g, ''));
let dataIV = openpgp.util.hexToUint8Array(`b7 32 37 9f 73 c4 92 8d e2 5f ac fe 65 17 ec 10`.replace(/\s+/g, ''));
let randomBytesStub = stub(openpgp.crypto.random, 'getRandomBytes');
randomBytesStub.onCall(0).returns(resolves(salt));
randomBytesStub.onCall(1).returns(resolves(sessionKey));
randomBytesStub.onCall(2).returns(resolves(sessionIV));
randomBytesStub.onCall(3).returns(resolves(dataIV));
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
randomBytesStub.onCall(0).returns(salt);
randomBytesStub.onCall(1).returns(sessionKey);
randomBytesStub.onCall(2).returns(sessionIV);
randomBytesStub.onCall(3).returns(dataIV);
let packetBytes = openpgp.util.hexToUint8Array(`
c3 3e 05 07 01 03 08 cd 5a 9f 70 fb e0 bc 65 90
@ -596,6 +602,9 @@ module.exports = () => describe("Packet", function() {
it('Sym. encrypted session key reading/writing test vector (AEAD, OCB)', async function() {
// From https://gitlab.com/openpgp-wg/rfc4880bis/blob/00b20923/back.mkd#sample-aead-ocb-encryption-and-decryption
const nodeCrypto = openpgp.util.getNodeCrypto();
if (!nodeCrypto) return;
let aeadProtectVal = openpgp.config.aeadProtect;
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
let s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte;
@ -608,11 +617,11 @@ module.exports = () => describe("Packet", function() {
let sessionIV = openpgp.util.hexToUint8Array(`99 e3 26 e5 40 0a 90 93 6c ef b4 e8 eb a0 8c`.replace(/\s+/g, ''));
let dataIV = openpgp.util.hexToUint8Array(`5e d2 bc 1e 47 0a be 8f 1d 64 4c 7a 6c 8a 56`.replace(/\s+/g, ''));
let randomBytesStub = stub(openpgp.crypto.random, 'getRandomBytes');
randomBytesStub.onCall(0).returns(resolves(salt));
randomBytesStub.onCall(1).returns(resolves(sessionKey));
randomBytesStub.onCall(2).returns(resolves(sessionIV));
randomBytesStub.onCall(3).returns(resolves(dataIV));
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
randomBytesStub.onCall(0).returns(salt);
randomBytesStub.onCall(1).returns(sessionKey);
randomBytesStub.onCall(2).returns(sessionIV);
randomBytesStub.onCall(3).returns(dataIV);
let packetBytes = openpgp.util.hexToUint8Array(`
c3 3d 05 07 02 03 08 9f 0b 7d a3 e5 ea 64 77 90
@ -683,12 +692,12 @@ module.exports = () => describe("Packet", function() {
'-----END PGP MESSAGE-----';
let key = new openpgp.PacketList();
await key.read((await openpgp.armor.decode(armored_key)).data, openpgp);
await key.read((await openpgp.unarmor(armored_key)).data, openpgp);
key = key[3];
await key.decrypt('test');
const msg = new openpgp.PacketList();
await msg.read((await openpgp.armor.decode(armored_msg)).data, openpgp);
await msg.read((await openpgp.unarmor(armored_msg)).data, openpgp);
return msg[0].decrypt(key).then(async () => {
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
@ -701,7 +710,7 @@ module.exports = () => describe("Packet", function() {
it('Secret key reading with signature verification.', async function() {
const key = new openpgp.PacketList();
await key.read((await openpgp.armor.decode(armored_key)).data, openpgp);
await key.read((await openpgp.unarmor(armored_key)).data, openpgp);
return Promise.all([
expect(key[2].verify(key[0],
openpgp.enums.signature.certGeneric,
@ -736,11 +745,11 @@ module.exports = () => describe("Packet", function() {
'-----END PGP MESSAGE-----';
const key = new openpgp.PacketList();
await key.read((await openpgp.armor.decode(armored_key)).data, openpgp);
await key.read((await openpgp.unarmor(armored_key)).data, openpgp);
await key[3].decrypt('test');
const msg = new openpgp.PacketList();
await msg.read((await openpgp.armor.decode(armored_msg)).data, openpgp);
await msg.read((await openpgp.unarmor(armored_msg)).data, openpgp);
return msg[0].decrypt(key[3]).then(async () => {
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);