git.iko.soy/typeable/oauth2-proxy
on proxy-mapping help sign in
132 lines 3.5 kB history raw
1package encryption
3import (
4 "crypto/aes"
5 "crypto/cipher"
6 "crypto/rand"
7 "encoding/base64"
8 "fmt"
9 "io"
12// Cipher provides methods to encrypt and decrypt
13type Cipher interface {
14 Encrypt(value []byte) ([]byte, error)
15 Decrypt(ciphertext []byte) ([]byte, error)
18type base64Cipher struct {
19 Cipher Cipher
22// NewBase64Cipher returns a new AES Cipher for encrypting cookie values
23// and wrapping them in Base64 -- Supports Legacy encryption scheme
24func NewBase64Cipher(c Cipher) Cipher {
25 return &base64Cipher{Cipher: c}
28// Encrypt encrypts a value with the embedded Cipher & Base64 encodes it
29func (c *base64Cipher) Encrypt(value []byte) ([]byte, error) {
30 encrypted, err := c.Cipher.Encrypt(value)
31 if err != nil {
32 return nil, err
33 }
35 return []byte(base64.StdEncoding.EncodeToString(encrypted)), nil
38// Decrypt Base64 decodes a value & decrypts it with the embedded Cipher
39func (c *base64Cipher) Decrypt(ciphertext []byte) ([]byte, error) {
40 encrypted, err := base64.StdEncoding.DecodeString(string(ciphertext))
41 if err != nil {
42 return nil, fmt.Errorf("failed to base64 decode value %s", err)
43 }
45 return c.Cipher.Decrypt(encrypted)
48type cfbCipher struct {
49 cipher.Block
52// NewCFBCipher returns a new AES CFB Cipher
53func NewCFBCipher(secret []byte) (Cipher, error) {
54 c, err := aes.NewCipher(secret)
55 if err != nil {
56 return nil, err
57 }
58 return &cfbCipher{Block: c}, err
61// Encrypt with AES CFB
62func (c *cfbCipher) Encrypt(value []byte) ([]byte, error) {
63 ciphertext := make([]byte, aes.BlockSize+len(value))
64 iv := ciphertext[:aes.BlockSize]
65 if _, err := io.ReadFull(rand.Reader, iv); err != nil {
66 return nil, fmt.Errorf("failed to create initialization vector %s", err)
67 }
69 stream := cipher.NewCFBEncrypter(c.Block, iv)
70 stream.XORKeyStream(ciphertext[aes.BlockSize:], value)
71 return ciphertext, nil
74// Decrypt an AES CFB ciphertext
75func (c *cfbCipher) Decrypt(ciphertext []byte) ([]byte, error) {
76 if len(ciphertext) < aes.BlockSize {
77 return nil, fmt.Errorf("encrypted value should be at least %d bytes, but is only %d bytes", aes.BlockSize, len(ciphertext))
78 }
80 iv, ciphertext := ciphertext[:aes.BlockSize], ciphertext[aes.BlockSize:]
81 plaintext := make([]byte, len(ciphertext))
82 stream := cipher.NewCFBDecrypter(c.Block, iv)
83 stream.XORKeyStream(plaintext, ciphertext)
85 return plaintext, nil
88type gcmCipher struct {
89 cipher.Block
92// NewGCMCipher returns a new AES GCM Cipher
93func NewGCMCipher(secret []byte) (Cipher, error) {
94 c, err := aes.NewCipher(secret)
95 if err != nil {
96 return nil, err
97 }
98 return &gcmCipher{Block: c}, err
101// Encrypt with AES GCM on raw bytes
102func (c *gcmCipher) Encrypt(value []byte) ([]byte, error) {
103 gcm, err := cipher.NewGCM(c.Block)
104 if err != nil {
105 return nil, err
106 }
107 nonce := make([]byte, gcm.NonceSize())
108 if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
109 return nil, err
110 }
111 // Using nonce as Seal's dst argument results in it being the first
112 // chunk of bytes in the ciphertext. Decrypt retrieves the nonce/IV from this.
113 ciphertext := gcm.Seal(nonce, nonce, value, nil)
114 return ciphertext, nil
117// Decrypt an AES GCM ciphertext
118func (c *gcmCipher) Decrypt(ciphertext []byte) ([]byte, error) {
119 gcm, err := cipher.NewGCM(c.Block)
120 if err != nil {
121 return nil, err
122 }
124 nonceSize := gcm.NonceSize()
125 nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
127 plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
128 if err != nil {
129 return nil, err
130 }
131 return plaintext, nil