-
Notifications
You must be signed in to change notification settings - Fork 0
/
AESGCM.go
224 lines (186 loc) · 5.53 KB
/
AESGCM.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
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
// Library code for authenticated encryption/decryption and symmetric key
// management. This code uses AES-GCM for authenticated encryption and a
// SHA-256 based key derivation function.
//
// SECURITY WARNING: This code is meant for educational purposes and may
// contain vulnerabilities or other bugs. Please do not use it for
// security-critical applications.
//
// GRADING NOTES: your code will be evaluated using this code. If you modify
// this code remember that the default version will be used for grading. You
// should add functions as needed in chatter.go or other supplemental files,
// not here.
//
// Original version
// Joseph Bonneau February 2019
package chatterbox
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"crypto/sha256"
"fmt"
"io"
)
const SYMMETRIC_KEY_LENGTH = 32 // 256-bit keys
const IV_LENGTH = 12 // 128-bit nonces
const HASH_OUTPUT_LENGTH = 32 // SHA-256
var fixedRandomReader FixedRandomReader
// Simple PRNG based on SHA256 hashing from a constant intput
type FixedRandom struct {
state []byte
index int
}
// Update updates the internal state, when necessary
func (a *FixedRandom) Update() {
h := sha256.New()
h.Write(a.state)
a.state = h.Sum(nil)
}
// Needed to comply with reader interface
type FixedRandomReader struct {
r *FixedRandom
}
// Read returns random bytes produced by a chain of SHA-256 hashes
func (a FixedRandomReader) Read(p []byte) (int, error) {
for i := 0; i < len(p); i++ {
if a.r.index == 0 {
a.r.Update()
}
p[i] = a.r.state[a.r.index]
a.r.index = (a.r.index + 1) % HASH_OUTPUT_LENGTH
}
return len(p), nil
}
// RandomnessSource reveals the real or test randomness source
func RandomnessSource() io.Reader {
if fixedRandomMode {
return fixedRandomReader
} else {
return rand.Reader
}
}
// set this flag (at run-time) to use a fixed RNG
var fixedRandomMode = false
func SetFixedRandomness(newValue bool) {
if newValue == true && fixedRandomMode == false {
fixedRandomReader = FixedRandomReader{
r: &FixedRandom{
state: make([]byte, HASH_OUTPUT_LENGTH),
},
}
}
fixedRandomMode = newValue
}
// SymmetricKey represents a symmetric key, which is simply a buffer of
// random bytes.
type SymmetricKey struct {
Key []byte
}
// RandomBytes fills a buffer with the requested number of bytes.
// The data is read from the system PRNG
func RandomBytes(n int) []byte {
buf := make([]byte, n)
if _, err := io.ReadFull(RandomnessSource(), buf); err != nil {
panic(err)
}
return buf
}
// NewSymmetricKey creates a new random symmetric key.
// Note: you should not need to call this for your chat application. Every
// key needed will be derived from DH outputs and chains of keys.
func NewSymmetricKey() *SymmetricKey {
return &SymmetricKey{
Key: RandomBytes(SYMMETRIC_KEY_LENGTH),
}
}
// NewSymmetricKey creates a new, random initialization vector
func NewIV() []byte {
// Use a fixed IV in test mode
if fixedRandomMode {
result := make([]byte, IV_LENGTH)
for i := 0; i < len(result); i++ {
result[i] = byte(i + 1)
}
return result
}
return RandomBytes(IV_LENGTH)
}
// String representation of a symmetric key.
func (k *SymmetricKey) String() string {
return fmt.Sprintf("Symmetric key : % 0X", k.Key)
}
// Duplicate produces an exact copy of a given key
func (k *SymmetricKey) Duplicate() *SymmetricKey {
r := SymmetricKey{
Key: make([]byte, SYMMETRIC_KEY_LENGTH),
}
copy(r.Key, k.Key)
return &r
}
// Zeroize overwrites the key bytes with zero bytes
func (k *SymmetricKey) Zeroize() {
for i := range k.Key {
k.Key[i] = 0
}
}
// DeriveKey evalutes a key derivation function (KDF) on a key and returns
// the result. The label modifiers how the KDF operates. Note that the original
// key is left intact and not zeroized.
func (k *SymmetricKey) DeriveKey(label byte) *SymmetricKey {
h := sha256.New()
h.Write([]byte{label})
h.Write(k.Key)
result := h.Sum(nil)
return &SymmetricKey{
Key: result[:SYMMETRIC_KEY_LENGTH],
}
}
// CombineKeys takes any number of keys as input and combines them into a new
// key. This combined key is a hash of the input keys so does not reveal
// any info about them. This does not zeroize the input keys.
// Note that the order the keys are passed in matters.
func CombineKeys(keys ...*SymmetricKey) *SymmetricKey {
h := sha256.New()
for _, k := range keys {
h.Write(k.Key)
}
result := h.Sum(nil)
return &SymmetricKey{
Key: result[:SYMMETRIC_KEY_LENGTH],
}
}
// AuthenticatedEncrypt uses a key k to encrypt a given plaintext and a buffer
// additionalData of data for authentication (but not encryption).
// Since AESGCM is a stream cipher, semantic security requires a new random IV
// for every encryption.
func (k *SymmetricKey) AuthenticatedEncrypt(plaintext string, additionalData, iv []byte) []byte {
block, err := aes.NewCipher(k.Key)
if err != nil {
panic(err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
return aesgcm.Seal(nil, iv, []byte(plaintext), additionalData)
}
// AuthenticatedDecrypt uses a key k to decrypt a given ciphertext and a buffer
// additionalData of data for authentication (but not encryption).
// If the ciphertext or additionalData have been modified, an
// error will be returned.
func (k *SymmetricKey) AuthenticatedDecrypt(ciphertext, additionalData, iv []byte) (string, error) {
block, err := aes.NewCipher(k.Key)
if err != nil {
panic(err)
}
aesgcm, err := cipher.NewGCM(block)
if err != nil {
panic(err)
}
plaintext, err := aesgcm.Open(nil, iv, ciphertext, additionalData)
if err != nil {
return "", err
}
return string(plaintext), nil
}