github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/internal/crypto/key_test.go (about)

     1  // Copyright (c) 2015-2021 MinIO, Inc.
     2  //
     3  // This file is part of MinIO Object Storage stack
     4  //
     5  // This program is free software: you can redistribute it and/or modify
     6  // it under the terms of the GNU Affero General Public License as published by
     7  // the Free Software Foundation, either version 3 of the License, or
     8  // (at your option) any later version.
     9  //
    10  // This program is distributed in the hope that it will be useful
    11  // but WITHOUT ANY WARRANTY; without even the implied warranty of
    12  // MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
    13  // GNU Affero General Public License for more details.
    14  //
    15  // You should have received a copy of the GNU Affero General Public License
    16  // along with this program.  If not, see <http://www.gnu.org/licenses/>.
    17  
    18  package crypto
    19  
    20  import (
    21  	"bytes"
    22  	"crypto/rand"
    23  	"encoding/hex"
    24  	"io"
    25  	"testing"
    26  
    27  	"github.com/minio/minio/internal/logger"
    28  )
    29  
    30  var shortRandom = func(limit int64) io.Reader { return io.LimitReader(rand.Reader, limit) }
    31  
    32  func recoverTest(i int, shouldPass bool, t *testing.T) {
    33  	if err := recover(); err == nil && !shouldPass {
    34  		t.Errorf("Test %d should fail but passed successfully", i)
    35  	} else if err != nil && shouldPass {
    36  		t.Errorf("Test %d should pass but failed: %v", i, err)
    37  	}
    38  }
    39  
    40  var generateKeyTests = []struct {
    41  	ExtKey     [32]byte
    42  	Random     io.Reader
    43  	ShouldPass bool
    44  }{
    45  	{ExtKey: [32]byte{}, Random: nil, ShouldPass: true},              // 0
    46  	{ExtKey: [32]byte{}, Random: rand.Reader, ShouldPass: true},      // 1
    47  	{ExtKey: [32]byte{}, Random: shortRandom(32), ShouldPass: true},  // 2
    48  	{ExtKey: [32]byte{}, Random: shortRandom(31), ShouldPass: false}, // 3
    49  }
    50  
    51  func TestGenerateKey(t *testing.T) {
    52  	defer func(l bool) { logger.DisableErrorLog = l }(logger.DisableErrorLog)
    53  	logger.DisableErrorLog = true
    54  
    55  	for i, test := range generateKeyTests {
    56  		i, test := i, test
    57  		func() {
    58  			defer recoverTest(i, test.ShouldPass, t)
    59  			key := GenerateKey(test.ExtKey[:], test.Random)
    60  			if [32]byte(key) == [32]byte{} {
    61  				t.Errorf("Test %d: generated key is zero key", i) // check that we generate random and unique key
    62  			}
    63  		}()
    64  	}
    65  }
    66  
    67  var generateIVTests = []struct {
    68  	Random     io.Reader
    69  	ShouldPass bool
    70  }{
    71  	{Random: nil, ShouldPass: true},              // 0
    72  	{Random: rand.Reader, ShouldPass: true},      // 1
    73  	{Random: shortRandom(32), ShouldPass: true},  // 2
    74  	{Random: shortRandom(31), ShouldPass: false}, // 3
    75  }
    76  
    77  func TestGenerateIV(t *testing.T) {
    78  	defer func(l bool) { logger.DisableErrorLog = l }(logger.DisableErrorLog)
    79  	logger.DisableErrorLog = true
    80  
    81  	for i, test := range generateIVTests {
    82  		i, test := i, test
    83  		func() {
    84  			defer recoverTest(i, test.ShouldPass, t)
    85  			iv := GenerateIV(test.Random)
    86  			if iv == [32]byte{} {
    87  				t.Errorf("Test %d: generated IV is zero IV", i) // check that we generate random and unique IV
    88  			}
    89  		}()
    90  	}
    91  }
    92  
    93  var sealUnsealKeyTests = []struct {
    94  	SealExtKey, SealIV                 [32]byte
    95  	SealDomain, SealBucket, SealObject string
    96  
    97  	UnsealExtKey                             [32]byte
    98  	UnsealDomain, UnsealBucket, UnsealObject string
    99  
   100  	ShouldPass bool
   101  }{
   102  	{
   103  		SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
   104  		UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object",
   105  		ShouldPass: true,
   106  	}, // 0
   107  	{
   108  		SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
   109  		UnsealExtKey: [32]byte{1}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object", // different ext-key
   110  		ShouldPass: false,
   111  	}, // 1
   112  	{
   113  		SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-S3", SealBucket: "bucket", SealObject: "object",
   114  		UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "object", // different domain
   115  		ShouldPass: false,
   116  	}, // 2
   117  	{
   118  		SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
   119  		UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "Bucket", UnsealObject: "object", // different bucket
   120  		ShouldPass: false,
   121  	}, // 3
   122  	{
   123  		SealExtKey: [32]byte{}, SealIV: [32]byte{}, SealDomain: "SSE-C", SealBucket: "bucket", SealObject: "object",
   124  		UnsealExtKey: [32]byte{}, UnsealDomain: "SSE-C", UnsealBucket: "bucket", UnsealObject: "Object", // different object
   125  		ShouldPass: false,
   126  	}, // 4
   127  }
   128  
   129  func TestSealUnsealKey(t *testing.T) {
   130  	for i, test := range sealUnsealKeyTests {
   131  		key := GenerateKey(test.SealExtKey[:], rand.Reader)
   132  		sealedKey := key.Seal(test.SealExtKey[:], test.SealIV, test.SealDomain, test.SealBucket, test.SealObject)
   133  		if err := key.Unseal(test.UnsealExtKey[:], sealedKey, test.UnsealDomain, test.UnsealBucket, test.UnsealObject); err == nil && !test.ShouldPass {
   134  			t.Errorf("Test %d should fail but passed successfully", i)
   135  		} else if err != nil && test.ShouldPass {
   136  			t.Errorf("Test %d should pass put failed: %v", i, err)
   137  		}
   138  	}
   139  
   140  	// Test legacy InsecureSealAlgorithm
   141  	var extKey, iv [32]byte
   142  	key := GenerateKey(extKey[:], rand.Reader)
   143  	sealedKey := key.Seal(extKey[:], iv, "SSE-S3", "bucket", "object")
   144  	sealedKey.Algorithm = InsecureSealAlgorithm
   145  	if err := key.Unseal(extKey[:], sealedKey, "SSE-S3", "bucket", "object"); err == nil {
   146  		t.Errorf("'%s' test succeeded but it should fail because the legacy algorithm was used", sealedKey.Algorithm)
   147  	}
   148  }
   149  
   150  var derivePartKeyTest = []struct {
   151  	PartID  uint32
   152  	PartKey string
   153  }{
   154  	{PartID: 0, PartKey: "aa7855e13839dd767cd5da7c1ff5036540c9264b7a803029315e55375287b4af"},
   155  	{PartID: 1, PartKey: "a3e7181c6eed030fd52f79537c56c4d07da92e56d374ff1dd2043350785b37d8"},
   156  	{PartID: 10000, PartKey: "f86e65c396ed52d204ee44bd1a0bbd86eb8b01b7354e67a3b3ae0e34dd5bd115"},
   157  }
   158  
   159  func TestDerivePartKey(t *testing.T) {
   160  	var key ObjectKey
   161  	for i, test := range derivePartKeyTest {
   162  		expectedPartKey, err := hex.DecodeString(test.PartKey)
   163  		if err != nil {
   164  			t.Fatalf("Test %d failed to decode expected part-key: %v", i, err)
   165  		}
   166  		partKey := key.DerivePartKey(test.PartID)
   167  		if !bytes.Equal(partKey[:], expectedPartKey) {
   168  			t.Errorf("Test %d derives wrong part-key: got '%s' want: '%s'", i, hex.EncodeToString(partKey[:]), test.PartKey)
   169  		}
   170  	}
   171  }
   172  
   173  var sealUnsealETagTests = []string{
   174  	"",
   175  	"90682b8e8cc7609c",
   176  	"90682b8e8cc7609c4671e1d64c73fc30",
   177  	"90682b8e8cc7609c4671e1d64c73fc307fb3104f",
   178  }
   179  
   180  func TestSealETag(t *testing.T) {
   181  	var key ObjectKey
   182  	for i := range key {
   183  		key[i] = byte(i)
   184  	}
   185  	for i, etag := range sealUnsealETagTests {
   186  		tag, err := hex.DecodeString(etag)
   187  		if err != nil {
   188  			t.Errorf("Test %d: failed to decode etag: %s", i, err)
   189  		}
   190  		sealedETag := key.SealETag(tag)
   191  		unsealedETag, err := key.UnsealETag(sealedETag)
   192  		if err != nil {
   193  			t.Errorf("Test %d: failed to decrypt etag: %s", i, err)
   194  		}
   195  		if !bytes.Equal(unsealedETag, tag) {
   196  			t.Errorf("Test %d: unsealed etag does not match: got %s - want %s", i, hex.EncodeToString(unsealedETag), etag)
   197  		}
   198  	}
   199  }