diff --git a/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee b/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee index be7d4eb156..c40d853875 100644 --- a/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee +++ b/libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee @@ -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 diff --git a/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee b/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee index a206840084..1463a0af22 100644 --- a/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee +++ b/libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee @@ -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()