mirror of
https://github.com/openpgpjs/openpgpjs.git
synced 2025-03-30 15:08:32 +00:00
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:
commit
5fac00eddb
3
.jsdocrc.js
Normal file
3
.jsdocrc.js
Normal file
@ -0,0 +1,3 @@
|
||||
module.exports = {
|
||||
plugins: ['plugins/markdown']
|
||||
};
|
@ -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');
|
||||
|
11
package.json
11
package.json
@ -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",
|
||||
|
@ -126,7 +126,7 @@ function pack() {
|
||||
return new Uint8Array(buffer);
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
wrap: wrap,
|
||||
unwrap: unwrap
|
||||
export default {
|
||||
wrap,
|
||||
unwrap
|
||||
};
|
||||
|
@ -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));
|
||||
};
|
||||
};
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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 */
|
||||
|
@ -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) {
|
||||
|
@ -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 {
|
||||
|
@ -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
|
||||
|
@ -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
@ -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,
|
||||
|
@ -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);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
@ -48,7 +48,4 @@ function decode(msg) {
|
||||
throw new Error('Invalid padding');
|
||||
}
|
||||
|
||||
module.exports = {
|
||||
encode: encode,
|
||||
decode: decode
|
||||
};
|
||||
export default { encode, decode };
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
@ -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)
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -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 };
|
||||
|
@ -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 };
|
||||
|
@ -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 };
|
||||
|
@ -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
|
||||
};
|
||||
|
@ -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"
|
||||
},
|
||||
|
@ -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
179
src/crypto/public_key/prime.js
Normal file
179
src/crypto/public_key/prime.js
Normal 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;
|
||||
};
|
@ -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
|
||||
};
|
||||
|
@ -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()
|
||||
|
||||
};
|
||||
|
||||
/**
|
||||
|
@ -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.');
|
||||
|
62
src/enums.js
62
src/enums.js
@ -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
|
||||
|
@ -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';
|
||||
|
45
src/key.js
45
src/key.js
@ -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;
|
||||
|
@ -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;
|
||||
|
@ -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 }
|
||||
|
@ -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';
|
||||
|
@ -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
|
||||
|
@ -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)]);
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
|
@ -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);
|
||||
|
@ -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]);
|
||||
}
|
||||
|
@ -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);
|
||||
|
@ -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])
|
||||
);
|
||||
|
||||
|
@ -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
|
||||
}
|
||||
|
@ -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
|
||||
|
@ -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).
|
||||
|
@ -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);
|
||||
};
|
||||
|
@ -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));
|
||||
};
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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;
|
||||
|
@ -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;
|
||||
};
|
||||
|
||||
|
117
src/type/mpi.js
117
src/type/mpi.js
@ -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);
|
||||
};
|
||||
|
@ -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;
|
||||
|
@ -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);
|
||||
|
356
src/util.js
356
src/util.js
@ -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);
|
||||
}
|
||||
};
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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
|
||||
);
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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();
|
||||
});
|
||||
|
@ -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
|
||||
});
|
||||
|
@ -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)));
|
||||
});
|
||||
});
|
||||
});
|
||||
|
@ -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;
|
||||
|
@ -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');
|
||||
|
@ -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',
|
||||
|
Loading…
x
Reference in New Issue
Block a user