DSA uses BN.js
added toBN for type_mpi
This commit is contained in:
parent
b126fd5be7
commit
2f3c0a86e9
|
@ -41,7 +41,6 @@ import type_mpi from '../type/mpi';
|
||||||
import type_oid from '../type/oid';
|
import type_oid from '../type/oid';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
|
|
||||||
function constructParams(types, data) {
|
function constructParams(types, data) {
|
||||||
return types.map(function(type, i) {
|
return types.map(function(type, i) {
|
||||||
if (data && data[i]) {
|
if (data && data[i]) {
|
||||||
|
|
|
@ -21,108 +21,103 @@
|
||||||
* @requires crypto/hash
|
* @requires crypto/hash
|
||||||
* @requires crypto/public_key/jsbn
|
* @requires crypto/public_key/jsbn
|
||||||
* @requires crypto/random
|
* @requires crypto/random
|
||||||
|
* @requires config
|
||||||
* @requires util
|
* @requires util
|
||||||
* @module crypto/public_key/dsa
|
* @module crypto/public_key/dsa
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import BigInteger from './jsbn.js';
|
import BN from 'bn.js';
|
||||||
|
import hash from '../hash';
|
||||||
import random from '../random.js';
|
import random from '../random.js';
|
||||||
import hashModule from '../hash';
|
|
||||||
import util from '../../util.js';
|
|
||||||
import config from '../../config';
|
import config from '../../config';
|
||||||
|
import util from '../../util';
|
||||||
|
|
||||||
export default function DSA() {
|
const one = new BN(1);
|
||||||
// s1 = ((g**s) mod p) mod q
|
const zero = new BN(0);
|
||||||
// s1 = ((s**-1)*(sha-1(m)+(s1*x) mod q)
|
|
||||||
function sign(hashalgo, m, g, p, q, x) {
|
/*
|
||||||
|
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;
|
||||||
|
// TODO benchmark BN.mont vs BN.red
|
||||||
|
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
|
// If the output size of the chosen hash is larger than the number of
|
||||||
// bits of q, the hash result is truncated to fit by taking the number
|
// bits of q, the hash result is truncated to fit by taking the number
|
||||||
// of leftmost bits equal to the number of bits of q. This (possibly
|
// of leftmost bits equal to the number of bits of q. This (possibly
|
||||||
// truncated) hash function result is treated as a number and used
|
// truncated) hash function result is treated as a number and used
|
||||||
// directly in the DSA signature algorithm.
|
// directly in the DSA signature algorithm.
|
||||||
const hashed_data = util.getLeftNBits(util.Uint8Array2str(hashModule.digest(hashalgo, util.str2Uint8Array(m))), q.bitLength());
|
// TODO rewrite getLeftNBits to work with Uint8Arrays
|
||||||
const hash = new BigInteger(util.hexstrdump(hashed_data), 16);
|
const h = new BN(
|
||||||
|
util.str2Uint8Array(
|
||||||
|
util.getLeftNBits(
|
||||||
|
util.Uint8Array2str(hash.digest(hash_algo, m)), q.bitLength())));
|
||||||
// FIPS-186-4, section 4.6:
|
// FIPS-186-4, section 4.6:
|
||||||
// The values of r and s shall be checked to determine if r = 0 or s = 0.
|
// The values of r and s shall be checked to determine if r = 0 or s = 0.
|
||||||
// If either r = 0 or s = 0, a new value of k shall be generated, and the
|
// If either r = 0 or s = 0, a new value of k shall be generated, and the
|
||||||
// signature shall be recalculated. It is extremely unlikely that r = 0
|
// signature shall be recalculated. It is extremely unlikely that r = 0
|
||||||
// or s = 0 if signatures are generated properly.
|
// or s = 0 if signatures are generated properly.
|
||||||
let k;
|
|
||||||
let s1;
|
|
||||||
let s2;
|
|
||||||
while (true) {
|
while (true) {
|
||||||
k = random.getRandomBigIntegerInRange(BigInteger.ONE, q.subtract(BigInteger.ONE));
|
k = random.getRandomBN(one, q);
|
||||||
s1 = (g.modPow(k, p)).mod(q);
|
r = gred.redPow(k).fromRed().toRed(redq); // (g**k mod p) mod q
|
||||||
s2 = (k.modInverse(q).multiply(hash.add(x.multiply(s1)))).mod(q);
|
if (zero.cmp(r) === 0) {
|
||||||
if (s1 !== 0 && s2 !== 0) {
|
continue;
|
||||||
break;
|
|
||||||
}
|
}
|
||||||
|
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 = [];
|
return {r: r.fromRed(), s: s.fromRed()};
|
||||||
result[0] = s1.toMPI();
|
},
|
||||||
result[1] = s2.toMPI();
|
|
||||||
return result;
|
|
||||||
}
|
|
||||||
|
|
||||||
function select_hash_algorithm(q) {
|
/*
|
||||||
const usersetting = config.prefer_hash_algorithm;
|
* hash_algo is integer
|
||||||
/*
|
* r, s are both BN
|
||||||
* 1024-bit key, 160-bit q, SHA-1, SHA-224, SHA-256, SHA-384, or SHA-512 hash
|
* m is string
|
||||||
* 2048-bit key, 224-bit q, SHA-224, SHA-256, SHA-384, or SHA-512 hash
|
* p, q, g, y are all BN
|
||||||
* 2048-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
|
* returns BN
|
||||||
* 3072-bit key, 256-bit q, SHA-256, SHA-384, or SHA-512 hash
|
*/
|
||||||
*/
|
verify: function(hash_algo, r, s, m, p, q, g, y) {
|
||||||
switch (Math.round(q.bitLength() / 8)) {
|
if (zero.ucmp(r) >= 0 || r.ucmp(q) >= 0 ||
|
||||||
case 20:
|
zero.ucmp(s) >= 0 || s.ucmp(q) >= 0) {
|
||||||
// 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) {
|
|
||||||
util.print_debug("invalid DSA Signature");
|
util.print_debug("invalid DSA Signature");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const w = s2.modInverse(q);
|
const redp = new BN.red(p);
|
||||||
if (BigInteger.ZERO.compareTo(w) === 0) {
|
const redq = new BN.red(q);
|
||||||
|
const h = new BN(
|
||||||
|
util.str2Uint8Array(
|
||||||
|
util.getLeftNBits(
|
||||||
|
util.Uint8Array2str(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");
|
util.print_debug("invalid DSA Signature");
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
const u1 = hash.multiply(w).mod(q);
|
const u1 = h.toRed(redq).redMul(w); // H(m) * w mod q
|
||||||
const u2 = s1.multiply(w).mod(q);
|
const u2 = r.toRed(redq).redMul(w); // r * w mod q
|
||||||
return g.modPow(u1, p).multiply(y.modPow(u2, p)).mod(p).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,13 +18,15 @@
|
||||||
// The GPG4Browsers crypto interface
|
// The GPG4Browsers crypto interface
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @requires bn.js
|
||||||
* @requires type/mpi
|
* @requires type/mpi
|
||||||
* @requires util
|
* @requires util
|
||||||
* @module crypto/random
|
* @module crypto/random
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import type_mpi from '../type/mpi.js';
|
import BN from 'bn.js';
|
||||||
import util from '../util.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
|
// Do not use util.getNodeCrypto because we need this regardless of use_native setting
|
||||||
const nodeCrypto = util.detectNode() && require('crypto');
|
const nodeCrypto = util.detectNode() && require('crypto');
|
||||||
|
@ -107,9 +109,9 @@ export default {
|
||||||
|
|
||||||
let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes));
|
let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes));
|
||||||
if (bits % 8 > 0) {
|
if (bits % 8 > 0) {
|
||||||
randomBits = String.fromCharCode(((2 ** (bits % 8)) - 1) &
|
randomBits = String.fromCharCode(
|
||||||
randomBits.charCodeAt(0)) +
|
((2 ** (bits % 8)) - 1) & randomBits.charCodeAt(0)
|
||||||
randomBits.substring(1);
|
) + randomBits.substring(1);
|
||||||
}
|
}
|
||||||
const mpi = new type_mpi(randomBits);
|
const mpi = new type_mpi(randomBits);
|
||||||
return mpi.toBigInteger();
|
return mpi.toBigInteger();
|
||||||
|
@ -128,6 +130,27 @@ export default {
|
||||||
return min.add(r);
|
return min.add(r);
|
||||||
},
|
},
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Create a secure random MPI in specified range
|
||||||
|
* @param {module:type/mpi} min Lower bound, included
|
||||||
|
* @param {module:type/mpi} max Upper bound, excluded
|
||||||
|
* @return {module:BN} Random MPI
|
||||||
|
*/
|
||||||
|
getRandomBN: function(min, max) {
|
||||||
|
if (max.cmp(min) <= 0) {
|
||||||
|
throw new Error('Illegal parameter value: max <= min');
|
||||||
|
}
|
||||||
|
|
||||||
|
const modulus = max.sub(min);
|
||||||
|
const length = modulus.byteLength();
|
||||||
|
let r = new BN(this.getRandomBytes(length));
|
||||||
|
// Using a while loop is necessary to avoid bias
|
||||||
|
while (r.cmp(modulus) >= 0) {
|
||||||
|
r = new BN(this.getRandomBytes(length));
|
||||||
|
}
|
||||||
|
return r.iadd(min);
|
||||||
|
},
|
||||||
|
|
||||||
randomBuffer: new RandomBuffer()
|
randomBuffer: new RandomBuffer()
|
||||||
|
|
||||||
};
|
};
|
||||||
|
|
|
@ -27,6 +27,7 @@ export default {
|
||||||
let s;
|
let s;
|
||||||
let Q;
|
let Q;
|
||||||
let curve;
|
let curve;
|
||||||
|
let signature;
|
||||||
|
|
||||||
data = util.Uint8Array2str(data);
|
data = util.Uint8Array2str(data);
|
||||||
|
|
||||||
|
@ -51,16 +52,15 @@ export default {
|
||||||
}
|
}
|
||||||
case 17: {
|
case 17: {
|
||||||
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
|
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
|
||||||
const dsa = new publicKey.dsa();
|
const dsa = publicKey.dsa;
|
||||||
const s1 = msg_MPIs[0].toBigInteger();
|
r = msg_MPIs[0].toBN();
|
||||||
const s2 = msg_MPIs[1].toBigInteger();
|
s = msg_MPIs[1].toBN();
|
||||||
const p = publickey_MPIs[0].toBigInteger();
|
const p = publickey_MPIs[0].toBN();
|
||||||
const q = publickey_MPIs[1].toBigInteger();
|
const q = publickey_MPIs[1].toBN();
|
||||||
const g = publickey_MPIs[2].toBigInteger();
|
const g = publickey_MPIs[2].toBN();
|
||||||
const y = publickey_MPIs[3].toBigInteger();
|
const y = publickey_MPIs[3].toBN();
|
||||||
m = data;
|
m = util.str2Uint8Array(data);
|
||||||
const dopublic = dsa.verify(hash_algo, s1, s2, m, p, q, g, y);
|
return dsa.verify(hash_algo, r, s, m, p, q, g, y);
|
||||||
return dopublic.compareTo(s1) === 0;
|
|
||||||
}
|
}
|
||||||
case 19: {
|
case 19: {
|
||||||
// ECDSA
|
// ECDSA
|
||||||
|
@ -122,15 +122,17 @@ export default {
|
||||||
}
|
}
|
||||||
case 17: {
|
case 17: {
|
||||||
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
|
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]
|
||||||
const dsa = new publicKey.dsa();
|
const dsa = publicKey.dsa;
|
||||||
|
const p = keyIntegers[0].toBN();
|
||||||
const p = keyIntegers[0].toBigInteger();
|
const q = keyIntegers[1].toBN();
|
||||||
const q = keyIntegers[1].toBigInteger();
|
const g = keyIntegers[2].toBN();
|
||||||
const g = keyIntegers[2].toBigInteger();
|
const x = keyIntegers[4].toBN();
|
||||||
const x = keyIntegers[4].toBigInteger();
|
m = util.str2Uint8Array(data);
|
||||||
m = data;
|
signature = dsa.sign(hash_algo, m, g, p, q, x);
|
||||||
const result = dsa.sign(hash_algo, m, g, p, q, x);
|
return util.concatUint8Array([
|
||||||
return util.str2Uint8Array(result[0].toString() + result[1].toString());
|
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
|
||||||
|
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
|
||||||
|
]);
|
||||||
}
|
}
|
||||||
case 16: {
|
case 16: {
|
||||||
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
|
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
|
||||||
|
|
|
@ -29,11 +29,13 @@
|
||||||
* An MPI consists of two pieces: a two-octet scalar that is the length
|
* 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
|
* of the MPI in bits followed by a string of octets that contain the
|
||||||
* actual integer.
|
* actual integer.
|
||||||
|
* @requires bn.js
|
||||||
* @requires crypto/public_key/jsbn
|
* @requires crypto/public_key/jsbn
|
||||||
* @requires util
|
* @requires util
|
||||||
* @module type/mpi
|
* @module type/mpi
|
||||||
*/
|
*/
|
||||||
|
|
||||||
|
import BN from 'bn.js';
|
||||||
import BigInteger from '../crypto/public_key/jsbn';
|
import BigInteger from '../crypto/public_key/jsbn';
|
||||||
import util from '../util';
|
import util from '../util';
|
||||||
|
|
||||||
|
@ -110,6 +112,10 @@ MPI.prototype.toBigInteger = function () {
|
||||||
return this.data.clone();
|
return this.data.clone();
|
||||||
};
|
};
|
||||||
|
|
||||||
|
MPI.prototype.toBN = function (bn) {
|
||||||
|
return new BN(this.write().slice(2));
|
||||||
|
};
|
||||||
|
|
||||||
MPI.prototype.fromBigInteger = function (bn) {
|
MPI.prototype.fromBigInteger = function (bn) {
|
||||||
this.data = bn.clone();
|
this.data = bn.clone();
|
||||||
};
|
};
|
||||||
|
|
|
@ -1,5 +1,5 @@
|
||||||
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
const openpgp = typeof window !== 'undefined' && window.openpgp ? window.openpgp : require('../../dist/openpgp');
|
||||||
const asmCrypto = require('asmcrypto-lite');
|
const asmCrypto = require('asmcrypto.js');
|
||||||
|
|
||||||
const chai = require('chai');
|
const chai = require('chai');
|
||||||
chai.use(require('chai-as-promised'));
|
chai.use(require('chai-as-promised'));
|
||||||
|
|
Loading…
Reference in New Issue
Block a user