Don't export default objects / namespaces
Import individual functions, instead.
This commit is contained in:
parent
f276e1ef51
commit
b3e08fdc26
|
@ -24,7 +24,7 @@
|
||||||
* @module cleartext
|
* @module cleartext
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import armor from './encoding/armor';
|
import { armor, unarmor } from './encoding/armor';
|
||||||
import enums from './enums';
|
import enums from './enums';
|
||||||
import util from './util';
|
import util from './util';
|
||||||
import { PacketList, LiteralDataPacket, SignaturePacket } from './packet';
|
import { PacketList, LiteralDataPacket, SignaturePacket } from './packet';
|
||||||
|
@ -140,7 +140,7 @@ export class CleartextMessage {
|
||||||
text: this.text,
|
text: this.text,
|
||||||
data: this.signature.packets.write()
|
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
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function readArmoredCleartextMessage(armoredText) {
|
export async function readArmoredCleartextMessage(armoredText) {
|
||||||
const input = await armor.decode(armoredText);
|
const input = await unarmor(armoredText);
|
||||||
if (input.type !== enums.armor.signed) {
|
if (input.type !== enums.armor.signed) {
|
||||||
throw new Error('No cleartext signed message.');
|
throw new Error('No cleartext signed message.');
|
||||||
}
|
}
|
||||||
|
|
|
@ -23,10 +23,17 @@
|
||||||
* @module crypto/aes_kw
|
* @module crypto/aes_kw
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cipher from './cipher';
|
import * as cipher from './cipher';
|
||||||
import util from '../util';
|
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 aes = new cipher["aes" + (key.length * 8)](key);
|
||||||
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
||||||
const P = unpack(data);
|
const P = unpack(data);
|
||||||
|
@ -58,7 +65,15 @@ function wrap(key, data) {
|
||||||
return pack(A, R);
|
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 aes = new cipher["aes" + (key.length * 8)](key);
|
||||||
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
const IV = new Uint32Array([0xA6A6A6A6, 0xA6A6A6A6]);
|
||||||
const C = unpack(data);
|
const C = unpack(data);
|
||||||
|
@ -131,23 +146,3 @@ function pack() {
|
||||||
}
|
}
|
||||||
return new Uint8Array(buffer);
|
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
|
|
||||||
};
|
|
||||||
|
|
|
@ -27,7 +27,7 @@
|
||||||
import { AES_CFB } from 'asmcrypto.js/dist_es8/aes/cfb';
|
import { AES_CFB } from 'asmcrypto.js/dist_es8/aes/cfb';
|
||||||
|
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import cipher from './cipher';
|
import * as cipher from './cipher';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
|
@ -38,7 +38,6 @@ const Buffer = util.getNodeBuffer();
|
||||||
const knownAlgos = nodeCrypto ? nodeCrypto.getCiphers() : [];
|
const knownAlgos = nodeCrypto ? nodeCrypto.getCiphers() : [];
|
||||||
const nodeAlgos = {
|
const nodeAlgos = {
|
||||||
idea: knownAlgos.includes('idea-cfb') ? 'idea-cfb' : undefined, /* Unused, not implemented */
|
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,
|
tripledes: knownAlgos.includes('des-ede3-cfb') ? 'des-ede3-cfb' : undefined,
|
||||||
cast5: knownAlgos.includes('cast5-cfb') ? 'cast5-cfb' : undefined,
|
cast5: knownAlgos.includes('cast5-cfb') ? 'cast5-cfb' : undefined,
|
||||||
blowfish: knownAlgos.includes('bf-cfb') ? 'bf-cfb' : undefined,
|
blowfish: knownAlgos.includes('bf-cfb') ? 'bf-cfb' : undefined,
|
||||||
|
@ -48,73 +47,71 @@ const nodeAlgos = {
|
||||||
/* twofish is not implemented in OpenSSL */
|
/* twofish is not implemented in OpenSSL */
|
||||||
};
|
};
|
||||||
|
|
||||||
export default {
|
export async function encrypt(algo, key, plaintext, iv) {
|
||||||
encrypt: function(algo, key, plaintext, iv) {
|
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
|
||||||
if (util.getNodeCrypto() && nodeAlgos[algo]) { // Node crypto library.
|
return nodeEncrypt(algo, key, plaintext, iv);
|
||||||
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);
|
|
||||||
}
|
}
|
||||||
};
|
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) {
|
function aesEncrypt(algo, key, pt, iv) {
|
||||||
if (
|
if (
|
||||||
|
|
|
@ -432,7 +432,7 @@ function des_removePadding(message, padding) {
|
||||||
|
|
||||||
// added by Recurity Labs
|
// added by Recurity Labs
|
||||||
|
|
||||||
function TripleDES(key) {
|
export function TripleDES(key) {
|
||||||
this.key = [];
|
this.key = [];
|
||||||
|
|
||||||
for (let i = 0; i < 3; i++) {
|
for (let i = 0; i < 3; i++) {
|
||||||
|
@ -459,7 +459,7 @@ TripleDES.blockSize = TripleDES.prototype.blockSize = 8;
|
||||||
|
|
||||||
// This is "original" DES
|
// This is "original" DES
|
||||||
|
|
||||||
function DES(key) {
|
export function DES(key) {
|
||||||
this.key = key;
|
this.key = key;
|
||||||
|
|
||||||
this.encrypt = function(block, padding) {
|
this.encrypt = function(block, padding) {
|
||||||
|
@ -472,5 +472,3 @@ function DES(key) {
|
||||||
return des(keys, block, false, 0, null, padding);
|
return des(keys, block, false, 0, null, padding);
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { DES, TripleDES };
|
|
||||||
|
|
|
@ -9,83 +9,80 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import aes from './aes';
|
import aes from './aes';
|
||||||
import des from './des.js';
|
import { DES, TripleDES } from './des.js';
|
||||||
import cast5 from './cast5';
|
import Cast5 from './cast5';
|
||||||
import twofish from './twofish';
|
import TF from './twofish';
|
||||||
import blowfish from './blowfish';
|
import BF from './blowfish';
|
||||||
|
|
||||||
export default {
|
/**
|
||||||
/**
|
* AES-128 encryption and decryption (ID 7)
|
||||||
* AES-128 encryption and decryption (ID 7)
|
* @function
|
||||||
* @function
|
* @param {String} key 128-bit key
|
||||||
* @param {String} key 128-bit key
|
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
||||||
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
||||||
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
* @returns {Object}
|
||||||
* @returns {Object}
|
* @requires asmcrypto.js
|
||||||
* @requires asmcrypto.js
|
*/
|
||||||
*/
|
export const aes128 = aes(128);
|
||||||
aes128: aes(128),
|
/**
|
||||||
/**
|
* AES-128 Block Cipher (ID 8)
|
||||||
* AES-128 Block Cipher (ID 8)
|
* @function
|
||||||
* @function
|
* @param {String} key 192-bit key
|
||||||
* @param {String} key 192-bit key
|
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
||||||
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
||||||
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
* @returns {Object}
|
||||||
* @returns {Object}
|
* @requires asmcrypto.js
|
||||||
* @requires asmcrypto.js
|
*/
|
||||||
*/
|
export const aes192 = aes(192);
|
||||||
aes192: aes(192),
|
/**
|
||||||
/**
|
* AES-128 Block Cipher (ID 9)
|
||||||
* AES-128 Block Cipher (ID 9)
|
* @function
|
||||||
* @function
|
* @param {String} key 256-bit key
|
||||||
* @param {String} key 256-bit key
|
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
||||||
* @see {@link https://github.com/asmcrypto/asmcrypto.js|asmCrypto}
|
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
||||||
* @see {@link https://csrc.nist.gov/publications/fips/fips197/fips-197.pdf|NIST FIPS-197}
|
* @returns {Object}
|
||||||
* @returns {Object}
|
* @requires asmcrypto.js
|
||||||
* @requires asmcrypto.js
|
*/
|
||||||
*/
|
export const aes256 = aes(256);
|
||||||
aes256: aes(256),
|
// Not in OpenPGP specifications
|
||||||
// Not in OpenPGP specifications
|
export const des = DES;
|
||||||
des: des.DES,
|
/**
|
||||||
/**
|
* Triple DES Block Cipher (ID 2)
|
||||||
* Triple DES Block Cipher (ID 2)
|
* @function
|
||||||
* @function
|
* @param {String} key 192-bit key
|
||||||
* @param {String} key 192-bit key
|
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
|
||||||
* @see {@link https://nvlpubs.nist.gov/nistpubs/SpecialPublications/NIST.SP.800-67r2.pdf|NIST SP 800-67}
|
* @returns {Object}
|
||||||
* @returns {Object}
|
*/
|
||||||
*/
|
export const tripledes = TripleDES;
|
||||||
tripledes: des.TripleDES,
|
/**
|
||||||
'3des': des.TripleDES,
|
* CAST-128 Block Cipher (ID 3)
|
||||||
/**
|
* @function
|
||||||
* CAST-128 Block Cipher (ID 3)
|
* @param {String} key 128-bit key
|
||||||
* @function
|
* @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
|
||||||
* @param {String} key 128-bit key
|
* @returns {Object}
|
||||||
* @see {@link https://tools.ietf.org/html/rfc2144|The CAST-128 Encryption Algorithm}
|
*/
|
||||||
* @returns {Object}
|
export const cast5 = Cast5;
|
||||||
*/
|
/**
|
||||||
cast5: cast5,
|
* Twofish Block Cipher (ID 10)
|
||||||
/**
|
* @function
|
||||||
* Twofish Block Cipher (ID 10)
|
* @param {String} key 256-bit key
|
||||||
* @function
|
* @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
|
||||||
* @param {String} key 256-bit key
|
* @returns {Object}
|
||||||
* @see {@link https://tools.ietf.org/html/rfc4880#ref-TWOFISH|TWOFISH}
|
*/
|
||||||
* @returns {Object}
|
export const twofish = TF;
|
||||||
*/
|
/**
|
||||||
twofish: twofish,
|
* Blowfish Block Cipher (ID 4)
|
||||||
/**
|
* @function
|
||||||
* Blowfish Block Cipher (ID 4)
|
* @param {String} key 128-bit key
|
||||||
* @function
|
* @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
|
||||||
* @param {String} key 128-bit key
|
* @returns {Object}
|
||||||
* @see {@link https://tools.ietf.org/html/rfc4880#ref-BLOWFISH|BLOWFISH}
|
*/
|
||||||
* @returns {Object}
|
export const blowfish = BF;
|
||||||
*/
|
/**
|
||||||
blowfish: blowfish,
|
* Not implemented
|
||||||
/**
|
* @function
|
||||||
* Not implemented
|
* @throws {Error}
|
||||||
* @function
|
*/
|
||||||
* @throws {Error}
|
export const idea = function() {
|
||||||
*/
|
throw new Error('IDEA symmetric-key algorithm not implemented');
|
||||||
idea: function() {
|
|
||||||
throw new Error('IDEA symmetric-key algorithm not implemented');
|
|
||||||
}
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -33,17 +33,17 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import publicKey from './public_key';
|
import publicKey from './public_key';
|
||||||
import cipher from './cipher';
|
import * as cipher from './cipher';
|
||||||
import random from './random';
|
import { getRandomBytes } from './random';
|
||||||
import type_ecdh_symkey from '../type/ecdh_symkey';
|
import type_ecdh_symkey from '../type/ecdh_symkey';
|
||||||
import KDFParams from '../type/kdf_params';
|
import KDFParams from '../type/kdf_params';
|
||||||
import type_mpi from '../type/mpi';
|
import type_mpi from '../type/mpi';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
import OID from '../type/oid';
|
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) {
|
return types.map(function(type, i) {
|
||||||
if (data && data[i]) {
|
if (data && data[i]) {
|
||||||
return new type(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.
|
||||||
* 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.
|
||||||
* 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 {module:enums.publicKey} algo Public key algorithm
|
* @param {Object} pubParams Algorithm-specific public key parameters
|
||||||
* @param {Object} pubParams Algorithm-specific public key parameters
|
* @param {Uint8Array} data Data to be encrypted
|
||||||
* @param {Uint8Array} data Data to be encrypted
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
* @param {Uint8Array} fingerprint Recipient fingerprint
|
* @returns {Array<module:type/mpi|
|
||||||
* @returns {Array<module:type/mpi|
|
* module:type/ecdh_symkey>} encrypted session key parameters
|
||||||
* module:type/ecdh_symkey>} encrypted session key parameters
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function publicKeyEncrypt(algo, publicParams, data, fingerprint) {
|
||||||
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
|
const types = getEncSessionKeyParamTypes(algo);
|
||||||
const types = this.getEncSessionKeyParamTypes(algo);
|
switch (algo) {
|
||||||
switch (algo) {
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaEncryptSign: {
|
||||||
case enums.publicKey.rsaEncryptSign: {
|
const { n, e } = publicParams;
|
||||||
const { n, e } = publicParams;
|
const res = await publicKey.rsa.encrypt(data, n, e);
|
||||||
const res = await publicKey.rsa.encrypt(data, n, e);
|
return constructParams(types, [res]);
|
||||||
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 [];
|
|
||||||
}
|
}
|
||||||
},
|
case enums.publicKey.elgamal: {
|
||||||
|
const { p, g, y } = publicParams;
|
||||||
/**
|
const res = await publicKey.elgamal.encrypt(data, p, g, y);
|
||||||
* Decrypts data using specified algorithm and private key parameters.
|
return constructParams(types, [res.c1, res.c2]);
|
||||||
* 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.ecdh: {
|
||||||
|
const { oid, Q, kdfParams } = publicParams;
|
||||||
/**
|
const { publicKey: V, wrappedKey: C } = await publicKey.elliptic.ecdh.encrypt(
|
||||||
* Parse public key material in binary form to get the key parameters
|
oid, kdfParams, data, Q, fingerprint);
|
||||||
* @param {module:enums.publicKey} algo The key algorithm
|
return constructParams(types, [V, C]);
|
||||||
* @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.');
|
|
||||||
}
|
}
|
||||||
},
|
default:
|
||||||
|
return [];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Parse private key material in binary form to get the key parameters
|
* Decrypts data using specified algorithm and private key parameters.
|
||||||
* @param {module:enums.publicKey} algo The key algorithm
|
* See {@link https://tools.ietf.org/html/rfc4880#section-5.5.3|RFC 4880 5.5.3}
|
||||||
* @param {Uint8Array} bytes The key material to parse
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {Object} publicParams (ECC only) public params, needed to format some private params
|
* @param {Object} publicKeyParams Algorithm-specific public key parameters
|
||||||
* @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
|
* @param {Object} privateKeyParams Algorithm-specific private key parameters
|
||||||
*/
|
* @param {Array<module:type/mpi|
|
||||||
parsePrivateKeyParams: function(algo, bytes, publicParams) {
|
module:type/ecdh_symkey>}
|
||||||
let read = 0;
|
data_params encrypted session key parameters
|
||||||
switch (algo) {
|
* @param {Uint8Array} fingerprint Recipient fingerprint
|
||||||
case enums.publicKey.rsaEncrypt:
|
* @returns {Uint8Array} decrypted data
|
||||||
case enums.publicKey.rsaEncryptSign:
|
* @async
|
||||||
case enums.publicKey.rsaSign: {
|
*/
|
||||||
const d = util.readMPI(bytes.subarray(read)); read += d.length + 2;
|
export async function publicKeyDecrypt(algo, publicKeyParams, privateKeyParams, data_params, fingerprint) {
|
||||||
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
switch (algo) {
|
||||||
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
case enums.publicKey.rsaEncryptSign:
|
||||||
const u = util.readMPI(bytes.subarray(read)); read += u.length + 2;
|
case enums.publicKey.rsaEncrypt: {
|
||||||
return { read, privateParams: { d, p, q, u } };
|
const c = data_params[0].toUint8Array();
|
||||||
}
|
const { n, e } = publicKeyParams;
|
||||||
case enums.publicKey.dsa:
|
const { d, p, q, u } = privateKeyParams;
|
||||||
case enums.publicKey.elgamal: {
|
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
|
||||||
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.');
|
|
||||||
}
|
}
|
||||||
},
|
case enums.publicKey.elgamal: {
|
||||||
|
const c1 = data_params[0].toUint8Array();
|
||||||
/** Returns the types comprising the encrypted session key of an algorithm
|
const c2 = data_params[1].toUint8Array();
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
const p = publicKeyParams.p;
|
||||||
* @returns {Array<Object>} The array of types
|
const x = privateKeyParams.x;
|
||||||
*/
|
return publicKey.elgamal.decrypt(c1, c2, p, x);
|
||||||
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.ecdh: {
|
||||||
|
const { oid, Q, kdfParams } = publicKeyParams;
|
||||||
/**
|
const { d } = privateKeyParams;
|
||||||
* Convert params to MPI and serializes them in the proper order
|
const V = data_params[0].toUint8Array();
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
const C = data_params[1].data;
|
||||||
* @param {Object} params The key parameters indexed by name
|
return publicKey.elliptic.ecdh.decrypt(
|
||||||
* @returns {Uint8Array} The array containing the MPIs
|
oid, kdfParams, V, C, Q, d, fingerprint);
|
||||||
*/
|
|
||||||
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.');
|
|
||||||
}
|
}
|
||||||
},
|
default:
|
||||||
|
throw new Error('Invalid public key encryption algorithm.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate algorithm-specific key parameters
|
* Parse public key material in binary form to get the key parameters
|
||||||
* @param {module:enums.publicKey} algo The public key algorithm
|
* @param {module:enums.publicKey} algo The key algorithm
|
||||||
* @param {Object} publicParams Algorithm-specific public key parameters
|
* @param {Uint8Array} bytes The key material to parse
|
||||||
* @param {Object} privateParams Algorithm-specific private key parameters
|
* @returns {Object} key parameters referenced by name
|
||||||
* @returns {Promise<Boolean>} whether the parameters are valid
|
* @returns { read: Number, publicParams: Object } number of read bytes plus key parameters referenced by name
|
||||||
* @async
|
*/
|
||||||
*/
|
export function parsePublicKeyParams(algo, bytes) {
|
||||||
validateParams: async function(algo, publicParams, privateParams) {
|
let read = 0;
|
||||||
if (!publicParams || !privateParams) {
|
switch (algo) {
|
||||||
throw new Error('Missing key parameters');
|
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.dsa: {
|
||||||
case enums.publicKey.rsaEncrypt:
|
const p = util.readMPI(bytes.subarray(read)); read += p.length + 2;
|
||||||
case enums.publicKey.rsaEncryptSign:
|
const q = util.readMPI(bytes.subarray(read)); read += q.length + 2;
|
||||||
case enums.publicKey.rsaSign: {
|
const g = util.readMPI(bytes.subarray(read)); read += g.length + 2;
|
||||||
const { n, e } = publicParams;
|
const y = util.readMPI(bytes.subarray(read)); read += y.length + 2;
|
||||||
const { d, p, q, u } = privateParams;
|
return { read, publicParams: { p, q, g, y } };
|
||||||
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.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
|
* Parse private key material in binary form to get the key parameters
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
|
* @param {module:enums.publicKey} algo The key algorithm
|
||||||
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
|
* @param {Uint8Array} bytes The key material to parse
|
||||||
* @returns {Uint8Array} Random bytes with length equal to the block size of the cipher, plus the last two bytes repeated.
|
* @param {Object} publicParams (ECC only) public params, needed to format some private params
|
||||||
* @async
|
* @returns { read: Number, privateParams: Object } number of read bytes plus the key parameters referenced by name
|
||||||
*/
|
*/
|
||||||
getPrefixRandom: async function(algo) {
|
export function parsePrivateKeyParams(algo, bytes, publicParams) {
|
||||||
const prefixrandom = await random.getRandomBytes(cipher[algo].blockSize);
|
let read = 0;
|
||||||
const repeat = new Uint8Array([prefixrandom[prefixrandom.length - 2], prefixrandom[prefixrandom.length - 1]]);
|
switch (algo) {
|
||||||
return util.concat([prefixrandom, repeat]);
|
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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/** Returns the types comprising the encrypted session key of an algorithm
|
||||||
* Generating a session key for the specified symmetric algorithm
|
* @param {module:enums.publicKey} algo The public key algorithm
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2} for algorithms.
|
* @returns {Array<Object>} The array of types
|
||||||
* @param {module:enums.symmetric} algo Symmetric encryption algorithm
|
*/
|
||||||
* @returns {Uint8Array} Random bytes as a string to be used as a key
|
export function getEncSessionKeyParamTypes(algo) {
|
||||||
* @async
|
switch (algo) {
|
||||||
*/
|
// Algorithm-Specific Fields for RSA encrypted session keys:
|
||||||
generateSessionKey: function(algo) {
|
// - MPI of RSA encrypted value m**e mod n.
|
||||||
return random.getRandomBytes(cipher[algo].keySize);
|
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);
|
||||||
|
}
|
||||||
|
|
|
@ -9,19 +9,19 @@
|
||||||
* @module crypto
|
* @module crypto
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import cipher from './cipher';
|
import * as cipher from './cipher';
|
||||||
import hash from './hash';
|
import hash from './hash';
|
||||||
import cfb from './cfb';
|
import * as cfb from './cfb';
|
||||||
import gcm from './gcm';
|
import gcm from './gcm';
|
||||||
import eax from './eax';
|
import eax from './eax';
|
||||||
import ocb from './ocb';
|
import ocb from './ocb';
|
||||||
import publicKey from './public_key';
|
import publicKey from './public_key';
|
||||||
import signature from './signature';
|
import * as signature from './signature';
|
||||||
import random from './random';
|
import * as random from './random';
|
||||||
import pkcs1 from './pkcs1';
|
import * as pkcs1 from './pkcs1';
|
||||||
import pkcs5 from './pkcs5';
|
import * as pkcs5 from './pkcs5';
|
||||||
import crypto from './crypto';
|
import * as crypto from './crypto';
|
||||||
import aes_kw from './aes_kw';
|
import * as aes_kw from './aes_kw';
|
||||||
|
|
||||||
// TODO move cfb and gcm to cipher
|
// TODO move cfb and gcm to cipher
|
||||||
const mod = {
|
const mod = {
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* @module crypto/ocb
|
* @module crypto/ocb
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import ciphers from './cipher';
|
import * as ciphers from './cipher';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
|
|
||||||
|
|
|
@ -25,14 +25,9 @@
|
||||||
* @module crypto/pkcs1
|
* @module crypto/pkcs1
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import random from './random';
|
import { getRandomBytes } from './random';
|
||||||
import hash from './hash';
|
import hash from './hash';
|
||||||
|
|
||||||
/** @namespace */
|
|
||||||
const eme = {};
|
|
||||||
/** @namespace */
|
|
||||||
const emsa = {};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ASN1 object identifiers for hashes
|
* ASN1 object identifiers for hashes
|
||||||
* @see {@link https://tools.ietf.org/html/rfc4880#section-5.2.2}
|
* @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);
|
const result = new Uint8Array(length);
|
||||||
let count = 0;
|
let count = 0;
|
||||||
while (count < length) {
|
while (count < length) {
|
||||||
const randomBytes = await random.getRandomBytes(length - count);
|
const randomBytes = await getRandomBytes(length - count);
|
||||||
for (let i = 0; i < randomBytes.length; i++) {
|
for (let i = 0; i < randomBytes.length; i++) {
|
||||||
if (randomBytes[i] !== 0) {
|
if (randomBytes[i] !== 0) {
|
||||||
result[count++] = randomBytes[i];
|
result[count++] = randomBytes[i];
|
||||||
|
@ -80,7 +75,7 @@ async function getPkcs1Padding(length) {
|
||||||
* @returns {Promise<Uint8Array>} EME-PKCS1 padded message
|
* @returns {Promise<Uint8Array>} EME-PKCS1 padded message
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
eme.encode = async function(message, keyLength) {
|
export async function emeEncode(message, keyLength) {
|
||||||
const mLength = message.length;
|
const mLength = message.length;
|
||||||
// length checking
|
// length checking
|
||||||
if (mLength > keyLength - 11) {
|
if (mLength > keyLength - 11) {
|
||||||
|
@ -98,7 +93,7 @@ eme.encode = async function(message, keyLength) {
|
||||||
// 0x00 bytes
|
// 0x00 bytes
|
||||||
encoded.set(message, keyLength - mLength);
|
encoded.set(message, keyLength - mLength);
|
||||||
return encoded;
|
return encoded;
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Decode a EME-PKCS1-v1_5 padded message
|
* Decode a EME-PKCS1-v1_5 padded message
|
||||||
|
@ -106,7 +101,7 @@ eme.encode = async function(message, keyLength) {
|
||||||
* @param {Uint8Array} encoded encoded message bytes
|
* @param {Uint8Array} encoded encoded message bytes
|
||||||
* @returns {Uint8Array} message
|
* @returns {Uint8Array} message
|
||||||
*/
|
*/
|
||||||
eme.decode = function(encoded) {
|
export function emeDecode(encoded) {
|
||||||
let i = 2;
|
let i = 2;
|
||||||
while (encoded[i] !== 0 && i < encoded.length) {
|
while (encoded[i] !== 0 && i < encoded.length) {
|
||||||
i++;
|
i++;
|
||||||
|
@ -117,7 +112,7 @@ eme.decode = function(encoded) {
|
||||||
return encoded.subarray(i);
|
return encoded.subarray(i);
|
||||||
}
|
}
|
||||||
throw new Error('Decryption error');
|
throw new Error('Decryption error');
|
||||||
};
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a EMSA-PKCS1-v1_5 padded message
|
* 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
|
* @param {Integer} emLen intended length in octets of the encoded message
|
||||||
* @returns {Uint8Array} encoded message
|
* @returns {Uint8Array} encoded message
|
||||||
*/
|
*/
|
||||||
emsa.encode = async function(algo, hashed, emLen) {
|
export async function emsaEncode(algo, hashed, emLen) {
|
||||||
let i;
|
let i;
|
||||||
if (hashed.length !== hash.getHashByteLength(algo)) {
|
if (hashed.length !== hash.getHashByteLength(algo)) {
|
||||||
throw new Error('Invalid hash length');
|
throw new Error('Invalid hash length');
|
||||||
|
@ -155,6 +150,4 @@ emsa.encode = async function(algo, hashed, emLen) {
|
||||||
EM.set(hashPrefix, emLen - tLen);
|
EM.set(hashPrefix, emLen - tLen);
|
||||||
EM.set(hashed, emLen - hashed.length);
|
EM.set(hashed, emLen - hashed.length);
|
||||||
return EM;
|
return EM;
|
||||||
};
|
}
|
||||||
|
|
||||||
export default { eme, emsa };
|
|
||||||
|
|
|
@ -28,7 +28,7 @@ import util from '../util';
|
||||||
* @param {Uint8Array} message message to pad
|
* @param {Uint8Array} message message to pad
|
||||||
* @returns {Uint8Array} padded message
|
* @returns {Uint8Array} padded message
|
||||||
*/
|
*/
|
||||||
function encode(message) {
|
export function encode(message) {
|
||||||
const c = 8 - (message.length % 8);
|
const c = 8 - (message.length % 8);
|
||||||
const padded = new Uint8Array(message.length + c).fill(c);
|
const padded = new Uint8Array(message.length + c).fill(c);
|
||||||
padded.set(message);
|
padded.set(message);
|
||||||
|
@ -40,7 +40,7 @@ function encode(message) {
|
||||||
* @param {Uint8Array} message message to remove padding from
|
* @param {Uint8Array} message message to remove padding from
|
||||||
* @returns {Uint8Array} message without padding
|
* @returns {Uint8Array} message without padding
|
||||||
*/
|
*/
|
||||||
function decode(message) {
|
export function decode(message) {
|
||||||
const len = message.length;
|
const len = message.length;
|
||||||
if (len > 0) {
|
if (len > 0) {
|
||||||
const c = message[len - 1];
|
const c = message[len - 1];
|
||||||
|
@ -54,5 +54,3 @@ function decode(message) {
|
||||||
}
|
}
|
||||||
throw new Error('Invalid padding');
|
throw new Error('Invalid padding');
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { encode, decode };
|
|
||||||
|
|
|
@ -21,9 +21,9 @@
|
||||||
* @requires util
|
* @requires util
|
||||||
* @module crypto/public_key/dsa
|
* @module crypto/public_key/dsa
|
||||||
*/
|
*/
|
||||||
import random from '../random';
|
import { getRandomBigInteger } from '../random';
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import prime from './prime';
|
import { isProbablePrime } from './prime';
|
||||||
|
|
||||||
/*
|
/*
|
||||||
TODO regarding the hash function, read:
|
TODO regarding the hash function, read:
|
||||||
|
@ -31,170 +31,168 @@ import prime from './prime';
|
||||||
https://tools.ietf.org/html/rfc4880#section-14
|
https://tools.ietf.org/html/rfc4880#section-14
|
||||||
*/
|
*/
|
||||||
|
|
||||||
export default {
|
/**
|
||||||
/**
|
* DSA Sign function
|
||||||
* DSA Sign function
|
* @param {Integer} hash_algo
|
||||||
* @param {Integer} hash_algo
|
* @param {Uint8Array} hashed
|
||||||
* @param {Uint8Array} hashed
|
* @param {Uint8Array} g
|
||||||
* @param {Uint8Array} g
|
* @param {Uint8Array} p
|
||||||
* @param {Uint8Array} p
|
* @param {Uint8Array} q
|
||||||
* @param {Uint8Array} q
|
* @param {Uint8Array} x
|
||||||
* @param {Uint8Array} x
|
* @returns {{ r: Uint8Array, s: Uint8Array }}
|
||||||
* @returns {{ r: Uint8Array, s: Uint8Array }}
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function sign(hash_algo, hashed, g, p, q, x) {
|
||||||
sign: async function(hash_algo, hashed, g, p, q, x) {
|
const BigInteger = await util.getBigInteger();
|
||||||
const BigInteger = await util.getBigInteger();
|
const one = new BigInteger(1);
|
||||||
const one = new BigInteger(1);
|
p = new BigInteger(p);
|
||||||
p = new BigInteger(p);
|
q = new BigInteger(q);
|
||||||
q = new BigInteger(q);
|
g = new BigInteger(g);
|
||||||
g = new BigInteger(g);
|
x = new BigInteger(x);
|
||||||
x = new BigInteger(x);
|
|
||||||
|
|
||||||
let k;
|
let k;
|
||||||
let r;
|
let r;
|
||||||
let s;
|
let s;
|
||||||
let t;
|
let t;
|
||||||
g = g.mod(p);
|
g = g.mod(p);
|
||||||
x = x.mod(q);
|
x = x.mod(q);
|
||||||
// If the output size of the chosen hash is larger than the number of
|
// 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
|
// 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
|
// of leftmost bits equal to the number of bits of q. This (possibly
|
||||||
// truncated) hash function result is treated as a number and used
|
// truncated) hash function result is treated as a number and used
|
||||||
// directly in the DSA signature algorithm.
|
// directly in the DSA signature algorithm.
|
||||||
const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q);
|
const h = new BigInteger(hashed.subarray(0, q.byteLength())).mod(q);
|
||||||
// FIPS-186-4, section 4.6:
|
// FIPS-186-4, section 4.6:
|
||||||
// The values of r and s shall be checked to determine if r = 0 or s = 0.
|
// 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
|
// 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
|
// signature shall be recalculated. It is extremely unlikely that r = 0
|
||||||
// or s = 0 if signatures are generated properly.
|
// or s = 0 if signatures are generated properly.
|
||||||
while (true) {
|
while (true) {
|
||||||
// See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
|
// 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]
|
k = await getRandomBigInteger(one, q); // returns in [1, q-1]
|
||||||
r = g.modExp(k, p).imod(q); // (g**k mod p) mod q
|
r = g.modExp(k, p).imod(q); // (g**k mod p) mod q
|
||||||
if (r.isZero()) {
|
if (r.isZero()) {
|
||||||
continue;
|
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;
|
|
||||||
}
|
}
|
||||||
return {
|
const xr = x.mul(r).imod(q);
|
||||||
r: r.toUint8Array('be', q.byteLength()),
|
t = h.add(xr).imod(q); // H(m) + x*r mod q
|
||||||
s: s.toUint8Array('be', q.byteLength())
|
s = k.modInv(q).imul(t).imod(q); // k**-1 * (H(m) + x*r) mod q
|
||||||
};
|
if (s.isZero()) {
|
||||||
},
|
continue;
|
||||||
|
|
||||||
/**
|
|
||||||
* 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 h = new BigInteger(hashed.subarray(0, q.byteLength())).imod(q);
|
break;
|
||||||
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;
|
|
||||||
}
|
}
|
||||||
};
|
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;
|
||||||
|
}
|
||||||
|
|
|
@ -23,125 +23,123 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import random from '../random';
|
import { getRandomBigInteger } from '../random';
|
||||||
import pkcs1 from '../pkcs1';
|
import { emeEncode, emeDecode } from '../pkcs1';
|
||||||
|
|
||||||
export default {
|
/**
|
||||||
/**
|
* ElGamal Encryption function
|
||||||
* ElGamal Encryption function
|
* Note that in OpenPGP, the message needs to be padded with PKCS#1 (same as RSA)
|
||||||
* 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} data to be padded and encrypted
|
* @param {Uint8Array} p
|
||||||
* @param {Uint8Array} p
|
* @param {Uint8Array} g
|
||||||
* @param {Uint8Array} g
|
* @param {Uint8Array} y
|
||||||
* @param {Uint8Array} y
|
* @returns {{ c1: Uint8Array, c2: Uint8Array }}
|
||||||
* @returns {{ c1: Uint8Array, c2: Uint8Array }}
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function encrypt(data, p, g, y) {
|
||||||
encrypt: async function(data, p, g, y) {
|
const BigInteger = await util.getBigInteger();
|
||||||
const BigInteger = await util.getBigInteger();
|
p = new BigInteger(p);
|
||||||
p = new BigInteger(p);
|
g = new BigInteger(g);
|
||||||
g = new BigInteger(g);
|
y = new BigInteger(y);
|
||||||
y = new BigInteger(y);
|
|
||||||
|
|
||||||
const padded = await pkcs1.eme.encode(data, p.byteLength());
|
const padded = await emeEncode(data, p.byteLength());
|
||||||
const m = new BigInteger(padded);
|
const m = new BigInteger(padded);
|
||||||
|
|
||||||
// OpenPGP uses a "special" version of ElGamal where g is generator of the full group Z/pZ*
|
// 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]
|
// 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());
|
const k = await getRandomBigInteger(new BigInteger(1), p.dec());
|
||||||
return {
|
return {
|
||||||
c1: g.modExp(k, p).toUint8Array(),
|
c1: g.modExp(k, p).toUint8Array(),
|
||||||
c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
|
c2: y.modExp(k, p).imul(m).imod(p).toUint8Array()
|
||||||
};
|
};
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* ElGamal Encryption function
|
* ElGamal Encryption function
|
||||||
* @param {Uint8Array} c1
|
* @param {Uint8Array} c1
|
||||||
* @param {Uint8Array} c2
|
* @param {Uint8Array} c2
|
||||||
* @param {Uint8Array} p
|
* @param {Uint8Array} p
|
||||||
* @param {Uint8Array} x
|
* @param {Uint8Array} x
|
||||||
* @returns {Uint8Array} unpadded message
|
* @returns {Uint8Array} unpadded message
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
decrypt: async function(c1, c2, p, x) {
|
export async function decrypt(c1, c2, p, x) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
c1 = new BigInteger(c1);
|
c1 = new BigInteger(c1);
|
||||||
c2 = new BigInteger(c2);
|
c2 = new BigInteger(c2);
|
||||||
p = new BigInteger(p);
|
p = new BigInteger(p);
|
||||||
x = new BigInteger(x);
|
x = new BigInteger(x);
|
||||||
|
|
||||||
const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
|
const padded = c1.modExp(x, p).modInv(p).imul(c2).imod(p);
|
||||||
return pkcs1.eme.decode(padded.toUint8Array('be', p.byteLength()));
|
return emeDecode(padded.toUint8Array('be', p.byteLength()));
|
||||||
},
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate ElGamal parameters
|
* Validate ElGamal parameters
|
||||||
* @param {Uint8Array} p ElGamal prime
|
* @param {Uint8Array} p ElGamal prime
|
||||||
* @param {Uint8Array} g ElGamal group generator
|
* @param {Uint8Array} g ElGamal group generator
|
||||||
* @param {Uint8Array} y ElGamal public key
|
* @param {Uint8Array} y ElGamal public key
|
||||||
* @param {Uint8Array} x ElGamal private exponent
|
* @param {Uint8Array} x ElGamal private exponent
|
||||||
* @returns {Promise<Boolean>} whether params are valid
|
* @returns {Promise<Boolean>} whether params are valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
validateParams: async function (p, g, y, x) {
|
export async function validateParams(p, g, y, x) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
p = new BigInteger(p);
|
p = new BigInteger(p);
|
||||||
g = new BigInteger(g);
|
g = new BigInteger(g);
|
||||||
y = new BigInteger(y);
|
y = new BigInteger(y);
|
||||||
|
|
||||||
const one = new BigInteger(1);
|
const one = new BigInteger(1);
|
||||||
// Check that 1 < g < p
|
// Check that 1 < g < p
|
||||||
if (g.lte(one) || g.gte(p)) {
|
if (g.lte(one) || g.gte(p)) {
|
||||||
return false;
|
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;
|
|
||||||
}
|
}
|
||||||
};
|
|
||||||
|
// 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;
|
||||||
|
}
|
||||||
|
|
|
@ -28,7 +28,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import nacl from 'tweetnacl/nacl-fast-light.js';
|
import nacl from 'tweetnacl/nacl-fast-light.js';
|
||||||
import random from '../../random';
|
import { getRandomBytes } from '../../random';
|
||||||
import enums from '../../../enums';
|
import enums from '../../../enums';
|
||||||
import util from '../../../util';
|
import util from '../../../util';
|
||||||
import OID from '../../../type/oid';
|
import OID from '../../../type/oid';
|
||||||
|
@ -186,7 +186,7 @@ class Curve {
|
||||||
case 'node':
|
case 'node':
|
||||||
return nodeGenKeyPair(this.name);
|
return nodeGenKeyPair(this.name);
|
||||||
case 'curve25519': {
|
case 'curve25519': {
|
||||||
const privateKey = await random.getRandomBytes(32);
|
const privateKey = await getRandomBytes(32);
|
||||||
privateKey[0] = (privateKey[0] & 127) | 64;
|
privateKey[0] = (privateKey[0] & 127) | 64;
|
||||||
privateKey[31] &= 248;
|
privateKey[31] &= 248;
|
||||||
const secretKey = privateKey.slice().reverse();
|
const secretKey = privateKey.slice().reverse();
|
||||||
|
@ -195,7 +195,7 @@ class Curve {
|
||||||
return { publicKey, privateKey };
|
return { publicKey, privateKey };
|
||||||
}
|
}
|
||||||
case 'ed25519': {
|
case 'ed25519': {
|
||||||
const privateKey = await random.getRandomBytes(32);
|
const privateKey = await getRandomBytes(32);
|
||||||
const keyPair = nacl.sign.keyPair.fromSeed(privateKey);
|
const keyPair = nacl.sign.keyPair.fromSeed(privateKey);
|
||||||
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
|
const publicKey = util.concatUint8Array([new Uint8Array([0x40]), keyPair.publicKey]);
|
||||||
return { publicKey, privateKey };
|
return { publicKey, privateKey };
|
||||||
|
@ -203,7 +203,7 @@ class Curve {
|
||||||
}
|
}
|
||||||
const indutnyCurve = await getIndutnyCurve(this.name);
|
const indutnyCurve = await getIndutnyCurve(this.name);
|
||||||
keyPair = await indutnyCurve.genKeyPair({
|
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) };
|
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;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
export default Curve;
|
|
||||||
|
|
||||||
export {
|
export {
|
||||||
curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams
|
Curve, curves, webCurves, nodeCurves, generate, getPreferredHashAlgo, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams
|
||||||
};
|
};
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
|
|
|
@ -30,14 +30,14 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import nacl from 'tweetnacl/nacl-fast-light.js';
|
import nacl from 'tweetnacl/nacl-fast-light.js';
|
||||||
import Curve, { jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams } from './curves';
|
import { Curve, jwkToRawPublic, rawPublicToJwk, privateToJwk, validateStandardParams } from './curves';
|
||||||
import aes_kw from '../../aes_kw';
|
import * as aes_kw from '../../aes_kw';
|
||||||
import cipher from '../../cipher';
|
import * as cipher from '../../cipher';
|
||||||
import random from '../../random';
|
import { getRandomBytes } from '../../random';
|
||||||
import hash from '../../hash';
|
import hash from '../../hash';
|
||||||
import enums from '../../../enums';
|
import enums from '../../../enums';
|
||||||
import util from '../../../util';
|
import util from '../../../util';
|
||||||
import pkcs5 from '../../pkcs5';
|
import * as pkcs5 from '../../pkcs5';
|
||||||
import MPI from '../../../type/mpi';
|
import MPI from '../../../type/mpi';
|
||||||
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
import { keyFromPublic, keyFromPrivate, getIndutnyCurve } from './indutnyKey';
|
||||||
|
|
||||||
|
@ -52,7 +52,7 @@ const nodeCrypto = util.getNodeCrypto();
|
||||||
* @returns {Promise<Boolean>} whether params are valid
|
* @returns {Promise<Boolean>} whether params are valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function validateParams(oid, Q, d) {
|
export async function validateParams(oid, Q, d) {
|
||||||
return validateStandardParams(enums.publicKey.ecdh, 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) {
|
async function genPublicEphemeralKey(curve, Q) {
|
||||||
switch (curve.type) {
|
switch (curve.type) {
|
||||||
case 'curve25519': {
|
case 'curve25519': {
|
||||||
const d = await random.getRandomBytes(32);
|
const d = await getRandomBytes(32);
|
||||||
const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d);
|
const { secretKey, sharedKey } = await genPrivateEphemeralKey(curve, Q, null, d);
|
||||||
let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
let { publicKey } = nacl.box.keyPair.fromSecretKey(secretKey);
|
||||||
publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
|
publicKey = util.concatUint8Array([new Uint8Array([0x40]), publicKey]);
|
||||||
|
@ -134,7 +134,7 @@ async function genPublicEphemeralKey(curve, Q) {
|
||||||
* @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
|
* @returns {Promise<{publicKey: Uint8Array, wrappedKey: Uint8Array}>}
|
||||||
* @async
|
* @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 m = new MPI(pkcs5.encode(data));
|
||||||
|
|
||||||
const curve = new Curve(oid);
|
const curve = new Curve(oid);
|
||||||
|
@ -196,7 +196,7 @@ async function genPrivateEphemeralKey(curve, V, Q, d) {
|
||||||
* @returns {Promise<Uint8Array>} Value derived from session key
|
* @returns {Promise<Uint8Array>} Value derived from session key
|
||||||
* @async
|
* @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 curve = new Curve(oid);
|
||||||
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
|
const { sharedKey } = await genPrivateEphemeralKey(curve, V, Q, d);
|
||||||
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
const param = buildEcdhParam(enums.publicKey.ecdh, oid, kdfParams, fingerprint);
|
||||||
|
@ -387,5 +387,3 @@ async function nodePublicEphemeralKey(curve, Q) {
|
||||||
const publicKey = new Uint8Array(sender.getPublicKey());
|
const publicKey = new Uint8Array(sender.getPublicKey());
|
||||||
return { publicKey, sharedKey };
|
return { publicKey, sharedKey };
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { encrypt, decrypt, genPublicEphemeralKey, genPrivateEphemeralKey, buildEcdhParam, kdf, webPublicEphemeralKey, webPrivateEphemeralKey, ellipticPublicEphemeralKey, ellipticPrivateEphemeralKey, nodePublicEphemeralKey, nodePrivateEphemeralKey, validateParams };
|
|
||||||
|
|
|
@ -26,9 +26,9 @@
|
||||||
|
|
||||||
import enums from '../../../enums';
|
import enums from '../../../enums';
|
||||||
import util from '../../../util';
|
import util from '../../../util';
|
||||||
import random from '../../random';
|
import { getRandomBytes } from '../../random';
|
||||||
import hash from '../../hash';
|
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';
|
import { getIndutnyCurve, keyFromPrivate, keyFromPublic } from './indutnyKey';
|
||||||
|
|
||||||
const webCrypto = util.getWebCrypto();
|
const webCrypto = util.getWebCrypto();
|
||||||
|
@ -46,7 +46,7 @@ const nodeCrypto = util.getNodeCrypto();
|
||||||
* s: Uint8Array}} Signature of the message
|
* s: Uint8Array}} Signature of the message
|
||||||
* @async
|
* @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);
|
const curve = new Curve(oid);
|
||||||
if (message && !util.isStream(message)) {
|
if (message && !util.isStream(message)) {
|
||||||
const keyPair = { publicKey, privateKey };
|
const keyPair = { publicKey, privateKey };
|
||||||
|
@ -91,7 +91,7 @@ async function sign(oid, hash_algo, message, publicKey, privateKey, hashed) {
|
||||||
* @returns {Boolean}
|
* @returns {Boolean}
|
||||||
* @async
|
* @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);
|
const curve = new Curve(oid);
|
||||||
if (message && !util.isStream(message)) {
|
if (message && !util.isStream(message)) {
|
||||||
switch (curve.type) {
|
switch (curve.type) {
|
||||||
|
@ -125,7 +125,7 @@ async function verify(oid, hash_algo, signature, message, publicKey, hashed) {
|
||||||
* @returns {Promise<Boolean>} whether params are valid
|
* @returns {Promise<Boolean>} whether params are valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function validateParams(oid, Q, d) {
|
export async function validateParams(oid, Q, d) {
|
||||||
const curve = new Curve(oid);
|
const curve = new Curve(oid);
|
||||||
// Reject curves x25519 and ed25519
|
// Reject curves x25519 and ed25519
|
||||||
if (curve.keyType !== enums.publicKey.ecdsa) {
|
if (curve.keyType !== enums.publicKey.ecdsa) {
|
||||||
|
@ -137,7 +137,7 @@ async function validateParams(oid, Q, d) {
|
||||||
switch (curve.type) {
|
switch (curve.type) {
|
||||||
case 'web':
|
case 'web':
|
||||||
case 'node': {
|
case 'node': {
|
||||||
const message = await random.getRandomBytes(8);
|
const message = await getRandomBytes(8);
|
||||||
const hashAlgo = enums.hash.sha256;
|
const hashAlgo = enums.hash.sha256;
|
||||||
const hashed = await hash.digest(hashAlgo, message);
|
const hashed = await hash.digest(hashAlgo, message);
|
||||||
try {
|
try {
|
||||||
|
@ -152,8 +152,6 @@ async function validateParams(oid, Q, d) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { sign, verify, ellipticVerify, ellipticSign, validateParams };
|
|
||||||
|
|
||||||
|
|
||||||
//////////////////////////
|
//////////////////////////
|
||||||
// //
|
// //
|
||||||
|
|
|
@ -42,7 +42,7 @@ nacl.hash = bytes => new Uint8Array(sha512().update(bytes).digest());
|
||||||
* S: Uint8Array}} Signature of the message
|
* S: Uint8Array}} Signature of the message
|
||||||
* @async
|
* @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 secretKey = util.concatUint8Array([privateKey, publicKey.subarray(1)]);
|
||||||
const signature = nacl.sign.detached(hashed, secretKey);
|
const signature = nacl.sign.detached(hashed, secretKey);
|
||||||
// EdDSA signature params are returned in little-endian format
|
// 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}
|
* @returns {Boolean}
|
||||||
* @async
|
* @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]);
|
const signature = util.concatUint8Array([R, S]);
|
||||||
return nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1));
|
return nacl.sign.detached.verify(hashed, signature, publicKey.subarray(1));
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Validate EdDSA parameters
|
* Validate EdDSA parameters
|
||||||
* @param {module:type/oid} oid Elliptic curve object identifier
|
* @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
|
* @returns {Promise<Boolean>} whether params are valid
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function validateParams(oid, Q, k) {
|
export async function validateParams(oid, Q, k) {
|
||||||
// Check whether the given curve is supported
|
// Check whether the given curve is supported
|
||||||
if (oid.getName() !== 'ed25519') {
|
if (oid.getName() !== 'ed25519') {
|
||||||
return false;
|
return false;
|
||||||
|
@ -91,5 +90,3 @@ async function validateParams(oid, Q, k) {
|
||||||
const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
|
const dG = new Uint8Array([0x40, ...publicKey]); // Add public key prefix
|
||||||
return util.equalsUint8Array(Q, dG);
|
return util.equalsUint8Array(Q, dG);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { sign, verify, validateParams };
|
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
* @module crypto/public_key/elliptic
|
* @module crypto/public_key/elliptic
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import Curve, { generate, getPreferredHashAlgo } from './curves';
|
import { Curve, generate, getPreferredHashAlgo } from './curves';
|
||||||
import ecdsa from './ecdsa';
|
import * as ecdsa from './ecdsa';
|
||||||
import eddsa from './eddsa';
|
import * as eddsa from './eddsa';
|
||||||
import ecdh from './ecdh';
|
import * as ecdh from './ecdh';
|
||||||
|
|
||||||
export default {
|
export {
|
||||||
Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo
|
Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo
|
||||||
};
|
};
|
||||||
|
|
|
@ -9,10 +9,10 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import nacl from 'tweetnacl/nacl-fast-light.js';
|
import nacl from 'tweetnacl/nacl-fast-light.js';
|
||||||
import rsa from './rsa';
|
import * as rsa from './rsa';
|
||||||
import elgamal from './elgamal';
|
import * as elgamal from './elgamal';
|
||||||
import elliptic from './elliptic';
|
import * as elliptic from './elliptic';
|
||||||
import dsa from './dsa';
|
import * as dsa from './dsa';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
/** @see module:crypto/public_key/rsa */
|
/** @see module:crypto/public_key/rsa */
|
||||||
|
|
|
@ -22,11 +22,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import random from '../random';
|
import { getRandomBigInteger } from '../random';
|
||||||
|
|
||||||
export default {
|
|
||||||
randomProbablePrime, isProbablePrime, fermat, millerRabin, divisionTest
|
|
||||||
};
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Probabilistic random number generator
|
* Probabilistic random number generator
|
||||||
|
@ -36,7 +32,7 @@ export default {
|
||||||
* @returns BigInteger
|
* @returns BigInteger
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function randomProbablePrime(bits, e, k) {
|
export async function randomProbablePrime(bits, e, k) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
const one = new BigInteger(1);
|
const one = new BigInteger(1);
|
||||||
const min = one.leftShift(new BigInteger(bits - 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 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();
|
let i = n.mod(thirty).toNumber();
|
||||||
|
|
||||||
do {
|
do {
|
||||||
|
@ -72,7 +68,7 @@ async function randomProbablePrime(bits, e, k) {
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function isProbablePrime(n, e, k) {
|
export async function isProbablePrime(n, e, k) {
|
||||||
if (e && !n.dec().gcd(e).isOne()) {
|
if (e && !n.dec().gcd(e).isOne()) {
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
@ -97,13 +93,13 @@ async function isProbablePrime(n, e, k) {
|
||||||
* @param {BigInteger} b Optional Fermat test base
|
* @param {BigInteger} b Optional Fermat test base
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
*/
|
*/
|
||||||
async function fermat(n, b) {
|
export async function fermat(n, b) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
b = b || new BigInteger(2);
|
b = b || new BigInteger(2);
|
||||||
return b.modExp(n.dec(), n).isOne();
|
return b.modExp(n.dec(), n).isOne();
|
||||||
}
|
}
|
||||||
|
|
||||||
async function divisionTest(n) {
|
export async function divisionTest(n) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
return smallPrimes.every(m => {
|
return smallPrimes.every(m => {
|
||||||
return n.mod(new BigInteger(m)) !== 0;
|
return n.mod(new BigInteger(m)) !== 0;
|
||||||
|
@ -232,7 +228,7 @@ const smallPrimes = [
|
||||||
* @returns {boolean}
|
* @returns {boolean}
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
async function millerRabin(n, k, rand) {
|
export async function millerRabin(n, k, rand) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
const len = n.bitLength();
|
const len = n.bitLength();
|
||||||
|
|
||||||
|
@ -248,7 +244,7 @@ async function millerRabin(n, k, rand) {
|
||||||
const d = n.rightShift(new BigInteger(s));
|
const d = n.rightShift(new BigInteger(s));
|
||||||
|
|
||||||
for (; k > 0; k--) {
|
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);
|
let x = a.modExp(d, n);
|
||||||
if (x.isOne() || x.equal(n1)) {
|
if (x.isOne() || x.equal(n1)) {
|
||||||
|
|
|
@ -24,11 +24,11 @@
|
||||||
* @module crypto/public_key/rsa
|
* @module crypto/public_key/rsa
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import prime from './prime';
|
import { randomProbablePrime } from './prime';
|
||||||
import random from '../random';
|
import { getRandomBigInteger } from '../random';
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
import util from '../../util';
|
import util from '../../util';
|
||||||
import pkcs1 from '../pkcs1';
|
import { emsaEncode, emeEncode, emeDecode } from '../pkcs1';
|
||||||
import enums from '../../enums';
|
import enums from '../../enums';
|
||||||
|
|
||||||
const webCrypto = util.getWebCrypto();
|
const webCrypto = util.getWebCrypto();
|
||||||
|
@ -73,484 +73,480 @@ const RSAPublicKey = util.detectNode() ? asn1.define('RSAPubliceKey', function (
|
||||||
}) : undefined;
|
}) : undefined;
|
||||||
/* eslint-enable no-invalid-this */
|
/* eslint-enable no-invalid-this */
|
||||||
|
|
||||||
export default {
|
/** Create signature
|
||||||
/** Create signature
|
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
* @param {Uint8Array} data message
|
||||||
* @param {Uint8Array} data message
|
* @param {Uint8Array} n RSA public modulus
|
||||||
* @param {Uint8Array} n RSA public modulus
|
* @param {Uint8Array} e RSA public exponent
|
||||||
* @param {Uint8Array} e RSA public exponent
|
* @param {Uint8Array} d RSA private exponent
|
||||||
* @param {Uint8Array} d RSA private exponent
|
* @param {Uint8Array} p RSA private prime p
|
||||||
* @param {Uint8Array} p RSA private prime p
|
* @param {Uint8Array} q RSA private prime q
|
||||||
* @param {Uint8Array} q RSA private prime q
|
* @param {Uint8Array} u RSA private coefficient
|
||||||
* @param {Uint8Array} u RSA private coefficient
|
* @param {Uint8Array} hashed hashed message
|
||||||
* @param {Uint8Array} hashed hashed message
|
* @returns {Uint8Array} RSA Signature
|
||||||
* @returns {Uint8Array} RSA Signature
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function sign(hash_algo, data, n, e, d, p, q, u, hashed) {
|
||||||
sign: async function(hash_algo, data, n, e, d, p, q, u, hashed) {
|
if (data && !util.isStream(data)) {
|
||||||
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
|
|
||||||
if (util.getWebCrypto()) {
|
if (util.getWebCrypto()) {
|
||||||
let keyPair;
|
try {
|
||||||
let keyGenOpt;
|
return await webSign(enums.read(enums.webHash, hash_algo), data, n, e, d, p, q, u);
|
||||||
if ((globalThis.crypto && globalThis.crypto.subtle) || globalThis.msCrypto) {
|
} catch (err) {
|
||||||
// current standard spec
|
util.printDebugError(err);
|
||||||
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');
|
|
||||||
}
|
}
|
||||||
|
} 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
|
* Verify signature
|
||||||
let jwk = webCrypto.exportKey('jwk', keyPair.privateKey);
|
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||||
jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.');
|
* @param {Uint8Array} data message
|
||||||
|
* @param {Uint8Array} s signature
|
||||||
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
|
* @param {Uint8Array} n RSA public modulus
|
||||||
if (jwk instanceof ArrayBuffer) {
|
* @param {Uint8Array} e RSA public exponent
|
||||||
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
|
* @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
|
} else if (util.getNodeCrypto()) {
|
||||||
return {
|
return nodeVerify(hash_algo, data, s, n, e);
|
||||||
n: util.b64ToUint8Array(jwk.n),
|
}
|
||||||
e: e.toUint8Array(),
|
}
|
||||||
d: util.b64ToUint8Array(jwk.d),
|
return bnVerify(hash_algo, s, n, e, hashed);
|
||||||
// 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
|
* Encrypt message
|
||||||
u: util.b64ToUint8Array(jwk.qi)
|
* @param {Uint8Array} data message
|
||||||
};
|
* @param {Uint8Array} n RSA public modulus
|
||||||
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
|
* @param {Uint8Array} e RSA public exponent
|
||||||
const opts = {
|
* @returns {Uint8Array} RSA Ciphertext
|
||||||
modulusLength: bits,
|
* @async
|
||||||
publicExponent: e.toNumber(),
|
*/
|
||||||
publicKeyEncoding: { type: 'pkcs1', format: 'der' },
|
export async function encrypt(data, n, e) {
|
||||||
privateKeyEncoding: { type: 'pkcs1', format: 'der' }
|
if (util.getNodeCrypto()) {
|
||||||
};
|
return nodeEncrypt(data, n, e);
|
||||||
const prv = await new Promise((resolve, reject) => nodeCrypto.generateKeyPair('rsa', opts, (err, _, der) => {
|
}
|
||||||
if (err) {
|
return bnEncrypt(data, n, e);
|
||||||
reject(err);
|
}
|
||||||
} else {
|
|
||||||
resolve(RSAPrivateKey.decode(der, 'der'));
|
/**
|
||||||
|
* 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
|
// export the generated keys as JsonWebKey (JWK)
|
||||||
// See https://stackoverflow.com/a/6330138 for justification
|
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
|
||||||
// Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
|
let jwk = webCrypto.exportKey('jwk', keyPair.privateKey);
|
||||||
let q = await prime.randomProbablePrime(bits - (bits >> 1), e, 40);
|
jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.');
|
||||||
let p = await prime.randomProbablePrime(bits >> 1, e, 40);
|
|
||||||
|
|
||||||
if (q.lt(p)) {
|
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
|
||||||
[p, q] = [q, p];
|
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 {
|
return {
|
||||||
n: p.mul(q).toUint8Array(),
|
n: util.b64ToUint8Array(jwk.n),
|
||||||
e: e.toUint8Array(),
|
e: e.toUint8Array(),
|
||||||
d: e.modInv(phi).toUint8Array(),
|
d: util.b64ToUint8Array(jwk.d),
|
||||||
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),
|
|
||||||
// switch p and q
|
// switch p and q
|
||||||
prime1: new BN(q),
|
p: util.b64ToUint8Array(jwk.q),
|
||||||
prime2: new BN(p),
|
q: util.b64ToUint8Array(jwk.p),
|
||||||
// switch dp and dq
|
// Since p and q are switched in places, u is the inverse of jwk.q
|
||||||
exponent1: dq,
|
u: util.b64ToUint8Array(jwk.qi)
|
||||||
exponent2: dp,
|
|
||||||
coefficient: new BN(u)
|
|
||||||
};
|
};
|
||||||
if (typeof nodeCrypto.createPrivateKey !== 'undefined') { //from version 11.6.0 Node supports der encoded key objects
|
} else if (util.getNodeCrypto() && nodeCrypto.generateKeyPair && RSAPrivateKey) {
|
||||||
const der = RSAPrivateKey.encode(keyObject, 'der');
|
const opts = {
|
||||||
return new Uint8Array(sign.sign({ key: der, format: 'der', type: 'pkcs1' }));
|
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', {
|
const pem = RSAPrivateKey.encode(keyObject, 'pem', {
|
||||||
label: 'RSA PRIVATE KEY'
|
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) {
|
async function bnDecrypt(data, n, e, d, p, q, u) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
n = new BigInteger(n);
|
data = new BigInteger(data);
|
||||||
s = new BigInteger(s);
|
n = new BigInteger(n);
|
||||||
e = new BigInteger(e);
|
e = new BigInteger(e);
|
||||||
if (s.gte(n)) {
|
d = new BigInteger(d);
|
||||||
throw new Error('Signature size cannot exceed modulus size');
|
p = new BigInteger(p);
|
||||||
}
|
q = new BigInteger(q);
|
||||||
const EM1 = s.modExp(e, n).toUint8Array('be', n.byteLength());
|
u = new BigInteger(u);
|
||||||
const EM2 = await pkcs1.emsa.encode(hash_algo, hashed, n.byteLength());
|
if (data.gte(n)) {
|
||||||
return util.equalsUint8Array(EM1, EM2);
|
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) {
|
let blinder;
|
||||||
const jwk = publicToJwk(n, e);
|
let unblinder;
|
||||||
const key = await webCrypto.importKey("jwk", jwk, {
|
if (config.rsaBlinding) {
|
||||||
name: "RSASSA-PKCS1-v1_5",
|
unblinder = (await getRandomBigInteger(new BigInteger(2), n)).mod(n);
|
||||||
hash: { name: hash_name }
|
blinder = unblinder.modInv(n).modExp(e, n);
|
||||||
}, false, ["verify"]);
|
data = data.mul(blinder).mod(n);
|
||||||
// add hash field for ms edge support
|
}
|
||||||
return webCrypto.verify({ "name": "RSASSA-PKCS1-v1_5", "hash": hash_name }, key, s, data);
|
|
||||||
},
|
|
||||||
|
|
||||||
nodeVerify: async function (hash_algo, data, s, n, e) {
|
const mp = data.modExp(dp, p); // data**{d mod (q-1)} mod p
|
||||||
const { default: BN } = await import('bn.js');
|
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));
|
let result = h.mul(p).add(mp); // result < n due to relations above
|
||||||
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;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
|
|
||||||
nodeEncrypt: async function (data, n, e) {
|
if (config.rsaBlinding) {
|
||||||
const { default: BN } = await import('bn.js');
|
result = result.mul(unblinder).mod(n);
|
||||||
|
}
|
||||||
|
|
||||||
const keyObject = {
|
return emeDecode(result.toUint8Array('be', n.byteLength()));
|
||||||
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
|
|
||||||
};
|
|
||||||
|
|
||||||
/** Convert Openpgp private key params to jwk key according to
|
/** Convert Openpgp private key params to jwk key according to
|
||||||
* @link https://tools.ietf.org/html/rfc7517
|
* @link https://tools.ietf.org/html/rfc7517
|
||||||
|
|
|
@ -94,53 +94,51 @@ class RandomBuffer {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
/**
|
||||||
/**
|
* Retrieve secure random byte array of the specified length
|
||||||
* Retrieve secure random byte array of the specified length
|
* @param {Integer} length Length in bytes to generate
|
||||||
* @param {Integer} length Length in bytes to generate
|
* @returns {Uint8Array} Random byte array
|
||||||
* @returns {Uint8Array} Random byte array
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function getRandomBytes(length) {
|
||||||
getRandomBytes: async function(length) {
|
const buf = new Uint8Array(length);
|
||||||
const buf = new Uint8Array(length);
|
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
||||||
if (typeof crypto !== 'undefined' && crypto.getRandomValues) {
|
crypto.getRandomValues(buf);
|
||||||
crypto.getRandomValues(buf);
|
} else if (typeof globalThis !== 'undefined' && typeof globalThis.msCrypto === 'object' && typeof globalThis.msCrypto.getRandomValues === 'function') {
|
||||||
} else if (typeof globalThis !== 'undefined' && typeof globalThis.msCrypto === 'object' && typeof globalThis.msCrypto.getRandomValues === 'function') {
|
globalThis.msCrypto.getRandomValues(buf);
|
||||||
globalThis.msCrypto.getRandomValues(buf);
|
} else if (nodeCrypto) {
|
||||||
} else if (nodeCrypto) {
|
const bytes = nodeCrypto.randomBytes(buf.length);
|
||||||
const bytes = nodeCrypto.randomBytes(buf.length);
|
buf.set(bytes);
|
||||||
buf.set(bytes);
|
} else if (randomBuffer.buffer) {
|
||||||
} else if (this.randomBuffer.buffer) {
|
await randomBuffer.get(buf);
|
||||||
await this.randomBuffer.get(buf);
|
} else {
|
||||||
} else {
|
throw new Error('No secure random number generator available.');
|
||||||
throw new Error('No secure random number generator available.');
|
}
|
||||||
}
|
return buf;
|
||||||
return buf;
|
}
|
||||||
},
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Create a secure random BigInteger that is greater than or equal to min and less than max.
|
* 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} min Lower bound, included
|
||||||
* @param {module:BigInteger} max Upper bound, excluded
|
* @param {module:BigInteger} max Upper bound, excluded
|
||||||
* @returns {module:BigInteger} Random BigInteger
|
* @returns {module:BigInteger} Random BigInteger
|
||||||
* @async
|
* @async
|
||||||
*/
|
*/
|
||||||
getRandomBigInteger: async function(min, max) {
|
export async function getRandomBigInteger(min, max) {
|
||||||
const BigInteger = await util.getBigInteger();
|
const BigInteger = await util.getBigInteger();
|
||||||
|
|
||||||
if (max.lt(min)) {
|
if (max.lt(min)) {
|
||||||
throw new Error('Illegal parameter value: max <= min');
|
throw new Error('Illegal parameter value: max <= min');
|
||||||
}
|
}
|
||||||
|
|
||||||
const modulus = max.sub(min);
|
const modulus = max.sub(min);
|
||||||
const bytes = modulus.byteLength();
|
const bytes = modulus.byteLength();
|
||||||
|
|
||||||
// Using a while loop is necessary to avoid bias introduced by the mod operation.
|
// 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.
|
// 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
|
// 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));
|
const r = new BigInteger(await getRandomBytes(bytes + 8));
|
||||||
return r.mod(modulus).add(min);
|
return r.mod(modulus).add(min);
|
||||||
},
|
}
|
||||||
|
|
||||||
randomBuffer: new RandomBuffer()
|
export const randomBuffer = new RandomBuffer();
|
||||||
};
|
|
||||||
|
|
|
@ -1,125 +1,121 @@
|
||||||
/**
|
/**
|
||||||
* @fileoverview Provides functions for asymmetric signing and signature verification
|
* @fileoverview Provides functions for asymmetric signing and signature verification
|
||||||
* @requires crypto/crypto
|
|
||||||
* @requires crypto/public_key
|
* @requires crypto/public_key
|
||||||
* @requires enums
|
* @requires enums
|
||||||
* @requires util
|
* @requires util
|
||||||
* @module crypto/signature
|
* @module crypto/signature
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import crypto from './crypto';
|
|
||||||
import publicKey from './public_key';
|
import publicKey from './public_key';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
export default {
|
/**
|
||||||
/**
|
* Verifies the signature provided for data using specified algorithms and public key parameters.
|
||||||
* 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}
|
||||||
* 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}
|
||||||
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
|
* for public key and hash algorithms.
|
||||||
* for public key and hash algorithms.
|
* @param {module:enums.publicKey} algo Public key algorithm
|
||||||
* @param {module:enums.publicKey} algo Public key algorithm
|
* @param {module:enums.hash} hash_algo Hash algorithm
|
||||||
* @param {module:enums.hash} hash_algo Hash algorithm
|
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
|
||||||
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
|
* @param {Object} publicParams Algorithm-specific public key parameters
|
||||||
* @param {Object} publicParams Algorithm-specific public key parameters
|
* @param {Uint8Array} data Data for which the signature was created
|
||||||
* @param {Uint8Array} data Data for which the signature was created
|
* @param {Uint8Array} hashed The hashed data
|
||||||
* @param {Uint8Array} hashed The hashed data
|
* @returns {Boolean} True if signature is valid
|
||||||
* @returns {Boolean} True if signature is valid
|
* @async
|
||||||
* @async
|
*/
|
||||||
*/
|
export async function verify(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
|
||||||
verify: async function(algo, hash_algo, msg_MPIs, publicParams, data, hashed) {
|
switch (algo) {
|
||||||
switch (algo) {
|
case enums.publicKey.rsaEncryptSign:
|
||||||
case enums.publicKey.rsaEncryptSign:
|
case enums.publicKey.rsaEncrypt:
|
||||||
case enums.publicKey.rsaEncrypt:
|
case enums.publicKey.rsaSign: {
|
||||||
case enums.publicKey.rsaSign: {
|
const { n, e } = publicParams;
|
||||||
const { n, e } = publicParams;
|
const m = msg_MPIs[0].toUint8Array('be', n.length);
|
||||||
const m = msg_MPIs[0].toUint8Array('be', n.length);
|
return publicKey.rsa.verify(hash_algo, data, m, n, e, hashed);
|
||||||
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.');
|
|
||||||
}
|
}
|
||||||
},
|
case enums.publicKey.dsa: {
|
||||||
|
const r = await msg_MPIs[0].toUint8Array();
|
||||||
/**
|
const s = await msg_MPIs[1].toUint8Array();
|
||||||
* Creates a signature on data using specified algorithms and private key parameters.
|
const { g, p, q, y } = publicParams;
|
||||||
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
|
return publicKey.dsa.verify(hash_algo, r, s, hashed, g, p, q, y);
|
||||||
* 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');
|
|
||||||
}
|
}
|
||||||
switch (algo) {
|
case enums.publicKey.ecdsa: {
|
||||||
case enums.publicKey.rsaEncryptSign:
|
const { oid, Q } = publicParams;
|
||||||
case enums.publicKey.rsaEncrypt:
|
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
|
||||||
case enums.publicKey.rsaSign: {
|
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q, hashed);
|
||||||
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.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.');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import base64 from './base64.js';
|
import * as base64 from './base64.js';
|
||||||
import enums from '../enums.js';
|
import enums from '../enums.js';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
@ -233,7 +233,7 @@ function splitChecksum(text) {
|
||||||
* @async
|
* @async
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function dearmor(input) {
|
export function unarmor(input) {
|
||||||
return new Promise(async (resolve, reject) => {
|
return new Promise(async (resolve, reject) => {
|
||||||
try {
|
try {
|
||||||
const reSplit = /^-----[^-]+-----$/m;
|
const reSplit = /^-----[^-]+-----$/m;
|
||||||
|
@ -359,7 +359,7 @@ function dearmor(input) {
|
||||||
* @returns {String | ReadableStream<String>} Armored text
|
* @returns {String | ReadableStream<String>} Armored text
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function armor(messagetype, body, partindex, parttotal, customComment) {
|
export function armor(messagetype, body, partindex, parttotal, customComment) {
|
||||||
let text;
|
let text;
|
||||||
let hash;
|
let hash;
|
||||||
if (messagetype === enums.armor.signed) {
|
if (messagetype === enums.armor.signed) {
|
||||||
|
@ -426,8 +426,3 @@ function armor(messagetype, body, partindex, parttotal, customComment) {
|
||||||
|
|
||||||
return util.concat(result);
|
return util.concat(result);
|
||||||
}
|
}
|
||||||
|
|
||||||
export default {
|
|
||||||
encode: armor,
|
|
||||||
decode: dearmor
|
|
||||||
};
|
|
||||||
|
|
|
@ -41,7 +41,7 @@ if (Buffer) {
|
||||||
* @returns {String | ReadableStream<String>} radix-64 version of input string
|
* @returns {String | ReadableStream<String>} radix-64 version of input string
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function encode(data) {
|
export function encode(data) {
|
||||||
let buf = new Uint8Array();
|
let buf = new Uint8Array();
|
||||||
return stream.transform(data, value => {
|
return stream.transform(data, value => {
|
||||||
buf = util.concatUint8Array([buf, value]);
|
buf = util.concatUint8Array([buf, value]);
|
||||||
|
@ -65,7 +65,7 @@ function encode(data) {
|
||||||
* @returns {Uint8Array | ReadableStream<Uint8Array>} binary array version of input string
|
* @returns {Uint8Array | ReadableStream<Uint8Array>} binary array version of input string
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
function decode(data) {
|
export function decode(data) {
|
||||||
let buf = '';
|
let buf = '';
|
||||||
return stream.transform(data, value => {
|
return stream.transform(data, value => {
|
||||||
buf += value;
|
buf += value;
|
||||||
|
@ -92,5 +92,3 @@ function decode(data) {
|
||||||
return decoded;
|
return decoded;
|
||||||
}, () => decodeChunk(buf));
|
}, () => decodeChunk(buf));
|
||||||
}
|
}
|
||||||
|
|
||||||
export default { encode, decode };
|
|
||||||
|
|
|
@ -126,7 +126,6 @@ export default {
|
||||||
plaintext: 0,
|
plaintext: 0,
|
||||||
/** Not implemented! */
|
/** Not implemented! */
|
||||||
idea: 1,
|
idea: 1,
|
||||||
'3des': 2,
|
|
||||||
tripledes: 2,
|
tripledes: 2,
|
||||||
cast5: 3,
|
cast5: 3,
|
||||||
blowfish: 4,
|
blowfish: 4,
|
||||||
|
|
|
@ -98,7 +98,7 @@ export { default as stream } from 'web-stream-tools';
|
||||||
* @see module:encoding/armor
|
* @see module:encoding/armor
|
||||||
* @name module:openpgp.armor
|
* @name module:openpgp.armor
|
||||||
*/
|
*/
|
||||||
export { default as armor } from './encoding/armor';
|
export * from './encoding/armor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @see module:enums
|
* @see module:enums
|
||||||
|
|
|
@ -33,7 +33,7 @@ import * as helper from './helper';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import armor from '../encoding/armor';
|
import { unarmor } from '../encoding/armor';
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Generates a new OpenPGP key. Supports RSA and ECC keys.
|
* Generates a new OpenPGP key. Supports RSA and ECC keys.
|
||||||
|
@ -283,7 +283,7 @@ export async function readKey(data) {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function readArmoredKey(armoredKey) {
|
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)) {
|
if (!(input.type === enums.armor.publicKey || input.type === enums.armor.privateKey)) {
|
||||||
throw new Error('Armored text not of type key');
|
throw new Error('Armored text not of type key');
|
||||||
}
|
}
|
||||||
|
@ -321,7 +321,7 @@ export async function readKeys(data) {
|
||||||
* @static
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function readArmoredKeys(armoredKey) {
|
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)) {
|
if (!(input.type === enums.armor.publicKey || input.type === enums.armor.privateKey)) {
|
||||||
throw new Error('Armored text not of type key');
|
throw new Error('Armored text not of type key');
|
||||||
}
|
}
|
||||||
|
|
|
@ -25,7 +25,7 @@
|
||||||
* @module key/Key
|
* @module key/Key
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import armor from '../encoding/armor';
|
import { armor, unarmor } from '../encoding/armor';
|
||||||
import {
|
import {
|
||||||
PacketList,
|
PacketList,
|
||||||
PublicKeyPacket,
|
PublicKeyPacket,
|
||||||
|
@ -280,7 +280,7 @@ class Key {
|
||||||
*/
|
*/
|
||||||
armor() {
|
armor() {
|
||||||
const type = this.isPublic() ? enums.armor.publicKey : enums.armor.privateKey;
|
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 revocationSignature = await helper.getLatestValidSignature(this.revocationSignatures, this.keyPacket, enums.signature.keyRevocation, dataToVerify, date);
|
||||||
const packetlist = new PacketList();
|
const packetlist = new PacketList();
|
||||||
packetlist.push(revocationSignature);
|
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
|
||||||
*/
|
*/
|
||||||
async applyRevocationCertificate(revocationCertificate) {
|
async applyRevocationCertificate(revocationCertificate) {
|
||||||
const input = await armor.decode(revocationCertificate);
|
const input = await unarmor(revocationCertificate);
|
||||||
const packetlist = new PacketList();
|
const packetlist = new PacketList();
|
||||||
await packetlist.read(input.data, { SignaturePacket });
|
await packetlist.read(input.data, { SignaturePacket });
|
||||||
const revocationSignature = packetlist.findPacket(enums.packet.signature);
|
const revocationSignature = packetlist.findPacket(enums.packet.signature);
|
||||||
|
|
|
@ -30,7 +30,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import armor from './encoding/armor';
|
import { armor, unarmor } from './encoding/armor';
|
||||||
import type_keyid from './type/keyid';
|
import type_keyid from './type/keyid';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import crypto from './crypto';
|
import crypto from './crypto';
|
||||||
|
@ -607,7 +607,7 @@ export class Message {
|
||||||
* @param {String|Uint8Array} detachedSignature The detached ASCII-armored or Uint8Array PGP signature
|
* @param {String|Uint8Array} detachedSignature The detached ASCII-armored or Uint8Array PGP signature
|
||||||
*/
|
*/
|
||||||
async appendSignature(detachedSignature) {
|
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
|
* @returns {ReadableStream<String>} ASCII armor
|
||||||
*/
|
*/
|
||||||
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') {
|
if (streamType === 'node') {
|
||||||
armoredText = stream.nodeToWeb(armoredText);
|
armoredText = stream.nodeToWeb(armoredText);
|
||||||
}
|
}
|
||||||
const input = await armor.decode(armoredText);
|
const input = await unarmor(armoredText);
|
||||||
return readMessage(input.data, streamType);
|
return readMessage(input.data, streamType);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -29,268 +29,266 @@ import stream from 'web-stream-tools';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
export default {
|
export function readSimpleLength(bytes) {
|
||||||
readSimpleLength: function(bytes) {
|
let len = 0;
|
||||||
let len = 0;
|
let offset;
|
||||||
let offset;
|
const type = bytes[0];
|
||||||
const type = bytes[0];
|
|
||||||
|
|
||||||
|
|
||||||
if (type < 192) {
|
if (type < 192) {
|
||||||
[len] = bytes;
|
[len] = bytes;
|
||||||
offset = 1;
|
offset = 1;
|
||||||
} else if (type < 255) {
|
} else if (type < 255) {
|
||||||
len = ((bytes[0] - 192) << 8) + (bytes[1]) + 192;
|
len = ((bytes[0] - 192) << 8) + (bytes[1]) + 192;
|
||||||
offset = 2;
|
offset = 2;
|
||||||
} else if (type === 255) {
|
} else if (type === 255) {
|
||||||
len = util.readNumber(bytes.subarray(1, 1 + 4));
|
len = util.readNumber(bytes.subarray(1, 1 + 4));
|
||||||
offset = 5;
|
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 {
|
let packet_length_type;
|
||||||
len: len,
|
if (format) {
|
||||||
offset: offset
|
// new format header
|
||||||
};
|
tag = headerByte & 0x3F; // bit 5-0
|
||||||
},
|
} else {
|
||||||
|
// old format header
|
||||||
/**
|
tag = (headerByte & 0x3F) >> 2; // bit 5-2
|
||||||
* Encodes a given integer of length to the openpgp length specifier to a
|
packet_length_type = headerByte & 0x03; // bit 1-0
|
||||||
* 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]);
|
|
||||||
}
|
}
|
||||||
return util.concatUint8Array([new Uint8Array([255]), util.writeNumber(length, 4)]);
|
|
||||||
},
|
|
||||||
|
|
||||||
writePartialLength: function(power) {
|
const packetSupportsStreaming = supportsStreaming(tag);
|
||||||
if (power < 0 || power > 30) {
|
let packet = null;
|
||||||
throw new Error('Partial Length power must be between 1 and 30');
|
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) {
|
let wasPartialLength;
|
||||||
/* we're only generating v4 packet headers here */
|
do {
|
||||||
return new Uint8Array([0xC0 | tag_type]);
|
if (!format) {
|
||||||
},
|
// 4.2.1. Old Format Packet Lengths
|
||||||
|
switch (packet_length_type) {
|
||||||
/**
|
case 0:
|
||||||
* Writes a packet header version 4 with the given tag_type and length to a
|
// The packet has a one-octet length. The header is 2 octets
|
||||||
* string
|
// long.
|
||||||
*
|
packet_length = await reader.readByte();
|
||||||
* @param {Integer} tag_type Tag type
|
break;
|
||||||
* @param {Integer} length Length of the payload
|
case 1:
|
||||||
* @returns {String} String of the header
|
// The packet has a two-octet length. The header is 3 octets
|
||||||
*/
|
// long.
|
||||||
writeHeader: function(tag_type, length) {
|
packet_length = (await reader.readByte() << 8) | await reader.readByte();
|
||||||
/* we're only generating v4 packet headers here */
|
break;
|
||||||
return util.concatUint8Array([this.writeTag(tag_type), this.writeSimpleLength(length)]);
|
case 2:
|
||||||
},
|
// The packet has a four-octet length. The header is 5
|
||||||
|
// octets long.
|
||||||
/**
|
|
||||||
* 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 {
|
|
||||||
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
packet_length = (await reader.readByte() << 24) | (await reader.readByte() << 16) | (await reader.readByte() <<
|
||||||
8) | 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 (wasPartialLength);
|
||||||
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);
|
|
||||||
|
|
||||||
// If this was not a packet that "supports streaming", we peek to check
|
// 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
|
// 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
|
// 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`'
|
// want to cut a `subarray` of the correct length into `web-stream-tools`'
|
||||||
// `externalBuffer` as a tiny optimization here.
|
// `externalBuffer` as a tiny optimization here.
|
||||||
//
|
//
|
||||||
// If it *was* a streaming packet (i.e. the data packets), we peek at the
|
// 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
|
// 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
|
// remainder of the stream to the packet data. (Note that this means we
|
||||||
// read/peek at all signature packets before closing the literal data
|
// read/peek at all signature packets before closing the literal data
|
||||||
// packet, for example.) This forwards MDC errors to 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
|
// stream, for example, so that they don't get lost / forgotten on
|
||||||
// decryptedMessage.packets.stream, which we never look at.
|
// decryptedMessage.packets.stream, which we never look at.
|
||||||
//
|
//
|
||||||
// An example of what we do when stream-parsing a message containing
|
// An example of what we do when stream-parsing a message containing
|
||||||
// [ one-pass signature packet, literal data packet, signature packet ]:
|
// [ one-pass signature packet, literal data packet, signature packet ]:
|
||||||
// 1. Read the one-pass signature packet
|
// 1. Read the one-pass signature packet
|
||||||
// 2. Peek 2 bytes of the literal data packet
|
// 2. Peek 2 bytes of the literal data packet
|
||||||
// 3. Parse the one-pass signature packet
|
// 3. Parse the one-pass signature packet
|
||||||
//
|
//
|
||||||
// 4. Read the literal data packet, simultaneously stream-parsing it
|
// 4. Read the literal data packet, simultaneously stream-parsing it
|
||||||
// 5. Peek until the end of the message
|
// 5. Peek until the end of the message
|
||||||
// 6. Finish parsing the literal data packet
|
// 6. Finish parsing the literal data packet
|
||||||
//
|
//
|
||||||
// 7. Read the signature packet again (we already peeked at it in step 5)
|
// 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)
|
// 8. Peek at the end of the stream again (`peekBytes` returns undefined)
|
||||||
// 9. Parse the signature packet
|
// 9. Parse the signature packet
|
||||||
//
|
//
|
||||||
// Note that this means that if there's an error in the very end of the
|
// 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
|
// 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.
|
// (or never), which is the point of this exercise.
|
||||||
const nextPacket = await reader.peekBytes(supportsStreaming ? Infinity : 2);
|
const nextPacket = await reader.peekBytes(packetSupportsStreaming ? Infinity : 2);
|
||||||
if (writer) {
|
if (writer) {
|
||||||
await writer.ready;
|
await writer.ready;
|
||||||
await writer.close();
|
await writer.close();
|
||||||
} else {
|
} else {
|
||||||
packet = util.concatUint8Array(packet);
|
packet = util.concatUint8Array(packet);
|
||||||
await callback({ tag, 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();
|
|
||||||
}
|
}
|
||||||
|
return !nextPacket || !nextPacket.length;
|
||||||
|
} catch (e) {
|
||||||
|
if (writer) {
|
||||||
|
await writer.abort(e);
|
||||||
|
return true;
|
||||||
|
} else {
|
||||||
|
throw e;
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
if (writer) {
|
||||||
|
await callbackReturned;
|
||||||
|
}
|
||||||
|
reader.releaseLock();
|
||||||
}
|
}
|
||||||
};
|
}
|
||||||
|
|
|
@ -10,7 +10,11 @@
|
||||||
|
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import * as packets from './all_packets';
|
import * as packets from './all_packets';
|
||||||
import packetParser from './packet';
|
import {
|
||||||
|
readPackets, supportsStreaming,
|
||||||
|
writeTag, writeHeader,
|
||||||
|
writePartialLength, writeSimpleLength
|
||||||
|
} from './packet';
|
||||||
import config from '../config';
|
import config from '../config';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
@ -33,7 +37,7 @@ class PacketList extends Array {
|
||||||
try {
|
try {
|
||||||
while (true) {
|
while (true) {
|
||||||
await writer.ready;
|
await writer.ready;
|
||||||
const done = await packetParser.read(readable, streaming, async parsed => {
|
const done = await readPackets(readable, streaming, async parsed => {
|
||||||
try {
|
try {
|
||||||
const tag = enums.read(enums.packet, parsed.tag);
|
const tag = enums.read(enums.packet, parsed.tag);
|
||||||
const packet = packets.newPacketFromTag(tag, allowedPackets);
|
const packet = packets.newPacketFromTag(tag, allowedPackets);
|
||||||
|
@ -42,7 +46,7 @@ class PacketList extends Array {
|
||||||
await packet.read(parsed.packet, streaming);
|
await packet.read(parsed.packet, streaming);
|
||||||
await writer.write(packet);
|
await writer.write(packet);
|
||||||
} catch (e) {
|
} 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
|
// The packets that support streaming are the ones that contain
|
||||||
// message data. Those are also the ones we want to be more strict
|
// message data. Those are also the ones we want to be more strict
|
||||||
// about and throw on parse errors for.
|
// about and throw on parse errors for.
|
||||||
|
@ -71,7 +75,7 @@ class PacketList extends Array {
|
||||||
} else {
|
} else {
|
||||||
this.stream = null;
|
this.stream = null;
|
||||||
}
|
}
|
||||||
if (done || packetParser.supportsStreaming(value.tag)) {
|
if (done || supportsStreaming(value.tag)) {
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -88,31 +92,31 @@ class PacketList extends Array {
|
||||||
|
|
||||||
for (let i = 0; i < this.length; i++) {
|
for (let i = 0; i < this.length; i++) {
|
||||||
const packetbytes = this[i].write();
|
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 buffer = [];
|
||||||
let bufferLength = 0;
|
let bufferLength = 0;
|
||||||
const minLength = 512;
|
const minLength = 512;
|
||||||
arr.push(packetParser.writeTag(this[i].tag));
|
arr.push(writeTag(this[i].tag));
|
||||||
arr.push(stream.transform(packetbytes, value => {
|
arr.push(stream.transform(packetbytes, value => {
|
||||||
buffer.push(value);
|
buffer.push(value);
|
||||||
bufferLength += value.length;
|
bufferLength += value.length;
|
||||||
if (bufferLength >= minLength) {
|
if (bufferLength >= minLength) {
|
||||||
const powerOf2 = Math.min(Math.log(bufferLength) / Math.LN2 | 0, 30);
|
const powerOf2 = Math.min(Math.log(bufferLength) / Math.LN2 | 0, 30);
|
||||||
const chunkSize = 2 ** powerOf2;
|
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)];
|
buffer = [bufferConcat.subarray(1 + chunkSize)];
|
||||||
bufferLength = buffer[0].length;
|
bufferLength = buffer[0].length;
|
||||||
return bufferConcat.subarray(0, 1 + chunkSize);
|
return bufferConcat.subarray(0, 1 + chunkSize);
|
||||||
}
|
}
|
||||||
}, () => util.concat([packetParser.writeSimpleLength(bufferLength)].concat(buffer))));
|
}, () => util.concat([writeSimpleLength(bufferLength)].concat(buffer))));
|
||||||
} else {
|
} else {
|
||||||
if (util.isStream(packetbytes)) {
|
if (util.isStream(packetbytes)) {
|
||||||
let length = 0;
|
let length = 0;
|
||||||
arr.push(stream.transform(stream.clone(packetbytes), value => {
|
arr.push(stream.transform(stream.clone(packetbytes), value => {
|
||||||
length += value.length;
|
length += value.length;
|
||||||
}, () => packetParser.writeHeader(this[i].tag, length)));
|
}, () => writeHeader(this[i].tag, length)));
|
||||||
} else {
|
} else {
|
||||||
arr.push(packetParser.writeHeader(this[i].tag, packetbytes.length));
|
arr.push(writeHeader(this[i].tag, packetbytes.length));
|
||||||
}
|
}
|
||||||
arr.push(packetbytes);
|
arr.push(packetbytes);
|
||||||
}
|
}
|
||||||
|
|
|
@ -26,7 +26,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import packet from './packet';
|
import { readSimpleLength, writeSimpleLength } from './packet';
|
||||||
import type_keyid from '../type/keyid.js';
|
import type_keyid from '../type/keyid.js';
|
||||||
import type_mpi from '../type/mpi.js';
|
import type_mpi from '../type/mpi.js';
|
||||||
import crypto from '../crypto';
|
import crypto from '../crypto';
|
||||||
|
@ -319,7 +319,7 @@ class SignaturePacket {
|
||||||
arr.push(write_sub_packet(sub.issuerFingerprint, bytes));
|
arr.push(write_sub_packet(sub.issuerFingerprint, bytes));
|
||||||
}
|
}
|
||||||
this.unhashedSubpackets.forEach(data => {
|
this.unhashedSubpackets.forEach(data => {
|
||||||
arr.push(packet.writeSimpleLength(data.length));
|
arr.push(writeSimpleLength(data.length));
|
||||||
arr.push(data);
|
arr.push(data);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -535,7 +535,7 @@ class SignaturePacket {
|
||||||
|
|
||||||
// subpacket data set (zero or more subpackets)
|
// subpacket data set (zero or more subpackets)
|
||||||
while (i < 2 + subpacket_length) {
|
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;
|
i += len.offset;
|
||||||
|
|
||||||
this.read_sub_packet(bytes.subarray(i, i + len.len), trusted);
|
this.read_sub_packet(bytes.subarray(i, i + len.len), trusted);
|
||||||
|
@ -767,7 +767,7 @@ class SignaturePacket {
|
||||||
*/
|
*/
|
||||||
function write_sub_packet(type, data) {
|
function write_sub_packet(type, data) {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
arr.push(packet.writeSimpleLength(data.length + 1));
|
arr.push(writeSimpleLength(data.length + 1));
|
||||||
arr.push(new Uint8Array([type]));
|
arr.push(new Uint8Array([type]));
|
||||||
arr.push(data);
|
arr.push(data);
|
||||||
return util.concat(arr);
|
return util.concat(arr);
|
||||||
|
|
|
@ -21,7 +21,7 @@
|
||||||
* @requires util
|
* @requires util
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import packet from './packet';
|
import { readSimpleLength, writeSimpleLength } from './packet';
|
||||||
import enums from '../enums';
|
import enums from '../enums';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
|
@ -56,7 +56,7 @@ class UserAttributePacket {
|
||||||
read(bytes) {
|
read(bytes) {
|
||||||
let i = 0;
|
let i = 0;
|
||||||
while (i < bytes.length) {
|
while (i < bytes.length) {
|
||||||
const len = packet.readSimpleLength(bytes.subarray(i, bytes.length));
|
const len = readSimpleLength(bytes.subarray(i, bytes.length));
|
||||||
i += len.offset;
|
i += len.offset;
|
||||||
|
|
||||||
this.attributes.push(util.uint8ArrayToStr(bytes.subarray(i, i + len.len)));
|
this.attributes.push(util.uint8ArrayToStr(bytes.subarray(i, i + len.len)));
|
||||||
|
@ -71,7 +71,7 @@ class UserAttributePacket {
|
||||||
write() {
|
write() {
|
||||||
const arr = [];
|
const arr = [];
|
||||||
for (let i = 0; i < this.attributes.length; i++) {
|
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]));
|
arr.push(util.strToUint8Array(this.attributes[i]));
|
||||||
}
|
}
|
||||||
return util.concatUint8Array(arr);
|
return util.concatUint8Array(arr);
|
||||||
|
|
|
@ -22,7 +22,7 @@
|
||||||
* @module signature
|
* @module signature
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import armor from './encoding/armor';
|
import { armor, unarmor } from './encoding/armor';
|
||||||
import { PacketList, SignaturePacket } from './packet';
|
import { PacketList, SignaturePacket } from './packet';
|
||||||
import enums from './enums';
|
import enums from './enums';
|
||||||
|
|
||||||
|
@ -50,7 +50,7 @@ export class Signature {
|
||||||
* @returns {ReadableStream<String>} ASCII armor
|
* @returns {ReadableStream<String>} ASCII armor
|
||||||
*/
|
*/
|
||||||
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
|
* @static
|
||||||
*/
|
*/
|
||||||
export async function readArmoredSignature(armoredText) {
|
export async function readArmoredSignature(armoredText) {
|
||||||
const input = await armor.decode(armoredText);
|
const input = await unarmor(armoredText);
|
||||||
return readSignature(input.data);
|
return readSignature(input.data);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -30,7 +30,7 @@ import emailAddresses from 'email-addresses';
|
||||||
import stream from 'web-stream-tools';
|
import stream from 'web-stream-tools';
|
||||||
import config from './config';
|
import config from './config';
|
||||||
import util from './util'; // re-import module to access util functions
|
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';
|
import { getBigInteger } from './biginteger';
|
||||||
|
|
||||||
export default {
|
export default {
|
||||||
|
|
|
@ -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()) {
|
if (!openpgp.config.useIndutnyElliptic && !openpgp.util.getNodeCrypto()) {
|
||||||
this.skip();
|
this.skip();
|
||||||
}
|
}
|
||||||
if (openpgp.util.getNodeCrypto()) {
|
if (openpgp.util.getNodeCrypto()) {
|
||||||
expect(verify_signature(
|
await expect(verify_signature(
|
||||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||||
)).to.eventually.be.false;
|
)).to.eventually.be.false;
|
||||||
}
|
}
|
||||||
if (openpgp.config.useIndutnyElliptic) {
|
if (openpgp.config.useIndutnyElliptic) {
|
||||||
expect(verify_signature_elliptic(
|
await expect(verify_signature_elliptic(
|
||||||
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
'secp256k1', 8, [], [], [], secp256k1_invalid_point
|
||||||
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
|
)).to.be.rejectedWith(Error, /Invalid elliptic public key/);
|
||||||
}
|
}
|
||||||
|
|
|
@ -377,8 +377,8 @@ NJCB6+LWtabSoVIjNVgKwyKqyTLaESNwC2ogZwkdE8qPGiDFEHo4Gg9zuRof
|
||||||
-----END PGP PUBLIC KEY BLOCK-----
|
-----END PGP PUBLIC KEY BLOCK-----
|
||||||
`;
|
`;
|
||||||
|
|
||||||
const { type, data } = await openpgp.armor.decode(pubKey);
|
const { type, data } = await openpgp.unarmor(pubKey);
|
||||||
const armor = await openpgp.stream.readToEnd(openpgp.armor.encode(type, data));
|
const armor = await openpgp.stream.readToEnd(openpgp.armor(type, data));
|
||||||
expect(
|
expect(
|
||||||
armor
|
armor
|
||||||
.replace(/^(Version|Comment): .*$\r\n/mg, '')
|
.replace(/^(Version|Comment): .*$\r\n/mg, '')
|
||||||
|
|
|
@ -2584,35 +2584,9 @@ function versionSpecificTests() {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = () => describe('Key', function() {
|
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 rsaGenStub;
|
||||||
let v5KeysVal;
|
let v5KeysVal;
|
||||||
let aeadProtectVal;
|
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, {
|
tryTests('V4', versionSpecificTests, {
|
||||||
if: !openpgp.config.ci,
|
if: !openpgp.config.ci,
|
||||||
|
@ -2706,7 +2680,7 @@ module.exports = () => describe('Key', function() {
|
||||||
|
|
||||||
const packetlist = new openpgp.PacketList();
|
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();
|
const subkeys = pubKey.getSubkeys();
|
||||||
expect(subkeys).to.exist;
|
expect(subkeys).to.exist;
|
||||||
|
@ -3132,10 +3106,10 @@ module.exports = () => describe('Key', function() {
|
||||||
const revKey = await openpgp.readArmoredKey(revoked_key_arm4);
|
const revKey = await openpgp.readArmoredKey(revoked_key_arm4);
|
||||||
const revocationCertificate = await revKey.getRevocationCertificate();
|
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();
|
const packetlist = new openpgp.PacketList();
|
||||||
await packetlist.read(input.data, { SignaturePacket: openpgp.SignaturePacket });
|
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, ''));
|
expect(revocationCertificate.replace(/^Comment: .*$\r\n/mg, '')).to.equal(armored.replace(/^Comment: .*$\r\n/mg, ''));
|
||||||
});
|
});
|
||||||
|
|
|
@ -549,29 +549,6 @@ function withCompression(tests) {
|
||||||
}
|
}
|
||||||
|
|
||||||
module.exports = () => describe('OpenPGP.js public api tests', function() {
|
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() {
|
describe('generateKey - validate user ids', function() {
|
||||||
it('should fail for invalid user name', async function() {
|
it('should fail for invalid user name', async function() {
|
||||||
|
|
|
@ -243,6 +243,9 @@ module.exports = () => describe("Packet", function() {
|
||||||
it('Sym. encrypted AEAD protected packet test vector (AEAD)', async function() {
|
it('Sym. encrypted AEAD protected packet test vector (AEAD)', async function() {
|
||||||
// From https://gitlab.com/openpgp-wg/rfc4880bis/commit/00b20923e6233fb6ff1666ecd5acfefceb32907d
|
// From https://gitlab.com/openpgp-wg/rfc4880bis/commit/00b20923e6233fb6ff1666ecd5acfefceb32907d
|
||||||
|
|
||||||
|
const nodeCrypto = openpgp.util.getNodeCrypto();
|
||||||
|
if (!nodeCrypto) return;
|
||||||
|
|
||||||
let packetBytes = openpgp.util.hexToUint8Array(`
|
let packetBytes = openpgp.util.hexToUint8Array(`
|
||||||
d4 4a 01 07 01 0e b7 32 37 9f 73 c4 92 8d e2 5f
|
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
|
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();
|
const msg2 = new openpgp.PacketList();
|
||||||
|
|
||||||
let randomBytesStub = stub(openpgp.crypto.random, 'getRandomBytes');
|
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
|
||||||
randomBytesStub.returns(resolves(iv));
|
randomBytesStub.returns(iv);
|
||||||
|
|
||||||
try {
|
try {
|
||||||
await enc.encrypt(algo, key);
|
await enc.encrypt(algo, key);
|
||||||
|
@ -298,7 +301,7 @@ module.exports = () => describe("Packet", function() {
|
||||||
'=VZ0/\n' +
|
'=VZ0/\n' +
|
||||||
'-----END PGP MESSAGE-----';
|
'-----END PGP MESSAGE-----';
|
||||||
|
|
||||||
const msgbytes = (await openpgp.armor.decode(msg)).data;
|
const msgbytes = (await openpgp.unarmor(msg)).data;
|
||||||
|
|
||||||
const parsed = new openpgp.PacketList();
|
const parsed = new openpgp.PacketList();
|
||||||
await parsed.read(msgbytes, openpgp);
|
await parsed.read(msgbytes, openpgp);
|
||||||
|
@ -365,7 +368,7 @@ module.exports = () => describe("Packet", function() {
|
||||||
'-----END PGP PRIVATE KEY BLOCK-----';
|
'-----END PGP PRIVATE KEY BLOCK-----';
|
||||||
|
|
||||||
let key = new openpgp.PacketList();
|
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];
|
key = key[0];
|
||||||
|
|
||||||
const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
|
const enc = new openpgp.PublicKeyEncryptedSessionKeyPacket();
|
||||||
|
@ -432,11 +435,11 @@ module.exports = () => describe("Packet", function() {
|
||||||
'-----END PGP MESSAGE-----';
|
'-----END PGP MESSAGE-----';
|
||||||
|
|
||||||
let key = new openpgp.PacketList();
|
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];
|
key = key[3];
|
||||||
|
|
||||||
const msg = new openpgp.PacketList();
|
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 () => {
|
return msg[0].decrypt(key).then(async () => {
|
||||||
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
|
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() {
|
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
|
// 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 aeadProtectVal = openpgp.config.aeadProtect;
|
||||||
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
|
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
|
||||||
let s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte;
|
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 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 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');
|
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
|
||||||
randomBytesStub.onCall(0).returns(resolves(salt));
|
randomBytesStub.onCall(0).returns(salt);
|
||||||
randomBytesStub.onCall(1).returns(resolves(sessionKey));
|
randomBytesStub.onCall(1).returns(sessionKey);
|
||||||
randomBytesStub.onCall(2).returns(resolves(sessionIV));
|
randomBytesStub.onCall(2).returns(sessionIV);
|
||||||
randomBytesStub.onCall(3).returns(resolves(dataIV));
|
randomBytesStub.onCall(3).returns(dataIV);
|
||||||
|
|
||||||
let packetBytes = openpgp.util.hexToUint8Array(`
|
let packetBytes = openpgp.util.hexToUint8Array(`
|
||||||
c3 3e 05 07 01 03 08 cd 5a 9f 70 fb e0 bc 65 90
|
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() {
|
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
|
// 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 aeadProtectVal = openpgp.config.aeadProtect;
|
||||||
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
|
let aeadChunkSizeByteVal = openpgp.config.aeadChunkSizeByte;
|
||||||
let s2kIterationCountByteVal = openpgp.config.s2kIterationCountByte;
|
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 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 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');
|
let randomBytesStub = stub(nodeCrypto, 'randomBytes');
|
||||||
randomBytesStub.onCall(0).returns(resolves(salt));
|
randomBytesStub.onCall(0).returns(salt);
|
||||||
randomBytesStub.onCall(1).returns(resolves(sessionKey));
|
randomBytesStub.onCall(1).returns(sessionKey);
|
||||||
randomBytesStub.onCall(2).returns(resolves(sessionIV));
|
randomBytesStub.onCall(2).returns(sessionIV);
|
||||||
randomBytesStub.onCall(3).returns(resolves(dataIV));
|
randomBytesStub.onCall(3).returns(dataIV);
|
||||||
|
|
||||||
let packetBytes = openpgp.util.hexToUint8Array(`
|
let packetBytes = openpgp.util.hexToUint8Array(`
|
||||||
c3 3d 05 07 02 03 08 9f 0b 7d a3 e5 ea 64 77 90
|
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-----';
|
'-----END PGP MESSAGE-----';
|
||||||
|
|
||||||
let key = new openpgp.PacketList();
|
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];
|
key = key[3];
|
||||||
await key.decrypt('test');
|
await key.decrypt('test');
|
||||||
|
|
||||||
const msg = new openpgp.PacketList();
|
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 () => {
|
return msg[0].decrypt(key).then(async () => {
|
||||||
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
|
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() {
|
it('Secret key reading with signature verification.', async function() {
|
||||||
const key = new openpgp.PacketList();
|
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([
|
return Promise.all([
|
||||||
expect(key[2].verify(key[0],
|
expect(key[2].verify(key[0],
|
||||||
openpgp.enums.signature.certGeneric,
|
openpgp.enums.signature.certGeneric,
|
||||||
|
@ -736,11 +745,11 @@ module.exports = () => describe("Packet", function() {
|
||||||
'-----END PGP MESSAGE-----';
|
'-----END PGP MESSAGE-----';
|
||||||
|
|
||||||
const key = new openpgp.PacketList();
|
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');
|
await key[3].decrypt('test');
|
||||||
|
|
||||||
const msg = new openpgp.PacketList();
|
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 () => {
|
return msg[0].decrypt(key[3]).then(async () => {
|
||||||
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
|
await msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
|
||||||
|
|
Loading…
Reference in New Issue
Block a user