mirror of
https://github.com/Ylianst/MeshCommander
synced 2025-12-05 21:53:19 +00:00
222 lines
6.5 KiB
JavaScript
222 lines
6.5 KiB
JavaScript
/**
|
|
* Javascript implementation of RSA-KEM.
|
|
*
|
|
* @author Lautaro Cozzani Rodriguez
|
|
* @author Dave Longley
|
|
*
|
|
* Copyright (c) 2014 Lautaro Cozzani <lautaro.cozzani@scytl.com>
|
|
* Copyright (c) 2014 Digital Bazaar, Inc.
|
|
*/
|
|
(function() {
|
|
/* ########## Begin module implementation ########## */
|
|
function initModule(forge) {
|
|
|
|
forge.kem = forge.kem || {};
|
|
|
|
var BigInteger = forge.jsbn.BigInteger;
|
|
|
|
/**
|
|
* The API for the RSA Key Encapsulation Mechanism (RSA-KEM) from ISO 18033-2.
|
|
*/
|
|
forge.kem.rsa = {};
|
|
|
|
/**
|
|
* Creates an RSA KEM API object for generating a secret asymmetric key.
|
|
*
|
|
* The symmetric key may be generated via a call to 'encrypt', which will
|
|
* produce a ciphertext to be transmitted to the recipient and a key to be
|
|
* kept secret. The ciphertext is a parameter to be passed to 'decrypt' which
|
|
* will produce the same secret key for the recipient to use to decrypt a
|
|
* message that was encrypted with the secret key.
|
|
*
|
|
* @param kdf the KDF API to use (eg: new forge.kem.kdf1()).
|
|
* @param options the options to use.
|
|
* [prng] a custom crypto-secure pseudo-random number generator to use,
|
|
* that must define "getBytesSync".
|
|
*/
|
|
forge.kem.rsa.create = function(kdf, options) {
|
|
options = options || {};
|
|
var prng = options.prng || forge.random;
|
|
|
|
var kem = {};
|
|
|
|
/**
|
|
* Generates a secret key and its encapsulation.
|
|
*
|
|
* @param publicKey the RSA public key to encrypt with.
|
|
* @param keyLength the length, in bytes, of the secret key to generate.
|
|
*
|
|
* @return an object with:
|
|
* encapsulation: the ciphertext for generating the secret key, as a
|
|
* binary-encoded string of bytes.
|
|
* key: the secret key to use for encrypting a message.
|
|
*/
|
|
kem.encrypt = function(publicKey, keyLength) {
|
|
// generate a random r where 1 > r > n
|
|
var byteLength = Math.ceil(publicKey.n.bitLength() / 8);
|
|
var r;
|
|
do {
|
|
r = new BigInteger(
|
|
forge.util.bytesToHex(prng.getBytesSync(byteLength)),
|
|
16).mod(publicKey.n);
|
|
} while(r.equals(BigInteger.ZERO));
|
|
|
|
// prepend r with zeros
|
|
r = forge.util.hexToBytes(r.toString(16));
|
|
var zeros = byteLength - r.length;
|
|
if(zeros > 0) {
|
|
r = forge.util.fillString(String.fromCharCode(0), zeros) + r;
|
|
}
|
|
|
|
// encrypt the random
|
|
var encapsulation = publicKey.encrypt(r, 'NONE');
|
|
|
|
// generate the secret key
|
|
var key = kdf.generate(r, keyLength);
|
|
|
|
return {encapsulation: encapsulation, key: key};
|
|
};
|
|
|
|
/**
|
|
* Decrypts an encapsulated secret key.
|
|
*
|
|
* @param privateKey the RSA private key to decrypt with.
|
|
* @param encapsulation the ciphertext for generating the secret key, as
|
|
* a binary-encoded string of bytes.
|
|
* @param keyLength the length, in bytes, of the secret key to generate.
|
|
*
|
|
* @return the secret key as a binary-encoded string of bytes.
|
|
*/
|
|
kem.decrypt = function(privateKey, encapsulation, keyLength) {
|
|
// decrypt the encapsulation and generate the secret key
|
|
var r = privateKey.decrypt(encapsulation, 'NONE');
|
|
return kdf.generate(r, keyLength);
|
|
};
|
|
|
|
return kem;
|
|
};
|
|
|
|
// TODO: add forge.kem.kdf.create('KDF1', {md: ..., ...}) API?
|
|
|
|
/**
|
|
* Creates a key derivation API object that implements KDF1 per ISO 18033-2.
|
|
*
|
|
* @param md the hash API to use.
|
|
* @param [digestLength] an optional digest length that must be positive and
|
|
* less than or equal to md.digestLength.
|
|
*
|
|
* @return a KDF1 API object.
|
|
*/
|
|
forge.kem.kdf1 = function(md, digestLength) {
|
|
_createKDF(this, md, 0, digestLength || md.digestLength);
|
|
};
|
|
|
|
/**
|
|
* Creates a key derivation API object that implements KDF2 per ISO 18033-2.
|
|
*
|
|
* @param md the hash API to use.
|
|
* @param [digestLength] an optional digest length that must be positive and
|
|
* less than or equal to md.digestLength.
|
|
*
|
|
* @return a KDF2 API object.
|
|
*/
|
|
forge.kem.kdf2 = function(md, digestLength) {
|
|
_createKDF(this, md, 1, digestLength || md.digestLength);
|
|
};
|
|
|
|
/**
|
|
* Creates a KDF1 or KDF2 API object.
|
|
*
|
|
* @param md the hash API to use.
|
|
* @param counterStart the starting index for the counter.
|
|
* @param digestLength the digest length to use.
|
|
*
|
|
* @return the KDF API object.
|
|
*/
|
|
function _createKDF(kdf, md, counterStart, digestLength) {
|
|
/**
|
|
* Generate a key of the specified length.
|
|
*
|
|
* @param x the binary-encoded byte string to generate a key from.
|
|
* @param length the number of bytes to generate (the size of the key).
|
|
*
|
|
* @return the key as a binary-encoded string.
|
|
*/
|
|
kdf.generate = function(x, length) {
|
|
var key = new forge.util.ByteBuffer();
|
|
|
|
// run counter from counterStart to ceil(length / Hash.len)
|
|
var k = Math.ceil(length / digestLength) + counterStart;
|
|
|
|
var c = new forge.util.ByteBuffer();
|
|
for(var i = counterStart; i < k; ++i) {
|
|
// I2OSP(i, 4): convert counter to an octet string of 4 octets
|
|
c.putInt32(i);
|
|
|
|
// digest 'x' and the counter and add the result to the key
|
|
md.start();
|
|
md.update(x + c.getBytes());
|
|
var hash = md.digest();
|
|
key.putBytes(hash.getBytes(digestLength));
|
|
}
|
|
|
|
// truncate to the correct key length
|
|
key.truncate(key.length() - length);
|
|
return key.getBytes();
|
|
};
|
|
}
|
|
|
|
} // end module implementation
|
|
|
|
/* ########## Begin module wrapper ########## */
|
|
var name = 'kem';
|
|
if(typeof define !== 'function') {
|
|
// NodeJS -> AMD
|
|
if(typeof module === 'object' && module.exports) {
|
|
var nodeJS = true;
|
|
define = function(ids, factory) {
|
|
factory(require, module);
|
|
};
|
|
} else {
|
|
// <script>
|
|
if(typeof forge === 'undefined') {
|
|
forge = {};
|
|
}
|
|
return initModule(forge);
|
|
}
|
|
}
|
|
// AMD
|
|
var deps;
|
|
var defineFunc = function(require, module) {
|
|
module.exports = function(forge) {
|
|
var mods = deps.map(function(dep) {
|
|
return require(dep);
|
|
}).concat(initModule);
|
|
// handle circular dependencies
|
|
forge = forge || {};
|
|
forge.defined = forge.defined || {};
|
|
if(forge.defined[name]) {
|
|
return forge[name];
|
|
}
|
|
forge.defined[name] = true;
|
|
for(var i = 0; i < mods.length; ++i) {
|
|
mods[i](forge);
|
|
}
|
|
return forge[name];
|
|
};
|
|
};
|
|
var tmpDefine = define;
|
|
define = function(ids, factory) {
|
|
deps = (typeof ids === 'string') ? factory.slice(2) : ids.slice(2);
|
|
if(nodeJS) {
|
|
delete define;
|
|
return tmpDefine.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
}
|
|
define = tmpDefine;
|
|
return define.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
};
|
|
define(['require', 'module', './util','./random','./jsbn'], function() {
|
|
defineFunc.apply(null, Array.prototype.slice.call(arguments, 0));
|
|
});
|
|
})();
|