implemented the core crypto functions for node
This commit is contained in:
parent
9cfd517880
commit
ac89cb7904
|
@ -28,11 +28,20 @@
|
||||||
return Buffer.concat([decipher.update(Buffer(data.slice(0, -16))), decipher.final()]);
|
return Buffer.concat([decipher.update(Buffer(data.slice(0, -16))), decipher.final()]);
|
||||||
}
|
}
|
||||||
|
|
||||||
function bnToB64(bn) {
|
function bnToBuffer(bn, size) {
|
||||||
if (bn.red) {
|
var buf = bn.toArrayLike(Buffer);
|
||||||
bn = bn.fromRed();
|
|
||||||
|
if (!size || buf.length === size) {
|
||||||
|
return buf;
|
||||||
|
} else if (buf.length < size) {
|
||||||
|
return Buffer.concat([Buffer(size-buf.length).fill(0), buf]);
|
||||||
|
} else if (buf.length > size) {
|
||||||
|
throw new Error('EC signature number bigger than expected');
|
||||||
}
|
}
|
||||||
var b64 = bn.toArrayLike(Buffer).toString('base64');
|
throw new Error('invalid size "'+size+'" converting BigNumber to Buffer');
|
||||||
|
}
|
||||||
|
function bnToB64(bn) {
|
||||||
|
var b64 = bnToBuffer(bn).toString('base64');
|
||||||
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, '');
|
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, '');
|
||||||
}
|
}
|
||||||
function genEcdsaKeyPair() {
|
function genEcdsaKeyPair() {
|
||||||
|
@ -41,8 +50,8 @@
|
||||||
key_ops: ['verify']
|
key_ops: ['verify']
|
||||||
, kty: 'EC'
|
, kty: 'EC'
|
||||||
, crv: 'P-256'
|
, crv: 'P-256'
|
||||||
, x: bnToB64(key.getPublic().x)
|
, x: bnToB64(key.getPublic().getX())
|
||||||
, y: bnToB64(key.getPublic().y)
|
, y: bnToB64(key.getPublic().getY())
|
||||||
};
|
};
|
||||||
|
|
||||||
var privJwk = JSON.parse(JSON.stringify(pubJwk));
|
var privJwk = JSON.parse(JSON.stringify(pubJwk));
|
||||||
|
@ -52,16 +61,6 @@
|
||||||
return {privateKey: privJwk, publicKey: pubJwk};
|
return {privateKey: privJwk, publicKey: pubJwk};
|
||||||
}
|
}
|
||||||
|
|
||||||
function bnToBuffer(bn, size) {
|
|
||||||
var buf = bn.toArrayLike(Buffer);
|
|
||||||
if (!size || buf.length === size) {
|
|
||||||
return buf;
|
|
||||||
}
|
|
||||||
if (buf > size) {
|
|
||||||
throw new Error("EC signature number bigger than expected");
|
|
||||||
}
|
|
||||||
return Buffer.concat([Buffer(size-buf.length).fill(0), buf]);
|
|
||||||
}
|
|
||||||
function sign(jwk, msg) {
|
function sign(jwk, msg) {
|
||||||
var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64'));
|
var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64'));
|
||||||
var sig = key.sign(sha256(msg));
|
var sig = key.sign(sha256(msg));
|
||||||
|
@ -80,7 +79,10 @@
|
||||||
function promiseWrap(func) {
|
function promiseWrap(func) {
|
||||||
return function() {
|
return function() {
|
||||||
var args = arguments;
|
var args = arguments;
|
||||||
return new Promise(function (resolve) {
|
// This fallback file should only be used when the browser doesn't support everything we
|
||||||
|
// need with WebCrypto. Since it is only used in the browser we should be able to assume
|
||||||
|
// that OAUTH3 has been placed in the global scope and that we can access it here.
|
||||||
|
return new OAUTH3.PromiseA(function (resolve) {
|
||||||
resolve(func.apply(null, args));
|
resolve(func.apply(null, args));
|
||||||
});
|
});
|
||||||
};
|
};
|
||||||
|
|
183
oauth3.crypto.js
183
oauth3.crypto.js
|
@ -3,6 +3,85 @@
|
||||||
|
|
||||||
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
|
var OAUTH3 = exports.OAUTH3 = exports.OAUTH3 || require('./oauth3.core.js').OAUTH3;
|
||||||
|
|
||||||
|
OAUTH3.crypto = {};
|
||||||
|
try {
|
||||||
|
OAUTH3.crypto.core = require('./oauth3.node.crypto');
|
||||||
|
} catch (error) {
|
||||||
|
OAUTH3.crypto.core = {};
|
||||||
|
|
||||||
|
// We don't currently have a fallback method for this function, so we assign
|
||||||
|
// it directly to the core object instead of the webCrypto object.
|
||||||
|
OAUTH3.crypto.core.randomBytes = function (size) {
|
||||||
|
var buf = OAUTH3._browser.window.crypto.getRandomValues(new Uint8Array(size));
|
||||||
|
return OAUTH3.PromiseA.resolve(buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
var webCrypto = {};
|
||||||
|
webCrypto.sha256 = function (buf) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.digest({name: 'SHA-256'}, buf);
|
||||||
|
};
|
||||||
|
|
||||||
|
webCrypto.pbkdf2 = function (password, salt) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey'])
|
||||||
|
.then(function (key) {
|
||||||
|
var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}};
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, true, ['encrypt', 'decrypt']);
|
||||||
|
})
|
||||||
|
.then(function (key) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.exportKey('raw', key);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
webCrypto.encrypt = function (rawKey, iv, data) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt'])
|
||||||
|
.then(function (key) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
webCrypto.decrypt = function (rawKey, iv, data) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt'])
|
||||||
|
.then(function (key) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, data);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
webCrypto.genEcdsaKeyPair = function () {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify'])
|
||||||
|
.then(function (keyPair) {
|
||||||
|
return OAUTH3.PromiseA.all([
|
||||||
|
OAUTH3._browser.window.crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
||||||
|
, OAUTH3._browser.window.crypto.subtle.exportKey('jwk', keyPair.publicKey)
|
||||||
|
]);
|
||||||
|
}).then(function (jwkPair) {
|
||||||
|
return { privateKey: jwkPair[0], publicKey: jwkPair[1] };
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
webCrypto.sign = function (jwk, msg) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign'])
|
||||||
|
.then(function (key) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, msg);
|
||||||
|
})
|
||||||
|
.then(function (sig) {
|
||||||
|
return new Uint8Array(sig);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
webCrypto.verify = function (jwk, msg, signature) {
|
||||||
|
// If the JWK has properties that should only exist on the private key or is missing
|
||||||
|
// "verify" in the key_ops, importing in as a public key won't work.
|
||||||
|
if (jwk.hasOwnProperty('d') || jwk.hasOwnProperty('key_ops')) {
|
||||||
|
jwk = JSON.parse(JSON.stringify(jwk));
|
||||||
|
delete jwk.d;
|
||||||
|
delete jwk.key_ops;
|
||||||
|
}
|
||||||
|
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['verify'])
|
||||||
|
.then(function (key) {
|
||||||
|
return OAUTH3._browser.window.crypto.subtle.verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, signature, msg);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
function checkWebCrypto() {
|
||||||
var loadFallback = function() {
|
var loadFallback = function() {
|
||||||
var prom;
|
var prom;
|
||||||
loadFallback = function () { return prom; };
|
loadFallback = function () { return prom; };
|
||||||
|
@ -22,75 +101,6 @@
|
||||||
});
|
});
|
||||||
return prom;
|
return prom;
|
||||||
};
|
};
|
||||||
|
|
||||||
var webCrypto = {};
|
|
||||||
webCrypto.sha256 = function (buf) {
|
|
||||||
return crypto.subtle.digest({name: 'SHA-256'}, buf);
|
|
||||||
};
|
|
||||||
|
|
||||||
webCrypto.pbkdf2 = function (password, salt) {
|
|
||||||
return crypto.subtle.importKey('raw', OAUTH3._binStr.binStrToBuffer(password), {name: 'PBKDF2'}, false, ['deriveKey'])
|
|
||||||
.then(function (key) {
|
|
||||||
var opts = {name: 'PBKDF2', salt: salt, iterations: 8192, hash: {name: 'SHA-256'}};
|
|
||||||
return crypto.subtle.deriveKey(opts, key, {name: 'AES-GCM', length: 128}, true, ['encrypt', 'decrypt']);
|
|
||||||
})
|
|
||||||
.then(function (key) {
|
|
||||||
return crypto.subtle.exportKey('raw', key);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
webCrypto.encrypt = function (rawKey, iv, data) {
|
|
||||||
return crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['encrypt'])
|
|
||||||
.then(function (key) {
|
|
||||||
return crypto.subtle.encrypt({name: 'AES-GCM', iv: iv}, key, data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
webCrypto.decrypt = function (rawKey, iv, data) {
|
|
||||||
return crypto.subtle.importKey('raw', rawKey, {name: 'AES-GCM'}, false, ['decrypt'])
|
|
||||||
.then(function (key) {
|
|
||||||
return crypto.subtle.decrypt({name: 'AES-GCM', iv: iv}, key, data);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
webCrypto.genEcdsaKeyPair = function () {
|
|
||||||
return crypto.subtle.generateKey({name: 'ECDSA', namedCurve: 'P-256'}, true, ['sign', 'verify'])
|
|
||||||
.then(function (keyPair) {
|
|
||||||
return OAUTH3.PromiseA.all([
|
|
||||||
crypto.subtle.exportKey('jwk', keyPair.privateKey)
|
|
||||||
, crypto.subtle.exportKey('jwk', keyPair.publicKey)
|
|
||||||
]);
|
|
||||||
}).then(function (jwkPair) {
|
|
||||||
return { privateKey: jwkPair[0], publicKey: jwkPair[1] };
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
webCrypto.sign = function (jwk, msg) {
|
|
||||||
return crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['sign'])
|
|
||||||
.then(function (key) {
|
|
||||||
return crypto.subtle.sign({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, msg);
|
|
||||||
})
|
|
||||||
.then(function (sig) {
|
|
||||||
return new Uint8Array(sig);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
webCrypto.verify = function (jwk, msg, signature) {
|
|
||||||
// If the JWK has properties that should only exist on the private key or is missing
|
|
||||||
// "verify" in the key_ops, importing in as a public key won't work.
|
|
||||||
if (jwk.hasOwnProperty('d') || jwk.hasOwnProperty('key_ops')) {
|
|
||||||
jwk = JSON.parse(JSON.stringify(jwk));
|
|
||||||
delete jwk.d;
|
|
||||||
delete jwk.key_ops;
|
|
||||||
}
|
|
||||||
|
|
||||||
return crypto.subtle.importKey('jwk', jwk, {name: 'ECDSA', namedCurve: jwk.crv}, false, ['verify'])
|
|
||||||
.then(function (key) {
|
|
||||||
return crypto.subtle.verify({name: 'ECDSA', hash: {name: 'SHA-256'}}, key, signature, msg);
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
OAUTH3.crypto = {};
|
|
||||||
OAUTH3.crypto.core = {};
|
|
||||||
function checkWebCrypto() {
|
|
||||||
function checkException(name, func) {
|
function checkException(name, func) {
|
||||||
new OAUTH3.PromiseA(function (resolve) { resolve(func()); })
|
new OAUTH3.PromiseA(function (resolve) { resolve(func()); })
|
||||||
.then(function () {
|
.then(function () {
|
||||||
|
@ -157,6 +167,7 @@
|
||||||
});
|
});
|
||||||
}
|
}
|
||||||
checkWebCrypto();
|
checkWebCrypto();
|
||||||
|
}
|
||||||
|
|
||||||
OAUTH3.crypto.thumbprintJwk = function (jwk) {
|
OAUTH3.crypto.thumbprintJwk = function (jwk) {
|
||||||
var keys;
|
var keys;
|
||||||
|
@ -183,12 +194,12 @@
|
||||||
};
|
};
|
||||||
|
|
||||||
OAUTH3.crypto._createKey = function (ppid) {
|
OAUTH3.crypto._createKey = function (ppid) {
|
||||||
var kekPromise, ecdsaPromise;
|
var saltProm = OAUTH3.crypto.core.randomBytes(16);
|
||||||
var salt = window.crypto.getRandomValues(new Uint8Array(16));
|
var kekProm = saltProm.then(function (salt) {
|
||||||
|
return OAUTH3.crypto.core.pbkdf2(ppid, salt);
|
||||||
|
});
|
||||||
|
|
||||||
kekPromise = OAUTH3.crypto.core.pbkdf2(ppid, salt);
|
var ecdsaProm = OAUTH3.crypto.core.genEcdsaKeyPair()
|
||||||
|
|
||||||
ecdsaPromise = OAUTH3.crypto.core.genEcdsaKeyPair()
|
|
||||||
.then(function (keyPair) {
|
.then(function (keyPair) {
|
||||||
return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) {
|
return OAUTH3.crypto.thumbprintJwk(keyPair.publicKey).then(function (kid) {
|
||||||
keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256';
|
keyPair.privateKey.alg = keyPair.publicKey.alg = 'ES256';
|
||||||
|
@ -197,18 +208,28 @@
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
return OAUTH3.PromiseA.all([kekPromise, ecdsaPromise]).then(function (keys) {
|
return OAUTH3.PromiseA.all([
|
||||||
var ecdsaIv = window.crypto.getRandomValues(new Uint8Array(12));
|
kekProm
|
||||||
var secretIv = window.crypto.getRandomValues(new Uint8Array(12));
|
, ecdsaProm
|
||||||
var userSecret = window.crypto.getRandomValues(new Uint8Array(16));
|
, saltProm
|
||||||
|
, OAUTH3.crypto.core.randomBytes(16)
|
||||||
|
, OAUTH3.crypto.core.randomBytes(12)
|
||||||
|
, OAUTH3.crypto.core.randomBytes(12)
|
||||||
|
]).then(function (results) {
|
||||||
|
var kek = results[0];
|
||||||
|
var keyPair = results[1];
|
||||||
|
var salt = results[2];
|
||||||
|
var userSecret = results[3];
|
||||||
|
var ecdsaIv = results[4];
|
||||||
|
var secretIv = results[5];
|
||||||
|
|
||||||
return OAUTH3.PromiseA.all([
|
return OAUTH3.PromiseA.all([
|
||||||
OAUTH3.crypto.core.encrypt(keys[0], ecdsaIv, OAUTH3._binStr.binStrToBuffer(JSON.stringify(keys[1].privateKey)))
|
OAUTH3.crypto.core.encrypt(kek, ecdsaIv, OAUTH3._binStr.binStrToBuffer(JSON.stringify(keyPair.privateKey)))
|
||||||
, OAUTH3.crypto.core.encrypt(keys[0], secretIv, userSecret)
|
, OAUTH3.crypto.core.encrypt(kek, secretIv, userSecret)
|
||||||
])
|
])
|
||||||
.then(function (encrypted) {
|
.then(function (encrypted) {
|
||||||
return {
|
return {
|
||||||
publicKey: keys[1].publicKey
|
publicKey: keyPair.publicKey
|
||||||
, privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0])
|
, privateKey: OAUTH3._base64.bufferToUrlSafe(encrypted[0])
|
||||||
, userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
|
, userSecret: OAUTH3._base64.bufferToUrlSafe(encrypted[1])
|
||||||
, salt: OAUTH3._base64.bufferToUrlSafe(salt)
|
, salt: OAUTH3._base64.bufferToUrlSafe(salt)
|
||||||
|
|
|
@ -0,0 +1,106 @@
|
||||||
|
;(function () {
|
||||||
|
'use strict';
|
||||||
|
|
||||||
|
var crypto = require('crypto');
|
||||||
|
var OAUTH3 = require('./oauth3.core.js').OAUTH3;
|
||||||
|
var ec = require('elliptic').ec('p256');
|
||||||
|
|
||||||
|
function randomBytes(size) {
|
||||||
|
return new OAUTH3.PromiseA(function (resolve, reject) {
|
||||||
|
crypto.randomBytes(size, function (err, buf) {
|
||||||
|
if (err) {
|
||||||
|
reject(err);
|
||||||
|
} else {
|
||||||
|
resolve(buf);
|
||||||
|
}
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
function sha256(buf) {
|
||||||
|
return crypto.createHash('sha256').update(buf).digest();
|
||||||
|
}
|
||||||
|
|
||||||
|
function pbkdf2(password, salt) {
|
||||||
|
// Derived AES key is 128 bit, and the function takes a size in bytes.
|
||||||
|
return crypto.pbkdf2Sync(password, Buffer(salt), 8192, 16, 'sha256');
|
||||||
|
}
|
||||||
|
|
||||||
|
function encrypt(key, iv, data) {
|
||||||
|
var cipher = crypto.createCipheriv('aes-128-gcm', Buffer(key), Buffer(iv));
|
||||||
|
|
||||||
|
return Buffer.concat([cipher.update(Buffer(data)), cipher.final(), cipher.getAuthTag()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function decrypt(key, iv, data) {
|
||||||
|
var decipher = crypto.createDecipheriv('aes-128-gcm', Buffer(key), Buffer(iv));
|
||||||
|
|
||||||
|
decipher.setAuthTag(Buffer(data.slice(-16)));
|
||||||
|
return Buffer.concat([decipher.update(Buffer(data.slice(0, -16))), decipher.final()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function bnToBuffer(bn, size) {
|
||||||
|
var buf = bn.toArrayLike(Buffer);
|
||||||
|
|
||||||
|
if (!size || buf.length === size) {
|
||||||
|
return buf;
|
||||||
|
} else if (buf.length < size) {
|
||||||
|
return Buffer.concat([Buffer(size-buf.length).fill(0), buf]);
|
||||||
|
} else if (buf.length > size) {
|
||||||
|
throw new Error('EC signature number bigger than expected');
|
||||||
|
}
|
||||||
|
throw new Error('invalid size "'+size+'" converting BigNumber to Buffer');
|
||||||
|
}
|
||||||
|
function bnToB64(bn) {
|
||||||
|
var b64 = bnToBuffer(bn).toString('base64');
|
||||||
|
return b64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=*$/, '');
|
||||||
|
}
|
||||||
|
function genEcdsaKeyPair() {
|
||||||
|
var key = ec.genKeyPair();
|
||||||
|
var pubJwk = {
|
||||||
|
key_ops: ['verify']
|
||||||
|
, kty: 'EC'
|
||||||
|
, crv: 'P-256'
|
||||||
|
, x: bnToB64(key.getPublic().getX())
|
||||||
|
, y: bnToB64(key.getPublic().getY())
|
||||||
|
};
|
||||||
|
|
||||||
|
var privJwk = JSON.parse(JSON.stringify(pubJwk));
|
||||||
|
privJwk.key_ops = ['sign'];
|
||||||
|
privJwk.d = bnToB64(key.getPrivate());
|
||||||
|
|
||||||
|
return {privateKey: privJwk, publicKey: pubJwk};
|
||||||
|
}
|
||||||
|
|
||||||
|
function sign(jwk, msg) {
|
||||||
|
var key = ec.keyFromPrivate(Buffer(jwk.d, 'base64'));
|
||||||
|
var sig = key.sign(sha256(msg));
|
||||||
|
return Buffer.concat([bnToBuffer(sig.r, 32), bnToBuffer(sig.s, 32)]);
|
||||||
|
}
|
||||||
|
|
||||||
|
function verify(jwk, msg, signature) {
|
||||||
|
var key = ec.keyFromPublic({x: Buffer(jwk.x, 'base64'), y: Buffer(jwk.y, 'base64')});
|
||||||
|
var sig = {
|
||||||
|
r: Buffer(signature.slice(0, signature.length/2))
|
||||||
|
, s: Buffer(signature.slice(signature.length/2))
|
||||||
|
};
|
||||||
|
return key.verify(sha256(msg), sig);
|
||||||
|
}
|
||||||
|
|
||||||
|
function promiseWrap(func) {
|
||||||
|
return function() {
|
||||||
|
var args = arguments;
|
||||||
|
return new OAUTH3.PromiseA(function (resolve) {
|
||||||
|
resolve(func.apply(null, args));
|
||||||
|
});
|
||||||
|
};
|
||||||
|
}
|
||||||
|
exports.sha256 = promiseWrap(sha256);
|
||||||
|
exports.pbkdf2 = promiseWrap(pbkdf2);
|
||||||
|
exports.encrypt = promiseWrap(encrypt);
|
||||||
|
exports.decrypt = promiseWrap(decrypt);
|
||||||
|
exports.sign = promiseWrap(sign);
|
||||||
|
exports.verify = promiseWrap(verify);
|
||||||
|
exports.genEcdsaKeyPair = promiseWrap(genEcdsaKeyPair);
|
||||||
|
exports.randomBytes = randomBytes;
|
||||||
|
}());
|
|
@ -31,10 +31,12 @@
|
||||||
"log",
|
"log",
|
||||||
"sign"
|
"sign"
|
||||||
],
|
],
|
||||||
|
"dependencies": {
|
||||||
|
"elliptic": "^6.4.0"
|
||||||
|
},
|
||||||
"devDependencies": {
|
"devDependencies": {
|
||||||
"browserify-aes": "^1.0.6",
|
"browserify-aes": "^1.0.6",
|
||||||
"create-hash": "^1.1.2",
|
"create-hash": "^1.1.2",
|
||||||
"elliptic": "^6.4.0",
|
|
||||||
"pbkdf2": "^3.0.9",
|
"pbkdf2": "^3.0.9",
|
||||||
|
|
||||||
"browserify": "^14.1.0",
|
"browserify": "^14.1.0",
|
||||||
|
|
Loading…
Reference in New Issue