-
Notifications
You must be signed in to change notification settings - Fork 435
/
gcm.go
143 lines (119 loc) · 3.44 KB
/
gcm.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
package xmlenc
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"encoding/base64"
"fmt"
"io"
"github.com/beevik/etree"
)
// GCM implements Decrypter and Encrypter for block ciphers in struct mode
type GCM struct {
keySize int
algorithm string
cipher func([]byte) (cipher.Block, error)
}
// KeySize returns the length of the key required.
func (e GCM) KeySize() int {
return e.keySize
}
// Algorithm returns the name of the algorithm, as will be found
// in an xenc:EncryptionMethod element.
func (e GCM) Algorithm() string {
return e.algorithm
}
// Encrypt encrypts plaintext with key and nonce
func (e GCM) Encrypt(key interface{}, plaintext []byte, nonce []byte) (*etree.Element, error) {
keyBuf, ok := key.([]byte)
if !ok {
return nil, ErrIncorrectKeyType("[]byte")
}
if len(keyBuf) != e.keySize {
return nil, ErrIncorrectKeyLength(e.keySize)
}
block, err := e.cipher(keyBuf)
if err != nil {
return nil, err
}
encryptedDataEl := etree.NewElement("xenc:EncryptedData")
encryptedDataEl.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
{
randBuf := make([]byte, 16)
if _, err := RandReader.Read(randBuf); err != nil {
return nil, err
}
encryptedDataEl.CreateAttr("Id", fmt.Sprintf("_%x", randBuf))
}
em := encryptedDataEl.CreateElement("xenc:EncryptionMethod")
em.CreateAttr("Algorithm", e.algorithm)
em.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
plaintext = appendPadding(plaintext, block.BlockSize())
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
if nonce == nil {
// generate random nonce when it's nil
nonce := make([]byte, aesgcm.NonceSize())
if _, err := io.ReadFull(rand.Reader, nonce); err != nil {
panic(err.Error())
}
}
ciphertext := make([]byte, len(plaintext))
text := aesgcm.Seal(nil, nonce, ciphertext, nil)
cd := encryptedDataEl.CreateElement("xenc:CipherData")
cd.CreateAttr("xmlns:xenc", "http://www.w3.org/2001/04/xmlenc#")
cd.CreateElement("xenc:CipherValue").SetText(base64.StdEncoding.EncodeToString(text))
return encryptedDataEl, nil
}
// Decrypt decrypts an encrypted element with key. If the ciphertext contains an
// EncryptedKey element, then the type of `key` is determined by the registered
// Decryptor for the EncryptedKey element. Otherwise, `key` must be a []byte of
// length KeySize().
func (e GCM) Decrypt(key interface{}, ciphertextEl *etree.Element) ([]byte, error) {
if encryptedKeyEl := ciphertextEl.FindElement("./KeyInfo/EncryptedKey"); encryptedKeyEl != nil {
var err error
key, err = Decrypt(key, encryptedKeyEl)
if err != nil {
return nil, err
}
}
keyBuf, ok := key.([]byte)
if !ok {
return nil, ErrIncorrectKeyType("[]byte")
}
if len(keyBuf) != e.KeySize() {
return nil, ErrIncorrectKeyLength(e.KeySize())
}
block, err := e.cipher(keyBuf)
if err != nil {
return nil, err
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
return nil, err
}
ciphertext, err := getCiphertext(ciphertextEl)
if err != nil {
return nil, err
}
nonce := ciphertext[:aesgcm.NonceSize()]
text := ciphertext[aesgcm.NonceSize():]
plainText, err := aesgcm.Open(nil, nonce, text, nil)
if err != nil {
return nil, err
}
return plainText, nil
}
var (
// AES128GCM implements AES128-GCM mode for encryption and decryption
AES128GCM BlockCipher = GCM{
keySize: 16,
algorithm: "http://www.w3.org/2009/xmlenc11#aes128-gcm",
cipher: aes.NewCipher,
}
)
func init() {
RegisterDecrypter(AES128GCM)
}