mirror of
https://github.com/minio/minio.git
synced 2025-01-11 06:53:22 -05:00
add SSE-C support for HEAD, GET, PUT (#4894)
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
This commit is contained in:
parent
7e7ae29d89
commit
ca6b4773ed
16
appveyor.yml
16
appveyor.yml
@ -40,17 +40,19 @@ test_script:
|
||||
- mkdir build\coverage
|
||||
- go test -v -timeout 17m -race github.com/minio/minio/cmd...
|
||||
- go test -v -race github.com/minio/minio/pkg...
|
||||
- go test -v -timeout 17m -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd
|
||||
# FIXME(aead): enable codecov after issue https://github.com/golang/go/issues/18468 is solved.
|
||||
# - go test -v -timeout 17m -coverprofile=build\coverage\coverage.txt -covermode=atomic github.com/minio/minio/cmd
|
||||
- ps: Update-AppveyorTest "Unit Tests" -Outcome Passed
|
||||
|
||||
after_test:
|
||||
- go tool cover -html=build\coverage\coverage.txt -o build\coverage\coverage.html
|
||||
- ps: Push-AppveyorArtifact build\coverage\coverage.txt
|
||||
- ps: Push-AppveyorArtifact build\coverage\coverage.html
|
||||
# FIXME(aead): enable codecov after issue https://github.com/golang/go/issues/18468 is solved.
|
||||
# - go tool cover -html=build\coverage\coverage.txt -o build\coverage\coverage.html
|
||||
# - ps: Push-AppveyorArtifact build\coverage\coverage.txt
|
||||
# - ps: Push-AppveyorArtifact build\coverage\coverage.html
|
||||
# Upload coverage report.
|
||||
- "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
|
||||
- pip install codecov
|
||||
- codecov -X gcov -f "build\coverage\coverage.txt"
|
||||
# - "SET PATH=C:\\Python34;C:\\Python34\\Scripts;%PATH%"
|
||||
# - pip install codecov
|
||||
# - codecov -X gcov -f "build\coverage\coverage.txt"
|
||||
|
||||
# to disable deployment
|
||||
deploy: off
|
||||
|
@ -122,6 +122,17 @@ const (
|
||||
ErrUnsupportedMetadata
|
||||
// Add new error codes here.
|
||||
|
||||
// Server-Side-Encryption (with Customer provided key) related API errors.
|
||||
|
||||
ErrInsecureSSECustomerRequest
|
||||
ErrSSEEncryptedObject
|
||||
ErrInvalidEncryptionParameters
|
||||
ErrInvalidSSECustomerAlgorithm
|
||||
ErrInvalidSSECustomerKey
|
||||
ErrMissingSSECustomerKey
|
||||
ErrMissingSSECustomerKeyMD5
|
||||
ErrSSECustomerKeyMD5Mismatch
|
||||
|
||||
// Bucket notification related errors.
|
||||
ErrEventNotification
|
||||
ErrARNNotification
|
||||
@ -159,6 +170,7 @@ const (
|
||||
ErrAdminConfigNoQuorum
|
||||
ErrAdminCredentialsMismatch
|
||||
ErrInsecureClientRequest
|
||||
ErrObjectTampered
|
||||
)
|
||||
|
||||
// error code to APIError structure, these fields carry respective
|
||||
@ -574,6 +586,51 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
||||
Description: "Range specified is not valid for source object",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrMetadataTooLarge: {
|
||||
Code: "InvalidArgument",
|
||||
Description: "Your metadata headers exceed the maximum allowed metadata size.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrInsecureSSECustomerRequest: {
|
||||
Code: "InvalidRequest",
|
||||
Description: errInsecureSSERequest.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrSSEEncryptedObject: {
|
||||
Code: "InvalidRequest",
|
||||
Description: errEncryptedObject.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrInvalidEncryptionParameters: {
|
||||
Code: "InvalidRequest",
|
||||
Description: "The encryption parameters are not applicable to this object.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrInvalidSSECustomerAlgorithm: {
|
||||
Code: "InvalidArgument",
|
||||
Description: errInvalidSSEAlgorithm.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrInvalidSSECustomerKey: {
|
||||
Code: "InvalidArgument",
|
||||
Description: errInvalidSSEKey.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrMissingSSECustomerKey: {
|
||||
Code: "InvalidArgument",
|
||||
Description: errMissingSSEKey.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrMissingSSECustomerKeyMD5: {
|
||||
Code: "InvalidArgument",
|
||||
Description: errMissingSSEKeyMD5.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrSSECustomerKeyMD5Mismatch: {
|
||||
Code: "InvalidArgument",
|
||||
Description: errSSEKeyMD5Mismatch.Error(),
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
|
||||
/// S3 extensions.
|
||||
ErrContentSHA256Mismatch: {
|
||||
@ -653,11 +710,6 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
||||
Description: "A timeout occurred while trying to lock a resource",
|
||||
HTTPStatusCode: http.StatusRequestTimeout,
|
||||
},
|
||||
ErrMetadataTooLarge: {
|
||||
Code: "InvalidArgument",
|
||||
Description: "Your metadata headers exceed the maximum allowed metadata size.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrUnsupportedMetadata: {
|
||||
Code: "InvalidArgument",
|
||||
Description: "Your metadata headers are not supported.",
|
||||
@ -668,6 +720,11 @@ var errorCodeResponse = map[APIErrorCode]APIError{
|
||||
Description: "All parts except the last part should be of the same size.",
|
||||
HTTPStatusCode: http.StatusBadRequest,
|
||||
},
|
||||
ErrObjectTampered: {
|
||||
Code: "XMinioObjectTampered",
|
||||
Description: errObjectTampered.Error(),
|
||||
HTTPStatusCode: http.StatusPartialContent,
|
||||
},
|
||||
// Add your error structure here.
|
||||
}
|
||||
|
||||
@ -699,6 +756,27 @@ func toAPIErrorCode(err error) (apiErr APIErrorCode) {
|
||||
return apiErr
|
||||
}
|
||||
|
||||
switch err { // SSE errors
|
||||
case errInsecureSSERequest:
|
||||
return ErrInsecureSSECustomerRequest
|
||||
case errInvalidSSEAlgorithm:
|
||||
return ErrInvalidSSECustomerAlgorithm
|
||||
case errInvalidSSEKey:
|
||||
return ErrInvalidSSECustomerKey
|
||||
case errMissingSSEKey:
|
||||
return ErrMissingSSECustomerKey
|
||||
case errMissingSSEKeyMD5:
|
||||
return ErrMissingSSECustomerKeyMD5
|
||||
case errSSEKeyMD5Mismatch:
|
||||
return ErrSSECustomerKeyMD5Mismatch
|
||||
case errObjectTampered:
|
||||
return ErrObjectTampered
|
||||
case errEncryptedObject:
|
||||
return ErrSSEEncryptedObject
|
||||
case errSSEKeyMismatch:
|
||||
return ErrAccessDenied // no access without correct key
|
||||
}
|
||||
|
||||
switch err.(type) {
|
||||
case StorageFull:
|
||||
apiErr = ErrStorageFull
|
||||
|
@ -23,116 +23,48 @@ import (
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
)
|
||||
|
||||
func TestAPIErrCode(t *testing.T) {
|
||||
testCases := []struct {
|
||||
var toAPIErrorCodeTests = []struct {
|
||||
err error
|
||||
errCode APIErrorCode
|
||||
}{
|
||||
// Valid cases.
|
||||
{
|
||||
hash.BadDigest{},
|
||||
ErrBadDigest,
|
||||
},
|
||||
{
|
||||
hash.SHA256Mismatch{},
|
||||
ErrContentSHA256Mismatch,
|
||||
},
|
||||
{
|
||||
IncompleteBody{},
|
||||
ErrIncompleteBody,
|
||||
},
|
||||
{
|
||||
ObjectExistsAsDirectory{},
|
||||
ErrObjectExistsAsDirectory,
|
||||
},
|
||||
{
|
||||
BucketNameInvalid{},
|
||||
ErrInvalidBucketName,
|
||||
},
|
||||
{
|
||||
BucketExists{},
|
||||
ErrBucketAlreadyOwnedByYou,
|
||||
},
|
||||
{
|
||||
ObjectNotFound{},
|
||||
ErrNoSuchKey,
|
||||
},
|
||||
{
|
||||
ObjectNameInvalid{},
|
||||
ErrInvalidObjectName,
|
||||
},
|
||||
{
|
||||
InvalidUploadID{},
|
||||
ErrNoSuchUpload,
|
||||
},
|
||||
{
|
||||
InvalidPart{},
|
||||
ErrInvalidPart,
|
||||
},
|
||||
{
|
||||
InsufficientReadQuorum{},
|
||||
ErrReadQuorum,
|
||||
},
|
||||
{
|
||||
InsufficientWriteQuorum{},
|
||||
ErrWriteQuorum,
|
||||
},
|
||||
{
|
||||
UnsupportedDelimiter{},
|
||||
ErrNotImplemented,
|
||||
},
|
||||
{
|
||||
InvalidMarkerPrefixCombination{},
|
||||
ErrNotImplemented,
|
||||
},
|
||||
{
|
||||
InvalidUploadIDKeyCombination{},
|
||||
ErrNotImplemented,
|
||||
},
|
||||
{
|
||||
MalformedUploadID{},
|
||||
ErrNoSuchUpload,
|
||||
},
|
||||
{
|
||||
PartTooSmall{},
|
||||
ErrEntityTooSmall,
|
||||
},
|
||||
{
|
||||
BucketNotEmpty{},
|
||||
ErrBucketNotEmpty,
|
||||
},
|
||||
{
|
||||
BucketNotFound{},
|
||||
ErrNoSuchBucket,
|
||||
},
|
||||
{
|
||||
StorageFull{},
|
||||
ErrStorageFull,
|
||||
},
|
||||
{
|
||||
NotImplemented{},
|
||||
ErrNotImplemented,
|
||||
},
|
||||
{
|
||||
errSignatureMismatch,
|
||||
ErrSignatureDoesNotMatch,
|
||||
}, // End of all valid cases.
|
||||
}{
|
||||
{err: hash.BadDigest{}, errCode: ErrBadDigest},
|
||||
{err: hash.SHA256Mismatch{}, errCode: ErrContentSHA256Mismatch},
|
||||
{err: IncompleteBody{}, errCode: ErrIncompleteBody},
|
||||
{err: ObjectExistsAsDirectory{}, errCode: ErrObjectExistsAsDirectory},
|
||||
{err: BucketNameInvalid{}, errCode: ErrInvalidBucketName},
|
||||
{err: BucketExists{}, errCode: ErrBucketAlreadyOwnedByYou},
|
||||
{err: ObjectNotFound{}, errCode: ErrNoSuchKey},
|
||||
{err: ObjectNameInvalid{}, errCode: ErrInvalidObjectName},
|
||||
{err: InvalidUploadID{}, errCode: ErrNoSuchUpload},
|
||||
{err: InvalidPart{}, errCode: ErrInvalidPart},
|
||||
{err: InsufficientReadQuorum{}, errCode: ErrReadQuorum},
|
||||
{err: InsufficientWriteQuorum{}, errCode: ErrWriteQuorum},
|
||||
{err: UnsupportedDelimiter{}, errCode: ErrNotImplemented},
|
||||
{err: InvalidMarkerPrefixCombination{}, errCode: ErrNotImplemented},
|
||||
{err: InvalidUploadIDKeyCombination{}, errCode: ErrNotImplemented},
|
||||
{err: MalformedUploadID{}, errCode: ErrNoSuchUpload},
|
||||
{err: PartTooSmall{}, errCode: ErrEntityTooSmall},
|
||||
{err: BucketNotEmpty{}, errCode: ErrBucketNotEmpty},
|
||||
{err: BucketNotFound{}, errCode: ErrNoSuchBucket},
|
||||
{err: StorageFull{}, errCode: ErrStorageFull},
|
||||
{err: NotImplemented{}, errCode: ErrNotImplemented},
|
||||
{err: errSignatureMismatch, errCode: ErrSignatureDoesNotMatch},
|
||||
|
||||
// Case where err is nil.
|
||||
{
|
||||
nil,
|
||||
ErrNone,
|
||||
},
|
||||
// SSE-C errors
|
||||
{err: errInsecureSSERequest, errCode: ErrInsecureSSECustomerRequest},
|
||||
{err: errInvalidSSEAlgorithm, errCode: ErrInvalidSSECustomerAlgorithm},
|
||||
{err: errMissingSSEKey, errCode: ErrMissingSSECustomerKey},
|
||||
{err: errInvalidSSEKey, errCode: ErrInvalidSSECustomerKey},
|
||||
{err: errMissingSSEKeyMD5, errCode: ErrMissingSSECustomerKeyMD5},
|
||||
{err: errSSEKeyMD5Mismatch, errCode: ErrSSECustomerKeyMD5Mismatch},
|
||||
{err: errObjectTampered, errCode: ErrObjectTampered},
|
||||
|
||||
// Case where err type is unknown.
|
||||
{
|
||||
errors.New("Custom error"),
|
||||
ErrInternalError,
|
||||
},
|
||||
}
|
||||
{err: nil, errCode: ErrNone},
|
||||
{err: errors.New("Custom error"), errCode: ErrInternalError}, // Case where err type is unknown.
|
||||
}
|
||||
|
||||
// Validate all the errors with their API error codes.
|
||||
for i, testCase := range testCases {
|
||||
func TestAPIErrCode(t *testing.T) {
|
||||
for i, testCase := range toAPIErrorCodeTests {
|
||||
errCode := toAPIErrorCode(testCase.err)
|
||||
if errCode != testCase.errCode {
|
||||
t.Errorf("Test %d: Expected error code %d, got %d", i+1, testCase.errCode, errCode)
|
||||
|
300
cmd/encryption-v1.go
Normal file
300
cmd/encryption-v1.go
Normal file
@ -0,0 +1,300 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/hmac"
|
||||
"crypto/md5"
|
||||
"crypto/rand"
|
||||
"encoding/base64"
|
||||
"errors"
|
||||
"io"
|
||||
"net/http"
|
||||
|
||||
sha256 "github.com/minio/sha256-simd"
|
||||
"github.com/minio/sio"
|
||||
)
|
||||
|
||||
var (
|
||||
// AWS errors for invalid SSE-C requests.
|
||||
errInsecureSSERequest = errors.New("Requests specifying Server Side Encryption with Customer provided keys must be made over a secure connection")
|
||||
errEncryptedObject = errors.New("The object was stored using a form of Server Side Encryption. The correct parameters must be provided to retrieve the object")
|
||||
errInvalidSSEAlgorithm = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide a valid encryption algorithm")
|
||||
errMissingSSEKey = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide an appropriate secret key")
|
||||
errInvalidSSEKey = errors.New("The secret key was invalid for the specified algorithm")
|
||||
errMissingSSEKeyMD5 = errors.New("Requests specifying Server Side Encryption with Customer provided keys must provide the client calculated MD5 of the secret key")
|
||||
errSSEKeyMD5Mismatch = errors.New("The calculated MD5 hash of the key did not match the hash that was provided")
|
||||
errSSEKeyMismatch = errors.New("The client provided key does not match the key provided when the object was encrypted") // this msg is not shown to the client
|
||||
|
||||
// Additional Minio errors for SSE-C requests.
|
||||
errObjectTampered = errors.New("The requested object was modified and may be compromised")
|
||||
)
|
||||
|
||||
const (
|
||||
// SSECustomerAlgorithm is the AWS SSE-C algorithm HTTP header key.
|
||||
SSECustomerAlgorithm = "X-Amz-Server-Side-Encryption-Customer-Algorithm"
|
||||
// SSECustomerKey is the AWS SSE-C encryption key HTTP header key.
|
||||
SSECustomerKey = "X-Amz-Server-Side-Encryption-Customer-Key"
|
||||
// SSECustomerKeyMD5 is the AWS SSE-C encryption key MD5 HTTP header key.
|
||||
SSECustomerKeyMD5 = "X-Amz-Server-Side-Encryption-Customer-Key-MD5"
|
||||
)
|
||||
|
||||
const (
|
||||
// SSECustomerKeySize is the size of valid client provided encryption keys in bytes.
|
||||
// Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes.
|
||||
SSECustomerKeySize = 32
|
||||
|
||||
// SSECustomerAlgorithmAES256 the only valid S3 SSE-C encryption algorithm identifier.
|
||||
SSECustomerAlgorithmAES256 = "AES256"
|
||||
)
|
||||
|
||||
// SSE-C key derivation:
|
||||
// H: Hash function, M: MAC function
|
||||
//
|
||||
// key := 32 bytes # client provided key
|
||||
// r := H(random(32 bytes)) # saved as object metadata [ServerSideEncryptionIV]
|
||||
// key_mac := M(H(key), r) # saved as object metadata [ServerSideEncryptionKeyMAC]
|
||||
// enc_key := M(key, key_mac)
|
||||
//
|
||||
//
|
||||
// SSE-C key verification:
|
||||
// H: Hash function, M: MAC function
|
||||
//
|
||||
// key := 32 bytes # client provided key
|
||||
// r := object metadata [ServerSideEncryptionIV]
|
||||
// key_mac := object metadata [ServerSideEncryptionKeyMAC]
|
||||
// key_mac' := M(H(key), r)
|
||||
//
|
||||
// check: key_mac != key_mac' => fail with invalid key
|
||||
//
|
||||
// enc_key := M(key, key_mac')
|
||||
const (
|
||||
// ServerSideEncryptionIV is a 32 byte randomly generated IV used to derive an
|
||||
// unique encryption key from the client provided key. The combination of this value
|
||||
// and the client-provided key must be unique to provide the DARE tamper-proof property.
|
||||
ServerSideEncryptionIV = ReservedMetadataPrefix + "Server-Side-Encryption-Iv"
|
||||
|
||||
// ServerSideEncryptionKDF is the combination of a hash and MAC function used to derive
|
||||
// the SSE-C encryption key from the user-provided key.
|
||||
ServerSideEncryptionKDF = ReservedMetadataPrefix + "Server-Side-Encryption-Kdf"
|
||||
|
||||
// ServerSideEncryptionKeyMAC is the MAC of the hash of the client-provided key and the
|
||||
// X-Minio-Server-Side-Encryption-Iv. This value must be used to verify that the client
|
||||
// provided the correct key to follow S3 spec.
|
||||
ServerSideEncryptionKeyMAC = ReservedMetadataPrefix + "Server-Side-Encryption-Key-Mac"
|
||||
)
|
||||
|
||||
// SSEKeyDerivationHmacSha256 specifies SHA-256 as hash function and HMAC-SHA256 as MAC function
|
||||
// as the functions used to derive the SSE-C encryption keys from the client-provided key.
|
||||
const SSEKeyDerivationHmacSha256 = "HMAC-SHA256"
|
||||
|
||||
// IsSSECustomerRequest returns true if the given HTTP header
|
||||
// contains server-side-encryption with customer provided key fields.
|
||||
func IsSSECustomerRequest(header http.Header) bool {
|
||||
return header.Get(SSECustomerAlgorithm) != "" || header.Get(SSECustomerKey) != "" || header.Get(SSECustomerKeyMD5) != ""
|
||||
}
|
||||
|
||||
// ParseSSECustomerRequest parses the SSE-C header fields of the provided request.
|
||||
// It returns the client provided key on success.
|
||||
func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) {
|
||||
if !globalIsSSL { // minio only supports HTTP or HTTPS requests not both at the same time
|
||||
// we cannot use r.TLS == nil here because Go's http implementation reflects on
|
||||
// the net.Conn and sets the TLS field of http.Request only if it's an tls.Conn.
|
||||
// Minio uses a BufConn (wrapping a tls.Conn) so the type check within the http package
|
||||
// will always fail -> r.TLS is always nil even for TLS requests.
|
||||
return nil, errInsecureSSERequest
|
||||
}
|
||||
header := r.Header
|
||||
if algorithm := header.Get(SSECustomerAlgorithm); algorithm != SSECustomerAlgorithmAES256 {
|
||||
return nil, errInvalidSSEAlgorithm
|
||||
}
|
||||
if header.Get(SSECustomerKey) == "" {
|
||||
return nil, errMissingSSEKey
|
||||
}
|
||||
if header.Get(SSECustomerKeyMD5) == "" {
|
||||
return nil, errMissingSSEKeyMD5
|
||||
}
|
||||
|
||||
key, err = base64.StdEncoding.DecodeString(header.Get(SSECustomerKey))
|
||||
if err != nil {
|
||||
return nil, errInvalidSSEKey
|
||||
}
|
||||
header.Del(SSECustomerKey) // make sure we do not save the key by accident
|
||||
|
||||
if len(key) != SSECustomerKeySize {
|
||||
return nil, errInvalidSSEKey
|
||||
}
|
||||
|
||||
keyMD5, err := base64.StdEncoding.DecodeString(header.Get(SSECustomerKeyMD5))
|
||||
if err != nil {
|
||||
return nil, errSSEKeyMD5Mismatch
|
||||
}
|
||||
if md5Sum := md5.Sum(key); !bytes.Equal(md5Sum[:], keyMD5) {
|
||||
return nil, errSSEKeyMD5Mismatch
|
||||
}
|
||||
return key, nil
|
||||
}
|
||||
|
||||
// EncryptRequest takes the client provided content and encrypts the data
|
||||
// with the client provided key. It also marks the object as client-side-encrypted
|
||||
// and sets the correct headers.
|
||||
func EncryptRequest(content io.Reader, r *http.Request, metadata map[string]string) (io.Reader, error) {
|
||||
key, err := ParseSSECustomerRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
||||
|
||||
// security notice:
|
||||
// Reusing a tuple (nonce, client provided key) will produce the same encryption key
|
||||
// twice and breaks the tamper-proof property. However objects are still confidential.
|
||||
// Therefore the nonce must be unique but need not to be undistinguishable from true
|
||||
// randomness.
|
||||
nonce := make([]byte, 32) // generate random nonce to derive encryption key
|
||||
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
iv := sha256.Sum256(nonce) // hash output to not reveal any stat. weaknesses of the PRNG
|
||||
|
||||
keyHash := sha256.Sum256(key) // derive MAC of the client-provided key
|
||||
mac := hmac.New(sha256.New, keyHash[:])
|
||||
mac.Write(iv[:])
|
||||
keyMAC := mac.Sum(nil)
|
||||
|
||||
mac = hmac.New(sha256.New, key) // derive encryption key
|
||||
mac.Write(keyMAC)
|
||||
reader, err := sio.EncryptReader(content, sio.Config{Key: mac.Sum(nil)})
|
||||
if err != nil {
|
||||
return nil, errInvalidSSEKey
|
||||
}
|
||||
|
||||
metadata[ServerSideEncryptionIV] = base64.StdEncoding.EncodeToString(iv[:])
|
||||
metadata[ServerSideEncryptionKDF] = SSEKeyDerivationHmacSha256
|
||||
metadata[ServerSideEncryptionKeyMAC] = base64.StdEncoding.EncodeToString(keyMAC)
|
||||
return reader, nil
|
||||
}
|
||||
|
||||
// DecryptRequest decrypts the object with the client provided key. It also removes
|
||||
// the client-side-encryption metadata from the object and sets the correct headers.
|
||||
func DecryptRequest(client io.Writer, r *http.Request, metadata map[string]string) (io.WriteCloser, error) {
|
||||
key, err := ParseSSECustomerRequest(r)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
delete(metadata, SSECustomerKey) // make sure we do not save the key by accident
|
||||
|
||||
if metadata[ServerSideEncryptionKDF] != SSEKeyDerivationHmacSha256 { // currently HMAC-SHA256 is the only option
|
||||
return nil, errObjectTampered
|
||||
}
|
||||
nonce, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionIV])
|
||||
if err != nil || len(nonce) != 32 {
|
||||
return nil, errObjectTampered
|
||||
}
|
||||
keyMAC, err := base64.StdEncoding.DecodeString(metadata[ServerSideEncryptionKeyMAC])
|
||||
if err != nil || len(keyMAC) != 32 {
|
||||
return nil, errObjectTampered
|
||||
}
|
||||
|
||||
keyHash := sha256.Sum256(key) // verify that client provided correct key
|
||||
mac := hmac.New(sha256.New, keyHash[:])
|
||||
mac.Write(nonce)
|
||||
if !hmac.Equal(keyMAC, mac.Sum(nil)) {
|
||||
return nil, errSSEKeyMismatch // client-provided key is wrong or object metadata was modified
|
||||
}
|
||||
|
||||
mac = hmac.New(sha256.New, key) // derive decryption key
|
||||
mac.Write(keyMAC)
|
||||
writer, err := sio.DecryptWriter(client, sio.Config{Key: mac.Sum(nil)})
|
||||
if err != nil {
|
||||
return nil, errInvalidSSEKey
|
||||
}
|
||||
|
||||
delete(metadata, ServerSideEncryptionIV)
|
||||
delete(metadata, ServerSideEncryptionKDF)
|
||||
delete(metadata, ServerSideEncryptionKeyMAC)
|
||||
return writer, nil
|
||||
}
|
||||
|
||||
// IsEncrypted returns true if the object is marked as encrypted.
|
||||
func (o *ObjectInfo) IsEncrypted() bool {
|
||||
if _, ok := o.UserDefined[ServerSideEncryptionIV]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := o.UserDefined[ServerSideEncryptionKDF]; ok {
|
||||
return true
|
||||
}
|
||||
if _, ok := o.UserDefined[ServerSideEncryptionKeyMAC]; ok {
|
||||
return true
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// DecryptedSize returns the size of the object after decryption in bytes.
|
||||
// It returns an error if the object is not encrypted or marked as encrypted
|
||||
// but has an invalid size.
|
||||
// DecryptedSize panics if the referred object is not encrypted.
|
||||
func (o *ObjectInfo) DecryptedSize() (int64, error) {
|
||||
if !o.IsEncrypted() {
|
||||
panic("cannot compute decrypted size of an object which is not encrypted")
|
||||
}
|
||||
if o.Size == 0 {
|
||||
return o.Size, nil
|
||||
}
|
||||
size := (o.Size / (32 + 64*1024)) * (64 * 1024)
|
||||
if mod := o.Size % (32 + 64*1024); mod > 0 {
|
||||
if mod < 33 {
|
||||
return -1, errObjectTampered // object is not 0 size but smaller than the smallest valid encrypted object
|
||||
}
|
||||
size += mod - 32
|
||||
}
|
||||
return size, nil
|
||||
}
|
||||
|
||||
// EncryptedSize returns the size of the object after encryption.
|
||||
// An encrypted object is always larger than a plain object
|
||||
// except for zero size objects.
|
||||
func (o *ObjectInfo) EncryptedSize() int64 {
|
||||
size := (o.Size / (64 * 1024)) * (32 + 64*1024)
|
||||
if mod := o.Size % (64 * 1024); mod > 0 {
|
||||
size += mod + 32
|
||||
}
|
||||
return size
|
||||
}
|
||||
|
||||
// DecryptObjectInfo tries to decrypt the provided object if it is encrypted.
|
||||
// It fails if the object is encrypted and the HTTP headers don't contain
|
||||
// SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior)
|
||||
// DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the
|
||||
// decryption succeeded.
|
||||
//
|
||||
// DecryptObjectInfo also returns whether the object is encrypted or not.
|
||||
func DecryptObjectInfo(info *ObjectInfo, headers http.Header) (apiErr APIErrorCode, encrypted bool) {
|
||||
if apiErr, encrypted = ErrNone, info.IsEncrypted(); !encrypted && IsSSECustomerRequest(headers) {
|
||||
apiErr = ErrInvalidEncryptionParameters
|
||||
} else if encrypted {
|
||||
if !IsSSECustomerRequest(headers) {
|
||||
apiErr = ErrSSEEncryptedObject
|
||||
return
|
||||
}
|
||||
var err error
|
||||
if info.Size, err = info.DecryptedSize(); err != nil {
|
||||
apiErr = toAPIErrorCode(err)
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
390
cmd/encryption-v1_test.go
Normal file
390
cmd/encryption-v1_test.go
Normal file
@ -0,0 +1,390 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package cmd
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"net/http"
|
||||
"testing"
|
||||
)
|
||||
|
||||
var isSSECustomerRequestTests = []struct {
|
||||
headers map[string]string
|
||||
sseRequest bool
|
||||
}{
|
||||
{headers: map[string]string{SSECustomerAlgorithm: "AES256", SSECustomerKey: "key", SSECustomerKeyMD5: "md5"}, sseRequest: true}, // 0
|
||||
{headers: map[string]string{SSECustomerAlgorithm: "AES256"}, sseRequest: true}, // 1
|
||||
{headers: map[string]string{SSECustomerKey: "key"}, sseRequest: true}, // 2
|
||||
{headers: map[string]string{SSECustomerKeyMD5: "md5"}, sseRequest: true}, // 3
|
||||
{headers: map[string]string{}, sseRequest: false}, // 4
|
||||
{headers: map[string]string{SSECustomerAlgorithm + " ": "AES256", " " + SSECustomerKey: "key", SSECustomerKeyMD5 + " ": "md5"}, sseRequest: false}, // 5
|
||||
{headers: map[string]string{SSECustomerAlgorithm: "", SSECustomerKey: "", SSECustomerKeyMD5: ""}, sseRequest: false}, // 6
|
||||
}
|
||||
|
||||
func TestIsSSECustomerRequest(t *testing.T) {
|
||||
for i, test := range isSSECustomerRequestTests {
|
||||
headers := http.Header{}
|
||||
for k, v := range test.headers {
|
||||
headers.Set(k, v)
|
||||
}
|
||||
if IsSSECustomerRequest(headers) != test.sseRequest {
|
||||
t.Errorf("Test %d: Expected IsSSECustomerRequest to return %v", i, test.sseRequest)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var parseSSECustomerRequestTests = []struct {
|
||||
headers map[string]string
|
||||
useTLS bool
|
||||
err error
|
||||
}{
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=", // 0
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
useTLS: true, err: nil,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=", // 1
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
useTLS: false, err: errInsecureSSERequest,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES 256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=", // 2
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
useTLS: true, err: errInvalidSSEAlgorithm,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "NjE0SL87s+ZhYtaTrg5eI5cjhCQLGPVMKenPG2bCJFw=", // 3
|
||||
SSECustomerKeyMD5: "H+jq/LwEOEO90YtiTuNFVw==",
|
||||
},
|
||||
useTLS: true, err: errSSEKeyMD5Mismatch,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: " jE0SL87s+ZhYtaTrg5eI5cjhCQLGPVMKenPG2bCJFw=", // 4
|
||||
SSECustomerKeyMD5: "H+jq/LwEOEO90YtiTuNFVw==",
|
||||
},
|
||||
useTLS: true, err: errInvalidSSEKey,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "NjE0SL87s+ZhYtaTrg5eI5cjhCQLGPVMKenPG2bCJFw=", // 5
|
||||
SSECustomerKeyMD5: " +jq/LwEOEO90YtiTuNFVw==",
|
||||
},
|
||||
useTLS: true, err: errSSEKeyMD5Mismatch,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "vFQ9ScFOF6Tu/BfzMS+rVMvlZGJHi5HmGJenJfrfKI45", // 6
|
||||
SSECustomerKeyMD5: "9KPgDdZNTHimuYCwnJTp5g==",
|
||||
},
|
||||
useTLS: true, err: errInvalidSSEKey,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "", // 7
|
||||
SSECustomerKeyMD5: "9KPgDdZNTHimuYCwnJTp5g==",
|
||||
},
|
||||
useTLS: true, err: errMissingSSEKey,
|
||||
},
|
||||
{
|
||||
headers: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "vFQ9ScFOF6Tu/BfzMS+rVMvlZGJHi5HmGJenJfrfKI45", // 8
|
||||
SSECustomerKeyMD5: "",
|
||||
},
|
||||
useTLS: true, err: errMissingSSEKeyMD5,
|
||||
},
|
||||
}
|
||||
|
||||
func TestParseSSECustomerRequest(t *testing.T) {
|
||||
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL)
|
||||
for i, test := range parseSSECustomerRequestTests {
|
||||
headers := http.Header{}
|
||||
for k, v := range test.headers {
|
||||
headers.Set(k, v)
|
||||
}
|
||||
request := &http.Request{}
|
||||
request.Header = headers
|
||||
globalIsSSL = test.useTLS
|
||||
|
||||
_, err := ParseSSECustomerRequest(request)
|
||||
if err != test.err {
|
||||
t.Errorf("Test %d: Parse returned: %v want: %v", i, err, test.err)
|
||||
}
|
||||
key := request.Header.Get(SSECustomerKey)
|
||||
if (err == nil || err == errSSEKeyMD5Mismatch) && key != "" {
|
||||
t.Errorf("Test %d: Client key survived parsing - found key: %v", i, key)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var encryptedSizeTests = []struct {
|
||||
size, encsize int64
|
||||
}{
|
||||
{size: 0, encsize: 0}, // 0
|
||||
{size: 1, encsize: 33}, // 1
|
||||
{size: 1024, encsize: 1024 + 32}, // 2
|
||||
{size: 2 * 64 * 1024, encsize: 2 * (64*1024 + 32)}, // 3
|
||||
{size: 100*64*1024 + 1, encsize: 100*(64*1024+32) + 33}, // 4
|
||||
{size: 64*1024 + 1, encsize: (64*1024 + 32) + 33}, // 5
|
||||
{size: 5 * 1024 * 1024 * 1024, encsize: 81920 * (64*1024 + 32)}, // 6
|
||||
}
|
||||
|
||||
func TestEncryptedSize(t *testing.T) {
|
||||
for i, test := range encryptedSizeTests {
|
||||
objInfo := ObjectInfo{Size: test.size}
|
||||
if size := objInfo.EncryptedSize(); test.encsize != size {
|
||||
t.Errorf("Test %d: got encrypted size: #%d want: #%d", i, size, test.encsize)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var decryptSSECustomerObjectInfoTests = []struct {
|
||||
encsize, size int64
|
||||
err error
|
||||
}{
|
||||
{encsize: 0, size: 0, err: nil}, // 0
|
||||
{encsize: 33, size: 1, err: nil}, // 1
|
||||
{encsize: 1024 + 32, size: 1024, err: nil}, // 2
|
||||
{encsize: 2 * (64*1024 + 32), size: 2 * 64 * 1024, err: nil}, // 3
|
||||
{encsize: 100*(64*1024+32) + 33, size: 100*64*1024 + 1, err: nil}, // 4
|
||||
{encsize: (64*1024 + 32) + 33, size: 64*1024 + 1, err: nil}, // 5
|
||||
{encsize: 81920 * (64*1024 + 32), size: 5 * 1024 * 1024 * 1024, err: nil}, // 6
|
||||
{encsize: 0, size: 0, err: nil}, // 7
|
||||
{encsize: 64*1024 + 32 + 31, size: 0, err: errObjectTampered}, // 8
|
||||
}
|
||||
|
||||
func TestDecryptedSize(t *testing.T) {
|
||||
for i, test := range decryptSSECustomerObjectInfoTests {
|
||||
objInfo := ObjectInfo{Size: test.encsize}
|
||||
objInfo.UserDefined = map[string]string{
|
||||
ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256,
|
||||
}
|
||||
|
||||
size, err := objInfo.DecryptedSize()
|
||||
if err != test.err || (size != test.size && err == nil) {
|
||||
t.Errorf("Test %d: decryption returned: %v want: %v", i, err, test.err)
|
||||
}
|
||||
if err == nil && size != test.size {
|
||||
t.Errorf("Test %d: got decrypted size: #%d want: #%d", i, size, test.size)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var encryptRequestTests = []struct {
|
||||
header map[string]string
|
||||
metadata map[string]string
|
||||
}{
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{},
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
},
|
||||
},
|
||||
}
|
||||
|
||||
func TestEncryptRequest(t *testing.T) {
|
||||
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL)
|
||||
globalIsSSL = true
|
||||
for i, test := range encryptRequestTests {
|
||||
content := bytes.NewReader(make([]byte, 64))
|
||||
req := &http.Request{Header: http.Header{}}
|
||||
for k, v := range test.header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
_, err := EncryptRequest(content, req, test.metadata)
|
||||
if err != nil {
|
||||
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
||||
}
|
||||
if key, ok := test.metadata[SSECustomerKey]; ok {
|
||||
t.Errorf("Test %d: Client provided key survived in metadata - key: %s", i, key)
|
||||
}
|
||||
if kdf, ok := test.metadata[ServerSideEncryptionKDF]; !ok {
|
||||
t.Errorf("Test %d: ServerSideEncryptionKDF must be part of metadata: %v", i, kdf)
|
||||
}
|
||||
if iv, ok := test.metadata[ServerSideEncryptionIV]; !ok {
|
||||
t.Errorf("Test %d: ServerSideEncryptionIV must be part of metadata: %v", i, iv)
|
||||
}
|
||||
if mac, ok := test.metadata[ServerSideEncryptionKeyMAC]; !ok {
|
||||
t.Errorf("Test %d: ServerSideEncryptionKeyMAC must be part of metadata: %v", i, mac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var decryptRequestTests = []struct {
|
||||
header map[string]string
|
||||
metadata map[string]string
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{
|
||||
ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256,
|
||||
ServerSideEncryptionIV: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
ServerSideEncryptionKeyMAC: "SY5E9AvI2tI7/nUrUAssIGE32Hcs4rR9z/CUuPqu5N4=",
|
||||
},
|
||||
shouldFail: false,
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{
|
||||
ServerSideEncryptionKDF: "HMAC-SHA3",
|
||||
ServerSideEncryptionIV: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
ServerSideEncryptionKeyMAC: "SY5E9AvI2tI7/nUrUAssIGE32Hcs4rR9z/CUuPqu5N4=",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{
|
||||
ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256,
|
||||
ServerSideEncryptionIV: "RrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
ServerSideEncryptionKeyMAC: "SY5E9AvI2tI7/nUrUAssIGE32Hcs4rR9z/CUuPqu5N4=",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
header: map[string]string{
|
||||
SSECustomerAlgorithm: "AES256",
|
||||
SSECustomerKey: "XAm0dRrJsEsyPb1UuFNezv1bl9hxuYsgUVC/MUctE2k=",
|
||||
SSECustomerKeyMD5: "bY4wkxQejw9mUJfo72k53A==",
|
||||
},
|
||||
metadata: map[string]string{
|
||||
ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256,
|
||||
ServerSideEncryptionIV: "XAm0dRrJsEsyPb1UuFNezv1bl9ehxuYsgUVC/MUctE2k=",
|
||||
ServerSideEncryptionKeyMAC: "SY5E9AvI2tI7/nUrUAssIGE32Hds4rR9z/CUuPqu5N4=",
|
||||
},
|
||||
shouldFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestDecryptRequest(t *testing.T) {
|
||||
defer func(flag bool) { globalIsSSL = flag }(globalIsSSL)
|
||||
globalIsSSL = true
|
||||
for i, test := range decryptRequestTests {
|
||||
client := bytes.NewBuffer(nil)
|
||||
req := &http.Request{Header: http.Header{}}
|
||||
for k, v := range test.header {
|
||||
req.Header.Set(k, v)
|
||||
}
|
||||
_, err := DecryptRequest(client, req, test.metadata)
|
||||
if err != nil && !test.shouldFail {
|
||||
t.Fatalf("Test %d: Failed to encrypt request: %v", i, err)
|
||||
}
|
||||
if key, ok := test.metadata[SSECustomerKey]; ok {
|
||||
t.Errorf("Test %d: Client provided key survived in metadata - key: %s", i, key)
|
||||
}
|
||||
if kdf, ok := test.metadata[ServerSideEncryptionKDF]; ok && !test.shouldFail {
|
||||
t.Errorf("Test %d: ServerSideEncryptionKDF should not be part of metadata: %v", i, kdf)
|
||||
}
|
||||
if iv, ok := test.metadata[ServerSideEncryptionIV]; ok && !test.shouldFail {
|
||||
t.Errorf("Test %d: ServerSideEncryptionIV should not be part of metadata: %v", i, iv)
|
||||
}
|
||||
if mac, ok := test.metadata[ServerSideEncryptionKeyMAC]; ok && !test.shouldFail {
|
||||
t.Errorf("Test %d: ServerSideEncryptionKeyMAC should not be part of metadata: %v", i, mac)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var decryptObjectInfoTests = []struct {
|
||||
info ObjectInfo
|
||||
headers http.Header
|
||||
expErr APIErrorCode
|
||||
}{
|
||||
{
|
||||
info: ObjectInfo{Size: 100},
|
||||
headers: http.Header{},
|
||||
expErr: ErrNone,
|
||||
},
|
||||
{
|
||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256}},
|
||||
headers: http.Header{SSECustomerAlgorithm: []string{SSECustomerAlgorithmAES256}},
|
||||
expErr: ErrNone,
|
||||
},
|
||||
{
|
||||
info: ObjectInfo{Size: 0, UserDefined: map[string]string{ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256}},
|
||||
headers: http.Header{SSECustomerAlgorithm: []string{SSECustomerAlgorithmAES256}},
|
||||
expErr: ErrNone,
|
||||
},
|
||||
{
|
||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256}},
|
||||
headers: http.Header{},
|
||||
expErr: ErrSSEEncryptedObject,
|
||||
},
|
||||
{
|
||||
info: ObjectInfo{Size: 100, UserDefined: map[string]string{}},
|
||||
headers: http.Header{SSECustomerAlgorithm: []string{SSECustomerAlgorithmAES256}},
|
||||
expErr: ErrInvalidEncryptionParameters,
|
||||
},
|
||||
{
|
||||
info: ObjectInfo{Size: 31, UserDefined: map[string]string{ServerSideEncryptionKDF: SSEKeyDerivationHmacSha256}},
|
||||
headers: http.Header{SSECustomerAlgorithm: []string{SSECustomerAlgorithmAES256}},
|
||||
expErr: ErrObjectTampered,
|
||||
},
|
||||
}
|
||||
|
||||
func TestDecryptObjectInfo(t *testing.T) {
|
||||
for i, test := range decryptObjectInfoTests {
|
||||
if err, encrypted := DecryptObjectInfo(&test.info, test.headers); err != test.expErr {
|
||||
t.Errorf("Test %d: Decryption returned wrong error code: got %d , want %d", i, err, test.expErr)
|
||||
} else if enc := test.info.IsEncrypted(); encrypted && enc != encrypted {
|
||||
t.Errorf("Test %d: Decryption thinks object is encrypted but it is not", i)
|
||||
} else if !encrypted && enc != encrypted {
|
||||
t.Errorf("Test %d: Decryption thinks object is not encrypted but it is", i)
|
||||
}
|
||||
}
|
||||
}
|
@ -958,7 +958,6 @@ func (l *gcsGateway) PutObjectPart(bucket string, key string, uploadID string, p
|
||||
}
|
||||
// Make sure to close the object writer upon success.
|
||||
w.Close()
|
||||
|
||||
return PartInfo{
|
||||
PartNumber: partNumber,
|
||||
ETag: etag,
|
||||
|
@ -18,7 +18,7 @@ package cmd
|
||||
|
||||
import (
|
||||
"io"
|
||||
"io/ioutil"
|
||||
goioutil "io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"strconv"
|
||||
@ -29,6 +29,7 @@ import (
|
||||
router "github.com/gorilla/mux"
|
||||
"github.com/minio/minio-go/pkg/policy"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
"github.com/minio/minio/pkg/ioutil"
|
||||
)
|
||||
|
||||
// GetObjectHandler - GET Object
|
||||
@ -118,32 +119,19 @@ func (api gatewayAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
startOffset = hrange.offsetBegin
|
||||
length = hrange.getLength()
|
||||
}
|
||||
// Indicates if any data was written to the http.ResponseWriter
|
||||
dataWritten := false
|
||||
// io.Writer type which keeps track if any data was written.
|
||||
writer := funcToWriter(func(p []byte) (int, error) {
|
||||
if !dataWritten {
|
||||
// Set headers on the first write.
|
||||
// Set standard object headers.
|
||||
setObjectHeaders(w, objInfo, hrange)
|
||||
|
||||
// Set any additional requested response headers.
|
||||
setHeadGetRespHeaders(w, r.URL.Query())
|
||||
|
||||
dataWritten = true
|
||||
}
|
||||
return w.Write(p)
|
||||
})
|
||||
|
||||
getObject := objectAPI.GetObject
|
||||
if reqAuthType == authTypeAnonymous {
|
||||
getObject = objectAPI.AnonGetObject
|
||||
}
|
||||
|
||||
setObjectHeaders(w, objInfo, hrange)
|
||||
setHeadGetRespHeaders(w, r.URL.Query())
|
||||
httpWriter := ioutil.WriteOnClose(w)
|
||||
// Reads the object at startOffset and writes to mw.
|
||||
if err = getObject(bucket, object, startOffset, length, writer); err != nil {
|
||||
if err = getObject(bucket, object, startOffset, length, httpWriter); err != nil {
|
||||
errorIf(err, "Unable to write to client.")
|
||||
if !dataWritten {
|
||||
if !httpWriter.HasWritten() {
|
||||
// Error response only if no data has been written to client yet. i.e if
|
||||
// partial data has already been written before an error
|
||||
// occurred then no point in setting StatusCode and
|
||||
@ -152,11 +140,11 @@ func (api gatewayAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
}
|
||||
return
|
||||
}
|
||||
if !dataWritten {
|
||||
// If ObjectAPI.GetObject did not return error and no data has
|
||||
// been written it would mean that it is a 0-byte object.
|
||||
// call wrter.Write(nil) to set appropriate headers.
|
||||
writer.Write(nil)
|
||||
if err = httpWriter.Close(); err != nil {
|
||||
if !httpWriter.HasWritten() { // write error response only if no data has been written to client yet
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get host and port from Request.RemoteAddr.
|
||||
@ -472,7 +460,7 @@ func (api gatewayAPIHandlers) PutBucketPolicyHandler(w http.ResponseWriter, r *h
|
||||
// Read access policy up to maxAccessPolicySize.
|
||||
// http://docs.aws.amazon.com/AmazonS3/latest/dev/access-policy-language-overview.html
|
||||
// bucket policies are limited to 20KB in size, using a limit reader.
|
||||
policyBytes, err := ioutil.ReadAll(io.LimitReader(r.Body, maxAccessPolicySize))
|
||||
policyBytes, err := goioutil.ReadAll(io.LimitReader(r.Body, maxAccessPolicySize))
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to read from client.")
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
|
@ -108,6 +108,40 @@ func isHTTPHeaderSizeTooLarge(header http.Header) bool {
|
||||
return false
|
||||
}
|
||||
|
||||
// ReservedMetadataPrefix is the prefix of a metadata key which
|
||||
// is reserved and for internal use only.
|
||||
const ReservedMetadataPrefix = "X-Minio-Internal-"
|
||||
|
||||
type reservedMetadataHandler struct {
|
||||
http.Handler
|
||||
}
|
||||
|
||||
func filterReservedMetadata(h http.Handler) http.Handler {
|
||||
return reservedMetadataHandler{h}
|
||||
}
|
||||
|
||||
// ServeHTTP fails if the request contains at least one reserved header which
|
||||
// would be treated as metadata.
|
||||
func (h reservedMetadataHandler) ServeHTTP(w http.ResponseWriter, r *http.Request) {
|
||||
if containsReservedMetadata(r.Header) {
|
||||
writeErrorResponse(w, ErrUnsupportedMetadata, r.URL)
|
||||
return
|
||||
}
|
||||
h.Handler.ServeHTTP(w, r)
|
||||
}
|
||||
|
||||
// containsReservedMetadata returns true if the http.Header contains
|
||||
// keys which are treated as metadata but are reserved for internal use
|
||||
// and must not set by clients
|
||||
func containsReservedMetadata(header http.Header) bool {
|
||||
for key := range header {
|
||||
if strings.HasPrefix(key, ReservedMetadataPrefix) {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
// Reserved bucket.
|
||||
const (
|
||||
minioReservedBucket = "minio"
|
||||
|
@ -144,3 +144,38 @@ func TestIsHTTPHeaderSizeTooLarge(t *testing.T) {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var containsReservedMetadataTests = []struct {
|
||||
header http.Header
|
||||
shouldFail bool
|
||||
}{
|
||||
{
|
||||
header: http.Header{"X-Minio-Key": []string{"value"}},
|
||||
},
|
||||
{
|
||||
header: http.Header{ServerSideEncryptionIV: []string{"iv"}},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
header: http.Header{ServerSideEncryptionKDF: []string{SSEKeyDerivationHmacSha256}},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
header: http.Header{ServerSideEncryptionKeyMAC: []string{"mac"}},
|
||||
shouldFail: true,
|
||||
},
|
||||
{
|
||||
header: http.Header{ReservedMetadataPrefix + "Key": []string{"value"}},
|
||||
shouldFail: true,
|
||||
},
|
||||
}
|
||||
|
||||
func TestContainsReservedMetadata(t *testing.T) {
|
||||
for i, test := range containsReservedMetadataTests {
|
||||
if contains := containsReservedMetadata(test.header); contains && !test.shouldFail {
|
||||
t.Errorf("Test %d: contains reserved header but should not fail", i)
|
||||
} else if !contains && test.shouldFail {
|
||||
t.Errorf("Test %d: does not contain reserved header but failed", i)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -19,7 +19,8 @@ package cmd
|
||||
import (
|
||||
"encoding/hex"
|
||||
"encoding/xml"
|
||||
"io/ioutil"
|
||||
"io"
|
||||
goioutil "io/ioutil"
|
||||
"net"
|
||||
"net/http"
|
||||
"net/url"
|
||||
@ -28,6 +29,7 @@ import (
|
||||
|
||||
mux "github.com/gorilla/mux"
|
||||
"github.com/minio/minio/pkg/hash"
|
||||
"github.com/minio/minio/pkg/ioutil"
|
||||
)
|
||||
|
||||
// supportedHeadGetReqParams - supported request parameters for GET and HEAD presigned request.
|
||||
@ -82,13 +84,6 @@ func errAllowableObjectNotFound(bucket string, r *http.Request) APIErrorCode {
|
||||
return ErrNoSuchKey
|
||||
}
|
||||
|
||||
// Simple way to convert a func to io.Writer type.
|
||||
type funcToWriter func([]byte) (int, error)
|
||||
|
||||
func (f funcToWriter) Write(p []byte) (int, error) {
|
||||
return f(p)
|
||||
}
|
||||
|
||||
// GetObjectHandler - GET Object
|
||||
// ----------
|
||||
// This implementation of the GET operation retrieves object. To use GET,
|
||||
@ -129,6 +124,10 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
writeErrorResponse(w, apiErr, r.URL)
|
||||
return
|
||||
}
|
||||
if apiErr, _ := DecryptObjectInfo(&objInfo, r.Header); apiErr != ErrNone {
|
||||
writeErrorResponse(w, apiErr, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Get request range.
|
||||
var hrange *httpRange
|
||||
@ -160,40 +159,41 @@ func (api objectAPIHandlers) GetObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
length = hrange.getLength()
|
||||
}
|
||||
|
||||
// Indicates if any data was written to the http.ResponseWriter
|
||||
dataWritten := false
|
||||
// io.Writer type which keeps track if any data was written.
|
||||
writer := funcToWriter(func(p []byte) (int, error) {
|
||||
if !dataWritten {
|
||||
// Set headers on the first write.
|
||||
// Set standard object headers.
|
||||
setObjectHeaders(w, objInfo, hrange)
|
||||
var writer io.Writer
|
||||
writer = w
|
||||
if IsSSECustomerRequest(r.Header) {
|
||||
writer, err = DecryptRequest(writer, r, objInfo.UserDefined)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
return
|
||||
}
|
||||
w.Header().Set(SSECustomerAlgorithm, r.Header.Get(SSECustomerAlgorithm))
|
||||
w.Header().Set(SSECustomerKeyMD5, r.Header.Get(SSECustomerKeyMD5))
|
||||
|
||||
// Set any additional requested response headers.
|
||||
if startOffset != 0 || length < objInfo.Size {
|
||||
writeErrorResponse(w, ErrNotImplemented, r.URL) // SSE-C requests with HTTP range are not supported yet
|
||||
return
|
||||
}
|
||||
length = objInfo.EncryptedSize()
|
||||
}
|
||||
|
||||
setObjectHeaders(w, objInfo, hrange)
|
||||
setHeadGetRespHeaders(w, r.URL.Query())
|
||||
|
||||
dataWritten = true
|
||||
}
|
||||
return w.Write(p)
|
||||
})
|
||||
|
||||
httpWriter := ioutil.WriteOnClose(writer)
|
||||
// Reads the object at startOffset and writes to mw.
|
||||
if err = objectAPI.GetObject(bucket, object, startOffset, length, writer); err != nil {
|
||||
if err = objectAPI.GetObject(bucket, object, startOffset, length, httpWriter); err != nil {
|
||||
errorIf(err, "Unable to write to client.")
|
||||
if !dataWritten {
|
||||
// Error response only if no data has been written to client yet. i.e if
|
||||
// partial data has already been written before an error
|
||||
// occurred then no point in setting StatusCode and
|
||||
// sending error XML.
|
||||
if !httpWriter.HasWritten() { // write error response only if no data has been written to client yet
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
}
|
||||
return
|
||||
}
|
||||
if !dataWritten {
|
||||
// If ObjectAPI.GetObject did not return error and no data has
|
||||
// been written it would mean that it is a 0-byte object.
|
||||
// call wrter.Write(nil) to set appropriate headers.
|
||||
writer.Write(nil)
|
||||
if err = httpWriter.Close(); err != nil {
|
||||
if !httpWriter.HasWritten() { // write error response only if no data has been written to client yet
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Get host and port from Request.RemoteAddr.
|
||||
@ -252,6 +252,15 @@ func (api objectAPIHandlers) HeadObjectHandler(w http.ResponseWriter, r *http.Re
|
||||
writeErrorResponseHeadersOnly(w, apiErr)
|
||||
return
|
||||
}
|
||||
if apiErr, encrypted := DecryptObjectInfo(&objInfo, r.Header); apiErr != ErrNone {
|
||||
writeErrorResponse(w, apiErr, r.URL)
|
||||
return
|
||||
} else if encrypted {
|
||||
if _, err = DecryptRequest(w, r, objInfo.UserDefined); err != nil {
|
||||
writeErrorResponse(w, ErrSSEEncryptedObject, r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
// Validate pre-conditions if any.
|
||||
if checkPreconditions(w, r, objInfo) {
|
||||
@ -530,9 +539,10 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
var (
|
||||
md5hex = hex.EncodeToString(md5Bytes)
|
||||
sha256hex = ""
|
||||
reader = r.Body
|
||||
reader io.Reader
|
||||
s3Err APIErrorCode
|
||||
)
|
||||
|
||||
reader = r.Body
|
||||
switch rAuthType {
|
||||
default:
|
||||
// For all unknown auth types return error.
|
||||
@ -541,31 +551,29 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
case authTypeAnonymous:
|
||||
// http://docs.aws.amazon.com/AmazonS3/latest/dev/using-with-s3-actions.html
|
||||
sourceIP := getSourceIPAddress(r)
|
||||
if s3Error := enforceBucketPolicy(bucket, "s3:PutObject", r.URL.Path,
|
||||
r.Referer(), sourceIP, r.URL.Query()); s3Error != ErrNone {
|
||||
writeErrorResponse(w, s3Error, r.URL)
|
||||
if s3Err = enforceBucketPolicy(bucket, "s3:PutObject", r.URL.Path, r.Referer(), sourceIP, r.URL.Query()); s3Err != ErrNone {
|
||||
writeErrorResponse(w, s3Err, r.URL)
|
||||
return
|
||||
}
|
||||
case authTypeStreamingSigned:
|
||||
// Initialize stream signature verifier.
|
||||
var s3Error APIErrorCode
|
||||
reader, s3Error = newSignV4ChunkedReader(r)
|
||||
if s3Error != ErrNone {
|
||||
reader, s3Err = newSignV4ChunkedReader(r)
|
||||
if s3Err != ErrNone {
|
||||
errorIf(errSignatureMismatch, "%s", dumpRequest(r))
|
||||
writeErrorResponse(w, s3Error, r.URL)
|
||||
writeErrorResponse(w, s3Err, r.URL)
|
||||
return
|
||||
}
|
||||
case authTypeSignedV2, authTypePresignedV2:
|
||||
s3Error := isReqAuthenticatedV2(r)
|
||||
if s3Error != ErrNone {
|
||||
s3Err = isReqAuthenticatedV2(r)
|
||||
if s3Err != ErrNone {
|
||||
errorIf(errSignatureMismatch, "%s", dumpRequest(r))
|
||||
writeErrorResponse(w, s3Error, r.URL)
|
||||
writeErrorResponse(w, s3Err, r.URL)
|
||||
return
|
||||
}
|
||||
case authTypePresigned, authTypeSigned:
|
||||
if s3Error := reqSignatureV4Verify(r, serverConfig.GetRegion()); s3Error != ErrNone {
|
||||
if s3Err = reqSignatureV4Verify(r, serverConfig.GetRegion()); s3Err != ErrNone {
|
||||
errorIf(errSignatureMismatch, "%s", dumpRequest(r))
|
||||
writeErrorResponse(w, s3Error, r.URL)
|
||||
writeErrorResponse(w, s3Err, r.URL)
|
||||
return
|
||||
}
|
||||
if !skipContentSha256Cksum(r) {
|
||||
@ -579,7 +587,20 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
return
|
||||
}
|
||||
|
||||
// Create the object..
|
||||
if IsSSECustomerRequest(r.Header) { // handle SSE-C requests
|
||||
reader, err = EncryptRequest(hashReader, r, metadata)
|
||||
if err != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
return
|
||||
}
|
||||
info := ObjectInfo{Size: size}
|
||||
hashReader, err = hash.NewReader(reader, info.EncryptedSize(), "", "") // do not try to verify encrypted content
|
||||
if err != nil {
|
||||
writeErrorResponse(w, toAPIErrorCode(err), r.URL)
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
objInfo, err := objectAPI.PutObject(bucket, object, hashReader, metadata)
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to create an object. %s", r.URL.Path)
|
||||
@ -587,6 +608,10 @@ func (api objectAPIHandlers) PutObjectHandler(w http.ResponseWriter, r *http.Req
|
||||
return
|
||||
}
|
||||
w.Header().Set("ETag", "\""+objInfo.ETag+"\"")
|
||||
if IsSSECustomerRequest(r.Header) {
|
||||
w.Header().Set(SSECustomerAlgorithm, r.Header.Get(SSECustomerAlgorithm))
|
||||
w.Header().Set(SSECustomerKeyMD5, r.Header.Get(SSECustomerKeyMD5))
|
||||
}
|
||||
writeSuccessResponseHeadersOnly(w)
|
||||
|
||||
// Get host and port from Request.RemoteAddr.
|
||||
@ -627,6 +652,12 @@ func (api objectAPIHandlers) NewMultipartUploadHandler(w http.ResponseWriter, r
|
||||
return
|
||||
}
|
||||
|
||||
if IsSSECustomerRequest(r.Header) { // handle SSE-C requests
|
||||
// SSE-C is not implemented for multipart operations yet
|
||||
writeErrorResponse(w, ErrNotImplemented, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Extract metadata that needs to be saved.
|
||||
metadata, err := extractMetadataFromHeader(r.Header)
|
||||
if err != nil {
|
||||
@ -666,6 +697,12 @@ func (api objectAPIHandlers) CopyObjectPartHandler(w http.ResponseWriter, r *htt
|
||||
return
|
||||
}
|
||||
|
||||
if IsSSECustomerRequest(r.Header) { // handle SSE-C requests
|
||||
// SSE-C is not implemented for multipart operations yet
|
||||
writeErrorResponse(w, ErrNotImplemented, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
// Copy source path.
|
||||
cpSrcPath, err := url.QueryUnescape(r.Header.Get("X-Amz-Copy-Source"))
|
||||
if err != nil {
|
||||
@ -784,6 +821,12 @@ func (api objectAPIHandlers) PutObjectPartHandler(w http.ResponseWriter, r *http
|
||||
return
|
||||
}
|
||||
|
||||
if IsSSECustomerRequest(r.Header) { // handle SSE-C requests
|
||||
// SSE-C is not implemented for multipart operations yet
|
||||
writeErrorResponse(w, ErrNotImplemented, r.URL)
|
||||
return
|
||||
}
|
||||
|
||||
/// if Content-Length is unknown/missing, throw away
|
||||
size := r.ContentLength
|
||||
|
||||
@ -977,7 +1020,7 @@ func (api objectAPIHandlers) CompleteMultipartUploadHandler(w http.ResponseWrite
|
||||
// Get upload id.
|
||||
uploadID, _, _, _ := getObjectResources(r.URL.Query())
|
||||
|
||||
completeMultipartBytes, err := ioutil.ReadAll(r.Body)
|
||||
completeMultipartBytes, err := goioutil.ReadAll(r.Body)
|
||||
if err != nil {
|
||||
errorIf(err, "Unable to complete multipart upload.")
|
||||
writeErrorResponse(w, ErrInternalError, r.URL)
|
||||
|
@ -112,6 +112,9 @@ func configureServerHandler(endpoints EndpointList) (http.Handler, error) {
|
||||
// routes them accordingly. Client receives a HTTP error for
|
||||
// invalid/unsupported signatures.
|
||||
setAuthHandler,
|
||||
// filters HTTP headers which are treated as metadata and are reserved
|
||||
// for internal use only.
|
||||
filterReservedMetadata,
|
||||
// Add new handlers here.
|
||||
}
|
||||
|
||||
|
61
pkg/ioutil/ioutil.go
Normal file
61
pkg/ioutil/ioutil.go
Normal file
@ -0,0 +1,61 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
// Package ioutil implements some I/O utility functions which are not covered
|
||||
// by the standard library.
|
||||
package ioutil
|
||||
|
||||
import (
|
||||
"io"
|
||||
)
|
||||
|
||||
// WriteOnCloser implements io.WriteCloser and always
|
||||
// exectues at least one write operation if it is closed.
|
||||
//
|
||||
// This can be useful within the context of HTTP. At least
|
||||
// one write operation must happen to send the HTTP headers
|
||||
// to the peer.
|
||||
type WriteOnCloser struct {
|
||||
io.Writer
|
||||
hasWritten bool
|
||||
}
|
||||
|
||||
func (w *WriteOnCloser) Write(p []byte) (int, error) {
|
||||
w.hasWritten = true
|
||||
return w.Writer.Write(p)
|
||||
}
|
||||
|
||||
// Close closes the WriteOnCloser. It behaves like io.Closer.
|
||||
func (w *WriteOnCloser) Close() error {
|
||||
if !w.hasWritten {
|
||||
_, err := w.Write(nil)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if closer, ok := w.Writer.(io.Closer); ok {
|
||||
return closer.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// HasWritten returns true if at least one write operation was performed.
|
||||
func (w *WriteOnCloser) HasWritten() bool { return w.hasWritten }
|
||||
|
||||
// WriteOnClose takes an io.Writer and returns an ioutil.WriteOnCloser.
|
||||
func WriteOnClose(w io.Writer) *WriteOnCloser {
|
||||
return &WriteOnCloser{w, false}
|
||||
}
|
39
pkg/ioutil/ioutil_test.go
Normal file
39
pkg/ioutil/ioutil_test.go
Normal file
@ -0,0 +1,39 @@
|
||||
/*
|
||||
* Minio Cloud Storage, (C) 2017 Minio, Inc.
|
||||
*
|
||||
* Licensed under the Apache License, Version 2.0 (the "License");
|
||||
* you may not use this file except in compliance with the License.
|
||||
* You may obtain a copy of the License at
|
||||
*
|
||||
* http://www.apache.org/licenses/LICENSE-2.0
|
||||
*
|
||||
* Unless required by applicable law or agreed to in writing, software
|
||||
* distributed under the License is distributed on an "AS IS" BASIS,
|
||||
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
* See the License for the specific language governing permissions and
|
||||
* limitations under the License.
|
||||
*/
|
||||
|
||||
package ioutil
|
||||
|
||||
import (
|
||||
goioutil "io/ioutil"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestCloseOnWriter(t *testing.T) {
|
||||
writer := WriteOnClose(goioutil.Discard)
|
||||
if writer.HasWritten() {
|
||||
t.Error("WriteOnCloser must not be marked as HasWritten")
|
||||
}
|
||||
writer.Write(nil)
|
||||
if !writer.HasWritten() {
|
||||
t.Error("WriteOnCloser must be marked as HasWritten")
|
||||
}
|
||||
|
||||
writer = WriteOnClose(goioutil.Discard)
|
||||
writer.Close()
|
||||
if !writer.HasWritten() {
|
||||
t.Error("WriteOnCloser must be marked as HasWritten")
|
||||
}
|
||||
}
|
223
vendor/github.com/minio/sio/DARE.md
generated
vendored
Normal file
223
vendor/github.com/minio/sio/DARE.md
generated
vendored
Normal file
@ -0,0 +1,223 @@
|
||||
# Data At Rest Encryption (DARE) - Version 1.0
|
||||
|
||||
**This is a draft**
|
||||
|
||||
## 1. Introduction
|
||||
|
||||
This document describes the Data At Rest Encryption (DARE) format for encrypting
|
||||
data in a tamper-resistant way. DARE is designed to securely encrypt data stored
|
||||
on (untrusted) storage providers.
|
||||
|
||||
## 2. Overview
|
||||
|
||||
DARE specifies how to split an arbitrary data stream into small chunks (packages)
|
||||
and concatenate them into a tamper-proof chain. Tamper-proof means that an attacker
|
||||
is not able to:
|
||||
- decrypt one or more packages.
|
||||
- modify the content of one or more packages.
|
||||
- reorder/rearrange one or more packages.
|
||||
|
||||
An attacker is defined as somebody who has full access to the encrypted data
|
||||
but not to the encryption key. An attacker can also act as storage provider.
|
||||
|
||||
### 2.1 Cryptographic Notation
|
||||
|
||||
DARE will use the following notations:
|
||||
- The set **{a,b}** means select **one** of the provided values **a**, **b**.
|
||||
- The concatenation of the byte sequences **a** and **b** is **a || b**.
|
||||
- The function **len(seq)** returns the length of a byte sequence **seq** in bytes.
|
||||
- The index access **seq[i]** accesses one byte at index **i** of the sequence **seq**.
|
||||
- The range access **seq[i : j]** accesses a range of bytes starting at **i** (inclusive)
|
||||
and ending at **j** (exclusive).
|
||||
- The compare functions **a == b => f** and **a != b => f** succeed when **a**
|
||||
is equal to **b** and **a** is not equal to **b** respectively and execute the command **f**.
|
||||
- The function **CTC(a, b)** returns **1** only if **a** and **b** are equal, 0 otherwise.
|
||||
CTC compares both values in **constant time**.
|
||||
- **ENC(key, nonce, plaintext, addData)** represents the byte sequence which is
|
||||
the output from an AEAD cipher authenticating the *addData*, encrypting and
|
||||
authenticating the *plaintext* with the secret encryption *key* and the *nonce*.
|
||||
- **DEC(key, nonce, ciphertext, addData)** represents the byte sequence which is
|
||||
the output from an AEAD cipher verifying the integrity of the *ciphertext* &
|
||||
*addData* and decrypting the *ciphertext* with the secret encryption *key* and
|
||||
the *nonce*. The decryption **always** fails if the integrity check fails.
|
||||
|
||||
All numbers must be converted into byte sequences by using the little endian byte
|
||||
order. An AEAD cipher will be either AES-256_GCM or CHACHA20_POLY1305.
|
||||
|
||||
## 2.2 Keys
|
||||
|
||||
Both ciphers - AES-256_GCM and CHACHA20_POLY1305 - require a 32 byte key. The key
|
||||
**must** be unique for one encrypted data stream. Reusing a key **compromises**
|
||||
some security properties provided by DARE. See Appendix A for recommendations
|
||||
about generating keys and preventing key reuse.
|
||||
|
||||
## 2.3 Errors
|
||||
|
||||
DARE defines the following errors:
|
||||
- **err_unsupported_version**: Indicates that the header version is not supported.
|
||||
- **err_unsupported_cipher**: Indicates that the cipher suite is not supported.
|
||||
- **err_missing_header**: Indicates that the payload header is missing or incomplete.
|
||||
- **err_payload_too_short**: Indicates that the actual payload size is smaller than the
|
||||
payload size field of the header.
|
||||
- **err_package_out_of_order**: Indicates that the sequence number of the package does
|
||||
not match the expected sequence number.
|
||||
- **err_tag_mismatch**: Indicates that the tag of the package does not match the tag
|
||||
computed while decrypting the package.
|
||||
|
||||
## 3. Package Format
|
||||
|
||||
DARE splits an arbitrary data stream into a sequence of packages. Each package is
|
||||
encrypted separately. A package consists of a header, a payload and an authentication
|
||||
tag.
|
||||
|
||||
Header | Payload | Tag
|
||||
---------|----------------|---------
|
||||
16 bytes | 1 byte - 64 KB | 16 bytes
|
||||
|
||||
The header contains information about the package. It consists of:
|
||||
|
||||
Version | Cipher suite | Payload size | Sequence number | nonce
|
||||
--------|--------------|------------------|------------------|---------
|
||||
1 byte | 1 byte | 2 bytes / uint16 | 4 bytes / uint32 | 8 bytes
|
||||
|
||||
The first byte specifies the version of the format and is equal to 0x10 for DARE
|
||||
version 1.0. The second byte specifies the cipher used to encrypt the package.
|
||||
|
||||
Cipher | Value
|
||||
------------------|-------
|
||||
AES-256_GCM | 0x00
|
||||
CHACHA20_POLY1305 | 0x01
|
||||
|
||||
The payload size is an uint16 number. The real payload size is defined as the payload
|
||||
size field as uint32 + 1. This ensures that the payload can be exactly 64 KB long and
|
||||
prevents empty packages without a payload.
|
||||
|
||||
The sequence number is an uint32 number identifying the package within a sequence of
|
||||
packages. It is a monotonically increasing number. The sequence number **must** be 0 for
|
||||
the first package and **must** be incremented for every subsequent package. The
|
||||
sequence number of the n-th package is n-1. This means a sequence of packages can consist
|
||||
of 2 ^ 32 packages and each package can hold up to 64 KB data. The maximum size
|
||||
of a data stream is limited by `64 KB * 2^32 = 256 TB`. This should be sufficient
|
||||
for current use cases. However, if necessary, the maximum size of a data stream can increased
|
||||
in the future by slightly tweaking the header (with a new version).
|
||||
|
||||
The nonce **should** be a random value for each data stream and **should** be kept constant
|
||||
for all its packages. Even if a key is accidentally used
|
||||
twice to encrypt two different data streams an attacker should not be able to decrypt one
|
||||
of those data streams. However, an attacker is always able to exchange corresponding packages
|
||||
between the streams whenever a key is reused. DARE is only tamper-proof when the encryption
|
||||
key is unique. See Appendix A.
|
||||
|
||||
The payload contains the encrypted data. It must be at least 1 byte long and can contain a maximum of 64 KB.
|
||||
|
||||
The authentication tag is generated by the AEAD cipher while encrypting and authenticating the
|
||||
package. The authentication tag **must** always be verified while decrypting the package.
|
||||
Decrypted content **must never** be returned before the authentication tag is successfully
|
||||
verified.
|
||||
|
||||
## 4. Encryption
|
||||
|
||||
DARE encrypts every package separately. The header version, cipher suite and nonce **should**
|
||||
be the same for all encrypted packages of one data stream. It is **recommended** to not change
|
||||
this values within one sequence of packages. The nonce **should** be generated randomly once
|
||||
at the beginning of the encryption process and repeated in every header. See Appendix B for
|
||||
recommendations about generating random numbers.
|
||||
|
||||
The sequence number is the sequence number of the previous package plus 1. The sequence number
|
||||
**must** be a monotonically increasing number within one sequence of packages. The sequence number
|
||||
of the first package is **always** 0.
|
||||
|
||||
The payload field is the length of the plaintext in bytes minus 1. The encryption process is
|
||||
defined as following:
|
||||
|
||||
```
|
||||
header[0] = 0x10
|
||||
header[1] = {AES-256_GCM, CHACHA20_POLY1305}
|
||||
header[2:4] = little_endian( len(plaintext) - 1 )
|
||||
header[4:8] = little_endian( sequence_number )
|
||||
header[8:16] = nonce
|
||||
|
||||
payload || tag = ENC(key, header[4:16], plaintext, header[0:4])
|
||||
|
||||
sequence_number = sequence_number + 1
|
||||
```
|
||||
|
||||
## 5. Decryption
|
||||
|
||||
DARE decrypts every package separately. Every package **must** be successfully decrypted **before**
|
||||
plaintext is returned. The decryption happens in three steps:
|
||||
|
||||
1. Verify that the header version is correct (`header[0] == 0x10`) and the cipher suite is supported.
|
||||
2. Verify that the sequence number of the packages matches the expected sequence number. It is required
|
||||
to save the first expected sequence number at the beginning of the decryption process. After every
|
||||
successfully decrypted package this sequence number is incremented by 1. The sequence number of
|
||||
all packages **must** match the saved / expected number.
|
||||
3. Verify that the authentication tag at the end of the package is equal to the authentication tag
|
||||
computed while decrypting the package. This **must** happen in constant time.
|
||||
|
||||
The decryption is defined as following:
|
||||
|
||||
```
|
||||
header[0] != 0x10 => err_unsupported_version
|
||||
header[1] != {AES-256_GCM,CHACHA20_POLY1305} => err_unsupported_cipher
|
||||
little_endian_uint32(header[4:8]) != expected_sequence_number => err_package_out_of_order
|
||||
|
||||
payload_size := little_endian_uint32(header[2:4]) + 1
|
||||
plaintext || tag := DEC(key, header[4:16], ciphertext, header[0:4])
|
||||
|
||||
CTC(ciphertext[len(plaintext) : len(plaintext) + 16], tag) != 1 => err_tag_mismatch
|
||||
|
||||
expected_sequence_number = expected_sequence_number + 1
|
||||
```
|
||||
|
||||
## Security
|
||||
|
||||
DARE provides confidentiality and integrity of the encrypted data as long as the encryption key
|
||||
is never reused. This means that a **different** encryption key **must** be used for every data
|
||||
stream. See Appendix A for recommendations.
|
||||
|
||||
If the same encryption key is used to encrypt two different data streams, an attacker is able to
|
||||
exchange packages with the same sequence number. This means that the attacker is able to replace
|
||||
any package of a sequence with another package as long as:
|
||||
- Both packages are encrypted with the same key.
|
||||
- The sequence numbers of both packages are equal.
|
||||
|
||||
If two data streams are encrypted with the same key the attacker will not be able to decrypt any
|
||||
package of those streams without breaking the cipher as long as the nonces are different. To be
|
||||
more precise the attacker may only be able to decrypt a package if:
|
||||
- There is another package encrypted with the same key.
|
||||
- The sequence number and nonce of those two packages (encrypted with the same key) are equal.
|
||||
|
||||
As long as the nonce of a sequence of packages differs from every other nonce (and the nonce is
|
||||
repeated within one sequence - which is **recommended**) the attacker will not be able to decrypt
|
||||
any package. It is not required that the nonce is indistinguishable from a truly random bit sequence.
|
||||
It is sufficient when the nonces differ from each other in at least one bit.
|
||||
|
||||
## Appendices
|
||||
|
||||
### Appendix A - Key Derivation from a Master Key
|
||||
|
||||
DARE needs a unique encryption key per data stream. The best approach to ensure that the keys
|
||||
are unique is to derive every encryption key from a master key. Therefore a key derivation function
|
||||
(KDF) - e.g. HKDF, BLAKE2X or HChaCha20 - can be used. The master key itself may be derived from
|
||||
a password using functions like Argon2 or scrypt. Deriving those keys is the responsibility of the
|
||||
users of DARE.
|
||||
|
||||
It is **not recommended** to derive encryption keys from a master key and an identifier (like the
|
||||
file path). If a different data stream is stored under the same identifier - e.g. overwriting the
|
||||
data - the derived key would be the same for both streams.
|
||||
|
||||
Instead encryption keys should be derived from a master key and a random value. It is not required
|
||||
that the random value is indistinguishable from a truly random bit sequence. The random value **must**
|
||||
be unique but need not be secret - depending on the security properties of the KDF.
|
||||
|
||||
To keep this simple: The combination of master key and random value used to derive the encryption key
|
||||
must be unique all the time.
|
||||
|
||||
### Appendix B - Generating random values
|
||||
|
||||
DARE does not require random values which are indistinguishable from a truly random bit sequence.
|
||||
However, a random value **must** never be repeated. Therefore it is **recommended** to use a
|
||||
cryptographically secure pseudorandom number generator (CSPRNG) to generate random values. Many
|
||||
operating systems and cryptographic libraries already provide appropriate PRNG implementations.
|
||||
These implementations should always be preferred over crafting a new one.
|
202
vendor/github.com/minio/sio/LICENSE
generated
vendored
Normal file
202
vendor/github.com/minio/sio/LICENSE
generated
vendored
Normal file
@ -0,0 +1,202 @@
|
||||
|
||||
Apache License
|
||||
Version 2.0, January 2004
|
||||
http://www.apache.org/licenses/
|
||||
|
||||
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
|
||||
|
||||
1. Definitions.
|
||||
|
||||
"License" shall mean the terms and conditions for use, reproduction,
|
||||
and distribution as defined by Sections 1 through 9 of this document.
|
||||
|
||||
"Licensor" shall mean the copyright owner or entity authorized by
|
||||
the copyright owner that is granting the License.
|
||||
|
||||
"Legal Entity" shall mean the union of the acting entity and all
|
||||
other entities that control, are controlled by, or are under common
|
||||
control with that entity. For the purposes of this definition,
|
||||
"control" means (i) the power, direct or indirect, to cause the
|
||||
direction or management of such entity, whether by contract or
|
||||
otherwise, or (ii) ownership of fifty percent (50%) or more of the
|
||||
outstanding shares, or (iii) beneficial ownership of such entity.
|
||||
|
||||
"You" (or "Your") shall mean an individual or Legal Entity
|
||||
exercising permissions granted by this License.
|
||||
|
||||
"Source" form shall mean the preferred form for making modifications,
|
||||
including but not limited to software source code, documentation
|
||||
source, and configuration files.
|
||||
|
||||
"Object" form shall mean any form resulting from mechanical
|
||||
transformation or translation of a Source form, including but
|
||||
not limited to compiled object code, generated documentation,
|
||||
and conversions to other media types.
|
||||
|
||||
"Work" shall mean the work of authorship, whether in Source or
|
||||
Object form, made available under the License, as indicated by a
|
||||
copyright notice that is included in or attached to the work
|
||||
(an example is provided in the Appendix below).
|
||||
|
||||
"Derivative Works" shall mean any work, whether in Source or Object
|
||||
form, that is based on (or derived from) the Work and for which the
|
||||
editorial revisions, annotations, elaborations, or other modifications
|
||||
represent, as a whole, an original work of authorship. For the purposes
|
||||
of this License, Derivative Works shall not include works that remain
|
||||
separable from, or merely link (or bind by name) to the interfaces of,
|
||||
the Work and Derivative Works thereof.
|
||||
|
||||
"Contribution" shall mean any work of authorship, including
|
||||
the original version of the Work and any modifications or additions
|
||||
to that Work or Derivative Works thereof, that is intentionally
|
||||
submitted to Licensor for inclusion in the Work by the copyright owner
|
||||
or by an individual or Legal Entity authorized to submit on behalf of
|
||||
the copyright owner. For the purposes of this definition, "submitted"
|
||||
means any form of electronic, verbal, or written communication sent
|
||||
to the Licensor or its representatives, including but not limited to
|
||||
communication on electronic mailing lists, source code control systems,
|
||||
and issue tracking systems that are managed by, or on behalf of, the
|
||||
Licensor for the purpose of discussing and improving the Work, but
|
||||
excluding communication that is conspicuously marked or otherwise
|
||||
designated in writing by the copyright owner as "Not a Contribution."
|
||||
|
||||
"Contributor" shall mean Licensor and any individual or Legal Entity
|
||||
on behalf of whom a Contribution has been received by Licensor and
|
||||
subsequently incorporated within the Work.
|
||||
|
||||
2. Grant of Copyright License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
copyright license to reproduce, prepare Derivative Works of,
|
||||
publicly display, publicly perform, sublicense, and distribute the
|
||||
Work and such Derivative Works in Source or Object form.
|
||||
|
||||
3. Grant of Patent License. Subject to the terms and conditions of
|
||||
this License, each Contributor hereby grants to You a perpetual,
|
||||
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
|
||||
(except as stated in this section) patent license to make, have made,
|
||||
use, offer to sell, sell, import, and otherwise transfer the Work,
|
||||
where such license applies only to those patent claims licensable
|
||||
by such Contributor that are necessarily infringed by their
|
||||
Contribution(s) alone or by combination of their Contribution(s)
|
||||
with the Work to which such Contribution(s) was submitted. If You
|
||||
institute patent litigation against any entity (including a
|
||||
cross-claim or counterclaim in a lawsuit) alleging that the Work
|
||||
or a Contribution incorporated within the Work constitutes direct
|
||||
or contributory patent infringement, then any patent licenses
|
||||
granted to You under this License for that Work shall terminate
|
||||
as of the date such litigation is filed.
|
||||
|
||||
4. Redistribution. You may reproduce and distribute copies of the
|
||||
Work or Derivative Works thereof in any medium, with or without
|
||||
modifications, and in Source or Object form, provided that You
|
||||
meet the following conditions:
|
||||
|
||||
(a) You must give any other recipients of the Work or
|
||||
Derivative Works a copy of this License; and
|
||||
|
||||
(b) You must cause any modified files to carry prominent notices
|
||||
stating that You changed the files; and
|
||||
|
||||
(c) You must retain, in the Source form of any Derivative Works
|
||||
that You distribute, all copyright, patent, trademark, and
|
||||
attribution notices from the Source form of the Work,
|
||||
excluding those notices that do not pertain to any part of
|
||||
the Derivative Works; and
|
||||
|
||||
(d) If the Work includes a "NOTICE" text file as part of its
|
||||
distribution, then any Derivative Works that You distribute must
|
||||
include a readable copy of the attribution notices contained
|
||||
within such NOTICE file, excluding those notices that do not
|
||||
pertain to any part of the Derivative Works, in at least one
|
||||
of the following places: within a NOTICE text file distributed
|
||||
as part of the Derivative Works; within the Source form or
|
||||
documentation, if provided along with the Derivative Works; or,
|
||||
within a display generated by the Derivative Works, if and
|
||||
wherever such third-party notices normally appear. The contents
|
||||
of the NOTICE file are for informational purposes only and
|
||||
do not modify the License. You may add Your own attribution
|
||||
notices within Derivative Works that You distribute, alongside
|
||||
or as an addendum to the NOTICE text from the Work, provided
|
||||
that such additional attribution notices cannot be construed
|
||||
as modifying the License.
|
||||
|
||||
You may add Your own copyright statement to Your modifications and
|
||||
may provide additional or different license terms and conditions
|
||||
for use, reproduction, or distribution of Your modifications, or
|
||||
for any such Derivative Works as a whole, provided Your use,
|
||||
reproduction, and distribution of the Work otherwise complies with
|
||||
the conditions stated in this License.
|
||||
|
||||
5. Submission of Contributions. Unless You explicitly state otherwise,
|
||||
any Contribution intentionally submitted for inclusion in the Work
|
||||
by You to the Licensor shall be under the terms and conditions of
|
||||
this License, without any additional terms or conditions.
|
||||
Notwithstanding the above, nothing herein shall supersede or modify
|
||||
the terms of any separate license agreement you may have executed
|
||||
with Licensor regarding such Contributions.
|
||||
|
||||
6. Trademarks. This License does not grant permission to use the trade
|
||||
names, trademarks, service marks, or product names of the Licensor,
|
||||
except as required for reasonable and customary use in describing the
|
||||
origin of the Work and reproducing the content of the NOTICE file.
|
||||
|
||||
7. Disclaimer of Warranty. Unless required by applicable law or
|
||||
agreed to in writing, Licensor provides the Work (and each
|
||||
Contributor provides its Contributions) on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
|
||||
implied, including, without limitation, any warranties or conditions
|
||||
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
|
||||
PARTICULAR PURPOSE. You are solely responsible for determining the
|
||||
appropriateness of using or redistributing the Work and assume any
|
||||
risks associated with Your exercise of permissions under this License.
|
||||
|
||||
8. Limitation of Liability. In no event and under no legal theory,
|
||||
whether in tort (including negligence), contract, or otherwise,
|
||||
unless required by applicable law (such as deliberate and grossly
|
||||
negligent acts) or agreed to in writing, shall any Contributor be
|
||||
liable to You for damages, including any direct, indirect, special,
|
||||
incidental, or consequential damages of any character arising as a
|
||||
result of this License or out of the use or inability to use the
|
||||
Work (including but not limited to damages for loss of goodwill,
|
||||
work stoppage, computer failure or malfunction, or any and all
|
||||
other commercial damages or losses), even if such Contributor
|
||||
has been advised of the possibility of such damages.
|
||||
|
||||
9. Accepting Warranty or Additional Liability. While redistributing
|
||||
the Work or Derivative Works thereof, You may choose to offer,
|
||||
and charge a fee for, acceptance of support, warranty, indemnity,
|
||||
or other liability obligations and/or rights consistent with this
|
||||
License. However, in accepting such obligations, You may act only
|
||||
on Your own behalf and on Your sole responsibility, not on behalf
|
||||
of any other Contributor, and only if You agree to indemnify,
|
||||
defend, and hold each Contributor harmless for any liability
|
||||
incurred by, or claims asserted against, such Contributor by reason
|
||||
of your accepting any such warranty or additional liability.
|
||||
|
||||
END OF TERMS AND CONDITIONS
|
||||
|
||||
APPENDIX: How to apply the Apache License to your work.
|
||||
|
||||
To apply the Apache License to your work, attach the following
|
||||
boilerplate notice, with the fields enclosed by brackets "[]"
|
||||
replaced with your own identifying information. (Don't include
|
||||
the brackets!) The text should be enclosed in the appropriate
|
||||
comment syntax for the file format. We also recommend that a
|
||||
file or class name and description of purpose be included on the
|
||||
same "printed page" as the copyright notice for easier
|
||||
identification within third-party archives.
|
||||
|
||||
Copyright [yyyy] [name of copyright owner]
|
||||
|
||||
Licensed under the Apache License, Version 2.0 (the "License");
|
||||
you may not use this file except in compliance with the License.
|
||||
You may obtain a copy of the License at
|
||||
|
||||
http://www.apache.org/licenses/LICENSE-2.0
|
||||
|
||||
Unless required by applicable law or agreed to in writing, software
|
||||
distributed under the License is distributed on an "AS IS" BASIS,
|
||||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
See the License for the specific language governing permissions and
|
||||
limitations under the License.
|
66
vendor/github.com/minio/sio/README.md
generated
vendored
Normal file
66
vendor/github.com/minio/sio/README.md
generated
vendored
Normal file
@ -0,0 +1,66 @@
|
||||
[![Godoc Reference](https://godoc.org/github.com/minio/sio?status.svg)](https://godoc.org/github.com/minio/sio)
|
||||
[![Travis CI](https://travis-ci.org/minio/sio.svg?branch=master)](https://travis-ci.org/minio/sio)
|
||||
[![Go Report Card](https://goreportcard.com/badge/minio/sio)](https://goreportcard.com/report/minio/sio)
|
||||
|
||||
# Secure IO
|
||||
## Go implementation of the Data At Rest Encryption (DARE) format.
|
||||
|
||||
## Introduction
|
||||
|
||||
It is a common problem to store data securely - especially on untrusted remote storage.
|
||||
One solution to this problem is cryptography. Before data is stored it is encrypted
|
||||
to ensure that the data is confidential. Unfortunately encrypting data is not enough to
|
||||
prevent more sophisticated attacks. Anyone who has access to the stored data can try to
|
||||
manipulate the data - even if the data is encrypted.
|
||||
|
||||
To prevent these kinds of attacks the data must be encrypted in a tamper-resistant way.
|
||||
This means an attacker should not be able to:
|
||||
- Read the stored data - this is achieved by modern encryption algorithms.
|
||||
- Modify the data by changing parts of the encrypted data.
|
||||
- Rearrange or reorder parts of the encrypted data.
|
||||
|
||||
Authenticated encryption schemes (AE) - like AES-GCM or ChaCha20-Poly1305 - encrypt and
|
||||
authenticate data. Any modification to the encrypted data (ciphertext) is detected while
|
||||
decrypting the data. But even an AE scheme alone is not sufficiently enough to prevent all
|
||||
kinds of data manipulation.
|
||||
|
||||
All modern AE schemes produce an authentication tag which is verified after the ciphertext
|
||||
is decrypted. If a large amount of data is decrypted it is not always possible to buffer
|
||||
all decrypted data until the authentication tag is verified. Returning unauthenticated
|
||||
data has the same issues like encrypting data without authentication.
|
||||
|
||||
Splitting the data into small chunks fixes the problem of deferred authentication checks
|
||||
but introduces a new one. The chunks can be reordered - e.g. exchanging chunk 1 and 2 -
|
||||
because every chunk is encrypted separately. Therefore the order of the chunks must be
|
||||
encoded somehow into the chunks itself to be able to detect rearranging any number of
|
||||
chunks.
|
||||
|
||||
This project specifies a [format](https://github.com/minio/sio/blob/master/DARE.md) for
|
||||
en/decrypting an arbitrary data stream and gives some [recommendations](https://github.com/minio/sio/blob/master/DARE.md#appendices)
|
||||
about how to use and implement data at rest encryption (DARE). Additionally this project
|
||||
provides a reference implementation in Go.
|
||||
|
||||
## Applications
|
||||
|
||||
DARE is designed with simplicity and efficiency in mind. It combines modern AE schemes
|
||||
with a very simple reorder protection mechanism to build a tamper-resistant encryption
|
||||
scheme. DARE can be used to encrypt files, backups and even large object storage systems.
|
||||
|
||||
Its main properties are:
|
||||
- Security and high performance by relying on modern AEAD ciphers
|
||||
- Small overhead - encryption increases the amount of data by ~0.05%
|
||||
- Support for long data streams - up to 256 TB under the same key
|
||||
- Random access - arbitrary sequences / ranges can be decrypted independently
|
||||
|
||||
**Install:** `go get -u github.com/minio/sio`
|
||||
|
||||
DARE and `github.com/minio/sio` are finalized and can be used in production.
|
||||
|
||||
## Performance
|
||||
|
||||
Cipher | 8 KB | 64 KB | 512 KB | 1 MB
|
||||
----------------- | -------- | --------- | --------- | --------
|
||||
AES_256_GCM | 90 MB/s | 1.96 GB/s | 2.64 GB/s | 2.83 GB/s
|
||||
CHACHA20_POLY1305 | 97 MB/s | 1.23 GB/s | 1.54 GB/s | 1.57 GB/s
|
||||
|
||||
*On i7-6500U 2 x 2.5 GHz | Linux 4.10.0-32-generic | Go 1.8.3 | AES-NI & AVX2*
|
277
vendor/github.com/minio/sio/dare.go
generated
vendored
Normal file
277
vendor/github.com/minio/sio/dare.go
generated
vendored
Normal file
@ -0,0 +1,277 @@
|
||||
// Copyright (C) 2017 Minio Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
// Package sio implements the DARE format. It provides an API
|
||||
// for secure en/decrypting IO operations.
|
||||
package sio // import "github.com/minio/sio"
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/rand"
|
||||
"errors"
|
||||
"io"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305"
|
||||
)
|
||||
|
||||
// Version10 specifies version 1.0
|
||||
const Version10 byte = 0x10
|
||||
|
||||
const (
|
||||
// AES_256_GCM specifies the cipher suite AES-GCM with 256 bit keys.
|
||||
AES_256_GCM byte = iota
|
||||
// CHACHA20_POLY1305 specifies the cipher suite ChaCha20Poly1305 with 256 bit keys.
|
||||
CHACHA20_POLY1305
|
||||
)
|
||||
|
||||
const (
|
||||
headerSize = 16
|
||||
payloadSize = 64 * 1024
|
||||
tagSize = 16
|
||||
)
|
||||
|
||||
var (
|
||||
errMissingHeader = errors.New("sio: incomplete header")
|
||||
errPayloadTooShort = errors.New("sio: payload too short")
|
||||
errPackageOutOfOrder = errors.New("sio: sequence number mismatch")
|
||||
errTagMismatch = errors.New("sio: authentication failed")
|
||||
errUnsupportedVersion = errors.New("sio: unsupported version")
|
||||
errUnsupportedCipher = errors.New("sio: unsupported cipher suite")
|
||||
)
|
||||
|
||||
var newAesGcm = func(key []byte) (cipher.AEAD, error) {
|
||||
aes256, err := aes.NewCipher(key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return cipher.NewGCM(aes256)
|
||||
}
|
||||
|
||||
var supportedCiphers = [...]func([]byte) (cipher.AEAD, error){
|
||||
AES_256_GCM: newAesGcm,
|
||||
CHACHA20_POLY1305: chacha20poly1305.New,
|
||||
}
|
||||
|
||||
// Encrypt reads from src until it encounters an io.EOF and encrypts all received
|
||||
// data. The encrypted data is written to dst. It returns the number of bytes
|
||||
// encrypted and the first error encountered while encrypting, if any.
|
||||
//
|
||||
// Encrypt returns the number of bytes written to dst.
|
||||
func Encrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) {
|
||||
encReader, err := EncryptReader(src, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return io.CopyBuffer(dst, encReader, make([]byte, payloadSize))
|
||||
}
|
||||
|
||||
// Decrypt reads from src until it encounters an io.EOF and decrypts all received
|
||||
// data. The decrypted data is written to dst. It returns the number of bytes
|
||||
// decrypted and the first error encountered while decrypting, if any.
|
||||
//
|
||||
// Decrypt returns the number of bytes written to dst. Decrypt only writes data to
|
||||
// dst if the data was decrypted successfully.
|
||||
func Decrypt(dst io.Writer, src io.Reader, config Config) (n int64, err error) {
|
||||
decReader, err := DecryptReader(src, config)
|
||||
if err != nil {
|
||||
return
|
||||
}
|
||||
return io.CopyBuffer(dst, decReader, make([]byte, headerSize+payloadSize+tagSize))
|
||||
}
|
||||
|
||||
// EncryptReader wraps the given src and returns an io.Reader which encrypts
|
||||
// all received data. EncryptReader returns an error if the provided encryption
|
||||
// configuration is invalid.
|
||||
func EncryptReader(src io.Reader, config Config) (io.Reader, error) {
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipher, err := supportedCiphers[config.CipherSuites[0]](config.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce, err := config.generateNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &encryptedReader{
|
||||
src: src,
|
||||
config: config,
|
||||
nonce: nonce,
|
||||
cipher: cipher,
|
||||
sequenceNumber: config.SequenceNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecryptReader wraps the given src and returns an io.Reader which decrypts
|
||||
// all received data. DecryptReader returns an error if the provided decryption
|
||||
// configuration is invalid.
|
||||
func DecryptReader(src io.Reader, config Config) (io.Reader, error) {
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphers, err := config.createCiphers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &decryptedReader{
|
||||
src: src,
|
||||
config: config,
|
||||
ciphers: ciphers,
|
||||
sequenceNumber: config.SequenceNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// EncryptWriter wraps the given dst and returns an io.WriteCloser which
|
||||
// encrypts all data written to it. EncryptWriter returns an error if the
|
||||
// provided decryption configuration is invalid.
|
||||
//
|
||||
// The returned io.WriteCloser must be closed successfully to finalize the
|
||||
// encryption process.
|
||||
func EncryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) {
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
cipher, err := supportedCiphers[config.CipherSuites[0]](config.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
nonce, err := config.generateNonce()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &encryptedWriter{
|
||||
dst: dst,
|
||||
config: config,
|
||||
nonce: nonce,
|
||||
cipher: cipher,
|
||||
sequenceNumber: config.SequenceNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
// DecryptWriter wraps the given dst and returns an io.WriteCloser which
|
||||
// decrypts all data written to it. DecryptWriter returns an error if the
|
||||
// provided decryption configuration is invalid.
|
||||
//
|
||||
// The returned io.WriteCloser must be closed successfully to finalize the
|
||||
// decryption process.
|
||||
func DecryptWriter(dst io.Writer, config Config) (io.WriteCloser, error) {
|
||||
if err := setConfigDefaults(&config); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphers, err := config.createCiphers()
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return &decryptedWriter{
|
||||
dst: dst,
|
||||
config: config,
|
||||
ciphers: ciphers,
|
||||
sequenceNumber: config.SequenceNumber,
|
||||
}, nil
|
||||
}
|
||||
|
||||
func setConfigDefaults(config *Config) error {
|
||||
if config.MinVersion > Version10 {
|
||||
return errors.New("sio: unknown minimum version")
|
||||
}
|
||||
if config.MaxVersion > Version10 {
|
||||
return errors.New("dare: unknown maximum version")
|
||||
}
|
||||
if len(config.Key) != 32 {
|
||||
return errors.New("sio: invalid key size")
|
||||
}
|
||||
if len(config.CipherSuites) > 2 {
|
||||
return errors.New("sio: too many cipher suites")
|
||||
}
|
||||
for _, c := range config.CipherSuites {
|
||||
if int(c) >= len(supportedCiphers) {
|
||||
return errors.New("sio: unknown cipher suite")
|
||||
}
|
||||
}
|
||||
if config.MinVersion < Version10 {
|
||||
config.MinVersion = Version10
|
||||
}
|
||||
if config.MaxVersion < Version10 {
|
||||
config.MaxVersion = config.MinVersion
|
||||
}
|
||||
if len(config.CipherSuites) == 0 {
|
||||
config.CipherSuites = []byte{AES_256_GCM, CHACHA20_POLY1305}
|
||||
}
|
||||
if config.Rand == nil {
|
||||
config.Rand = rand.Reader
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
// Config contains the format configuration. The only field
|
||||
// which must always be set manually is the secret key.
|
||||
type Config struct {
|
||||
// The minimal supported version of the format. If
|
||||
// not set the default value is used.
|
||||
MinVersion byte
|
||||
// The highest supported version of the format. If
|
||||
// not set the default value is used.
|
||||
MaxVersion byte
|
||||
// A list of supported cipher suites. If not set the
|
||||
// default value is used.
|
||||
CipherSuites []byte
|
||||
// The secret encryption key. It must be 32 bytes long.
|
||||
Key []byte
|
||||
// The first expected sequence number. It should only
|
||||
// be set manually when appending data to an existing
|
||||
// sequence of packages or decrypting a range within
|
||||
// a sequence of packages.
|
||||
SequenceNumber uint32
|
||||
// The RNG used to generate random values. If not set
|
||||
// the default value (crypto/rand.Reader) is used.
|
||||
Rand io.Reader
|
||||
}
|
||||
|
||||
func (c *Config) verifyHeader(header headerV10) error {
|
||||
if version := header.Version(); version < c.MinVersion || version > c.MaxVersion {
|
||||
return errUnsupportedVersion
|
||||
}
|
||||
if !c.isCipherSuiteSupported(header.Cipher()) {
|
||||
return errUnsupportedCipher
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (c *Config) createCiphers() ([]cipher.AEAD, error) {
|
||||
ciphers := make([]cipher.AEAD, len(supportedCiphers))
|
||||
for _, v := range c.CipherSuites {
|
||||
aeadCipher, err := supportedCiphers[v](c.Key)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
ciphers[v] = aeadCipher
|
||||
}
|
||||
return ciphers, nil
|
||||
}
|
||||
|
||||
func (c *Config) generateNonce() (nonce [8]byte, err error) {
|
||||
_, err = io.ReadFull(c.Rand, nonce[:])
|
||||
return nonce, err
|
||||
}
|
||||
|
||||
func (c *Config) isCipherSuiteSupported(suite byte) bool {
|
||||
for _, c := range c.CipherSuites {
|
||||
if suite == c {
|
||||
return true
|
||||
}
|
||||
}
|
||||
return false
|
||||
}
|
39
vendor/github.com/minio/sio/header.go
generated
vendored
Normal file
39
vendor/github.com/minio/sio/header.go
generated
vendored
Normal file
@ -0,0 +1,39 @@
|
||||
// Copyright (C) 2017 Minio Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sio
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
type headerV10 []byte
|
||||
|
||||
func header(b []byte) headerV10 { return headerV10(b[:headerSize]) }
|
||||
|
||||
func (h headerV10) Version() byte { return h[0] }
|
||||
|
||||
func (h headerV10) Cipher() byte { return h[1] }
|
||||
|
||||
func (h headerV10) Len() int { return int(binary.LittleEndian.Uint16(h[2:])) + 1 }
|
||||
|
||||
func (h headerV10) SequenceNumber() uint32 { return binary.LittleEndian.Uint32(h[4:]) }
|
||||
|
||||
func (h headerV10) SetVersion(version byte) { h[0] = version }
|
||||
|
||||
func (h headerV10) SetCipher(suite byte) { h[1] = suite }
|
||||
|
||||
func (h headerV10) SetLen(length int) { binary.LittleEndian.PutUint16(h[2:], uint16(length-1)) }
|
||||
|
||||
func (h headerV10) SetSequenceNumber(num uint32) { binary.LittleEndian.PutUint32(h[4:], num) }
|
||||
|
||||
func (h headerV10) SetNonce(nonce [8]byte) { copy(h[8:], nonce[:]) }
|
169
vendor/github.com/minio/sio/reader.go
generated
vendored
Normal file
169
vendor/github.com/minio/sio/reader.go
generated
vendored
Normal file
@ -0,0 +1,169 @@
|
||||
// Copyright (C) 2017 Minio Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sio
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
)
|
||||
|
||||
type encryptedReader struct {
|
||||
src io.Reader
|
||||
config Config
|
||||
|
||||
sequenceNumber uint32
|
||||
nonce [8]byte
|
||||
cipher cipher.AEAD
|
||||
|
||||
pack [headerSize + payloadSize + tagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (r *encryptedReader) Read(p []byte) (n int, err error) {
|
||||
if r.offset > 0 {
|
||||
remaining := headerSize + header(r.pack[:]).Len() + tagSize - r.offset
|
||||
if len(p) < remaining {
|
||||
n = copy(p, r.pack[r.offset:r.offset+len(p)])
|
||||
r.offset += n
|
||||
return
|
||||
}
|
||||
n = copy(p, r.pack[r.offset:r.offset+remaining])
|
||||
p = p[remaining:]
|
||||
r.offset = 0
|
||||
}
|
||||
for len(p) >= headerSize+payloadSize+tagSize {
|
||||
nn, err := io.ReadFull(r.src, r.pack[headerSize:headerSize+payloadSize])
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return n, err
|
||||
}
|
||||
r.encrypt(p, nn)
|
||||
n += headerSize + nn + tagSize
|
||||
p = p[headerSize+nn+tagSize:]
|
||||
}
|
||||
if len(p) > 0 {
|
||||
nn, err := io.ReadFull(r.src, r.pack[headerSize:headerSize+payloadSize])
|
||||
if err != nil && err != io.ErrUnexpectedEOF {
|
||||
return n, err
|
||||
}
|
||||
r.encrypt(r.pack[:], nn)
|
||||
if headerSize+nn+tagSize < len(p) {
|
||||
r.offset = copy(p, r.pack[:headerSize+nn+tagSize])
|
||||
} else {
|
||||
r.offset = copy(p, r.pack[:len(p)])
|
||||
}
|
||||
n += r.offset
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *encryptedReader) encrypt(dst []byte, length int) {
|
||||
header := header(dst)
|
||||
header.SetVersion(r.config.MaxVersion)
|
||||
header.SetCipher(r.config.CipherSuites[0])
|
||||
header.SetLen(length)
|
||||
header.SetSequenceNumber(r.sequenceNumber)
|
||||
header.SetNonce(r.nonce)
|
||||
|
||||
copy(dst[:headerSize], header)
|
||||
r.cipher.Seal(dst[headerSize:headerSize], header[4:headerSize], r.pack[headerSize:headerSize+length], header[:4])
|
||||
|
||||
r.sequenceNumber++
|
||||
}
|
||||
|
||||
type decryptedReader struct {
|
||||
src io.Reader
|
||||
config Config
|
||||
|
||||
sequenceNumber uint32
|
||||
ciphers []cipher.AEAD
|
||||
|
||||
pack [headerSize + payloadSize + tagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (r *decryptedReader) Read(p []byte) (n int, err error) {
|
||||
if r.offset > 0 {
|
||||
remaining := header(r.pack[:]).Len() - r.offset
|
||||
if len(p) < remaining {
|
||||
n = copy(p, r.pack[headerSize+r.offset:headerSize+r.offset+len(p)])
|
||||
r.offset += n
|
||||
return
|
||||
}
|
||||
n = copy(p, r.pack[headerSize+r.offset:headerSize+r.offset+remaining])
|
||||
p = p[remaining:]
|
||||
r.offset = 0
|
||||
}
|
||||
for len(p) >= payloadSize {
|
||||
if err = r.readHeader(); err != nil {
|
||||
return n, err
|
||||
}
|
||||
nn, err := r.decrypt(p[:payloadSize])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
p = p[nn:]
|
||||
n += nn
|
||||
}
|
||||
if len(p) > 0 {
|
||||
if err = r.readHeader(); err != nil {
|
||||
return n, err
|
||||
}
|
||||
nn, err := r.decrypt(r.pack[headerSize:])
|
||||
if err != nil {
|
||||
return n, err
|
||||
}
|
||||
if nn < len(p) {
|
||||
r.offset = copy(p, r.pack[headerSize:headerSize+nn])
|
||||
} else {
|
||||
r.offset = copy(p, r.pack[headerSize:headerSize+len(p)])
|
||||
}
|
||||
n += r.offset
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (r *decryptedReader) readHeader() error {
|
||||
n, err := io.ReadFull(r.src, header(r.pack[:]))
|
||||
if n != headerSize && err == io.ErrUnexpectedEOF {
|
||||
return errMissingHeader
|
||||
} else if err != nil {
|
||||
return err
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (r *decryptedReader) decrypt(dst []byte) (n int, err error) {
|
||||
header := header(r.pack[:])
|
||||
if err = r.config.verifyHeader(header); err != nil {
|
||||
return 0, err
|
||||
}
|
||||
if header.SequenceNumber() != r.sequenceNumber {
|
||||
return 0, errPackageOutOfOrder
|
||||
}
|
||||
ciphertext := r.pack[headerSize : headerSize+header.Len()+tagSize]
|
||||
n, err = io.ReadFull(r.src, ciphertext)
|
||||
if err == io.EOF || err == io.ErrUnexpectedEOF {
|
||||
return 0, errPayloadTooShort
|
||||
} else if err != nil {
|
||||
return 0, err
|
||||
}
|
||||
aeadCipher := r.ciphers[header.Cipher()]
|
||||
plaintext, err := aeadCipher.Open(dst[:0], header[4:], ciphertext, header[:4])
|
||||
if err != nil {
|
||||
return 0, errTagMismatch
|
||||
}
|
||||
r.sequenceNumber++
|
||||
return len(plaintext), nil
|
||||
}
|
193
vendor/github.com/minio/sio/writer.go
generated
vendored
Normal file
193
vendor/github.com/minio/sio/writer.go
generated
vendored
Normal file
@ -0,0 +1,193 @@
|
||||
// Copyright (C) 2017 Minio Inc.
|
||||
//
|
||||
// Licensed under the Apache License, Version 2.0 (the "License");
|
||||
// you may not use this file except in compliance with the License.
|
||||
// You may obtain a copy of the License at
|
||||
//
|
||||
// http://www.apache.org/licenses/LICENSE-2.0
|
||||
//
|
||||
// Unless required by applicable law or agreed to in writing, software
|
||||
// distributed under the License is distributed on an "AS IS" BASIS,
|
||||
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
|
||||
// See the License for the specific language governing permissions and
|
||||
// limitations under the License.
|
||||
|
||||
package sio
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"io"
|
||||
)
|
||||
|
||||
type decryptedWriter struct {
|
||||
dst io.Writer
|
||||
config Config
|
||||
|
||||
sequenceNumber uint32
|
||||
ciphers []cipher.AEAD
|
||||
|
||||
pack [headerSize + payloadSize + tagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (w *decryptedWriter) Write(p []byte) (n int, err error) {
|
||||
if w.offset > 0 && w.offset < headerSize {
|
||||
remaining := headerSize - w.offset
|
||||
if len(p) < remaining {
|
||||
n = copy(w.pack[w.offset:], p)
|
||||
w.offset += n
|
||||
return
|
||||
}
|
||||
n = copy(w.pack[w.offset:], p[:remaining])
|
||||
p = p[remaining:]
|
||||
w.offset += n
|
||||
}
|
||||
if w.offset >= headerSize {
|
||||
remaining := headerSize + header(w.pack[:]).Len() + tagSize - w.offset
|
||||
if len(p) < remaining {
|
||||
nn := copy(w.pack[w.offset:], p)
|
||||
w.offset += nn
|
||||
return n + nn, err
|
||||
}
|
||||
n += copy(w.pack[w.offset:], p[:remaining])
|
||||
if err = w.decrypt(w.pack[:]); err != nil {
|
||||
return n, err
|
||||
}
|
||||
p = p[remaining:]
|
||||
w.offset = 0
|
||||
}
|
||||
for len(p) > headerSize {
|
||||
header := header(p)
|
||||
if len(p) < headerSize+header.Len()+tagSize {
|
||||
w.offset = copy(w.pack[:], p)
|
||||
n += w.offset
|
||||
return
|
||||
}
|
||||
if err = w.decrypt(p); err != nil {
|
||||
return n, err
|
||||
}
|
||||
p = p[headerSize+header.Len()+tagSize:]
|
||||
n += headerSize + header.Len() + tagSize
|
||||
}
|
||||
w.offset = copy(w.pack[:], p)
|
||||
n += w.offset
|
||||
return
|
||||
}
|
||||
|
||||
func (w *decryptedWriter) Close() error {
|
||||
if w.offset > 0 {
|
||||
if w.offset < headerSize {
|
||||
return errMissingHeader
|
||||
}
|
||||
if w.offset < headerSize+header(w.pack[:]).Len()+tagSize {
|
||||
return errPayloadTooShort
|
||||
}
|
||||
if err := w.decrypt(w.pack[:]); err != nil {
|
||||
return err
|
||||
}
|
||||
}
|
||||
if dst, ok := w.dst.(io.Closer); ok {
|
||||
return dst.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *decryptedWriter) decrypt(src []byte) error {
|
||||
header := header(src)
|
||||
if err := w.config.verifyHeader(header); err != nil {
|
||||
return err
|
||||
}
|
||||
if header.SequenceNumber() != w.sequenceNumber {
|
||||
return errPackageOutOfOrder
|
||||
}
|
||||
|
||||
aeadCipher := w.ciphers[header.Cipher()]
|
||||
plaintext, err := aeadCipher.Open(w.pack[headerSize:headerSize], header[4:headerSize], src[headerSize:headerSize+header.Len()+tagSize], header[:4])
|
||||
if err != nil {
|
||||
return errTagMismatch
|
||||
}
|
||||
|
||||
n, err := w.dst.Write(plaintext)
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != len(plaintext) {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
w.sequenceNumber++
|
||||
return nil
|
||||
}
|
||||
|
||||
type encryptedWriter struct {
|
||||
dst io.Writer
|
||||
config Config
|
||||
|
||||
nonce [8]byte
|
||||
sequenceNumber uint32
|
||||
cipher cipher.AEAD
|
||||
|
||||
pack [headerSize + payloadSize + tagSize]byte
|
||||
offset int
|
||||
}
|
||||
|
||||
func (w *encryptedWriter) Write(p []byte) (n int, err error) {
|
||||
if w.offset > 0 {
|
||||
remaining := payloadSize - w.offset
|
||||
if len(p) < remaining {
|
||||
n = copy(w.pack[headerSize+w.offset:], p)
|
||||
w.offset += n
|
||||
return
|
||||
}
|
||||
n = copy(w.pack[headerSize+w.offset:], p[:remaining])
|
||||
if err = w.encrypt(w.pack[headerSize : headerSize+payloadSize]); err != nil {
|
||||
return
|
||||
}
|
||||
p = p[remaining:]
|
||||
w.offset = 0
|
||||
}
|
||||
for len(p) >= payloadSize {
|
||||
if err = w.encrypt(p[:payloadSize]); err != nil {
|
||||
return
|
||||
}
|
||||
p = p[payloadSize:]
|
||||
n += payloadSize
|
||||
}
|
||||
if len(p) > 0 {
|
||||
w.offset = copy(w.pack[headerSize:], p)
|
||||
n += w.offset
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
func (w *encryptedWriter) Close() error {
|
||||
if w.offset > 0 {
|
||||
return w.encrypt(w.pack[headerSize : headerSize+w.offset])
|
||||
}
|
||||
if dst, ok := w.dst.(io.Closer); ok {
|
||||
return dst.Close()
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func (w *encryptedWriter) encrypt(src []byte) error {
|
||||
header := header(w.pack[:])
|
||||
header.SetVersion(w.config.MaxVersion)
|
||||
header.SetCipher(w.config.CipherSuites[0])
|
||||
header.SetLen(len(src))
|
||||
header.SetSequenceNumber(w.sequenceNumber)
|
||||
header.SetNonce(w.nonce)
|
||||
|
||||
w.cipher.Seal(w.pack[headerSize:headerSize], header[4:headerSize], src, header[:4])
|
||||
|
||||
n, err := w.dst.Write(w.pack[:headerSize+len(src)+tagSize])
|
||||
if err != nil {
|
||||
return err
|
||||
}
|
||||
if n != headerSize+len(src)+tagSize {
|
||||
return io.ErrShortWrite
|
||||
}
|
||||
|
||||
w.sequenceNumber++
|
||||
return nil
|
||||
}
|
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
27
vendor/golang.org/x/crypto/LICENSE
generated
vendored
Normal file
@ -0,0 +1,27 @@
|
||||
Copyright (c) 2009 The Go Authors. All rights reserved.
|
||||
|
||||
Redistribution and use in source and binary forms, with or without
|
||||
modification, are permitted provided that the following conditions are
|
||||
met:
|
||||
|
||||
* Redistributions of source code must retain the above copyright
|
||||
notice, this list of conditions and the following disclaimer.
|
||||
* Redistributions in binary form must reproduce the above
|
||||
copyright notice, this list of conditions and the following disclaimer
|
||||
in the documentation and/or other materials provided with the
|
||||
distribution.
|
||||
* Neither the name of Google Inc. nor the names of its
|
||||
contributors may be used to endorse or promote products derived from
|
||||
this software without specific prior written permission.
|
||||
|
||||
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/PATENTS
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
Additional IP Rights Grant (Patents)
|
||||
|
||||
"This implementation" means the copyrightable works distributed by
|
||||
Google as part of the Go project.
|
||||
|
||||
Google hereby grants to You a perpetual, worldwide, non-exclusive,
|
||||
no-charge, royalty-free, irrevocable (except as stated in this section)
|
||||
patent license to make, have made, use, offer to sell, sell, import,
|
||||
transfer and otherwise run, modify and propagate the contents of this
|
||||
implementation of Go, where such license applies only to those patent
|
||||
claims, both currently owned or controlled by Google and acquired in
|
||||
the future, licensable by Google that are necessarily infringed by this
|
||||
implementation of Go. This grant does not include claims that would be
|
||||
infringed only as a consequence of further modification of this
|
||||
implementation. If you or your agent or exclusive licensee institute or
|
||||
order or agree to the institution of patent litigation against any
|
||||
entity (including a cross-claim or counterclaim in a lawsuit) alleging
|
||||
that this implementation of Go or any code incorporated within this
|
||||
implementation of Go constitutes direct or contributory patent
|
||||
infringement, or inducement of patent infringement, then any patent
|
||||
rights granted to you under this License for this implementation of Go
|
||||
shall terminate as of the date such litigation is filed.
|
83
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go
generated
vendored
Normal file
83
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305.go
generated
vendored
Normal file
@ -0,0 +1,83 @@
|
||||
// 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.
|
||||
|
||||
// Package chacha20poly1305 implements the ChaCha20-Poly1305 AEAD as specified in RFC 7539.
|
||||
package chacha20poly1305 // import "golang.org/x/crypto/chacha20poly1305"
|
||||
|
||||
import (
|
||||
"crypto/cipher"
|
||||
"errors"
|
||||
)
|
||||
|
||||
const (
|
||||
// KeySize is the size of the key used by this AEAD, in bytes.
|
||||
KeySize = 32
|
||||
// NonceSize is the size of the nonce used with this AEAD, in bytes.
|
||||
NonceSize = 12
|
||||
)
|
||||
|
||||
type chacha20poly1305 struct {
|
||||
key [32]byte
|
||||
}
|
||||
|
||||
// New returns a ChaCha20-Poly1305 AEAD that uses the given, 256-bit key.
|
||||
func New(key []byte) (cipher.AEAD, error) {
|
||||
if len(key) != KeySize {
|
||||
return nil, errors.New("chacha20poly1305: bad key length")
|
||||
}
|
||||
ret := new(chacha20poly1305)
|
||||
copy(ret.key[:], key)
|
||||
return ret, nil
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) NonceSize() int {
|
||||
return NonceSize
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) Overhead() int {
|
||||
return 16
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) Seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
if len(nonce) != NonceSize {
|
||||
panic("chacha20poly1305: bad nonce length passed to Seal")
|
||||
}
|
||||
|
||||
if uint64(len(plaintext)) > (1<<38)-64 {
|
||||
panic("chacha20poly1305: plaintext too large")
|
||||
}
|
||||
|
||||
return c.seal(dst, nonce, plaintext, additionalData)
|
||||
}
|
||||
|
||||
var errOpen = errors.New("chacha20poly1305: message authentication failed")
|
||||
|
||||
func (c *chacha20poly1305) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
if len(nonce) != NonceSize {
|
||||
panic("chacha20poly1305: bad nonce length passed to Open")
|
||||
}
|
||||
if len(ciphertext) < 16 {
|
||||
return nil, errOpen
|
||||
}
|
||||
if uint64(len(ciphertext)) > (1<<38)-48 {
|
||||
panic("chacha20poly1305: ciphertext too large")
|
||||
}
|
||||
|
||||
return c.open(dst, nonce, ciphertext, additionalData)
|
||||
}
|
||||
|
||||
// sliceForAppend takes a slice and a requested number of bytes. It returns a
|
||||
// slice with the contents of the given slice followed by that many bytes and a
|
||||
// second slice that aliases into it and contains only the extra bytes. If the
|
||||
// original slice has sufficient capacity then no allocation is performed.
|
||||
func sliceForAppend(in []byte, n int) (head, tail []byte) {
|
||||
if total := len(in) + n; cap(in) >= total {
|
||||
head = in[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
copy(head, in)
|
||||
}
|
||||
tail = head[len(in):]
|
||||
return
|
||||
}
|
127
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
generated
vendored
Normal file
127
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.go
generated
vendored
Normal file
@ -0,0 +1,127 @@
|
||||
// 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
|
||||
}
|
2714
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
generated
vendored
Normal file
2714
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_amd64.s
generated
vendored
Normal file
File diff suppressed because it is too large
Load Diff
70
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
generated
vendored
Normal file
70
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_generic.go
generated
vendored
Normal file
@ -0,0 +1,70 @@
|
||||
// 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.
|
||||
|
||||
package chacha20poly1305
|
||||
|
||||
import (
|
||||
"encoding/binary"
|
||||
|
||||
"golang.org/x/crypto/chacha20poly1305/internal/chacha20"
|
||||
"golang.org/x/crypto/poly1305"
|
||||
)
|
||||
|
||||
func roundTo16(n int) int {
|
||||
return 16 * ((n + 15) / 16)
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) sealGeneric(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
var counter [16]byte
|
||||
copy(counter[4:], nonce)
|
||||
|
||||
var polyKey [32]byte
|
||||
chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
|
||||
|
||||
ret, out := sliceForAppend(dst, len(plaintext)+poly1305.TagSize)
|
||||
counter[0] = 1
|
||||
chacha20.XORKeyStream(out, plaintext, &counter, &c.key)
|
||||
|
||||
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(plaintext))+8+8)
|
||||
copy(polyInput, additionalData)
|
||||
copy(polyInput[roundTo16(len(additionalData)):], out[:len(plaintext)])
|
||||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
|
||||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(plaintext)))
|
||||
|
||||
var tag [poly1305.TagSize]byte
|
||||
poly1305.Sum(&tag, polyInput, &polyKey)
|
||||
copy(out[len(plaintext):], tag[:])
|
||||
|
||||
return ret
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) openGeneric(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
var tag [poly1305.TagSize]byte
|
||||
copy(tag[:], ciphertext[len(ciphertext)-16:])
|
||||
ciphertext = ciphertext[:len(ciphertext)-16]
|
||||
|
||||
var counter [16]byte
|
||||
copy(counter[4:], nonce)
|
||||
|
||||
var polyKey [32]byte
|
||||
chacha20.XORKeyStream(polyKey[:], polyKey[:], &counter, &c.key)
|
||||
|
||||
polyInput := make([]byte, roundTo16(len(additionalData))+roundTo16(len(ciphertext))+8+8)
|
||||
copy(polyInput, additionalData)
|
||||
copy(polyInput[roundTo16(len(additionalData)):], ciphertext)
|
||||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-16:], uint64(len(additionalData)))
|
||||
binary.LittleEndian.PutUint64(polyInput[len(polyInput)-8:], uint64(len(ciphertext)))
|
||||
|
||||
ret, out := sliceForAppend(dst, len(ciphertext))
|
||||
if !poly1305.Verify(&tag, polyInput, &polyKey) {
|
||||
for i := range out {
|
||||
out[i] = 0
|
||||
}
|
||||
return nil, errOpen
|
||||
}
|
||||
|
||||
counter[0] = 1
|
||||
chacha20.XORKeyStream(out, ciphertext, &counter, &c.key)
|
||||
return ret, nil
|
||||
}
|
15
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
generated
vendored
Normal file
15
vendor/golang.org/x/crypto/chacha20poly1305/chacha20poly1305_noasm.go
generated
vendored
Normal file
@ -0,0 +1,15 @@
|
||||
// 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 !amd64 !go1.7 gccgo appengine
|
||||
|
||||
package chacha20poly1305
|
||||
|
||||
func (c *chacha20poly1305) seal(dst, nonce, plaintext, additionalData []byte) []byte {
|
||||
return c.sealGeneric(dst, nonce, plaintext, additionalData)
|
||||
}
|
||||
|
||||
func (c *chacha20poly1305) open(dst, nonce, ciphertext, additionalData []byte) ([]byte, error) {
|
||||
return c.openGeneric(dst, nonce, ciphertext, additionalData)
|
||||
}
|
199
vendor/golang.org/x/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go
generated
vendored
Normal file
199
vendor/golang.org/x/crypto/chacha20poly1305/internal/chacha20/chacha_generic.go
generated
vendored
Normal file
@ -0,0 +1,199 @@
|
||||
// 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.
|
||||
|
||||
// Package ChaCha20 implements the core ChaCha20 function as specified in https://tools.ietf.org/html/rfc7539#section-2.3.
|
||||
package chacha20
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
const rounds = 20
|
||||
|
||||
// core applies the ChaCha20 core function to 16-byte input in, 32-byte key k,
|
||||
// and 16-byte constant c, and puts the result into 64-byte array out.
|
||||
func core(out *[64]byte, in *[16]byte, k *[32]byte) {
|
||||
j0 := uint32(0x61707865)
|
||||
j1 := uint32(0x3320646e)
|
||||
j2 := uint32(0x79622d32)
|
||||
j3 := uint32(0x6b206574)
|
||||
j4 := binary.LittleEndian.Uint32(k[0:4])
|
||||
j5 := binary.LittleEndian.Uint32(k[4:8])
|
||||
j6 := binary.LittleEndian.Uint32(k[8:12])
|
||||
j7 := binary.LittleEndian.Uint32(k[12:16])
|
||||
j8 := binary.LittleEndian.Uint32(k[16:20])
|
||||
j9 := binary.LittleEndian.Uint32(k[20:24])
|
||||
j10 := binary.LittleEndian.Uint32(k[24:28])
|
||||
j11 := binary.LittleEndian.Uint32(k[28:32])
|
||||
j12 := binary.LittleEndian.Uint32(in[0:4])
|
||||
j13 := binary.LittleEndian.Uint32(in[4:8])
|
||||
j14 := binary.LittleEndian.Uint32(in[8:12])
|
||||
j15 := binary.LittleEndian.Uint32(in[12:16])
|
||||
|
||||
x0, x1, x2, x3, x4, x5, x6, x7 := j0, j1, j2, j3, j4, j5, j6, j7
|
||||
x8, x9, x10, x11, x12, x13, x14, x15 := j8, j9, j10, j11, j12, j13, j14, j15
|
||||
|
||||
for i := 0; i < rounds; i += 2 {
|
||||
x0 += x4
|
||||
x12 ^= x0
|
||||
x12 = (x12 << 16) | (x12 >> (16))
|
||||
x8 += x12
|
||||
x4 ^= x8
|
||||
x4 = (x4 << 12) | (x4 >> (20))
|
||||
x0 += x4
|
||||
x12 ^= x0
|
||||
x12 = (x12 << 8) | (x12 >> (24))
|
||||
x8 += x12
|
||||
x4 ^= x8
|
||||
x4 = (x4 << 7) | (x4 >> (25))
|
||||
x1 += x5
|
||||
x13 ^= x1
|
||||
x13 = (x13 << 16) | (x13 >> 16)
|
||||
x9 += x13
|
||||
x5 ^= x9
|
||||
x5 = (x5 << 12) | (x5 >> 20)
|
||||
x1 += x5
|
||||
x13 ^= x1
|
||||
x13 = (x13 << 8) | (x13 >> 24)
|
||||
x9 += x13
|
||||
x5 ^= x9
|
||||
x5 = (x5 << 7) | (x5 >> 25)
|
||||
x2 += x6
|
||||
x14 ^= x2
|
||||
x14 = (x14 << 16) | (x14 >> 16)
|
||||
x10 += x14
|
||||
x6 ^= x10
|
||||
x6 = (x6 << 12) | (x6 >> 20)
|
||||
x2 += x6
|
||||
x14 ^= x2
|
||||
x14 = (x14 << 8) | (x14 >> 24)
|
||||
x10 += x14
|
||||
x6 ^= x10
|
||||
x6 = (x6 << 7) | (x6 >> 25)
|
||||
x3 += x7
|
||||
x15 ^= x3
|
||||
x15 = (x15 << 16) | (x15 >> 16)
|
||||
x11 += x15
|
||||
x7 ^= x11
|
||||
x7 = (x7 << 12) | (x7 >> 20)
|
||||
x3 += x7
|
||||
x15 ^= x3
|
||||
x15 = (x15 << 8) | (x15 >> 24)
|
||||
x11 += x15
|
||||
x7 ^= x11
|
||||
x7 = (x7 << 7) | (x7 >> 25)
|
||||
x0 += x5
|
||||
x15 ^= x0
|
||||
x15 = (x15 << 16) | (x15 >> 16)
|
||||
x10 += x15
|
||||
x5 ^= x10
|
||||
x5 = (x5 << 12) | (x5 >> 20)
|
||||
x0 += x5
|
||||
x15 ^= x0
|
||||
x15 = (x15 << 8) | (x15 >> 24)
|
||||
x10 += x15
|
||||
x5 ^= x10
|
||||
x5 = (x5 << 7) | (x5 >> 25)
|
||||
x1 += x6
|
||||
x12 ^= x1
|
||||
x12 = (x12 << 16) | (x12 >> 16)
|
||||
x11 += x12
|
||||
x6 ^= x11
|
||||
x6 = (x6 << 12) | (x6 >> 20)
|
||||
x1 += x6
|
||||
x12 ^= x1
|
||||
x12 = (x12 << 8) | (x12 >> 24)
|
||||
x11 += x12
|
||||
x6 ^= x11
|
||||
x6 = (x6 << 7) | (x6 >> 25)
|
||||
x2 += x7
|
||||
x13 ^= x2
|
||||
x13 = (x13 << 16) | (x13 >> 16)
|
||||
x8 += x13
|
||||
x7 ^= x8
|
||||
x7 = (x7 << 12) | (x7 >> 20)
|
||||
x2 += x7
|
||||
x13 ^= x2
|
||||
x13 = (x13 << 8) | (x13 >> 24)
|
||||
x8 += x13
|
||||
x7 ^= x8
|
||||
x7 = (x7 << 7) | (x7 >> 25)
|
||||
x3 += x4
|
||||
x14 ^= x3
|
||||
x14 = (x14 << 16) | (x14 >> 16)
|
||||
x9 += x14
|
||||
x4 ^= x9
|
||||
x4 = (x4 << 12) | (x4 >> 20)
|
||||
x3 += x4
|
||||
x14 ^= x3
|
||||
x14 = (x14 << 8) | (x14 >> 24)
|
||||
x9 += x14
|
||||
x4 ^= x9
|
||||
x4 = (x4 << 7) | (x4 >> 25)
|
||||
}
|
||||
|
||||
x0 += j0
|
||||
x1 += j1
|
||||
x2 += j2
|
||||
x3 += j3
|
||||
x4 += j4
|
||||
x5 += j5
|
||||
x6 += j6
|
||||
x7 += j7
|
||||
x8 += j8
|
||||
x9 += j9
|
||||
x10 += j10
|
||||
x11 += j11
|
||||
x12 += j12
|
||||
x13 += j13
|
||||
x14 += j14
|
||||
x15 += j15
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:4], x0)
|
||||
binary.LittleEndian.PutUint32(out[4:8], x1)
|
||||
binary.LittleEndian.PutUint32(out[8:12], x2)
|
||||
binary.LittleEndian.PutUint32(out[12:16], x3)
|
||||
binary.LittleEndian.PutUint32(out[16:20], x4)
|
||||
binary.LittleEndian.PutUint32(out[20:24], x5)
|
||||
binary.LittleEndian.PutUint32(out[24:28], x6)
|
||||
binary.LittleEndian.PutUint32(out[28:32], x7)
|
||||
binary.LittleEndian.PutUint32(out[32:36], x8)
|
||||
binary.LittleEndian.PutUint32(out[36:40], x9)
|
||||
binary.LittleEndian.PutUint32(out[40:44], x10)
|
||||
binary.LittleEndian.PutUint32(out[44:48], x11)
|
||||
binary.LittleEndian.PutUint32(out[48:52], x12)
|
||||
binary.LittleEndian.PutUint32(out[52:56], x13)
|
||||
binary.LittleEndian.PutUint32(out[56:60], x14)
|
||||
binary.LittleEndian.PutUint32(out[60:64], x15)
|
||||
}
|
||||
|
||||
// XORKeyStream crypts bytes from in to out using the given key and counters.
|
||||
// In and out may be the same slice but otherwise should not overlap. Counter
|
||||
// contains the raw ChaCha20 counter bytes (i.e. block counter followed by
|
||||
// nonce).
|
||||
func XORKeyStream(out, in []byte, counter *[16]byte, key *[32]byte) {
|
||||
var block [64]byte
|
||||
var counterCopy [16]byte
|
||||
copy(counterCopy[:], counter[:])
|
||||
|
||||
for len(in) >= 64 {
|
||||
core(&block, &counterCopy, key)
|
||||
for i, x := range block {
|
||||
out[i] = in[i] ^ x
|
||||
}
|
||||
u := uint32(1)
|
||||
for i := 0; i < 4; i++ {
|
||||
u += uint32(counterCopy[i])
|
||||
counterCopy[i] = byte(u)
|
||||
u >>= 8
|
||||
}
|
||||
in = in[64:]
|
||||
out = out[64:]
|
||||
}
|
||||
|
||||
if len(in) > 0 {
|
||||
core(&block, &counterCopy, key)
|
||||
for i, v := range in {
|
||||
out[i] = v ^ block[i]
|
||||
}
|
||||
}
|
||||
}
|
33
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
Normal file
33
vendor/golang.org/x/crypto/poly1305/poly1305.go
generated
vendored
Normal file
@ -0,0 +1,33 @@
|
||||
// Copyright 2012 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.
|
||||
|
||||
/*
|
||||
Package poly1305 implements Poly1305 one-time message authentication code as
|
||||
specified in https://cr.yp.to/mac/poly1305-20050329.pdf.
|
||||
|
||||
Poly1305 is a fast, one-time authentication function. It is infeasible for an
|
||||
attacker to generate an authenticator for a message without the key. However, a
|
||||
key must only be used for a single message. Authenticating two different
|
||||
messages with the same key allows an attacker to forge authenticators for other
|
||||
messages with the same key.
|
||||
|
||||
Poly1305 was originally coupled with AES in order to make Poly1305-AES. AES was
|
||||
used with a fixed key in order to generate one-time keys from an nonce.
|
||||
However, in this package AES isn't used and the one-time key is specified
|
||||
directly.
|
||||
*/
|
||||
package poly1305 // import "golang.org/x/crypto/poly1305"
|
||||
|
||||
import "crypto/subtle"
|
||||
|
||||
// TagSize is the size, in bytes, of a poly1305 authenticator.
|
||||
const TagSize = 16
|
||||
|
||||
// Verify returns true if mac is a valid authenticator for m with the given
|
||||
// key.
|
||||
func Verify(mac *[16]byte, m []byte, key *[32]byte) bool {
|
||||
var tmp [16]byte
|
||||
Sum(&tmp, m, key)
|
||||
return subtle.ConstantTimeCompare(tmp[:], mac[:]) == 1
|
||||
}
|
22
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/poly1305/sum_amd64.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2012 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 amd64,!gccgo,!appengine
|
||||
|
||||
package poly1305
|
||||
|
||||
// This function is implemented in sum_amd64.s
|
||||
//go:noescape
|
||||
func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]byte)
|
||||
|
||||
// Sum generates an authenticator for m using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
poly1305(out, mPtr, uint64(len(m)), key)
|
||||
}
|
125
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
Normal file
125
vendor/golang.org/x/crypto/poly1305/sum_amd64.s
generated
vendored
Normal file
@ -0,0 +1,125 @@
|
||||
// Copyright 2012 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 amd64,!gccgo,!appengine
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
#define POLY1305_ADD(msg, h0, h1, h2) \
|
||||
ADDQ 0(msg), h0; \
|
||||
ADCQ 8(msg), h1; \
|
||||
ADCQ $1, h2; \
|
||||
LEAQ 16(msg), msg
|
||||
|
||||
#define POLY1305_MUL(h0, h1, h2, r0, r1, t0, t1, t2, t3) \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h0; \
|
||||
MOVQ AX, t0; \
|
||||
MOVQ DX, t1; \
|
||||
MOVQ r0, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ r0, t2; \
|
||||
IMULQ h2, t2; \
|
||||
ADDQ DX, t2; \
|
||||
\
|
||||
MOVQ r1, AX; \
|
||||
MULQ h0; \
|
||||
ADDQ AX, t1; \
|
||||
ADCQ $0, DX; \
|
||||
MOVQ DX, h0; \
|
||||
MOVQ r1, t3; \
|
||||
IMULQ h2, t3; \
|
||||
MOVQ r1, AX; \
|
||||
MULQ h1; \
|
||||
ADDQ AX, t2; \
|
||||
ADCQ DX, t3; \
|
||||
ADDQ h0, t2; \
|
||||
ADCQ $0, t3; \
|
||||
\
|
||||
MOVQ t0, h0; \
|
||||
MOVQ t1, h1; \
|
||||
MOVQ t2, h2; \
|
||||
ANDQ $3, h2; \
|
||||
MOVQ t2, t0; \
|
||||
ANDQ $0xFFFFFFFFFFFFFFFC, t0; \
|
||||
ADDQ t0, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2; \
|
||||
SHRQ $2, t3, t2; \
|
||||
SHRQ $2, t3; \
|
||||
ADDQ t2, h0; \
|
||||
ADCQ t3, h1; \
|
||||
ADCQ $0, h2
|
||||
|
||||
DATA ·poly1305Mask<>+0x00(SB)/8, $0x0FFFFFFC0FFFFFFF
|
||||
DATA ·poly1305Mask<>+0x08(SB)/8, $0x0FFFFFFC0FFFFFFC
|
||||
GLOBL ·poly1305Mask<>(SB), RODATA, $16
|
||||
|
||||
// func poly1305(out *[16]byte, m *byte, mlen uint64, key *[32]key)
|
||||
TEXT ·poly1305(SB), $0-32
|
||||
MOVQ out+0(FP), DI
|
||||
MOVQ m+8(FP), SI
|
||||
MOVQ mlen+16(FP), R15
|
||||
MOVQ key+24(FP), AX
|
||||
|
||||
MOVQ 0(AX), R11
|
||||
MOVQ 8(AX), R12
|
||||
ANDQ ·poly1305Mask<>(SB), R11 // r0
|
||||
ANDQ ·poly1305Mask<>+8(SB), R12 // r1
|
||||
XORQ R8, R8 // h0
|
||||
XORQ R9, R9 // h1
|
||||
XORQ R10, R10 // h2
|
||||
|
||||
CMPQ R15, $16
|
||||
JB bytes_between_0_and_15
|
||||
|
||||
loop:
|
||||
POLY1305_ADD(SI, R8, R9, R10)
|
||||
|
||||
multiply:
|
||||
POLY1305_MUL(R8, R9, R10, R11, R12, BX, CX, R13, R14)
|
||||
SUBQ $16, R15
|
||||
CMPQ R15, $16
|
||||
JAE loop
|
||||
|
||||
bytes_between_0_and_15:
|
||||
TESTQ R15, R15
|
||||
JZ done
|
||||
MOVQ $1, BX
|
||||
XORQ CX, CX
|
||||
XORQ R13, R13
|
||||
ADDQ R15, SI
|
||||
|
||||
flush_buffer:
|
||||
SHLQ $8, BX, CX
|
||||
SHLQ $8, BX
|
||||
MOVB -1(SI), R13
|
||||
XORQ R13, BX
|
||||
DECQ SI
|
||||
DECQ R15
|
||||
JNZ flush_buffer
|
||||
|
||||
ADDQ BX, R8
|
||||
ADCQ CX, R9
|
||||
ADCQ $0, R10
|
||||
MOVQ $16, R15
|
||||
JMP multiply
|
||||
|
||||
done:
|
||||
MOVQ R8, AX
|
||||
MOVQ R9, BX
|
||||
SUBQ $0xFFFFFFFFFFFFFFFB, AX
|
||||
SBBQ $0xFFFFFFFFFFFFFFFF, BX
|
||||
SBBQ $3, R10
|
||||
CMOVQCS R8, AX
|
||||
CMOVQCS R9, BX
|
||||
MOVQ key+24(FP), R8
|
||||
ADDQ 16(R8), AX
|
||||
ADCQ 24(R8), BX
|
||||
|
||||
MOVQ AX, 0(DI)
|
||||
MOVQ BX, 8(DI)
|
||||
RET
|
22
vendor/golang.org/x/crypto/poly1305/sum_arm.go
generated
vendored
Normal file
22
vendor/golang.org/x/crypto/poly1305/sum_arm.go
generated
vendored
Normal file
@ -0,0 +1,22 @@
|
||||
// Copyright 2015 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 arm,!gccgo,!appengine,!nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
// This function is implemented in sum_arm.s
|
||||
//go:noescape
|
||||
func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]byte)
|
||||
|
||||
// Sum generates an authenticator for m using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[16]byte, m []byte, key *[32]byte) {
|
||||
var mPtr *byte
|
||||
if len(m) > 0 {
|
||||
mPtr = &m[0]
|
||||
}
|
||||
poly1305_auth_armv6(out, mPtr, uint32(len(m)), key)
|
||||
}
|
427
vendor/golang.org/x/crypto/poly1305/sum_arm.s
generated
vendored
Normal file
427
vendor/golang.org/x/crypto/poly1305/sum_arm.s
generated
vendored
Normal file
@ -0,0 +1,427 @@
|
||||
// Copyright 2015 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 arm,!gccgo,!appengine,!nacl
|
||||
|
||||
#include "textflag.h"
|
||||
|
||||
// This code was translated into a form compatible with 5a from the public
|
||||
// domain source by Andrew Moon: github.com/floodyberry/poly1305-opt/blob/master/app/extensions/poly1305.
|
||||
|
||||
DATA ·poly1305_init_constants_armv6<>+0x00(SB)/4, $0x3ffffff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x04(SB)/4, $0x3ffff03
|
||||
DATA ·poly1305_init_constants_armv6<>+0x08(SB)/4, $0x3ffc0ff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x0c(SB)/4, $0x3f03fff
|
||||
DATA ·poly1305_init_constants_armv6<>+0x10(SB)/4, $0x00fffff
|
||||
GLOBL ·poly1305_init_constants_armv6<>(SB), 8, $20
|
||||
|
||||
// Warning: the linker may use R11 to synthesize certain instructions. Please
|
||||
// take care and verify that no synthetic instructions use it.
|
||||
|
||||
TEXT poly1305_init_ext_armv6<>(SB), NOSPLIT, $0
|
||||
// Needs 16 bytes of stack and 64 bytes of space pointed to by R0. (It
|
||||
// might look like it's only 60 bytes of space but the final four bytes
|
||||
// will be written by another function.) We need to skip over four
|
||||
// bytes of stack because that's saving the value of 'g'.
|
||||
ADD $4, R13, R8
|
||||
MOVM.IB [R4-R7], (R8)
|
||||
MOVM.IA.W (R1), [R2-R5]
|
||||
MOVW $·poly1305_init_constants_armv6<>(SB), R7
|
||||
MOVW R2, R8
|
||||
MOVW R2>>26, R9
|
||||
MOVW R3>>20, g
|
||||
MOVW R4>>14, R11
|
||||
MOVW R5>>8, R12
|
||||
ORR R3<<6, R9, R9
|
||||
ORR R4<<12, g, g
|
||||
ORR R5<<18, R11, R11
|
||||
MOVM.IA (R7), [R2-R6]
|
||||
AND R8, R2, R2
|
||||
AND R9, R3, R3
|
||||
AND g, R4, R4
|
||||
AND R11, R5, R5
|
||||
AND R12, R6, R6
|
||||
MOVM.IA.W [R2-R6], (R0)
|
||||
EOR R2, R2, R2
|
||||
EOR R3, R3, R3
|
||||
EOR R4, R4, R4
|
||||
EOR R5, R5, R5
|
||||
EOR R6, R6, R6
|
||||
MOVM.IA.W [R2-R6], (R0)
|
||||
MOVM.IA.W (R1), [R2-R5]
|
||||
MOVM.IA [R2-R6], (R0)
|
||||
ADD $20, R13, R0
|
||||
MOVM.DA (R0), [R4-R7]
|
||||
RET
|
||||
|
||||
#define MOVW_UNALIGNED(Rsrc, Rdst, Rtmp, offset) \
|
||||
MOVBU (offset+0)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+0)(Rdst); \
|
||||
MOVBU (offset+1)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+1)(Rdst); \
|
||||
MOVBU (offset+2)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+2)(Rdst); \
|
||||
MOVBU (offset+3)(Rsrc), Rtmp; \
|
||||
MOVBU Rtmp, (offset+3)(Rdst)
|
||||
|
||||
TEXT poly1305_blocks_armv6<>(SB), NOSPLIT, $0
|
||||
// Needs 24 bytes of stack for saved registers and then 88 bytes of
|
||||
// scratch space after that. We assume that 24 bytes at (R13) have
|
||||
// already been used: four bytes for the link register saved in the
|
||||
// prelude of poly1305_auth_armv6, four bytes for saving the value of g
|
||||
// in that function and 16 bytes of scratch space used around
|
||||
// poly1305_finish_ext_armv6_skip1.
|
||||
ADD $24, R13, R12
|
||||
MOVM.IB [R4-R8, R14], (R12)
|
||||
MOVW R0, 88(R13)
|
||||
MOVW R1, 92(R13)
|
||||
MOVW R2, 96(R13)
|
||||
MOVW R1, R14
|
||||
MOVW R2, R12
|
||||
MOVW 56(R0), R8
|
||||
WORD $0xe1180008 // TST R8, R8 not working see issue 5921
|
||||
EOR R6, R6, R6
|
||||
MOVW.EQ $(1<<24), R6
|
||||
MOVW R6, 84(R13)
|
||||
ADD $116, R13, g
|
||||
MOVM.IA (R0), [R0-R9]
|
||||
MOVM.IA [R0-R4], (g)
|
||||
CMP $16, R12
|
||||
BLO poly1305_blocks_armv6_done
|
||||
|
||||
poly1305_blocks_armv6_mainloop:
|
||||
WORD $0xe31e0003 // TST R14, #3 not working see issue 5921
|
||||
BEQ poly1305_blocks_armv6_mainloop_aligned
|
||||
ADD $100, R13, g
|
||||
MOVW_UNALIGNED(R14, g, R0, 0)
|
||||
MOVW_UNALIGNED(R14, g, R0, 4)
|
||||
MOVW_UNALIGNED(R14, g, R0, 8)
|
||||
MOVW_UNALIGNED(R14, g, R0, 12)
|
||||
MOVM.IA (g), [R0-R3]
|
||||
ADD $16, R14
|
||||
B poly1305_blocks_armv6_mainloop_loaded
|
||||
|
||||
poly1305_blocks_armv6_mainloop_aligned:
|
||||
MOVM.IA.W (R14), [R0-R3]
|
||||
|
||||
poly1305_blocks_armv6_mainloop_loaded:
|
||||
MOVW R0>>26, g
|
||||
MOVW R1>>20, R11
|
||||
MOVW R2>>14, R12
|
||||
MOVW R14, 92(R13)
|
||||
MOVW R3>>8, R4
|
||||
ORR R1<<6, g, g
|
||||
ORR R2<<12, R11, R11
|
||||
ORR R3<<18, R12, R12
|
||||
BIC $0xfc000000, R0, R0
|
||||
BIC $0xfc000000, g, g
|
||||
MOVW 84(R13), R3
|
||||
BIC $0xfc000000, R11, R11
|
||||
BIC $0xfc000000, R12, R12
|
||||
ADD R0, R5, R5
|
||||
ADD g, R6, R6
|
||||
ORR R3, R4, R4
|
||||
ADD R11, R7, R7
|
||||
ADD $116, R13, R14
|
||||
ADD R12, R8, R8
|
||||
ADD R4, R9, R9
|
||||
MOVM.IA (R14), [R0-R4]
|
||||
MULLU R4, R5, (R11, g)
|
||||
MULLU R3, R5, (R14, R12)
|
||||
MULALU R3, R6, (R11, g)
|
||||
MULALU R2, R6, (R14, R12)
|
||||
MULALU R2, R7, (R11, g)
|
||||
MULALU R1, R7, (R14, R12)
|
||||
ADD R4<<2, R4, R4
|
||||
ADD R3<<2, R3, R3
|
||||
MULALU R1, R8, (R11, g)
|
||||
MULALU R0, R8, (R14, R12)
|
||||
MULALU R0, R9, (R11, g)
|
||||
MULALU R4, R9, (R14, R12)
|
||||
MOVW g, 76(R13)
|
||||
MOVW R11, 80(R13)
|
||||
MOVW R12, 68(R13)
|
||||
MOVW R14, 72(R13)
|
||||
MULLU R2, R5, (R11, g)
|
||||
MULLU R1, R5, (R14, R12)
|
||||
MULALU R1, R6, (R11, g)
|
||||
MULALU R0, R6, (R14, R12)
|
||||
MULALU R0, R7, (R11, g)
|
||||
MULALU R4, R7, (R14, R12)
|
||||
ADD R2<<2, R2, R2
|
||||
ADD R1<<2, R1, R1
|
||||
MULALU R4, R8, (R11, g)
|
||||
MULALU R3, R8, (R14, R12)
|
||||
MULALU R3, R9, (R11, g)
|
||||
MULALU R2, R9, (R14, R12)
|
||||
MOVW g, 60(R13)
|
||||
MOVW R11, 64(R13)
|
||||
MOVW R12, 52(R13)
|
||||
MOVW R14, 56(R13)
|
||||
MULLU R0, R5, (R11, g)
|
||||
MULALU R4, R6, (R11, g)
|
||||
MULALU R3, R7, (R11, g)
|
||||
MULALU R2, R8, (R11, g)
|
||||
MULALU R1, R9, (R11, g)
|
||||
ADD $52, R13, R0
|
||||
MOVM.IA (R0), [R0-R7]
|
||||
MOVW g>>26, R12
|
||||
MOVW R4>>26, R14
|
||||
ORR R11<<6, R12, R12
|
||||
ORR R5<<6, R14, R14
|
||||
BIC $0xfc000000, g, g
|
||||
BIC $0xfc000000, R4, R4
|
||||
ADD.S R12, R0, R0
|
||||
ADC $0, R1, R1
|
||||
ADD.S R14, R6, R6
|
||||
ADC $0, R7, R7
|
||||
MOVW R0>>26, R12
|
||||
MOVW R6>>26, R14
|
||||
ORR R1<<6, R12, R12
|
||||
ORR R7<<6, R14, R14
|
||||
BIC $0xfc000000, R0, R0
|
||||
BIC $0xfc000000, R6, R6
|
||||
ADD R14<<2, R14, R14
|
||||
ADD.S R12, R2, R2
|
||||
ADC $0, R3, R3
|
||||
ADD R14, g, g
|
||||
MOVW R2>>26, R12
|
||||
MOVW g>>26, R14
|
||||
ORR R3<<6, R12, R12
|
||||
BIC $0xfc000000, g, R5
|
||||
BIC $0xfc000000, R2, R7
|
||||
ADD R12, R4, R4
|
||||
ADD R14, R0, R0
|
||||
MOVW R4>>26, R12
|
||||
BIC $0xfc000000, R4, R8
|
||||
ADD R12, R6, R9
|
||||
MOVW 96(R13), R12
|
||||
MOVW 92(R13), R14
|
||||
MOVW R0, R6
|
||||
CMP $32, R12
|
||||
SUB $16, R12, R12
|
||||
MOVW R12, 96(R13)
|
||||
BHS poly1305_blocks_armv6_mainloop
|
||||
|
||||
poly1305_blocks_armv6_done:
|
||||
MOVW 88(R13), R12
|
||||
MOVW R5, 20(R12)
|
||||
MOVW R6, 24(R12)
|
||||
MOVW R7, 28(R12)
|
||||
MOVW R8, 32(R12)
|
||||
MOVW R9, 36(R12)
|
||||
ADD $48, R13, R0
|
||||
MOVM.DA (R0), [R4-R8, R14]
|
||||
RET
|
||||
|
||||
#define MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp) \
|
||||
MOVBU.P 1(Rsrc), Rtmp; \
|
||||
MOVBU.P Rtmp, 1(Rdst); \
|
||||
MOVBU.P 1(Rsrc), Rtmp; \
|
||||
MOVBU.P Rtmp, 1(Rdst)
|
||||
|
||||
#define MOVWP_UNALIGNED(Rsrc, Rdst, Rtmp) \
|
||||
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp); \
|
||||
MOVHUP_UNALIGNED(Rsrc, Rdst, Rtmp)
|
||||
|
||||
// func poly1305_auth_armv6(out *[16]byte, m *byte, mlen uint32, key *[32]key)
|
||||
TEXT ·poly1305_auth_armv6(SB), $196-16
|
||||
// The value 196, just above, is the sum of 64 (the size of the context
|
||||
// structure) and 132 (the amount of stack needed).
|
||||
//
|
||||
// At this point, the stack pointer (R13) has been moved down. It
|
||||
// points to the saved link register and there's 196 bytes of free
|
||||
// space above it.
|
||||
//
|
||||
// The stack for this function looks like:
|
||||
//
|
||||
// +---------------------
|
||||
// |
|
||||
// | 64 bytes of context structure
|
||||
// |
|
||||
// +---------------------
|
||||
// |
|
||||
// | 112 bytes for poly1305_blocks_armv6
|
||||
// |
|
||||
// +---------------------
|
||||
// | 16 bytes of final block, constructed at
|
||||
// | poly1305_finish_ext_armv6_skip8
|
||||
// +---------------------
|
||||
// | four bytes of saved 'g'
|
||||
// +---------------------
|
||||
// | lr, saved by prelude <- R13 points here
|
||||
// +---------------------
|
||||
MOVW g, 4(R13)
|
||||
|
||||
MOVW out+0(FP), R4
|
||||
MOVW m+4(FP), R5
|
||||
MOVW mlen+8(FP), R6
|
||||
MOVW key+12(FP), R7
|
||||
|
||||
ADD $136, R13, R0 // 136 = 4 + 4 + 16 + 112
|
||||
MOVW R7, R1
|
||||
|
||||
// poly1305_init_ext_armv6 will write to the stack from R13+4, but
|
||||
// that's ok because none of the other values have been written yet.
|
||||
BL poly1305_init_ext_armv6<>(SB)
|
||||
BIC.S $15, R6, R2
|
||||
BEQ poly1305_auth_armv6_noblocks
|
||||
ADD $136, R13, R0
|
||||
MOVW R5, R1
|
||||
ADD R2, R5, R5
|
||||
SUB R2, R6, R6
|
||||
BL poly1305_blocks_armv6<>(SB)
|
||||
|
||||
poly1305_auth_armv6_noblocks:
|
||||
ADD $136, R13, R0
|
||||
MOVW R5, R1
|
||||
MOVW R6, R2
|
||||
MOVW R4, R3
|
||||
|
||||
MOVW R0, R5
|
||||
MOVW R1, R6
|
||||
MOVW R2, R7
|
||||
MOVW R3, R8
|
||||
AND.S R2, R2, R2
|
||||
BEQ poly1305_finish_ext_armv6_noremaining
|
||||
EOR R0, R0
|
||||
ADD $8, R13, R9 // 8 = offset to 16 byte scratch space
|
||||
MOVW R0, (R9)
|
||||
MOVW R0, 4(R9)
|
||||
MOVW R0, 8(R9)
|
||||
MOVW R0, 12(R9)
|
||||
WORD $0xe3110003 // TST R1, #3 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_aligned
|
||||
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip8
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
|
||||
poly1305_finish_ext_armv6_skip8:
|
||||
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip4
|
||||
MOVWP_UNALIGNED(R1, R9, g)
|
||||
|
||||
poly1305_finish_ext_armv6_skip4:
|
||||
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip2
|
||||
MOVHUP_UNALIGNED(R1, R9, g)
|
||||
B poly1305_finish_ext_armv6_skip2
|
||||
|
||||
poly1305_finish_ext_armv6_aligned:
|
||||
WORD $0xe3120008 // TST R2, #8 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip8_aligned
|
||||
MOVM.IA.W (R1), [g-R11]
|
||||
MOVM.IA.W [g-R11], (R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip8_aligned:
|
||||
WORD $0xe3120004 // TST $4, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip4_aligned
|
||||
MOVW.P 4(R1), g
|
||||
MOVW.P g, 4(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip4_aligned:
|
||||
WORD $0xe3120002 // TST $2, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip2
|
||||
MOVHU.P 2(R1), g
|
||||
MOVH.P g, 2(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip2:
|
||||
WORD $0xe3120001 // TST $1, R2 not working see issue 5921
|
||||
BEQ poly1305_finish_ext_armv6_skip1
|
||||
MOVBU.P 1(R1), g
|
||||
MOVBU.P g, 1(R9)
|
||||
|
||||
poly1305_finish_ext_armv6_skip1:
|
||||
MOVW $1, R11
|
||||
MOVBU R11, 0(R9)
|
||||
MOVW R11, 56(R5)
|
||||
MOVW R5, R0
|
||||
ADD $8, R13, R1
|
||||
MOVW $16, R2
|
||||
BL poly1305_blocks_armv6<>(SB)
|
||||
|
||||
poly1305_finish_ext_armv6_noremaining:
|
||||
MOVW 20(R5), R0
|
||||
MOVW 24(R5), R1
|
||||
MOVW 28(R5), R2
|
||||
MOVW 32(R5), R3
|
||||
MOVW 36(R5), R4
|
||||
MOVW R4>>26, R12
|
||||
BIC $0xfc000000, R4, R4
|
||||
ADD R12<<2, R12, R12
|
||||
ADD R12, R0, R0
|
||||
MOVW R0>>26, R12
|
||||
BIC $0xfc000000, R0, R0
|
||||
ADD R12, R1, R1
|
||||
MOVW R1>>26, R12
|
||||
BIC $0xfc000000, R1, R1
|
||||
ADD R12, R2, R2
|
||||
MOVW R2>>26, R12
|
||||
BIC $0xfc000000, R2, R2
|
||||
ADD R12, R3, R3
|
||||
MOVW R3>>26, R12
|
||||
BIC $0xfc000000, R3, R3
|
||||
ADD R12, R4, R4
|
||||
ADD $5, R0, R6
|
||||
MOVW R6>>26, R12
|
||||
BIC $0xfc000000, R6, R6
|
||||
ADD R12, R1, R7
|
||||
MOVW R7>>26, R12
|
||||
BIC $0xfc000000, R7, R7
|
||||
ADD R12, R2, g
|
||||
MOVW g>>26, R12
|
||||
BIC $0xfc000000, g, g
|
||||
ADD R12, R3, R11
|
||||
MOVW $-(1<<26), R12
|
||||
ADD R11>>26, R12, R12
|
||||
BIC $0xfc000000, R11, R11
|
||||
ADD R12, R4, R9
|
||||
MOVW R9>>31, R12
|
||||
SUB $1, R12
|
||||
AND R12, R6, R6
|
||||
AND R12, R7, R7
|
||||
AND R12, g, g
|
||||
AND R12, R11, R11
|
||||
AND R12, R9, R9
|
||||
MVN R12, R12
|
||||
AND R12, R0, R0
|
||||
AND R12, R1, R1
|
||||
AND R12, R2, R2
|
||||
AND R12, R3, R3
|
||||
AND R12, R4, R4
|
||||
ORR R6, R0, R0
|
||||
ORR R7, R1, R1
|
||||
ORR g, R2, R2
|
||||
ORR R11, R3, R3
|
||||
ORR R9, R4, R4
|
||||
ORR R1<<26, R0, R0
|
||||
MOVW R1>>6, R1
|
||||
ORR R2<<20, R1, R1
|
||||
MOVW R2>>12, R2
|
||||
ORR R3<<14, R2, R2
|
||||
MOVW R3>>18, R3
|
||||
ORR R4<<8, R3, R3
|
||||
MOVW 40(R5), R6
|
||||
MOVW 44(R5), R7
|
||||
MOVW 48(R5), g
|
||||
MOVW 52(R5), R11
|
||||
ADD.S R6, R0, R0
|
||||
ADC.S R7, R1, R1
|
||||
ADC.S g, R2, R2
|
||||
ADC.S R11, R3, R3
|
||||
MOVM.IA [R0-R3], (R8)
|
||||
MOVW R5, R12
|
||||
EOR R0, R0, R0
|
||||
EOR R1, R1, R1
|
||||
EOR R2, R2, R2
|
||||
EOR R3, R3, R3
|
||||
EOR R4, R4, R4
|
||||
EOR R5, R5, R5
|
||||
EOR R6, R6, R6
|
||||
EOR R7, R7, R7
|
||||
MOVM.IA.W [R0-R7], (R12)
|
||||
MOVM.IA [R0-R7], (R12)
|
||||
MOVW 4(R13), g
|
||||
RET
|
141
vendor/golang.org/x/crypto/poly1305/sum_ref.go
generated
vendored
Normal file
141
vendor/golang.org/x/crypto/poly1305/sum_ref.go
generated
vendored
Normal file
@ -0,0 +1,141 @@
|
||||
// Copyright 2012 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 !amd64,!arm gccgo appengine nacl
|
||||
|
||||
package poly1305
|
||||
|
||||
import "encoding/binary"
|
||||
|
||||
// Sum generates an authenticator for msg using a one-time key and puts the
|
||||
// 16-byte result into out. Authenticating two different messages with the same
|
||||
// key allows an attacker to forge messages at will.
|
||||
func Sum(out *[TagSize]byte, msg []byte, key *[32]byte) {
|
||||
var (
|
||||
h0, h1, h2, h3, h4 uint32 // the hash accumulators
|
||||
r0, r1, r2, r3, r4 uint64 // the r part of the key
|
||||
)
|
||||
|
||||
r0 = uint64(binary.LittleEndian.Uint32(key[0:]) & 0x3ffffff)
|
||||
r1 = uint64((binary.LittleEndian.Uint32(key[3:]) >> 2) & 0x3ffff03)
|
||||
r2 = uint64((binary.LittleEndian.Uint32(key[6:]) >> 4) & 0x3ffc0ff)
|
||||
r3 = uint64((binary.LittleEndian.Uint32(key[9:]) >> 6) & 0x3f03fff)
|
||||
r4 = uint64((binary.LittleEndian.Uint32(key[12:]) >> 8) & 0x00fffff)
|
||||
|
||||
R1, R2, R3, R4 := r1*5, r2*5, r3*5, r4*5
|
||||
|
||||
for len(msg) >= TagSize {
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(msg[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(msg[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(msg[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(msg[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(msg[12:]) >> 8) | (1 << 24)
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
|
||||
msg = msg[TagSize:]
|
||||
}
|
||||
|
||||
if len(msg) > 0 {
|
||||
var block [TagSize]byte
|
||||
off := copy(block[:], msg)
|
||||
block[off] = 0x01
|
||||
|
||||
// h += msg
|
||||
h0 += binary.LittleEndian.Uint32(block[0:]) & 0x3ffffff
|
||||
h1 += (binary.LittleEndian.Uint32(block[3:]) >> 2) & 0x3ffffff
|
||||
h2 += (binary.LittleEndian.Uint32(block[6:]) >> 4) & 0x3ffffff
|
||||
h3 += (binary.LittleEndian.Uint32(block[9:]) >> 6) & 0x3ffffff
|
||||
h4 += (binary.LittleEndian.Uint32(block[12:]) >> 8)
|
||||
|
||||
// h *= r
|
||||
d0 := (uint64(h0) * r0) + (uint64(h1) * R4) + (uint64(h2) * R3) + (uint64(h3) * R2) + (uint64(h4) * R1)
|
||||
d1 := (d0 >> 26) + (uint64(h0) * r1) + (uint64(h1) * r0) + (uint64(h2) * R4) + (uint64(h3) * R3) + (uint64(h4) * R2)
|
||||
d2 := (d1 >> 26) + (uint64(h0) * r2) + (uint64(h1) * r1) + (uint64(h2) * r0) + (uint64(h3) * R4) + (uint64(h4) * R3)
|
||||
d3 := (d2 >> 26) + (uint64(h0) * r3) + (uint64(h1) * r2) + (uint64(h2) * r1) + (uint64(h3) * r0) + (uint64(h4) * R4)
|
||||
d4 := (d3 >> 26) + (uint64(h0) * r4) + (uint64(h1) * r3) + (uint64(h2) * r2) + (uint64(h3) * r1) + (uint64(h4) * r0)
|
||||
|
||||
// h %= p
|
||||
h0 = uint32(d0) & 0x3ffffff
|
||||
h1 = uint32(d1) & 0x3ffffff
|
||||
h2 = uint32(d2) & 0x3ffffff
|
||||
h3 = uint32(d3) & 0x3ffffff
|
||||
h4 = uint32(d4) & 0x3ffffff
|
||||
|
||||
h0 += uint32(d4>>26) * 5
|
||||
h1 += h0 >> 26
|
||||
h0 = h0 & 0x3ffffff
|
||||
}
|
||||
|
||||
// h %= p reduction
|
||||
h2 += h1 >> 26
|
||||
h1 &= 0x3ffffff
|
||||
h3 += h2 >> 26
|
||||
h2 &= 0x3ffffff
|
||||
h4 += h3 >> 26
|
||||
h3 &= 0x3ffffff
|
||||
h0 += 5 * (h4 >> 26)
|
||||
h4 &= 0x3ffffff
|
||||
h1 += h0 >> 26
|
||||
h0 &= 0x3ffffff
|
||||
|
||||
// h - p
|
||||
t0 := h0 + 5
|
||||
t1 := h1 + (t0 >> 26)
|
||||
t2 := h2 + (t1 >> 26)
|
||||
t3 := h3 + (t2 >> 26)
|
||||
t4 := h4 + (t3 >> 26) - (1 << 26)
|
||||
t0 &= 0x3ffffff
|
||||
t1 &= 0x3ffffff
|
||||
t2 &= 0x3ffffff
|
||||
t3 &= 0x3ffffff
|
||||
|
||||
// select h if h < p else h - p
|
||||
t_mask := (t4 >> 31) - 1
|
||||
h_mask := ^t_mask
|
||||
h0 = (h0 & h_mask) | (t0 & t_mask)
|
||||
h1 = (h1 & h_mask) | (t1 & t_mask)
|
||||
h2 = (h2 & h_mask) | (t2 & t_mask)
|
||||
h3 = (h3 & h_mask) | (t3 & t_mask)
|
||||
h4 = (h4 & h_mask) | (t4 & t_mask)
|
||||
|
||||
// h %= 2^128
|
||||
h0 |= h1 << 26
|
||||
h1 = ((h1 >> 6) | (h2 << 20))
|
||||
h2 = ((h2 >> 12) | (h3 << 14))
|
||||
h3 = ((h3 >> 18) | (h4 << 8))
|
||||
|
||||
// s: the s part of the key
|
||||
// tag = (h + s) % (2^128)
|
||||
t := uint64(h0) + uint64(binary.LittleEndian.Uint32(key[16:]))
|
||||
h0 = uint32(t)
|
||||
t = uint64(h1) + uint64(binary.LittleEndian.Uint32(key[20:])) + (t >> 32)
|
||||
h1 = uint32(t)
|
||||
t = uint64(h2) + uint64(binary.LittleEndian.Uint32(key[24:])) + (t >> 32)
|
||||
h2 = uint32(t)
|
||||
t = uint64(h3) + uint64(binary.LittleEndian.Uint32(key[28:])) + (t >> 32)
|
||||
h3 = uint32(t)
|
||||
|
||||
binary.LittleEndian.PutUint32(out[0:], h0)
|
||||
binary.LittleEndian.PutUint32(out[4:], h1)
|
||||
binary.LittleEndian.PutUint32(out[8:], h2)
|
||||
binary.LittleEndian.PutUint32(out[12:], h3)
|
||||
}
|
24
vendor/vendor.json
vendored
24
vendor/vendor.json
vendored
@ -389,6 +389,12 @@
|
||||
"revision": "43ed500fe4d485d97534014d9f98521216240002",
|
||||
"revisionTime": "2017-08-28T17:39:33Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "/VYXTlcksV92qL4Avr5S3NJ/Fxs=",
|
||||
"path": "github.com/minio/sio",
|
||||
"revision": "d8be2518a912f0db0dd17dc55f46da4360ee60d8",
|
||||
"revisionTime": "2017-09-06T19:57:40Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "zvQr4zOz1/g/Fui6co0sctxrJ28=",
|
||||
"path": "github.com/nats-io/go-nats",
|
||||
@ -488,6 +494,24 @@
|
||||
"revision": "3b8db5e93c4c02efbc313e17b2e796b0914a01fb",
|
||||
"revisionTime": "2016-12-15T19:56:52Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "CvMkf3KUUGUVHibg6G/zI7XtVbM=",
|
||||
"path": "golang.org/x/crypto/chacha20poly1305",
|
||||
"revision": "81e90905daefcd6fd217b62423c0908922eadb30",
|
||||
"revisionTime": "2017-08-25T20:24:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "8f1lWFSOLu7O6Jqk/e+ydH1yoms=",
|
||||
"path": "golang.org/x/crypto/chacha20poly1305/internal/chacha20",
|
||||
"revision": "81e90905daefcd6fd217b62423c0908922eadb30",
|
||||
"revisionTime": "2017-08-25T20:24:07Z"
|
||||
},
|
||||
{
|
||||
"checksumSHA1": "kVKE0OX1Xdw5mG7XKT86DLLKE2I=",
|
||||
"path": "golang.org/x/crypto/poly1305",
|
||||
"revision": "81e90905daefcd6fd217b62423c0908922eadb30",
|
||||
"revisionTime": "2017-08-25T20:24:07Z"
|
||||
},
|
||||
{
|
||||
"path": "golang.org/x/net/context",
|
||||
"revision": "a728288923b47049b2ce791836767ffbe964a5bd",
|
||||
|
Loading…
Reference in New Issue
Block a user