Add crypto context errors (#8740)

Currently when connections to vault fail, client
perpetually retries this leads to assumptions that
the server has issues and masks the problem.

Re-purpose *crypto.Error* type to send appropriate
errors back to the client.
This commit is contained in:
Harshavardhana 2020-01-06 16:15:22 -08:00 committed by kannappanr
parent 796cca4166
commit 933c60bc3a
18 changed files with 139 additions and 100 deletions

View File

@ -12,7 +12,7 @@ RUN \
git clone https://github.com/minio/minio && cd minio && \ git clone https://github.com/minio/minio && cd minio && \
go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)" go install -v -ldflags "$(go run buildscripts/gen-ldflags.go)"
FROM alpine:3.9 FROM alpine:3.10
ENV MINIO_UPDATE off ENV MINIO_UPDATE off
ENV MINIO_ACCESS_KEY_FILE=access_key \ ENV MINIO_ACCESS_KEY_FILE=access_key \

View File

@ -1,4 +1,4 @@
FROM alpine:3.9 FROM alpine:3.10
LABEL maintainer="MinIO Inc <dev@min.io>" LABEL maintainer="MinIO Inc <dev@min.io>"

View File

@ -8,7 +8,7 @@ RUN \
apk add --no-cache git && \ apk add --no-cache git && \
git clone https://github.com/minio/minio git clone https://github.com/minio/minio
FROM alpine:3.9 FROM alpine:3.10
LABEL maintainer="MinIO Inc <dev@min.io>" LABEL maintainer="MinIO Inc <dev@min.io>"

View File

@ -1727,8 +1727,6 @@ func toAPIErrorCode(ctx context.Context, err error) (apiErr APIErrorCode) {
apiErr = ErrUnsupportedNotification apiErr = ErrUnsupportedNotification
case BackendDown: case BackendDown:
apiErr = ErrBackendDown apiErr = ErrBackendDown
case crypto.Error:
apiErr = ErrObjectTampered
case ObjectNameTooLong: case ObjectNameTooLong:
apiErr = ErrKeyTooLongError apiErr = ErrKeyTooLongError
default: default:
@ -1781,7 +1779,7 @@ func toAPIError(ctx context.Context, err error) APIError {
} }
case crypto.Error: case crypto.Error:
apiErr = APIError{ apiErr = APIError{
Code: "XKMSInternalError", Code: "XMinIOEncryptionError",
Description: e.Error(), Description: e.Error(),
HTTPStatusCode: http.StatusBadRequest, HTTPStatusCode: http.StatusBadRequest,
} }

View File

@ -16,7 +16,6 @@ package crypto
import ( import (
"errors" "errors"
"fmt"
"net/http" "net/http"
"reflect" "reflect"
"strconv" "strconv"
@ -342,7 +341,7 @@ func LookupVaultConfig(kvs config.KVS) (VaultConfig, error) {
if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" { if keyVersion := env.Get(EnvKMSVaultKeyVersion, kvs.Get(KMSVaultKeyVersion)); keyVersion != "" {
vcfg.Key.Version, err = strconv.Atoi(keyVersion) vcfg.Key.Version, err = strconv.Atoi(keyVersion)
if err != nil { if err != nil {
return vcfg, fmt.Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion) return vcfg, Errorf("Unable to parse VaultKeyVersion value (`%s`)", keyVersion)
} }
} }

View File

@ -14,60 +14,79 @@
package crypto package crypto
import "errors" import (
"fmt"
)
// Error is the generic type for any error happening during decrypting // Error is the generic type for any error happening during decrypting
// an object. It indicates that the object itself or its metadata was // an object. It indicates that the object itself or its metadata was
// modified accidentally or maliciously. // modified accidentally or maliciously.
type Error string type Error struct {
err error
}
func (e Error) Error() string { return string(e) } // Errorf - formats according to a format specifier and returns
// the string as a value that satisfies error of type crypto.Error
func Errorf(format string, a ...interface{}) error {
return Error{err: fmt.Errorf(format, a...)}
}
// Unwrap the internal error.
func (e Error) Unwrap() error { return e.err }
// Error 'error' compatible method.
func (e Error) Error() string {
if e.err == nil {
return "crypto: cause <nil>"
}
return e.err.Error()
}
var ( var (
// ErrInvalidEncryptionMethod indicates that the specified SSE encryption method // ErrInvalidEncryptionMethod indicates that the specified SSE encryption method
// is not supported. // is not supported.
ErrInvalidEncryptionMethod = errors.New("The encryption method is not supported") ErrInvalidEncryptionMethod = Errorf("The encryption method is not supported")
// ErrInvalidCustomerAlgorithm indicates that the specified SSE-C algorithm // ErrInvalidCustomerAlgorithm indicates that the specified SSE-C algorithm
// is not supported. // is not supported.
ErrInvalidCustomerAlgorithm = errors.New("The SSE-C algorithm is not supported") ErrInvalidCustomerAlgorithm = Errorf("The SSE-C algorithm is not supported")
// ErrMissingCustomerKey indicates that the HTTP headers contains no SSE-C client key. // ErrMissingCustomerKey indicates that the HTTP headers contains no SSE-C client key.
ErrMissingCustomerKey = errors.New("The SSE-C request is missing the customer key") ErrMissingCustomerKey = Errorf("The SSE-C request is missing the customer key")
// ErrMissingCustomerKeyMD5 indicates that the HTTP headers contains no SSE-C client key // ErrMissingCustomerKeyMD5 indicates that the HTTP headers contains no SSE-C client key
// MD5 checksum. // MD5 checksum.
ErrMissingCustomerKeyMD5 = errors.New("The SSE-C request is missing the customer key MD5") ErrMissingCustomerKeyMD5 = Errorf("The SSE-C request is missing the customer key MD5")
// ErrInvalidCustomerKey indicates that the SSE-C client key is not valid - e.g. not a // ErrInvalidCustomerKey indicates that the SSE-C client key is not valid - e.g. not a
// base64-encoded string or not 256 bits long. // base64-encoded string or not 256 bits long.
ErrInvalidCustomerKey = errors.New("The SSE-C client key is invalid") ErrInvalidCustomerKey = Errorf("The SSE-C client key is invalid")
// ErrSecretKeyMismatch indicates that the provided secret key (SSE-C client key / SSE-S3 KMS key) // ErrSecretKeyMismatch indicates that the provided secret key (SSE-C client key / SSE-S3 KMS key)
// does not match the secret key used during encrypting the object. // does not match the secret key used during encrypting the object.
ErrSecretKeyMismatch = errors.New("The secret key does not match the secret key used during upload") ErrSecretKeyMismatch = Errorf("The secret key does not match the secret key used during upload")
// ErrCustomerKeyMD5Mismatch indicates that the SSE-C key MD5 does not match the // ErrCustomerKeyMD5Mismatch indicates that the SSE-C key MD5 does not match the
// computed MD5 sum. This means that the client provided either the wrong key for // computed MD5 sum. This means that the client provided either the wrong key for
// a certain MD5 checksum or the wrong MD5 for a certain key. // a certain MD5 checksum or the wrong MD5 for a certain key.
ErrCustomerKeyMD5Mismatch = errors.New("The provided SSE-C key MD5 does not match the computed MD5 of the SSE-C key") ErrCustomerKeyMD5Mismatch = Errorf("The provided SSE-C key MD5 does not match the computed MD5 of the SSE-C key")
// ErrIncompatibleEncryptionMethod indicates that both SSE-C headers and SSE-S3 headers were specified, and are incompatible // ErrIncompatibleEncryptionMethod indicates that both SSE-C headers and SSE-S3 headers were specified, and are incompatible
// The client needs to remove the SSE-S3 header or the SSE-C headers // The client needs to remove the SSE-S3 header or the SSE-C headers
ErrIncompatibleEncryptionMethod = errors.New("Server side encryption specified with both SSE-C and SSE-S3 headers") ErrIncompatibleEncryptionMethod = Errorf("Server side encryption specified with both SSE-C and SSE-S3 headers")
) )
const ( var (
errMissingInternalIV Error = "The object metadata is missing the internal encryption IV" errMissingInternalIV = Errorf("The object metadata is missing the internal encryption IV")
errMissingInternalSealAlgorithm Error = "The object metadata is missing the internal seal algorithm" errMissingInternalSealAlgorithm = Errorf("The object metadata is missing the internal seal algorithm")
errInvalidInternalIV Error = "The internal encryption IV is malformed" errInvalidInternalIV = Errorf("The internal encryption IV is malformed")
errInvalidInternalSealAlgorithm Error = "The internal seal algorithm is invalid and not supported" errInvalidInternalSealAlgorithm = Errorf("The internal seal algorithm is invalid and not supported")
errMissingUpdatedKey Error = "The key update returned no error but also no sealed key" errMissingUpdatedKey = Errorf("The key update returned no error but also no sealed key")
) )
var ( var (
// errOutOfEntropy indicates that the a source of randomness (PRNG) wasn't able // errOutOfEntropy indicates that the a source of randomness (PRNG) wasn't able
// to produce enough random data. This is fatal error and should cause a panic. // to produce enough random data. This is fatal error and should cause a panic.
errOutOfEntropy = errors.New("Unable to read enough randomness from the system") errOutOfEntropy = Errorf("Unable to read enough randomness from the system")
) )

View File

@ -19,7 +19,6 @@ import (
"crypto/tls" "crypto/tls"
"crypto/x509" "crypto/x509"
"encoding/json" "encoding/json"
"errors"
"fmt" "fmt"
"io" "io"
"io/ioutil" "io/ioutil"
@ -76,13 +75,13 @@ type KesConfig struct {
func (k KesConfig) Verify() (err error) { func (k KesConfig) Verify() (err error) {
switch { switch {
case k.Endpoint == "": case k.Endpoint == "":
err = errors.New("crypto: missing kes endpoint") err = Errorf("crypto: missing kes endpoint")
case k.CertFile == "": case k.CertFile == "":
err = errors.New("crypto: missing cert file") err = Errorf("crypto: missing cert file")
case k.KeyFile == "": case k.KeyFile == "":
err = errors.New("crypto: missing key file") err = Errorf("crypto: missing key file")
case k.DefaultKeyID == "": case k.DefaultKeyID == "":
err = errors.New("crypto: missing default key id") err = Errorf("crypto: missing default key id")
} }
return err return err
} }
@ -153,7 +152,7 @@ func (kes *kesService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
return key, nil, err return key, nil, err
} }
if len(plainKey) != len(key) { if len(plainKey) != len(key) {
return key, nil, errors.New("crypto: received invalid plaintext key size from KMS") return key, nil, Errorf("crypto: received invalid plaintext key size from KMS")
} }
copy(key[:], plainKey) copy(key[:], plainKey)
return key, sealedKey, nil return key, sealedKey, nil
@ -176,7 +175,7 @@ func (kes *kesService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k
return key, err return key, err
} }
if len(plainKey) != len(key) { if len(plainKey) != len(key) {
return key, errors.New("crypto: received invalid plaintext key size from KMS") return key, Errorf("crypto: received invalid plaintext key size from KMS")
} }
copy(key[:], plainKey) copy(key[:], plainKey)
return key, nil return key, nil
@ -301,7 +300,7 @@ func (c *kesClient) parseErrorResponse(resp *http.Response) error {
if _, err := io.Copy(&errMsg, io.LimitReader(resp.Body, limit)); err != nil { if _, err := io.Copy(&errMsg, io.LimitReader(resp.Body, limit)); err != nil {
return err return err
} }
return fmt.Errorf("%s: %s", http.StatusText(resp.StatusCode), errMsg.String()) return Errorf("%s: %s", http.StatusText(resp.StatusCode), errMsg.String())
} }
// loadCACertificates returns a new CertPool // loadCACertificates returns a new CertPool
@ -334,7 +333,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) {
if os.IsNotExist(err) || os.IsPermission(err) { if os.IsNotExist(err) || os.IsPermission(err) {
return rootCAs, nil return rootCAs, nil
} }
return nil, fmt.Errorf("crypto: cannot open '%s': %v", path, err) return nil, Errorf("crypto: cannot open '%s': %v", path, err)
} }
// If path is a file, parse as PEM-encoded certifcate // If path is a file, parse as PEM-encoded certifcate
@ -346,7 +345,7 @@ func loadCACertificates(path string) (*x509.CertPool, error) {
return nil, err return nil, err
} }
if !rootCAs.AppendCertsFromPEM(cert) { if !rootCAs.AppendCertsFromPEM(cert) {
return nil, fmt.Errorf("crypto: '%s' is not a valid PEM-encoded certificate", path) return nil, Errorf("crypto: '%s' is not a valid PEM-encoded certificate", path)
} }
return rootCAs, nil return rootCAs, nil
} }

View File

@ -21,7 +21,6 @@ import (
"crypto/rand" "crypto/rand"
"encoding/binary" "encoding/binary"
"errors" "errors"
"fmt"
"io" "io"
"path" "path"
@ -108,7 +107,7 @@ func (key *ObjectKey) Unseal(extKey [32]byte, sealedKey SealedKey, domain, bucke
) )
switch sealedKey.Algorithm { switch sealedKey.Algorithm {
default: default:
return Error(fmt.Sprintf("The sealing algorithm '%s' is not supported", sealedKey.Algorithm)) return Errorf("The sealing algorithm '%s' is not supported", sealedKey.Algorithm)
case SealAlgorithm: case SealAlgorithm:
mac := hmac.New(sha256.New, extKey[:]) mac := hmac.New(sha256.New, extKey[:])
mac.Write(sealedKey.IV[:]) mac.Write(sealedKey.IV[:])

View File

@ -17,7 +17,6 @@
package crypto package crypto
import ( import (
"fmt"
"reflect" "reflect"
"strconv" "strconv"
@ -167,7 +166,7 @@ func lookupConfigLegacy(kvs config.KVS) (VaultConfig, error) {
if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" { if keyVersion := env.Get(EnvLegacyVaultKeyVersion, ""); keyVersion != "" {
vcfg.Key.Version, err = strconv.Atoi(keyVersion) vcfg.Key.Version, err = strconv.Atoi(keyVersion)
if err != nil { if err != nil {
return vcfg, fmt.Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)", return vcfg, Errorf("Invalid ENV variable: Unable to parse %s value (`%s`)",
EnvLegacyVaultKeyVersion, keyVersion) EnvLegacyVaultKeyVersion, keyVersion)
} }
} }

View File

@ -18,7 +18,6 @@ import (
"context" "context"
"encoding/base64" "encoding/base64"
"errors" "errors"
"fmt"
"github.com/minio/minio/cmd/logger" "github.com/minio/minio/cmd/logger"
) )
@ -126,7 +125,7 @@ func CreateMultipartMetadata(metadata map[string]string) map[string]string {
// is nil. // is nil.
func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string { func (s3) CreateMetadata(metadata map[string]string, keyID string, kmsKey []byte, sealedKey SealedKey) map[string]string {
if sealedKey.Algorithm != SealAlgorithm { if sealedKey.Algorithm != SealAlgorithm {
logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm)) logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-S3", sealedKey.Algorithm))
} }
// There are two possibilites: // There are two possibilites:
@ -172,7 +171,7 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
} }
b64SealedKey, ok := metadata[S3SealedKey] b64SealedKey, ok := metadata[S3SealedKey]
if !ok { if !ok {
return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal sealed key for SSE-S3") return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-S3")
} }
// There are two possibilites: // There are two possibilites:
@ -182,10 +181,10 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
keyID, idPresent := metadata[S3KMSKeyID] keyID, idPresent := metadata[S3KMSKeyID]
b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey] b64KMSSealedKey, kmsKeyPresent := metadata[S3KMSSealedKey]
if !idPresent && kmsKeyPresent { if !idPresent && kmsKeyPresent {
return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal KMS key-ID for SSE-S3") return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3")
} }
if idPresent && !kmsKeyPresent { if idPresent && !kmsKeyPresent {
return keyID, kmsKey, sealedKey, Error("The object metadata is missing the internal sealed KMS data key for SSE-S3") return keyID, kmsKey, sealedKey, Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3")
} }
// Check whether all extracted values are well-formed // Check whether all extracted values are well-formed
@ -198,12 +197,12 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
} }
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
if err != nil || len(encryptedKey) != 64 { if err != nil || len(encryptedKey) != 64 {
return keyID, kmsKey, sealedKey, Error("The internal sealed key for SSE-S3 is invalid") return keyID, kmsKey, sealedKey, Errorf("The internal sealed key for SSE-S3 is invalid")
} }
if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key. if idPresent && kmsKeyPresent { // We are using a KMS -> parse the sealed KMS data key.
kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey) kmsKey, err = base64.StdEncoding.DecodeString(b64KMSSealedKey)
if err != nil { if err != nil {
return keyID, kmsKey, sealedKey, Error("The internal sealed KMS data key for SSE-S3 is invalid") return keyID, kmsKey, sealedKey, Errorf("The internal sealed KMS data key for SSE-S3 is invalid")
} }
} }
@ -217,7 +216,7 @@ func (s3) ParseMetadata(metadata map[string]string) (keyID string, kmsKey []byte
// It allocates a new metadata map if metadata is nil. // It allocates a new metadata map if metadata is nil.
func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string { func (ssec) CreateMetadata(metadata map[string]string, sealedKey SealedKey) map[string]string {
if sealedKey.Algorithm != SealAlgorithm { if sealedKey.Algorithm != SealAlgorithm {
logger.CriticalIf(context.Background(), fmt.Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm)) logger.CriticalIf(context.Background(), Errorf("The seal algorithm '%s' is invalid for SSE-C", sealedKey.Algorithm))
} }
if metadata == nil { if metadata == nil {
@ -244,7 +243,7 @@ func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err
} }
b64SealedKey, ok := metadata[SSECSealedKey] b64SealedKey, ok := metadata[SSECSealedKey]
if !ok { if !ok {
return sealedKey, Error("The object metadata is missing the internal sealed key for SSE-C") return sealedKey, Errorf("The object metadata is missing the internal sealed key for SSE-C")
} }
// Check whether all extracted values are well-formed // Check whether all extracted values are well-formed
@ -257,7 +256,7 @@ func (ssec) ParseMetadata(metadata map[string]string) (sealedKey SealedKey, err
} }
encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey) encryptedKey, err := base64.StdEncoding.DecodeString(b64SealedKey)
if err != nil || len(encryptedKey) != 64 { if err != nil || len(encryptedKey) != 64 {
return sealedKey, Error("The internal sealed key for SSE-C is invalid") return sealedKey, Errorf("The internal sealed key for SSE-C is invalid")
} }
sealedKey.Algorithm = algorithm sealedKey.Algorithm = algorithm

View File

@ -125,15 +125,15 @@ var s3ParseMetadataTests = []struct {
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 1 }, // 1
{ {
ExpectedErr: Error("The object metadata is missing the internal sealed key for SSE-S3"), ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-S3"),
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 2 }, // 2
{ {
ExpectedErr: Error("The object metadata is missing the internal KMS key-ID for SSE-S3"), ExpectedErr: Errorf("The object metadata is missing the internal KMS key-ID for SSE-S3"),
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSSealedKey: "IAAF0b=="}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 3 }, // 3
{ {
ExpectedErr: Error("The object metadata is missing the internal sealed KMS data key for SSE-S3"), ExpectedErr: Errorf("The object metadata is missing the internal sealed KMS data key for SSE-S3"),
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""}, Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: "", S3SealedKey: "", S3KMSKeyID: ""},
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 4 }, // 4
@ -150,7 +150,7 @@ var s3ParseMetadataTests = []struct {
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 6 }, // 6
{ {
ExpectedErr: Error("The internal sealed key for SSE-S3 is invalid"), ExpectedErr: Errorf("The internal sealed key for SSE-S3 is invalid"),
Metadata: map[string]string{ Metadata: map[string]string{
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "", SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, S3SealedKey: "",
S3KMSKeyID: "", S3KMSSealedKey: "", S3KMSKeyID: "", S3KMSSealedKey: "",
@ -158,7 +158,7 @@ var s3ParseMetadataTests = []struct {
DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{}, DataKey: []byte{}, KeyID: "", SealedKey: SealedKey{},
}, // 7 }, // 7
{ {
ExpectedErr: Error("The internal sealed KMS data key for SSE-S3 is invalid"), ExpectedErr: Errorf("The internal sealed KMS data key for SSE-S3 is invalid"),
Metadata: map[string]string{ Metadata: map[string]string{
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm,
S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1", S3SealedKey: base64.StdEncoding.EncodeToString(make([]byte, 64)), S3KMSKeyID: "key-1",
@ -218,7 +218,7 @@ var ssecParseMetadataTests = []struct {
{ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0 {ExpectedErr: errMissingInternalIV, Metadata: map[string]string{}, SealedKey: SealedKey{}}, // 0
{ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1 {ExpectedErr: errMissingInternalSealAlgorithm, Metadata: map[string]string{SSEIV: ""}, SealedKey: SealedKey{}}, // 1
{ {
ExpectedErr: Error("The object metadata is missing the internal sealed key for SSE-C"), ExpectedErr: Errorf("The object metadata is missing the internal sealed key for SSE-C"),
Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{}, Metadata: map[string]string{SSEIV: "", SSESealAlgorithm: ""}, SealedKey: SealedKey{},
}, // 2 }, // 2
{ {
@ -233,7 +233,7 @@ var ssecParseMetadataTests = []struct {
SealedKey: SealedKey{}, SealedKey: SealedKey{},
}, // 4 }, // 4
{ {
ExpectedErr: Error("The internal sealed key for SSE-C is invalid"), ExpectedErr: Errorf("The internal sealed key for SSE-C is invalid"),
Metadata: map[string]string{ Metadata: map[string]string{
SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "", SSEIV: base64.StdEncoding.EncodeToString(make([]byte, 32)), SSESealAlgorithm: SealAlgorithm, SSECSealedKey: "",
}, },

View File

@ -16,7 +16,6 @@ package crypto
import ( import (
"encoding/hex" "encoding/hex"
"fmt"
"strings" "strings"
) )
@ -25,18 +24,18 @@ import (
func ParseMasterKey(envArg string) (KMS, error) { func ParseMasterKey(envArg string) (KMS, error) {
values := strings.SplitN(envArg, ":", 2) values := strings.SplitN(envArg, ":", 2)
if len(values) != 2 { if len(values) != 2 {
return nil, fmt.Errorf("Invalid KMS master key: %s does not contain a ':'", envArg) return nil, Errorf("Invalid KMS master key: %s does not contain a ':'", envArg)
} }
var ( var (
keyID = values[0] keyID = values[0]
hexKey = values[1] hexKey = values[1]
) )
if len(hexKey) != 64 { // 2 hex bytes = 1 byte if len(hexKey) != 64 { // 2 hex bytes = 1 byte
return nil, fmt.Errorf("Invalid KMS master key: %s not a 32 bytes long HEX value", hexKey) return nil, Errorf("Invalid KMS master key: %s not a 32 bytes long HEX value", hexKey)
} }
var masterKey [32]byte var masterKey [32]byte
if _, err := hex.Decode(masterKey[:], []byte(hexKey)); err != nil { if _, err := hex.Decode(masterKey[:], []byte(hexKey)); err != nil {
return nil, err return nil, Errorf("Invalid KMS master key: %v", err)
} }
return NewMasterKey(keyID, masterKey), nil return NewMasterKey(keyID, masterKey), nil
} }

View File

@ -17,7 +17,6 @@ package crypto
import ( import (
"bytes" "bytes"
"encoding/base64" "encoding/base64"
"errors"
"fmt" "fmt"
"strings" "strings"
"time" "time"
@ -27,7 +26,7 @@ import (
var ( var (
//ErrKMSAuthLogin is raised when there is a failure authenticating to KMS //ErrKMSAuthLogin is raised when there is a failure authenticating to KMS
ErrKMSAuthLogin = errors.New("Vault service did not return auth info") ErrKMSAuthLogin = Errorf("Vault service did not return auth info")
) )
// VaultKey represents vault encryption key-ring. // VaultKey represents vault encryption key-ring.
@ -75,17 +74,17 @@ var _ KMS = (*vaultService)(nil) // compiler check that *vaultService implements
func (v *VaultConfig) Verify() (err error) { func (v *VaultConfig) Verify() (err error) {
switch { switch {
case v.Endpoint == "": case v.Endpoint == "":
err = errors.New("crypto: missing hashicorp vault endpoint") err = Errorf("crypto: missing hashicorp vault endpoint")
case strings.ToLower(v.Auth.Type) != "approle": case strings.ToLower(v.Auth.Type) != "approle":
err = fmt.Errorf("crypto: invalid hashicorp vault authentication type: %s is not supported", v.Auth.Type) err = Errorf("crypto: invalid hashicorp vault authentication type: %s is not supported", v.Auth.Type)
case v.Auth.AppRole.ID == "": case v.Auth.AppRole.ID == "":
err = errors.New("crypto: missing hashicorp vault AppRole ID") err = Errorf("crypto: missing hashicorp vault AppRole ID")
case v.Auth.AppRole.Secret == "": case v.Auth.AppRole.Secret == "":
err = errors.New("crypto: missing hashicorp vault AppSecret ID") err = Errorf("crypto: missing hashicorp vault AppSecret ID")
case v.Key.Name == "": case v.Key.Name == "":
err = errors.New("crypto: missing hashicorp vault key name") err = Errorf("crypto: missing hashicorp vault key name")
case v.Key.Version < 0: case v.Key.Version < 0:
err = errors.New("crypto: invalid hashicorp vault key version: The key version must not be negative") err = Errorf("crypto: invalid hashicorp vault key version: The key version must not be negative")
} }
return return
} }
@ -107,7 +106,7 @@ func NewVault(config VaultConfig) (KMS, error) {
} }
client, err := vault.NewClient(&vaultCfg) client, err := vault.NewClient(&vaultCfg)
if err != nil { if err != nil {
return nil, err return nil, Errorf("crypto: client error %w", err)
} }
if config.Namespace != "" { if config.Namespace != "" {
client.SetNamespace(config.Namespace) client.SetNamespace(config.Namespace)
@ -167,6 +166,7 @@ func (v *vaultService) authenticate() (err error) {
var secret *vault.Secret var secret *vault.Secret
secret, err = v.client.Logical().Write("auth/approle/login", payload) secret, err = v.client.Logical().Write("auth/approle/login", payload)
if err != nil { if err != nil {
err = Errorf("crypto: client error %w", err)
return return
} }
if secret == nil { if secret == nil {
@ -217,14 +217,23 @@ func (v *vaultService) GenerateKey(keyID string, ctx Context) (key [32]byte, sea
} }
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload) s, err := v.client.Logical().Write(fmt.Sprintf("/transit/datakey/plaintext/%s", keyID), payload)
if err != nil { if err != nil {
return key, sealedKey, err return key, sealedKey, Errorf("crypto: client error %w", err)
} }
sealKey := s.Data["ciphertext"].(string) sealKey, ok := s.Data["ciphertext"].(string)
plainKey, err := base64.StdEncoding.DecodeString(s.Data["plaintext"].(string)) if !ok {
return key, sealedKey, Errorf("crypto: incorrect 'ciphertext' key type %v", s.Data["ciphertext"])
}
plainKeyB64, ok := s.Data["plaintext"].(string)
if !ok {
return key, sealedKey, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
}
plainKey, err := base64.StdEncoding.DecodeString(plainKeyB64)
if err != nil { if err != nil {
return key, sealedKey, err return key, sealedKey, Errorf("crypto: invalid base64 key %w", err)
} }
copy(key[:], []byte(plainKey)) copy(key[:], plainKey)
return key, []byte(sealKey), nil return key, []byte(sealKey), nil
} }
@ -243,16 +252,22 @@ func (v *vaultService) UnsealKey(keyID string, sealedKey []byte, ctx Context) (k
"ciphertext": string(sealedKey), "ciphertext": string(sealedKey),
"context": base64.StdEncoding.EncodeToString(contextStream.Bytes()), "context": base64.StdEncoding.EncodeToString(contextStream.Bytes()),
} }
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload) s, err := v.client.Logical().Write(fmt.Sprintf("/transit/decrypt/%s", keyID), payload)
if err != nil { if err != nil {
return key, err return key, Errorf("crypto: client error %w", err)
} }
base64Key := s.Data["plaintext"].(string)
base64Key, ok := s.Data["plaintext"].(string)
if !ok {
return key, Errorf("crypto: incorrect 'plaintext' key type %v", s.Data["plaintext"])
}
plainKey, err := base64.StdEncoding.DecodeString(base64Key) plainKey, err := base64.StdEncoding.DecodeString(base64Key)
if err != nil { if err != nil {
return key, err return key, Errorf("crypto: invalid base64 key %w", err)
} }
copy(key[:], []byte(plainKey)) copy(key[:], plainKey)
return key, nil return key, nil
} }
@ -273,7 +288,7 @@ func (v *vaultService) UpdateKey(keyID string, sealedKey []byte, ctx Context) (r
} }
s, err := v.client.Logical().Write(fmt.Sprintf("/transit/rewrap/%s", keyID), payload) s, err := v.client.Logical().Write(fmt.Sprintf("/transit/rewrap/%s", keyID), payload)
if err != nil { if err != nil {
return nil, err return nil, Errorf("crypto: client error %w", err)
} }
ciphertext, ok := s.Data["ciphertext"] ciphertext, ok := s.Data["ciphertext"]
if !ok { if !ok {

View File

@ -2026,13 +2026,20 @@ func toJSONError(ctx context.Context, err error, params ...string) (jerr *json2.
// toWebAPIError - convert into error into APIError. // toWebAPIError - convert into error into APIError.
func toWebAPIError(ctx context.Context, err error) APIError { func toWebAPIError(ctx context.Context, err error) APIError {
switch err { switch err {
case errNoAuthToken:
return APIError{
Code: "WebTokenMissing",
HTTPStatusCode: http.StatusBadRequest,
Description: err.Error(),
}
case errServerNotInitialized: case errServerNotInitialized:
return APIError{ return APIError{
Code: "XMinioServerNotInitialized", Code: "XMinioServerNotInitialized",
HTTPStatusCode: http.StatusServiceUnavailable, HTTPStatusCode: http.StatusServiceUnavailable,
Description: err.Error(), Description: err.Error(),
} }
case errAuthentication, auth.ErrInvalidAccessKeyLength, auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID: case errAuthentication, auth.ErrInvalidAccessKeyLength,
auth.ErrInvalidSecretKeyLength, errInvalidAccessKeyID:
return APIError{ return APIError{
Code: "AccessDenied", Code: "AccessDenied",
HTTPStatusCode: http.StatusForbidden, HTTPStatusCode: http.StatusForbidden,

View File

@ -18,22 +18,25 @@ package iampolicy
import "fmt" import "fmt"
// Error generic iam policy error type // Error is the generic type for any error happening during policy
// parsing.
type Error struct { type Error struct {
Err string err error
} }
// Errorf - formats according to a format specifier and returns // Errorf - formats according to a format specifier and returns
// the string as a value that satisfies error of type iampolicy.Error // the string as a value that satisfies error of type policy.Error
func Errorf(format string, a ...interface{}) error { func Errorf(format string, a ...interface{}) error {
return Error{Err: fmt.Sprintf(format, a...)} return Error{err: fmt.Errorf(format, a...)}
} }
// New initializes a new Error // Unwrap the internal error.
func New(err string) error { func (e Error) Unwrap() error { return e.err }
return Error{Err: err}
}
// Error 'error' compatible method.
func (e Error) Error() string { func (e Error) Error() string {
return e.Err if e.err == nil {
return "iam: cause <nil>"
}
return e.err.Error()
} }

View File

@ -165,7 +165,7 @@ func ParseConfig(reader io.Reader) (*Policy, error) {
decoder := json.NewDecoder(reader) decoder := json.NewDecoder(reader)
decoder.DisallowUnknownFields() decoder.DisallowUnknownFields()
if err := decoder.Decode(&iamp); err != nil { if err := decoder.Decode(&iamp); err != nil {
return nil, err return nil, Errorf("%w", err)
} }
return &iamp, iamp.Validate() return &iamp, iamp.Validate()

View File

@ -18,22 +18,25 @@ package policy
import "fmt" import "fmt"
// Error generic policy parse error type // Error is the generic type for any error happening during policy
// parsing.
type Error struct { type Error struct {
Err string err error
} }
// Errorf - formats according to a format specifier and returns // Errorf - formats according to a format specifier and returns
// the string as a value that satisfies error of type policy.Error // the string as a value that satisfies error of type policy.Error
func Errorf(format string, a ...interface{}) error { func Errorf(format string, a ...interface{}) error {
return Error{Err: fmt.Sprintf(format, a...)} return Error{err: fmt.Errorf(format, a...)}
} }
// New initializes a new Error // Unwrap the internal error.
func New(err string) error { func (e Error) Unwrap() error { return e.err }
return Error{Err: err}
}
// Error 'error' compatible method.
func (e Error) Error() string { func (e Error) Error() string {
return e.Err if e.err == nil {
return "policy: cause <nil>"
}
return e.err.Error()
} }

View File

@ -167,7 +167,7 @@ func ParseConfig(reader io.Reader, bucketName string) (*Policy, error) {
decoder := json.NewDecoder(reader) decoder := json.NewDecoder(reader)
decoder.DisallowUnknownFields() decoder.DisallowUnknownFields()
if err := decoder.Decode(&policy); err != nil { if err := decoder.Decode(&policy); err != nil {
return nil, err return nil, Errorf("%w", err)
} }
err := policy.Validate(bucketName) err := policy.Validate(bucketName)