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 util from '../util';
|
||||
|
||||
|
||||
function constructParams(types, data) {
|
||||
return types.map(function(type, i) {
|
||||
if (data && data[i]) {
|
||||
|
|
|
@ -21,108 +21,103 @@
|
|||
* @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 BN from 'bn.js';
|
||||
import hash from '../hash';
|
||||
import random from '../random.js';
|
||||
import hashModule from '../hash';
|
||||
import util from '../../util.js';
|
||||
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;
|
||||
// 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
|
||||
// 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);
|
||||
// TODO rewrite getLeftNBits to work with Uint8Arrays
|
||||
const h = new BN(
|
||||
util.str2Uint8Array(
|
||||
util.getLeftNBits(
|
||||
util.Uint8Array2str(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;
|
||||
k = random.getRandomBN(one, q);
|
||||
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.fromRed(), s: s.fromRed()};
|
||||
},
|
||||
|
||||
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.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");
|
||||
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,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');
|
||||
|
@ -107,9 +109,9 @@ export default {
|
|||
|
||||
let randomBits = util.Uint8Array2str(this.getRandomBytes(numBytes));
|
||||
if (bits % 8 > 0) {
|
||||
randomBits = String.fromCharCode(((2 ** (bits % 8)) - 1) &
|
||||
randomBits.charCodeAt(0)) +
|
||||
randomBits.substring(1);
|
||||
randomBits = String.fromCharCode(
|
||||
((2 ** (bits % 8)) - 1) & randomBits.charCodeAt(0)
|
||||
) + randomBits.substring(1);
|
||||
}
|
||||
const mpi = new type_mpi(randomBits);
|
||||
return mpi.toBigInteger();
|
||||
|
@ -128,6 +130,27 @@ export default {
|
|||
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()
|
||||
|
||||
};
|
||||
|
|
|
@ -27,6 +27,7 @@ export default {
|
|||
let s;
|
||||
let Q;
|
||||
let curve;
|
||||
let signature;
|
||||
|
||||
data = util.Uint8Array2str(data);
|
||||
|
||||
|
@ -51,16 +52,15 @@ export default {
|
|||
}
|
||||
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;
|
||||
const dsa = publicKey.dsa;
|
||||
r = msg_MPIs[0].toBN();
|
||||
s = msg_MPIs[1].toBN();
|
||||
const p = publickey_MPIs[0].toBN();
|
||||
const q = publickey_MPIs[1].toBN();
|
||||
const g = publickey_MPIs[2].toBN();
|
||||
const y = publickey_MPIs[3].toBN();
|
||||
m = util.str2Uint8Array(data);
|
||||
return dsa.verify(hash_algo, r, s, m, p, q, g, y);
|
||||
}
|
||||
case 19: {
|
||||
// ECDSA
|
||||
|
@ -122,15 +122,17 @@ export default {
|
|||
}
|
||||
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());
|
||||
const dsa = publicKey.dsa;
|
||||
const p = keyIntegers[0].toBN();
|
||||
const q = keyIntegers[1].toBN();
|
||||
const g = keyIntegers[2].toBN();
|
||||
const x = keyIntegers[4].toBN();
|
||||
m = util.str2Uint8Array(data);
|
||||
signature = dsa.sign(hash_algo, m, g, p, q, x);
|
||||
return util.concatUint8Array([
|
||||
util.Uint8Array2MPI(signature.r.toArrayLike(Uint8Array)),
|
||||
util.Uint8Array2MPI(signature.s.toArrayLike(Uint8Array))
|
||||
]);
|
||||
}
|
||||
case 16: {
|
||||
// Elgamal (Encrypt-Only) [ELGAMAL] [HAC]
|
||||
|
|
|
@ -29,11 +29,13 @@
|
|||
* 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 bn.js
|
||||
* @requires crypto/public_key/jsbn
|
||||
* @requires util
|
||||
* @module type/mpi
|
||||
*/
|
||||
|
||||
import BN from 'bn.js';
|
||||
import BigInteger from '../crypto/public_key/jsbn';
|
||||
import util from '../util';
|
||||
|
||||
|
@ -110,6 +112,10 @@ MPI.prototype.toBigInteger = function () {
|
|||
return this.data.clone();
|
||||
};
|
||||
|
||||
MPI.prototype.toBN = function (bn) {
|
||||
return new BN(this.write().slice(2));
|
||||
};
|
||||
|
||||
MPI.prototype.fromBigInteger = function (bn) {
|
||||
this.data = bn.clone();
|
||||
};
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
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');
|
||||
chai.use(require('chai-as-promised'));
|
||||
|
|
Loading…
Reference in New Issue
Block a user