From fe202607ebb5d2d44ccfb9db208fbd664f3736ca Mon Sep 17 00:00:00 2001 From: Friedel Ziegelmayer Date: Fri, 20 May 2016 12:50:16 +0200 Subject: [PATCH] first pass at stretchKey --- README.md | 8 +++- src/ephemeral-keys.js | 7 ++- src/key-stretcher.js | 92 ++++++++++++++++++++++++++++++++++++- test/ephemeral-keys.spec.js | 8 +++- test/fixtures/go-rsa.key | 0 test/key-stretcher.spec.js | 25 ++++++++++ 6 files changed, 135 insertions(+), 5 deletions(-) create mode 100644 test/fixtures/go-rsa.key create mode 100644 test/key-stretcher.spec.js diff --git a/README.md b/README.md index 49dafd0..78304aa 100644 --- a/README.md +++ b/README.md @@ -34,7 +34,13 @@ Generates an ephemeral public key and returns a function that will compute the s Focuses only on ECDH now, but can be made more general in the future. -Returns a `Buffer`. +Returns an object of the form +```js +{ + key: Buffer, + genSharedKey: Function +} +``` ### `marshalPublicKey(key[, type])` diff --git a/src/ephemeral-keys.js b/src/ephemeral-keys.js index 12ce31c..ff2f5c2 100644 --- a/src/ephemeral-keys.js +++ b/src/ephemeral-keys.js @@ -22,9 +22,14 @@ module.exports = (curveName) => { const priv = ec.genKeyPair() - return (theirPub) => { + const genSharedKey = (theirPub) => { const pub = ec.keyFromPublic(theirPub, 'hex') return priv.derive(pub.getPublic()).toBuffer('le') } + + return { + key: priv.getPublic(), + genSharedKey + } } diff --git a/src/key-stretcher.js b/src/key-stretcher.js index 0c05312..9751253 100644 --- a/src/key-stretcher.js +++ b/src/key-stretcher.js @@ -1,7 +1,97 @@ 'use strict' +const forge = require('node-forge') +const createBuffer = forge.util.createBuffer + +const cipherMap = { + 'AES-128': { + ivSize: 16, + keySize: 16 + }, + 'AES-256': { + ivSize: 16, + keySize: 32 + }, + 'Blowfish': { + ivSize: 8, + cipherKeySize: 32 + } +} + +const hashMap = { + 'SHA1': 'sha1', + 'SHA256': 'sha256', + 'SHA512': 'sha512' +} + // Generates a set of keys for each party by stretching the shared key. // (myIV, theirIV, myCipherKey, theirCipherKey, myMACKey, theirMACKey) module.exports = (cipherType, hashType, secret) => { - throw new Error('Not implemented') + const cipher = cipherMap[cipherType] + const hash = hashMap[hashType] + + if (!cipher) { + throw new Error('unkown cipherType passed') + } + + if (!hash) { + throw new Error('unkown hashType passed') + } + + if (Buffer.isBuffer(secret)) { + secret = createBuffer(secret.toString('binary')) + } + + const cipherKeySize = cipher.keySize + const ivSize = cipher.ivSize + const hmacKeySize = 20 + const seed = 'key expansion' + const resultLength = 2 * (ivSize + cipherKeySize + hmacKeySize) + + const m = forge.hmac.create() + m.start(hash, secret) + m.update(seed) + + let a = m.digest().bytes() + const result = createBuffer() + + let j = 0 + for (; j < resultLength;) { + m.start(hash, secret) + m.update(a) + m.update(seed) + + const b = createBuffer(m.digest(), 'raw') + let todo = b.length() + + if (j + todo > resultLength) { + todo = resultLength - j + } + + result.putBytes(b.getBytes(todo)) + + j += todo + + m.start(hash, secret) + m.update(a) + a = m.digest().bytes() + } + + const half = resultLength / 2 + const r1 = createBuffer(result.getBytes(half)) + const r2 = createBuffer(result.getBytes()) + + const k1 = { + IV: r1.getBytes(ivSize), + CipherKey: r1.getBytes(cipherKeySize), + MacKey: r1.getBytes() + } + + const k2 = { + IV: r2.getBytes(ivSize), + CipherKey: r2.getBytes(cipherKeySize), + MacKey: r2.getBytes() + } + + return {k1, k2} } diff --git a/test/ephemeral-keys.spec.js b/test/ephemeral-keys.spec.js index f6a4ea9..30cec4e 100644 --- a/test/ephemeral-keys.spec.js +++ b/test/ephemeral-keys.spec.js @@ -7,11 +7,15 @@ const crypto = require('../src') describe('generateEphemeralKeyPair', () => { it('returns a function that generates a shared secret', () => { - const maker = crypto.generateEphemeralKeyPair('P-256') + const res = crypto.generateEphemeralKeyPair('P-256') const ourPublic = '044374add0df35706db7dade25f3959fc051d2ef5166f8a6a0aa632d0ab41cdb4d30e1a064e121ac56155235a6b8d4c5d8fe35e019f507f4e2ff1445e229d7af43' expect( - maker(ourPublic) + res.genSharedKey(ourPublic) ).to.have.length(32) + + expect( + res.key + ).to.exist }) }) diff --git a/test/fixtures/go-rsa.key b/test/fixtures/go-rsa.key new file mode 100644 index 0000000..e69de29 diff --git a/test/key-stretcher.spec.js b/test/key-stretcher.spec.js new file mode 100644 index 0000000..a8f590e --- /dev/null +++ b/test/key-stretcher.spec.js @@ -0,0 +1,25 @@ +/* eslint-env mocha */ +'use strict' + +const expect = require('chai').expect + +const crypto = require('../src') + +describe('keyStretcher', () => { + describe('generate', () => { + const ciphers = ['AES-128', 'AES-256', 'Blowfish'] + const hashes = ['SHA1', 'SHA256'/*, 'SHA512'*/] + const res = crypto.generateEphemeralKeyPair('P-256') + const secret = res.genSharedKey(res.key) + + ciphers.forEach((cipher) => { + hashes.forEach((hash) => { + it(`${cipher} - ${hash}`, () => { + const keys = crypto.keyStretcher(cipher, hash, secret) + expect(keys.k1).to.exist + expect(keys.k2).to.exist + }) + }) + }) + }) +})