1 /*
2  *  Make.org Core API
3  *  Copyright (C) 2018 Make.org
4  *
5  * This program is free software: you can redistribute it and/or modify
6  *  it under the terms of the GNU Affero General Public License as
7  *  published by the Free Software Foundation, either version 3 of the
8  *  License, or (at your option) any later version.
9  *
10  *  This program is distributed in the hope that it will be useful,
11  *  but WITHOUT ANY WARRANTY; without even the implied warranty of
12  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
13  *  GNU Affero General Public License for more details.
14  *
15  *  You should have received a copy of the GNU Affero General Public License
16  *  along with this program.  If not, see <https://www.gnu.org/licenses/>.
17  *
18  */
19 
20 package org.make.api.technical.security
21 
22 import grizzled.slf4j.Logging
23 import org.make.api.technical.Base64Encoding
24 
25 import java.nio.ByteBuffer
26 import java.nio.charset.StandardCharsets
27 import java.security.SecureRandom
28 import javax.crypto.spec.{GCMParameterSpec, SecretKeySpec}
29 import javax.crypto.Cipher
30 import scala.util.{Failure, Success, Try}
31 
32 trait AESEncryptionComponent {
33   def aesEncryption: AESEncryption
34 }
35 
36 trait AESEncryption {
37   def encryptToken(token: Array[Byte]): Array[Byte]
38   def decryptToken(buffer: Array[Byte]): Array[Byte]
39   def encryptAndEncode(token: String): String
40   def decodeAndDecrypt(token: String): Try[String]
41 }
42 
43 trait DefaultAESEncryptionComponent extends AESEncryptionComponent {
44   this: SecurityConfigurationComponent with Logging =>
45 
46   override lazy val aesEncryption: DefaultAESEncryption = new DefaultAESEncryption
47   val random = new SecureRandom()
48 
49   class DefaultAESEncryption extends AESEncryption {
50     private val EncryptionAlgorithm: String = "AES/GCM/NoPadding"
51     private val TagLengthBit: Int = 128
52     private val IvSize = 16
53 
54     @SuppressWarnings(Array("org.wartremover.warts.Throw"))
55     val secretKey: SecretKeySpec = {
56       Base64Encoding.decode(securityConfiguration.aesSecretKey) match {
57         case Success(key) => new SecretKeySpec(key, "AES")
58         case Failure(exception) =>
59           logger.error("Cannot decode aes secret key. Key might contain an invalid Base64 format.")
60           throw exception
61       }
62     }
63 
64     override def encryptToken(token: Array[Byte]): Array[Byte] = {
65       val cipher: Cipher = Cipher.getInstance(EncryptionAlgorithm)
66       val iv = newIV()
67       cipher.init(Cipher.ENCRYPT_MODE, secretKey, new GCMParameterSpec(TagLengthBit, iv))
68       val encryptedText = cipher.doFinal(token)
69       ByteBuffer.allocate(iv.length + encryptedText.length).put(iv).put(encryptedText).array()
70     }
71 
72     override def decryptToken(token: Array[Byte]): Array[Byte] = {
73       val buffer = ByteBuffer.wrap(token)
74       val iv = Array.ofDim[Byte](IvSize)
75       buffer.get(iv)
76       val encryptedText = Array.ofDim[Byte](buffer.remaining())
77       buffer.get(encryptedText)
78       val cipher: Cipher = Cipher.getInstance(EncryptionAlgorithm)
79       cipher.init(Cipher.DECRYPT_MODE, secretKey, new GCMParameterSpec(TagLengthBit, iv))
80       cipher.doFinal(encryptedText)
81     }
82 
83     private def newIV(): Array[Byte] = {
84       val buffer = Array.ofDim[Byte](IvSize)
85       random.nextBytes(buffer)
86       buffer
87     }
88 
89     override def encryptAndEncode(token: String): String = {
90       val encryptedToken = encryptToken(token.getBytes(StandardCharsets.UTF_8))
91       Base64Encoding.encode(encryptedToken)
92     }
93 
94     override def decodeAndDecrypt(token: String): Try[String] = {
95       Base64Encoding
96         .decode(token)
97         .map(tokenDecoded => new String(decryptToken(tokenDecoded), StandardCharsets.UTF_8))
98     }
99   }
100 }
Line Stmt Id Pos Tree Symbol Tests Code
47 212 1612 - 1630 Apply java.security.SecureRandom.<init> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest new java.security.SecureRandom()
50 84 1731 - 1750 Literal <nosymbol> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest "AES/GCM/NoPadding"
51 31 1787 - 1790 Literal <nosymbol> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest 128
52 230 1816 - 1818 Literal <nosymbol> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest 16
56 119 1945 - 1979 Select org.make.api.technical.security.SecurityConfiguration.aesSecretKey org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryptionComponent.this.securityConfiguration.aesSecretKey
56 312 1923 - 1980 Apply org.make.api.technical.Base64Encoding.decode org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest org.make.api.technical.Base64Encoding.decode(DefaultAESEncryptionComponent.this.securityConfiguration.aesSecretKey)
57 187 2018 - 2047 Apply javax.crypto.spec.SecretKeySpec.<init> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest new javax.crypto.spec.SecretKeySpec(key, "AES")
59 137 2093 - 2182 Apply grizzled.slf4j.Logger.error DefaultAESEncryptionComponent.this.logger.error("Cannot decode aes secret key. Key might contain an invalid Base64 format.")
60 12 2193 - 2208 Throw <nosymbol> throw exception
65 85 2318 - 2357 Apply javax.crypto.Cipher.getInstance org.make.api.technical.security.aesencryptiontest javax.crypto.Cipher.getInstance(DefaultAESEncryption.this.EncryptionAlgorithm)
65 225 2337 - 2356 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.EncryptionAlgorithm org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.EncryptionAlgorithm
66 26 2373 - 2380 Apply org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.newIV org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.newIV()
67 198 2431 - 2469 Apply javax.crypto.spec.GCMParameterSpec.<init> org.make.api.technical.security.aesencryptiontest new javax.crypto.spec.GCMParameterSpec(DefaultAESEncryption.this.TagLengthBit, iv)
67 243 2399 - 2418 Literal <nosymbol> org.make.api.technical.security.aesencryptiontest 1
67 129 2387 - 2470 Apply javax.crypto.Cipher.init org.make.api.technical.security.aesencryptiontest cipher.init(1, DefaultAESEncryption.this.secretKey, new javax.crypto.spec.GCMParameterSpec(DefaultAESEncryption.this.TagLengthBit, iv))
67 120 2420 - 2429 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.secretKey org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.secretKey
67 323 2452 - 2464 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.TagLengthBit org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.TagLengthBit
68 7 2497 - 2518 Apply javax.crypto.Cipher.doFinal org.make.api.technical.security.aesencryptiontest cipher.doFinal(token)
69 226 2525 - 2613 Apply java.nio.ByteBuffer.array org.make.api.technical.security.aesencryptiontest java.nio.ByteBuffer.allocate(iv.length.+(encryptedText.length)).put(iv).put(encryptedText).array()
73 100 2707 - 2729 Apply java.nio.ByteBuffer.wrap org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest java.nio.ByteBuffer.wrap(token)
74 244 2745 - 2770 ApplyToImplicitArgs scala.Array.ofDim org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest scala.Array.ofDim[Byte](DefaultAESEncryption.this.IvSize)((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
74 301 2763 - 2769 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.IvSize org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryption.this.IvSize
75 113 2777 - 2791 Apply java.nio.ByteBuffer.get org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest buffer.get(iv)
76 193 2818 - 2855 ApplyToImplicitArgs scala.Array.ofDim org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest scala.Array.ofDim[Byte](buffer.remaining())((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
76 326 2836 - 2854 Apply java.nio.Buffer.remaining org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest buffer.remaining()
77 131 2862 - 2887 Apply java.nio.ByteBuffer.get org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest buffer.get(encryptedText)
78 218 2915 - 2954 Apply javax.crypto.Cipher.getInstance org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest javax.crypto.Cipher.getInstance(DefaultAESEncryption.this.EncryptionAlgorithm)
78 9 2934 - 2953 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.EncryptionAlgorithm org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryption.this.EncryptionAlgorithm
79 229 3026 - 3038 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.TagLengthBit org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryption.this.TagLengthBit
79 303 2994 - 3003 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.secretKey org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryption.this.secretKey
79 114 3005 - 3043 Apply javax.crypto.spec.GCMParameterSpec.<init> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest new javax.crypto.spec.GCMParameterSpec(DefaultAESEncryption.this.TagLengthBit, iv)
79 90 2973 - 2992 Literal <nosymbol> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest 2
79 317 2961 - 3044 Apply javax.crypto.Cipher.init org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest cipher.init(2, DefaultAESEncryption.this.secretKey, new javax.crypto.spec.GCMParameterSpec(DefaultAESEncryption.this.TagLengthBit, iv))
80 194 3051 - 3080 Apply javax.crypto.Cipher.doFinal org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest cipher.doFinal(encryptedText)
84 132 3166 - 3172 Select org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.IvSize org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.IvSize
84 3 3148 - 3173 ApplyToImplicitArgs scala.Array.ofDim org.make.api.technical.security.aesencryptiontest scala.Array.ofDim[Byte](DefaultAESEncryption.this.IvSize)((ClassTag.Byte: scala.reflect.ClassTag[Byte]))
85 219 3180 - 3204 Apply java.security.SecureRandom.nextBytes org.make.api.technical.security.aesencryptiontest DefaultAESEncryptionComponent.this.random.nextBytes(buffer)
90 98 3341 - 3363 Select java.nio.charset.StandardCharsets.UTF_8 org.make.api.technical.security.aesencryptiontest java.nio.charset.StandardCharsets.UTF_8
90 298 3326 - 3364 Apply java.lang.String.getBytes org.make.api.technical.security.aesencryptiontest token.getBytes(java.nio.charset.StandardCharsets.UTF_8)
90 231 3313 - 3365 Apply org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.encryptToken org.make.api.technical.security.aesencryptiontest DefaultAESEncryption.this.encryptToken(token.getBytes(java.nio.charset.StandardCharsets.UTF_8))
91 107 3372 - 3409 Apply org.make.api.technical.Base64Encoding.encode org.make.api.technical.security.aesencryptiontest org.make.api.technical.Base64Encoding.encode(encryptedToken)
97 322 3567 - 3593 Apply org.make.api.technical.security.DefaultAESEncryptionComponent.DefaultAESEncryption.decryptToken org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest DefaultAESEncryption.this.decryptToken(tokenDecoded)
97 4 3489 - 3619 Apply scala.util.Try.map org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest org.make.api.technical.Base64Encoding.decode(token).map[String](((tokenDecoded: Array[Byte]) => new scala.Predef.String(DefaultAESEncryption.this.decryptToken(tokenDecoded), java.nio.charset.StandardCharsets.UTF_8)))
97 72 3556 - 3618 Apply java.lang.String.<init> org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest new scala.Predef.String(DefaultAESEncryption.this.decryptToken(tokenDecoded), java.nio.charset.StandardCharsets.UTF_8)
97 188 3595 - 3617 Select java.nio.charset.StandardCharsets.UTF_8 org.make.api.technical.security.aesencryptiontest,org.make.api.demographics.demographicscardservicetest java.nio.charset.StandardCharsets.UTF_8