This commit is contained in:
Danny Coates 2014-01-07 13:48:59 -08:00
Родитель abf68d70ec
Коммит 0879b14770
4 изменённых файлов: 16 добавлений и 301 удалений

Просмотреть файл

@ -5,7 +5,7 @@
var crypto = require('crypto')
var P = require('p-promise')
var ClientApi = require('./api')
var keyStretch = require('../crypto/keystretch')
var butil = require('../crypto/butil')
var pbkdf2 = require('../crypto/pbkdf2')
var hkdf = require('../crypto/hkdf')
var tokens = require('../tokens')({ trace: function () {}})
@ -34,7 +34,7 @@ Client.Api = ClientApi
Client.prototype.setupCredentials = function (email, password) {
this.email = email
return pbkdf2.derive(Buffer(password), keyStretch.KWE('quickStretch', email), 1000, 32)
return pbkdf2.derive(Buffer(password), hkdf.KWE('quickStretch', email), 1000, 32)
.then(
function (stretch) {
return hkdf(stretch, 'authPW', null, 32)
@ -218,7 +218,7 @@ Client.prototype.changePassword = function (newPassword) {
)
.then(
function () {
this.wrapKb = keyStretch.xor(this.kB, this.unwrapBKey)
this.wrapKb = butil.xorBuffers(this.kB, this.unwrapBKey)
return this.api.passwordChangeFinish(this.passwordChangeToken, this.authPW, this.wrapKb)
}.bind(this)
)
@ -251,7 +251,7 @@ Client.prototype.keys = function () {
this.keyFetchToken = null
this.kA = keys.kA
this.wrapKb = keys.wrapKb
this.kB = keys.kB = keyStretch.xor(this.wrapKb, this.unwrapBKey)
this.kB = keys.kB = butil.xorBuffers(this.wrapKb, this.unwrapBKey)
return keys
}.bind(this),
function (err) {

Просмотреть файл

@ -5,15 +5,21 @@
var HKDF = require('hkdf')
var P = require('p-promise')
function kw(name) {
return 'identity.mozilla.com/picl/v1/' + name
const NAMESPACE = 'identity.mozilla.com/picl/v1/'
function KWE(name, email) {
return Buffer(NAMESPACE + name + ':' + email)
}
function KW(name) {
return Buffer(NAMESPACE + name)
}
function hkdf(km, info, salt, len) {
var d = P.defer()
var df = new HKDF('sha256', salt, km)
df.derive(
kw(info),
KW(info),
len,
function(key) {
d.resolve(key)
@ -22,4 +28,7 @@ function hkdf(km, info, salt, len) {
return d.promise
}
hkdf.KW = KW
hkdf.KWE = KWE
module.exports = hkdf

Просмотреть файл

@ -1,128 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var P = require('p-promise')
var pbkdf2 = require('./pbkdf2')
var scrypt = require('./scrypt')
var hkdf = require('./hkdf')
// The namespace for the salt functions
const NAMESPACE = 'identity.mozilla.com/picl/v1/'
const SCRYPT_HELPER = 'https://scrypt-accounts.dev.lcip.org/'
const ITERATIONS = 20000
const LENGTH = 8 * 32
/** Derive a key from an email and password pair
*
* @param {Buffer} email The email hex buffer of the user
* @param {Buffer} password The password of the user
* @param {String} saltHex The salt to derive hkdf as a hex string
* @return p.promise object - It will resolve with
* {Buffer} srpPw srp password
* {Buffer} unwrapBKey unwrapBKey
* or fail with {object} err
*/
function derive(email, password, saltHex) {
var p = P.defer()
if (!password || !email || !saltHex) {
p.reject('Bad password, salt or email input')
return p.promise
}
var salt = Buffer(saltHex, 'hex')
// derive the first key from pbkdf2
pbkdf2
.derive(password, KWE('first-PBKDF', email), ITERATIONS, LENGTH)
.then(
function(K1) {
// request a hash from scrypt based on the first key
return scrypt.hash(K1, KW("scrypt"), module.exports.SCRYPT_HELPER)
}
)
.then(
function (K2) {
// combine the K2 hex string and a password UTF8 into a bit array
var scryptPassword = Buffer.concat([
Buffer(K2, 'hex'),
password
])
// derive the second key from pbkdf2
return pbkdf2.derive(scryptPassword, KWE('second-PBKDF', email), ITERATIONS, LENGTH)
}
)
.then(
function (stretchedPw) {
var input = new Buffer (stretchedPw, 'hex')
var lengthHkdf = 2 * 32
return hkdf(input, 'mainKDF', salt, lengthHkdf)
}
)
.done(
function (hkdfResult) {
var hkdfResultHex = hkdfResult.toString('hex')
var srpPw = Buffer(hkdfResultHex.substring(0,64), 'hex')
var unwrapBKey = Buffer(hkdfResultHex.substring(64,128), 'hex')
p.resolve({ srpPw: srpPw, unwrapBKey: unwrapBKey })
},
function (err) {
p.reject(err)
}
)
return p.promise
}
/** XOR
*
* @param {Buffer|String} input1 first value of the buffer as a hex string or a buffer
* @param {Buffer|String} input2 second value of the buffer as hex string or a buffer
* @return {Buffer} xorResult Result XOR buffer
*/
function xor(input1, input2) {
var buf1 = Buffer.isBuffer(input1) ? input1 : Buffer(input1, 'hex')
var buf2 = Buffer.isBuffer(input2) ? input2 : Buffer(input2, 'hex')
var xorResult = Buffer(buf1.length)
if (buf1.length !== buf2.length) {
throw new Error(
'XOR buffers must be same length %d != %d',
buf1.length,
buf2.length
)
}
for (var i = 0; i < xorResult.length; i++) {
xorResult[i] = buf2[i] ^ buf1[i]
}
return xorResult
}
/** KWE
*
* @param {String} name The name of the salt
* @param {Buffer} email The email of the user.
* @return {Buffer} the salt combination with the namespace
*/
function KWE(name, email) {
return Buffer(NAMESPACE + name + ':' + email)
}
/** KW
*
* @param {String} name The name of the salt
* @return {Buffer} the salt combination with the namespace
*/
function KW(name) {
return Buffer(NAMESPACE + name)
}
module.exports.SCRYPT_HELPER = SCRYPT_HELPER
module.exports.derive = derive
module.exports.xor = xor
module.exports.KWE = KWE
module.exports.KW = KW

Просмотреть файл

@ -1,166 +0,0 @@
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this
* file, You can obtain one at http://mozilla.org/MPL/2.0/. */
var test = require('../ptaptest')
var keyStretch = require('../../crypto/keystretch')
test(
'basic key stretching, test vectors',
function (t) {
var emailBuf = Buffer('andré@example.org')
var password = Buffer('pässwörd')
var salt = '00f000000000000000000000000000000000000000000000000000000000034d'
return keyStretch.derive(emailBuf, password, salt)
.then(
function (result) {
t.equal(result.srpPw.toString('hex'), '00f9b71800ab5337d51177d8fbc682a3653fa6dae5b87628eeec43a18af59a9d')
t.equal(result.unwrapBKey.toString('hex'), '6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a')
}
)
}
)
test(
'basic key stretching, longer credentials',
function (t) {
var salt = '00f000000000000000000000000000000000000000000000000000000000034d'
var email = 'ijqmkkafer3xsj5rzoq+msnxsacvkmqxabtsvxvj@some-test-domain-with-a-long-name-example.org'
var emailBuf = Buffer(email)
var password = Buffer('mSnxsacVkMQxAbtSVxVjCCoWArNUsFhiJqmkkafER3XSJ5rzoQ')
return keyStretch.derive(emailBuf, password, salt)
.then(
function (result) {
t.equal(result.srpPw.toString('hex'), '261559a74f7b7199fef846c8138db08333bbcc7f5177194da5c965ba953a346b')
t.equal(result.unwrapBKey.toString('hex'), 'cf48fbc1613a46c794d37c2fe5423c7813b70e5b6c525d5c4463056f267959ff')
}
)
}
)
test(
'false input both',
function (t) {
return keyStretch.derive('', '', '')
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'false input email',
function (t) {
var email = 'me@example.org'
return keyStretch.derive(email, '', '')
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'false input password',
function (t) {
var email = ''
var password = 'password'
var salt = ''
return keyStretch.derive(email, password, salt)
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'undefined input',
function (t) {
var email
var password
var salt
return keyStretch.derive(email, password, salt)
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'not enough arguments',
function (t) {
return keyStretch.derive()
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'one argument',
function (t) {
return keyStretch.derive(Buffer('andré@example.org'))
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'null input',
function (t) {
return keyStretch.derive(null, null, null)
.then(
function (stretchedPassword) {
t.fail('Got a stretchedPassword from false input')
},
function (err) {
t.equal(err, 'Bad password, salt or email input')
}
)
}
)
test(
'wrapkB xor string and buffer test',
function (t) {
var wrapkB = '404142434445464748494a4b4c4d4e4f505152535455565758595a5b5c5d5e5f'
var unwrapBKey = '6ea660be9c89ec355397f89afb282ea0bf21095760c8c5009bbcc894155bbe2a'
var kBgood = '2ee722fdd8ccaa721bdeb2d1b76560efef705b04349d9357c3e592cf4906e075'
var kBResult = keyStretch.xor(wrapkB, unwrapBKey)
var kBResultBuffer = keyStretch.xor(Buffer(wrapkB, 'hex'), Buffer(unwrapBKey, 'hex'))
t.equal(kBResult.toString('hex'), kBgood)
t.equal(kBResultBuffer.toString('hex'), kBgood)
t.end()
}
)