github.com/minio/minio@v0.0.0-20240328213742-3f72439b8a27/cmd/encryption-v1.go (about) 1 // Copyright (c) 2015-2023 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 cmd 19 20 import ( 21 "bufio" 22 "bytes" 23 "context" 24 "crypto/hmac" 25 "crypto/rand" 26 "crypto/subtle" 27 "encoding/binary" 28 "encoding/hex" 29 "errors" 30 "fmt" 31 "io" 32 "net/http" 33 "path" 34 "strconv" 35 "strings" 36 37 "github.com/minio/kms-go/kes" 38 "github.com/minio/minio/internal/crypto" 39 "github.com/minio/minio/internal/etag" 40 "github.com/minio/minio/internal/fips" 41 "github.com/minio/minio/internal/hash" 42 "github.com/minio/minio/internal/hash/sha256" 43 xhttp "github.com/minio/minio/internal/http" 44 "github.com/minio/minio/internal/kms" 45 "github.com/minio/minio/internal/logger" 46 "github.com/minio/sio" 47 ) 48 49 var ( 50 // AWS errors for invalid SSE-C requests. 51 errEncryptedObject = errors.New("The object was stored using a form of SSE") 52 errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied 53 errKMSNotConfigured = errors.New("KMS not configured for a server side encrypted objects") 54 errKMSKeyNotFound = errors.New("Unknown KMS key ID") 55 errKMSDefaultKeyAlreadyConfigured = errors.New("A default encryption already exists on KMS") 56 // Additional MinIO errors for SSE-C requests. 57 errObjectTampered = errors.New("The requested object was modified and may be compromised") 58 // error returned when invalid encryption parameters are specified 59 errInvalidEncryptionParameters = errors.New("The encryption parameters are not applicable to this object") 60 errInvalidEncryptionParametersSSEC = errors.New("SSE-C encryption parameters are not supported on this bucket") 61 ) 62 63 const ( 64 // SSECustomerKeySize is the size of valid client provided encryption keys in bytes. 65 // Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes. 66 SSECustomerKeySize = 32 67 68 // SSEIVSize is the size of the IV data 69 SSEIVSize = 32 // 32 bytes 70 71 // SSEDAREPackageBlockSize - SSE dare package block size. 72 SSEDAREPackageBlockSize = 64 * 1024 // 64KiB bytes 73 74 // SSEDAREPackageMetaSize - SSE dare package meta padding bytes. 75 SSEDAREPackageMetaSize = 32 // 32 bytes 76 77 ) 78 79 // KMSKeyID returns in AWS compatible KMS KeyID() format. 80 func (o *ObjectInfo) KMSKeyID() string { return kmsKeyIDFromMetadata(o.UserDefined) } 81 82 // KMSKeyID returns in AWS compatible KMS KeyID() format. 83 func (o *MultipartInfo) KMSKeyID() string { return kmsKeyIDFromMetadata(o.UserDefined) } 84 85 // kmsKeyIDFromMetadata returns any AWS S3 KMS key ID in the 86 // metadata, if any. It returns an empty ID if no key ID is 87 // present. 88 func kmsKeyIDFromMetadata(metadata map[string]string) string { 89 const ARNPrefix = crypto.ARNPrefix 90 if len(metadata) == 0 { 91 return "" 92 } 93 kmsID, ok := metadata[crypto.MetaKeyID] 94 if !ok { 95 return "" 96 } 97 if strings.HasPrefix(kmsID, ARNPrefix) { 98 return kmsID 99 } 100 return ARNPrefix + kmsID 101 } 102 103 // DecryptETags decryptes the ETag of all ObjectInfos using the KMS. 104 // 105 // It adjusts the size of all encrypted objects since encrypted 106 // objects are slightly larger due to encryption overhead. 107 // Further, it decrypts all single-part SSE-S3 encrypted objects 108 // and formats ETags of SSE-C / SSE-KMS encrypted objects to 109 // be AWS S3 compliant. 110 // 111 // DecryptETags uses a KMS bulk decryption API, if available, which 112 // is more efficient than decrypting ETags sequentually. 113 func DecryptETags(ctx context.Context, k kms.KMS, objects []ObjectInfo) error { 114 const BatchSize = 250 // We process the objects in batches - 250 is a reasonable default. 115 var ( 116 metadata = make([]map[string]string, 0, BatchSize) 117 buckets = make([]string, 0, BatchSize) 118 names = make([]string, 0, BatchSize) 119 ) 120 for len(objects) > 0 { 121 N := BatchSize 122 if len(objects) < BatchSize { 123 N = len(objects) 124 } 125 batch := objects[:N] 126 127 // We have to decrypt only ETags of SSE-S3 single-part 128 // objects. 129 // Therefore, we remember which objects (there index) 130 // in the current batch are single-part SSE-S3 objects. 131 metadata = metadata[:0:N] 132 buckets = buckets[:0:N] 133 names = names[:0:N] 134 SSES3SinglePartObjects := make(map[int]bool) 135 for i, object := range batch { 136 if kind, ok := crypto.IsEncrypted(object.UserDefined); ok && kind == crypto.S3 && !crypto.IsMultiPart(object.UserDefined) { 137 SSES3SinglePartObjects[i] = true 138 139 metadata = append(metadata, object.UserDefined) 140 buckets = append(buckets, object.Bucket) 141 names = append(names, object.Name) 142 } 143 } 144 145 // If there are no SSE-S3 single-part objects 146 // we can skip the decryption process. However, 147 // we still have to adjust the size and ETag 148 // of SSE-C and SSE-KMS objects. 149 if len(SSES3SinglePartObjects) == 0 { 150 for i := range batch { 151 size, err := batch[i].GetActualSize() 152 if err != nil { 153 return err 154 } 155 batch[i].Size = size 156 157 if _, ok := crypto.IsEncrypted(batch[i].UserDefined); ok { 158 ETag, err := etag.Parse(batch[i].ETag) 159 if err != nil { 160 return err 161 } 162 batch[i].ETag = ETag.Format().String() 163 } 164 } 165 objects = objects[N:] 166 continue 167 } 168 169 // There is at least one SSE-S3 single-part object. 170 // For all SSE-S3 single-part objects we have to 171 // fetch their decryption keys. We do this using 172 // a Bulk-Decryption API call, if available. 173 keys, err := crypto.S3.UnsealObjectKeys(ctx, k, metadata, buckets, names) 174 if err != nil { 175 return err 176 } 177 178 // Now, we have to decrypt the ETags of SSE-S3 single-part 179 // objects and adjust the size and ETags of all encrypted 180 // objects. 181 for i := range batch { 182 size, err := batch[i].GetActualSize() 183 if err != nil { 184 return err 185 } 186 batch[i].Size = size 187 188 if _, ok := crypto.IsEncrypted(batch[i].UserDefined); ok { 189 ETag, err := etag.Parse(batch[i].ETag) 190 if err != nil { 191 return err 192 } 193 if SSES3SinglePartObjects[i] && ETag.IsEncrypted() { 194 ETag, err = etag.Decrypt(keys[0][:], ETag) 195 if err != nil { 196 return err 197 } 198 keys = keys[1:] 199 } 200 batch[i].ETag = ETag.Format().String() 201 } 202 } 203 objects = objects[N:] 204 } 205 return nil 206 } 207 208 // isMultipart returns true if the current object is 209 // uploaded by the user using multipart mechanism: 210 // initiate new multipart, upload part, complete upload 211 func (o *ObjectInfo) isMultipart() bool { 212 _, encrypted := crypto.IsEncrypted(o.UserDefined) 213 if encrypted { 214 if !crypto.IsMultiPart(o.UserDefined) { 215 return false 216 } 217 for _, part := range o.Parts { 218 _, err := sio.DecryptedSize(uint64(part.Size)) 219 if err != nil { 220 return false 221 } 222 } 223 } 224 225 // Further check if this object is uploaded using multipart mechanism 226 // by the user and it is not about Erasure internally splitting the 227 // object into parts in PutObject() 228 return len(o.ETag) != 32 229 } 230 231 // ParseSSECopyCustomerRequest parses the SSE-C header fields of the provided request. 232 // It returns the client provided key on success. 233 func ParseSSECopyCustomerRequest(h http.Header, metadata map[string]string) (key []byte, err error) { 234 if crypto.S3.IsEncrypted(metadata) && crypto.SSECopy.IsRequested(h) { 235 return nil, crypto.ErrIncompatibleEncryptionMethod 236 } 237 k, err := crypto.SSECopy.ParseHTTP(h) 238 return k[:], err 239 } 240 241 // ParseSSECustomerRequest parses the SSE-C header fields of the provided request. 242 // It returns the client provided key on success. 243 func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) { 244 return ParseSSECustomerHeader(r.Header) 245 } 246 247 // ParseSSECustomerHeader parses the SSE-C header fields and returns 248 // the client provided key on success. 249 func ParseSSECustomerHeader(header http.Header) (key []byte, err error) { 250 if crypto.S3.IsRequested(header) && crypto.SSEC.IsRequested(header) { 251 return key, crypto.ErrIncompatibleEncryptionMethod 252 } 253 254 k, err := crypto.SSEC.ParseHTTP(header) 255 return k[:], err 256 } 257 258 // This function rotates old to new key. 259 func rotateKey(ctx context.Context, oldKey []byte, newKeyID string, newKey []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) error { 260 kind, _ := crypto.IsEncrypted(metadata) 261 switch kind { 262 case crypto.S3: 263 if GlobalKMS == nil { 264 return errKMSNotConfigured 265 } 266 keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) 267 if err != nil { 268 return err 269 } 270 oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, kms.Context{bucket: path.Join(bucket, object)}) 271 if err != nil { 272 return err 273 } 274 var objectKey crypto.ObjectKey 275 if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil { 276 return err 277 } 278 279 newKey, err := GlobalKMS.GenerateKey(ctx, "", kms.Context{bucket: path.Join(bucket, object)}) 280 if err != nil { 281 return err 282 } 283 sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) 284 crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey) 285 return nil 286 case crypto.S3KMS: 287 if GlobalKMS == nil { 288 return errKMSNotConfigured 289 } 290 objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object) 291 if err != nil { 292 return err 293 } 294 295 if len(cryptoCtx) == 0 { 296 _, _, _, cryptoCtx, err = crypto.S3KMS.ParseMetadata(metadata) 297 if err != nil { 298 return err 299 } 300 } 301 302 // If the context does not contain the bucket key 303 // we must add it for key generation. However, 304 // the context must be stored exactly like the 305 // client provided it. Therefore, we create a copy 306 // of the client provided context and add the bucket 307 // key, if not present. 308 kmsCtx := kms.Context{} 309 for k, v := range cryptoCtx { 310 kmsCtx[k] = v 311 } 312 if _, ok := kmsCtx[bucket]; !ok { 313 kmsCtx[bucket] = path.Join(bucket, object) 314 } 315 newKey, err := GlobalKMS.GenerateKey(ctx, newKeyID, kmsCtx) 316 if err != nil { 317 return err 318 } 319 320 sealedKey := objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object) 321 crypto.S3KMS.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey, cryptoCtx) 322 return nil 323 case crypto.SSEC: 324 sealedKey, err := crypto.SSEC.ParseMetadata(metadata) 325 if err != nil { 326 return err 327 } 328 329 var objectKey crypto.ObjectKey 330 if err = objectKey.Unseal(oldKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { 331 if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { 332 return errInvalidSSEParameters // AWS returns special error for equal but invalid keys. 333 } 334 return crypto.ErrInvalidCustomerKey // To provide strict AWS S3 compatibility we return: access denied. 335 336 } 337 if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && sealedKey.Algorithm == crypto.SealAlgorithm { 338 return nil // don't rotate on equal keys if seal algorithm is latest 339 } 340 sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object) 341 crypto.SSEC.CreateMetadata(metadata, sealedKey) 342 return nil 343 default: 344 return errObjectTampered 345 } 346 } 347 348 func newEncryptMetadata(ctx context.Context, kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) (crypto.ObjectKey, error) { 349 var sealedKey crypto.SealedKey 350 switch kind { 351 case crypto.S3: 352 if GlobalKMS == nil { 353 return crypto.ObjectKey{}, errKMSNotConfigured 354 } 355 key, err := GlobalKMS.GenerateKey(ctx, "", kms.Context{bucket: path.Join(bucket, object)}) 356 if err != nil { 357 return crypto.ObjectKey{}, err 358 } 359 360 objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) 361 sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) 362 crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey) 363 return objectKey, nil 364 case crypto.S3KMS: 365 if GlobalKMS == nil { 366 return crypto.ObjectKey{}, errKMSNotConfigured 367 } 368 369 // If the context does not contain the bucket key 370 // we must add it for key generation. However, 371 // the context must be stored exactly like the 372 // client provided it. Therefore, we create a copy 373 // of the client provided context and add the bucket 374 // key, if not present. 375 kmsCtx := kms.Context{} 376 for k, v := range cryptoCtx { 377 kmsCtx[k] = v 378 } 379 if _, ok := kmsCtx[bucket]; !ok { 380 kmsCtx[bucket] = path.Join(bucket, object) 381 } 382 key, err := GlobalKMS.GenerateKey(ctx, keyID, kmsCtx) 383 if err != nil { 384 if errors.Is(err, kes.ErrKeyNotFound) { 385 return crypto.ObjectKey{}, errKMSKeyNotFound 386 } 387 return crypto.ObjectKey{}, err 388 } 389 390 objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) 391 sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3KMS.String(), bucket, object) 392 crypto.S3KMS.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey, cryptoCtx) 393 return objectKey, nil 394 case crypto.SSEC: 395 objectKey := crypto.GenerateKey(key, rand.Reader) 396 sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object) 397 crypto.SSEC.CreateMetadata(metadata, sealedKey) 398 return objectKey, nil 399 default: 400 return crypto.ObjectKey{}, fmt.Errorf("encryption type '%v' not supported", kind) 401 } 402 } 403 404 func newEncryptReader(ctx context.Context, content io.Reader, kind crypto.Type, keyID string, key []byte, bucket, object string, metadata map[string]string, cryptoCtx kms.Context) (io.Reader, crypto.ObjectKey, error) { 405 objectEncryptionKey, err := newEncryptMetadata(ctx, kind, keyID, key, bucket, object, metadata, cryptoCtx) 406 if err != nil { 407 return nil, crypto.ObjectKey{}, err 408 } 409 410 reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20, CipherSuites: fips.DARECiphers()}) 411 if err != nil { 412 return nil, crypto.ObjectKey{}, crypto.ErrInvalidCustomerKey 413 } 414 415 return reader, objectEncryptionKey, nil 416 } 417 418 // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of 419 // SSE-S3 420 func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) { 421 var ( 422 key []byte 423 keyID string 424 kmsCtx kms.Context 425 ) 426 kind, _ := crypto.IsRequested(r.Header) 427 switch kind { 428 case crypto.SSEC: 429 key, err = ParseSSECustomerRequest(r) 430 if err != nil { 431 return err 432 } 433 case crypto.S3KMS: 434 keyID, kmsCtx, err = crypto.S3KMS.ParseHTTP(r.Header) 435 if err != nil { 436 return err 437 } 438 } 439 _, err = newEncryptMetadata(r.Context(), kind, keyID, key, bucket, object, metadata, kmsCtx) 440 return 441 } 442 443 // EncryptRequest takes the client provided content and encrypts the data 444 // with the client provided key. It also marks the object as client-side-encrypted 445 // and sets the correct headers. 446 func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) { 447 if r.ContentLength > encryptBufferThreshold { 448 // The encryption reads in blocks of 64KB. 449 // We add a buffer on bigger files to reduce the number of syscalls upstream. 450 content = bufio.NewReaderSize(content, encryptBufferSize) 451 } 452 453 var ( 454 key []byte 455 keyID string 456 ctx kms.Context 457 err error 458 ) 459 kind, _ := crypto.IsRequested(r.Header) 460 if kind == crypto.SSEC { 461 key, err = ParseSSECustomerRequest(r) 462 if err != nil { 463 return nil, crypto.ObjectKey{}, err 464 } 465 } 466 if kind == crypto.S3KMS { 467 keyID, ctx, err = crypto.S3KMS.ParseHTTP(r.Header) 468 if err != nil { 469 return nil, crypto.ObjectKey{}, err 470 } 471 } 472 return newEncryptReader(r.Context(), content, kind, keyID, key, bucket, object, metadata, ctx) 473 } 474 475 func decryptObjectMeta(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) { 476 switch kind, _ := crypto.IsEncrypted(metadata); kind { 477 case crypto.S3: 478 KMS := GlobalKMS 479 if KMS == nil { 480 return nil, errKMSNotConfigured 481 } 482 objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object) 483 if err != nil { 484 return nil, err 485 } 486 return objectKey[:], nil 487 case crypto.S3KMS: 488 if GlobalKMS == nil { 489 return nil, errKMSNotConfigured 490 } 491 objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object) 492 if err != nil { 493 return nil, err 494 } 495 return objectKey[:], nil 496 case crypto.SSEC: 497 sealedKey, err := crypto.SSEC.ParseMetadata(metadata) 498 if err != nil { 499 return nil, err 500 } 501 var objectKey crypto.ObjectKey 502 if err = objectKey.Unseal(key, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { 503 return nil, err 504 } 505 return objectKey[:], nil 506 default: 507 return nil, errObjectTampered 508 } 509 } 510 511 // Adding support for reader based interface 512 513 // DecryptRequestWithSequenceNumberR - same as 514 // DecryptRequestWithSequenceNumber but with a reader 515 func DecryptRequestWithSequenceNumberR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 516 if crypto.SSEC.IsEncrypted(metadata) { 517 key, err := ParseSSECustomerHeader(h) 518 if err != nil { 519 return nil, err 520 } 521 return newDecryptReader(client, key, bucket, object, seqNumber, metadata) 522 } 523 return newDecryptReader(client, nil, bucket, object, seqNumber, metadata) 524 } 525 526 // DecryptCopyRequestR - same as DecryptCopyRequest, but with a 527 // Reader 528 func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 529 var ( 530 key []byte 531 err error 532 ) 533 if crypto.SSECopy.IsRequested(h) { 534 key, err = ParseSSECopyCustomerRequest(h, metadata) 535 if err != nil { 536 return nil, err 537 } 538 } 539 return newDecryptReader(client, key, bucket, object, seqNumber, metadata) 540 } 541 542 func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 543 objectEncryptionKey, err := decryptObjectMeta(key, bucket, object, metadata) 544 if err != nil { 545 return nil, err 546 } 547 return newDecryptReaderWithObjectKey(client, objectEncryptionKey, seqNumber) 548 } 549 550 func newDecryptReaderWithObjectKey(client io.Reader, objectEncryptionKey []byte, seqNumber uint32) (io.Reader, error) { 551 reader, err := sio.DecryptReader(client, sio.Config{ 552 Key: objectEncryptionKey, 553 SequenceNumber: seqNumber, 554 CipherSuites: fips.DARECiphers(), 555 }) 556 if err != nil { 557 return nil, crypto.ErrInvalidCustomerKey 558 } 559 return reader, nil 560 } 561 562 // DecryptBlocksRequestR - same as DecryptBlocksRequest but with a 563 // reader 564 func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint32, partStart int, oi ObjectInfo, copySource bool) (io.Reader, error) { 565 bucket, object := oi.Bucket, oi.Name 566 // Single part case 567 if !oi.isMultipart() { 568 var reader io.Reader 569 var err error 570 if copySource { 571 reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, seqNumber, oi.UserDefined) 572 } else { 573 reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined) 574 } 575 if err != nil { 576 return nil, err 577 } 578 return reader, nil 579 } 580 581 partDecRelOffset := int64(seqNumber) * SSEDAREPackageBlockSize 582 partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) 583 584 w := &DecryptBlocksReader{ 585 reader: inputReader, 586 startSeqNum: seqNumber, 587 partDecRelOffset: partDecRelOffset, 588 partEncRelOffset: partEncRelOffset, 589 parts: oi.Parts, 590 partIndex: partStart, 591 header: h, 592 bucket: bucket, 593 object: object, 594 customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey), 595 copySource: copySource, 596 metadata: cloneMSS(oi.UserDefined), 597 } 598 599 if w.copySource { 600 w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey) 601 } 602 603 if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil { 604 return nil, err 605 } 606 607 return w, nil 608 } 609 610 // DecryptBlocksReader - decrypts multipart parts, while implementing 611 // a io.Reader compatible interface. 612 type DecryptBlocksReader struct { 613 // Source of the encrypted content that will be decrypted 614 reader io.Reader 615 // Current decrypter for the current encrypted data block 616 decrypter io.Reader 617 // Start sequence number 618 startSeqNum uint32 619 // Current part index 620 partIndex int 621 // Parts information 622 parts []ObjectPartInfo 623 header http.Header 624 bucket, object string 625 metadata map[string]string 626 627 partDecRelOffset, partEncRelOffset int64 628 629 copySource bool 630 // Customer Key 631 customerKeyHeader string 632 } 633 634 func (d *DecryptBlocksReader) buildDecrypter(partID int) error { 635 m := cloneMSS(d.metadata) 636 // Initialize the first decrypter; new decrypters will be 637 // initialized in Read() operation as needed. 638 var key []byte 639 var err error 640 if d.copySource { 641 if crypto.SSEC.IsEncrypted(d.metadata) { 642 d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader) 643 key, err = ParseSSECopyCustomerRequest(d.header, d.metadata) 644 } 645 } else { 646 if crypto.SSEC.IsEncrypted(d.metadata) { 647 d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader) 648 key, err = ParseSSECustomerHeader(d.header) 649 } 650 } 651 if err != nil { 652 return err 653 } 654 655 objectEncryptionKey, err := decryptObjectMeta(key, d.bucket, d.object, m) 656 if err != nil { 657 return err 658 } 659 660 var partIDbin [4]byte 661 binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID 662 663 mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key 664 mac.Write(partIDbin[:]) 665 partEncryptionKey := mac.Sum(nil) 666 667 // Limit the reader, so the decryptor doesn't receive bytes 668 // from the next part (different DARE stream) 669 encLenToRead := d.parts[d.partIndex].Size - d.partEncRelOffset 670 decrypter, err := newDecryptReaderWithObjectKey(io.LimitReader(d.reader, encLenToRead), partEncryptionKey, d.startSeqNum) 671 if err != nil { 672 return err 673 } 674 675 d.decrypter = decrypter 676 return nil 677 } 678 679 func (d *DecryptBlocksReader) Read(p []byte) (int, error) { 680 var err error 681 var n1 int 682 decPartSize, _ := sio.DecryptedSize(uint64(d.parts[d.partIndex].Size)) 683 unreadPartLen := int64(decPartSize) - d.partDecRelOffset 684 if int64(len(p)) < unreadPartLen { 685 n1, err = d.decrypter.Read(p) 686 if err != nil { 687 return 0, err 688 } 689 d.partDecRelOffset += int64(n1) 690 } else { 691 n1, err = io.ReadFull(d.decrypter, p[:unreadPartLen]) 692 if err != nil { 693 return 0, err 694 } 695 696 // We should now proceed to next part, reset all 697 // values appropriately. 698 d.partEncRelOffset = 0 699 d.partDecRelOffset = 0 700 d.startSeqNum = 0 701 702 d.partIndex++ 703 if d.partIndex == len(d.parts) { 704 return n1, io.EOF 705 } 706 707 err = d.buildDecrypter(d.parts[d.partIndex].Number) 708 if err != nil { 709 return 0, err 710 } 711 712 n1, err = d.decrypter.Read(p[n1:]) 713 if err != nil { 714 return 0, err 715 } 716 717 d.partDecRelOffset += int64(n1) 718 } 719 return len(p), nil 720 } 721 722 // DecryptedSize returns the size of the object after decryption in bytes. 723 // It returns an error if the object is not encrypted or marked as encrypted 724 // but has an invalid size. 725 func (o ObjectInfo) DecryptedSize() (int64, error) { 726 if _, ok := crypto.IsEncrypted(o.UserDefined); !ok { 727 return 0, errors.New("Cannot compute decrypted size of an unencrypted object") 728 } 729 if !o.isMultipart() { 730 size, err := sio.DecryptedSize(uint64(o.Size)) 731 if err != nil { 732 err = errObjectTampered // assign correct error type 733 } 734 return int64(size), err 735 } 736 737 var size int64 738 for _, part := range o.Parts { 739 partSize, err := sio.DecryptedSize(uint64(part.Size)) 740 if err != nil { 741 return 0, errObjectTampered 742 } 743 size += int64(partSize) 744 } 745 return size, nil 746 } 747 748 // DecryptETag decrypts the ETag that is part of given object 749 // with the given object encryption key. 750 // 751 // However, DecryptETag does not try to decrypt the ETag if 752 // it consists of a 128 bit hex value (32 hex chars) and exactly 753 // one '-' followed by a 32-bit number. 754 // This special case addresses randomly-generated ETags generated 755 // by the MinIO server when running in non-compat mode. These 756 // random ETags are not encrypt. 757 // 758 // Calling DecryptETag with a non-randomly generated ETag will 759 // fail. 760 func DecryptETag(key crypto.ObjectKey, object ObjectInfo) (string, error) { 761 if n := strings.Count(object.ETag, "-"); n > 0 { 762 if n != 1 { 763 return "", errObjectTampered 764 } 765 i := strings.IndexByte(object.ETag, '-') 766 if len(object.ETag[:i]) != 32 { 767 return "", errObjectTampered 768 } 769 if _, err := hex.DecodeString(object.ETag[:32]); err != nil { 770 return "", errObjectTampered 771 } 772 if _, err := strconv.ParseInt(object.ETag[i+1:], 10, 32); err != nil { 773 return "", errObjectTampered 774 } 775 return object.ETag, nil 776 } 777 778 etag, err := hex.DecodeString(object.ETag) 779 if err != nil { 780 return "", err 781 } 782 etag, err = key.UnsealETag(etag) 783 if err != nil { 784 return "", err 785 } 786 return hex.EncodeToString(etag), nil 787 } 788 789 // For encrypted objects, the ETag sent by client if available 790 // is stored in encrypted form in the backend. Decrypt the ETag 791 // if ETag was previously encrypted. 792 func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool) (decryptedETag string) { 793 var ( 794 key [32]byte 795 err error 796 ) 797 // If ETag is contentMD5Sum return it as is. 798 if len(objInfo.ETag) == 32 { 799 return objInfo.ETag 800 } 801 802 if crypto.IsMultiPart(objInfo.UserDefined) { 803 return objInfo.ETag 804 } 805 806 if crypto.SSECopy.IsRequested(headers) { 807 key, err = crypto.SSECopy.ParseHTTP(headers) 808 if err != nil { 809 return objInfo.ETag 810 } 811 } 812 813 // As per AWS S3 Spec, ETag for SSE-C encrypted objects need not be MD5Sum of the data. 814 // Since server side copy with same source and dest just replaces the ETag, we save 815 // encrypted content MD5Sum as ETag for both SSE-C and SSE-KMS, we standardize the ETag 816 // encryption across SSE-C and SSE-KMS, and only return last 32 bytes for SSE-C 817 if (crypto.SSEC.IsEncrypted(objInfo.UserDefined) || crypto.S3KMS.IsEncrypted(objInfo.UserDefined)) && !copySource { 818 return objInfo.ETag[len(objInfo.ETag)-32:] 819 } 820 821 objectEncryptionKey, err := decryptObjectMeta(key[:], objInfo.Bucket, objInfo.Name, objInfo.UserDefined) 822 if err != nil { 823 return objInfo.ETag 824 } 825 return tryDecryptETag(objectEncryptionKey, objInfo.ETag, true) 826 } 827 828 // helper to decrypt Etag given object encryption key and encrypted ETag 829 func tryDecryptETag(key []byte, encryptedETag string, sses3 bool) string { 830 // ETag for SSE-C or SSE-KMS encrypted objects need not be content MD5Sum.While encrypted 831 // md5sum is stored internally, return just the last 32 bytes of hex-encoded and 832 // encrypted md5sum string for SSE-C 833 if !sses3 { 834 return encryptedETag[len(encryptedETag)-32:] 835 } 836 var objectKey crypto.ObjectKey 837 copy(objectKey[:], key) 838 encBytes, err := hex.DecodeString(encryptedETag) 839 if err != nil { 840 return encryptedETag 841 } 842 etagBytes, err := objectKey.UnsealETag(encBytes) 843 if err != nil { 844 return encryptedETag 845 } 846 return hex.EncodeToString(etagBytes) 847 } 848 849 // GetDecryptedRange - To decrypt the range (off, length) of the 850 // decrypted object stream, we need to read the range (encOff, 851 // encLength) of the encrypted object stream to decrypt it, and 852 // compute skipLen, the number of bytes to skip in the beginning of 853 // the encrypted range. 854 // 855 // In addition we also compute the object part number for where the 856 // requested range starts, along with the DARE sequence number within 857 // that part. For single part objects, the partStart will be 0. 858 func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) { 859 if _, ok := crypto.IsEncrypted(o.UserDefined); !ok { 860 err = errors.New("Object is not encrypted") 861 return 862 } 863 864 if rs == nil { 865 // No range, so offsets refer to the whole object. 866 return 0, o.Size, 0, 0, 0, nil 867 } 868 869 // Assemble slice of (decrypted) part sizes in `sizes` 870 var sizes []int64 871 var decObjSize int64 // decrypted total object size 872 if o.isMultipart() { 873 sizes = make([]int64, len(o.Parts)) 874 for i, part := range o.Parts { 875 var partSize uint64 876 partSize, err = sio.DecryptedSize(uint64(part.Size)) 877 if err != nil { 878 err = errObjectTampered 879 return 880 } 881 sizes[i] = int64(partSize) 882 decObjSize += int64(partSize) 883 } 884 } else { 885 var partSize uint64 886 partSize, err = sio.DecryptedSize(uint64(o.Size)) 887 if err != nil { 888 err = errObjectTampered 889 return 890 } 891 sizes = []int64{int64(partSize)} 892 decObjSize = sizes[0] 893 } 894 895 var off, length int64 896 off, length, err = rs.GetOffsetLength(decObjSize) 897 if err != nil { 898 return 899 } 900 901 // At this point, we have: 902 // 903 // 1. the decrypted part sizes in `sizes` (single element for 904 // single part object) and total decrypted object size `decObjSize` 905 // 906 // 2. the (decrypted) start offset `off` and (decrypted) 907 // length to read `length` 908 // 909 // These are the inputs to the rest of the algorithm below. 910 911 // Locate the part containing the start of the required range 912 var partEnd int 913 var cumulativeSum, encCumulativeSum int64 914 for i, size := range sizes { 915 if off < cumulativeSum+size { 916 partStart = i 917 break 918 } 919 cumulativeSum += size 920 encPartSize, _ := sio.EncryptedSize(uint64(size)) 921 encCumulativeSum += int64(encPartSize) 922 } 923 // partStart is always found in the loop above, 924 // because off is validated. 925 926 sseDAREEncPackageBlockSize := int64(SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) 927 startPkgNum := (off - cumulativeSum) / SSEDAREPackageBlockSize 928 929 // Now we can calculate the number of bytes to skip 930 skipLen = (off - cumulativeSum) % SSEDAREPackageBlockSize 931 932 encOff = encCumulativeSum + startPkgNum*sseDAREEncPackageBlockSize 933 // Locate the part containing the end of the required range 934 endOffset := off + length - 1 935 for i1, size := range sizes[partStart:] { 936 i := partStart + i1 937 if endOffset < cumulativeSum+size { 938 partEnd = i 939 break 940 } 941 cumulativeSum += size 942 encPartSize, _ := sio.EncryptedSize(uint64(size)) 943 encCumulativeSum += int64(encPartSize) 944 } 945 // partEnd is always found in the loop above, because off and 946 // length are validated. 947 endPkgNum := (endOffset - cumulativeSum) / SSEDAREPackageBlockSize 948 // Compute endEncOffset with one additional DARE package (so 949 // we read the package containing the last desired byte). 950 endEncOffset := encCumulativeSum + (endPkgNum+1)*sseDAREEncPackageBlockSize 951 // Check if the DARE package containing the end offset is a 952 // full sized package (as the last package in the part may be 953 // smaller) 954 lastPartSize, _ := sio.EncryptedSize(uint64(sizes[partEnd])) 955 if endEncOffset > encCumulativeSum+int64(lastPartSize) { 956 endEncOffset = encCumulativeSum + int64(lastPartSize) 957 } 958 encLength = endEncOffset - encOff 959 // Set the sequence number as the starting package number of 960 // the requested block 961 seqNumber = uint32(startPkgNum) 962 return encOff, encLength, skipLen, seqNumber, partStart, nil 963 } 964 965 // EncryptedSize returns the size of the object after encryption. 966 // An encrypted object is always larger than a plain object 967 // except for zero size objects. 968 func (o *ObjectInfo) EncryptedSize() int64 { 969 size, err := sio.EncryptedSize(uint64(o.Size)) 970 if err != nil { 971 // This cannot happen since AWS S3 allows parts to be 5GB at most 972 // sio max. size is 256 TB 973 reqInfo := (&logger.ReqInfo{}).AppendTags("size", strconv.FormatUint(size, 10)) 974 ctx := logger.SetReqInfo(GlobalContext, reqInfo) 975 logger.CriticalIf(ctx, err) 976 } 977 return int64(size) 978 } 979 980 // DecryptObjectInfo tries to decrypt the provided object if it is encrypted. 981 // It fails if the object is encrypted and the HTTP headers don't contain 982 // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior) 983 // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the 984 // decryption succeeded. 985 // 986 // DecryptObjectInfo also returns whether the object is encrypted or not. 987 func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err error) { 988 // Directories are never encrypted. 989 if info.IsDir { 990 return false, nil 991 } 992 if r == nil { 993 return false, errInvalidArgument 994 } 995 996 headers := r.Header 997 998 // disallow X-Amz-Server-Side-Encryption header on HEAD and GET 999 switch r.Method { 1000 case http.MethodGet, http.MethodHead: 1001 if crypto.S3.IsRequested(headers) || crypto.S3KMS.IsRequested(headers) { 1002 return false, errInvalidEncryptionParameters 1003 } 1004 } 1005 1006 _, encrypted = crypto.IsEncrypted(info.UserDefined) 1007 if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" { 1008 return false, errInvalidEncryptionParameters 1009 } 1010 1011 if encrypted { 1012 if crypto.SSEC.IsEncrypted(info.UserDefined) { 1013 if !(crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers)) { 1014 return encrypted, errEncryptedObject 1015 } 1016 } 1017 1018 if crypto.S3.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" { 1019 if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) { 1020 return encrypted, errEncryptedObject 1021 } 1022 } 1023 1024 if crypto.S3KMS.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" { 1025 if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) { 1026 return encrypted, errEncryptedObject 1027 } 1028 } 1029 1030 if _, err = info.DecryptedSize(); err != nil { 1031 return encrypted, err 1032 } 1033 1034 if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) { 1035 info.ETag = getDecryptedETag(headers, *info, false) 1036 } 1037 } 1038 1039 return encrypted, nil 1040 } 1041 1042 type ( 1043 objectMetaEncryptFn func(baseKey string, data []byte) []byte 1044 objectMetaDecryptFn func(baseKey string, data []byte) ([]byte, error) 1045 ) 1046 1047 // metadataEncrypter returns a function that will read data from input, 1048 // encrypt it using the provided key and return the result. 1049 // 0 sized inputs are passed through. 1050 func metadataEncrypter(key crypto.ObjectKey) objectMetaEncryptFn { 1051 return func(baseKey string, data []byte) []byte { 1052 if len(data) == 0 { 1053 return data 1054 } 1055 var buffer bytes.Buffer 1056 mac := hmac.New(sha256.New, key[:]) 1057 mac.Write([]byte(baseKey)) 1058 if _, err := sio.Encrypt(&buffer, bytes.NewReader(data), sio.Config{Key: mac.Sum(nil), CipherSuites: fips.DARECiphers()}); err != nil { 1059 logger.CriticalIf(context.Background(), errors.New("unable to encrypt using object key")) 1060 } 1061 return buffer.Bytes() 1062 } 1063 } 1064 1065 // metadataDecrypter reverses metadataEncrypter. 1066 func (o *ObjectInfo) metadataDecrypter() objectMetaDecryptFn { 1067 return func(baseKey string, input []byte) ([]byte, error) { 1068 if len(input) == 0 { 1069 return input, nil 1070 } 1071 1072 key, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined) 1073 if err != nil { 1074 return nil, err 1075 } 1076 mac := hmac.New(sha256.New, key) 1077 mac.Write([]byte(baseKey)) 1078 return sio.DecryptBuffer(nil, input, sio.Config{Key: mac.Sum(nil), CipherSuites: fips.DARECiphers()}) 1079 } 1080 } 1081 1082 // decryptChecksums will attempt to decode checksums and return it/them if set. 1083 // if part > 0, and we have the checksum for the part that will be returned. 1084 func (o *ObjectInfo) decryptPartsChecksums() { 1085 data := o.Checksum 1086 if len(data) == 0 { 1087 return 1088 } 1089 if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted { 1090 decrypted, err := o.metadataDecrypter()("object-checksum", data) 1091 if err != nil { 1092 logger.LogIf(GlobalContext, err) 1093 return 1094 } 1095 data = decrypted 1096 } 1097 cs := hash.ReadPartCheckSums(data) 1098 if len(cs) == len(o.Parts) { 1099 for i := range o.Parts { 1100 o.Parts[i].Checksums = cs[i] 1101 } 1102 } 1103 return 1104 } 1105 1106 // metadataEncryptFn provides an encryption function for metadata. 1107 // Will return nil, nil if unencrypted. 1108 func (o *ObjectInfo) metadataEncryptFn(headers http.Header) (objectMetaEncryptFn, error) { 1109 kind, _ := crypto.IsEncrypted(o.UserDefined) 1110 switch kind { 1111 case crypto.SSEC: 1112 if crypto.SSECopy.IsRequested(headers) { 1113 key, err := crypto.SSECopy.ParseHTTP(headers) 1114 if err != nil { 1115 return nil, err 1116 } 1117 objectEncryptionKey, err := decryptObjectMeta(key[:], o.Bucket, o.Name, o.UserDefined) 1118 if err != nil { 1119 return nil, err 1120 } 1121 if len(objectEncryptionKey) == 32 { 1122 var key crypto.ObjectKey 1123 copy(key[:], objectEncryptionKey) 1124 return metadataEncrypter(key), nil 1125 } 1126 return nil, errors.New("metadataEncryptFn: unexpected key size") 1127 } 1128 case crypto.S3, crypto.S3KMS: 1129 objectEncryptionKey, err := decryptObjectMeta(nil, o.Bucket, o.Name, o.UserDefined) 1130 if err != nil { 1131 return nil, err 1132 } 1133 if len(objectEncryptionKey) == 32 { 1134 var key crypto.ObjectKey 1135 copy(key[:], objectEncryptionKey) 1136 return metadataEncrypter(key), nil 1137 } 1138 return nil, errors.New("metadataEncryptFn: unexpected key size") 1139 } 1140 1141 return nil, nil 1142 } 1143 1144 // decryptChecksums will attempt to decode checksums and return it/them if set. 1145 // if part > 0, and we have the checksum for the part that will be returned. 1146 func (o *ObjectInfo) decryptChecksums(part int) map[string]string { 1147 data := o.Checksum 1148 if len(data) == 0 { 1149 return nil 1150 } 1151 if _, encrypted := crypto.IsEncrypted(o.UserDefined); encrypted { 1152 decrypted, err := o.metadataDecrypter()("object-checksum", data) 1153 if err != nil { 1154 logger.LogIf(GlobalContext, err) 1155 return nil 1156 } 1157 data = decrypted 1158 } 1159 return hash.ReadCheckSums(data, part) 1160 }