mirror of
https://github.com/kataras/iris.git
synced 2026-01-24 20:35:59 +00:00
implement the Iris Crypto Library for Request Authentication and Verification. With Examples and Tests.
Relative to this one as well: https://github.com/kataras/iris/issues/1200 Former-commit-id: 3a29e7398b7fdeb9b48a118b742d419d5681d56b
This commit is contained in:
134
crypto/gcm/gcm.go
Normal file
134
crypto/gcm/gcm.go
Normal file
@@ -0,0 +1,134 @@
|
||||
// Package gcm implements encryption/decription using the AES algorithm and the Galois/Counter Mode.
|
||||
package gcm
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"crypto/sha512"
|
||||
"encoding/hex"
|
||||
)
|
||||
|
||||
// MustGenerateKey generates an aes key.
|
||||
// It panics if any error occurred.
|
||||
func MustGenerateKey() []byte {
|
||||
aesKey, err := GenerateKey()
|
||||
if err != nil {
|
||||
panic(err)
|
||||
}
|
||||
|
||||
return aesKey
|
||||
}
|
||||
|
||||
// GenerateKey returns a random aes key.
|
||||
func GenerateKey() ([]byte, error) {
|
||||
key := make([]byte, 64)
|
||||
n, err := rand.Read(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return encode(key[:n]), nil
|
||||
}
|
||||
|
||||
// Encrypt encrypts and authenticates the plain data and additional data
|
||||
// and returns the ciphertext of it.
|
||||
// It uses the AEAD cipher mode providing authenticated encryption with associated
|
||||
// data.
|
||||
// The same additional data must be kept the same for `Decrypt`.
|
||||
func Encrypt(aesKey, data, additionalData []byte) ([]byte, error) {
|
||||
key, err := decode(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(key)
|
||||
digest := encode(h.Sum(nil))
|
||||
|
||||
// key based on the hash itself, we have space because of sha512.
|
||||
newKey, err := decode(digest[:64])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
// nonce based on the hash itself.
|
||||
nonce, err := decode(digest[64:(64 + 24)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
aData, err := decode(additionalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext := encode(gcm.Seal(nil, nonce, data, aData))
|
||||
return ciphertext, nil
|
||||
}
|
||||
|
||||
// Decrypt decrypts and authenticates ciphertext, authenticates the
|
||||
// additional data and, if successful, returns the resulting plain data.
|
||||
// The additional data must match the value passed to `Encrypt`.
|
||||
func Decrypt(aesKey, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
key, err := decode(aesKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
h := sha512.New()
|
||||
h.Write(key)
|
||||
digest := encode(h.Sum(nil))
|
||||
|
||||
newKey, err := decode(digest[:64])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce, err := decode(digest[64:(64 + 24)])
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
additionalData, err = decode(additionalData)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
block, err := aes.NewCipher(newKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
gcm, err := cipher.NewGCM(block)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
ciphertext, err = decode(ciphertext)
|
||||
return gcm.Open(nil, nonce, ciphertext, additionalData)
|
||||
}
|
||||
|
||||
func decode(src []byte) ([]byte, error) {
|
||||
buf := make([]byte, hex.DecodedLen(len(src)))
|
||||
n, err := hex.Decode(buf, src)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
return buf[:n], nil
|
||||
}
|
||||
|
||||
func encode(src []byte) []byte {
|
||||
buf := make([]byte, hex.EncodedLen(len(src)))
|
||||
hex.Encode(buf, src)
|
||||
return buf
|
||||
}
|
||||
46
crypto/gcm/gcm_test.go
Normal file
46
crypto/gcm/gcm_test.go
Normal file
@@ -0,0 +1,46 @@
|
||||
package gcm
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var testKey = MustGenerateKey()
|
||||
|
||||
func TestEncryptDecrypt(t *testing.T) {
|
||||
if len(testKey) == 0 {
|
||||
t.Fatalf("testKey is empty??")
|
||||
}
|
||||
|
||||
tests := []struct {
|
||||
payload []byte
|
||||
aData []byte // IV of a random aes-256-cbc, 32 size.
|
||||
}{
|
||||
{[]byte("test my content 1"), []byte("FFA0A43EA6B8C829AD403817B2F5B7A2")},
|
||||
{[]byte("test my content 2"), []byte("364787B9AF1AEE4BE26690EA8CBF4AB7")},
|
||||
}
|
||||
|
||||
for i, tt := range tests {
|
||||
ciphertext, err := Encrypt(testKey, tt.payload, tt.aData)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] encrypt error: %v", i, err)
|
||||
}
|
||||
|
||||
payload, err := Decrypt(testKey, ciphertext, tt.aData)
|
||||
if err != nil {
|
||||
t.Fatalf("[%d] decrypt error: %v", i, err)
|
||||
}
|
||||
|
||||
if !bytes.Equal(payload, tt.payload) {
|
||||
t.Fatalf("[%d] expected data to be decrypted to: '%s' but got: '%s'", i, tt.payload, payload)
|
||||
}
|
||||
|
||||
// test with other, invalid key, should fail to decrypt.
|
||||
tempKey := MustGenerateKey()
|
||||
|
||||
payload, err = Decrypt(tempKey, ciphertext, tt.aData)
|
||||
if err == nil || len(payload) > 0 {
|
||||
t.Fatalf("[%d] verification should fail but passed for '%s'", i, tt.payload)
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user