change to be a class, with settings passed in

This commit is contained in:
Shane Kilkelly 2016-03-25 14:33:38 +00:00
parent 7786ef9ed8
commit e6e3fb6948
2 changed files with 35 additions and 32 deletions

View file

@ -1,37 +1,38 @@
crypto = require('crypto')
settings = require('settings-sharelatex')
cipherLabel = settings.cipherLabel
throw Error("cipherLabel must not contain a colon (:)") if cipherLabel?.match(/:/)
cipherPassword = settings.cipherPasswords[settings.cipherLabel]
throw Error("cipherPassword not set") if not cipherPassword?
throw Error("cipherPassword too short") if cipherPassword.length < 16
ALGORITHM = 'aes-256-ctr'
keyFn = (password, salt, callback)->
return crypto.pbkdf2(password, salt, 10000, 64, callback)
module.exports =
class AccessTokenEncryptor
encryptJson: (json, callback)->
constructor: (settings) ->
@settings = settings
@cipherLabel = @settings.cipherLabel
throw Error("cipherLabel must not contain a colon (:)") if @cipherLabel?.match(/:/)
@cipherPassword = @settings.cipherPasswords[@cipherLabel]
throw Error("cipherPassword not set") if not @cipherPassword?
throw Error("cipherPassword too short") if @cipherPassword.length < 16
encryptJson: (json, callback) ->
string = JSON.stringify(json)
salt = crypto.randomBytes(16)
keyFn cipherPassword, salt, (err, key)->
keyFn @cipherPassword, salt, (err, key) =>
if err?
logger.err err:err, "error getting Fn key"
return callback(err)
cipher = crypto.createCipher(ALGORITHM, key)
crypted = cipher.update(string, 'utf8', 'base64') + cipher.final('base64')
callback(null, cipherLabel + ":" + salt.toString('hex') + ":" + crypted)
callback(null, @cipherLabel + ":" + salt.toString('hex') + ":" + crypted)
decryptToJson: (encryptedJson, callback)->
decryptToJson: (encryptedJson, callback) ->
[label, salt, cipherText] = encryptedJson.split(':', 3)
password = settings.cipherPasswords[label]
password = @settings.cipherPasswords[label]
return callback(new Error("invalid password")) if not password? or password.length < 16
keyFn password, new Buffer(salt, 'hex'), (err, key)->
keyFn password, new Buffer(salt, 'hex'), (err, key) =>
if err?
logger.err err:err, "error getting Fn key"
return callback(err)
@ -42,3 +43,5 @@ module.exports =
catch e
return callback(new Error("error decrypting token"))
callback(null, json)
module.exports = AccessTokenEncryptor

View file

@ -15,56 +15,56 @@ describe 'AccessTokenEncryptor', ->
@badLabel = "xxxxxx:c7a39310056b694c:jQf+Uh5Den3JREtvc82GW5Q="
@badKey = "2015.1:d7a39310056b694c:jQf+Uh5Den3JREtvc82GW5Q="
@badCipherText = "2015.1:c7a39310056b694c:xQf+Uh5Den3JREtvc82GW5Q="
@requires = requires:
"settings-sharelatex":
cipherLabel: "2016.1"
cipherPasswords:
"2016.1": "11111111111111111111111111111111111111"
"2015.1": "22222222222222222222222222222222222222"
@AccessTokenEncryptor = SandboxedModule.require modulePath, @requires
@settings =
cipherLabel: "2016.1"
cipherPasswords:
"2016.1": "11111111111111111111111111111111111111"
"2015.1": "22222222222222222222222222222222222222"
AccessTokenEncryptor = SandboxedModule.require modulePath, @requires
@encryptor = new AccessTokenEncryptor(@settings)
describe "encrypt", ->
it 'should encrypt the object', (done)->
@AccessTokenEncryptor.encryptJson @testObject, (err, encrypted)->
@encryptor.encryptJson @testObject, (err, encrypted)->
expect(err).to.be.null
encrypted.should.match(/^2016.1:[0-9a-f]+:[a-zA-Z0-9=+\/]+$/)
done()
it 'should encrypt the object differently the next time', (done)->
@AccessTokenEncryptor.encryptJson @testObject, (err, encrypted1)=>
@AccessTokenEncryptor.encryptJson @testObject, (err, encrypted2)=>
@encryptor.encryptJson @testObject, (err, encrypted1)=>
@encryptor.encryptJson @testObject, (err, encrypted2)=>
encrypted1.should.not.equal(encrypted2)
done()
describe "decrypt", ->
it 'should decrypt the string to get the same object', (done)->
@AccessTokenEncryptor.encryptJson @testObject, (err, encrypted) =>
@encryptor.encryptJson @testObject, (err, encrypted) =>
expect(err).to.be.null
@AccessTokenEncryptor.decryptToJson encrypted, (err, decrypted) =>
@encryptor.decryptToJson encrypted, (err, decrypted) =>
expect(err).to.be.null
expect(decrypted).to.deep.equal @testObject
done()
it 'should decrypt an old string to get the same object', (done)->
@AccessTokenEncryptor.decryptToJson @oldEncrypted, (err, decrypted)=>
@encryptor.decryptToJson @oldEncrypted, (err, decrypted)=>
expect(err).to.be.null
expect(decrypted).to.deep.equal @testObject
done()
it 'should return an error when decrypting an invalid label', (done)->
@AccessTokenEncryptor.decryptToJson @badLabel, (err, decrypted)->
@encryptor.decryptToJson @badLabel, (err, decrypted)->
expect(err).to.be.instanceof(Error)
expect(decrypted).to.be.undefined
done()
it 'should return an error when decrypting an invalid key', (done)->
@AccessTokenEncryptor.decryptToJson @badKey, (err, decrypted)->
@encryptor.decryptToJson @badKey, (err, decrypted)->
expect(err).to.be.instanceof(Error)
expect(decrypted).to.be.undefined
done()
it 'should return an error when decrypting an invalid ciphertext',(done)->
@AccessTokenEncryptor.decryptToJson @badCipherText, (err, decrypted)->
@encryptor.decryptToJson @badCipherText, (err, decrypted)->
expect(err).to.be.instanceof(Error)
expect(decrypted).to.be.undefined
done()