Merge pull request #654 from openpgpjs/feat/asmcrypto

Modernizes MPI handling and public key algorithms; jsbn.js is dead, long live bn.js!
This commit is contained in:
Bart Butler 2018-02-28 16:10:24 -08:00 committed by GitHub
commit 5fac00eddb
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
71 changed files with 1791 additions and 4815 deletions

3
.jsdocrc.js Normal file
View File

@ -0,0 +1,3 @@
module.exports = {
plugins: ['plugins/markdown']
};

View File

@ -52,6 +52,8 @@ module.exports = function(grunt) {
external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'],
transform: [
["babelify", {
global: true,
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules
plugins: ["transform-async-to-generator",
"syntax-async-functions",
"transform-regenerator",
@ -76,6 +78,8 @@ module.exports = function(grunt) {
external: ['crypto', 'zlib', 'node-localstorage', 'node-fetch', 'asn1.js', 'jwk-to-pem'],
transform: [
["babelify", {
global: true,
only: /^(?:.*\/node_modules\/asmcrypto\.js\/|(?!.*\/node_modules\/)).*$/, // Only babelify asmcrypto in node_modules
plugins: ["transform-async-to-generator",
"syntax-async-functions",
"transform-regenerator",
@ -177,6 +181,7 @@ module.exports = function(grunt) {
dist: {
src: ['README.md', 'src'],
options: {
configure: '.jsdocrc.js',
destination: 'doc',
recurse: true
}
@ -263,7 +268,7 @@ module.exports = function(grunt) {
// Load the plugin(s)
grunt.loadNpmTasks('grunt-browserify');
grunt.loadNpmTasks('grunt-contrib-uglify');
grunt.loadNpmTasks('grunt-contrib-uglify-es');
grunt.loadNpmTasks('grunt-text-replace');
grunt.loadNpmTasks('grunt-jsbeautifier');
grunt.loadNpmTasks('grunt-jsdoc');

View File

@ -5,7 +5,7 @@
"license": "LGPL-3.0+",
"homepage": "https://openpgpjs.org/",
"engines": {
"node": ">=0.8"
"node": ">= 8.0.0"
},
"keywords": [
"crypto",
@ -57,11 +57,10 @@
"grunt-contrib-clean": "~1.1.0",
"grunt-contrib-connect": "~1.0.2",
"grunt-contrib-copy": "~1.0.0",
"grunt-contrib-uglify": "~3.2.1",
"grunt-contrib-uglify-es": "^3.3.0",
"grunt-contrib-watch": "^1.0.0",
"grunt-jsbeautifier": "^0.2.13",
"grunt-jsdoc": "^2.2.1",
"grunt-keepalive": "^1.0.0",
"grunt-mocha-istanbul": "^5.0.2",
"grunt-mocha-test": "^0.13.3",
"grunt-saucelabs": "9.0.0",
@ -73,12 +72,12 @@
"whatwg-fetch": "^2.0.3"
},
"dependencies": {
"asmcrypto-lite": "github:openpgpjs/asmcrypto-lite",
"asmcrypto.js": "^0.22.0",
"asn1.js": "^5.0.0",
"bn.js": "^4.11.8",
"buffer": "^5.0.8",
"compressjs": "github:openpgpjs/compressjs.git",
"elliptic": "github:openpgpjs/elliptic.git",
"compressjs": "github:openpgpjs/compressjs",
"elliptic": "github:openpgpjs/elliptic",
"hash.js": "^1.1.3",
"jwk-to-pem": "^1.2.6",
"node-fetch": "^1.7.3",

View File

@ -126,7 +126,7 @@ function pack() {
return new Uint8Array(buffer);
}
module.exports = {
wrap: wrap,
unwrap: unwrap
export default {
wrap,
unwrap
};

View File

@ -1,8 +1,9 @@
/**
* @requires asmcrypto.js
* @module crypto/cipher/aes
*/
import asmCrypto from 'asmcrypto-lite';
import { AES_ECB } from 'asmcrypto.js/src/aes/ecb/exports';
// TODO use webCrypto or nodeCrypto when possible.
export default function aes(length) {
@ -11,12 +12,12 @@ export default function aes(length) {
this.encrypt = function(block) {
block = Uint8Array.from(block);
return Array.from(asmCrypto.AES_ECB.encrypt(block, this.key, false));
return Array.from(AES_ECB.encrypt(block, this.key, false));
};
this.decrypt = function(block) {
block = Uint8Array.from(block);
return Array.from(asmCrypto.AES_ECB.decrypt(block, this.key, false));
return Array.from(AES_ECB.decrypt(block, this.key, false));
};
};

View File

@ -436,7 +436,7 @@ function des_removePadding(message, padding) {
// added by Recurity Labs
function Des(key) {
function TripleDES(key) {
this.key = [];
for (let i = 0; i < 3; i++) {
@ -458,13 +458,12 @@ function Des(key) {
};
}
Des.keySize = Des.prototype.keySize = 24;
Des.blockSize = Des.prototype.blockSize = 8;
TripleDES.keySize = TripleDES.prototype.keySize = 24;
TripleDES.blockSize = TripleDES.prototype.blockSize = 8;
// This is "original" DES - Des is actually Triple DES.
// This is only exported so we can unit test.
// This is "original" DES
function OriginalDes(key) {
function DES(key) {
this.key = key;
this.encrypt = function(block, padding) {
@ -478,9 +477,4 @@ function OriginalDes(key) {
};
}
export default {
/** @static */
des: Des,
/** @static */
originalDes: OriginalDes
};
export default { DES, TripleDES };

View File

@ -1,26 +1,27 @@
/**
* @requires crypto/cipher/aes
* @requires crypto/cipher/blowfish
* @requires crypto/cipher/des
* @requires crypto/cipher/cast5
* @requires crypto/cipher/twofish
* @requires crypto/cipher/blowfish
* @module crypto/cipher
*/
import aes from './aes.js';
import desModule from './des.js';
import cast5 from './cast5.js';
import twofish from './twofish.js';
import blowfish from './blowfish.js';
import aes from './aes';
import des from './des.js';
import cast5 from './cast5';
import twofish from './twofish';
import blowfish from './blowfish';
export default {
/** @see module:crypto/cipher/aes */
aes128: aes(128),
aes192: aes(192),
aes256: aes(256),
/** @see module:crypto/cipher/des.originalDes */
des: desModule.originalDes,
/** @see module:crypto/cipher/des.des */
tripledes: desModule.des,
/** @see module:crypto/cipher/des~DES */
des: des.DES,
/** @see module:crypto/cipher/des~TripleDES */
tripledes: des.TripleDES,
/** @see module:crypto/cipher/cast5 */
cast5: cast5,
/** @see module:crypto/cipher/twofish */

View File

@ -18,24 +18,27 @@
// The GPG4Browsers crypto interface
/**
* @requires crypto/cipher
* @requires crypto/public_key
* @requires crypto/cipher
* @requires crypto/random
* @requires type/ecdh_symkey
* @requires type/kdf_params
* @requires type/mpi
* @requires type/oid
* @requires enums
* @requires util
* @module crypto/crypto
*/
import random from './random.js';
import cipher from './cipher';
import publicKey from './public_key';
import type_ecdh_symkey from '../type/ecdh_symkey.js';
import type_kdf_params from '../type/kdf_params.js';
import type_mpi from '../type/mpi.js';
import type_oid from '../type/oid.js';
import cipher from './cipher';
import random from './random';
import type_ecdh_symkey from '../type/ecdh_symkey';
import type_kdf_params from '../type/kdf_params';
import type_mpi from '../type/mpi';
import type_oid from '../type/oid';
import enums from '../enums';
import util from '../util';
function constructParams(types, data) {
return types.map(function(type, i) {
@ -48,41 +51,43 @@ function constructParams(types, data) {
export default {
/**
* Encrypts data using the specified public key multiprecision integers
* and the specified algorithm.
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1})
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} publicParams Algorithm dependent params
* @param {module:type/mpi} data Data to be encrypted as MPI
* @param {String} fingerprint Recipient fingerprint
* @return {Array<module:type/mpi|module:type/oid|module:type/kdf_params|module:type/ecdh_symkey>} encrypted session 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.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Array<module:type/mpi|
module:type/oid|
module:type/kdf_params>} pub_params Algorithm-specific public key parameters
* @param {module:type/mpi} data Data to be encrypted as MPI
* @param {String} fingerprint Recipient fingerprint
* @return {Array<module:type/mpi|
module:type/ecdh_symkey>} encrypted session key parameters
*/
publicKeyEncrypt: async function(algo, publicParams, data, fingerprint) {
publicKeyEncrypt: async function(algo, pub_params, data, fingerprint) {
const types = this.getEncSessionKeyParamTypes(algo);
return (async function() {
let m;
switch (algo) {
case 'rsa_encrypt':
case 'rsa_encrypt_sign': {
const rsa = new publicKey.rsa();
const n = publicParams[0].toBigInteger();
const e = publicParams[1].toBigInteger();
m = data.toBigInteger();
return constructParams(types, [rsa.encrypt(m, e, n)]);
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_encrypt_sign: {
const m = data.toBN();
const n = pub_params[0].toBN();
const e = pub_params[1].toBN();
const res = await publicKey.rsa.encrypt(m, n, e);
return constructParams(types, [res]);
}
case 'elgamal': {
const elgamal = new publicKey.elgamal();
const p = publicParams[0].toBigInteger();
const g = publicParams[1].toBigInteger();
const y = publicParams[2].toBigInteger();
m = data.toBigInteger();
return constructParams(types, elgamal.encrypt(m, g, p, y));
case enums.publicKey.elgamal: {
const m = data.toBN();
const p = pub_params[0].toBN();
const g = pub_params[1].toBN();
const y = pub_params[2].toBN();
const res = await publicKey.elgamal.encrypt(m, p, g, y);
return constructParams(types, [res.c1, res.c2]);
}
case 'ecdh': {
const { ecdh } = publicKey.elliptic;
const curve = publicParams[0];
const kdf_params = publicParams[2];
const R = publicParams[1].toBigInteger();
const res = await ecdh.encrypt(curve.oid, kdf_params.cipher, kdf_params.hash, data, R, fingerprint);
case enums.publicKey.ecdh: {
const oid = pub_params[0];
const Q = pub_params[1].toUint8Array();
const kdf_params = pub_params[2];
const res = await publicKey.elliptic.ecdh.encrypt(
oid, kdf_params.cipher, kdf_params.hash, data, Q, fingerprint);
return constructParams(types, [res.V, res.C]);
}
default:
@ -92,52 +97,50 @@ export default {
},
/**
* Decrypts data using the specified public key multiprecision integers of the private key,
* the specified secretMPIs of the private key and the specified algorithm.
* @param {module:enums.publicKey} algo Algorithm to be used (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1})
* @param {Array<module:type/mpi|module:type/oid|module:type/kdf_params>} keyIntegers Algorithm dependent params
* @param {Array<module:type/mpi|module:type/ecdh_symkey>} dataIntegers encrypted session key parameters
* @param {String} fingerprint Recipient fingerprint
* @return {module:type/mpi} returns a big integer containing the decrypted data; otherwise null
* Decrypts data using specified algorithm and private key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1} for public key algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {Array<module:type/mpi|
module:type/oid|
module:type/kdf_params>} key_params Algorithm-specific public, private key parameters
* @param {Array<module:type/mpi|
module:type/ecdh_symkey>}
data_params encrypted session key parameters
* @param {String} fingerprint Recipient fingerprint
* @return {module:type/mpi} An MPI containing the decrypted data
*/
publicKeyDecrypt: async function(algo, keyIntegers, dataIntegers, fingerprint) {
let p;
publicKeyDecrypt: async function(algo, key_params, data_params, fingerprint) {
return new type_mpi(await (async function() {
switch (algo) {
case 'rsa_encrypt_sign':
case 'rsa_encrypt': {
const rsa = new publicKey.rsa();
// 0 and 1 are the public key.
const n = keyIntegers[0].toBigInteger();
const e = keyIntegers[1].toBigInteger();
// 2 to 5 are the private key.
const d = keyIntegers[2].toBigInteger();
p = keyIntegers[3].toBigInteger();
const q = keyIntegers[4].toBigInteger();
const u = keyIntegers[5].toBigInteger();
const m = dataIntegers[0].toBigInteger();
return rsa.decrypt(m, n, e, d, p, q, u);
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_encrypt: {
const c = data_params[0].toBN();
const n = key_params[0].toBN(); // n = pq
const e = key_params[1].toBN();
const d = key_params[2].toBN(); // de = 1 mod (p-1)(q-1)
const p = key_params[3].toBN();
const q = key_params[4].toBN();
const u = key_params[5].toBN(); // q^-1 mod p
return publicKey.rsa.decrypt(c, n, e, d, p, q, u);
}
case 'elgamal': {
const elgamal = new publicKey.elgamal();
const x = keyIntegers[3].toBigInteger();
const c1 = dataIntegers[0].toBigInteger();
const c2 = dataIntegers[1].toBigInteger();
p = keyIntegers[0].toBigInteger();
return elgamal.decrypt(c1, c2, p, x);
case enums.publicKey.elgamal: {
const c1 = data_params[0].toBN();
const c2 = data_params[1].toBN();
const p = key_params[0].toBN();
const x = key_params[3].toBN();
return publicKey.elgamal.decrypt(c1, c2, p, x);
}
case 'ecdh': {
const { ecdh } = publicKey.elliptic;
const curve = keyIntegers[0];
const kdf_params = keyIntegers[2];
const V = dataIntegers[0].toBigInteger();
const C = dataIntegers[1].data;
const r = keyIntegers[3].toBigInteger();
return ecdh.decrypt(curve.oid, kdf_params.cipher, kdf_params.hash, V, C, r, fingerprint);
case enums.publicKey.ecdh: {
const oid = key_params[0];
const kdf_params = key_params[2];
const V = data_params[0].toUint8Array();
const C = data_params[1].data;
const d = key_params[3].toUint8Array();
return publicKey.elliptic.ecdh.decrypt(
oid, kdf_params.cipher, kdf_params.hash, V, C, d, fingerprint);
}
default:
return null;
throw new Error('Invalid public key encryption algorithm.');
}
}()));
},
@ -148,31 +151,31 @@ export default {
*/
getPrivKeyParamTypes: function(algo) {
switch (algo) {
case 'rsa_encrypt':
case 'rsa_encrypt_sign':
case 'rsa_sign':
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
// Algorithm-Specific Fields for RSA secret keys:
// - multiprecision integer (MPI) of RSA secret exponent d.
// - MPI of RSA secret prime value p.
// - MPI of RSA secret prime value q (p < q).
// - MPI of u, the multiplicative inverse of p, mod q.
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_sign:
return [type_mpi, type_mpi, type_mpi, type_mpi];
case 'elgamal':
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
// Algorithm-Specific Fields for Elgamal secret keys:
// - MPI of Elgamal secret exponent x.
case enums.publicKey.elgamal:
return [type_mpi];
case 'dsa':
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
// Algorithm-Specific Fields for DSA secret keys:
// - MPI of DSA secret exponent x.
case enums.publicKey.dsa:
return [type_mpi];
case 'ecdh':
case 'ecdsa':
case 'eddsa':
// Algorithm-Specific Fields for ECDSA or ECDH secret keys:
// - MPI of an integer representing the secret key.
// Algorithm-Specific Fields for ECDSA or ECDH secret keys:
// - MPI of an integer representing the secret key.
case enums.publicKey.ecdh:
case enums.publicKey.ecdsa:
case enums.publicKey.eddsa:
return [type_mpi];
default:
throw new Error('Unknown algorithm');
throw new Error('Invalid public key encryption algorithm.');
}
},
@ -181,41 +184,41 @@ export default {
* @return {Array<String>} The array of types
*/
getPubKeyParamTypes: function(algo) {
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
switch (algo) {
case 'rsa_encrypt':
case 'rsa_encrypt_sign':
case 'rsa_sign':
// Algorithm-Specific Fields for RSA public keys:
// - a multiprecision integer (MPI) of RSA public modulus n;
// - an MPI of RSA public encryption exponent e.
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_sign:
return [type_mpi, type_mpi];
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
case 'elgamal':
// Algorithm-Specific Fields for Elgamal public keys:
// - MPI of Elgamal prime p;
// - MPI of Elgamal group generator g;
// - MPI of Elgamal public key value y (= g**x mod p where x is secret).
case enums.publicKey.elgamal:
return [type_mpi, type_mpi, type_mpi];
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
case 'dsa':
// Algorithm-Specific Fields for DSA public keys:
// - MPI of DSA prime p;
// - MPI of DSA group order q (q is a prime divisor of p-1);
// - MPI of DSA group generator g;
// - MPI of DSA public-key value y (= g**x mod p where x is secret).
case enums.publicKey.dsa:
return [type_mpi, type_mpi, type_mpi, type_mpi];
// Algorithm-Specific Fields for ECDSA/EdDSA public keys:
// - OID of curve;
// - MPI of EC point representing public key.
case 'ecdsa':
case 'eddsa':
// Algorithm-Specific Fields for ECDSA/EdDSA public keys:
// - OID of curve;
// - MPI of EC point representing public key.
case enums.publicKey.ecdsa:
case enums.publicKey.eddsa:
return [type_oid, type_mpi];
// Algorithm-Specific Fields for ECDH public keys:
// - OID of curve;
// - MPI of EC point representing public key.
// - KDF: variable-length field containing KDF parameters.
case 'ecdh':
// Algorithm-Specific Fields for ECDH public keys:
// - OID of curve;
// - MPI of EC point representing public key.
// - KDF: variable-length field containing KDF parameters.
case enums.publicKey.ecdh:
return [type_oid, type_mpi, type_kdf_params];
default:
throw new Error('Unknown algorithm.');
throw new Error('Invalid public key encryption algorithm.');
}
},
@ -225,65 +228,67 @@ export default {
*/
getEncSessionKeyParamTypes: function(algo) {
switch (algo) {
// Algorithm-Specific Fields for RSA encrypted session keys:
// - MPI of RSA encrypted value m**e mod n.
case 'rsa_encrypt':
case 'rsa_encrypt_sign':
// Algorithm-Specific Fields for RSA encrypted session keys:
// - MPI of RSA encrypted value m**e mod n.
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_encrypt_sign:
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 'elgamal':
// 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 'ecdh':
// 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('Unknown algorithm.');
throw new Error('Invalid public key encryption algorithm.');
}
},
/** Generate algorithm-specific key parameters
* @param {String} algo The public key algorithm
* @return {Array} The array of parameters
* @param {String} algo The public key algorithm
* @param {Integer} bits Bit length for RSA keys
* @param {module:type/oid} oid Object identifier for ECC keys
* @return {Array} The array of parameters
*/
generateParams: function(algo, bits, curve) {
const types = this.getPubKeyParamTypes(algo).concat(this.getPrivKeyParamTypes(algo));
generateParams: function(algo, bits, oid) {
const types = [].concat(this.getPubKeyParamTypes(algo), this.getPrivKeyParamTypes(algo));
switch (algo) {
case 'rsa_encrypt':
case 'rsa_encrypt_sign':
case 'rsa_sign': {
//remember "publicKey" refers to the crypto/public_key dir
const rsa = new publicKey.rsa();
return rsa.generate(bits, "10001").then(function(keyObject) {
return constructParams(types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u]);
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_sign: {
return publicKey.rsa.generate(bits, "10001").then(function(keyObject) {
return constructParams(
types, [keyObject.n, keyObject.e, keyObject.d, keyObject.p, keyObject.q, keyObject.u]
);
});
}
case 'ecdsa':
case 'eddsa':
return publicKey.elliptic.generate(curve).then(function (keyObject) {
case enums.publicKey.dsa:
case enums.publicKey.elgamal:
throw new Error('Unsupported algorithm for key generation.');
case enums.publicKey.ecdsa:
case enums.publicKey.eddsa:
return publicKey.elliptic.generate(oid).then(function (keyObject) {
return constructParams(types, [keyObject.oid, keyObject.Q, keyObject.d]);
});
case 'ecdh':
return publicKey.elliptic.generate(curve).then(function (keyObject) {
case enums.publicKey.ecdh:
return publicKey.elliptic.generate(oid).then(function (keyObject) {
return constructParams(types, [keyObject.oid, keyObject.Q, [keyObject.hash, keyObject.cipher], keyObject.d]);
});
default:
throw new Error('Unsupported algorithm for key generation.');
throw new Error('Invalid public key algorithm.');
}
},
/**
* generate random byte prefix as string for the specified algorithm
* @param {module:enums.symmetric} algo Algorithm to use (see {@link https://tools.ietf.org/html/rfc4880#section-9.2|RFC 4880 9.2})
* @return {Uint8Array} Random bytes with length equal to the block
* 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
* @return {Uint8Array} Random bytes with length equal to the block
* size of the cipher
*/
getPrefixRandom: function(algo) {

View File

@ -18,10 +18,15 @@
/**
* @fileoverview This module wraps native AES-GCM en/decryption for both
* the WebCrypto api as well as node.js' crypto api.
* @requires asmcrypto.js
* @requires config
* @requires util
* @module crypto/gcm
*/
import asmCrypto from 'asmcrypto-lite';
import util from '../util.js';
import { AES_GCM } from 'asmcrypto.js/src/aes/gcm/exports';
import config from '../config';
import util from '../util';
const webCrypto = util.getWebCrypto(); // no GCM support in IE11, Safari 9
const nodeCrypto = util.getNodeCrypto();
@ -49,7 +54,7 @@ function encrypt(cipher, plaintext, key, iv) {
} else if (nodeCrypto) { // Node crypto library
return nodeEncrypt(plaintext, key, iv);
} // asm.js fallback
return Promise.resolve(asmCrypto.AES_GCM.encrypt(plaintext, key, iv));
return Promise.resolve(AES_GCM.encrypt(plaintext, key, iv));
}
/**
@ -70,7 +75,7 @@ function decrypt(cipher, ciphertext, key, iv) {
} else if (nodeCrypto) { // Node crypto library
return nodeDecrypt(ciphertext, key, iv);
} // asm.js fallback
return Promise.resolve(asmCrypto.AES_GCM.decrypt(ciphertext, key, iv));
return Promise.resolve(AES_GCM.decrypt(ciphertext, key, iv));
}
export default {

View File

@ -1,18 +1,20 @@
/**
* @requires crypto/hash/sha
* @requires rusha
* @requires asmcrypto.js
* @requires hash.js
* @requires crypto/hash/md5
* @requires util
* @module crypto/hash
*/
import Rusha from 'rusha';
import asmCrypto from 'asmcrypto-lite';
import { SHA256 } from 'asmcrypto.js/src/hash/sha256/exports';
import sha224 from 'hash.js/lib/hash/sha/224';
import sha384 from 'hash.js/lib/hash/sha/384';
import sha512 from 'hash.js/lib/hash/sha/512';
import { ripemd160 } from 'hash.js/lib/hash/ripemd';
import md5 from './md5';
import util from '../../util.js';
import util from '../../util';
const rusha = new Rusha();
const nodeCrypto = util.getNodeCrypto();
@ -28,7 +30,7 @@ function node_hash(type) {
function hashjs_hash(hash) {
return function(data) {
return util.str2Uint8Array(util.hex2bin(hash().update(data).digest('hex')));
return util.str_to_Uint8Array(util.hex_to_str(hash().update(data).digest('hex')));
};
}
@ -49,12 +51,12 @@ if (nodeCrypto) { // Use Node native crypto for all hash functions
md5: md5,
/** @see module:rusha */
sha1: function(data) {
return util.str2Uint8Array(util.hex2bin(rusha.digest(data)));
return util.str_to_Uint8Array(util.hex_to_str(rusha.digest(data)));
},
/** @see module:hash.js */
sha224: hashjs_hash(sha224),
/** @see module:asmcrypto */
sha256: asmCrypto.SHA256.bytes,
sha256: SHA256.bytes,
/** @see module:hash.js */
sha384: hashjs_hash(sha384),
// TODO, benchmark this vs asmCrypto's SHA512

View File

@ -24,8 +24,8 @@ import util from '../../util.js';
* @param {String} entree string to hash
*/
export default function(entree) {
const hex = md5(util.Uint8Array2str(entree));
const bin = util.str2Uint8Array(util.hex2bin(hex));
const hex = md5(util.Uint8Array_to_str(entree));
const bin = util.str_to_Uint8Array(util.hex_to_str(hex));
return bin;
}

File diff suppressed because it is too large Load Diff

View File

@ -15,6 +15,7 @@ import pkcs5 from './pkcs5.js';
import crypto from './crypto.js';
import aes_kw from './aes_kw.js';
// TODO move cfb and gcm to cipher
const mod = {
/** @see module:crypto/cipher */
cipher: cipher,

View File

@ -17,18 +17,15 @@
/**
* PKCS1 encoding
* @requires crypto/crypto
* @requires crypto/hash
* @requires crypto/public_key/jsbn
* @requires crypto/random
* @requires crypto/hash
* @requires util
* @module crypto/pkcs1
*/
import random from './random.js';
import util from '../util.js';
import BigInteger from './public_key/jsbn.js';
import random from './random';
import hash from './hash';
import util from '../util';
/**
* ASN1 object identifiers for hashes (See {@link https://tools.ietf.org/html/rfc4880#section-5.2.2})
@ -98,7 +95,7 @@ export default {
* @return {String} message, an octet string
*/
decode: function(EM) {
// leading zeros truncated by jsbn
// leading zeros truncated by bn.js
if (EM.charCodeAt(0) !== 0) {
EM = String.fromCharCode(0) + EM;
}
@ -128,7 +125,7 @@ export default {
encode: function(algo, M, emLen) {
let i;
// Apply the hash function to the message M to produce a hash value H
const H = util.Uint8Array2str(hash.digest(algo, util.str2Uint8Array(M)));
const H = util.Uint8Array_to_str(hash.digest(algo, util.str_to_Uint8Array(M)));
if (H.length !== hash.getHashByteLength(algo)) {
throw new Error('Invalid hash length');
}
@ -158,7 +155,7 @@ export default {
PS +
String.fromCharCode(0x00) +
T;
return new BigInteger(util.hexstrdump(EM), 16);
return util.str_to_hex(EM);
}
}
};

View File

@ -48,7 +48,4 @@ function decode(msg) {
throw new Error('Invalid padding');
}
module.exports = {
encode: encode,
decode: decode
};
export default { encode, decode };

View File

@ -18,111 +18,106 @@
// A Digital signature algorithm implementation
/**
* @requires bn.js
* @requires crypto/hash
* @requires crypto/public_key/jsbn
* @requires crypto/random
* @requires config
* @requires util
* @module crypto/public_key/dsa
*/
import BigInteger from './jsbn.js';
import random from '../random.js';
import hashModule from '../hash';
import util from '../../util.js';
import BN from 'bn.js';
import hash from '../hash';
import random from '../random';
import config from '../../config';
import util from '../../util';
export default function DSA() {
// s1 = ((g**s) mod p) mod q
// s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q)
function sign(hashalgo, m, g, p, q, x) {
const one = new BN(1);
const zero = new BN(0);
/*
TODO regarding the hash function, read:
https://tools.ietf.org/html/rfc4880#section-13.6
https://tools.ietf.org/html/rfc4880#section-14
*/
export default {
/*
* hash_algo is integer
* m is string
* g, p, q, x are all BN
* returns { r: BN, s: BN }
*/
sign: function(hash_algo, m, g, p, q, x) {
let k;
let r;
let s;
let t;
const redp = new BN.red(p);
const redq = new BN.red(q);
const gred = g.toRed(redp);
const xred = x.toRed(redq);
// If the output size of the chosen hash is larger than the number of
// bits of q, the hash result is truncated to fit by taking the number
// of leftmost bits equal to the number of bits of q. This (possibly
// truncated) hash function result is treated as a number and used
// directly in the DSA signature algorithm.
const hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength());
const hash = new BigInteger(util.hexstrdump(hashed_data), 16);
const h = new BN(
util.str_to_Uint8Array(
util.getLeftNBits(
util.Uint8Array_to_str(hash.digest(hash_algo, m)), q.bitLength())));
// FIPS-186-4, section 4.6:
// The values of r and s shall be checked to determine if r = 0 or s = 0.
// If either r = 0 or s = 0, a new value of k shall be generated, and the
// signature shall be recalculated. It is extremely unlikely that r = 0
// or s = 0 if signatures are generated properly.
let k;
let s1;
let s2;
while (true) {
k = random.getRandomBigIntegerInRange(BigInteger.ONE, q.subtract(BigInteger.ONE));
s1 = (g.modPow(k, p)).mod(q);
s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q);
if (s1 !== 0 && s2 !== 0) {
break;
// See Appendix B here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
k = random.getRandomBN(one, q); // returns in [1, q-1]
r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q
if (zero.cmp(r) === 0) {
continue;
}
t = h.add(x.mul(r)).toRed(redq); // H(m) + x*r mod q
s = k.toRed(redq).redInvm().redMul(t); // k**-1 * (H(m) + x*r) mod q
if (zero.cmp(s) === 0) {
continue;
}
break;
}
const result = [];
result[0] = s1.toMPI();
result[1] = s2.toMPI();
return result;
}
return { r: r.toArrayLike(Uint8Array),
s: s.toArrayLike(Uint8Array) };
},
function select_hash_algorithm(q) {
const usersetting = config.prefer_hash_algorithm;
/*
* 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash
* 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash
* 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
* 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
*/
switch (Math.round(q.bitLength() / 8)) {
case 20:
// 1024 bit
if (usersetting !== 2 &&
usersetting > 11 &&
usersetting !== 10 &&
usersetting < 8) {
return 2; // prefer sha1
}
return usersetting;
case 28:
// 2048 bit
if (usersetting > 11 &&
usersetting < 8) {
return 11;
}
return usersetting;
case 32:
// 4096 bit // prefer sha224
if (usersetting > 10 &&
usersetting < 8) {
return 8; // prefer sha256
}
return usersetting;
default:
util.print_debug("DSA select hash algorithm: returning null for an unknown length of q");
return null;
}
}
this.select_hash_algorithm = select_hash_algorithm;
function verify(hashalgo, s1, s2, m, p, q, g, y) {
const hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength());
const hash = new BigInteger(util.hexstrdump(hashed_data), 16);
if (BigInteger.ZERO.compareTo(s1) >= 0 ||
s1.compareTo(q) >= 0 ||
BigInteger.ZERO.compareTo(s2) >= 0 ||
s2.compareTo(q) >= 0) {
/*
* hash_algo is integer
* r, s are both BN
* m is string
* p, q, g, y are all BN
* returns BN
*/
verify: function(hash_algo, r, s, m, p, q, g, y) {
if (zero.ucmp(r) >= 0 || r.ucmp(q) >= 0 ||
zero.ucmp(s) >= 0 || s.ucmp(q) >= 0) {
util.print_debug("invalid DSA Signature");
return null;
}
const w = s2.modInverse(q);
if (BigInteger.ZERO.compareTo(w) === 0) {
const redp = new BN.red(p);
const redq = new BN.red(q);
const h = new BN(
util.str_to_Uint8Array(
util.getLeftNBits(
util.Uint8Array_to_str(hash.digest(hash_algo, m)), q.bitLength())));
const w = s.toRed(redq).redInvm(); // s**-1 mod q
if (zero.cmp(w) === 0) {
util.print_debug("invalid DSA Signature");
return null;
}
const u1 = hash.multiply(w).mod(q);
const u2 = s1.multiply(w).mod(q);
return g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).mod(q);
const u1 = h.toRed(redq).redMul(w); // H(m) * w mod q
const u2 = r.toRed(redq).redMul(w); // r * w mod q
const t1 = g.toRed(redp).redPow(u1.fromRed()); // g**u1 mod p
const t2 = y.toRed(redp).redPow(u2.fromRed()); // y**u2 mod p
const v = t1.redMul(t2).fromRed().mod(q); // (g**u1 * y**u2 mod p) mod q
return v.cmp(r) === 0;
}
this.sign = sign;
this.verify = verify;
}
};

View File

@ -18,39 +18,42 @@
// ElGamal implementation
/**
* @requires crypto/public_key/jsbn
* @requires bn.js
* @requires crypto/random
* @requires util
* @module crypto/public_key/elgamal
*/
import BigInteger from './jsbn.js';
import random from '../random.js';
import util from '../../util.js';
import BN from 'bn.js';
import random from '../random';
export default function Elgamal() {
function encrypt(m, g, p, y) {
// choose k in {2,...,p-2}
const pMinus2 = p.subtract(BigInteger.TWO);
let k = random.getRandomBigIntegerInRange(BigInteger.ONE, pMinus2);
k = k.mod(pMinus2).add(BigInteger.ONE);
const c = [];
c[0] = g.modPow(k, p);
c[1] = y.modPow(k, p).multiply(m).mod(p);
return c;
const zero = new BN(0);
export default {
/*
* m, p, g, y are all BN
* returns { c1: BN, c2: BN }
*/
encrypt: function(m, p, g, y) {
const redp = new BN.red(p);
const mred = m.toRed(redp);
const gred = g.toRed(redp);
const yred = y.toRed(redp);
// See Section 11.5 here: https://crypto.stanford.edu/~dabo/cryptobook/BonehShoup_0_4.pdf
const k = random.getRandomBN(zero, p); // returns in [0, p-1]
return {
c1: gred.redPow(k).fromRed(),
c2: yred.redPow(k).redMul(mred).fromRed()
};
},
/*
* c1, c2, p, x are all BN
* returns BN
*/
decrypt: function(c1, c2, p, x) {
const redp = new BN.red(p);
const c1red = c1.toRed(redp);
const c2red = c2.toRed(redp);
return c1red.redPow(x).redInvm().redMul(c2red).fromRed();
}
function decrypt(c1, c2, p, x) {
util.print_debug("Elgamal Decrypt:\nc1:" + util.hexstrdump(c1.toMPI()) + "\n" +
"c2:" + util.hexstrdump(c2.toMPI()) + "\n" +
"p:" + util.hexstrdump(p.toMPI()) + "\n" +
"x:" + util.hexstrdump(x.toMPI()));
return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p);
//var c = c1.pow(x).modInverse(p); // c0^-a mod p
//return c.multiply(c2).mod(p);
}
// signing and signature verification using Elgamal is not required by OpenPGP.
this.encrypt = encrypt;
this.decrypt = decrypt;
}
};

View File

@ -18,46 +18,45 @@
// Wrapper of an instance of an Elliptic Curve
/**
* @requires bn.js
* @requires elliptic
* @requires crypto/public_key/elliptic/key
* @requires crypto/public_key/jsbn
* @requires crypto/random
* @requires type/oid
* @requires enums
* @requires util
* @module crypto/public_key/elliptic/curve
*/
import BN from 'bn.js';
import { ec as EC, eddsa as EdDSA } from 'elliptic';
import { KeyPair } from './key';
import BigInteger from '../jsbn';
import KeyPair from './key';
import random from '../../random';
import enums from '../../../enums';
import util from '../../../util';
import OID from '../../../type/oid';
import base64 from '../../../encoding/base64';
const webCrypto = util.getWebCrypto();
const nodeCrypto = util.getNodeCrypto();
let webCurves = {};
let nodeCurves = {};
webCurves = {
const nodeCurves = {};
const webCurves = {
'p256': 'P-256',
'p384': 'P-384',
'p521': 'P-521'
};
if (nodeCrypto) {
const knownCurves = nodeCrypto.getCurves();
nodeCurves = {
'secp256k1': knownCurves.includes('secp256k1') ? 'secp256k1' : undefined,
'p256': knownCurves.includes('prime256v1') ? 'prime256v1' : undefined,
'p384': knownCurves.includes('secp384r1') ? 'secp384r1' : undefined,
'p521': knownCurves.includes('secp521r1') ? 'secp521r1' : undefined
// TODO add more here
};
nodeCurves.secp256k1 = knownCurves.includes('secp256k1') ? 'secp256k1' : undefined;
nodeCurves.p256 = knownCurves.includes('prime256v1') ? 'prime256v1' : undefined;
nodeCurves.p384 = knownCurves.includes('secp384r1') ? 'secp384r1' : undefined;
nodeCurves.p521 = knownCurves.includes('secp521r1') ? 'secp521r1' : undefined;
// TODO add more here
}
const curves = {
p256: {
oid: util.bin2str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]),
oid: util.Uint8Array_to_str([0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128,
@ -66,7 +65,7 @@ const curves = {
payloadSize: 32
},
p384: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x22]),
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x22]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha384,
cipher: enums.symmetric.aes192,
@ -75,7 +74,7 @@ const curves = {
payloadSize: 48
},
p521: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x23]),
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x23]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha512,
cipher: enums.symmetric.aes256,
@ -84,54 +83,68 @@ const curves = {
payloadSize: 66
},
secp256k1: {
oid: util.bin2str([0x2B, 0x81, 0x04, 0x00, 0x0A]),
oid: util.Uint8Array_to_str([0x2B, 0x81, 0x04, 0x00, 0x0A]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128,
node: false // FIXME when we replace jwk-to-pem or it supports this curve
},
ed25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]),
oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0xDA, 0x47, 0x0F, 0x01]),
keyType: enums.publicKey.eddsa,
hash: enums.hash.sha512,
payloadSize: 32
},
curve25519: {
oid: util.bin2str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]),
oid: util.Uint8Array_to_str([0x2B, 0x06, 0x01, 0x04, 0x01, 0x97, 0x55, 0x01, 0x05, 0x01]),
keyType: enums.publicKey.ecdsa,
hash: enums.hash.sha256,
cipher: enums.symmetric.aes128
},
brainpoolP256r1: { // TODO 1.3.36.3.3.2.8.1.1.7
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07])
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x07])
},
brainpoolP384r1: { // TODO 1.3.36.3.3.2.8.1.1.11
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B])
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0B])
},
brainpoolP512r1: { // TODO 1.3.36.3.3.2.8.1.1.13
oid: util.bin2str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D])
oid: util.Uint8Array_to_str([0x2B, 0x24, 0x03, 0x03, 0x02, 0x08, 0x01, 0x01, 0x0D])
}
};
function Curve(name, params) {
export default function Curve(oid_or_name, params) {
if (OID.prototype.isPrototypeOf(oid_or_name) &&
enums.curve[oid_or_name.toHex()]) {
this.name = oid_or_name.toHex(); // by curve OID
} else if (enums.curve[oid_or_name]) {
this.name = oid_or_name; // by curve name
} else if (enums.curve[util.str_to_hex(oid_or_name)]) {
this.name = util.str_to_hex(oid_or_name); // by oid string
} else {
throw new Error('Not valid curve');
}
this.name = enums.write(enums.curve, this.name);
this.oid = new OID(curves[this.name].oid);
params = params || curves[this.name];
this.keyType = params.keyType;
switch (this.keyType) {
case enums.publicKey.eddsa:
this.curve = new EdDSA(name);
this.curve = new EdDSA(this.name);
break;
case enums.publicKey.ecdsa:
this.curve = new EC(name);
this.curve = new EC(this.name);
break;
default:
throw new Error('Unknown elliptic key type;');
}
this.name = name;
this.oid = curves[name].oid;
this.hash = params.hash;
this.cipher = params.cipher;
this.node = params.node && curves[name].node;
this.web = params.web && curves[name].web;
this.payloadSize = curves[name].payloadSize;
this.node = params.node && curves[this.name].node;
this.web = params.web && curves[this.name].web;
this.payloadSize = curves[this.name].payloadSize;
}
Curve.prototype.keyFromPrivate = function (priv) { // Not for ed25519
@ -152,47 +165,33 @@ Curve.prototype.genKeyPair = async function () {
// If browser doesn't support a curve, we'll catch it
try {
keyPair = await webGenKeyPair(this.name);
return new KeyPair(this.curve, keyPair);
} catch (err) {
util.print_debug("Browser did not support signing: " + err.message);
}
} else if (nodeCrypto && this.node) {
keyPair = await nodeGenKeyPair(this.name);
return new KeyPair(this.curve, keyPair);
}
const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
const r = await this.curve.genKeyPair();
if (this.keyType === enums.publicKey.eddsa) {
keyPair = { secret: r.getSecret() };
} else {
keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() };
if (!keyPair || !keyPair.priv) {
// elliptic fallback
const r = await this.curve.genKeyPair({ entropy: util.Uint8Array_to_str(random.getRandomBytes(32)) });
const compact = this.curve.curve.type === 'edwards' || this.curve.curve.type === 'mont';
if (this.keyType === enums.publicKey.eddsa) {
keyPair = { secret: r.getSecret() };
} else {
keyPair = { pub: r.getPublic('array', compact), priv: r.getPrivate().toArray() };
}
}
return new KeyPair(this.curve, keyPair);
};
function get(oid_or_name) {
let name;
if (OID.prototype.isPrototypeOf(oid_or_name) &&
enums.curve[oid_or_name.toHex()]) {
name = enums.write(enums.curve, oid_or_name.toHex()); // by curve OID
return new Curve(name, curves[name]);
} else if (enums.curve[oid_or_name]) {
name = enums.write(enums.curve, oid_or_name); // by curve name
return new Curve(name, curves[name]);
} else if (enums.curve[util.hexstrdump(oid_or_name)]) {
name = enums.write(enums.curve, util.hexstrdump(oid_or_name)); // by oid string
return new Curve(name, curves[name]);
}
throw new Error('Not valid curve');
}
async function generate(curve) {
curve = get(curve);
curve = new Curve(curve);
const keyPair = await curve.genKeyPair();
return {
oid: curve.oid,
Q: new BigInteger(keyPair.getPublic()),
d: new BigInteger(keyPair.getPrivate()),
Q: new BN(keyPair.getPublic()),
d: new BN(keyPair.getPrivate()),
hash: curve.hash,
cipher: curve.cipher
};
@ -202,14 +201,8 @@ function getPreferredHashAlgo(oid) {
return curves[enums.write(enums.curve, oid.toHex())].hash;
}
module.exports = {
Curve,
curves,
webCurves,
nodeCurves,
getPreferredHashAlgo,
generate,
get
export {
curves, webCurves, nodeCurves, generate, getPreferredHashAlgo
};
@ -229,10 +222,10 @@ async function webGenKeyPair(name) {
return {
pub: {
x: base64.decode(publicKey.x, true),
y: base64.decode(publicKey.y, true)
x: util.b64_to_Uint8Array(publicKey.x, true),
y: util.b64_to_Uint8Array(publicKey.y, true)
},
priv: base64.decode(privateKey.d, true)
priv: util.b64_to_Uint8Array(privateKey.d, true)
};
}

View File

@ -19,10 +19,9 @@
/**
* @requires crypto/public_key/elliptic/curves
* @requires crypto/public_key/jsbn
* @requires crypto/aes_kw
* @requires crypto/cipher
* @requires crypto/hash
* @requires crypto/aes_kw
* @requires type/oid
* @requires type/kdf_params
* @requires enums
@ -30,26 +29,24 @@
* @module crypto/public_key/elliptic/ecdh
*/
import curves from './curves';
import BigInteger from '../jsbn';
import BN from 'bn.js';
import Curve from './curves';
import aes_kw from '../../aes_kw';
import cipher from '../../cipher';
import hash from '../../hash';
import aes_kw from '../../aes_kw';
import type_kdf_params from '../../../type/kdf_params';
import type_oid from '../../../type/oid';
import enums from '../../../enums';
import util from '../../../util';
// Build Param for ECDH algorithm (RFC 6637)
function buildEcdhParam(public_algo, oid, cipher_algo, hash_algo, fingerprint) {
oid = new type_oid(oid);
const kdf_params = new type_kdf_params([hash_algo, cipher_algo]);
return util.concatUint8Array([
oid.write(),
new Uint8Array([public_algo]),
kdf_params.write(),
util.str2Uint8Array("Anonymous Sender "),
util.str_to_Uint8Array("Anonymous Sender "),
fingerprint
]);
}
@ -67,26 +64,26 @@ function kdf(hash_algo, X, length, param) {
/**
* Encrypt and wrap a session key
*
* @param {String} oid OID of the curve to use
* @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash to use
* @param {Uint8Array} m Value derived from session key (RFC 6637)
* @param {BigInteger} Q Recipient public key
* @param {String} fingerprint Recipient fingerprint
* @return {{V: BigInteger, C: Uint8Array}} Returns ephemeral key and encoded session key
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash algorithm to use
* @param {module:type/mpi} m Value derived from session key (RFC 6637)
* @param {Uint8Array} Q Recipient public key
* @param {String} fingerprint Recipient fingerprint
* @return {{V: BN, C: BN}} Returns ephemeral key and encoded session key
*/
async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
fingerprint = util.hex2Uint8Array(fingerprint);
fingerprint = util.hex_to_Uint8Array(fingerprint);
const curve = new Curve(oid);
const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
const curve = curves.get(oid);
cipher_algo = enums.read(enums.symmetric, cipher_algo);
const v = await curve.genKeyPair();
Q = curve.keyFromPublic(Q.toByteArray());
Q = curve.keyFromPublic(Q);
const S = v.derive(Q);
const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param);
const C = aes_kw.wrap(Z, m.toBytes());
const C = aes_kw.wrap(Z, m.toString());
return {
V: new BigInteger(v.getPublic()),
V: new BN(v.getPublic()),
C: C
};
}
@ -94,30 +91,25 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
/**
* Decrypt and unwrap the value derived from session key
*
* @param {String} oid Curve OID
* @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash algorithm to use
* @param {BigInteger} V Public part of ephemeral key
* @param {Uint8Array} C Encrypted and wrapped value derived from session key
* @param {BigInteger} d Recipient private key
* @param {String} fingerprint Recipient fingerprint
* @return {Uint8Array} Value derived from session
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {Enums} cipher_algo Symmetric cipher to use
* @param {Enums} hash_algo Hash algorithm to use
* @param {BN} V Public part of ephemeral key
* @param {Uint8Array} C Encrypted and wrapped value derived from session key
* @param {Uint8Array} d Recipient private key
* @param {String} fingerprint Recipient fingerprint
* @return {Uint8Array} Value derived from session
*/
async function decrypt(oid, cipher_algo, hash_algo, V, C, d, fingerprint) {
fingerprint = util.hex2Uint8Array(fingerprint);
fingerprint = util.hex_to_Uint8Array(fingerprint);
const curve = new Curve(oid);
const param = buildEcdhParam(enums.publicKey.ecdh, oid, cipher_algo, hash_algo, fingerprint);
const curve = curves.get(oid);
cipher_algo = enums.read(enums.symmetric, cipher_algo);
V = curve.keyFromPublic(V.toByteArray());
d = curve.keyFromPrivate(d.toByteArray());
V = curve.keyFromPublic(V);
d = curve.keyFromPrivate(d);
const S = d.derive(V);
const Z = kdf(hash_algo, S, cipher[cipher_algo].keySize, param);
return new BigInteger(aes_kw.unwrap(Z, C));
return new BN(aes_kw.unwrap(Z, C));
}
module.exports = {
buildEcdhParam: buildEcdhParam,
kdf: kdf,
encrypt: encrypt,
decrypt: decrypt
};
export default { encrypt, decrypt };

View File

@ -19,51 +19,44 @@
/**
* @requires crypto/hash
* @requires crypto/public_key/jsbn
* @requires crypto/public_key/elliptic/curves
* @module crypto/public_key/elliptic/ecdsa
*/
import hash from '../../hash';
import curves from './curves';
import BigInteger from '../jsbn';
import Curve from './curves';
/**
* Sign a message using the provided key
* @param {String} oid Elliptic curve for the key
* @param {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign
* @param {BigInteger} d Private key used to sign
* @return {{r: BigInteger, s: BigInteger}} Signature of the message
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign
* @param {Uint8Array} d Private key used to sign the message
* @return {{r: Uint8Array,
s: Uint8Array}} Signature of the message
*/
async function sign(oid, hash_algo, m, d) {
const curve = curves.get(oid);
const key = curve.keyFromPrivate(d.toByteArray());
const curve = new Curve(oid);
const key = curve.keyFromPrivate(d);
const signature = await key.sign(m, hash_algo);
return {
r: new BigInteger(signature.r.toArray()),
s: new BigInteger(signature.s.toArray())
};
return { r: signature.r.toArrayLike(Uint8Array),
s: signature.s.toArrayLike(Uint8Array) };
}
/**
* Verifies if a signature is valid for a message
* @param {String} oid Elliptic curve for the key
* @param {enums.hash} hash_algo Hash algorithm used in the signature
* @param {{r: BigInteger, s: BigInteger}} signature Signature to verify
* @param {Uint8Array} m Message to verify
* @param {BigInteger} Q Public key used to verify the message
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used in the signature
* @param {{r: Uint8Array,
s: Uint8Array}} signature Signature to verify
* @param {Uint8Array} m Message to verify
* @param {Uint8Array} Q Public key used to verify the message
* @return {Boolean}
*/
async function verify(oid, hash_algo, signature, m, Q) {
const curve = curves.get(oid);
const key = curve.keyFromPublic(Q.toByteArray());
return key.verify(
m, { r: signature.r.toByteArray(), s: signature.s.toByteArray() }, hash_algo
);
const curve = new Curve(oid);
const key = curve.keyFromPublic(Q);
return key.verify(m, signature, hash_algo);
}
module.exports = {
sign: sign,
verify: verify
};
export default { sign, verify };

View File

@ -26,49 +26,40 @@
import BN from 'bn.js';
import hash from '../../hash';
import curves from './curves';
import Curve from './curves';
/**
* Sign a message using the provided key
* @param {String} oid Elliptic curve for the key
* @param {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign
* @param {BigInteger} d Private key used to sign
* @return {{R: BN, S: BN}} Signature of the message
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used to sign
* @param {Uint8Array} m Message to sign
* @param {Uint8Array} d Private key used to sign
* @return {{R: Uint8Array,
S: Uint8Array}} Signature of the message
*/
async function sign(oid, hash_algo, m, d) {
const curve = curves.get(oid);
const key = curve.keyFromSecret(d.toByteArray());
const curve = new Curve(oid);
const key = curve.keyFromSecret(d);
const signature = await key.sign(m, hash_algo);
// EdDSA signature params are returned in little-endian format
return {
R: new BN(Array.from(signature.Rencoded()).reverse()),
S: new BN(Array.from(signature.Sencoded()).reverse())
};
return { R: new Uint8Array(signature.Rencoded()),
S: new Uint8Array(signature.Sencoded()) };
}
/**
* Verifies if a signature is valid for a message
* @param {String} oid Elliptic curve for the key
* @param {enums.hash} hash_algo Hash algorithm used in the signature
* @param {{R: BigInteger, S: BigInteger}} signature Signature to verify
* @param {Uint8Array} m Message to verify
* @param {BigInteger} Q Public key used to verify the message
* @param {module:type/oid} oid Elliptic curve object identifier
* @param {enums.hash} hash_algo Hash algorithm used in the signature
* @param {{R: Uint8Array,
S: Uint8Array}} signature Signature to verify the message
* @param {Uint8Array} m Message to verify
* @param {Uint8Array} Q Public key used to verify the message
* @return {Boolean}
*/
async function verify(oid, hash_algo, signature, m, Q) {
const curve = curves.get(oid);
const key = curve.keyFromPublic(Q.toByteArray());
// EdDSA signature params are expected in little-endian format
const R = Array.from(signature.R.toByteArray()).reverse();
const S = Array.from(signature.S.toByteArray()).reverse();
return key.verify(m, {
R: [].concat(R, Array(curve.payloadSize - R.length).fill(0)),
S: [].concat(S, Array(curve.payloadSize - S.length).fill(0))
}, hash_algo);
const curve = new Curve(oid);
const key = curve.keyFromPublic(Q);
return key.verify(m, signature, hash_algo);
}
module.exports = {
sign: sign,
verify: verify
};
export default { sign, verify };

View File

@ -25,16 +25,11 @@
* @module crypto/public_key/elliptic
*/
import { get, generate, getPreferredHashAlgo } from './curves';
import Curve, { generate, getPreferredHashAlgo } from './curves';
import ecdsa from './ecdsa';
import eddsa from './eddsa';
import ecdh from './ecdh';
module.exports = {
ecdsa: ecdsa,
eddsa: eddsa,
ecdh: ecdh,
get: get,
generate: generate,
getPreferredHashAlgo: getPreferredHashAlgo
export default {
Curve, ecdh, ecdsa, eddsa, generate, getPreferredHashAlgo
};

View File

@ -18,32 +18,24 @@
// Wrapper for a KeyPair of an Elliptic Curve
/**
* @requires bn.js
* @requires crypto/public_key/elliptic/curves
* @requires crypto/public_key/jsbn
* @requires crypto/hash
* @requires util
* @requires enums
* @requires encoding/base64
* @requires jwk-to-pem
* @requires asn1.js
* @module crypto/public_key/elliptic/key
*/
import curves from './curves';
import BigInteger from '../jsbn';
import BN from 'bn.js';
import { webCurves, nodeCurves } from './curves';
import hash from '../../hash';
import util from '../../../util';
import enums from '../../../enums';
import base64 from '../../../encoding/base64';
const webCrypto = util.getWebCrypto();
const { webCurves } = curves;
const nodeCrypto = util.getNodeCrypto();
const { nodeCurves } = curves;
// const webCrypto = util.getWebCrypto();
// const nodeCrypto = util.getNodeCrypto();
// const { webCurves, nodeCurves } = curves;
const jwkToPem = nodeCrypto ? require('jwk-to-pem') : undefined;
const ECDSASignature = nodeCrypto ?
@ -54,16 +46,13 @@ const ECDSASignature = nodeCrypto ?
);
}) : undefined;
function KeyPair(curve, options) {
export default function KeyPair(curve, options) {
this.curve = curve;
this.keyType = curve.curve.type === 'edwards' ? enums.publicKey.eddsa : enums.publicKey.ecdsa;
this.keyPair = this.curve.keyPair(options);
}
KeyPair.prototype.sign = async function (message, hash_algo) {
if (util.isString(message)) {
message = util.str2Uint8Array(message);
}
if (webCrypto && this.curve.web) {
// If browser doesn't support a curve, we'll catch it
try {
@ -79,9 +68,6 @@ KeyPair.prototype.sign = async function (message, hash_algo) {
};
KeyPair.prototype.verify = async function (message, signature, hash_algo) {
if (util.isString(message)) {
message = util.str2Uint8Array(message);
}
if (webCrypto && this.curve.web) {
// If browser doesn't support a curve, we'll catch it
try {
@ -115,10 +101,6 @@ KeyPair.prototype.getPrivate = function () {
return this.keyPair.getPrivate().toArray();
};
module.exports = {
KeyPair: KeyPair
};
//////////////////////////
// //
@ -134,9 +116,9 @@ async function webSign(curve, hash_algo, message, keyPair) {
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), true),
"y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), true),
"d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray('be', l)), true),
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray('be', l)), true),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray('be', l)), true),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray('be', l)), true),
"use": "sig",
"kid": "ECDSA Private Key"
},
@ -174,8 +156,8 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) {
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": base64.encode(new Uint8Array(publicKey.getX().toArray('be', l)), true),
"y": base64.encode(new Uint8Array(publicKey.getY().toArray('be', l)), true),
"x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray('be', l)), true),
"y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray('be', l)), true),
"use": "sig",
"kid": "ECDSA Public Key"
},
@ -202,13 +184,23 @@ async function webVerify(curve, hash_algo, { r, s }, message, publicKey) {
async function nodeSign(curve, hash_algo, message, keyPair) {
console.log({
"kty": "EC",
"crv": webCurves[curve.name],
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())),
"use": "sig",
"kid": "ECDSA Private Key"
});
const key = jwkToPem(
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": base64.encode(new Uint8Array(keyPair.getPublic().getX().toArray())),
"y": base64.encode(new Uint8Array(keyPair.getPublic().getY().toArray())),
"d": base64.encode(new Uint8Array(keyPair.getPrivate().toArray())),
"x": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPublic().getY().toArray())),
"d": util.Uint8Array_to_b64(new Uint8Array(keyPair.getPrivate().toArray())),
"use": "sig",
"kid": "ECDSA Private Key"
},
@ -226,13 +218,13 @@ async function nodeSign(curve, hash_algo, message, keyPair) {
}
async function nodeVerify(curve, hash_algo, { r, s }, message, publicKey) {
const signature = ECDSASignature.encode({ r: new BigInteger(r), s: new BigInteger(s) }, 'der');
const signature = ECDSASignature.encode({ r: new BN(r), s: new BN(s) }, 'der');
const key = jwkToPem(
{
"kty": "EC",
"crv": webCurves[curve.name],
"x": base64.encode(new Uint8Array(publicKey.getX().toArray())),
"y": base64.encode(new Uint8Array(publicKey.getY().toArray())),
"x": util.Uint8Array_to_b64(new Uint8Array(publicKey.getX().toArray())),
"y": util.Uint8Array_to_b64(new Uint8Array(publicKey.getY().toArray())),
"use": "sig",
"kid": "ECDSA Public Key"
},

View File

@ -7,13 +7,13 @@
*/
/** @see module:crypto/public_key/rsa */
import rsa from './rsa.js';
import rsa from './rsa';
/** @see module:crypto/public_key/elgamal */
import elgamal from './elgamal.js';
import elgamal from './elgamal';
/** @see module:crypto/public_key/elliptic */
import elliptic from './elliptic';
/** @see module:crypto/public_key/dsa */
import dsa from './dsa.js';
import dsa from './dsa';
export default {
rsa: rsa,

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,179 @@
// OpenPGP.js - An OpenPGP implementation in javascript
// Copyright (C) 2018 Proton Technologies AG
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 3.0 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// Algorithms for probabilistic random prime generation
/**
* @requires bn.js
* @requires crypto/random
* @module crypto/public_key/prime
*/
import BN from 'bn.js';
import random from '../random';
export default {
randomProbablePrime, isProbablePrime, fermat, millerRabin
};
/**
* Probabilistic random number generator
* @param {Integer} bits Bit length of the prime
* @param {BN} e Optional RSA exponent to check against the prime
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return BN
*/
function randomProbablePrime(bits, e, k) {
const min = new BN(1).shln(bits - 1);
let n = random.getRandomBN(min, min.shln(1));
if (n.isEven()) {
n.iaddn(1); // force odd
}
while (!isProbablePrime(n, e, k)) {
n.iaddn(2);
// If reached the maximum, go back to the minimum.
if (n.bitLength() > bits) {
n = n.mod(min.shln(1)).iadd(min);
}
}
return n;
}
/**
* Probabilistic primality testing
* @param {BN} n Number to test
* @param {BN} e Optional RSA exponent to check against the prime
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @return {boolean}
*/
function isProbablePrime(n, e, k) {
if (e && !n.subn(1).gcd(e).eqn(1)) {
return false;
}
if (!fermat(n)) {
return false;
}
if (!millerRabin(n, k, () => new BN(lowprimes[Math.random() * lowprimes.length | 0]))) {
return false;
}
if (!millerRabin(n, k)) {
return false;
}
// TODO implement the Lucas test
// See Section C.3.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
return true;
}
/**
* Tests whether n is probably prime or not using Fermat's test with b = 2.
* Fails if b^(n-1) mod n === 1.
* @param {BN} n Number to test
* @param {Integer} b Optional Fermat test base
* @return {boolean}
*/
function fermat(n, b) {
b = b || new BN(2);
return b.toRed(BN.mont(n)).redPow(n.subn(1)).fromRed().cmpn(1) === 0;
}
const lowprimes = [
2, 3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, 67, 71,
73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, 137, 139, 149, 151, 157, 163, 167, 173,
179, 181, 191, 193, 197, 199, 211, 223, 227, 229, 233, 239, 241, 251, 257, 263, 269, 271, 277, 281,
283, 293, 307, 311, 313, 317, 331, 337, 347, 349, 353, 359, 367, 373, 379, 383, 389, 397, 401, 409,
419, 421, 431, 433, 439, 443, 449, 457, 461, 463, 467, 479, 487, 491, 499, 503, 509, 521, 523, 541,
547, 557, 563, 569, 571, 577, 587, 593, 599, 601, 607, 613, 617, 619, 631, 641, 643, 647, 653, 659,
661, 673, 677, 683, 691, 701, 709, 719, 727, 733, 739, 743, 751, 757, 761, 769, 773, 787, 797, 809,
811, 821, 823, 827, 829, 839, 853, 857, 859, 863, 877, 881, 883, 887, 907, 911, 919, 929, 937, 941,
947, 953, 967, 971, 977, 983, 991, 997];
// Miller-Rabin - Miller Rabin algorithm for primality test
// Copyright Fedor Indutny, 2014.
//
// This software is licensed under the MIT License.
//
// Permission is hereby granted, free of charge, to any person obtaining a
// copy of this software and associated documentation files (the
// "Software"), to deal in the Software without restriction, including
// without limitation the rights to use, copy, modify, merge, publish,
// distribute, sublicense, and/or sell copies of the Software, and to permit
// persons to whom the Software is furnished to do so, subject to the
// following conditions:
//
// The above copyright notice and this permission notice shall be included
// in all copies or substantial portions of the Software.
//
// THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS
// OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
// MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN
// NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM,
// DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR
// OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE
// USE OR OTHER DEALINGS IN THE SOFTWARE.
// Adapted on Jan 2018 from version 4.0.1 at https://github.com/indutny/miller-rabin
/**
* Tests whether n is probably prime or not using the Miller-Rabin test.
* See HAC Remark 4.28.
* @param {BN} n Number to test
* @param {Integer} k Optional number of iterations of Miller-Rabin test
* @param {Function} rand Optional function to generate potential witnesses
* @return {boolean}
*/
function millerRabin(n, k, rand) {
const len = n.bitLength();
const red = BN.mont(n);
const rone = new BN(1).toRed(red);
if (!k)
k = Math.max(1, (len / 48) | 0);
const n1 = n.subn(1);
const rn1 = n1.toRed(red);
// Find d and s, (n - 1) = (2 ^ s) * d;
let s = 0;
while (!n1.testn(s)) { s++; }
const d = n.shrn(s);
for (; k > 0; k--) {
let a = rand ? rand() : random.getRandomBN(new BN(2), n1);
let x = a.toRed(red).redPow(d);
if (x.eq(rone) || x.eq(rn1))
continue;
let i;
for (i = 1; i < s; i++) {
x = x.redSqr();
if (x.eq(rone))
return false;
if (x.eq(rn1))
break;
}
if (i === s)
return false;
}
return true;
};

View File

@ -18,254 +18,211 @@
// RSA implementation
/**
* @requires crypto/public_key/jsbn
* @requires bn.js
* @requires crypto/public_key/prime
* @requires crypto/random
* @requires config
* @requires util
* @module crypto/public_key/rsa
*/
import BigInteger from './jsbn.js';
import util from '../../util.js';
import random from '../random.js';
import BN from 'bn.js';
import prime from './prime';
import random from '../random';
import config from '../../config';
import util from '../../util';
function SecureRandom() {
function nextBytes(byteArray) {
for (let n = 0; n < byteArray.length; n++) {
byteArray[n] = random.getSecureRandomOctet();
}
// Helper for IE11 KeyOperation objects
function promisifyIE11Op(keyObj, err) {
if (typeof keyObj.then !== 'function') { // IE11 KeyOperation
return new Promise(function(resolve, reject) {
keyObj.onerror = function () {
reject(new Error(err));
};
keyObj.oncomplete = function (e) {
resolve(e.target.result);
};
});
}
this.nextBytes = nextBytes;
return keyObj;
}
let blinder = BigInteger.ZERO;
let unblinder = BigInteger.ZERO;
function blind(m, n, e) {
if (unblinder.bitLength() === n.bitLength()) {
unblinder = unblinder.square().mod(n);
} else {
unblinder = random.getRandomBigIntegerInRange(BigInteger.TWO, n);
}
blinder = unblinder.modInverse(n).modPow(e, n);
return m.multiply(blinder).mod(n);
}
function unblind(t, n) {
return t.multiply(unblinder).mod(n);
}
export default function RSA() {
/**
* This function uses jsbn Big Num library to decrypt RSA
* @param m
* message
* @param n
* RSA public modulus n as BigInteger
* @param e
* RSA public exponent as BigInteger
* @param d
* RSA d as BigInteger
* @param p
* RSA p as BigInteger
* @param q
* RSA q as BigInteger
* @param u
* RSA u as BigInteger
* @return {BigInteger} The decrypted value of the message
export default {
/** Create signature
* @param m message as BN
* @param n public MPI part as BN
* @param e public MPI part as BN
* @param d private MPI part as BN
* @return BN
*/
function decrypt(m, n, e, d, p, q, u) {
if (config.rsa_blinding) {
m = blind(m, n, e);
sign: function(m, n, e, d) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
const xp = m.mod(p).modPow(d.mod(p.subtract(BigInteger.ONE)), p);
const xq = m.mod(q).modPow(d.mod(q.subtract(BigInteger.ONE)), q);
util.print_debug("rsa.js decrypt\nxpn:" + util.hexstrdump(xp.toMPI()) + "\nxqn:" + util.hexstrdump(xq.toMPI()));
let t = xq.subtract(xp);
if (t[0] === 0) {
t = xp.subtract(xq);
t = t.multiply(u).mod(q);
t = q.subtract(t);
} else {
t = t.multiply(u).mod(q);
}
t = t.multiply(p).add(xp);
if (config.rsa_blinding) {
t = unblind(t, n);
}
return t;
}
const nred = new BN.red(n);
return m.toRed(nred).redPow(d).toArrayLike(Uint8Array, 'be', n.byteLength());
},
/**
* encrypt message
* @param m message as BigInteger
* @param e public MPI part as BigInteger
* @param n public MPI part as BigInteger
* @return BigInteger
* Verify signature
* @param s signature as BN
* @param n public MPI part as BN
* @param e public MPI part as BN
* @return BN
*/
function encrypt(m, e, n) {
return m.modPowInt(e, n);
}
verify: function(s, n, e) {
if (n.cmp(s) <= 0) {
throw new Error('Data too large.');
}
const nred = new BN.red(n);
return s.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength());
},
/* Sign and Verify */
function sign(m, d, n) {
return m.modPow(d, n);
}
/**
* Encrypt message
* @param m message as BN
* @param n public MPI part as BN
* @param e public MPI part as BN
* @return BN
*/
encrypt: function(m, n, e) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
const nred = new BN.red(n);
return m.toRed(nred).redPow(e).toArrayLike(Uint8Array, 'be', n.byteLength());
},
function verify(x, e, n) {
return x.modPowInt(e, n);
}
/**
* Decrypt RSA message
* @param m message as BN
* @param n RSA public modulus n as BN
* @param e RSA public exponent as BN
* @param d RSA d as BN
* @param p RSA p as BN
* @param q RSA q as BN
* @param u RSA u as BN
* @return {BN} The decrypted value of the message
*/
decrypt: function(m, n, e, d, p, q, u) {
if (n.cmp(m) <= 0) {
throw new Error('Data too large.');
}
const dq = d.mod(q.subn(1)); // d mod (q-1)
const dp = d.mod(p.subn(1)); // d mod (p-1)
const pred = new BN.red(p);
const qred = new BN.red(q);
const nred = new BN.red(n);
// "empty" RSA key constructor
let blinder;
let unblinder;
if (config.rsa_blinding) {
unblinder = random.getRandomBN(new BN(2), n).toRed(nred);
blinder = unblinder.redInvm().redPow(e);
m = m.toRed(nred).redMul(blinder).fromRed();
}
function KeyObject() {
this.n = null;
this.e = 0;
this.ee = null;
this.d = null;
this.p = null;
this.q = null;
this.dmp1 = null;
this.dmq1 = null;
this.u = null;
}
const mp = m.toRed(pred).redPow(dp);
const mq = m.toRed(qred).redPow(dq);
const t = mq.redSub(mp.fromRed().toRed(qred));
const h = u.toRed(qred).redMul(t).fromRed();
// Generate a new random private key B bits long, using public expt E
let result = h.mul(p).add(mp).toRed(nred);
function generate(B, E) {
if (config.rsa_blinding) {
result = result.redMul(unblinder);
}
return result.toArrayLike(Uint8Array, 'be', n.byteLength());
},
/**
* Generate a new random private key B bits long with public exponent E
* @param {Integer} B RSA bit length
* @param {String} E RSA public exponent in hex string
* @return {{n: BN, e: BN, d: BN,
p: BN, q: BN, u: BN}} RSA public modulus, RSA public exponent, RSA private exponent,
RSA private prime p, RSA private prime q, u = q ** -1 mod p
*/
generate: async function(B, E) {
let key;
E = new BN(E, 16);
const webCrypto = util.getWebCryptoAll();
//
// Native RSA keygen using Web Crypto
//
if (webCrypto) {
const Euint32 = new Uint32Array([parseInt(E, 16)]); // get integer of exponent
const Euint8 = new Uint8Array(Euint32.buffer); // get bytes of exponent
let keyPair;
let keyGenOpt;
let keys;
if ((window.crypto && window.crypto.subtle) || window.msCrypto) {
// current standard spec
keyGenOpt = {
name: 'RSASSA-PKCS1-v1_5',
modulusLength: B, // the specified keysize in bits
publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537)
publicExponent: E.toArrayLike(Uint8Array), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keys = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
if (typeof keys.then !== 'function') { // IE11 KeyOperation
keys = util.promisifyIE11Op(keys, 'Error generating RSA key pair.');
}
}
else if (window.crypto && window.crypto.webkitSubtle) {
keyPair = webCrypto.generateKey(keyGenOpt, true, ['sign', 'verify']);
keyPair = await promisifyIE11Op(keyPair, 'Error generating RSA key pair.');
} else if (window.crypto && window.crypto.webkitSubtle) {
// outdated spec implemented by old Webkit
keyGenOpt = {
name: 'RSA-OAEP',
modulusLength: B, // the specified keysize in bits
publicExponent: Euint8.subarray(0, 3), // take three bytes (max 65537)
publicExponent: E.toArrayLike(Uint8Array), // take three bytes (max 65537) for exponent
hash: {
name: 'SHA-1' // not required for actual RSA keys, but for crypto api 'sign' and 'verify'
}
};
keys = webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']);
}
else {
keyPair = await webCrypto.generateKey(keyGenOpt, true, ['encrypt', 'decrypt']);
} else {
throw new Error('Unknown WebCrypto implementation');
}
return keys.then(exportKey).then(function(key) {
if (key instanceof ArrayBuffer) {
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
return decodeKey(JSON.parse(String.fromCharCode.apply(null, new Uint8Array(key))));
}
return decodeKey(key);
});
}
function exportKey(keypair) {
// export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
let key = webCrypto.exportKey('jwk', keypair.privateKey);
if (typeof key.then !== 'function') { // IE11 KeyOperation
key = util.promisifyIE11Op(key, 'Error exporting RSA key pair.');
let jwk = webCrypto.exportKey('jwk', keyPair.privateKey);
jwk = await promisifyIE11Op(jwk, 'Error exporting RSA key pair.');
// parse raw ArrayBuffer bytes to jwk/json (WebKit/Safari/IE11 quirk)
if (jwk instanceof ArrayBuffer) {
jwk = JSON.parse(String.fromCharCode.apply(null, new Uint8Array(jwk)));
}
// map JWK parameters to BN
key = {};
key.n = new BN(util.b64_to_Uint8Array(jwk.n));
key.e = E;
key.d = new BN(util.b64_to_Uint8Array(jwk.d));
key.p = new BN(util.b64_to_Uint8Array(jwk.p));
key.q = new BN(util.b64_to_Uint8Array(jwk.q));
key.u = key.p.invm(key.q);
return key;
}
function decodeKey(jwk) {
// map JWK parameters to local BigInteger type system
const key = new KeyObject();
key.n = toBigInteger(jwk.n);
key.ee = new BigInteger(E, 16);
key.d = toBigInteger(jwk.d);
key.p = toBigInteger(jwk.p);
key.q = toBigInteger(jwk.q);
key.u = key.p.modInverse(key.q);
// RSA keygen fallback using 40 iterations of the Miller-Rabin test
// See https://stackoverflow.com/a/6330138 for justification
// Also see section C.3 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST
let p = prime.randomProbablePrime(B - (B >> 1), E, 40);
let q = prime.randomProbablePrime(B >> 1, E, 40);
function toBigInteger(base64url) {
const base64 = base64url.replace(/\-/g, '+').replace(/_/g, '/');
const hex = util.hexstrdump(atob(base64));
return new BigInteger(hex, 16);
}
return key;
if (p.cmp(q) < 0) {
[p, q] = [q, p];
}
//
// JS code
//
const phi = p.subn(1).mul(q.subn(1));
return {
n: p.mul(q),
e: E,
d: E.invm(phi),
p: p,
q: q,
// dp: d.mod(p.subn(1)),
// dq: d.mod(q.subn(1)),
u: p.invm(q)
};
},
return new Promise(function(resolve) {
const key = new KeyObject();
const rng = new SecureRandom();
const qs = B >> 1;
key.e = parseInt(E, 16);
key.ee = new BigInteger(E, 16);
for (;;) {
for (;;) {
key.p = new BigInteger(B - qs, 1, rng);
if (key.p.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.p.isProbablePrime(10)) {
break;
}
}
for (;;) {
key.q = new BigInteger(qs, 1, rng);
if (key.q.subtract(BigInteger.ONE).gcd(key.ee).compareTo(BigInteger.ONE) === 0 && key.q.isProbablePrime(10)) {
break;
}
}
if (key.p.compareTo(key.q) <= 0) {
const t = key.p;
key.p = key.q;
key.q = t;
}
const p1 = key.p.subtract(BigInteger.ONE);
const q1 = key.q.subtract(BigInteger.ONE);
const phi = p1.multiply(q1);
if (phi.gcd(key.ee).compareTo(BigInteger.ONE) === 0) {
key.n = key.p.multiply(key.q);
key.d = key.ee.modInverse(phi);
key.dmp1 = key.d.mod(p1);
key.dmq1 = key.d.mod(q1);
key.u = key.p.modInverse(key.q);
break;
}
}
resolve(key);
});
}
this.encrypt = encrypt;
this.decrypt = decrypt;
this.verify = verify;
this.sign = sign;
this.generate = generate;
this.keyObject = KeyObject;
}
prime: prime
};

View File

@ -18,13 +18,15 @@
// The GPG4Browsers crypto interface
/**
* @requires bn.js
* @requires type/mpi
* @requires util
* @module crypto/random
*/
import type_mpi from '../type/mpi.js';
import util from '../util.js';
import BN from 'bn.js';
import type_mpi from '../type/mpi';
import util from '../util';
// Do not use util.getNodeCrypto because we need this regardless of use_native setting
const nodeCrypto = util.detectNode() && require('crypto');
@ -95,41 +97,27 @@ export default {
},
/**
* Create a secure random big integer of bits length
* @param {Integer} bits Bit length of the MPI to create
* @return {BigInteger} Resulting big integer
* Create a secure random MPI that is greater than or equal to min and less than max.
* @param {module:type/mpi} min Lower bound, included
* @param {module:type/mpi} max Upper bound, excluded
* @return {module:BN} Random MPI
*/
getRandomBigInteger: function(bits) {
if (bits < 1) {
throw new Error('Illegal parameter value: bits < 1');
}
const numBytes = Math.floor((bits + 7) / 8);
let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes));
if (bits % 8 > 0) {
randomBits = String.fromCharCode(((2 ** (bits % 8)) - 1) &
randomBits.charCodeAt(0)) +
randomBits.substring(1);
}
const mpi = new type_mpi(randomBits);
return mpi.toBigInteger();
},
getRandomBigIntegerInRange: function(min, max) {
if (max.compareTo(min) <= 0) {
getRandomBN: function(min, max) {
if (max.cmp(min) <= 0) {
throw new Error('Illegal parameter value: max <= min');
}
const range = max.subtract(min);
let r = this.getRandomBigInteger(range.bitLength());
while (r.compareTo(range) > 0) {
r = this.getRandomBigInteger(range.bitLength());
}
return min.add(r);
const modulus = max.sub(min);
const bytes = modulus.byteLength();
// Using a while loop is necessary to avoid bias introduced by the mod operation.
// However, we request 64 extra random bits so that the bias is negligible.
// Section B.1.1 here: https://nvlpubs.nist.gov/nistpubs/FIPS/NIST.FIPS.186-4.pdf
const r = new BN(this.getRandomBytes(bytes + 8));
return r.mod(modulus).add(min);
},
randomBuffer: new RandomBuffer()
};
/**

View File

@ -1,85 +1,66 @@
/**
* @requires util
* @requires crypto/hash
* @requires crypto/pkcs1
* @requires bn.js
* @requires crypto/public_key
* @module crypto/signature */
* @requires crypto/pkcs1
* @requires enums
* @requires util
* @module crypto/signature
*/
import util from '../util';
import BN from 'bn.js';
import publicKey from './public_key';
import pkcs1 from './pkcs1.js';
import pkcs1 from './pkcs1';
import enums from '../enums';
import util from '../util';
export default {
/**
*
* @param {module:enums.publicKey} algo public Key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Array<module:type/mpi>} msg_MPIs Signature multiprecision integers
* @param {Array<module:type/mpi>} publickey_MPIs Public key multiprecision integers
* @param {Uint8Array} data Data on where the signature was computed on.
* @return {Boolean} true if signature (sig_data was equal to data over hash)
* Verifies the signature provided for data using specified algorithms and public key parameters.
* See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1}
* and {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4}
* for public key and hash algorithms.
* @param {module:enums.publicKey} algo Public key algorithm
* @param {module:enums.hash} hash_algo Hash algorithm
* @param {Array<module:type/mpi>} msg_MPIs Algorithm-specific signature parameters
* @param {Array<module:type/mpi>} pub_MPIs Algorithm-specific public key parameters
* @param {Uint8Array} data Data for which the signature was created
* @return {Boolean} True if signature is valid
*/
verify: async function(algo, hash_algo, msg_MPIs, publickey_MPIs, data) {
let m;
let r;
let s;
let Q;
let curve;
data = util.Uint8Array2str(data);
verify: async function(algo, hash_algo, msg_MPIs, pub_MPIs, data) {
switch (algo) {
case 1:
// RSA (Encrypt or Sign) [HAC]
case 2:
// RSA Encrypt-Only [HAC]
case 3: {
// RSA Sign-Only [HAC]
const rsa = new publicKey.rsa();
const n = publickey_MPIs[0].toBigInteger();
const k = publickey_MPIs[0].byteLength();
const e = publickey_MPIs[1].toBigInteger();
m = msg_MPIs[0].toBigInteger();
const EM = rsa.verify(m, e, n);
const EM2 = pkcs1.emsa.encode(hash_algo, data, k);
return EM.compareTo(EM2) === 0;
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_sign: {
const m = msg_MPIs[0].toBN();
const n = pub_MPIs[0].toBN();
const e = pub_MPIs[1].toBN();
const EM = publicKey.rsa.verify(m, n, e);
const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array_to_str(data), n.byteLength());
return util.Uint8Array_to_hex(EM) === EM2;
}
case 16: {
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
throw new Error("signing with Elgamal is not defined in the OpenPGP standard.");
case enums.publicKey.dsa: {
const r = msg_MPIs[0].toBN();
const s = msg_MPIs[1].toBN();
const p = pub_MPIs[0].toBN();
const q = pub_MPIs[1].toBN();
const g = pub_MPIs[2].toBN();
const y = pub_MPIs[3].toBN();
return publicKey.dsa.verify(hash_algo, r, s, data, p, q, g, y);
}
case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
const dsa = new publicKey.dsa();
const s1 = msg_MPIs[0].toBigInteger();
const s2 = msg_MPIs[1].toBigInteger();
const p = publickey_MPIs[0].toBigInteger();
const q = publickey_MPIs[1].toBigInteger();
const g = publickey_MPIs[2].toBigInteger();
const y = publickey_MPIs[3].toBigInteger();
m = data;
const dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
return dopublic.compareTo(s1) === 0;
case enums.publicKey.ecdsa: {
const oid = pub_MPIs[0];
const signature = { r: msg_MPIs[0].toUint8Array(), s: msg_MPIs[1].toUint8Array() };
const Q = pub_MPIs[1].toUint8Array();
return publicKey.elliptic.ecdsa.verify(oid, hash_algo, signature, data, Q);
}
case 19: {
// ECDSA
const { ecdsa } = publicKey.elliptic;
[curve] = publickey_MPIs;
r = msg_MPIs[0].toBigInteger();
s = msg_MPIs[1].toBigInteger();
m = data;
Q = publickey_MPIs[1].toBigInteger();
return ecdsa.verify(curve.oid, hash_algo, { r: r, s: s }, m, Q);
}
case 22: {
// EdDSA
const { eddsa } = publicKey.elliptic;
[curve] = publickey_MPIs;
r = msg_MPIs[0].toBigInteger();
s = msg_MPIs[1].toBigInteger();
m = data;
Q = publickey_MPIs[1].toBigInteger();
return eddsa.verify(curve.oid, hash_algo, { R: r, S: s }, m, Q);
case enums.publicKey.eddsa: {
const oid = pub_MPIs[0];
// TODO refactor elliptic to accept Uint8Array
// EdDSA signature params are expected in little-endian format
const signature = { R: Array.from(msg_MPIs[0].toUint8Array('le', 32)),
S: Array.from(msg_MPIs[1].toUint8Array('le', 32)) };
const Q = Array.from(pub_MPIs[1].toUint8Array('be', 33));
return publicKey.elliptic.eddsa.verify(oid, hash_algo, signature, data, Q);
}
default:
throw new Error('Invalid signature algorithm.');
@ -87,73 +68,60 @@ export default {
},
/**
* Create a signature on data using the specified algorithm
* @param {module:enums.hash} hash_algo hash Algorithm to use (See {@link https://tools.ietf.org/html/rfc4880#section-9.4|RFC 4880 9.4})
* @param {module:enums.publicKey} algo Asymmetric cipher algorithm to use (See {@link https://tools.ietf.org/html/rfc4880#section-9.1|RFC 4880 9.1})
* @param {Array<module:type/mpi>} keyIntegers Public followed by Private key multiprecision algorithm-specific parameters
* @param {Uint8Array} data Data to be signed
* @return {Array<module:type/mpi>}
* 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 {Array<module:type/mpi>} key_params Algorithm-specific public and private key parameters
* @param {Uint8Array} data Data to be signed
* @return {Uint8Array} Signature
*/
sign: async function(hash_algo, algo, keyIntegers, data) {
data = util.Uint8Array2str(data);
let m;
let d;
let curve;
let signature;
sign: async function(algo, hash_algo, key_params, data) {
switch (algo) {
case 1:
// RSA (Encrypt or Sign) [HAC]
case 2:
// RSA Encrypt-Only [HAC]
case 3: {
// RSA Sign-Only [HAC]
const rsa = new publicKey.rsa();
d = keyIntegers[2].toBigInteger();
const n = keyIntegers[0].toBigInteger();
m = pkcs1.emsa.encode(
hash_algo,
data, keyIntegers[0].byteLength()
);
return util.str2Uint8Array(rsa.sign(m, d, n).toMPI());
case enums.publicKey.rsa_encrypt_sign:
case enums.publicKey.rsa_encrypt:
case enums.publicKey.rsa_sign: {
const n = key_params[0].toBN();
const e = key_params[1].toBN();
const d = key_params[2].toBN();
data = util.Uint8Array_to_str(data);
const m = new BN(pkcs1.emsa.encode(hash_algo, data, n.byteLength()), 16);
const signature = publicKey.rsa.sign(m, n, e, d);
return util.Uint8Array_to_MPI(signature);
}
case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
const dsa = new publicKey.dsa();
const p = keyIntegers[0].toBigInteger();
const q = keyIntegers[1].toBigInteger();
const g = keyIntegers[2].toBigInteger();
const x = keyIntegers[4].toBigInteger();
m = data;
const result = dsa.sign(hash_algo, m, g, p, q, x);
return util.str2Uint8Array(result[0].toString() + result[1].toString());
case enums.publicKey.dsa: {
const p = key_params[0].toBN();
const q = key_params[1].toBN();
const g = key_params[2].toBN();
const x = key_params[4].toBN();
const signature = publicKey.dsa.sign(hash_algo, data, g, p, q, x);
return util.concatUint8Array([
util.Uint8Array_to_MPI(signature.r),
util.Uint8Array_to_MPI(signature.s)
]);
}
case 16: {
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
case enums.publicKey.elgamal: {
throw new Error('Signing with Elgamal is not defined in the OpenPGP standard.');
}
case 19: {
// ECDSA
const { ecdsa } = publicKey.elliptic;
[curve] = keyIntegers;
d = keyIntegers[2].toBigInteger();
m = data;
signature = await ecdsa.sign(curve.oid, hash_algo, m, d);
return util.str2Uint8Array(signature.r.toMPI() + signature.s.toMPI());
case enums.publicKey.ecdsa: {
const oid = key_params[0];
const d = key_params[2].toUint8Array();
const signature = await publicKey.elliptic.ecdsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([
util.Uint8Array_to_MPI(signature.r),
util.Uint8Array_to_MPI(signature.s)
]);
}
case 22: {
// EdDSA
const { eddsa } = publicKey.elliptic;
[curve] = keyIntegers;
d = keyIntegers[2].toBigInteger();
m = data;
signature = await eddsa.sign(curve.oid, hash_algo, m, d);
return new Uint8Array([].concat(
util.Uint8Array2MPI(signature.R.toArrayLike(Uint8Array, 'le', 32)),
util.Uint8Array2MPI(signature.S.toArrayLike(Uint8Array, 'le', 32))
));
case enums.publicKey.eddsa: {
const oid = key_params[0];
const d = Array.from(key_params[2].toUint8Array('be', 32));
const signature = await publicKey.elliptic.eddsa.sign(oid, hash_algo, data, d);
return util.concatUint8Array([
util.Uint8Array_to_MPI(signature.R),
util.Uint8Array_to_MPI(signature.S)
]);
}
default:
throw new Error('Invalid signature algorithm.');

View File

@ -72,13 +72,22 @@ export default {
* @readonly
*/
publicKey: {
/** RSA (Encrypt or Sign) [HAC] */
rsa_encrypt_sign: 1,
/** RSA (Encrypt only) [HAC] */
rsa_encrypt: 2,
/** RSA (Sign only) [HAC] */
rsa_sign: 3,
/** Elgamal (Encrypt only) [ELGAMAL] [HAC] */
elgamal: 16,
/** DSA (Sign only) [FIPS186] [HAC] */
dsa: 17,
/** ECDH (Encrypt only) [RFC6637] */
ecdh: 18,
/** ECDSA (Sign only) [RFC6637] */
ecdsa: 19,
/** EdDSA (Sign only)
* [{@link https://tools.ietf.org/html/draft-koch-eddsa-for-openpgp-04|Draft RFC}] */
eddsa: 22
},
@ -183,37 +192,44 @@ export default {
signature: {
/** 0x00: Signature of a binary document. */
binary: 0,
/** 0x01: Signature of a canonical text document.<br/>
/** 0x01: Signature of a canonical text document.
*
* Canonicalyzing the document by converting line endings. */
text: 1,
/** 0x02: Standalone signature.<br/>
/** 0x02: Standalone signature.
*
* This signature is a signature of only its own subpacket contents.
* It is calculated identically to a signature over a zero-lengh
* binary document. Note that it doesn't make sense to have a V3
* standalone signature. */
standalone: 2,
/** 0x10: Generic certification of a User ID and Public-Key packet.<br/>
/** 0x10: Generic certification of a User ID and Public-Key packet.
*
* The issuer of this certification does not make any particular
* assertion as to how well the certifier has checked that the owner
* of the key is in fact the person described by the User ID. */
cert_generic: 16,
/** 0x11: Persona certification of a User ID and Public-Key packet.<br/>
/** 0x11: Persona certification of a User ID and Public-Key packet.
*
* The issuer of this certification has not done any verification of
* the claim that the owner of this key is the User ID specified. */
cert_persona: 17,
/** 0x12: Casual certification of a User ID and Public-Key packet.<br/>
/** 0x12: Casual certification of a User ID and Public-Key packet.
*
* The issuer of this certification has done some casual
* verification of the claim of identity. */
cert_casual: 18,
/** 0x13: Positive certification of a User ID and Public-Key packet.<br/>
/** 0x13: Positive certification of a User ID and Public-Key packet.
*
* The issuer of this certification has done substantial
* verification of the claim of identity.<br/>
* <br/>
* verification of the claim of identity.
*
* Most OpenPGP implementations make their "key signatures" as 0x10
* certifications. Some implementations can issue 0x11-0x13
* certifications, but few differentiate between the types. */
cert_positive: 19,
/** 0x30: Certification revocation signature<br/>
/** 0x30: Certification revocation signature
*
* This signature revokes an earlier User ID certification signature
* (signature class 0x10 through 0x13) or direct-key signature
* (0x1F). It should be issued by the same key that issued the
@ -222,7 +238,8 @@ export default {
* revokes, and should have a later creation date than that
* certificate. */
cert_revocation: 48,
/** 0x18: Subkey Binding Signature<br/>
/** 0x18: Subkey Binding Signature
*
* This signature is a statement by the top-level signing key that
* indicates that it owns the subkey. This signature is calculated
* directly on the primary key and subkey, and not on any User ID or
@ -231,12 +248,13 @@ export default {
* contains a 0x19 signature made by the signing subkey on the
* primary key and subkey. */
subkey_binding: 24,
/** 0x19: Primary Key Binding Signature<br/>
/** 0x19: Primary Key Binding Signature
*
* This signature is a statement by a signing subkey, indicating
* that it is owned by the primary key and subkey. This signature
* is calculated the same way as a 0x18 signature: directly on the
* primary key and subkey, and not on any User ID or other packets.<br/>
* <br/>
* primary key and subkey, and not on any User ID or other packets.
*
* When a signature is made over a key, the hash data starts with the
* octet 0x99, followed by a two-octet length of the key, and then body
* of the key packet. (Note that this is an old-style packet header for
@ -245,7 +263,8 @@ export default {
* the subkey using the same format as the main key (also using 0x99 as
* the first octet). */
key_binding: 25,
/** 0x1F: Signature directly on a key<br/>
/** 0x1F: Signature directly on a key
*
* This signature is calculated directly on a key. It binds the
* information in the Signature subpackets to the key, and is
* appropriate to be used for subpackets that provide information
@ -254,27 +273,30 @@ export default {
* about the key itself, rather than the binding between a key and a
* name. */
key: 31,
/** 0x20: Key revocation signature<br/>
/** 0x20: Key revocation signature
*
* The signature is calculated directly on the key being revoked. A
* revoked key is not to be used. Only revocation signatures by the
* key being revoked, or by an authorized revocation key, should be
* considered valid revocation signatures.a */
key_revocation: 32,
/** 0x28: Subkey revocation signature<br/>
/** 0x28: Subkey revocation signature
*
* The signature is calculated directly on the subkey being revoked.
* A revoked subkey is not to be used. Only revocation signatures
* by the top-level signature key that is bound to this subkey, or
* by an authorized revocation key, should be considered valid
* revocation signatures.<br/>
* <br/>
* revocation signatures.
*
* Key revocation signatures (types 0x20 and 0x28)
* hash only the key being revoked. */
subkey_revocation: 40,
/** 0x40: Timestamp signature.<br/>
/** 0x40: Timestamp signature.
* This signature is only meaningful for the timestamp contained in
* it. */
timestamp: 64,
/** 0x50: Third-Party Confirmation signature.<br/>
/** 0x50: Third-Party Confirmation signature.
*
* This signature is a signature over some other OpenPGP Signature
* packet(s). It is analogous to a notary seal on the signed data.
* A third-party signature SHOULD include Signature Target

View File

@ -18,6 +18,7 @@
/**
* @fileoverview This class implements a client for the OpenPGP HTTP Keyserver Protocol (HKP)
* in order to lookup and upload keys on standard public key servers.
* @module hkp
*/
import config from './config';

View File

@ -16,21 +16,21 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* @requires config
* @requires crypto
* @requires encoding/armor
* @requires crypto
* @requires packet
* @requires config
* @requires enums
* @requires util
* @requires packet
* @module key
*/
import config from './config';
import crypto from './crypto';
import armor from './encoding/armor';
import crypto from './crypto';
import packet from './packet';
import config from './config';
import enums from './enums';
import util from './util';
import packet from './packet';
/**
* @class
@ -209,7 +209,7 @@ Key.prototype.getKeyIds = function() {
/**
* Returns array containing first key packet for given key ID or all key packets in the case of a wildcard ID
* @param {type/keyid>} keyIds
* @param {type/keyid} keyId
* @return {(module:packet/public_subkey|module:packet/public_key|
* module:packet/secret_subkey|module:packet/secret_key|null)}
*/
@ -235,7 +235,7 @@ Key.prototype.getUserIds = function() {
const userids = [];
for (let i = 0; i < this.users.length; i++) {
if (this.users[i].userId) {
userids.push(util.Uint8Array2str(this.users[i].userId.write()));
userids.push(util.Uint8Array_to_str(this.users[i].userId.write()));
}
}
return userids;
@ -514,8 +514,10 @@ function getExpirationTime(keyPacket, selfCertificate, defaultValue=null) {
/**
* Returns primary user and most significant (latest valid) self signature
* - if multiple users are marked as primary users returns the one with the latest self signature
* - if no primary user is found returns the user with the latest self signature
* - if multiple primary users exist, returns the one with the latest self signature
* - otherwise, returns the user with the latest self signature
*
* NOTE: call verifyPrimaryUser before calling this function.
* @param {Date} date use the given date for verification instead of the current time
* @return {{user: Array<module:packet/User>, selfCertificate: Array<module:packet/signature>}|null} The primary user and the self signature
*/
@ -696,23 +698,31 @@ Key.prototype.verifyPrimaryUser = async function(keys) {
return;
}
const dataToVerify = { userid: user.userId || user.userAttribute, key: primaryKey };
await Promise.all(user.selfCertifications.map(async function(selfCertification) {
// TODO replace when Promise.forEach is implemented
for (let i = 0; i < user.selfCertifications.length; i++) {
const selfCertification = user.selfCertifications[i];
// skip if certificate is not the most recent
if ((selfCertification.isPrimaryUserID &&
selfCertification.isPrimaryUserID < lastPrimaryUserID) ||
(!lastPrimaryUserID && selfCertification.created < lastCreated)) {
return;
}
// skip if certificates is not valid
if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify)) ||
(selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) ||
selfCertification.isExpired()) {
// skip if certificates is invalid, revoked, or expired
// eslint-disable-next-line no-await-in-loop
if (!(selfCertification.verified || await selfCertification.verify(primaryKey, dataToVerify))) {
return;
}
// eslint-disable-next-line no-await-in-loop
if (selfCertification.revoked || await user.isRevoked(primaryKey, selfCertification)) {
return;
}
if (selfCertification.isExpired()) {
return;
}
lastPrimaryUserID = selfCertification.isPrimaryUserID;
lastCreated = selfCertification.created;
primaryUsers.push(user);
}));
}
}));
const user = primaryUsers.pop();
const results = !user ? [] : keys ? await user.verifyAllCertifications(primaryKey, keys) :
@ -781,6 +791,7 @@ User.prototype.toPacketlist = function() {
* @return {Boolean} True if the certificate is revoked
*/
User.prototype.isRevoked = async function(primaryKey, certificate, key) {
certificate.revoked = null;
if (this.revocationCertifications) {
const dataToVerify = { userid: this.userId || this.userAttribute, key: primaryKey };
// TODO clarify OpenPGP's behavior given an expired revocation signature
@ -1294,7 +1305,7 @@ async function wrapKeyObject(secretKeyPacket, secretSubkeyPacket, options) {
await Promise.all(options.userIds.map(async function(userId, index) {
const userIdPacket = new packet.Userid();
userIdPacket.read(util.str2Uint8Array(userId));
userIdPacket.read(util.str_to_Uint8Array(userId));
const dataToSign = {};
dataToSign.userid = userIdPacket;

View File

@ -188,7 +188,7 @@ Message.prototype.decryptSessionKeys = function(privateKeys, passwords) {
if (keyPackets.length > 1) {
const seen = {};
keyPackets = keyPackets.filter(function(item) {
const k = item.sessionKeyAlgorithm + util.Uint8Array2str(item.sessionKey);
const k = item.sessionKeyAlgorithm + util.Uint8Array_to_str(item.sessionKey);
if (seen.hasOwnProperty(k)) {
return false;
}
@ -622,7 +622,7 @@ export function read(input) {
*/
export function readSignedContent(content, detachedSignature) {
const literalDataPacket = new packet.Literal();
literalDataPacket.setBytes(util.str2Uint8Array(content), enums.read(enums.literal, enums.literal.binary));
literalDataPacket.setBytes(util.str_to_Uint8Array(content), enums.read(enums.literal, enums.literal.binary));
const packetlist = new packet.List();
packetlist.push(literalDataPacket);
const input = armor.decode(detachedSignature).data;

View File

@ -393,7 +393,7 @@ export function encryptSessionKey({ data, algorithm, publicKeys, passwords, wild
* Decrypt symmetric session keys with a private key or password. Either a private key or
* a password must be specified.
* @param {Message} message a message object containing the encrypted session key packets
* @param {Key|Array<Key} privateKeys (optional) private keys with decrypted secret key data
* @param {Key|Array<Key>} privateKeys (optional) private keys with decrypted secret key data
* @param {String|Array<String>} passwords (optional) passwords to decrypt the session key
* @return {Promise<Object|undefined>} Array of decrypted session key, algorithm pairs in form:
* { data:Uint8Array, algorithm:String }

View File

@ -19,6 +19,7 @@
* @fileoverview This module implements packet list cloning required to
* pass certain object types between the web worker and main thread using
* the structured cloning algorithm.
* @module packet/clone
*/
import { Key } from '../key';

View File

@ -16,9 +16,10 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Compressed Data Packet (Tag 8)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}: The Compressed Data packet contains compressed data. Typically,
* Implementation of the Compressed Data Packet (Tag 8)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.6|RFC4880 5.6}:
* The Compressed Data packet contains compressed data. Typically,
* this packet is found as the contents of an encrypted packet, or following
* a Signature or One-Pass Signature packet, and contains a literal data packet.
* @requires compression/zlib

View File

@ -16,10 +16,11 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Literal Data Packet (Tag 11)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}: A Literal Data packet contains the body of a message; data that
* is not to be further interpreted.
* Implementation of the Literal Data Packet (Tag 11)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.9|RFC4880 5.9}:
* A Literal Data packet contains the body of a message; data that is not to be
* further interpreted.
* @requires enums
* @requires util
* @module packet/literal
@ -49,7 +50,7 @@ Literal.prototype.setText = function(text) {
// normalize EOL to \r\n
text = text.replace(/\r\n/g, '\n').replace(/\r/g, '\n').replace(/\n/g, '\r\n');
// encode UTF8
this.data = this.format === 'utf8' ? util.str2Uint8Array(util.encode_utf8(text)) : util.str2Uint8Array(text);
this.data = this.format === 'utf8' ? util.str_to_Uint8Array(util.encode_utf8(text)) : util.str_to_Uint8Array(text);
};
/**
@ -59,7 +60,7 @@ Literal.prototype.setText = function(text) {
*/
Literal.prototype.getText = function() {
// decode UTF8
const text = util.decode_utf8(util.Uint8Array2str(this.data));
const text = util.decode_utf8(util.Uint8Array_to_str(this.data));
// normalize EOL to \n
return text.replace(/\r\n/g, '\n');
};
@ -113,7 +114,7 @@ Literal.prototype.read = function(bytes) {
const format = enums.read(enums.literal, bytes[0]);
const filename_len = bytes[1];
this.filename = util.decode_utf8(util.Uint8Array2str(bytes.subarray(2, 2 + filename_len)));
this.filename = util.decode_utf8(util.Uint8Array_to_str(bytes.subarray(2, 2 + filename_len)));
this.date = util.readDate(bytes.subarray(2 + filename_len, 2 + filename_len + 4));
@ -128,7 +129,7 @@ Literal.prototype.read = function(bytes) {
* @return {Uint8Array} Uint8Array representation of the packet
*/
Literal.prototype.write = function() {
const filename = util.str2Uint8Array(util.encode_utf8(this.filename));
const filename = util.str_to_Uint8Array(util.encode_utf8(this.filename));
const filename_length = new Uint8Array([filename.length]);
const format = new Uint8Array([enums.write(enums.literal, this.format)]);

View File

@ -15,15 +15,15 @@
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the strange "Marker packet" (Tag 10)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}: An experimental version of PGP used this packet as the Literal
* Implementation of the strange "Marker packet" (Tag 10)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.8|RFC4880 5.8}:
* An experimental version of PGP used this packet as the Literal
* packet, but no released version of PGP generated Literal packets with this
* tag. With PGP 5.x, this packet has been reassigned and is reserved for use as
* the Marker packet.<br/>
* <br/>
* the Marker packet.
*
* Such a packet MUST be ignored when received.
* @requires enums
* @module packet/marker

View File

@ -16,14 +16,15 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the One-Pass Signature Packets (Tag 4)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}: The One-Pass Signature packet precedes the signed data and contains
* Implementation of the One-Pass Signature Packets (Tag 4)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.4|RFC4880 5.4}:
* The One-Pass Signature packet precedes the signed data and contains
* enough information to allow the receiver to begin calculating any
* hashes needed to verify the signature. It allows the Signature
* packet to be placed at the end of the message, so that the signer
* can compute the entire signed message in one pass.
* @requires util
* @requires util
* @requires enums
* @requires type/keyid
* @module packet/one_pass_signature

View File

@ -16,8 +16,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Key Material Packet (Tag 5,6,7,14)<br/>
* <br/>
* Implementation of the Key Material Packet (Tag 5,6,7,14)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}:
* A key material packet contains all the information about a public or
* private key. There are four variants of this packet type, and two
@ -85,8 +85,8 @@ PublicKey.prototype.read = function (bytes) {
// - A one-octet number denoting the public-key algorithm of this key.
this.algorithm = enums.read(enums.publicKey, bytes[pos++]);
const types = crypto.getPubKeyParamTypes(this.algorithm);
const algo = enums.write(enums.publicKey, this.algorithm);
const types = crypto.getPubKeyParamTypes(algo);
this.params = crypto.constructParams(types);
const b = bytes.subarray(pos, bytes.length);
@ -123,10 +123,10 @@ PublicKey.prototype.write = function () {
if (this.version === 3) {
arr.push(util.writeNumber(this.expirationTimeV3, 2));
}
arr.push(new Uint8Array([enums.write(enums.publicKey, this.algorithm)]));
const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length;
// Algorithm-specific params
const algo = enums.write(enums.publicKey, this.algorithm);
const paramCount = crypto.getPubKeyParamTypes(algo).length;
arr.push(new Uint8Array([algo]));
for (let i = 0; i < paramCount; i++) {
arr.push(this.params[i].write());
}
@ -159,7 +159,7 @@ PublicKey.prototype.getKeyId = function () {
}
this.keyid = new type_keyid();
if (this.version === 4) {
this.keyid.read(util.str2Uint8Array(util.hex2bin(this.getFingerprint()).substr(12, 8)));
this.keyid.read(util.str_to_Uint8Array(util.hex_to_str(this.getFingerprint()).substr(12, 8)));
} else if (this.version === 3) {
const arr = this.params[0].write();
this.keyid.read(arr.subarray(arr.length - 8, arr.length));
@ -178,21 +178,22 @@ PublicKey.prototype.getFingerprint = function () {
let toHash = '';
if (this.version === 4) {
toHash = this.writeOld();
this.fingerprint = util.Uint8Array2str(crypto.hash.sha1(toHash));
this.fingerprint = util.Uint8Array_to_str(crypto.hash.sha1(toHash));
} else if (this.version === 3) {
const paramCount = crypto.getPubKeyParamTypes(this.algorithm).length;
const algo = enums.write(enums.publicKey, this.algorithm);
const paramCount = crypto.getPubKeyParamTypes(algo).length;
for (let i = 0; i < paramCount; i++) {
toHash += this.params[i].toBytes();
toHash += this.params[i].toString();
}
this.fingerprint = util.Uint8Array2str(crypto.hash.md5(util.str2Uint8Array(toHash)));
this.fingerprint = util.Uint8Array_to_str(crypto.hash.md5(util.str_to_Uint8Array(toHash)));
}
this.fingerprint = util.hexstrdump(this.fingerprint);
this.fingerprint = util.str_to_hex(this.fingerprint);
return this.fingerprint;
};
/**
* Returns algorithm information
* @return {Promise<Object} An object of the form {algorithm: String, bits:int, curve:String}
* @return {Promise<Object>} An object of the form {algorithm: String, bits:int, curve:String}
*/
PublicKey.prototype.getAlgorithmInfo = function () {
const result = {};
@ -200,7 +201,7 @@ PublicKey.prototype.getAlgorithmInfo = function () {
if (this.params[0] instanceof type_mpi) {
result.bits = this.params[0].byteLength() * 8;
} else {
result.curve = crypto.publicKey.elliptic.get(this.params[0]).name;
result.curve = this.params[0].getName();
}
return result;
};
@ -209,7 +210,8 @@ PublicKey.prototype.getAlgorithmInfo = function () {
* Fix custom types after cloning
*/
PublicKey.prototype.postCloneTypeFix = function() {
const types = crypto.getPubKeyParamTypes(this.algorithm);
const algo = enums.write(enums.publicKey, this.algorithm);
const types = crypto.getPubKeyParamTypes(algo);
for (let i = 0; i < types.length; i++) {
const param = this.params[i];
this.params[i] = types[i].fromClone(param);

View File

@ -16,9 +16,10 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Public-Key Encrypted Session Key Packets (Tag 1)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key
* Public-Key Encrypted Session Key Packets (Tag 1)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}:
* A Public-Key Encrypted Session Key packet holds the session key
* used to encrypt a message. Zero or more Public-Key Encrypted Session Key
* packets and/or Symmetric-Key Encrypted Session Key packets may precede a
* Symmetrically Encrypted Data Packet, which holds an encrypted message. The
@ -75,7 +76,8 @@ PublicKeyEncryptedSessionKey.prototype.read = function (bytes) {
let i = 10;
const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm);
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const types = crypto.getEncSessionKeyParamTypes(algo);
this.encrypted = crypto.constructParams(types);
for (let j = 0; j < types.length; j++) {
@ -101,23 +103,20 @@ PublicKeyEncryptedSessionKey.prototype.write = function () {
PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
let data = String.fromCharCode(enums.write(enums.symmetric, this.sessionKeyAlgorithm));
data += util.Uint8Array2str(this.sessionKey);
data += util.Uint8Array_to_str(this.sessionKey);
const checksum = util.calc_checksum(this.sessionKey);
data += util.Uint8Array2str(util.writeNumber(checksum, 2));
data += util.Uint8Array_to_str(util.writeNumber(checksum, 2));
let toEncrypt;
if (this.publicKeyAlgorithm === 'ecdh') {
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
if (algo === enums.publicKey.ecdh) {
toEncrypt = new type_mpi(crypto.pkcs5.encode(data));
} else {
toEncrypt = new type_mpi(crypto.pkcs1.eme.encode(data, key.params[0].byteLength()));
}
this.encrypted = await crypto.publicKeyEncrypt(
this.publicKeyAlgorithm,
key.params,
toEncrypt,
key.fingerprint
);
algo, key.params, toEncrypt, key.fingerprint);
};
/**
@ -129,24 +128,21 @@ PublicKeyEncryptedSessionKey.prototype.encrypt = async function (key) {
* @return {String} The unencrypted session key
*/
PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
const result = (await crypto.publicKeyDecrypt(
this.publicKeyAlgorithm,
key.params,
this.encrypted,
key.fingerprint
)).toBytes();
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const result = await crypto.publicKeyDecrypt(
algo, key.params, this.encrypted, key.fingerprint);
let checksum;
let decoded;
if (this.publicKeyAlgorithm === 'ecdh') {
decoded = crypto.pkcs5.decode(result);
checksum = util.readNumber(util.str2Uint8Array(decoded.substr(decoded.length - 2)));
if (algo === enums.publicKey.ecdh) {
decoded = crypto.pkcs5.decode(result.toString());
checksum = util.readNumber(util.str_to_Uint8Array(decoded.substr(decoded.length - 2)));
} else {
decoded = crypto.pkcs1.eme.decode(result);
checksum = util.readNumber(util.str2Uint8Array(result.substr(result.length - 2)));
decoded = crypto.pkcs1.eme.decode(result.toString());
checksum = util.readNumber(result.toUint8Array().slice(result.byteLength() - 2));
}
key = util.str2Uint8Array(decoded.substring(1, decoded.length - 2));
key = util.str_to_Uint8Array(decoded.substring(1, decoded.length - 2));
if (checksum !== util.calc_checksum(key)) {
throw new Error('Checksum mismatch');
@ -161,7 +157,8 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
*/
PublicKeyEncryptedSessionKey.prototype.postCloneTypeFix = function() {
this.publicKeyId = type_keyid.fromClone(this.publicKeyId);
const types = crypto.getEncSessionKeyParamTypes(this.publicKeyAlgorithm);
const algo = enums.write(enums.publicKey, this.publicKeyAlgorithm);
const types = crypto.getEncSessionKeyParamTypes(algo);
for (let i = 0; i < this.encrypted.length; i++) {
this.encrypted[i] = types[i].fromClone(this.encrypted[i]);
}

View File

@ -16,8 +16,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Key Material Packet (Tag 5,6,7,14)<br/>
* <br/>
* Implementation of the Key Material Packet (Tag 5,6,7,14)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.5|RFC4480 5.5}:
* A key material packet contains all the information about a public or
* private key. There are four variants of this packet type, and two
@ -76,15 +76,16 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
const hashlen = get_hash_len(hash_algorithm);
const hashfn = get_hash_fn(hash_algorithm);
const hashtext = util.Uint8Array2str(cleartext.subarray(cleartext.length - hashlen, cleartext.length));
const hashtext = util.Uint8Array_to_str(cleartext.subarray(cleartext.length - hashlen, cleartext.length));
cleartext = cleartext.subarray(0, cleartext.length - hashlen);
const hash = util.Uint8Array2str(hashfn(cleartext));
const hash = util.Uint8Array_to_str(hashfn(cleartext));
if (hash !== hashtext) {
return new Error("Hash mismatch.");
}
const types = crypto.getPrivKeyParamTypes(algorithm);
const algo = enums.write(enums.publicKey, algorithm);
const types = crypto.getPrivKeyParamTypes(algo);
const params = crypto.constructParams(types);
let p = 0;
@ -100,7 +101,8 @@ function parse_cleartext_params(hash_algorithm, cleartext, algorithm) {
function write_cleartext_params(hash_algorithm, algorithm, params) {
const arr = [];
const numPublicParams = crypto.getPubKeyParamTypes(algorithm).length;
const algo = enums.write(enums.publicKey, algorithm);
const numPublicParams = crypto.getPubKeyParamTypes(algo).length;
for (let i = numPublicParams; i < params.length; i++) {
arr.push(params[i].write());
@ -269,8 +271,8 @@ SecretKey.prototype.decrypt = function (passphrase) {
SecretKey.prototype.generate = function (bits, curve) {
const that = this;
return crypto.generateParams(that.algorithm, bits, curve).then(function(params) {
const algo = enums.write(enums.publicKey, that.algorithm);
return crypto.generateParams(algo, bits, curve).then(function(params) {
that.params = params;
that.isDecrypted = true;
});
@ -283,7 +285,8 @@ SecretKey.prototype.clearPrivateParams = function () {
if (!this.encrypted) {
throw new Error('If secret key is not encrypted, clearing private params is irreversible.');
}
this.params = this.params.slice(0, crypto.getPubKeyParamTypes(this.algorithm).length);
const algo = enums.write(enums.publicKey, this.algorithm);
this.params = this.params.slice(0, crypto.getPubKeyParamTypes(algo).length);
this.isDecrypted = false;
};
@ -291,7 +294,8 @@ SecretKey.prototype.clearPrivateParams = function () {
* Fix custom types after cloning
*/
SecretKey.prototype.postCloneTypeFix = function() {
const types = crypto.getPubKeyParamTypes(this.algorithm).concat(crypto.getPrivKeyParamTypes(this.algorithm));
const algo = enums.write(enums.publicKey, this.algorithm);
const types = [].concat(crypto.getPubKeyParamTypes(algo), crypto.getPrivKeyParamTypes(algo));
for (let i = 0; i < this.params.length; i++) {
const param = this.params[i];
this.params[i] = types[i].fromClone(param);

View File

@ -16,8 +16,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Signature Packet (Tag 2)<br/>
* <br/>
* Implementation of the Signature Packet (Tag 2)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.2|RFC4480 5.2}:
* A Signature packet describes a binding between some public key and
* some data. The most common signatures are a signature of a file or a
@ -85,7 +85,7 @@ export default function Signature(date=new Date()) {
this.signatureTargetHash = null;
this.embeddedSignature = null;
this.verified = false;
this.verified = null;
}
/**
@ -245,8 +245,7 @@ Signature.prototype.sign = async function (key, data) {
this.signedHashValue = hash.subarray(0, 2);
this.signature = await crypto.signature.sign(
hashAlgorithm,
publicKeyAlgorithm, key.params, toHash
publicKeyAlgorithm, hashAlgorithm, key.params, toHash
);
};
@ -281,7 +280,7 @@ Signature.prototype.write_all_sub_packets = function () {
arr.push(write_sub_packet(sub.key_expiration_time, util.writeNumber(this.keyExpirationTime, 4)));
}
if (this.preferredSymmetricAlgorithms !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.preferredSymmetricAlgorithms));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredSymmetricAlgorithms));
arr.push(write_sub_packet(sub.preferred_symmetric_algorithms, bytes));
}
if (this.revocationKeyClass !== null) {
@ -301,51 +300,51 @@ Signature.prototype.write_all_sub_packets = function () {
bytes.push(util.writeNumber(name.length, 2));
// 2 octets of value length
bytes.push(util.writeNumber(value.length, 2));
bytes.push(util.str2Uint8Array(name + value));
bytes.push(util.str_to_Uint8Array(name + value));
bytes = util.concatUint8Array(bytes);
arr.push(write_sub_packet(sub.notation_data, bytes));
}
}
}
if (this.preferredHashAlgorithms !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.preferredHashAlgorithms));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredHashAlgorithms));
arr.push(write_sub_packet(sub.preferred_hash_algorithms, bytes));
}
if (this.preferredCompressionAlgorithms !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.preferredCompressionAlgorithms));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.preferredCompressionAlgorithms));
arr.push(write_sub_packet(sub.preferred_compression_algorithms, bytes));
}
if (this.keyServerPreferences !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.keyServerPreferences));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.keyServerPreferences));
arr.push(write_sub_packet(sub.key_server_preferences, bytes));
}
if (this.preferredKeyServer !== null) {
arr.push(write_sub_packet(sub.preferred_key_server, util.str2Uint8Array(this.preferredKeyServer)));
arr.push(write_sub_packet(sub.preferred_key_server, util.str_to_Uint8Array(this.preferredKeyServer)));
}
if (this.isPrimaryUserID !== null) {
arr.push(write_sub_packet(sub.primary_user_id, new Uint8Array([this.isPrimaryUserID ? 1 : 0])));
}
if (this.policyURI !== null) {
arr.push(write_sub_packet(sub.policy_uri, util.str2Uint8Array(this.policyURI)));
arr.push(write_sub_packet(sub.policy_uri, util.str_to_Uint8Array(this.policyURI)));
}
if (this.keyFlags !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.keyFlags));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.keyFlags));
arr.push(write_sub_packet(sub.key_flags, bytes));
}
if (this.signersUserId !== null) {
arr.push(write_sub_packet(sub.signers_user_id, util.str2Uint8Array(this.signersUserId)));
arr.push(write_sub_packet(sub.signers_user_id, util.str_to_Uint8Array(this.signersUserId)));
}
if (this.reasonForRevocationFlag !== null) {
bytes = util.str2Uint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString);
bytes = util.str_to_Uint8Array(String.fromCharCode(this.reasonForRevocationFlag) + this.reasonForRevocationString);
arr.push(write_sub_packet(sub.reason_for_revocation, bytes));
}
if (this.features !== null) {
bytes = util.str2Uint8Array(util.bin2str(this.features));
bytes = util.str_to_Uint8Array(util.Uint8Array_to_str(this.features));
arr.push(write_sub_packet(sub.features, bytes));
}
if (this.signatureTargetPublicKeyAlgorithm !== null) {
bytes = [new Uint8Array([this.signatureTargetPublicKeyAlgorithm, this.signatureTargetHashAlgorithm])];
bytes.push(util.str2Uint8Array(this.signatureTargetHash));
bytes.push(util.str_to_Uint8Array(this.signatureTargetHash));
bytes = util.concatUint8Array(bytes);
arr.push(write_sub_packet(sub.signature_target, bytes));
}
@ -460,8 +459,8 @@ Signature.prototype.read_sub_packet = function (bytes) {
const n = util.readNumber(bytes.subarray(mypos, mypos + 2));
mypos += 2;
const name = util.Uint8Array2str(bytes.subarray(mypos, mypos + m));
const value = util.Uint8Array2str(bytes.subarray(mypos + m, mypos + m + n));
const name = util.Uint8Array_to_str(bytes.subarray(mypos, mypos + m));
const value = util.Uint8Array_to_str(bytes.subarray(mypos + m, mypos + m + n));
this.notation = this.notation || {};
this.notation[name] = value;
@ -483,7 +482,7 @@ Signature.prototype.read_sub_packet = function (bytes) {
break;
case 24:
// Preferred Key Server
this.preferredKeyServer = util.Uint8Array2str(bytes.subarray(mypos, bytes.length));
this.preferredKeyServer = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length));
break;
case 25:
// Primary User ID
@ -491,7 +490,7 @@ Signature.prototype.read_sub_packet = function (bytes) {
break;
case 26:
// Policy URI
this.policyURI = util.Uint8Array2str(bytes.subarray(mypos, bytes.length));
this.policyURI = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length));
break;
case 27:
// Key Flags
@ -499,12 +498,12 @@ Signature.prototype.read_sub_packet = function (bytes) {
break;
case 28:
// Signer's User ID
this.signersUserId += util.Uint8Array2str(bytes.subarray(mypos, bytes.length));
this.signersUserId += util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length));
break;
case 29:
// Reason for Revocation
this.reasonForRevocationFlag = bytes[mypos++];
this.reasonForRevocationString = util.Uint8Array2str(bytes.subarray(mypos, bytes.length));
this.reasonForRevocationString = util.Uint8Array_to_str(bytes.subarray(mypos, bytes.length));
break;
case 30:
// Features
@ -518,7 +517,7 @@ Signature.prototype.read_sub_packet = function (bytes) {
const len = crypto.getHashByteLength(this.signatureTargetHashAlgorithm);
this.signatureTargetHash = util.Uint8Array2str(bytes.subarray(mypos, mypos + len));
this.signatureTargetHash = util.Uint8Array_to_str(bytes.subarray(mypos, mypos + len));
break;
}
case 32:
@ -652,8 +651,7 @@ Signature.prototype.verify = async function (key, data) {
}
this.verified = await crypto.signature.verify(
publicKeyAlgorithm,
hashAlgorithm, mpi, key.params,
publicKeyAlgorithm, hashAlgorithm, mpi, key.params,
util.concatUint8Array([bytes, this.signatureData, trailer])
);

View File

@ -16,25 +16,25 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Sym. Encrypted Integrity Protected Data
* Packet (Tag 18)<br/>
* <br/>
* Implementation of the Sym. Encrypted Integrity Protected Data Packet (Tag 18)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.13|RFC4880 5.13}:
* The Symmetrically Encrypted Integrity Protected Data packet is
* a variant of the Symmetrically Encrypted Data packet. It is a new feature
* created for OpenPGP that addresses the problem of detecting a modification to
* encrypted data. It is used in combination with a Modification Detection Code
* packet.
* @requires asmcrypto.js
* @requires crypto
* @requires util
* @requires enums
* @requires util
* @module packet/sym_encrypted_integrity_protected
*/
import asmCrypto from 'asmcrypto-lite';
import util from '../util.js';
import { AES_CFB } from 'asmcrypto.js/src/aes/cfb/exports';
import crypto from '../crypto';
import enums from '../enums.js';
import enums from '../enums';
import util from '../util';
const nodeCrypto = util.getNodeCrypto();
const Buffer = util.getNodeBuffer();
@ -121,8 +121,8 @@ SymEncryptedIntegrityProtected.prototype.decrypt = function (sessionKeyAlgorithm
const prefix = crypto.cfb.mdc(sessionKeyAlgorithm, key, this.encrypted);
const bytes = decrypted.subarray(0, decrypted.length - 20);
const tohash = util.concatUint8Array([prefix, bytes]);
this.hash = util.Uint8Array2str(crypto.hash.sha1(tohash));
const mdc = util.Uint8Array2str(decrypted.subarray(decrypted.length - 20, decrypted.length));
this.hash = util.Uint8Array_to_str(crypto.hash.sha1(tohash));
const mdc = util.Uint8Array_to_str(decrypted.subarray(decrypted.length - 20, decrypted.length));
if (this.hash !== mdc) {
throw new Error('Modification detected.');
@ -145,7 +145,7 @@ function aesEncrypt(algo, prefix, pt, key) {
if (nodeCrypto) { // Node crypto library.
return nodeEncrypt(algo, prefix, pt, key);
} // asm.js fallback
return asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, pt]), key);
return AES_CFB.encrypt(util.concatUint8Array([prefix, pt]), key);
}
function aesDecrypt(algo, ct, key) {
@ -153,7 +153,7 @@ function aesDecrypt(algo, ct, key) {
if (nodeCrypto) { // Node crypto library.
pt = nodeDecrypt(algo, ct, key);
} else { // asm.js fallback
pt = asmCrypto.AES_CFB.decrypt(ct, key);
pt = AES_CFB.decrypt(ct, key);
}
return pt.subarray(crypto.cipher[algo].blockSize + 2, pt.length); // Remove random prefix
}

View File

@ -16,9 +16,10 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Public-Key Encrypted Session Key Packets (Tag 1)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}: A Public-Key Encrypted Session Key packet holds the session key
* Public-Key Encrypted Session Key Packets (Tag 1)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.1|RFC4880 5.1}:
* A Public-Key Encrypted Session Key packet holds the session key
* used to encrypt a message. Zero or more Public-Key Encrypted Session Key
* packets and/or Symmetric-Key Encrypted Session Key packets may precede a
* Symmetrically Encrypted Data Packet, which holds an encrypted message. The

View File

@ -16,10 +16,11 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the Symmetrically Encrypted Data Packet (Tag 9)<br/>
* <br/>
* {@link https://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}: The Symmetrically Encrypted Data packet contains data encrypted
* with a symmetric-key algorithm. When it has been decrypted, it contains other
* Implementation of the Symmetrically Encrypted Data Packet (Tag 9)
*
* {@link https://tools.ietf.org/html/rfc4880#section-5.7|RFC4880 5.7}:
* The Symmetrically Encrypted Data packet contains data encrypted with a
* symmetric-key algorithm. When it has been decrypted, it contains other
* packets (usually a literal data packet or compressed data packet, but in
* theory other Symmetrically Encrypted Data packets or sequences of packets
* that form whole OpenPGP messages).

View File

@ -16,15 +16,15 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the User Attribute Packet (Tag 17)<br/>
* <br/>
* Implementation of the User Attribute Packet (Tag 17)
*
* The User Attribute packet is a variation of the User ID packet. It
* is capable of storing more types of data than the User ID packet,
* which is limited to text. Like the User ID packet, a User Attribute
* packet may be certified by the key owner ("self-signed") or any other
* key owner who cares to certify it. Except as noted, a User Attribute
* packet may be used anywhere that a User ID packet may be used.
* <br/>
*
* While User Attribute packets are not a required part of the OpenPGP
* standard, implementations SHOULD provide at least enough
* compatibility to properly handle a certification signature on the
@ -58,7 +58,7 @@ UserAttribute.prototype.read = function(bytes) {
const len = packet.readSimpleLength(bytes.subarray(i, bytes.length));
i += len.offset;
this.attributes.push(util.Uint8Array2str(bytes.subarray(i, i + len.len)));
this.attributes.push(util.Uint8Array_to_str(bytes.subarray(i, i + len.len)));
i += len.len;
}
};
@ -71,7 +71,7 @@ UserAttribute.prototype.write = function() {
const arr = [];
for (let i = 0; i < this.attributes.length; i++) {
arr.push(packet.writeSimpleLength(this.attributes[i].length));
arr.push(util.str2Uint8Array(this.attributes[i]));
arr.push(util.str_to_Uint8Array(this.attributes[i]));
}
return util.concatUint8Array(arr);
};

View File

@ -16,8 +16,8 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the User ID Packet (Tag 13)<br/>
* <br/>
* Implementation of the User ID Packet (Tag 13)
*
* A User ID packet consists of UTF-8 text that is intended to represent
* the name and email address of the key holder. By convention, it
* includes an RFC 2822 [RFC2822] mail name-addr, but there are no
@ -48,7 +48,7 @@ export default function Userid() {
* @param {Uint8Array} input payload of a tag 13 packet
*/
Userid.prototype.read = function (bytes) {
this.userid = util.decode_utf8(util.Uint8Array2str(bytes));
this.userid = util.decode_utf8(util.Uint8Array_to_str(bytes));
};
/**
@ -56,5 +56,5 @@ Userid.prototype.read = function (bytes) {
* @return {Uint8Array} binary representation
*/
Userid.prototype.write = function () {
return util.str2Uint8Array(util.encode_utf8(this.userid));
return util.str_to_Uint8Array(util.encode_utf8(this.userid));
};

View File

@ -16,24 +16,22 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Encoded symmetric key for ECDH<br/>
* <br/>
* Encoded symmetric key for ECDH
*
* @requires util
* @module type/ecdh_symkey
*/
import util from '../util';
module.exports = ECDHSymmetricKey;
/**
* @constructor
*/
function ECDHSymmetricKey(data) {
export default function ECDHSymmetricKey(data) {
if (typeof data === 'undefined') {
data = new Uint8Array([]);
} else if (util.isString(data)) {
data = util.str2Uint8Array(data);
data = util.str_to_Uint8Array(data);
} else {
data = new Uint8Array(data);
}

View File

@ -16,16 +16,14 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of type KDF parameters RFC 6637<br/>
* <br/>
* Implementation of type KDF parameters RFC 6637
*
* @requires enums
* @module type/kdf_params
*/
import enums from '../enums.js';
module.exports = KDFParams;
/**
* @constructor
* @param {enums.hash} hash Hash algorithm
@ -66,3 +64,5 @@ KDFParams.prototype.write = function () {
KDFParams.fromClone = function (clone) {
return new KDFParams(clone.hash, clone.cipher);
};
export default KDFParams;

View File

@ -16,8 +16,9 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of type key id ({@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3})<br/>
* <br/>
* Implementation of type key id
*
* {@link https://tools.ietf.org/html/rfc4880#section-3.3|RFC4880 3.3}:
* A Key ID is an eight-octet scalar that identifies a key.
* Implementations SHOULD NOT assume that Key IDs are unique. The
* section "Enhanced Key Formats" below describes how Key IDs are
@ -40,15 +41,15 @@ export default function Keyid() {
* @param {Uint8Array} input Input to read the key id from
*/
Keyid.prototype.read = function(bytes) {
this.bytes = util.Uint8Array2str(bytes.subarray(0, 8));
this.bytes = util.Uint8Array_to_str(bytes.subarray(0, 8));
};
Keyid.prototype.write = function() {
return util.str2Uint8Array(this.bytes);
return util.str_to_Uint8Array(this.bytes);
};
Keyid.prototype.toHex = function() {
return util.hexstrdump(this.bytes);
return util.str_to_hex(this.bytes);
};
Keyid.prototype.equals = function(keyid) {
@ -75,7 +76,7 @@ Keyid.fromClone = function (clone) {
Keyid.fromId = function (hex) {
const keyid = new Keyid();
keyid.read(util.str2Uint8Array(util.hex2bin(hex)));
keyid.read(util.str_to_Uint8Array(util.hex_to_str(hex)));
return keyid;
};

View File

@ -21,20 +21,19 @@
// - MPI = c | d << 8 | e << ((MPI.length -2)*8) | f ((MPI.length -2)*8)
/**
* Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})<br/>
* <br/>
* Implementation of type MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2})
* Multiprecision integers (also called MPIs) are unsigned integers used
* to hold large integers such as the ones used in cryptographic
* calculations.
* An MPI consists of two pieces: a two-octet scalar that is the length
* of the MPI in bits followed by a string of octets that contain the
* actual integer.
* @requires crypto/public_key/jsbn
* @requires bn.js
* @requires util
* @module type/mpi
*/
import BigInteger from '../crypto/public_key/jsbn';
import BN from 'bn.js';
import util from '../util';
/**
@ -42,83 +41,99 @@ import util from '../util';
*/
export default function MPI(data) {
/** An implementation dependent integer */
if (data instanceof BigInteger) {
this.fromBigInteger(data);
if (BN.isBN(data)) {
this.fromBN(data);
} else if (util.isUint8Array(data)) {
this.fromUint8Array(data);
} else if (util.isString(data)) {
this.fromBytes(data);
this.fromString(data);
} else {
this.data = null;
}
}
/**
* Parsing function for a mpi ({@link https://tools.ietf.org/html/rfc4880#section3.2|RFC 4880 3.2}).
* @param {String} input Payload of mpi data
* @param {String} endian Endianness of the payload; 'be' for big-endian and 'le' for little-endian
* @return {Integer} Length of data read
* Parsing function for a MPI ({@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC 4880 3.2}).
* @param {Uint8Array} input Payload of MPI data
* @param {String} endian Endianness of the data; 'be' for big-endian or 'le' for little-endian
* @return {Integer} Length of data read
*/
MPI.prototype.read = function (bytes, endian='be') {
if (util.isString(bytes)) {
bytes = util.str2Uint8Array(bytes);
bytes = util.str_to_Uint8Array(bytes);
}
const bits = (bytes[0] << 8) | bytes[1];
const bytelen = (bits + 7) >>> 3;
const payload = bytes.subarray(2, 2 + bytelen);
// Additional rules:
//
// The size of an MPI is ((MPI.length + 7) / 8) + 2 octets.
//
// The length field of an MPI describes the length starting from its
// most significant non-zero bit. Thus, the MPI [00 02 01] is not
// formed correctly. It should be [00 01 01].
// TODO: Verification of this size method! This size calculation as
// specified above is not applicable in JavaScript
const bytelen = Math.ceil(bits / 8);
let payload = bytes.subarray(2, 2 + bytelen);
if (endian === 'le') {
payload = new Uint8Array(payload).reverse();
}
const raw = util.Uint8Array2str(payload);
this.fromBytes(raw);
this.fromUint8Array(payload, endian);
return 2 + bytelen;
};
MPI.prototype.fromBytes = function (bytes) {
this.data = new BigInteger(util.hexstrdump(bytes), 16);
/**
* Converts the mpi object to a bytes as specified in
* {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2}
* @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian
* @param {Integer} length Length of the data part of the MPI
* @return {Uint8Aray} mpi Byte representation
*/
MPI.prototype.write = function (endian, length) {
return util.Uint8Array_to_MPI(this.toUint8Array(endian, length));
};
MPI.prototype.toBytes = function () {
const bytes = util.Uint8Array2str(this.write());
return bytes.substr(2);
MPI.prototype.bitLength = function () {
return (this.data.length - 1) * 8 + util.nbits(this.data[0]);
};
MPI.prototype.byteLength = function () {
return this.toBytes().length;
return this.data.length;
};
/**
* Converts the mpi object to a bytes as specified in {@link https://tools.ietf.org/html/rfc4880#section-3.2|RFC4880 3.2}
* @return {Uint8Aray} mpi Byte representation
*/
MPI.prototype.write = function () {
return util.str2Uint8Array(this.data.toMPI());
MPI.prototype.toUint8Array = function (endian, length) {
endian = endian || 'be';
length = length || this.data.length;
const payload = new Uint8Array(length);
const start = length - this.data.length;
if (start < 0) {
throw new Error('Payload is too large.');
}
payload.set(this.data, start);
if (endian === 'le') {
payload.reverse();
}
return payload;
};
MPI.prototype.toBigInteger = function () {
return this.data.clone();
MPI.prototype.fromUint8Array = function (bytes, endian='be') {
this.data = new Uint8Array(bytes.length);
this.data.set(bytes);
if (endian === 'le') {
this.data.reverse();
}
};
MPI.prototype.fromBigInteger = function (bn) {
this.data = bn.clone();
MPI.prototype.toString = function () {
return util.Uint8Array_to_str(this.toUint8Array());
};
MPI.prototype.fromString = function (str, endian='be') {
this.fromUint8Array(util.str_to_Uint8Array(str), endian);
};
MPI.prototype.toBN = function () {
return new BN(this.toUint8Array());
};
MPI.prototype.fromBN = function (bn) {
this.data = bn.toArrayLike(Uint8Array);
};
MPI.fromClone = function (clone) {
clone.data.copyTo = BigInteger.prototype.copyTo;
const bn = new BigInteger();
clone.data.copyTo(bn);
const mpi = new MPI();
mpi.data = bn;
return mpi;
return new MPI(clone.data);
};

View File

@ -16,27 +16,30 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Wrapper to an OID value<br/>
* <br/>
* An object identifier type from {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}.
* Wrapper to an OID value
*
* An object identifier type from
* {@link https://tools.ietf.org/html/rfc6637#section-11|RFC6637, section 11}.
* @requires util
* @requires enums
* @module type/oid
*/
import util from '../util.js';
module.exports = OID;
import util from '../util';
import enums from '../enums';
/**
* @constructor
*/
function OID(oid) {
if (typeof oid === 'undefined') {
if (oid instanceof OID) {
oid = oid.oid;
} else if (typeof oid === 'undefined') {
oid = '';
} else if (util.isArray(oid)) {
oid = util.bin2str(oid);
oid = util.Uint8Array_to_str(oid);
} else if (util.isUint8Array(oid)) {
oid = util.Uint8Array2str(oid);
oid = util.Uint8Array_to_str(oid);
}
this.oid = oid;
}
@ -50,7 +53,7 @@ OID.prototype.read = function (input) {
if (input.length >= 1) {
const length = input[0];
if (input.length >= 1+length) {
this.oid = util.Uint8Array2str(input.subarray(1, 1+length));
this.oid = util.Uint8Array_to_str(input.subarray(1, 1+length));
return 1+this.oid.length;
}
}
@ -62,7 +65,7 @@ OID.prototype.read = function (input) {
* @return {Uint8Array} Array with the serialized value the OID
*/
OID.prototype.write = function () {
return util.str2Uint8Array(String.fromCharCode(this.oid.length)+this.oid);
return util.str_to_Uint8Array(String.fromCharCode(this.oid.length)+this.oid);
};
/**
@ -70,10 +73,25 @@ OID.prototype.write = function () {
* @return {string} String with the hex value of the OID
*/
OID.prototype.toHex = function() {
return util.hexstrdump(this.oid);
return util.str_to_hex(this.oid);
};
/**
* If a known curve object identifier, return the canonical name of the curve
* @return {string} String with the canonical name of the curve
*/
OID.prototype.getName = function() {
const hex = this.toHex();
if (enums.curve[hex]) {
return enums.write(enums.curve, hex);
} else {
throw new Error('Unknown curve object identifier.');
}
};
OID.fromClone = function (clone) {
const oid = new OID(clone.oid);
return oid;
};
export default OID;

View File

@ -16,8 +16,9 @@
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
/**
* Implementation of the String-to-key specifier ({@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7})<br/>
* <br/>
* Implementation of the String-to-key specifier
*
* {@link https://tools.ietf.org/html/rfc4880#section-3.7|RFC4880 3.7}:
* String-to-key (S2K) specifiers are used to convert passphrase strings
* into symmetric-key encryption/decryption keys. They are used in two
* places, currently: to encrypt the secret part of private keys in the
@ -83,7 +84,7 @@ S2K.prototype.read = function (bytes) {
break;
case 'gnu':
if (util.Uint8Array2str(bytes.subarray(i, 3)) === "GNU") {
if (util.Uint8Array_to_str(bytes.subarray(i, 3)) === "GNU") {
i += 3; // GNU
const gnuExtType = 1000 + bytes[i++];
if (gnuExtType === 1001) {
@ -139,7 +140,7 @@ S2K.prototype.write = function () {
* hashAlgorithm hash length
*/
S2K.prototype.produce_key = function (passphrase, numBytes) {
passphrase = util.str2Uint8Array(util.encode_utf8(passphrase));
passphrase = util.str_to_Uint8Array(util.encode_utf8(passphrase));
function round(prefix, s2k) {
const algorithm = enums.write(enums.hash, s2k.algorithm);

View File

@ -37,21 +37,6 @@ export default {
return Uint8Array.prototype.isPrototypeOf(data);
},
isEmailAddress: function(data) {
if (!this.isString(data)) {
return false;
}
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}|xn--[a-zA-Z\-0-9]+)))$/;
return re.test(data);
},
isUserId: function(data) {
if (!this.isString(data)) {
return false;
}
return /</.test(data) && />$/.test(data);
},
/**
* Get transferable objects to pass buffers with zero copy (similar to "pass by reference" in C++)
* See: https://developer.mozilla.org/en-US/docs/Web/API/Worker/postMessage
@ -114,32 +99,12 @@ export default {
return time === null ? time : new Date(Math.floor(+time / 1000) * 1000);
},
hexdump: function (str) {
const r = [];
const e = str.length;
let c = 0;
let h;
let i = 0;
while (c < e) {
h = str.charCodeAt(c++).toString(16);
while (h.length < 2) {
h = "0" + h;
}
r.push(" " + h);
i++;
if (i % 32 === 0) {
r.push("\n ");
}
}
return r.join('');
},
/**
* Create hexstring from a binary
* Create hex string from a binary
* @param {String} str String to convert
* @return {String} String containing the hexadecimal values
*/
hexstrdump: function (str) {
str_to_hex: function (str) {
if (str === null) {
return "";
}
@ -160,9 +125,9 @@ export default {
/**
* Create binary string from a hex encoded string
* @param {String} str Hex string to convert
* @return {String} String containing the binary values
* @return {String}
*/
hex2bin: function (hex) {
hex_to_str: function (hex) {
let str = '';
for (let i = 0; i < hex.length; i += 2) {
str += String.fromCharCode(parseInt(hex.substr(i, 2), 16));
@ -170,27 +135,68 @@ export default {
return str;
},
/**
* Convert a Uint8Array to an MPI-formatted Uint8Array.
* Note: the output is **not** an MPI object.
* @see {@link module:type/mpi/MPI.fromUint8Array}
* @see {@link module:type/mpi/MPI.toUint8Array}
* @param {Uint8Array} bin An array of 8-bit integers to convert
* @return {Uint8Array} MPI-formatted Uint8Array
*/
Uint8Array_to_MPI: function (bin) {
const size = (bin.length - 1) * 8 + this.nbits(bin[0]);
const prefix = Uint8Array.from([(size & 0xFF00) >> 8, size & 0xFF]);
return this.concatUint8Array([prefix, bin]);
},
hex2Uint8Array: function (hex) {
const result = new Uint8Array(hex.length/2);
for (let k=0; k<hex.length/2; k++) {
result[k] = parseInt(hex.substr(2*k, 2), 16);
/**
* Convert a Base-64 encoded string an array of 8-bit integer
*
* Note: accepts both Radix-64 and URL-safe strings
* @param {String} base64 Base-64 encoded string to convert
* @return {Uint8Array} An array of 8-bit integers
*/
b64_to_Uint8Array: function (base64) {
const str = atob(base64.replace(/\-/g, '+').replace(/_/g, '/'));
return this.str_to_Uint8Array(str);
},
/**
* Convert an array of 8-bit integer to a Base-64 encoded string
* @param {Uint8Array} bytes An array of 8-bit integers to convert
* @param {bool} url If true, output is URL-safe
* @return {String} Base-64 encoded string
*/
Uint8Array_to_b64: function (bytes, url) {
const base64 = btoa(this.Uint8Array_to_str(bytes));
return url ? base64.replace(/\+/g, '-').replace(/\//g, '_') : base64;
},
/**
* Convert a hex string to an array of 8-bit integers
* @param {String} hex A hex string to convert
* @return {Uint8Array} An array of 8-bit integers
*/
hex_to_Uint8Array: function (hex) {
const result = new Uint8Array(hex.length >> 1);
for (let k = 0; k < hex.length >> 1; k++) {
result[k] = parseInt(hex.substr(k << 1, 2), 16);
}
return result;
},
/**
* Creating a hex string from an binary array of integers (0..255)
* @param {String} str Array of bytes to convert
* Convert an array of 8-bit integers to a hex string
* @param {Uint8Array} bytes Array of 8-bit integers to convert
* @return {String} Hexadecimal representation of the array
*/
hexidump: function (str) {
Uint8Array_to_hex: function (bytes) {
const r = [];
const e = str.length;
const e = bytes.length;
let c = 0;
let h;
while (c < e) {
h = str[c++].toString(16);
h = bytes[c++].toString(16);
while (h.length < 2) {
h = "0" + h;
}
@ -199,6 +205,39 @@ export default {
return r.join('');
},
/**
* Convert a string to an array of 8-bit integers
* @param {String} str String to convert
* @return {Uint8Array} An array of 8-bit integers
*/
str_to_Uint8Array: function (str) {
if (!this.isString(str)) {
throw new Error('str_to_Uint8Array: Data must be in the form of a string');
}
const result = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
result[i] = str.charCodeAt(i);
}
return result;
},
/**
* Convert an array of 8-bit integers to a string
* @param {Uint8Array} bytes An array of 8-bit integers to convert
* @return {String} String representation of the array
*/
Uint8Array_to_str: function (bytes) {
bytes = new Uint8Array(bytes);
const result = [];
const bs = 1 << 14;
const j = bytes.length;
for (let i = 0; i < j; i += bs) {
result.push(String.fromCharCode.apply(String, bytes.subarray(i, i+bs < j ? i+bs : j)));
}
return result.join('');
},
/**
* Convert a native javascript string to a string of utf8 bytes
@ -225,129 +264,20 @@ export default {
}
},
/**
* Convert an array of integers(0.255) to a string
* @param {Array<Integer>} bin An array of (binary) integers to convert
* @return {String} The string representation of the array
*/
bin2str: function (bin) {
const result = [];
for (let i = 0; i < bin.length; i++) {
result[i] = String.fromCharCode(bin[i]);
}
return result.join('');
},
/**
* Convert a string to an array of integers(0.255)
* @param {String} str String to convert
* @return {Array<Integer>} An array of (binary) integers
*/
str2bin: function (str) {
const result = [];
for (let i = 0; i < str.length; i++) {
result[i] = str.charCodeAt(i);
}
return result;
},
/**
* Convert a string to a Uint8Array
* @param {String} str String to convert
* @return {Uint8Array} The array of (binary) integers
*/
str2Uint8Array: function (str) {
if (typeof str !== 'string' && !String.prototype.isPrototypeOf(str)) {
throw new Error('str2Uint8Array: Data must be in the form of a string');
}
const result = new Uint8Array(str.length);
for (let i = 0; i < str.length; i++) {
result[i] = str.charCodeAt(i);
}
return result;
},
/**
* Convert a Uint8Array to a string. This currently functions
* the same as bin2str.
* @function module:util.Uint8Array2str
* @param {Uint8Array} bin An array of (binary) integers to convert
* @return {String} String representation of the array
*/
Uint8Array2str: function (bin) {
if (!Uint8Array.prototype.isPrototypeOf(bin)) {
throw new Error('Uint8Array2str: Data must be in the form of a Uint8Array');
}
const result = [];
const bs = 16384;
const j = bin.length;
for (let i = 0; i < j; i += bs) {
result.push(String.fromCharCode.apply(String, bin.subarray(i, i+bs < j ? i+bs : j)));
}
return result.join('');
},
// returns bit length of the integer x
nbits: function (x) {
let r = 1;
let t = x >>> 16;
if (t !== 0) {
x = t;
r += 16;
}
t = x >> 8;
if (t !== 0) {
x = t;
r += 8;
}
t = x >> 4;
if (t !== 0) {
x = t;
r += 4;
}
t = x >> 2;
if (t !== 0) {
x = t;
r += 2;
}
t = x >> 1;
if (t !== 0) {
x = t;
r += 1;
}
return r;
},
/**
* Convert a Uint8Array to an MPI array.
* @function module:util.Uint8Array2MPI
* @param {Uint8Array} bin An array of (binary) integers to convert
* @return {Array<Integer>} MPI-formatted array
*/
Uint8Array2MPI: function (bin) {
const size = (bin.length - 1) * 8 + this.nbits(bin[0]);
return [(size & 0xFF00) >> 8, size & 0xFF].concat(Array.from(bin));
},
/**
* Concat Uint8arrays
* @function module:util.concatUint8Array
* @param {Array<Uint8array>} Array of Uint8Arrays to concatenate
* @return {Uint8array} Concatenated array
*/
concatUint8Array: function (arrays) {
let totalLength = 0;
arrays.forEach(function (element) {
if (!Uint8Array.prototype.isPrototypeOf(element)) {
for (let i = 0; i < arrays.length; i++) {
if (!this.isUint8Array(arrays[i])) {
throw new Error('concatUint8Array: Data must be in the form of a Uint8Array');
}
totalLength += element.length;
});
totalLength += arrays[i].length;
}
const result = new Uint8Array(totalLength);
let pos = 0;
@ -361,12 +291,11 @@ export default {
/**
* Deep copy Uint8Array
* @function module:util.copyUint8Array
* @param {Uint8Array} Array to copy
* @return {Uint8Array} new Uint8Array
*/
copyUint8Array: function (array) {
if (!Uint8Array.prototype.isPrototypeOf(array)) {
if (!this.isUint8Array(array)) {
throw new Error('Data must be in the form of a Uint8Array');
}
@ -377,13 +306,12 @@ export default {
/**
* Check Uint8Array equality
* @function module:util.equalsUint8Array
* @param {Uint8Array} first array
* @param {Uint8Array} second array
* @return {Boolean} equality
*/
equalsUint8Array: function (array1, array2) {
if (!Uint8Array.prototype.isPrototypeOf(array1) || !Uint8Array.prototype.isPrototypeOf(array2)) {
if (!this.isUint8Array(array1) || !this.isUint8Array(array2)) {
throw new Error('Data must be in the form of a Uint8Array');
}
@ -435,16 +363,17 @@ export default {
* Helper function to print a debug message. Debug
* messages are only printed if
* @link module:config/config.debug is set to true.
* Different than print_debug because will call hexstrdump iff necessary.
* Different than print_debug because will call str_to_hex iff necessary.
* @param {String} str String of the debug message
*/
print_debug_hexstr_dump: function (str, strToHex) {
if (config.debug) {
str += this.hexstrdump(strToHex);
str += this.str_to_hex(strToHex);
console.log(str);
}
},
// TODO rewrite getLeftNBits to work with Uint8Arrays
getLeftNBits: function (string, bitcount) {
const rest = bitcount % 8;
if (rest === 0) {
@ -455,6 +384,37 @@ export default {
return this.shiftRight(result, 8 - rest); // +String.fromCharCode(string.charCodeAt(bytes -1) << (8-rest) & 0xFF);
},
// returns bit length of the integer x
nbits: function (x) {
let r = 1;
let t = x >>> 16;
if (t !== 0) {
x = t;
r += 16;
}
t = x >> 8;
if (t !== 0) {
x = t;
r += 8;
}
t = x >> 4;
if (t !== 0) {
x = t;
r += 4;
}
t = x >> 2;
if (t !== 0) {
x = t;
r += 2;
}
t = x >> 1;
if (t !== 0) {
x = t;
r += 1;
}
return r;
},
/**
* Shifting a string to n bits right
* @param {String} value The string to shift
@ -463,7 +423,7 @@ export default {
* @return {String} Resulting string.
*/
shiftRight: function (value, bitcount) {
const temp = this.str2bin(value);
const temp = this.str_to_Uint8Array(value);
if (bitcount % 8 !== 0) {
for (let i = temp.length - 1; i >= 0; i--) {
temp[i] >>= bitcount % 8;
@ -474,31 +434,7 @@ export default {
} else {
return value;
}
return this.bin2str(temp);
},
/**
* Return the algorithm type as string
* @return {String} String representing the message type
*/
get_hashAlgorithmString: function (algo) {
switch (algo) {
case 1:
return "MD5";
case 2:
return "SHA1";
case 3:
return "RIPEMD160";
case 8:
return "SHA256";
case 9:
return "SHA384";
case 10:
return "SHA512";
case 11:
return "SHA224";
}
return "unknown";
return this.Uint8Array_to_str(temp);
},
/**
@ -537,25 +473,6 @@ export default {
}
},
/**
* Converts an IE11 web crypto api result to a promise.
* This is required since IE11 implements an old version of the
* Web Crypto specification that does not use promises.
* @param {Object} cryptoOp The return value of an IE11 web cryptro api call
* @param {String} errmsg An error message for a specific operation
* @return {Promise} The resulting Promise
*/
promisifyIE11Op: function(cryptoOp, errmsg) {
return new Promise(function(resolve, reject) {
cryptoOp.onerror = function () {
reject(new Error(errmsg));
};
cryptoOp.oncomplete = function (e) {
resolve(e.target.result);
};
});
},
/**
* Detect Node.js runtime.
*/
@ -598,5 +515,20 @@ export default {
}
return require('zlib');
},
isEmailAddress: function(data) {
if (!this.isString(data)) {
return false;
}
const re = /^(([^<>()[\]\\.,;:\s@\"]+(\.[^<>()[\]\\.,;:\s@\"]+)*)|(\".+\"))@((\[[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\.[0-9]{1,3}\])|(([a-zA-Z\-0-9]+\.)+([a-zA-Z]{2,}|xn--[a-zA-Z\-0-9]+)))$/;
return re.test(data);
},
isUserId: function(data) {
if (!this.isString(data)) {
return false;
}
return /</.test(data) && />$/.test(data);
}
};

View File

@ -44,13 +44,13 @@ describe('AES Key Wrap and Unwrap', function () {
test_vectors.forEach(function(test) {
it(test[0], function(done) {
const kek = openpgp.util.hex2Uint8Array(test[1]);
const kek = openpgp.util.hex_to_Uint8Array(test[1]);
const input = test[2].replace(/\s/g, "");
const input_bin = openpgp.util.hex2bin(input);
const input_bin = openpgp.util.hex_to_str(input);
const output = test[3].replace(/\s/g, "");
const output_bin = openpgp.util.hex2bin(output);
expect(openpgp.util.hexidump(openpgp.crypto.aes_kw.wrap(kek, input_bin)).toUpperCase()).to.equal(output);
expect(openpgp.util.hexidump(openpgp.crypto.aes_kw.unwrap(kek, output_bin)).toUpperCase()).to.equal(input);
const output_bin = openpgp.util.hex_to_str(output);
expect(openpgp.util.Uint8Array_to_hex(openpgp.crypto.aes_kw.wrap(kek, input_bin)).toUpperCase()).to.equal(output);
expect(openpgp.util.Uint8Array_to_hex(openpgp.crypto.aes_kw.unwrap(kek, output_bin)).toUpperCase()).to.equal(input);
done();
});
});

View File

@ -9,9 +9,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function
function test_aes(input, key, output) {
const aes = new openpgp.crypto.cipher.aes128(key);
const result = util.bin2str(aes.encrypt(new Uint8Array(input)));
const result = util.Uint8Array_to_str(aes.encrypt(new Uint8Array(input)));
return util.hexstrdump(result) === util.hexstrdump(util.bin2str(output));
return util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output));
}
const testvectors128 = [[[0x00,0x01,0x02,0x03,0x05,0x06,0x07,0x08,0x0A,0x0B,0x0C,0x0D,0x0F,0x10,0x11,0x12],[0x50,0x68,0x12,0xA4,0x5F,0x08,0xC8,0x89,0xB9,0x7F,0x59,0x80,0x03,0x8B,0x83,0x59],[0xD8,0xF5,0x32,0x53,0x82,0x89,0xEF,0x7D,0x06,0xB5,0x06,0xA4,0xFD,0x5B,0xE9,0xC9]],
@ -65,9 +65,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function
it('128 bit key', function (done) {
for (let i = 0; i < testvectors128.length; i++) {
const res = test_aes(testvectors128[i][1],testvectors128[i][0],testvectors128[i][2]);
expect(res, 'block ' + util.hexidump(testvectors128[i][1]) +
' and key '+util.hexidump(testvectors128[i][0]) +
' should be '+util.hexidump(testvectors128[i][2])).to.be.true;
expect(res, 'block ' + util.Uint8Array_to_hex(testvectors128[i][1]) +
' and key '+util.Uint8Array_to_hex(testvectors128[i][0]) +
' should be '+util.Uint8Array_to_hex(testvectors128[i][2])).to.be.true;
}
done();
});
@ -75,9 +75,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function
it('192 bit key', function (done) {
for (let i = 0; i < testvectors192.length; i++) {
const res = test_aes(testvectors192[i][1],testvectors192[i][0],testvectors192[i][2]);
expect(res, 'block ' + util.hexidump(testvectors192[i][1]) +
' and key ' + util.hexidump(testvectors192[i][0])+
' should be ' + util.hexidump(testvectors192[i][2])).to.be.true;
expect(res, 'block ' + util.Uint8Array_to_hex(testvectors192[i][1]) +
' and key ' + util.Uint8Array_to_hex(testvectors192[i][0])+
' should be ' + util.Uint8Array_to_hex(testvectors192[i][2])).to.be.true;
}
done();
});
@ -85,9 +85,9 @@ describe('AES Rijndael cipher test with test vectors from ecb_tbl.txt', function
it('256 bit key', function (done) {
for (let i = 0; i < testvectors256.length; i++) {
const res = test_aes(testvectors256[i][1],testvectors256[i][0],testvectors256[i][2]);
expect(res, 'block ' + util.hexidump(testvectors256[i][1]) +
' and key ' + util.hexidump(testvectors256[i][0]) +
' should be ' + util.hexidump(testvectors256[i][2])).to.be.true;
expect(res, 'block ' + util.Uint8Array_to_hex(testvectors256[i][1]) +
' and key ' + util.Uint8Array_to_hex(testvectors256[i][0]) +
' should be ' + util.Uint8Array_to_hex(testvectors256[i][2])).to.be.true;
}
done();
});

View File

@ -8,10 +8,10 @@ const { expect } = chai;
it('Blowfish cipher test with test vectors from https://www.schneier.com/code/vectors.txt', function(done) {
function test_bf(input, key, output) {
const blowfish = new openpgp.crypto.cipher.blowfish(util.bin2str(key));
const result = util.bin2str(blowfish.encrypt(input));
const blowfish = new openpgp.crypto.cipher.blowfish(util.Uint8Array_to_str(key));
const result = util.Uint8Array_to_str(blowfish.encrypt(input));
return (util.hexstrdump(result) === util.hexstrdump(util.bin2str(output)));
return (util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output)));
}
const testvectors = [[[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00],[0x4E,0xF9,0x97,0x45,0x61,0x98,0xDD,0x78]],
@ -51,9 +51,9 @@ it('Blowfish cipher test with test vectors from https://www.schneier.com/code/ve
for (let i = 0; i < testvectors.length; i++) {
const res = test_bf(testvectors[i][1],testvectors[i][0],testvectors[i][2]);
expect(res, 'vector '+ i + '" with block ' + util.hexidump(testvectors[i][0])+
' and key ' + util.hexidump(testvectors[i][1]) +
' should be ' + util.hexidump(testvectors[i][2]), false);
expect(res, 'vector '+ i + '" with block ' + util.Uint8Array_to_hex(testvectors[i][0])+
' and key ' + util.Uint8Array_to_hex(testvectors[i][1]) +
' should be ' + util.Uint8Array_to_hex(testvectors[i][2]), false);
}
done();
});

View File

@ -8,18 +8,18 @@ const { expect } = chai;
it('CAST-128 cipher test with test vectors from RFC2144', function (done) {
function test_cast(input, key, output) {
const cast5 = new openpgp.crypto.cipher.cast5(key);
const result = util.bin2str(cast5.encrypt(input));
const result = util.Uint8Array_to_str(cast5.encrypt(input));
return util.hexstrdump(result) === util.hexstrdump(util.bin2str(output));
return util.str_to_hex(result) === util.str_to_hex(util.Uint8Array_to_str(output));
}
const testvectors = [[[0x01,0x23,0x45,0x67,0x12,0x34,0x56,0x78,0x23,0x45,0x67,0x89,0x34,0x56,0x78,0x9A],[0x01,0x23,0x45,0x67,0x89,0xAB,0xCD,0xEF],[0x23,0x8B,0x4F,0xE5,0x84,0x7E,0x44,0xB2]]];
for (let i = 0; i < testvectors.length; i++) {
const res = test_cast(testvectors[i][1],testvectors[i][0],testvectors[i][2]);
expect(res, 'vector with block ' + util.hexidump(testvectors[i][0]) +
' and key ' + util.hexidump(testvectors[i][1]) +
' should be ' + util.hexidump(testvectors[i][2])).to.be.true;
expect(res, 'vector with block ' + util.Uint8Array_to_hex(testvectors[i][0]) +
' and key ' + util.Uint8Array_to_hex(testvectors[i][1]) +
' should be ' + util.Uint8Array_to_hex(testvectors[i][2])).to.be.true;
}
done();
});

View File

@ -77,12 +77,12 @@ describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', fu
for (let i = 0; i < testvectors.length; i++) {
const des = new openpgp.crypto.cipher.tripledes(key);
const encr = util.bin2str(des.encrypt(testvectors[i][0], key));
const encr = util.Uint8Array_to_str(des.encrypt(testvectors[i][0], key));
expect(encr, 'vector with block ' + util.hexidump(testvectors[i][0]) +
' and key ' + util.hexstrdump(util.Uint8Array2str(key)) +
' should be ' + util.hexidump(testvectors[i][1]) +
' != ' + util.hexidump(encr)).to.be.equal(util.bin2str(testvectors[i][1]));
expect(encr, 'vector with block ' + util.Uint8Array_to_hex(testvectors[i][0]) +
' and key ' + util.str_to_hex(util.Uint8Array_to_str(key)) +
' should be ' + util.Uint8Array_to_hex(testvectors[i][1]) +
' != ' + util.Uint8Array_to_hex(encr)).to.be.equal(util.Uint8Array_to_str(testvectors[i][1]));
}
done();
});
@ -124,18 +124,18 @@ describe('TripleDES (EDE) cipher test with test vectors from NIST SP 800-20', fu
const encrypted = des.encrypt(thisVectorSet[i][0], padding);
const decrypted = des.decrypt(encrypted, padding);
expect(util.bin2str(encrypted), 'vector with block [' + util.hexidump(thisVectorSet[i][0]) +
'] and key [' + util.hexstrdump(util.Uint8Array2str(key)) +
expect(util.Uint8Array_to_str(encrypted), 'vector with block [' + util.Uint8Array_to_hex(thisVectorSet[i][0]) +
'] and key [' + util.str_to_hex(util.Uint8Array_to_str(key)) +
'] and padding [' + padding +
'] should be ' + util.hexidump(thisVectorSet[i][1]) +
' - Actually [' + util.hexidump(encrypted) +
']').to.equal(util.bin2str(thisVectorSet[i][1]));
expect(util.bin2str(decrypted), 'vector with block [' + util.hexidump(thisVectorSet[i][0]) +
'] and key [' + util.hexstrdump(util.Uint8Array2str(key)) +
'] should be ' + util.Uint8Array_to_hex(thisVectorSet[i][1]) +
' - Actually [' + util.Uint8Array_to_hex(encrypted) +
']').to.equal(util.Uint8Array_to_str(thisVectorSet[i][1]));
expect(util.Uint8Array_to_str(decrypted), 'vector with block [' + util.Uint8Array_to_hex(thisVectorSet[i][0]) +
'] and key [' + util.str_to_hex(util.Uint8Array_to_str(key)) +
'] and padding [' + padding +
'] should be ' + util.hexidump(thisVectorSet[i][0]) +
' - Actually [' + util.hexidump(decrypted) +
']').to.equal(util.bin2str(thisVectorSet[i][0]));
'] should be ' + util.Uint8Array_to_hex(thisVectorSet[i][0]) +
' - Actually [' + util.Uint8Array_to_hex(decrypted) +
']').to.equal(util.Uint8Array_to_str(thisVectorSet[i][0]));
}
}
done();

View File

@ -7,7 +7,7 @@ const { expect } = chai;
it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt', function(done) {
function TFencrypt(block, key) {
const tf = new openpgp.crypto.cipher.twofish(util.str2Uint8Array(key));
const tf = new openpgp.crypto.cipher.twofish(util.str_to_Uint8Array(key));
return tf.encrypt(block);
}
@ -36,36 +36,36 @@ it('Twofish with test vectors from https://www.schneier.com/code/ecb_ival.txt',
if (i === 0) {
blk = start_short;
key = util.bin2str(start);
key = util.Uint8Array_to_str(start);
ct = testvectors[0];
res = util.bin2str(TFencrypt(blk,key));
exp = util.bin2str(ct);
res = util.Uint8Array_to_str(TFencrypt(blk,key));
exp = util.Uint8Array_to_str(ct);
} else if (i === 1) {
blk = testvectors[0];
key = util.bin2str(start);
key = util.Uint8Array_to_str(start);
ct = testvectors[1];
res = util.bin2str(TFencrypt(blk,key));
exp = util.bin2str(ct);
res = util.Uint8Array_to_str(TFencrypt(blk,key));
exp = util.Uint8Array_to_str(ct);
} else if (i === 2) {
blk = testvectors[i-1];
key = util.bin2str(testvectors[i-2].concat(start_short));
key = util.Uint8Array_to_str(testvectors[i-2].concat(start_short));
ct = testvectors[i];
res = util.bin2str(TFencrypt(blk,key));
exp = util.bin2str(ct);
res = util.Uint8Array_to_str(TFencrypt(blk,key));
exp = util.Uint8Array_to_str(ct);
} else if (i < 10 || i > 46) {
blk = testvectors[i-1];
key = util.bin2str(testvectors[i-2].concat(testvectors[i-3]));
key = util.Uint8Array_to_str(testvectors[i-2].concat(testvectors[i-3]));
ct = testvectors[i];
res = util.bin2str(TFencrypt(blk,key));
exp = util.bin2str(ct);
res = util.Uint8Array_to_str(TFencrypt(blk,key));
exp = util.Uint8Array_to_str(ct);
} else {
testvectors[i] = TFencrypt(testvectors[i-1],util.bin2str(testvectors[i-2].concat(testvectors[i-3])));
testvectors[i] = TFencrypt(testvectors[i-1],util.Uint8Array_to_str(testvectors[i-2].concat(testvectors[i-3])));
continue;
}
expect(res, 'vector with block ' + util.hexidump(blk) +
' with key ' + util.hexstrdump(key) +
' should be ' + util.hexidump(ct) +
' but is ' + util.hexidump(TFencrypt(blk,key))).to.equal(exp);
expect(res, 'vector with block ' + util.Uint8Array_to_hex(blk) +
' with key ' + util.str_to_hex(key) +
' should be ' + util.Uint8Array_to_hex(ct) +
' but is ' + util.Uint8Array_to_hex(TFencrypt(blk,key))).to.equal(exp);
}
done();
});

View File

@ -1,5 +1,4 @@
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
const asmCrypto = require('asmcrypto-lite');
const chai = require('chai');
chai.use(require('chai-as-promised'));
@ -8,6 +7,7 @@ const expect = chai.expect;
describe('API functional testing', function() {
const util = openpgp.util;
const crypto = openpgp.crypto;
const RSApubMPIstrs = [
new Uint8Array([0x08,0x00,0xac,0x15,0xb3,0xd6,0xd2,0x0f,0xf0,0x7a,0xdd,0x21,0xb7,
0xbf,0x61,0xfa,0xca,0x93,0x86,0xc8,0x55,0x5a,0x4b,0xa6,0xa4,0x1a,
@ -231,18 +231,19 @@ describe('API functional testing', function() {
ElgamalpubMPIs[i].read(ElgamalpubMPIstrs[i]);
}
const data = util.str2Uint8Array("foobar");
const data = util.str_to_Uint8Array("foobar");
describe('Sign and verify', function () {
it('RSA', function () {
// FIXME
//Originally we passed public and secret MPI separately, now they are joined. Is this what we want to do long term?
// RSA
return openpgp.crypto.signature.sign(
2, 1, RSApubMPIs.concat(RSAsecMPIs), data
return crypto.signature.sign(
1, 2, RSApubMPIs.concat(RSAsecMPIs), data
).then(RSAsignedData => {
const RSAsignedDataMPI = new openpgp.MPI();
RSAsignedDataMPI.read(RSAsignedData);
return openpgp.crypto.signature.verify(
return crypto.signature.verify(
1, 2, [RSAsignedDataMPI], RSApubMPIs, data
).then(success => {
return expect(success).to.be.true;
@ -252,16 +253,16 @@ describe('API functional testing', function() {
it('DSA', function () {
// DSA
return openpgp.crypto.signature.sign(
2, 17, DSApubMPIs.concat(DSAsecMPIs), data
return crypto.signature.sign(
17, 2, DSApubMPIs.concat(DSAsecMPIs), data
).then(DSAsignedData => {
DSAsignedData = util.Uint8Array2str(DSAsignedData);
DSAsignedData = util.Uint8Array_to_str(DSAsignedData);
const DSAmsgMPIs = [];
DSAmsgMPIs[0] = new openpgp.MPI();
DSAmsgMPIs[1] = new openpgp.MPI();
DSAmsgMPIs[0].read(DSAsignedData.substring(0,34));
DSAmsgMPIs[1].read(DSAsignedData.substring(34,68));
return openpgp.crypto.signature.verify(
return crypto.signature.verify(
17, 2, DSAmsgMPIs, DSApubMPIs, data
).then(success => {
return expect(success).to.be.true;
@ -278,9 +279,9 @@ describe('API functional testing', function() {
function testCFB(plaintext, resync) {
symmAlgos.forEach(function(algo) {
const symmKey = openpgp.crypto.generateSessionKey(algo);
const symmencData = openpgp.crypto.cfb.encrypt(openpgp.crypto.getPrefixRandom(algo), algo, util.str2Uint8Array(plaintext), symmKey, resync);
const text = util.Uint8Array2str(openpgp.crypto.cfb.decrypt(algo, symmKey, symmencData, resync));
const symmKey = crypto.generateSessionKey(algo);
const symmencData = crypto.cfb.encrypt(crypto.getPrefixRandom(algo), algo, util.str_to_Uint8Array(plaintext), symmKey, resync);
const text = util.Uint8Array_to_str(crypto.cfb.decrypt(algo, symmKey, symmencData, resync));
expect(text).to.equal(plaintext);
});
}
@ -288,19 +289,16 @@ describe('API functional testing', function() {
function testAESCFB(plaintext) {
symmAlgos.forEach(function(algo) {
if(algo.substr(0,3) === 'aes') {
const symmKey = openpgp.crypto.generateSessionKey(algo);
const rndm = openpgp.crypto.getPrefixRandom(algo);
const symmKey = crypto.generateSessionKey(algo);
const rndm = crypto.getPrefixRandom(algo);
const repeat = new Uint8Array([rndm[rndm.length - 2], rndm[rndm.length - 1]]);
const prefix = util.concatUint8Array([rndm, repeat]);
const symmencData = openpgp.crypto.cfb.encrypt(rndm, algo, util.str2Uint8Array(plaintext), symmKey, false);
const symmencData2 = asmCrypto.AES_CFB.encrypt(util.concatUint8Array([prefix, util.str2Uint8Array(plaintext)]), symmKey);
const symmencData = crypto.cfb.encrypt(rndm, algo, util.str_to_Uint8Array(plaintext), symmKey, false);
const decrypted = crypto.cfb.decrypt(algo, symmKey, symmencData, false);
let decrypted = asmCrypto.AES_CFB.decrypt(symmencData, symmKey);
decrypted = decrypted.subarray(openpgp.crypto.cipher[algo].blockSize + 2, decrypted.length);
expect(util.Uint8Array2str(symmencData)).to.equal(util.Uint8Array2str(symmencData2));
const text = util.Uint8Array2str(decrypted);
const text = util.Uint8Array_to_str(decrypted);
expect(text).to.equal(plaintext);
}
});
@ -309,16 +307,17 @@ describe('API functional testing', function() {
function testAESGCM(plaintext) {
symmAlgos.forEach(function(algo) {
if(algo.substr(0,3) === 'aes') {
it(algo, function(done) {
const key = openpgp.crypto.generateSessionKey(algo);
const iv = openpgp.crypto.random.getRandomValues(new Uint8Array(openpgp.crypto.gcm.ivLength));
it(algo, function() {
const key = crypto.generateSessionKey(algo);
const iv = crypto.random.getRandomValues(new Uint8Array(crypto.gcm.ivLength));
openpgp.crypto.gcm.encrypt(algo, util.str2Uint8Array(plaintext), key, iv).then(function(ciphertext) {
return openpgp.crypto.gcm.decrypt(algo, ciphertext, key, iv);
return crypto.gcm.encrypt(
algo, util.str_to_Uint8Array(plaintext), key, iv
).then(function(ciphertext) {
return crypto.gcm.decrypt(algo, ciphertext, key, iv);
}).then(function(decrypted) {
const decryptedStr = util.Uint8Array2str(decrypted);
const decryptedStr = util.Uint8Array_to_str(decrypted);
expect(decryptedStr).to.equal(plaintext);
done();
});
});
}
@ -372,40 +371,43 @@ describe('API functional testing', function() {
testAESGCM("12345678901234567890123456789012345678901234567890");
});
it('Asymmetric using RSA with eme_pkcs1 padding', function (done) {
const symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256'));
const RSAUnencryptedData = new openpgp.MPI();
RSAUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength()));
openpgp.crypto.publicKeyEncrypt(
"rsa_encrypt_sign", RSApubMPIs, RSAUnencryptedData
it('Asymmetric using RSA with eme_pkcs1 padding', function () {
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
const RSAUnencryptedData = crypto.pkcs1.eme.encode(symmKey, RSApubMPIs[0].byteLength())
const RSAUnencryptedMPI = new openpgp.MPI(RSAUnencryptedData);
return crypto.publicKeyEncrypt(
1, RSApubMPIs, RSAUnencryptedMPI
).then(RSAEncryptedData => {
openpgp.crypto.publicKeyDecrypt("rsa_encrypt_sign", RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData).then(data => {
return crypto.publicKeyDecrypt(
1, RSApubMPIs.concat(RSAsecMPIs), RSAEncryptedData
).then(data => {
data = data.write();
data = util.Uint8Array2str(data.subarray(2, data.length));
data = util.Uint8Array_to_str(data.subarray(2, data.length));
const result = openpgp.crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength());
const result = crypto.pkcs1.eme.decode(data, RSApubMPIs[0].byteLength());
expect(result).to.equal(symmKey);
done();
});
});
});
it('Asymmetric using Elgamal with eme_pkcs1 padding', function (done) {
const symmKey = util.Uint8Array2str(openpgp.crypto.generateSessionKey('aes256'));
const ElgamalUnencryptedData = new openpgp.MPI();
ElgamalUnencryptedData.fromBytes(openpgp.crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength()));
openpgp.crypto.publicKeyEncrypt(
"elgamal", ElgamalpubMPIs, ElgamalUnencryptedData
it('Asymmetric using Elgamal with eme_pkcs1 padding', function () {
const symmKey = util.Uint8Array_to_str(crypto.generateSessionKey('aes256'));
const ElgamalUnencryptedData = crypto.pkcs1.eme.encode(symmKey, ElgamalpubMPIs[0].byteLength());
const ElgamalUnencryptedMPI = new openpgp.MPI(ElgamalUnencryptedData);
return crypto.publicKeyEncrypt(
16, ElgamalpubMPIs, ElgamalUnencryptedMPI
).then(ElgamalEncryptedData => {
const data = openpgp.crypto.publicKeyDecrypt("elgamal", ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData).then(data => {
return crypto.publicKeyDecrypt(
16, ElgamalpubMPIs.concat(ElgamalsecMPIs), ElgamalEncryptedData
).then(data => {
data = data.write();
data = util.Uint8Array2str(data.subarray(2, data.length));
data = util.Uint8Array_to_str(data.subarray(2, data.length));
const result = openpgp.crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength());
const result = crypto.pkcs1.eme.decode(data, ElgamalpubMPIs[0].byteLength());
expect(result).to.equal(symmKey);
done();
});
});
});

View File

@ -1,17 +1,11 @@
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
const BN = require('bn.js');
const chai = require('chai');
chai.use(require('chai-as-promised'));
const expect = chai.expect;
const bin2bi = function (bytes) {
const mpi = new openpgp.MPI();
bytes = openpgp.util.bin2str(bytes);
mpi.fromBytes(bytes);
return mpi.toBigInteger();
};
describe('Elliptic Curve Cryptography', function () {
const elliptic_curves = openpgp.crypto.publicKey.elliptic;
const key_data = {
@ -143,21 +137,21 @@ describe('Elliptic Curve Cryptography', function () {
it('Creating curve with name', function (done) {
const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519'];
names.forEach(function (name) {
expect(elliptic_curves.get(name)).to.exist;
expect(new elliptic_curves.Curve(name)).to.exist;
});
done();
});
it('Creating curve from oid', function (done) {
const oids = ['2A8648CE3D030107', '2B81040022', '2B81040023', '2B8104000A'];
oids.forEach(function (oid) {
expect(elliptic_curves.get(openpgp.util.hex2bin(oid))).to.exist;
expect(new elliptic_curves.Curve(openpgp.util.hex_to_str(oid))).to.exist;
});
done();
});
it('Creating KeyPair', function () {
const names = ['p256', 'p384', 'p521', 'secp256k1', 'curve25519'];
return Promise.all(names.map(function (name) {
const curve = elliptic_curves.get(name);
const curve = new elliptic_curves.Curve(name);
return curve.genKeyPair().then(keyPair => {
expect(keyPair).to.exist;
});
@ -166,30 +160,30 @@ describe('Elliptic Curve Cryptography', function () {
it('Creating KeyPair from data', function (done) {
for (const name in key_data) {
const pair = key_data[name];
const curve = elliptic_curves.get(name);
const curve = new elliptic_curves.Curve(name);
expect(curve).to.exist;
const keyPair = curve.keyFromPrivate(pair.priv);
expect(keyPair).to.exist;
const pub = keyPair.getPublic();
expect(pub).to.exist;
expect(openpgp.util.hexidump(pub)).to.equal(openpgp.util.hexidump(pair.pub));
expect(openpgp.util.Uint8Array_to_hex(pub)).to.equal(openpgp.util.Uint8Array_to_hex(pair.pub));
}
done();
});
it('Signature verification', function (done) {
const curve = elliptic_curves.get('p256');
const curve = new elliptic_curves.Curve('p256');
const key = curve.keyFromPublic(signature_data.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.true;
done();
});
it('Invalid signature', function (done) {
const curve = elliptic_curves.get('p256');
const curve = new elliptic_curves.Curve('p256');
const key = curve.keyFromPublic(key_data.p256.pub);
expect(key.verify(signature_data.message, signature_data.signature, 8)).to.eventually.be.false;
done();
});
it('Signature generation', function () {
const curve = elliptic_curves.get('p256');
const curve = new elliptic_curves.Curve('p256');
let key = curve.keyFromPrivate(key_data.p256.priv);
return key.sign(signature_data.message, 8).then(signature => {
key = curve.keyFromPublic(key_data.p256.pub);
@ -197,13 +191,13 @@ describe('Elliptic Curve Cryptography', function () {
});
});
it('Shared secret generation', function (done) {
const curve = elliptic_curves.get('p256');
const curve = new elliptic_curves.Curve('p256');
let key1 = curve.keyFromPrivate(key_data.p256.priv);
let key2 = curve.keyFromPublic(signature_data.pub);
const shared1 = openpgp.util.hexidump(key1.derive(key2));
const shared1 = openpgp.util.Uint8Array_to_hex(key1.derive(key2));
key1 = curve.keyFromPublic(key_data.p256.pub);
key2 = curve.keyFromPrivate(signature_data.priv);
const shared2 = openpgp.util.hexidump(key2.derive(key1));
const shared2 = openpgp.util.Uint8Array_to_hex(key2.derive(key1));
expect(shared1).to.equal(shared2);
done();
});
@ -211,7 +205,7 @@ describe('Elliptic Curve Cryptography', function () {
describe('ECDSA signature', function () {
const verify_signature = function (oid, hash, r, s, message, pub) {
if (openpgp.util.isString(message)) {
message = openpgp.util.str2Uint8Array(message);
message = openpgp.util.str_to_Uint8Array(message);
} else if (!openpgp.util.isUint8Array(message)) {
message = new Uint8Array(message);
}
@ -219,9 +213,9 @@ describe('Elliptic Curve Cryptography', function () {
return ecdsa.verify(
oid,
hash,
{r: bin2bi(r), s: bin2bi(s)},
{r: new BN(r), s: new BN(s)},
message,
bin2bi(pub)
new Uint8Array(pub)
);
};
const secp256k1_dummy_value = new Uint8Array([
@ -295,10 +289,10 @@ describe('Elliptic Curve Cryptography', function () {
.to.eventually.be.true.notify(done);
});
it('Sign and verify message', function () {
const curve = elliptic_curves.get('p521');
const curve = new elliptic_curves.Curve('p521');
return curve.genKeyPair().then(keyPair => {
const keyPublic = bin2bi(keyPair.getPublic());
const keyPrivate = bin2bi(keyPair.getPrivate());
const keyPublic = new Uint8Array(keyPair.getPublic());
const keyPrivate = new Uint8Array(keyPair.getPrivate());
const oid = curve.oid;
const message = p384_message;
return elliptic_curves.ecdsa.sign(oid, 10, message, keyPrivate).then(signature => {
@ -311,19 +305,19 @@ describe('Elliptic Curve Cryptography', function () {
describe('ECDH key exchange', function () {
const decrypt_message = function (oid, hash, cipher, priv, ephemeral, data, fingerprint) {
if (openpgp.util.isString(data)) {
data = openpgp.util.str2Uint8Array(data);
data = openpgp.util.str_to_Uint8Array(data);
} else {
data = new Uint8Array(data);
}
return Promise.resolve().then(() => {
const ecdh = elliptic_curves.ecdh;
return ecdh.decrypt(
oid,
const curve = new elliptic_curves.Curve(oid);
return elliptic_curves.ecdh.decrypt(
curve.oid,
cipher,
hash,
bin2bi(ephemeral),
new Uint8Array(ephemeral),
data,
bin2bi(priv),
new Uint8Array(priv),
fingerprint
);
});

View File

@ -7,11 +7,11 @@ const MD5 = openpgp.crypto.hash.md5;
const { expect } = chai;
it('MD5 with test vectors from RFC 1321', function(done) {
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array(''))), 'MD5("") = d41d8cd98f00b204e9800998ecf8427e')).to.equal('d41d8cd98f00b204e9800998ecf8427e');
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('abc'))), 'MD5("a") = 0cc175b9c0f1b6a831c399e269772661')).to.equal('900150983cd24fb0d6963f7d28e17f72');
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('message digest'))), 'MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0')).to.equal('f96b697d7cb7938d525a2f31aaf161d0');
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('abcdefghijklmnopqrstuvwxyz'))), 'MD5("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b')).to.equal('c3fcd3d76192e4007dfb496cca67e13b');
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))), 'MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f')).to.equal('d174ab98d277d9f5a5611c2c9f419d9f');
expect(util.hexstrdump(util.Uint8Array2str(MD5(util.str2Uint8Array('12345678901234567890123456789012345678901234567890123456789012345678901234567890'))), 'MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a')).to.equal('57edf4a22be3c955ac49da2e2107b67a');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array(''))), 'MD5("") = d41d8cd98f00b204e9800998ecf8427e')).to.equal('d41d8cd98f00b204e9800998ecf8427e');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('abc'))), 'MD5("a") = 0cc175b9c0f1b6a831c399e269772661')).to.equal('900150983cd24fb0d6963f7d28e17f72');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('message digest'))), 'MD5("message digest") = f96b697d7cb7938d525a2f31aaf161d0')).to.equal('f96b697d7cb7938d525a2f31aaf161d0');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('abcdefghijklmnopqrstuvwxyz'))), 'MD5("abcdefghijklmnopqrstuvwxyz") = c3fcd3d76192e4007dfb496cca67e13b')).to.equal('c3fcd3d76192e4007dfb496cca67e13b');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'))), 'MD5("ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789") = d174ab98d277d9f5a5611c2c9f419d9f')).to.equal('d174ab98d277d9f5a5611c2c9f419d9f');
expect(util.str_to_hex(util.Uint8Array_to_str(MD5(util.str_to_Uint8Array('12345678901234567890123456789012345678901234567890123456789012345678901234567890'))), 'MD5("12345678901234567890123456789012345678901234567890123456789012345678901234567890") = 57edf4a22be3c955ac49da2e2107b67a')).to.equal('57edf4a22be3c955ac49da2e2107b67a');
done();
});

View File

@ -7,9 +7,9 @@ const RMDstring = openpgp.crypto.hash.ripemd;
const { expect } = chai;
it("RIPE-MD 160 bits with test vectors from https://homes.esat.kuleuven.be/~bosselae/ripemd160.html", function(done) {
expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array(''))), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31');
expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('a'))), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe');
expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('abc'))), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc');
expect(util.hexstrdump(util.Uint8Array2str(RMDstring(util.str2Uint8Array('message digest'))), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36');
expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array(''))), 'RMDstring("") = 9c1185a5c5e9fc54612808977ee8f548b2258d31')).to.equal('9c1185a5c5e9fc54612808977ee8f548b2258d31');
expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('a'))), 'RMDstring("a") = 0bdc9d2d256b3ee9daae347be6f4dc835a467ffe')).to.equal('0bdc9d2d256b3ee9daae347be6f4dc835a467ffe');
expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('abc'))), 'RMDstring("abc") = 8eb208f7e05d987a9b044a8e98c6b087f15a0bfc')).to.equal('8eb208f7e05d987a9b044a8e98c6b087f15a0bfc');
expect(util.str_to_hex(util.Uint8Array_to_str(RMDstring(util.str_to_Uint8Array('message digest'))), 'RMDstring("message digest") = 5d0689ef49d2fae572b881b123a85ffa21595f36')).to.equal('5d0689ef49d2fae572b881b123a85ffa21595f36');
done();
});

View File

@ -7,15 +7,15 @@ const { hash } = openpgp.crypto;
const { expect } = chai;
it('SHA* with test vectors from NIST FIPS 180-2', function(done) {
expect(util.hexstrdump(util.Uint8Array2str(hash.sha1(util.str2Uint8Array('abc'))), 'hash.sha1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha1(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983e441c3bd26ebaae4aa1f95129e5e54670f1')).to.equal('84983e441c3bd26ebaae4aa1f95129e5e54670f1');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha224(util.str2Uint8Array('abc'))), 'hash.sha224("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7')).to.equal('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha224(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525')).to.equal('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha256(util.str2Uint8Array('abc'))), 'hash.sha256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')).to.equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha256(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1')).to.equal('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha384(util.str2Uint8Array('abc'))), 'hash.sha384("abc") = cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')).to.equal('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha384(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha512(util.str2Uint8Array('abc'))), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f');
expect(util.hexstrdump(util.Uint8Array2str(hash.sha512(util.str2Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha1(util.str_to_Uint8Array('abc'))), 'hash.sha1("abc") = a9993e364706816aba3e25717850c26c9cd0d89d')).to.equal('a9993e364706816aba3e25717850c26c9cd0d89d');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha1(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha1("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 84983e441c3bd26ebaae4aa1f95129e5e54670f1')).to.equal('84983e441c3bd26ebaae4aa1f95129e5e54670f1');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha224(util.str_to_Uint8Array('abc'))), 'hash.sha224("abc") = 23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7')).to.equal('23097d223405d8228642a477bda255b32aadbce4bda0b3f7e36c9da7');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha224(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha224("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525')).to.equal('75388b16512776cc5dba5da1fd890150b0c6455cb4f58b1952522525');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha256(util.str_to_Uint8Array('abc'))), 'hash.sha256("abc") = ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad')).to.equal('ba7816bf8f01cfea414140de5dae2223b00361a396177a9cb410ff61f20015ad');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha256(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha256("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1')).to.equal('248d6a61d20638b8e5c026930c3e6039a33ce45964ff2167f6ecedd419db06c1');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha384(util.str_to_Uint8Array('abc'))), 'hash.sha384("abc") = cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7')).to.equal('cb00753f45a35e8bb5a03d699ac65007272c32ab0eded1631a8b605a43ff5bed8086072ba1e7cc2358baeca134c825a7');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha384(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha384("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b')).to.equal('3391fdddfc8dc7393707a65b1b4709397cf8b1d162af05abfe8f450de5f36bc6b0455a8520bc4e6f5fe95b1fe3c8452b');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha512(util.str_to_Uint8Array('abc'))), 'hash.sha512("abc") = ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f')).to.equal('ddaf35a193617abacc417349ae20413112e6fa4e89a97ea20a9eeee64b55d39a2192992a274fc1a836ba3c23a3feebbd454d4423643ce80e2a9ac94fa54ca49f');
expect(util.str_to_hex(util.Uint8Array_to_str(hash.sha512(util.str_to_Uint8Array('abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq'))), 'hash.sha512("abcdbcdecdefdefgefghfghighijhijkijkljklmklmnlmnomnopnopq") = 204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445')).to.equal('204a8fc6dda82f0a0ced7beb8e08a41657c16ef468b228a8279be331a703c33596fd15c13b1b07f9aa1d3bea57789ca031ad85c7a71dd70354ec631238ca3445');
done();
});

View File

@ -1,3 +1,5 @@
/* globals tryTests: true */
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
const chai = require('chai');
@ -234,4 +236,75 @@ describe('Elliptic Curve Cryptography', function () {
expect(key.publicKeyArmored).to.exist;
});
});
function omnibus() {
it('Omnibus NIST P-256 Test', function () {
const options = { userIds: {name: "Hi", email: "hi@hel.lo"}, curve: "p256" };
return openpgp.generateKey(options).then(function (firstKey) {
const hi = firstKey.key;
const pubHi = hi.toPublic();
const options = { userIds: { name: "Bye", email: "bye@good.bye" }, curve: "p256" };
return openpgp.generateKey(options).then(function (secondKey) {
const bye = secondKey.key;
const pubBye = bye.toPublic();
return Promise.all([
// Signing message
openpgp.sign(
{ data: 'Hi, this is me, Hi!', privateKeys: hi }
).then(signed => {
const msg = openpgp.cleartext.readArmored(signed.data);
// Verifying signed message
return Promise.all([
openpgp.verify(
{ message: msg, publicKeys: pubHi }
).then(output => expect(output.signatures[0].valid).to.be.true),
// Verifying detached signature
openpgp.verify(
{ message: openpgp.message.fromText('Hi, this is me, Hi!'),
publicKeys: pubHi,
signature: openpgp.signature.readArmored(signed.data) }
).then(output => expect(output.signatures[0].valid).to.be.true)
]);
}),
// Encrypting and signing
openpgp.encrypt(
{ data: 'Hi, Hi wrote this but only Bye can read it!',
publicKeys: [pubBye],
privateKeys: [hi] }
).then(encrypted => {
const msg = openpgp.message.readArmored(encrypted.data);
// Decrypting and verifying
return openpgp.decrypt(
{ message: msg,
privateKeys: bye,
publicKeys: [pubHi] }
).then(output => {
expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!');
expect(output.signatures[0].valid).to.be.true;
});
})
]);
});
});
});
}
omnibus();
tryTests('ECC Worker Tests', omnibus, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.use_native = true;
},
after: function() {
openpgp.destroyWorker();
}
});
// TODO find test vectors
});

View File

@ -14,7 +14,7 @@ describe('Oid tests', function() {
expect(oid).to.exist;
expect(oid.oid).to.exist;
expect(oid.oid).to.have.length(data.length);
expect(oid.oid).to.equal(openpgp.util.Uint8Array2str(data));
expect(oid.oid).to.equal(openpgp.util.Uint8Array_to_str(data));
});
});
it('Reading and writing', function() {
@ -25,12 +25,12 @@ describe('Oid tests', function() {
expect(oid.read(data)).to.equal(data.length);
expect(oid.oid).to.exist;
expect(oid.oid).to.have.length(data.length-1);
expect(oid.oid).to.equal(openpgp.util.Uint8Array2str(data.subarray(1)));
expect(oid.oid).to.equal(openpgp.util.Uint8Array_to_str(data.subarray(1)));
const result = oid.write();
expect(result).to.exist;
expect(result).to.have.length(data.length);
expect(result[0]).to.equal(data.length-1);
expect(openpgp.util.Uint8Array2str(result.subarray(1))).to.equal(openpgp.util.Uint8Array2str(data.subarray(1)));
expect(openpgp.util.Uint8Array_to_str(result.subarray(1))).to.equal(openpgp.util.Uint8Array_to_str(data.subarray(1)));
});
});
});

View File

@ -171,16 +171,14 @@ describe("Packet", function() {
});
it('Public key encrypted symmetric key packet', function() {
const rsa = new openpgp.crypto.publicKey.rsa();
const rsa = openpgp.crypto.publicKey.rsa;
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
return rsa.generate(keySize, "10001").then(function(mpiGen) {
let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
mpi = mpi.map(function(k) {
const mpi = new openpgp.MPI();
mpi.fromBigInteger(k);
return mpi;
return new openpgp.MPI(k);
});
const enc = new openpgp.packet.PublicKeyEncryptedSessionKey();
@ -206,7 +204,7 @@ describe("Packet", function() {
});
});
it('Secret key packet (reading, unencrypted)', function(done) {
it('Secret key packet (reading, unencrypted)', function() {
const armored_key =
'-----BEGIN PGP PRIVATE KEY BLOCK-----\n' +
'Version: GnuPG v2.0.19 (GNU/Linux)\n' +
@ -241,17 +239,14 @@ describe("Packet", function() {
enc.sessionKeyAlgorithm = 'aes256';
enc.publicKeyId.bytes = '12345678';
enc.encrypt(key).then(() => {
enc.decrypt(key).then(() => {
return enc.encrypt(key).then(() => {
return enc.decrypt(key).then(() => {
expect(stringify(enc.sessionKey)).to.equal(stringify(secret));
done();
});
});
});
it('Public key encrypted packet (reading, GPG)', function(done) {
it('Public key encrypted packet (reading, GPG)', function() {
const armored_key =
'-----BEGIN PGP PRIVATE KEY BLOCK-----\n' +
'Version: GnuPG v2.0.19 (GNU/Linux)\n' +
@ -306,17 +301,16 @@ describe("Packet", function() {
const msg = new openpgp.packet.List();
msg.read(openpgp.armor.decode(armored_msg).data);
msg[0].decrypt(key).then(() => {
msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
return msg[0].decrypt(key).then(() => {
return msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
const text = stringify(msg[1].packets[0].packets[0].data);
expect(text).to.equal('Hello world!');
done();
});
});
it('Sym encrypted session key reading/writing', function(done) {
it('Sym. encrypted session key reading/writing', function(done) {
const passphrase = 'hello';
const algo = 'aes256';
@ -348,7 +342,7 @@ describe("Packet", function() {
done();
});
it('Secret key encryption/decryption test', function(done) {
it('Secret key encryption/decryption test', function() {
const armored_msg =
'-----BEGIN PGP MESSAGE-----\n' +
'Version: GnuPG v2.0.19 (GNU/Linux)\n' +
@ -369,13 +363,12 @@ describe("Packet", function() {
const msg = new openpgp.packet.List();
msg.read(openpgp.armor.decode(armored_msg).data);
msg[0].decrypt(key).then(() => {
msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
return msg[0].decrypt(key).then(() => {
return msg[1].decrypt(msg[0].sessionKeyAlgorithm, msg[0].sessionKey);
const text = stringify(msg[1].packets[0].packets[0].data);
expect(text).to.equal('Hello world!');
done();
});
});
@ -435,15 +428,13 @@ describe("Packet", function() {
const key = new openpgp.packet.List();
key.push(new openpgp.packet.SecretKey());
const rsa = new openpgp.crypto.publicKey.rsa();
const rsa = openpgp.crypto.publicKey.rsa;
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
return rsa.generate(keySize, "10001").then(function(mpiGen) {
let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
mpi = mpi.map(function(k) {
const mpi = new openpgp.MPI();
mpi.fromBigInteger(k);
return mpi;
return new openpgp.MPI(k);
});
key[0].params = mpi;
@ -463,15 +454,13 @@ describe("Packet", function() {
it('Writing and verification of a signature packet.', function() {
const key = new openpgp.packet.SecretKey();
const rsa = new openpgp.crypto.publicKey.rsa();
const rsa = openpgp.crypto.publicKey.rsa;
const keySize = openpgp.util.getWebCryptoAll() ? 2048 : 512; // webkit webcrypto accepts minimum 2048 bit keys
return rsa.generate(keySize, "10001").then(function(mpiGen) {
let mpi = [mpiGen.n, mpiGen.ee, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
let mpi = [mpiGen.n, mpiGen.e, mpiGen.d, mpiGen.p, mpiGen.q, mpiGen.u];
mpi = mpi.map(function(k) {
const mpi = new openpgp.MPI();
mpi.fromBigInteger(k);
return mpi;
return new openpgp.MPI(k);
});
key.params = mpi;

View File

@ -603,7 +603,7 @@ describe("Signature", function() {
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - armored', function() {
const plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말');
const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말');
const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey.primaryKey.decrypt('hello world');
@ -623,7 +623,7 @@ describe("Signature", function() {
});
it('Sign text with openpgp.sign and verify with openpgp.verify leads to same bytes cleartext and valid signatures - not armored', function() {
const plaintext = openpgp.util.str2Uint8Array('short message\nnext line\n한국어/조선말');
const plaintext = openpgp.util.str_to_Uint8Array('short message\nnext line\n한국어/조선말');
const pubKey = openpgp.key.readArmored(pub_key_arm2).keys[0];
const privKey = openpgp.key.readArmored(priv_key_arm2).keys[0];
privKey.primaryKey.decrypt('hello world');

View File

@ -147,6 +147,8 @@ describe('X25519 Cryptography', function () {
done();
});
// This test is slow because the keys are generated by GPG2, which
// by default chooses a larger number for S2K iterations than we do.
it('Load private key', function (done) {
load_priv_key('light');
load_priv_key('night');
@ -217,131 +219,148 @@ describe('X25519 Cryptography', function () {
});
});
// TODO generate, export, then reimport key and validate
it('Omnibus Ed25519/Curve25519 Test', function () {
const options = {
userIds: {name: "Hi", email: "hi@hel.lo"},
curve: "ed25519"
};
return openpgp.generateKey(options).then(function (firstKey) {
expect(firstKey).to.exist;
expect(firstKey.privateKeyArmored).to.exist;
expect(firstKey.publicKeyArmored).to.exist;
expect(firstKey.key).to.exist;
expect(firstKey.key.primaryKey).to.exist;
expect(firstKey.key.subKeys).to.have.length(1);
expect(firstKey.key.subKeys[0].subKey).to.exist;
const hi = firstKey.key;
const primaryKey = hi.primaryKey;
const subKey = hi.subKeys[0].subKey;
expect(primaryKey.params[0].oid).to.equal(elliptic.get('ed25519').oid);
expect(primaryKey.algorithm).to.equal('eddsa');
expect(subKey.params[0].oid).to.equal(elliptic.get('curve25519').oid);
expect(subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid
const user = hi.users[0];
expect(user.selfCertifications[0].verify(
primaryKey, { userid: user.userId, key: primaryKey }
)).to.eventually.be.true;
expect(user.verifyCertificate(
primaryKey, user.selfCertifications[0], [hi.toPublic()]
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
// TODO export, then reimport key and validate
function omnibus() {
it('Omnibus Ed25519/Curve25519 Test', function () {
const options = {
userIds: { name: "Bye", email: "bye@good.bye" },
curve: "curve25519"
userIds: {name: "Hi", email: "hi@hel.lo"},
curve: "ed25519"
};
return openpgp.generateKey(options).then(function (secondKey) {
const bye = secondKey.key;
expect(bye.primaryKey.params[0].oid).to.equal(elliptic.get('ed25519').oid);
expect(bye.primaryKey.algorithm).to.equal('eddsa');
expect(bye.subKeys[0].subKey.params[0].oid).to.equal(elliptic.get('curve25519').oid);
expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh');
return openpgp.generateKey(options).then(function (firstKey) {
expect(firstKey).to.exist;
expect(firstKey.privateKeyArmored).to.exist;
expect(firstKey.publicKeyArmored).to.exist;
expect(firstKey.key).to.exist;
expect(firstKey.key.primaryKey).to.exist;
expect(firstKey.key.subKeys).to.have.length(1);
expect(firstKey.key.subKeys[0].subKey).to.exist;
const hi = firstKey.key;
const primaryKey = hi.primaryKey;
const subKey = hi.subKeys[0].subKey;
expect(primaryKey.params[0].getName()).to.equal("ed25519");
expect(primaryKey.algorithm).to.equal('eddsa');
expect(subKey.params[0].getName()).to.equal('curve25519');
expect(subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid
const user = bye.users[0];
const user = hi.users[0];
expect(user.selfCertifications[0].verify(
bye.primaryKey, { userid: user.userId, key: bye.primaryKey }
primaryKey, { userid: user.userId, key: primaryKey }
)).to.eventually.be.true;
expect(user.verifyCertificate(
bye.primaryKey, user.selfCertifications[0], [bye.toPublic()]
primaryKey, user.selfCertifications[0], [hi.toPublic()]
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
return Promise.all([
// Hi trusts Bye!
bye.toPublic().signPrimaryUser([hi]).then(trustedBye => {
expect(trustedBye.users[0].otherCertifications[0].verify(
primaryKey, { userid: user.userId, key: bye.toPublic().primaryKey }
)).to.eventually.be.true;
}),
// Signing message
openpgp.sign(
{ data: 'Hi, this is me, Hi!', privateKeys: hi }
).then(signed => {
const msg = openpgp.cleartext.readArmored(signed.data);
// Verifying signed message
return Promise.all([
openpgp.verify(
{ message: msg, publicKeys: hi.toPublic() }
).then(output => expect(output.signatures[0].valid).to.be.true),
// Verifying detached signature
openpgp.verify(
{ message: openpgp.message.fromText('Hi, this is me, Hi!'),
publicKeys: hi.toPublic(),
signature: openpgp.signature.readArmored(signed.data) }
).then(output => expect(output.signatures[0].valid).to.be.true)
]);
}),
// Encrypting and signing
openpgp.encrypt(
{ data: 'Hi, Hi wrote this but only Bye can read it!',
publicKeys: [bye.toPublic()],
privateKeys: [hi] }
).then(encrypted => {
const msg = openpgp.message.readArmored(encrypted.data);
// Decrypting and verifying
return openpgp.decrypt(
{ message: msg,
privateKeys: bye,
publicKeys: [hi.toPublic()] }
).then(output => {
expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!');
expect(output.signatures[0].valid).to.be.true;
});
})
]);
const options = {
userIds: { name: "Bye", email: "bye@good.bye" },
curve: "curve25519"
};
return openpgp.generateKey(options).then(function (secondKey) {
const bye = secondKey.key;
expect(bye.primaryKey.params[0].getName()).to.equal('ed25519');
expect(bye.primaryKey.algorithm).to.equal('eddsa');
expect(bye.subKeys[0].subKey.params[0].getName()).to.equal('curve25519');
expect(bye.subKeys[0].subKey.algorithm).to.equal('ecdh');
// Self Certificate is valid
const user = bye.users[0];
expect(user.selfCertifications[0].verify(
bye.primaryKey, { userid: user.userId, key: bye.primaryKey }
)).to.eventually.be.true;
expect(user.verifyCertificate(
bye.primaryKey, user.selfCertifications[0], [bye.toPublic()]
)).to.eventually.equal(openpgp.enums.keyStatus.valid);
return Promise.all([
// Hi trusts Bye!
bye.toPublic().signPrimaryUser([hi]).then(trustedBye => {
expect(trustedBye.users[0].otherCertifications[0].verify(
primaryKey, { userid: user.userId, key: bye.toPublic().primaryKey }
)).to.eventually.be.true;
}),
// Signing message
openpgp.sign(
{ data: 'Hi, this is me, Hi!', privateKeys: hi }
).then(signed => {
const msg = openpgp.cleartext.readArmored(signed.data);
// Verifying signed message
return Promise.all([
openpgp.verify(
{ message: msg, publicKeys: hi.toPublic() }
).then(output => expect(output.signatures[0].valid).to.be.true),
// Verifying detached signature
openpgp.verify(
{ message: openpgp.message.fromText('Hi, this is me, Hi!'),
publicKeys: hi.toPublic(),
signature: openpgp.signature.readArmored(signed.data) }
).then(output => expect(output.signatures[0].valid).to.be.true)
]);
}),
// Encrypting and signing
openpgp.encrypt(
{ data: 'Hi, Hi wrote this but only Bye can read it!',
publicKeys: [bye.toPublic()],
privateKeys: [hi] }
).then(encrypted => {
const msg = openpgp.message.readArmored(encrypted.data);
// Decrypting and verifying
return openpgp.decrypt(
{ message: msg,
privateKeys: bye,
publicKeys: [hi.toPublic()] }
).then(output => {
expect(output.data).to.equal('Hi, Hi wrote this but only Bye can read it!');
expect(output.signatures[0].valid).to.be.true;
});
})
]);
});
});
});
}
omnibus();
tryTests('X25519 Worker Tests', omnibus, {
if: typeof window !== 'undefined' && window.Worker,
before: function() {
openpgp.initWorker({ path:'../dist/openpgp.worker.js' });
},
beforeEach: function() {
openpgp.config.use_native = true;
},
after: function() {
openpgp.destroyWorker();
}
});
describe('Ed25519 Test Vectors from RFC8032', function () {
// https://tools.ietf.org/html/rfc8032#section-7.1
const crypto = openpgp.crypto.signature;
const curve = openpgp.crypto.publicKey.elliptic.get('ed25519');
const signature = openpgp.crypto.signature;
const curve = new elliptic.Curve('ed25519');
const util = openpgp.util;
function testVector(vector) {
const S = curve.keyFromSecret(vector.SECRET_KEY);
const P = curve.keyFromPublic(vector.PUBLIC_KEY);
const P = curve.keyFromPublic('40'+vector.PUBLIC_KEY);
expect(S.getPublic()).to.deep.equal(P.getPublic());
const data = util.str2Uint8Array(vector.MESSAGE);
const data = util.str_to_Uint8Array(vector.MESSAGE);
const keyIntegers = [
openpgp.OID.fromClone(curve),
new openpgp.MPI(util.hex2bin(vector.PUBLIC_KEY)),
new openpgp.MPI(util.hex2bin(vector.SECRET_KEY))
new openpgp.MPI(util.hex_to_str('40'+vector.PUBLIC_KEY)),
new openpgp.MPI(util.hex_to_str(vector.SECRET_KEY))
];
const msg_MPIs = [
new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.R).reverse())),
new openpgp.MPI(util.Uint8Array2str(util.hex2Uint8Array(vector.SIGNATURE.S).reverse()))
new openpgp.MPI(util.Uint8Array_to_str(util.hex_to_Uint8Array(vector.SIGNATURE.R).reverse())),
new openpgp.MPI(util.Uint8Array_to_str(util.hex_to_Uint8Array(vector.SIGNATURE.S).reverse()))
];
return Promise.all([
crypto.sign(undefined, 22, keyIntegers, data).then(signature => {
const len = ((signature[0] << 8 | signature[1]) + 7) / 8;
expect(util.hex2Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signature.slice(2, 2 + len));
expect(util.hex2Uint8Array(vector.SIGNATURE.S)).to.deep.eq(signature.slice(4 + len));
signature.sign(22, undefined, keyIntegers, data).then(signed => {
const len = ((signed[0] << 8| signed[1]) + 7) / 8;
expect(util.hex_to_Uint8Array(vector.SIGNATURE.R)).to.deep.eq(signed.slice(2, 2 + len));
expect(util.hex_to_Uint8Array(vector.SIGNATURE.S)).to.deep.eq(signed.slice(4 + len));
}),
crypto.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => {
signature.verify(22, undefined, msg_MPIs, keyIntegers, data).then(result => {
expect(result).to.be.true;
})
]);
@ -372,7 +391,7 @@ describe('X25519 Cryptography', function () {
PUBLIC_KEY:
['3d4017c3e843895a92b70aa74d1b7ebc',
'9c982ccf2ec4968cc0cd55f12af4660c'].join(''),
MESSAGE: util.hex2bin('72'),
MESSAGE: util.hex_to_str('72'),
SIGNATURE:
{ R: ['92a009a9f0d4cab8720e820b5f642540',
'a2b27b5416503f8fb3762223ebdb69da'].join(''),
@ -389,7 +408,7 @@ describe('X25519 Cryptography', function () {
PUBLIC_KEY:
['fc51cd8e6218a1a38da47ed00230f058',
'0816ed13ba3303ac5deb911548908025'].join(''),
MESSAGE: util.hex2bin('af82'),
MESSAGE: util.hex_to_str('af82'),
SIGNATURE:
{ R: ['6291d657deec24024827e69c3abe01a3',
'0ce548a284743a445e3680d7db5ac3ac'].join(''),
@ -406,7 +425,7 @@ describe('X25519 Cryptography', function () {
PUBLIC_KEY:
['278117fc144c72340f67d0f2316e8386',
'ceffbf2b2428c9c51fef7c597f1d426e'].join(''),
MESSAGE: util.hex2bin([
MESSAGE: util.hex_to_str([
'08b8b2b733424243760fe426a4b54908',
'632110a66c2f6591eabd3345e3e4eb98',
'fa6e264bf09efe12ee50f8f54e9f77b1',
@ -488,7 +507,7 @@ describe('X25519 Cryptography', function () {
PUBLIC_KEY:
['ec172b93ad5e563bf4932c70e1245034',
'c35467ef2efd4d64ebf819683467e2bf'].join(''),
MESSAGE: util.hex2bin([
MESSAGE: util.hex_to_str([
'ddaf35a193617abacc417349ae204131',
'12e6fa4e89a97ea20a9eeee64b55d39a',
'2192992a274fc1a836ba3c23a3feebbd',