17
17
package auth
18
18
19
19
import (
20
+ "bytes"
20
21
"crypto/aes"
21
22
"crypto/cipher"
22
- "crypto/rand "
23
+ "crypto/hmac "
23
24
"crypto/sha1"
25
+ "crypto/sha256"
24
26
"encoding/base64"
25
27
"errors"
26
28
"fmt"
27
29
"io"
30
+ "io/ioutil"
28
31
"log"
29
32
"net/http"
30
33
"strings"
@@ -33,13 +36,17 @@ import (
33
36
"github.com/minio/console/models"
34
37
"github.com/minio/console/pkg/auth/token"
35
38
"github.com/minio/minio-go/v7/pkg/credentials"
39
+ "github.com/secure-io/sio-go/sioutil"
40
+ "golang.org/x/crypto/chacha20"
41
+ "golang.org/x/crypto/chacha20poly1305"
36
42
"golang.org/x/crypto/pbkdf2"
37
43
)
38
44
39
45
var (
40
46
errNoAuthToken = errors .New ("session token missing" )
41
47
errReadingToken = errors .New ("session token internal data is malformed" )
42
48
errClaimsFormat = errors .New ("encrypted session token claims not in the right format" )
49
+ errorGeneric = errors .New ("an error has occurred" )
43
50
)
44
51
45
52
// derivedKey is the key used to encrypt the session token claims, its derived using pbkdf on CONSOLE_PBKDF_PASSPHRASE with CONSOLE_PBKDF_SALT
@@ -102,9 +109,10 @@ func NewEncryptedTokenForClient(credentials *credentials.Value, actions []string
102
109
// returns a base64 encoded ciphertext
103
110
func encryptClaims (accessKeyID , secretAccessKey , sessionToken string , actions []string ) (string , error ) {
104
111
payload := []byte (fmt .Sprintf ("%s#%s#%s#%s" , accessKeyID , secretAccessKey , sessionToken , strings .Join (actions , "," )))
105
- ciphertext , err := encrypt (payload )
112
+ ciphertext , err := encrypt (payload , [] byte {} )
106
113
if err != nil {
107
- return "" , err
114
+ log .Println (err )
115
+ return "" , errorGeneric
108
116
}
109
117
return base64 .StdEncoding .EncodeToString (ciphertext ), nil
110
118
}
@@ -116,7 +124,7 @@ func decryptClaims(ciphertext string) (*DecryptedClaims, error) {
116
124
log .Println (err )
117
125
return nil , errClaimsFormat
118
126
}
119
- plaintext , err := decrypt (decoded )
127
+ plaintext , err := decrypt (decoded , [] byte {} )
120
128
if err != nil {
121
129
log .Println (err )
122
130
return nil , errClaimsFormat
@@ -136,37 +144,137 @@ func decryptClaims(ciphertext string) (*DecryptedClaims, error) {
136
144
}, nil
137
145
}
138
146
139
- // Encrypt a blob of data using AEAD (AES-GCM) with a pbkdf2 derived key
140
- func encrypt (plaintext []byte ) ([]byte , error ) {
141
- block , _ := aes .NewCipher (derivedKey )
142
- gcm , err := cipher .NewGCM (block )
147
+ const (
148
+ aesGcm = 0x00
149
+ c20p1305 = 0x01
150
+ )
151
+
152
+ // Encrypt a blob of data using AEAD scheme, AES-GCM if the executing CPU
153
+ // provides AES hardware support, otherwise will use ChaCha20-Poly1305
154
+ // with a pbkdf2 derived key, this function should be used to encrypt a session
155
+ // or data key provided as plaintext.
156
+ //
157
+ // The returned ciphertext data consists of:
158
+ // iv | AEAD ID | nonce | encrypted data
159
+ // 32 1 12 ~ len(data)
160
+ func encrypt (plaintext , associatedData []byte ) ([]byte , error ) {
161
+ iv , err := sioutil .Random (32 ) // 32 bit IV
143
162
if err != nil {
144
163
return nil , err
145
164
}
146
- nonce := make ([]byte , gcm .NonceSize ())
147
- if _ , err = io .ReadFull (rand .Reader , nonce ); err != nil {
165
+ var algorithm byte
166
+ if sioutil .NativeAES () {
167
+ algorithm = aesGcm
168
+ } else {
169
+ algorithm = c20p1305
170
+ }
171
+ var aead cipher.AEAD
172
+ switch algorithm {
173
+ case aesGcm :
174
+ mac := hmac .New (sha256 .New , derivedKey )
175
+ mac .Write (iv )
176
+ sealingKey := mac .Sum (nil )
177
+
178
+ var block cipher.Block
179
+ block , err = aes .NewCipher (sealingKey )
180
+ if err != nil {
181
+ return nil , err
182
+ }
183
+ aead , err = cipher .NewGCM (block )
184
+ if err != nil {
185
+ return nil , err
186
+ }
187
+ case c20p1305 :
188
+ var sealingKey []byte
189
+ sealingKey , err = chacha20 .HChaCha20 (derivedKey , iv )
190
+ if err != nil {
191
+ return nil , err
192
+ }
193
+ aead , err = chacha20poly1305 .New (sealingKey )
194
+ if err != nil {
195
+ return nil , err
196
+ }
197
+ }
198
+ nonce , err := sioutil .Random (aead .NonceSize ())
199
+ if err != nil {
148
200
return nil , err
149
201
}
150
- cipherText := gcm .Seal (nonce , nonce , plaintext , nil )
151
- return cipherText , nil
202
+
203
+ sealedBytes := aead .Seal (nil , nonce , plaintext , associatedData )
204
+
205
+ // ciphertext = iv | AEAD ID | nonce | sealed bytes
206
+
207
+ var buf bytes.Buffer
208
+ buf .Write (iv )
209
+ buf .WriteByte (algorithm )
210
+ buf .Write (nonce )
211
+ buf .Write (sealedBytes )
212
+
213
+ return buf .Bytes (), nil
152
214
}
153
215
154
- // Decrypts a blob of data using AEAD (AES-GCM) with a pbkdf2 derived key
155
- func decrypt (data []byte ) ([]byte , error ) {
156
- block , err := aes .NewCipher (derivedKey )
157
- if err != nil {
216
+ // Decrypts a blob of data using AEAD scheme AES-GCM if the executing CPU
217
+ // provides AES hardware support, otherwise will use ChaCha20-Poly1305with
218
+ // and a pbkdf2 derived key
219
+ func decrypt (ciphertext []byte , associatedData []byte ) ([]byte , error ) {
220
+ var (
221
+ iv [32 ]byte
222
+ algorithm [1 ]byte
223
+ nonce [12 ]byte // This depends on the AEAD but both used ciphers have the same nonce length.
224
+ )
225
+
226
+ r := bytes .NewReader (ciphertext )
227
+ if _ , err := io .ReadFull (r , iv [:]); err != nil {
228
+ return nil , err
229
+ }
230
+ if _ , err := io .ReadFull (r , algorithm [:]); err != nil {
231
+ return nil , err
232
+ }
233
+ if _ , err := io .ReadFull (r , nonce [:]); err != nil {
158
234
return nil , err
159
235
}
160
- gcm , err := cipher .NewGCM (block )
236
+
237
+ var aead cipher.AEAD
238
+ switch algorithm [0 ] {
239
+ case aesGcm :
240
+ mac := hmac .New (sha256 .New , derivedKey )
241
+ mac .Write (iv [:])
242
+ sealingKey := mac .Sum (nil )
243
+ block , err := aes .NewCipher (sealingKey [:])
244
+ if err != nil {
245
+ return nil , err
246
+ }
247
+ aead , err = cipher .NewGCM (block )
248
+ if err != nil {
249
+ return nil , err
250
+ }
251
+ case c20p1305 :
252
+ sealingKey , err := chacha20 .HChaCha20 (derivedKey , iv [:])
253
+ if err != nil {
254
+ return nil , err
255
+ }
256
+ aead , err = chacha20poly1305 .New (sealingKey )
257
+ if err != nil {
258
+ return nil , err
259
+ }
260
+ default :
261
+ return nil , fmt .Errorf ("invalid algorithm: %v" , algorithm )
262
+ }
263
+
264
+ if len (nonce ) != aead .NonceSize () {
265
+ return nil , fmt .Errorf ("invalid nonce size %d, expected %d" , len (nonce ), aead .NonceSize ())
266
+ }
267
+
268
+ sealedBytes , err := ioutil .ReadAll (r )
161
269
if err != nil {
162
270
return nil , err
163
271
}
164
- nonceSize := gcm .NonceSize ()
165
- nonce , cipherText := data [:nonceSize ], data [nonceSize :]
166
- plaintext , err := gcm .Open (nil , nonce , cipherText , nil )
272
+
273
+ plaintext , err := aead .Open (nil , nonce [:], sealedBytes , associatedData )
167
274
if err != nil {
168
275
return nil , err
169
276
}
277
+
170
278
return plaintext , nil
171
279
}
172
280
0 commit comments