Просмотр исходного кода

Keep oldmethods and use old method when app uses an older label

Simon Detheridge 6 лет назад
Родитель
Сommit
a9e39af24f

+ 43 - 17
libraries/access-token-encryptor/lib/coffee/AccessTokenEncryptor.coffee

@@ -3,33 +3,46 @@ async = require('async')
 
 ALGORITHM = 'aes-256-ctr'
 
-keyFn = (password, salt, keyLength, callback)->
-	return crypto.pbkdf2(password, salt, 10000, keyLength, 'sha1', callback)
+keyFn = (password, salt, callback)->
+	return crypto.pbkdf2(password, salt, 10000, 64, 'sha1', callback)
+
+keyFn32 = (password, salt, keyLength, callback)->
+	return crypto.pbkdf2(password, salt, 10000, 32, 'sha1', callback)
 
 class AccessTokenEncryptor
 
 	constructor: (settings) ->
 
 		@settings = settings
-		@cipherLabel = "2019.1"
+		@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) ->
+		unless ["2015.1", "2016.1"].includes(@cipherLabel)
+			return @encryptJsonV2(json, callback)
+
 		string = JSON.stringify(json)
-		async.parallel [
-			(cb) -> crypto.randomBytes(16, cb)
-			(cb) -> crypto.randomBytes(16, cb)
-		], (err, results) =>
+		salt = crypto.randomBytes(16)
+		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)
 
-			salt = results[0]
-			iv = results[1]
+	encryptJsonV2: (json, callback) ->
+		string = JSON.stringify(json)
+		crypto.randomBytes 32, (err, bytes) =>
+			return callback(err) if err
+			salt = bytes.slice(0, 16)
+			iv = bytes.slice(16, 32)
 
-			keyFn @cipherPassword, salt, 32, (err, key) =>
+			keyFn32 @cipherPassword, salt, 32, (err, key) =>
 				if err?
 					logger.err err:err, "error getting Fn key"
 					return callback(err)
@@ -41,21 +54,34 @@ class AccessTokenEncryptor
 
 	decryptToJson: (encryptedJson, callback) ->
 		[label, salt, cipherText, iv] = encryptedJson.split(':', 4)
+		if iv and iv.length > 0
+			return @decryptToJsonV2(encryptedJson, callback)
 
 		password = @settings.cipherPasswords[label]
 		return callback(new Error("invalid password")) if not password? or password.length < 16
-
-		keyLength = if label == "2019.1" then 32 else 64
-		keyFn password, Buffer.from(salt, 'hex'), keyLength, (err, key) =>
+		keyFn password, Buffer.from(salt, 'hex'), (err, key) =>
 			if err?
 				logger.err err:err, "error getting Fn key"
 				return callback(err)
+			decipher = crypto.createDecipher(ALGORITHM, key)
+			dec = decipher.update(cipherText, 'base64', 'utf8') + decipher.final('utf8')
+			try
+				json = JSON.parse(dec)
+			catch e
+				return callback(new Error("error decrypting token"))
+			callback(null, json)
 
-			decipher = if label == "2019.1"
-				crypto.createDecipheriv(ALGORITHM, key, Buffer.from(iv, 'hex'))
-			else
-				crypto.createDecipher(ALGORITHM, key)
+	decryptToJsonV2: (encryptedJson, callback) ->
+		[label, salt, cipherText, iv] = encryptedJson.split(':', 4)
+		password = @settings.cipherPasswords[label]
+		return callback(new Error("invalid password")) if not password? or password.length < 16
+
+		keyFn32 password, Buffer.from(salt, 'hex'), 32, (err, key) =>
+			if err?
+				logger.err err:err, "error getting Fn key"
+				return callback(err)
 
+			decipher = crypto.createDecipheriv(ALGORITHM, key, Buffer.from(iv, 'hex'))
 			dec = decipher.update(cipherText, 'base64', 'utf8') + decipher.final('utf8')
 			try
 				json = JSON.parse(dec)

+ 11 - 3
libraries/access-token-encryptor/test/unit/coffee/AccessTokenEncryptorTests.coffee

@@ -21,14 +21,14 @@ describe 'AccessTokenEncryptor', ->
 				"2016.1": "11111111111111111111111111111111111111"
 				"2015.1": "22222222222222222222222222222222222222"
 				"2019.1": "33333333333333333333333333333333333333"
-		AccessTokenEncryptor = SandboxedModule.require modulePath
-		@encryptor = new AccessTokenEncryptor(@settings)
+		@AccessTokenEncryptor = SandboxedModule.require modulePath
+		@encryptor = new @AccessTokenEncryptor(@settings)
 
 	describe "encrypt", ->
 		it 'should encrypt the object', (done)->
 			@encryptor.encryptJson @testObject, (err, encrypted)->
 				expect(err).to.be.null
-				encrypted.should.match(/^2019.1:[0-9a-f]+:[a-zA-Z0-9=+\/]+:[0-9a-f]+$/)
+				encrypted.should.match(/^2019.1:[0-9a-f]{32}:[a-zA-Z0-9=+\/]+:[0-9a-f]{32}$/)
 				done()
 
 		it 'should encrypt the object differently the next time', (done)->
@@ -37,6 +37,14 @@ describe 'AccessTokenEncryptor', ->
 					encrypted1.should.not.equal(encrypted2)
 					done()
 
+		it 'should encrypt the object in v1 format for an old label', (done)->
+			@settings.cipherLabel = "2016.1"
+			@encryptor = new @AccessTokenEncryptor(@settings)
+			@encryptor.encryptJson @testObject, (err, encrypted)->
+				expect(err).to.be.null
+				encrypted.should.match(/^2016.1:[0-9a-f]{32}:[a-zA-Z0-9=+\/]+$/)
+				done()
+
 	describe "decrypt", ->
 		it 'should decrypt the string to get the same object', (done)->
 			@encryptor.encryptJson @testObject, (err, encrypted) =>