This change adds server-side-encryption support for HEAD, GET and PUT operations. This PR only addresses single-part PUTs and GETs without HTTP ranges. Further this change adds the concept of reserved object metadata which is required to make encrypted objects tamper-proof and provide API compatibility to AWS S3. This PR adds the following reserved metadata entries: - X-Minio-Internal-Server-Side-Encryption-Iv ('guarantees' tamper-proof property) - X-Minio-Internal-Server-Side-Encryption-Kdf (makes Key-MAC computation negotiable in future) - X-Minio-Internal-Server-Side-Encryption-Key-Mac (provides AWS S3 API compatibility) The prefix `X-Minio_Internal` specifies an internal metadata entry which must not send to clients. All client requests containing a metadata key starting with `X-Minio-Internal` must also rejected. This is implemented by a generic-handler. This PR implements SSE-C separated from client-side-encryption (CSE). This cannot decrypt server-side-encrypted objects on the client-side. However, clients can encrypted the same object with CSE and SSE-C. This PR does not address: - SSE-C Copy and Copy part - SSE-C GET with HTTP ranges - SSE-C multipart PUT - SSE-C Gateway Each point must be addressed in a separate PR. Added to vendor dir: - x/crypto/chacha20poly1305 - x/crypto/poly1305 - github.com/minio/sio
127 lines
3.3 KiB
Go
127 lines
3.3 KiB
Go
// Copyright 2016 The Go Authors. All rights reserved.
|
|
// Use of this source code is governed by a BSD-style
|
|
// license that can be found in the LICENSE file.
|
|
|
|
// +build go1.7,amd64,!gccgo,!appengine
|
|
|
|
package chacha20poly1305
|
|
|
|
import "encoding/binary"
|
|
|
|
//go:noescape
|
|
func chacha20Poly1305Open(dst []byte, key []uint32, src, ad []byte) bool
|
|
|
|
//go:noescape
|
|
func chacha20Poly1305Seal(dst []byte, key []uint32, src, ad []byte)
|
|
|
|
// cpuid is implemented in chacha20poly1305_amd64.s.
|
|
func cpuid(eaxArg, ecxArg uint32) (eax, ebx, ecx, edx uint32)
|
|
|
|
// xgetbv with ecx = 0 is implemented in chacha20poly1305_amd64.s.
|
|
func xgetbv() (eax, edx uint32)
|
|
|
|
var (
|
|
useASM bool
|
|
useAVX2 bool
|
|
)
|
|
|
|
func init() {
|
|
detectCPUFeatures()
|
|
}
|
|
|
|
// detectCPUFeatures is used to detect if cpu instructions
|
|
// used by the functions implemented in assembler in
|
|
// chacha20poly1305_amd64.s are supported.
|
|
func detectCPUFeatures() {
|
|
maxID, _, _, _ := cpuid(0, 0)
|
|
if maxID < 1 {
|
|
return
|
|
}
|
|
|
|
_, _, ecx1, _ := cpuid(1, 0)
|
|
|
|
haveSSSE3 := isSet(9, ecx1)
|
|
useASM = haveSSSE3
|
|
|
|
haveOSXSAVE := isSet(27, ecx1)
|
|
|
|
osSupportsAVX := false
|
|
// For XGETBV, OSXSAVE bit is required and sufficient.
|
|
if haveOSXSAVE {
|
|
eax, _ := xgetbv()
|
|
// Check if XMM and YMM registers have OS support.
|
|
osSupportsAVX = isSet(1, eax) && isSet(2, eax)
|
|
}
|
|
haveAVX := isSet(28, ecx1) && osSupportsAVX
|
|
|
|
if maxID < 7 {
|
|
return
|
|
}
|
|
|
|
_, ebx7, _, _ := cpuid(7, 0)
|
|
haveAVX2 := isSet(5, ebx7) && haveAVX
|
|
haveBMI2 := isSet(8, ebx7)
|
|
|
|
useAVX2 = haveAVX2 && haveBMI2
|
|
}
|
|
|
|
// isSet checks if bit at bitpos is set in value.
|
|
func isSet(bitpos uint, value uint32) bool {
|
|
return value&(1<<bitpos) != 0
|
|
}
|
|
|
|
// setupState writes a ChaCha20 input matrix to state. See
|
|
// https://tools.ietf.org/html/rfc7539#section-2.3.
|
|
func setupState(state *[16]uint32, key *[32]byte, nonce []byte) {
|
|
state[0] = 0x61707865
|
|
state[1] = 0x3320646e
|
|
state[2] = 0x79622d32
|
|
state[3] = 0x6b206574
|
|
|
|
state[4] = binary.LittleEndian.Uint32(key[:4])
|
|
state[5] = binary.LittleEndian.Uint32(key[4:8])
|
|
state[6] = binary.LittleEndian.Uint32(key[8:12])
|
|
state[7] = binary.LittleEndian.Uint32(key[12:16])
|
|
state[8] = binary.LittleEndian.Uint32(key[16:20])
|
|
state[9] = binary.LittleEndian.Uint32(key[20:24])
|
|
state[10] = binary.LittleEndian.Uint32(key[24:28])
|
|
state[11] = binary.LittleEndian.Uint32(key[28:32])
|
|
|
|
state[12] = 0
|
|
state[13] = binary.LittleEndian.Uint32(nonce[:4])
|
|
state[14] = binary.LittleEndian.Uint32(nonce[4:8])
|
|
state[15] = binary.LittleEndian.Uint32(nonce[8:12])
|
|
}
|
|
|
|
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
|
if !useASM {
|
|
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
|
}
|
|
|
|
var state [16]uint32
|
|
setupState(&state, &c.key, nonce)
|
|
|
|
ret, out := sliceForAppend(dst, len(plaintext)+16)
|
|
chacha20Poly1305Seal(out[:], state[:], plaintext, additionalData)
|
|
return ret
|
|
}
|
|
|
|
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
|
if !useASM {
|
|
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
|
}
|
|
|
|
var state [16]uint32
|
|
setupState(&state, &c.key, nonce)
|
|
|
|
ciphertext = ciphertext[:len(ciphertext)-16]
|
|
ret, out := sliceForAppend(dst, len(ciphertext))
|
|
if !chacha20Poly1305Open(out, state[:], ciphertext, additionalData) {
|
|
for i := range out {
|
|
out[i] = 0
|
|
}
|
|
return nil, errOpen
|
|
}
|
|
|
|
return ret, nil
|
|
}
|