2021-04-18 15:41:13 -04:00
|
|
|
// Copyright (c) 2015-2021 MinIO, Inc.
|
2018-06-28 15:47:42 -04:00
|
|
|
//
|
2021-04-18 15:41:13 -04:00
|
|
|
// This file is part of MinIO Object Storage stack
|
2018-06-28 15:47:42 -04:00
|
|
|
//
|
2021-04-18 15:41:13 -04:00
|
|
|
// This program is free software: you can redistribute it and/or modify
|
|
|
|
// it under the terms of the GNU Affero General Public License as published by
|
|
|
|
// the Free Software Foundation, either version 3 of the License, or
|
|
|
|
// (at your option) any later version.
|
2018-06-28 15:47:42 -04:00
|
|
|
//
|
2021-04-18 15:41:13 -04:00
|
|
|
// This program is distributed in the hope that it will be useful
|
|
|
|
// but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
|
|
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
|
|
// GNU Affero General Public License for more details.
|
|
|
|
//
|
|
|
|
// You should have received a copy of the GNU Affero General Public License
|
|
|
|
// along with this program. If not, see <http://www.gnu.org/licenses/>.
|
2018-06-28 15:47:42 -04:00
|
|
|
|
|
|
|
package crypto
|
|
|
|
|
|
|
|
import (
|
|
|
|
"bytes"
|
|
|
|
"crypto/rand"
|
|
|
|
"encoding/hex"
|
|
|
|
"io"
|
|
|
|
"testing"
|
2018-07-16 10:49:50 -04:00
|
|
|
|
2021-06-01 17:59:40 -04:00
|
|
|
"github.com/minio/minio/internal/logger"
|
2018-06-28 15:47:42 -04:00
|
|
|
)
|
|
|
|
|
|
|
|
var shortRandom = func(limit int64) io.Reader { return io.LimitReader(rand.Reader, limit) }
|
|
|
|
|
|
|
|
func recoverTest(i int, shouldPass bool, t *testing.T) {
|
|
|
|
if err := recover(); err == nil && !shouldPass {
|
|
|
|
t.Errorf("Test %d should fail but passed successfully", i)
|
|
|
|
} else if err != nil && shouldPass {
|
|
|
|
t.Errorf("Test %d should pass but failed: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
var generateKeyTests = []struct {
|
|
|
|
ExtKey [32]byte
|
|
|
|
Random io.Reader
|
|
|
|
ShouldPass bool
|
|
|
|
}{
|
2018-07-16 10:49:50 -04:00
|
|
|
{ExtKey: [32]byte{}, Random: nil, ShouldPass: true}, // 0
|
|
|
|
{ExtKey: [32]byte{}, Random: rand.Reader, ShouldPass: true}, // 1
|
|
|
|
{ExtKey: [32]byte{}, Random: shortRandom(32), ShouldPass: true}, // 2
|
|
|
|
{ExtKey: [32]byte{}, Random: shortRandom(31), ShouldPass: false}, // 3
|
2018-06-28 15:47:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
func TestGenerateKey(t *testing.T) {
|
2018-07-16 10:49:50 -04:00
|
|
|
defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable)
|
|
|
|
logger.Disable = true
|
|
|
|
|
2018-06-28 15:47:42 -04:00
|
|
|
for i, test := range generateKeyTests {
|
2018-07-16 10:49:50 -04:00
|
|
|
i, test := i, test
|
2018-06-28 15:47:42 -04:00
|
|
|
func() {
|
|
|
|
defer recoverTest(i, test.ShouldPass, t)
|
2021-04-15 11:47:33 -04:00
|
|
|
key := GenerateKey(test.ExtKey[:], test.Random)
|
2018-06-28 15:47:42 -04:00
|
|
|
if [32]byte(key) == [32]byte{} {
|
|
|
|
t.Errorf("Test %d: generated key is zero key", i) // check that we generate random and unique key
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-08-01 04:02:07 -04:00
|
|
|
var generateIVTests = []struct {
|
|
|
|
Random io.Reader
|
|
|
|
ShouldPass bool
|
|
|
|
}{
|
|
|
|
{Random: nil, ShouldPass: true}, // 0
|
|
|
|
{Random: rand.Reader, ShouldPass: true}, // 1
|
|
|
|
{Random: shortRandom(32), ShouldPass: true}, // 2
|
|
|
|
{Random: shortRandom(31), ShouldPass: false}, // 3
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestGenerateIV(t *testing.T) {
|
|
|
|
defer func(disableLog bool) { logger.Disable = disableLog }(logger.Disable)
|
|
|
|
logger.Disable = true
|
|
|
|
|
|
|
|
for i, test := range generateIVTests {
|
|
|
|
i, test := i, test
|
|
|
|
func() {
|
|
|
|
defer recoverTest(i, test.ShouldPass, t)
|
|
|
|
iv := GenerateIV(test.Random)
|
|
|
|
if iv == [32]byte{} {
|
|
|
|
t.Errorf("Test %d: generated IV is zero IV", i) // check that we generate random and unique IV
|
|
|
|
}
|
|
|
|
}()
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
2018-06-28 15:47:42 -04:00
|
|
|
var sealUnsealKeyTests = []struct {
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey, SealIV [32]byte
|
|
|
|
SealDomain, SealBucket, SealObject string
|
2018-06-28 15:47:42 -04:00
|
|
|
|
2018-07-16 10:49:50 -04:00
|
|
|
UnsealExtKey [32]byte
|
|
|
|
UnsealDomain, UnsealBucket, UnsealObject string
|
2018-06-28 15:47:42 -04:00
|
|
|
|
|
|
|
ShouldPass bool
|
|
|
|
}{
|
|
|
|
{
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
|
|
|
|
UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object",
|
2018-06-28 15:47:42 -04:00
|
|
|
ShouldPass: true,
|
|
|
|
}, // 0
|
|
|
|
{
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
|
|
|
|
UnsealExtKey: [32]byte{1}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object", // different ext-key
|
2018-06-28 15:47:42 -04:00
|
|
|
ShouldPass: false,
|
|
|
|
}, // 1
|
|
|
|
{
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-S3", SealBucket: "bucket", SealObject: "object",
|
|
|
|
UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object", // different domain
|
2018-06-28 15:47:42 -04:00
|
|
|
ShouldPass: false,
|
|
|
|
}, // 2
|
|
|
|
{
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
|
|
|
|
UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "Bucket", UnsealObject: "object", // different bucket
|
2018-06-28 15:47:42 -04:00
|
|
|
ShouldPass: false,
|
|
|
|
}, // 3
|
|
|
|
{
|
2018-07-16 10:49:50 -04:00
|
|
|
SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
|
|
|
|
UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "Object", // different object
|
2018-06-28 15:47:42 -04:00
|
|
|
ShouldPass: false,
|
|
|
|
}, // 4
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSealUnsealKey(t *testing.T) {
|
|
|
|
for i, test := range sealUnsealKeyTests {
|
2021-04-15 11:47:33 -04:00
|
|
|
key := GenerateKey(test.SealExtKey[:], rand.Reader)
|
|
|
|
sealedKey := key.Seal(test.SealExtKey[:], test.SealIV, test.SealDomain, test.SealBucket, test.SealObject)
|
|
|
|
if err := key.Unseal(test.UnsealExtKey[:], sealedKey, test.UnsealDomain, test.UnsealBucket, test.UnsealObject); err == nil && !test.ShouldPass {
|
2018-06-28 15:47:42 -04:00
|
|
|
t.Errorf("Test %d should fail but passed successfully", i)
|
|
|
|
} else if err != nil && test.ShouldPass {
|
|
|
|
t.Errorf("Test %d should pass put failed: %v", i, err)
|
|
|
|
}
|
|
|
|
}
|
2018-07-16 10:49:50 -04:00
|
|
|
|
|
|
|
// Test legacy InsecureSealAlgorithm
|
|
|
|
var extKey, iv [32]byte
|
2021-04-15 11:47:33 -04:00
|
|
|
key := GenerateKey(extKey[:], rand.Reader)
|
|
|
|
sealedKey := key.Seal(extKey[:], iv, "SSE-S3", "bucket", "object")
|
2018-07-16 10:49:50 -04:00
|
|
|
sealedKey.Algorithm = InsecureSealAlgorithm
|
2021-04-15 11:47:33 -04:00
|
|
|
if err := key.Unseal(extKey[:], sealedKey, "SSE-S3", "bucket", "object"); err == nil {
|
2018-07-16 10:49:50 -04:00
|
|
|
t.Errorf("'%s' test succeeded but it should fail because the legacy algorithm was used", sealedKey.Algorithm)
|
|
|
|
}
|
2018-06-28 15:47:42 -04:00
|
|
|
}
|
|
|
|
|
|
|
|
var derivePartKeyTest = []struct {
|
|
|
|
PartID uint32
|
|
|
|
PartKey string
|
|
|
|
}{
|
|
|
|
{PartID: 0, PartKey: "aa7855e13839dd767cd5da7c1ff5036540c9264b7a803029315e55375287b4af"},
|
|
|
|
{PartID: 1, PartKey: "a3e7181c6eed030fd52f79537c56c4d07da92e56d374ff1dd2043350785b37d8"},
|
|
|
|
{PartID: 10000, PartKey: "f86e65c396ed52d204ee44bd1a0bbd86eb8b01b7354e67a3b3ae0e34dd5bd115"},
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestDerivePartKey(t *testing.T) {
|
|
|
|
var key ObjectKey
|
|
|
|
for i, test := range derivePartKeyTest {
|
|
|
|
expectedPartKey, err := hex.DecodeString(test.PartKey)
|
|
|
|
if err != nil {
|
|
|
|
t.Fatalf("Test %d failed to decode expected part-key: %v", i, err)
|
|
|
|
}
|
|
|
|
partKey := key.DerivePartKey(test.PartID)
|
|
|
|
if !bytes.Equal(partKey[:], expectedPartKey[:]) {
|
|
|
|
t.Errorf("Test %d derives wrong part-key: got '%s' want: '%s'", i, hex.EncodeToString(partKey[:]), test.PartKey)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|
2018-10-16 13:02:19 -04:00
|
|
|
|
|
|
|
var sealUnsealETagTests = []string{
|
|
|
|
"",
|
|
|
|
"90682b8e8cc7609c",
|
|
|
|
"90682b8e8cc7609c4671e1d64c73fc30",
|
|
|
|
"90682b8e8cc7609c4671e1d64c73fc307fb3104f",
|
|
|
|
}
|
|
|
|
|
|
|
|
func TestSealETag(t *testing.T) {
|
|
|
|
var key ObjectKey
|
|
|
|
for i := range key {
|
|
|
|
key[i] = byte(i)
|
|
|
|
}
|
|
|
|
for i, etag := range sealUnsealETagTests {
|
|
|
|
tag, err := hex.DecodeString(etag)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Test %d: failed to decode etag: %s", i, err)
|
|
|
|
}
|
|
|
|
sealedETag := key.SealETag(tag)
|
|
|
|
unsealedETag, err := key.UnsealETag(sealedETag)
|
|
|
|
if err != nil {
|
|
|
|
t.Errorf("Test %d: failed to decrypt etag: %s", i, err)
|
|
|
|
}
|
|
|
|
if !bytes.Equal(unsealedETag, tag) {
|
|
|
|
t.Errorf("Test %d: unsealed etag does not match: got %s - want %s", i, hex.EncodeToString(unsealedETag), etag)
|
|
|
|
}
|
|
|
|
}
|
|
|
|
}
|