native-crypto
Version:
native-crypto ===
491 lines (483 loc) • 18.4 kB
JavaScript
;
var raw;
if (process.browser) {
window.myDebug = require('debug');
window.myDebug.enable('native-crypto:*');
} else {
try {
raw = require('raw-ecdsa');
} catch (e) {}
}
var base64url = require('../lib/base64url');
var jwk = require('../lib/jwk');
var crypto = require('crypto');
var test = require('tape');
var Hash = require('../hash');
var Hmac = require('../hmac');
var encrypt = require('../encrypt');
var decrypt = require('../decrypt');
var ECDH = require('../lib/ecdh');
var Signature = require('../signature');
var jwk2pem = require('jwk-to-pem');
var format = require('ecdsa-sig-formatter');
var fromDer = format.derToJose;
var toDer = format.joseToDer;
var EC = require('elliptic').ec;
var rsa = require('../rsa');
var pbkdf2Fixtures = require('./pbkdf2-fixtures.json');
require('./test-generate');
// from the node module pbkdf2
var pbkdf2 = require('../pbkdf2');
test('hash', function(t) {
var buf = new Buffer(8);
buf.fill(0);
eachAlgo('sha1', t);
eachAlgo('sha256', t);
eachAlgo('sha384', t);
eachAlgo('sha512', t);
function eachAlgo(algo, t) {
t.test(algo, function(t) {
var nodeHash = crypto.createHash(algo).update(buf).digest().toString('hex');
new Hash(algo).update(buf).digest().then(function(ourHash) {
t.equals(nodeHash, ourHash.toString('hex'));
t.end();
}).catch(function(e) {
t.ok(false, e.stack);
t.end();
});
});
}
});
test('hmac sign', function(t) {
var buf = new Buffer(8);
buf.fill(0);
eachAlgo('sha1', t);
eachAlgo('sha256', t);
eachAlgo('sha384', t);
eachAlgo('sha512', t);
function eachAlgo(algo, t) {
t.test(algo, function(t) {
var nodeHash = crypto.createHmac(algo, buf).update(buf).digest().toString('hex');
new Hmac(algo, buf).update(buf).digest().then(function(ourHash) {
t.equals(nodeHash, ourHash.toString('hex'), 'worked');
t.end();
}).catch(function(e) {
t.ok(false, e.stack);
t.end();
});
});
}
});
test('hmac verify', function(t) {
var buf = new Buffer(8);
buf.fill(0);
eachAlgo('sha1', t);
eachAlgo('sha256', t);
eachAlgo('sha384', t);
eachAlgo('sha512', t);
function eachAlgo(algo, t) {
t.test(algo, function(t) {
var nodeHash = crypto.createHmac(algo, buf).update(buf).digest();
new Hmac(algo, buf, nodeHash).update(buf).verify().then(function(ourHash) {
t.ok(ourHash, 'worked');
t.end();
}).catch(function(e) {
t.ok(false, e.stack);
t.end();
});
});
}
});
test('encrypt/decrypt', function(t) {
var key = new Buffer(16);
var iv = new Buffer(12);
var aad = new Buffer(8);
var data = new Buffer(64);
key.fill(0);
iv.fill(0);
aad.fill(0);
data.fill(0);
var nodeCipher = crypto.createCipheriv('aes-128-gcm', key, iv);
nodeCipher.setAAD(aad);
var out = nodeCipher.update(data);
nodeCipher.final();
var tag = nodeCipher.getAuthTag();
var expected = Buffer.concat([out, tag]).toString('hex');
encrypt(key, iv, data, aad).then(function(res) {
t.equals(res.toString('hex'), expected, 'encrypted');
return decrypt(key, iv, res, aad);
}).then(function(res) {
t.equals(res.toString('hex'), data.toString('hex'), 'decrypted');
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
test('ecdh p-256', function(t) {
var nodeECDH = crypto.createECDH('prime256v1');
var ourECDH = new ECDH('prime256v1');
var nodePublic, ourPublic;
ourECDH.getPublic().then(function(_ourPublic) {
ourPublic = _ourPublic;
nodePublic = nodeECDH.generateKeys();
return ourECDH.computeSecret(jwk.toJwk(nodePublic, 'P-256'));
}).then(function(ourValue) {
var nodeValue = nodeECDH.computeSecret(jwk.fromJwk(ourPublic));
t.equals(ourValue.toString('hex'), nodeValue.toString('hex'));
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
test('ecdh p-256 with privatekeys', function(t) {
var nodeECDH = crypto.createECDH('prime256v1');
var ourECDH = new ECDH('prime256v1');
ourECDH.getPrivate().then(function(priv) {
var ourECDH2 = new ECDH('prime256v1', priv);
var nodePublic, ourPublic, nodeValue;
ourECDH.getPublic().then(function(_ourPublic) {
ourPublic = _ourPublic;
nodePublic = nodeECDH.generateKeys();
return ourECDH.computeSecret(jwk.toJwk(nodePublic, 'P-256'));
}).then(function(ourValue) {
nodeValue = nodeECDH.computeSecret(jwk.fromJwk(ourPublic));
t.equals(ourValue.toString('hex'), nodeValue.toString('hex'));
return ourECDH2.getPublic();
}).then(function(pub) {
var ourValue2 = nodeECDH.computeSecret(jwk.fromJwk(pub));
t.equals(ourValue2.toString('hex'), nodeValue.toString('hex'));
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
});
if (!process.browser) {
test('ecdh p-384', function(t) {
var nodeECDH = crypto.createECDH('secp384r1');
var ourECDH = new ECDH('secp384r1');
var nodePublic, ourPublic;
ourECDH.getPublic().then(function(_ourPublic) {
ourPublic = _ourPublic;
nodePublic = nodeECDH.generateKeys();
return ourECDH.computeSecret(jwk.toJwk(nodePublic, 'P-384'));
}).then(function(ourValue) {
var nodeValue = nodeECDH.computeSecret(jwk.fromJwk(ourPublic));
t.equals(ourValue.toString('hex'), nodeValue.toString('hex'));
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
test('ecdh p-521', function(t) {
var nodeECDH = crypto.createECDH('secp521r1');
var ourECDH = new ECDH('secp521r1');
var nodePublic, ourPublic;
ourECDH.getPublic().then(function(_ourPublic) {
ourPublic = _ourPublic;
nodePublic = nodeECDH.generateKeys();
return ourECDH.computeSecret(jwk.toJwk(nodePublic, 'P-521'));
}).then(function(ourValue) {
var nodeValue = nodeECDH.computeSecret(jwk.fromJwk(ourPublic));
t.equals(ourValue.toString('hex'), nodeValue.toString('hex'));
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
}
test('rsa', function(t) {
var priv = {
'alg': 'RS256',
'd': 'sdK3B7o5KLYyqtmJXOAjEAYEgxUgzBuNawX2jn0Gdq1KDmDve7jnWvqH15N6S79-nim_D9QCjdEnnW9G_x58aU0BBB4o5EGyR5jjViAaP_hqg2GpofYAdP0prP4v6Z5gAq7Dj0pOT9f4zMShhQrOdtYo_-Qm_O4QG420D-qnQgE',
'dp': 'k0oeKJrXapFiBKQ_9h1hQk4NgYBlBeWBCFTUKkl2fHHDy6o4e8UYF2afGuIFUl8I678jmxt7rYNI_9YwC11OFw',
'dq': 'bYkZ8wm-oo_1KNZVC3nKslFlCUYEAYFsCnMSc2nhos7YiBcpAEkSzCGRX0jhpeWiFfYPzVWLpRDDM11vG_KCgQ',
'e': 'AQAB',
'ext': true,
'key_ops': ['sign'],
'kty': 'RSA',
'n': '1t4f0uvLHN-gszBOCpk_JOL-aOwqj5AziPXSY36LyMaK_b02VGAcyoNcGI8xYFempmOT3TztQGgqlgg7zbFE7TvKWQ2BLx8F41N0ErJmZlj1X_hNo93MEagOUQbkoq3QRC9RVjmnnjPHv4LA_kka_8wUPqYq1aFxpHptT2RX0-s',
'p': '9UDFpGn3kRGtAZUPtchOhFtKygv4oEfaDaHIb2OXX1I1Yg0nC3orBa7N8lyHz5W3XaehGtmcUpjTpFZMH2Kbaw',
'q': '4Eh99SLMOtLpYRpKD_Bv0QDFSknFJSGc7sG1dc8TOa3ielrSJD-oowHcEAvoqtY9sfx6l6RAfJ2yFQIr24JJgQ',
'qi': 'P93LJwlJ-k4BGjY78HqNgLFR7eOTYoEDgG4zLHjo43PACBEDmkGzDGgK7c5M-VMgdrckTNDHPBkp7vxO4cqddg'
};
var pub = {
'alg': 'RS256',
'e': 'AQAB',
'ext': true,
'key_ops': ['verify'],
'kty': 'RSA',
'n': '1t4f0uvLHN-gszBOCpk_JOL-aOwqj5AziPXSY36LyMaK_b02VGAcyoNcGI8xYFempmOT3TztQGgqlgg7zbFE7TvKWQ2BLx8F41N0ErJmZlj1X_hNo93MEagOUQbkoq3QRC9RVjmnnjPHv4LA_kka_8wUPqYq1aFxpHptT2RX0-s'
};
var nodePriv = jwk2pem(priv, {
private: true
});
var nodePub = jwk2pem(pub);
var data = new Buffer('fooooooo');
var nodeSig = crypto.createSign('RSA-SHA256').update(data).sign(nodePriv);
new Signature(priv).update(data).sign().then(function(sig) {
t.ok(crypto.createVerify('RSA-SHA256').update(data).verify(nodePub, sig), 'node verify');
t.equals(nodeSig.toString('hex'), sig.toString('hex'));
return new Signature(pub, nodeSig).update(data).verify();
}).then(function(res) {
t.ok(res, 'we verify');
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
function runedsa(i) {
test('run ' + i, function(t) {
var priv256 = {
'crv': 'P-256',
'd': 'EbZoCsc-k8QhV4s6YjomZyB1qtgdA6dnOjKqOqx8OEE',
'ext': true,
'key_ops': ['sign'],
'kty': 'EC',
'x': '1lw1cUhf1bDx7Ij_WpRU7ZvrhZJMJOFxn0xc5JJDrEg',
'y': 'HtJQX9tK8gllFtZjf-z7HRzLhosF9bgGS77L5pAcCsM'
};
var pub256 = {
'crv': 'P-256',
'ext': true,
'key_ops': ['verify'],
'kty': 'EC',
'x': '1lw1cUhf1bDx7Ij_WpRU7ZvrhZJMJOFxn0xc5JJDrEg',
'y': 'HtJQX9tK8gllFtZjf-z7HRzLhosF9bgGS77L5pAcCsM'
};
var priv384 = {
'crv': 'P-384',
'd': 'Il-D741GZgCA5CRRzC5XIJh5zLB9ofnlX0GqB4Vrnp1eHJOhWxRuyimAr6HD-oyd',
'ext': true,
'key_ops': ['sign'],
'kty': 'EC',
'x': '-sJ_JrOrbzO3k-qZSEwItkl8_Dxk9dQhFh-y4akAYZHMSb0AjGROcEUC9A6_7NOh',
'y': 'Hnms0caSuoofsHI86V1yw2hBzSYWSpGAaRe1ZcCgsFryQLjZvnVoKOa4Cg1X4GhN'
};
var pub384 = {
'crv': 'P-384',
'ext': true,
'key_ops': ['verify'],
'kty': 'EC',
'x': '-sJ_JrOrbzO3k-qZSEwItkl8_Dxk9dQhFh-y4akAYZHMSb0AjGROcEUC9A6_7NOh',
'y': 'Hnms0caSuoofsHI86V1yw2hBzSYWSpGAaRe1ZcCgsFryQLjZvnVoKOa4Cg1X4GhN'
};
var priv521 = {
'crv': 'P-521',
'd': 'Af8h6dGJGbIp4Nmet-ZpV1mDJB3l5hw58lBfSL9Q1yXwLlonOWrIwSZIy2Udm9I_Lx9zP4W7A-oHcQXAekKAhCUx',
'ext': true,
'key_ops': ['sign'],
'kty': 'EC',
'x': 'ARL4r-1H5vUinQSFVsEEfBunX_gwuyJ-Xk_nCCiP4ZTf2iaaSwJXzbPCObgr44eFHzHhzY0sxdnl3UmpDmwj0W1U',
'y': 'AJ2LoHDZSHNDz-1c8y1LeQbS8h20IzCL-8w-oxwUv2en-1JrvAwjdhfn4xBMSCbPm7V5UOx-4sG0EkCCo16DuAO8'
}
var pub521 = {
'crv': 'P-521',
'ext': true,
'key_ops': ['verify'],
'kty': 'EC',
'x': 'ARL4r-1H5vUinQSFVsEEfBunX_gwuyJ-Xk_nCCiP4ZTf2iaaSwJXzbPCObgr44eFHzHhzY0sxdnl3UmpDmwj0W1U',
'y': 'AJ2LoHDZSHNDz-1c8y1LeQbS8h20IzCL-8w-oxwUv2en-1JrvAwjdhfn4xBMSCbPm7V5UOx-4sG0EkCCo16DuAO8'
};
if (process.browser) {
runTestBrowser('p256', 'sha256', priv256, pub256);
runTestBrowser('p384', 'sha384', priv384, pub384);
runTestBrowser('p521', 'sha512', priv521, pub521);
} else {
runTestNode('p256', 'sha256', priv256, pub256);
runTestNode('p384', 'sha384', priv384, pub384);
runTestNode('p521', 'sha512', priv521, pub521);
}
var otherECNames = {
'p256': 'ES256',
'p384': 'ES384',
'p521': 'ES512'
}
function runTestNode(curve, hash, priv, pub) {
t.test('ecdsa ' + curve, function(t) {
var nodePriv = new Buffer(jwk2pem(priv, {
private: true
}));
var nodePub = new Buffer(jwk2pem(pub));
var data = new Buffer('fooooooo');
var npriv = new raw.Key(nodePriv);
var nodeSig = npriv.sign(crypto.createHash(hash).update(data).digest());
var roundtrip = toDer(new Buffer(fromDer(nodeSig, otherECNames[curve]), 'base64'), otherECNames[curve]);
t.equals(roundtrip.toString('hex'), nodeSig.toString('hex'), 'round trips');
new Signature(priv).update(data).sign().then(function(sig) {
var npub = new raw.Key(nodePub);
var h = crypto.createHash(hash).update(data).digest();
t.ok(npub.verify(toDer(sig, otherECNames[curve]), h), 'node verify');
return new Signature(pub, new Buffer(fromDer(nodeSig, otherECNames[curve]), 'base64')).update(data).verify();
}).then(function(res) {
t.ok(res, 'we verify');
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
}
function runTestBrowser(curve, hash, priv, pub) {
t.test('ecdsa ' + curve, function(t) {
var nodePriv = new Buffer(jwk2pem(priv, {
private: true
}));
let ec = new EC(curve);
var data = new Buffer('fooooooo');
var npriv = ec.keyFromPrivate(base64url.decode(priv.d));
var nodeSig = new Buffer(npriv.sign(crypto.createHash(hash).update(data).digest()).toDER());
var roundtrip = toDer(new Buffer(fromDer(nodeSig, otherECNames[curve]), 'base64'), otherECNames[curve]);
t.equals(roundtrip.toString('hex'), nodeSig.toString('hex'), 'round trips');
new Signature(priv).update(data).sign().then(function(sig) {
var h = crypto.createHash(hash).update(data).digest();
t.ok(ec.verify(h, toDer(sig, otherECNames[curve]), {
x: base64url.decode(pub.x).toString('hex'),
y: base64url.decode(pub.y).toString('hex')
}), 'node verify');
return new Signature(pub, new Buffer(fromDer(nodeSig, otherECNames[curve]), 'base64')).update(data).verify();
}).then(function(res) {
t.ok(res, 'we verify');
t.end();
}).catch(function(e) {
t.error(e);
t.end();
});
});
}
});
}
var len = 5;
var i = 0;
while (++i < len) {
runedsa(i);
}
test('pbkdf2', function(t) {
pbkdf2Fixtures.forEach(function(fixture, i) {
t.test('fixture: ' + i, function(t) {
var key, salt;
if (fixture.key) {
key = new Buffer(fixture.key, 'binary');
} else if (fixture.keyHex) {
key = new Buffer(fixture.keyHex, 'hex');
}
if (fixture.salt) {
salt = new Buffer(fixture.salt, 'binary');
} else if (fixture.saltHex) {
salt = new Buffer(fixture.saltHex, 'hex');
}
var length = fixture.dkLen;
var iterations = fixture.iterations;
Object.keys(fixture.results).forEach(function(algo) {
t.test(algo, function(t) {
t.plan(1);
var result = fixture.results[algo];
pbkdf2(key, salt, iterations, length, algo).then(function(res) {
t.equals(res.toString('hex'), result);
}, function(err) {
t.notOk(err || true);
});
});
});
});
t.test('fixture text: ' + i, function(t) {
var key, salt;
if (fixture.key) {
key = fixture.key;
} else if (fixture.keyHex) {
key = new Buffer(fixture.keyHex, 'hex');
}
if (fixture.salt) {
salt = new Buffer(fixture.salt, 'binary');
} else if (fixture.saltHex) {
salt = new Buffer(fixture.saltHex, 'hex');
}
var length = fixture.dkLen;
var iterations = fixture.iterations;
Object.keys(fixture.results).forEach(function(algo) {
t.test(algo, function(t) {
t.plan(1);
var result = fixture.results[algo];
pbkdf2(key, salt, iterations, length, algo).then(function(res) {
t.equals(res.toString('hex'), result);
}, function(err) {
t.error(err || true, 'should be no error');
});
});
});
});
});
});
test('rsa', function(t) {
var jwkKeys = {
'private': {
'alg': 'RSA-OAEP',
'd': 'cy41l5g8Wjyxzy_O-JrwHUtfgLkYOu4FsqR6TnhnUcHghhp4kDHs-xiUzMCKxCKUzs4m4B6vTeBGfVsggug2f4L1I4PidXw7Bl_to8A2Oggu9GHeWCiQvIIXFVZKo930QmYGBnvSO50yYFRXHl403a-bYtB36j1OylajJ4wWI1SquaqoYq26rggJY3F4Pv4MzO0CNmchxKnYRMM94fkI-TUkOwvh39AhE1zrCzEC_D_O2OTyZA0qDSYt5ylwSp3NsSA_IXaP86gfHikrySCW7Xs56UVagNhdEOI8738R0TWfR3RU9yFsgx62GpuLg3XOSqBCKpvlib6IfJN1cnX74Q',
'dp': 'pofEZI0RbklsReHuhT4N1xpoWtKzL2y_kGl-k-IVP6DvL1les9hiQomZBQkDpU89FeT3r_K8J_XJFgbRr_4ZOVEtybK78FFRrii2Vl2jrXhOalBppxv_OEuJFTXwqbDRrIc3PVrYJABLKa3k0eNwMsypCcBFhQJ_aPnrmGTlwRk',
'dq': 'OXNF5cF02vGDFludOO8mh70QINyTg_gIxpEHObTsNotQ_BMFRzllgFAV5cFOXwjLF-pgTnWUug9IaW7Fx3E-T-1hP7X2ZlIo_DdqosNnlogJgf1LeG2rPdec05eNLPYim54XPf9HIHGegEEYwNc1tI6TerMrYRobfX_ANAH3l00',
'e': 'AQAB',
'ext': true,
'key_ops': [
'decrypt'
],
'kty': 'RSA',
'n': 't3H10hxU2VQn3YTrPftwI-f3RBIlsFG6EHmep7SiSicfCbSHBMZXlvgqr7XKFyn-TdJoi1d_uG143wfuazjnCECUwQpodsstnLBvm6ZNHuQqdSbuYA_7WmghJLub8zzTjRHQb2XEIpyaMrIP-w5cNhpyeXOAuqvZ2UzMeF1nj_D2k05dkpUDQFrnnayxUhFYVy2Ee4az5ep7sYsTGUOFV8xIGPyxhpKZX-Qqmawo_S27RmYvqVhEH8eVicLU2E3McFSkXGvr2e4PB9bge7uauus1MV2x27JCmrAlQHRXYCETm7aBwZ2fs6AIJD2Btky-bbg7RLKZS9VyN_K6nABobQ',
'p': '6i3AI_Q75E8ihP86Stm0FR3JEOhdIOh1AbbRK0LDJxsAn0ycW9DFoZjUG-_8eayYzHtQoBm6ytgcO5v13UtSIhpDMeJyILZKL2rhJuJ26wASHoYEclnNCa1h3IVhMjVa8Y2JQYxQskhFnidrmcQmJ86KVliCf4OTUvqJojc3V3k',
'q': 'yIn6llUXeLeOjcK3yDXFDcCYP8SAV2wmiofNpEq_L1I6KMTo-TuG_2h2NvBVvhOG--p1oNM5hZVvH2QAyzR6darL3EPglqYYDrs_4hXEOP1lbqkDlQ-3gkSs_fI9LJ5TVa3foZ_jMuIHeGgTxSZx56bFpTLfs7fL2pMlgcubt5U',
'qi': 'mLUv01qhMus7cnkwAqAZ8oQDMhvliZ9CmFjHtQUUnHCJDH3Y_oHAEZcYSYD97Cc_qzCszidxUpWZ46aq9AuC_ivIax7Pp-4q2S2XZZ3TFsB5kDCLkOJJvcw6AyYE5Fg_iFQuyC4rssvQij4ANshLQZEyqcQpkFpDixLf2LAykn8'
},
'public': {
'alg': 'RSA-OAEP',
'e': 'AQAB',
'ext': true,
'key_ops': [
'encrypt'
],
'kty': 'RSA',
'n': 't3H10hxU2VQn3YTrPftwI-f3RBIlsFG6EHmep7SiSicfCbSHBMZXlvgqr7XKFyn-TdJoi1d_uG143wfuazjnCECUwQpodsstnLBvm6ZNHuQqdSbuYA_7WmghJLub8zzTjRHQb2XEIpyaMrIP-w5cNhpyeXOAuqvZ2UzMeF1nj_D2k05dkpUDQFrnnayxUhFYVy2Ee4az5ep7sYsTGUOFV8xIGPyxhpKZX-Qqmawo_S27RmYvqVhEH8eVicLU2E3McFSkXGvr2e4PB9bge7uauus1MV2x27JCmrAlQHRXYCETm7aBwZ2fs6AIJD2Btky-bbg7RLKZS9VyN_K6nABobQ'
}
};
var pemKeys = {
public: jwk2pem(jwkKeys.public),
private: jwk2pem(jwkKeys.private, {
private: true
})
};
t.test('we encrypt, we decrypt', function (t) {
t.plan(1);
var data = new Buffer('we encrypt, we decrypt');
rsa.encrypt(jwkKeys.public, data).then(resp=>{
return rsa.decrypt(jwkKeys.private, resp);
}).then(function (decrypt) {
t.equals(decrypt.toString(), 'we encrypt, we decrypt');
}).catch(function (e) {
t.error(e||'error');
});
});
t.test('we encrypt, node decrypts', function (t) {
t.plan(1);
var data = new Buffer('we encrypt, node decrypts');
rsa.encrypt(jwkKeys.public, data).then(resp=>{
var decrypt = crypto.privateDecrypt(pemKeys.private, resp);
t.equals(decrypt.toString(), 'we encrypt, node decrypts');
}).catch(function (e) {
t.error(e||'error');
});
});
t.test('node encrypt, we decrypt', function (t) {
t.plan(1);
var data = new Buffer('node encrypt, we decrypt');
var encrypted = crypto.publicEncrypt(pemKeys.public, data);
rsa.decrypt(jwkKeys.private, encrypted).then(function (decrypt) {
t.equals(decrypt.toString(), 'node encrypt, we decrypt');
}).catch(function (e) {
t.error(e||'error');
});
});
});