2019-05-24 17:06:48 +01:00
package encryption
2015-06-23 07:23:39 -04:00
import (
2020-05-21 11:29:45 -07:00
"crypto/rand"
2020-05-05 18:41:48 -07:00
"crypto/sha1"
"crypto/sha256"
2016-06-20 07:17:39 -04:00
"encoding/base64"
2020-05-21 11:29:45 -07:00
"fmt"
"io"
2015-06-23 07:23:39 -04:00
"testing"
2017-10-23 12:23:46 -04:00
"github.com/stretchr/testify/assert"
2015-06-23 07:23:39 -04:00
)
2020-05-21 11:29:45 -07:00
func TestSecretBytesEncoded ( t * testing . T ) {
for _ , secretSize := range [ ] int { 16 , 24 , 32 } {
t . Run ( fmt . Sprintf ( "%d" , secretSize ) , func ( t * testing . T ) {
secret := make ( [ ] byte , secretSize )
_ , err := io . ReadFull ( rand . Reader , secret )
assert . Equal ( t , nil , err )
// We test both padded & raw Base64 to ensure we handle both
// potential user input routes for Base64
base64Padded := base64 . URLEncoding . EncodeToString ( secret )
sb := SecretBytes ( base64Padded )
assert . Equal ( t , secret , sb )
assert . Equal ( t , len ( sb ) , secretSize )
base64Raw := base64 . RawURLEncoding . EncodeToString ( secret )
sb = SecretBytes ( base64Raw )
assert . Equal ( t , secret , sb )
assert . Equal ( t , len ( sb ) , secretSize )
} )
}
}
// A string that isn't intended as Base64 and still decodes (but to unintended length)
// will return the original secret as bytes
func TestSecretBytesEncodedWrongSize ( t * testing . T ) {
for _ , secretSize := range [ ] int { 15 , 20 , 28 , 33 , 44 } {
t . Run ( fmt . Sprintf ( "%d" , secretSize ) , func ( t * testing . T ) {
secret := make ( [ ] byte , secretSize )
_ , err := io . ReadFull ( rand . Reader , secret )
assert . Equal ( t , nil , err )
// We test both padded & raw Base64 to ensure we handle both
// potential user input routes for Base64
base64Padded := base64 . URLEncoding . EncodeToString ( secret )
sb := SecretBytes ( base64Padded )
assert . NotEqual ( t , secret , sb )
assert . NotEqual ( t , len ( sb ) , secretSize )
// The given secret is returned as []byte
assert . Equal ( t , base64Padded , string ( sb ) )
base64Raw := base64 . RawURLEncoding . EncodeToString ( secret )
sb = SecretBytes ( base64Raw )
assert . NotEqual ( t , secret , sb )
assert . NotEqual ( t , len ( sb ) , secretSize )
// The given secret is returned as []byte
assert . Equal ( t , base64Raw , string ( sb ) )
} )
}
}
func TestSecretBytesNonBase64 ( t * testing . T ) {
trailer := "equals=========="
assert . Equal ( t , trailer , string ( SecretBytes ( trailer ) ) )
raw16 := "asdflkjhqwer)(*&"
sb16 := SecretBytes ( raw16 )
assert . Equal ( t , raw16 , string ( sb16 ) )
assert . Equal ( t , 16 , len ( sb16 ) )
raw24 := "asdflkjhqwer)(*&CJEN#$%^"
sb24 := SecretBytes ( raw24 )
assert . Equal ( t , raw24 , string ( sb24 ) )
assert . Equal ( t , 24 , len ( sb24 ) )
raw32 := "asdflkjhqwer)(*&1234lkjhqwer)(*&"
sb32 := SecretBytes ( raw32 )
assert . Equal ( t , raw32 , string ( sb32 ) )
assert . Equal ( t , 32 , len ( sb32 ) )
}
2020-05-05 18:41:48 -07:00
func TestSignAndValidate ( t * testing . T ) {
seed := "0123456789abcdef"
key := "cookie-name"
value := base64 . URLEncoding . EncodeToString ( [ ] byte ( "I am soooo encoded" ) )
epoch := "123456789"
sha256sig := cookieSignature ( sha256 . New , seed , key , value , epoch )
sha1sig := cookieSignature ( sha1 . New , seed , key , value , epoch )
assert . True ( t , checkSignature ( sha256sig , seed , key , value , epoch ) )
// This should be switched to False after fully deprecating SHA1
assert . True ( t , checkSignature ( sha1sig , seed , key , value , epoch ) )
assert . False ( t , checkSignature ( sha256sig , seed , key , "tampered" , epoch ) )
assert . False ( t , checkSignature ( sha1sig , seed , key , "tampered" , epoch ) )
}
2015-06-23 07:23:39 -04:00
func TestEncodeAndDecodeAccessToken ( t * testing . T ) {
const secret = "0123456789abcdefghijklmnopqrstuv"
const token = "my access token"
2016-06-20 07:17:39 -04:00
c , err := NewCipher ( [ ] byte ( secret ) )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
encoded , err := c . Encrypt ( [ ] byte ( token ) )
2016-06-20 07:17:39 -04:00
assert . Equal ( t , nil , err )
decoded , err := c . Decrypt ( encoded )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
assert . NotEqual ( t , [ ] byte ( token ) , encoded )
assert . Equal ( t , [ ] byte ( token ) , decoded )
2016-06-20 07:17:39 -04:00
}
func TestEncodeAndDecodeAccessTokenB64 ( t * testing . T ) {
2018-11-29 14:26:41 +00:00
const secretBase64 = "A3Xbr6fu6Al0HkgrP1ztjb-mYiwmxgNPP-XbNsz1WBk="
2016-06-20 07:17:39 -04:00
const token = "my access token"
2018-11-29 14:26:41 +00:00
secret , err := base64 . URLEncoding . DecodeString ( secretBase64 )
assert . Equal ( t , nil , err )
2016-06-20 07:17:39 -04:00
c , err := NewCipher ( [ ] byte ( secret ) )
2015-06-23 07:23:39 -04:00
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
encoded , err := c . Encrypt ( [ ] byte ( token ) )
2015-06-23 07:23:39 -04:00
assert . Equal ( t , nil , err )
decoded , err := c . Decrypt ( encoded )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
assert . NotEqual ( t , [ ] byte ( token ) , encoded )
assert . Equal ( t , [ ] byte ( token ) , decoded )
}
2020-05-09 17:34:32 -07:00
func TestEncryptAndDecrypt ( t * testing . T ) {
2020-05-09 17:01:51 -07:00
var err error
2020-05-09 17:34:32 -07:00
// Test our 3 cipher types
for _ , initCipher := range [ ] func ( [ ] byte ) ( Cipher , error ) { NewCipher , NewCFBCipher , NewGCMCipher } {
// Test all 3 valid AES sizes
for _ , secretSize := range [ ] int { 16 , 24 , 32 } {
secret := make ( [ ] byte , secretSize )
_ , err = io . ReadFull ( rand . Reader , secret )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
c , err := initCipher ( secret )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
// Test various sizes sessions might be
for _ , dataSize := range [ ] int { 10 , 100 , 1000 , 5000 , 10000 } {
data := make ( [ ] byte , dataSize )
_ , err := io . ReadFull ( rand . Reader , data )
assert . Equal ( t , nil , err )
encrypted , err := c . Encrypt ( data )
assert . Equal ( t , nil , err )
assert . NotEqual ( t , encrypted , data )
decrypted , err := c . Decrypt ( encrypted )
assert . Equal ( t , nil , err )
assert . Equal ( t , data , decrypted )
assert . NotEqual ( t , encrypted , decrypted )
}
2020-05-09 17:01:51 -07:00
}
}
}
2020-05-09 17:34:32 -07:00
func TestDecryptWrongSecret ( t * testing . T ) {
secret1 := [ ] byte ( "0123456789abcdefghijklmnopqrstuv" )
secret2 := [ ] byte ( "9876543210abcdefghijklmnopqrstuv" )
// Test CFB & Base64 (GCM is authenticated, it errors differently)
for _ , initCipher := range [ ] func ( [ ] byte ) ( Cipher , error ) { NewCipher , NewCFBCipher } {
c1 , err := initCipher ( secret1 )
assert . Equal ( t , nil , err )
c2 , err := initCipher ( secret2 )
assert . Equal ( t , nil , err )
data := [ ] byte ( "f3928pufm982374dj02y485dsl34890u2t9nd4028s94dm58y2394087dhmsyt29h8df" )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
ciphertext , err := c1 . Encrypt ( data )
assert . Equal ( t , nil , err )
wrongData , err := c2 . Decrypt ( ciphertext )
assert . Equal ( t , nil , err )
assert . NotEqual ( t , data , wrongData )
}
}
func TestDecryptGCMWrongSecret ( t * testing . T ) {
2020-05-09 17:01:51 -07:00
secret1 := [ ] byte ( "0123456789abcdefghijklmnopqrstuv" )
secret2 := [ ] byte ( "9876543210abcdefghijklmnopqrstuv" )
2020-05-09 17:34:32 -07:00
c1 , err := NewGCMCipher ( secret1 )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
c2 , err := NewGCMCipher ( secret2 )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
data := [ ] byte ( "f3928pufm982374dj02y485dsl34890u2t9nd4028s94dm58y2394087dhmsyt29h8df" )
ciphertext , err := c1 . Encrypt ( data )
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
// GCM is authenticated - this should lead to message authentication failed
_ , err = c2 . Decrypt ( ciphertext )
assert . Error ( t , err )
2020-05-09 17:01:51 -07:00
}
2020-05-09 17:34:32 -07:00
func TestIntermixCiphersErrors ( t * testing . T ) {
2020-05-09 17:01:51 -07:00
var err error
2020-05-09 17:34:32 -07:00
// Encrypt with GCM, Decrypt with CFB: Results in Garbage data
2020-05-09 17:01:51 -07:00
// Test all 3 valid AES sizes
for _ , secretSize := range [ ] int { 16 , 24 , 32 } {
secret := make ( [ ] byte , secretSize )
_ , err = io . ReadFull ( rand . Reader , secret )
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
gcm , err := NewGCMCipher ( secret )
assert . Equal ( t , nil , err )
cfb , err := NewCFBCipher ( secret )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
// Test various sizes sessions might be
for _ , dataSize := range [ ] int { 10 , 100 , 1000 , 5000 , 10000 } {
data := make ( [ ] byte , dataSize )
_ , err := io . ReadFull ( rand . Reader , data )
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
encrypted , err := gcm . Encrypt ( data )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
assert . NotEqual ( t , encrypted , data )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
decrypted , err := cfb . Decrypt ( encrypted )
2020-05-09 17:01:51 -07:00
assert . Equal ( t , nil , err )
2020-05-09 17:34:32 -07:00
// Data is mangled
assert . NotEqual ( t , data , decrypted )
assert . NotEqual ( t , encrypted , decrypted )
2020-05-09 17:01:51 -07:00
}
}
2020-05-09 17:34:32 -07:00
// Encrypt with CFB, Decrypt with GCM: Results in errors
// Test all 3 valid AES sizes
for _ , secretSize := range [ ] int { 16 , 24 , 32 } {
secret := make ( [ ] byte , secretSize )
_ , err = io . ReadFull ( rand . Reader , secret )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
gcm , err := NewGCMCipher ( secret )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
cfb , err := NewCFBCipher ( secret )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
// Test various sizes sessions might be
for _ , dataSize := range [ ] int { 10 , 100 , 1000 , 5000 , 10000 } {
data := make ( [ ] byte , dataSize )
_ , err := io . ReadFull ( rand . Reader , data )
assert . Equal ( t , nil , err )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
encrypted , err := cfb . Encrypt ( data )
assert . Equal ( t , nil , err )
assert . NotEqual ( t , encrypted , data )
2020-05-09 17:01:51 -07:00
2020-05-09 17:34:32 -07:00
// GCM is authenticated - this should lead to message authentication failed
_ , err = gcm . Decrypt ( encrypted )
assert . Error ( t , err )
}
}
2015-06-23 07:23:39 -04:00
}
2020-05-30 08:53:38 +01:00
func TestEncodeIntoAndDecodeIntoAccessToken ( t * testing . T ) {
const secret = "0123456789abcdefghijklmnopqrstuv"
c , err := NewCipher ( [ ] byte ( secret ) )
assert . Equal ( t , nil , err )
token := "my access token"
originalToken := token
assert . Equal ( t , nil , c . EncryptInto ( & token ) )
assert . NotEqual ( t , originalToken , token )
assert . Equal ( t , nil , c . DecryptInto ( & token ) )
assert . Equal ( t , originalToken , token )
// Check no errors with empty or nil strings
empty := ""
assert . Equal ( t , nil , c . EncryptInto ( & empty ) )
assert . Equal ( t , nil , c . DecryptInto ( & empty ) )
assert . Equal ( t , nil , c . EncryptInto ( nil ) )
assert . Equal ( t , nil , c . DecryptInto ( nil ) )
}