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 }