ElGamal and MPI use bn.js; TODO: RSA Key Generation

This commit is contained in:
Mahrud Sayrafi 2018-02-20 03:08:42 -08:00
parent 490b1dc0f0
commit e1d85ba682
No known key found for this signature in database
GPG Key ID: C24071B956C3245F
10 changed files with 145 additions and 134 deletions

View File

@ -32,7 +32,6 @@
*/
import BN from 'bn.js';
import { RSA_RAW } from 'asmcrypto.js';
import publicKey from './public_key';
import cipher from './cipher';
import random from './random';
@ -68,18 +67,19 @@ export default {
switch (algo) {
case 'rsa_encrypt':
case 'rsa_encrypt_sign': {
const m = data.toUint8Array();
const n = publicParams[0].toUint8Array();
const e = publicParams[1].toUint8Array();
const m = data.toUint8Array();
return constructParams(types, [new BN(RSA_RAW.encrypt(m, [n, e]))]);
const res = await publicKey.rsa.encrypt(m, n, e);
return constructParams(types, [new BN(res)]);
}
case 'elgamal': {
const elgamal = new publicKey.elgamal();
const p = publicParams[0].toBigInteger();
const g = publicParams[1].toBigInteger();
const y = publicParams[2].toBigInteger();
const m = data.toBigInteger();
return constructParams(types, elgamal.encrypt(m, g, p, y));
const m = data.toBN();
const p = publicParams[0].toBN();
const g = publicParams[1].toBN();
const y = publicParams[2].toBN();
const res = await publicKey.elgamal.encrypt(m, p, g, y);
return constructParams(types, [res.c1, res.c2]);
}
case 'ecdh': {
const oid = publicParams[0];
@ -117,18 +117,14 @@ export default {
const p = keyIntegers[3].toUint8Array();
const q = keyIntegers[4].toUint8Array();
const u = keyIntegers[5].toUint8Array(); // q^-1 mod p
const dd = new BN(d);
const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1)
const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1)
return new BN(RSA_RAW.decrypt(c, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice
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();
const p = keyIntegers[0].toBigInteger();
return elgamal.decrypt(c1, c2, p, x);
const c1 = dataIntegers[0].toBN();
const c2 = dataIntegers[1].toBN();
const p = keyIntegers[0].toBN();
const x = keyIntegers[3].toBN();
return publicKey.elgamal.decrypt(c1, c2, p, x);
}
case 'ecdh': {
const oid = keyIntegers[0];
@ -263,8 +259,7 @@ export default {
case 'rsa_encrypt':
case 'rsa_encrypt_sign':
case 'rsa_sign': {
const rsa = new publicKey.rsa();
return rsa.generate(bits, "10001").then(function(keyObject) {
return publicKey.rsa.generate(bits, "10001").then(function(keyObject) {
return constructParams(
types, [keyObject.n, keyObject.ee, keyObject.d, keyObject.p, keyObject.q, keyObject.u]
);

View File

@ -86,7 +86,7 @@ export default {
}
break;
}
return {r: r.fromRed(), s: s.fromRed()};
return { r: r.fromRed(), s: s.fromRed() };
},
/*

View File

@ -18,39 +18,41 @@
// 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 one = new BN(1);
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);
const k = random.getRandomBN(one, p);
return {
c1: gred.redPow(k).fromRed(),
c2: yred.redPow(k).redMul(mred).fromRed()
};
},
/*
* c1, c2, p, x are all BN
* returns BN
*/
decrypt: function(c1, c2, p, x) {
const redp = new BN.red(p);
const c1red = c1.toRed(redp);
const c2red = c2.toRed(redp);
return c1red.redPow(x).redInvm().redMul(c2red).fromRed();
}
function decrypt(c1, c2, p, x) {
util.print_debug("Elgamal Decrypt:\nc1:" + util.hexstrdump(c1.toMPI()) + "\n" +
"c2:" + util.hexstrdump(c2.toMPI()) + "\n" +
"p:" + util.hexstrdump(p.toMPI()) + "\n" +
"x:" + util.hexstrdump(x.toMPI()));
return (c1.modPow(x, p).modInverse(p)).multiply(c2).mod(p);
//var c = c1.pow(x).modInverse(p); // c0^-a mod p
//return c.multiply(c2).mod(p);
}
// signing and signature verification using Elgamal is not required by OpenPGP.
this.encrypt = encrypt;
this.decrypt = decrypt;
}
};

View File

@ -67,7 +67,7 @@ function kdf(hash_algo, X, length, param) {
* @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 {Uint8Array} m Value derived from session key (RFC 6637)
* @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
@ -81,7 +81,7 @@ async function encrypt(oid, cipher_algo, hash_algo, m, Q, fingerprint) {
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 BN(v.getPublic()),
C: C

View File

@ -18,6 +18,7 @@
// RSA implementation
/**
* @requires bn.js
* @requires asmcrypto.js
* @requires crypto/public_key/jsbn
* @requires crypto/random
@ -27,6 +28,7 @@
*/
import BN from 'bn.js';
import { RSA_RAW } from 'asmcrypto.js';
import BigInteger from './jsbn';
import random from '../random';
@ -59,7 +61,7 @@ function unblind(t, n) {
return t.multiply(unblinder).mod(n);
}
export default function RSA() {
export default {
/**
* This function uses jsbn Big Num library to decrypt RSA
* @param m message
@ -71,7 +73,12 @@ export default function RSA() {
* @param u RSA u as BigInteger
* @return {BigInteger} The decrypted value of the message
*/
function decrypt(m, n, e, d, p, q, u) {
decrypt: function(m, n, e, d, p, q, u) {
const dd = new BN(d);
const dp = dd.mod(new BN(p).subn(1)).toArrayLike(Uint8Array); // d mod (p-1)
const dq = dd.mod(new BN(q).subn(1)).toArrayLike(Uint8Array); // d mod (q-1)
return new BN(RSA_RAW.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1)); // FIXME remove slice
/*
if (config.rsa_blinding) {
m = blind(m, n, e);
}
@ -89,53 +96,39 @@ export default function RSA() {
}
t = t.multiply(p).add(xp);
// var t = RSA.decrypt(m, [n, e, d, q, p, dq, dp, u]).slice(1)
if (config.rsa_blinding) {
t = unblind(t, n);
}
return t;
}
*/
},
/**
* encrypt message
* @param m message as BigInteger
* @param e public MPI part as BigInteger
* @param n public MPI part as BigInteger
* @param e public MPI part as BigInteger
* @return BigInteger
*/
function encrypt(m, e, n) {
return m.modPowInt(e, n);
}
encrypt: function(m, n, e) {
// return m.modPowInt(e, n);
return RSA_RAW.encrypt(m, [n, e]);
},
/* Sign and Verify */
function sign(m, d, n) {
return m.modPow(d, n);
// return RSA_RAW.sign(m, [n, e, d]);
}
sign: function(m, n, e, d) {
// return m.modPow(d, n);
return RSA_RAW.sign(m, [n, e, d]);
},
function verify(x, e, n) {
return x.modPowInt(e, n);
// return RSA_RAW.verify(x, [n, e]);
}
// "empty" RSA key constructor
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;
}
verify: function(x, n, e) {
// return x.modPowInt(e, n);
return RSA_RAW.verify(x, [n, e]);
},
// Generate a new random private key B bits long, using public expt E
function generate(B, E) {
generate: function(B, E) {
const webCrypto = util.getWebCryptoAll();
//
@ -189,6 +182,20 @@ export default function RSA() {
});
}
// "empty" RSA key constructor
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;
}
function exportKey(keypair) {
// export the generated keys as JsonWebKey (JWK)
// https://tools.ietf.org/html/draft-ietf-jose-json-web-key-33
@ -263,11 +270,4 @@ export default function RSA() {
resolve(key);
});
}
this.encrypt = encrypt;
this.decrypt = decrypt;
this.verify = verify;
this.sign = sign;
this.generate = generate;
this.keyObject = KeyObject;
}
};

View File

@ -6,7 +6,7 @@
* @module crypto/signature
*/
import { RSA_RAW } from 'asmcrypto.js';
// FIXME wrap rsa.js around this
import publicKey from './public_key';
import pkcs1 from './pkcs1';
import util from '../util';
@ -32,7 +32,7 @@ export default {
const m = msg_MPIs[0].toUint8Array();
const n = publickey_MPIs[0].toUint8Array();
const e = publickey_MPIs[1].toUint8Array();
const EM = RSA_RAW.verify(m, [n, e]);
const EM = publicKey.rsa.verify(m, n, e);
const EM2 = pkcs1.emsa.encode(hash_algo, util.Uint8Array2str(data), n.length);
return util.hexidump(EM) === EM2;
}
@ -93,7 +93,8 @@ export default {
const m = util.hex2Uint8Array(
'00'+pkcs1.emsa.encode(hash_algo, data, n.length) // FIXME remove '00'
);
return util.Uint8Array2MPI(RSA_RAW.sign(m, [n, e, d]));
const signature = publicKey.rsa.sign(m, n, e, d);
return util.Uint8Array2MPI(signature);
}
case 17: {
// DSA (Digital Signature Algorithm) [FIPS186] [HAC]

View File

@ -182,7 +182,7 @@ PublicKey.prototype.getFingerprint = function () {
} else if (this.version === 3) {
const paramCount = crypto.getPubKeyParamTypes(this.algorithm).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)));
}

View File

@ -134,7 +134,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
key.params,
this.encrypted,
key.fingerprint
)).toBytes();
)).toString();
let checksum;
let decoded;

View File

@ -44,12 +44,14 @@ import util from '../util';
*/
export default function MPI(data) {
/** An implementation dependent integer */
if (data instanceof BigInteger) {
if (data instanceof BN) {
this.fromBN(data);
} else if (data instanceof BigInteger) {
this.fromBigInteger(data);
} else if (data instanceof BN) {
this.fromBytes(util.Uint8Array2str(data.toArrayLike(Uint8Array)));
} else if (util.isUint8Array(data)) {
this.fromUint8Array(data);
} else if (util.isString(data)) {
this.fromBytes(data);
this.fromString(data);
} else {
this.data = null;
}
@ -57,8 +59,8 @@ export default function MPI(data) {
/**
* 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
* @param {Uint8Array} input Payload of mpi data
* @param {String} endian Endianness of the payload; 'be' for big-endian or 'le' for little-endian
* @return {Integer} Length of data read
*/
MPI.prototype.read = function (bytes, endian='be') {
@ -81,7 +83,7 @@ MPI.prototype.read = function (bytes, endian='be') {
const bytelen = Math.ceil(bits / 8);
let payload = bytes.subarray(2, 2 + bytelen);
if (endian === 'le') {
payload = new Uint8Array(payload).reverse();
payload = Uint8Array.from(payload).reverse();
}
const raw = util.Uint8Array2str(payload);
this.fromBytes(raw);
@ -89,47 +91,58 @@ MPI.prototype.read = function (bytes, endian='be') {
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.Uint8Array2MPI(this.data.toArrayLike(Uint8Array, endian, length));
};
MPI.prototype.toBytes = function () {
return util.Uint8Array2str(this.toUint8Array());
MPI.prototype.byteLength = function () {
return this.write().length - 2;
};
MPI.prototype.toUint8Array = function () {
return this.write().slice(2);
};
MPI.prototype.byteLength = function () {
return this.toBytes().length;
MPI.prototype.fromUint8Array = function (bytes) {
this.data = new BN(bytes);
};
/**
* 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.toString = function () {
return util.Uint8Array2str(this.toUint8Array());
};
MPI.prototype.toBigInteger = function () {
MPI.prototype.fromString = function (str) {
this.data = new BN(util.str2Uint8Array(str));
};
// TODO remove this
MPI.prototype.fromBytes = MPI.prototype.fromString;
MPI.prototype.toBN = function () {
return this.data.clone();
};
MPI.prototype.toBN = function (bn) {
return new BN(this.write().slice(2));
};
MPI.prototype.fromBigInteger = function (bn) {
MPI.prototype.fromBN = function (bn) {
this.data = bn.clone();
};
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;
MPI.prototype.toBigInteger = function () {
return new BigInteger(util.hexidump(this.write()), 16);
};
MPI.prototype.fromBigInteger = function (bn) {
this.data = new BN(bn.toByteArray());
};
MPI.fromClone = function (clone) {
const bn = new BN();
clone.data.copy(bn);
return new MPI(bn);
};

View File

@ -171,7 +171,7 @@ 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) {
@ -435,7 +435,7 @@ 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) {
@ -463,7 +463,7 @@ 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) {