ElGamal and MPI use bn.js; TODO: RSA Key Generation
This commit is contained in:
parent
490b1dc0f0
commit
e1d85ba682
|
@ -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]
|
||||
);
|
||||
|
|
|
@ -86,7 +86,7 @@ export default {
|
|||
}
|
||||
break;
|
||||
}
|
||||
return {r: r.fromRed(), s: s.fromRed()};
|
||||
return { r: r.fromRed(), s: s.fromRed() };
|
||||
},
|
||||
|
||||
/*
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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;
|
||||
}
|
||||
};
|
||||
|
|
|
@ -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]
|
||||
|
|
|
@ -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)));
|
||||
}
|
||||
|
|
|
@ -134,7 +134,7 @@ PublicKeyEncryptedSessionKey.prototype.decrypt = async function (key) {
|
|||
key.params,
|
||||
this.encrypted,
|
||||
key.fingerprint
|
||||
)).toBytes();
|
||||
)).toString();
|
||||
|
||||
let checksum;
|
||||
let decoded;
|
||||
|
|
|
@ -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);
|
||||
};
|
||||
|
|
|
@ -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) {
|
||||
|
|
Loading…
Reference in New Issue
Block a user