storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/encryption-v1.go (about) 1 /* 2 * MinIO Cloud Storage, (C) 2017-2020 MinIO, Inc. 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 package cmd 18 19 import ( 20 "bufio" 21 "crypto/hmac" 22 "crypto/rand" 23 "crypto/sha256" 24 "crypto/subtle" 25 "encoding/binary" 26 "encoding/hex" 27 "errors" 28 "io" 29 "net/http" 30 "path" 31 "strconv" 32 "strings" 33 34 "github.com/minio/sio" 35 36 "storj.io/minio/cmd/crypto" 37 xhttp "storj.io/minio/cmd/http" 38 "storj.io/minio/cmd/logger" 39 "storj.io/minio/pkg/fips" 40 ) 41 42 var ( 43 // AWS errors for invalid SSE-C requests. 44 errEncryptedObject = errors.New("The object was stored using a form of SSE") 45 errInvalidSSEParameters = errors.New("The SSE-C key for key-rotation is not correct") // special access denied 46 errKMSNotConfigured = errors.New("KMS not configured for a server side encrypted object") 47 // Additional MinIO errors for SSE-C requests. 48 errObjectTampered = errors.New("The requested object was modified and may be compromised") 49 // error returned when invalid encryption parameters are specified 50 errInvalidEncryptionParameters = errors.New("The encryption parameters are not applicable to this object") 51 ) 52 53 const ( 54 // SSECustomerKeySize is the size of valid client provided encryption keys in bytes. 55 // Currently AWS supports only AES256. So the SSE-C key size is fixed to 32 bytes. 56 SSECustomerKeySize = 32 57 58 // SSEIVSize is the size of the IV data 59 SSEIVSize = 32 // 32 bytes 60 61 // SSEDAREPackageBlockSize - SSE dare package block size. 62 SSEDAREPackageBlockSize = 64 * 1024 // 64KiB bytes 63 64 // SSEDAREPackageMetaSize - SSE dare package meta padding bytes. 65 SSEDAREPackageMetaSize = 32 // 32 bytes 66 67 ) 68 69 // isEncryptedMultipart returns true if the current object is 70 // uploaded by the user using multipart mechanism: 71 // initiate new multipart, upload part, complete upload 72 func isEncryptedMultipart(objInfo ObjectInfo) bool { 73 if len(objInfo.Parts) == 0 { 74 return false 75 } 76 if !crypto.IsMultiPart(objInfo.UserDefined) { 77 return false 78 } 79 for _, part := range objInfo.Parts { 80 _, err := sio.DecryptedSize(uint64(part.Size)) 81 if err != nil { 82 return false 83 } 84 } 85 // Further check if this object is uploaded using multipart mechanism 86 // by the user and it is not about Erasure internally splitting the 87 // object into parts in PutObject() 88 return !(objInfo.backendType == BackendErasure && len(objInfo.ETag) == 32) 89 } 90 91 // ParseSSECopyCustomerRequest parses the SSE-C header fields of the provided request. 92 // It returns the client provided key on success. 93 func ParseSSECopyCustomerRequest(h http.Header, metadata map[string]string) (key []byte, err error) { 94 if crypto.S3.IsEncrypted(metadata) && crypto.SSECopy.IsRequested(h) { 95 return nil, crypto.ErrIncompatibleEncryptionMethod 96 } 97 k, err := crypto.SSECopy.ParseHTTP(h) 98 return k[:], err 99 } 100 101 // ParseSSECustomerRequest parses the SSE-C header fields of the provided request. 102 // It returns the client provided key on success. 103 func ParseSSECustomerRequest(r *http.Request) (key []byte, err error) { 104 return ParseSSECustomerHeader(r.Header) 105 } 106 107 // ParseSSECustomerHeader parses the SSE-C header fields and returns 108 // the client provided key on success. 109 func ParseSSECustomerHeader(header http.Header) (key []byte, err error) { 110 if crypto.S3.IsRequested(header) && crypto.SSEC.IsRequested(header) { 111 return key, crypto.ErrIncompatibleEncryptionMethod 112 } 113 114 k, err := crypto.SSEC.ParseHTTP(header) 115 return k[:], err 116 } 117 118 // This function rotates old to new key. 119 func rotateKey(oldKey []byte, newKey []byte, bucket, object string, metadata map[string]string) error { 120 switch { 121 default: 122 return errObjectTampered 123 case crypto.SSEC.IsEncrypted(metadata): 124 sealedKey, err := crypto.SSEC.ParseMetadata(metadata) 125 if err != nil { 126 return err 127 } 128 129 var objectKey crypto.ObjectKey 130 if err = objectKey.Unseal(oldKey, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { 131 if subtle.ConstantTimeCompare(oldKey, newKey) == 1 { 132 return errInvalidSSEParameters // AWS returns special error for equal but invalid keys. 133 } 134 return crypto.ErrInvalidCustomerKey // To provide strict AWS S3 compatibility we return: access denied. 135 136 } 137 if subtle.ConstantTimeCompare(oldKey, newKey) == 1 && sealedKey.Algorithm == crypto.SealAlgorithm { 138 return nil // don't rotate on equal keys if seal algorithm is latest 139 } 140 sealedKey = objectKey.Seal(newKey, sealedKey.IV, crypto.SSEC.String(), bucket, object) 141 crypto.SSEC.CreateMetadata(metadata, sealedKey) 142 return nil 143 case crypto.S3.IsEncrypted(metadata): 144 if GlobalKMS == nil { 145 return errKMSNotConfigured 146 } 147 keyID, kmsKey, sealedKey, err := crypto.S3.ParseMetadata(metadata) 148 if err != nil { 149 return err 150 } 151 oldKey, err := GlobalKMS.DecryptKey(keyID, kmsKey, crypto.Context{bucket: path.Join(bucket, object)}) 152 if err != nil { 153 return err 154 } 155 var objectKey crypto.ObjectKey 156 if err = objectKey.Unseal(oldKey, sealedKey, crypto.S3.String(), bucket, object); err != nil { 157 return err 158 } 159 160 newKey, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)}) 161 if err != nil { 162 return err 163 } 164 sealedKey = objectKey.Seal(newKey.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) 165 crypto.S3.CreateMetadata(metadata, newKey.KeyID, newKey.Ciphertext, sealedKey) 166 return nil 167 } 168 } 169 170 func newEncryptMetadata(key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (crypto.ObjectKey, error) { 171 var sealedKey crypto.SealedKey 172 if sseS3 { 173 if GlobalKMS == nil { 174 return crypto.ObjectKey{}, errKMSNotConfigured 175 } 176 key, err := GlobalKMS.GenerateKey("", crypto.Context{bucket: path.Join(bucket, object)}) 177 if err != nil { 178 return crypto.ObjectKey{}, err 179 } 180 181 objectKey := crypto.GenerateKey(key.Plaintext, rand.Reader) 182 sealedKey = objectKey.Seal(key.Plaintext, crypto.GenerateIV(rand.Reader), crypto.S3.String(), bucket, object) 183 crypto.S3.CreateMetadata(metadata, key.KeyID, key.Ciphertext, sealedKey) 184 return objectKey, nil 185 } 186 objectKey := crypto.GenerateKey(key, rand.Reader) 187 sealedKey = objectKey.Seal(key, crypto.GenerateIV(rand.Reader), crypto.SSEC.String(), bucket, object) 188 crypto.SSEC.CreateMetadata(metadata, sealedKey) 189 return objectKey, nil 190 } 191 192 func newEncryptReader(content io.Reader, key []byte, bucket, object string, metadata map[string]string, sseS3 bool) (io.Reader, crypto.ObjectKey, error) { 193 objectEncryptionKey, err := newEncryptMetadata(key, bucket, object, metadata, sseS3) 194 if err != nil { 195 return nil, crypto.ObjectKey{}, err 196 } 197 198 reader, err := sio.EncryptReader(content, sio.Config{Key: objectEncryptionKey[:], MinVersion: sio.Version20, CipherSuites: fips.CipherSuitesDARE()}) 199 if err != nil { 200 return nil, crypto.ObjectKey{}, crypto.ErrInvalidCustomerKey 201 } 202 203 return reader, objectEncryptionKey, nil 204 } 205 206 // set new encryption metadata from http request headers for SSE-C and generated key from KMS in the case of 207 // SSE-S3 208 func setEncryptionMetadata(r *http.Request, bucket, object string, metadata map[string]string) (err error) { 209 var ( 210 key []byte 211 ) 212 if crypto.SSEC.IsRequested(r.Header) { 213 key, err = ParseSSECustomerRequest(r) 214 if err != nil { 215 return 216 } 217 } 218 _, err = newEncryptMetadata(key, bucket, object, metadata, crypto.S3.IsRequested(r.Header)) 219 return 220 } 221 222 // EncryptRequest takes the client provided content and encrypts the data 223 // with the client provided key. It also marks the object as client-side-encrypted 224 // and sets the correct headers. 225 func EncryptRequest(content io.Reader, r *http.Request, bucket, object string, metadata map[string]string) (io.Reader, crypto.ObjectKey, error) { 226 if crypto.S3.IsRequested(r.Header) && crypto.SSEC.IsRequested(r.Header) { 227 return nil, crypto.ObjectKey{}, crypto.ErrIncompatibleEncryptionMethod 228 } 229 if r.ContentLength > encryptBufferThreshold { 230 // The encryption reads in blocks of 64KB. 231 // We add a buffer on bigger files to reduce the number of syscalls upstream. 232 content = bufio.NewReaderSize(content, encryptBufferSize) 233 } 234 235 var key []byte 236 if crypto.SSEC.IsRequested(r.Header) { 237 var err error 238 key, err = ParseSSECustomerRequest(r) 239 if err != nil { 240 return nil, crypto.ObjectKey{}, err 241 } 242 } 243 return newEncryptReader(content, key, bucket, object, metadata, crypto.S3.IsRequested(r.Header)) 244 } 245 246 func decryptObjectInfo(key []byte, bucket, object string, metadata map[string]string) ([]byte, error) { 247 switch kind, _ := crypto.IsEncrypted(metadata); kind { 248 case crypto.S3: 249 var KMS crypto.KMS = GlobalKMS 250 if isCacheEncrypted(metadata) { 251 KMS = globalCacheKMS 252 } 253 if KMS == nil { 254 return nil, errKMSNotConfigured 255 } 256 objectKey, err := crypto.S3.UnsealObjectKey(KMS, metadata, bucket, object) 257 if err != nil { 258 return nil, err 259 } 260 return objectKey[:], nil 261 case crypto.S3KMS: 262 if GlobalKMS == nil { 263 return nil, errKMSNotConfigured 264 } 265 objectKey, err := crypto.S3KMS.UnsealObjectKey(GlobalKMS, metadata, bucket, object) 266 if err != nil { 267 return nil, err 268 } 269 return objectKey[:], nil 270 case crypto.SSEC: 271 sealedKey, err := crypto.SSEC.ParseMetadata(metadata) 272 if err != nil { 273 return nil, err 274 } 275 var objectKey crypto.ObjectKey 276 if err = objectKey.Unseal(key, sealedKey, crypto.SSEC.String(), bucket, object); err != nil { 277 return nil, err 278 } 279 return objectKey[:], nil 280 default: 281 return nil, errObjectTampered 282 } 283 } 284 285 // Adding support for reader based interface 286 287 // DecryptRequestWithSequenceNumberR - same as 288 // DecryptRequestWithSequenceNumber but with a reader 289 func DecryptRequestWithSequenceNumberR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 290 if crypto.S3.IsEncrypted(metadata) { 291 return newDecryptReader(client, nil, bucket, object, seqNumber, metadata) 292 } 293 294 key, err := ParseSSECustomerHeader(h) 295 if err != nil { 296 return nil, err 297 } 298 return newDecryptReader(client, key, bucket, object, seqNumber, metadata) 299 } 300 301 // DecryptCopyRequestR - same as DecryptCopyRequest, but with a 302 // Reader 303 func DecryptCopyRequestR(client io.Reader, h http.Header, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 304 var ( 305 key []byte 306 err error 307 ) 308 if crypto.SSECopy.IsRequested(h) { 309 key, err = ParseSSECopyCustomerRequest(h, metadata) 310 if err != nil { 311 return nil, err 312 } 313 } 314 return newDecryptReader(client, key, bucket, object, seqNumber, metadata) 315 } 316 317 func newDecryptReader(client io.Reader, key []byte, bucket, object string, seqNumber uint32, metadata map[string]string) (io.Reader, error) { 318 objectEncryptionKey, err := decryptObjectInfo(key, bucket, object, metadata) 319 if err != nil { 320 return nil, err 321 } 322 return newDecryptReaderWithObjectKey(client, objectEncryptionKey, seqNumber) 323 } 324 325 func newDecryptReaderWithObjectKey(client io.Reader, objectEncryptionKey []byte, seqNumber uint32) (io.Reader, error) { 326 reader, err := sio.DecryptReader(client, sio.Config{ 327 Key: objectEncryptionKey, 328 SequenceNumber: seqNumber, 329 CipherSuites: fips.CipherSuitesDARE(), 330 }) 331 if err != nil { 332 return nil, crypto.ErrInvalidCustomerKey 333 } 334 return reader, nil 335 } 336 337 // DecryptBlocksRequestR - same as DecryptBlocksRequest but with a 338 // reader 339 func DecryptBlocksRequestR(inputReader io.Reader, h http.Header, seqNumber uint32, partStart int, oi ObjectInfo, copySource bool) (io.Reader, error) { 340 341 bucket, object := oi.Bucket, oi.Name 342 // Single part case 343 if !isEncryptedMultipart(oi) { 344 var reader io.Reader 345 var err error 346 if copySource { 347 reader, err = DecryptCopyRequestR(inputReader, h, bucket, object, seqNumber, oi.UserDefined) 348 } else { 349 reader, err = DecryptRequestWithSequenceNumberR(inputReader, h, bucket, object, seqNumber, oi.UserDefined) 350 } 351 if err != nil { 352 return nil, err 353 } 354 return reader, nil 355 } 356 357 partDecRelOffset := int64(seqNumber) * SSEDAREPackageBlockSize 358 partEncRelOffset := int64(seqNumber) * (SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) 359 360 w := &DecryptBlocksReader{ 361 reader: inputReader, 362 startSeqNum: seqNumber, 363 partDecRelOffset: partDecRelOffset, 364 partEncRelOffset: partEncRelOffset, 365 parts: oi.Parts, 366 partIndex: partStart, 367 header: h, 368 bucket: bucket, 369 object: object, 370 customerKeyHeader: h.Get(xhttp.AmzServerSideEncryptionCustomerKey), 371 copySource: copySource, 372 metadata: cloneMSS(oi.UserDefined), 373 } 374 375 if w.copySource { 376 w.customerKeyHeader = h.Get(xhttp.AmzServerSideEncryptionCopyCustomerKey) 377 } 378 379 if err := w.buildDecrypter(w.parts[w.partIndex].Number); err != nil { 380 return nil, err 381 } 382 383 return w, nil 384 } 385 386 // DecryptBlocksReader - decrypts multipart parts, while implementing 387 // a io.Reader compatible interface. 388 type DecryptBlocksReader struct { 389 // Source of the encrypted content that will be decrypted 390 reader io.Reader 391 // Current decrypter for the current encrypted data block 392 decrypter io.Reader 393 // Start sequence number 394 startSeqNum uint32 395 // Current part index 396 partIndex int 397 // Parts information 398 parts []ObjectPartInfo 399 header http.Header 400 bucket, object string 401 metadata map[string]string 402 403 partDecRelOffset, partEncRelOffset int64 404 405 copySource bool 406 // Customer Key 407 customerKeyHeader string 408 } 409 410 func (d *DecryptBlocksReader) buildDecrypter(partID int) error { 411 m := cloneMSS(d.metadata) 412 // Initialize the first decrypter; new decrypters will be 413 // initialized in Read() operation as needed. 414 var key []byte 415 var err error 416 if d.copySource { 417 if crypto.SSEC.IsEncrypted(d.metadata) { 418 d.header.Set(xhttp.AmzServerSideEncryptionCopyCustomerKey, d.customerKeyHeader) 419 key, err = ParseSSECopyCustomerRequest(d.header, d.metadata) 420 } 421 } else { 422 if crypto.SSEC.IsEncrypted(d.metadata) { 423 d.header.Set(xhttp.AmzServerSideEncryptionCustomerKey, d.customerKeyHeader) 424 key, err = ParseSSECustomerHeader(d.header) 425 } 426 } 427 if err != nil { 428 return err 429 } 430 431 objectEncryptionKey, err := decryptObjectInfo(key, d.bucket, d.object, m) 432 if err != nil { 433 return err 434 } 435 436 var partIDbin [4]byte 437 binary.LittleEndian.PutUint32(partIDbin[:], uint32(partID)) // marshal part ID 438 439 mac := hmac.New(sha256.New, objectEncryptionKey) // derive part encryption key from part ID and object key 440 mac.Write(partIDbin[:]) 441 partEncryptionKey := mac.Sum(nil) 442 443 // Limit the reader, so the decryptor doesnt receive bytes 444 // from the next part (different DARE stream) 445 encLenToRead := d.parts[d.partIndex].Size - d.partEncRelOffset 446 decrypter, err := newDecryptReaderWithObjectKey(io.LimitReader(d.reader, encLenToRead), partEncryptionKey, d.startSeqNum) 447 if err != nil { 448 return err 449 } 450 451 d.decrypter = decrypter 452 return nil 453 } 454 455 func (d *DecryptBlocksReader) Read(p []byte) (int, error) { 456 var err error 457 var n1 int 458 decPartSize, _ := sio.DecryptedSize(uint64(d.parts[d.partIndex].Size)) 459 unreadPartLen := int64(decPartSize) - d.partDecRelOffset 460 if int64(len(p)) < unreadPartLen { 461 n1, err = d.decrypter.Read(p) 462 if err != nil { 463 return 0, err 464 } 465 d.partDecRelOffset += int64(n1) 466 } else { 467 n1, err = io.ReadFull(d.decrypter, p[:unreadPartLen]) 468 if err != nil { 469 return 0, err 470 } 471 472 // We should now proceed to next part, reset all 473 // values appropriately. 474 d.partEncRelOffset = 0 475 d.partDecRelOffset = 0 476 d.startSeqNum = 0 477 478 d.partIndex++ 479 if d.partIndex == len(d.parts) { 480 return n1, io.EOF 481 } 482 483 err = d.buildDecrypter(d.parts[d.partIndex].Number) 484 if err != nil { 485 return 0, err 486 } 487 488 n1, err = d.decrypter.Read(p[n1:]) 489 if err != nil { 490 return 0, err 491 } 492 493 d.partDecRelOffset += int64(n1) 494 } 495 return len(p), nil 496 } 497 498 // DecryptedSize returns the size of the object after decryption in bytes. 499 // It returns an error if the object is not encrypted or marked as encrypted 500 // but has an invalid size. 501 func (o *ObjectInfo) DecryptedSize() (int64, error) { 502 if _, ok := crypto.IsEncrypted(o.UserDefined); !ok { 503 return 0, errors.New("Cannot compute decrypted size of an unencrypted object") 504 } 505 if !isEncryptedMultipart(*o) { 506 size, err := sio.DecryptedSize(uint64(o.Size)) 507 if err != nil { 508 err = errObjectTampered // assign correct error type 509 } 510 return int64(size), err 511 } 512 513 var size int64 514 for _, part := range o.Parts { 515 partSize, err := sio.DecryptedSize(uint64(part.Size)) 516 if err != nil { 517 return 0, errObjectTampered 518 } 519 size += int64(partSize) 520 } 521 return size, nil 522 } 523 524 // DecryptETag decrypts the ETag that is part of given object 525 // with the given object encryption key. 526 // 527 // However, DecryptETag does not try to decrypt the ETag if 528 // it consists of a 128 bit hex value (32 hex chars) and exactly 529 // one '-' followed by a 32-bit number. 530 // This special case adresses randomly-generated ETags generated 531 // by the MinIO server when running in non-compat mode. These 532 // random ETags are not encrypt. 533 // 534 // Calling DecryptETag with a non-randomly generated ETag will 535 // fail. 536 func DecryptETag(key crypto.ObjectKey, object ObjectInfo) (string, error) { 537 if n := strings.Count(object.ETag, "-"); n > 0 { 538 if n != 1 { 539 return "", errObjectTampered 540 } 541 i := strings.IndexByte(object.ETag, '-') 542 if len(object.ETag[:i]) != 32 { 543 return "", errObjectTampered 544 } 545 if _, err := hex.DecodeString(object.ETag[:32]); err != nil { 546 return "", errObjectTampered 547 } 548 if _, err := strconv.ParseInt(object.ETag[i+1:], 10, 32); err != nil { 549 return "", errObjectTampered 550 } 551 return object.ETag, nil 552 } 553 554 etag, err := hex.DecodeString(object.ETag) 555 if err != nil { 556 return "", err 557 } 558 etag, err = key.UnsealETag(etag) 559 if err != nil { 560 return "", err 561 } 562 return hex.EncodeToString(etag), nil 563 } 564 565 // For encrypted objects, the ETag sent by client if available 566 // is stored in encrypted form in the backend. Decrypt the ETag 567 // if ETag was previously encrypted. 568 func getDecryptedETag(headers http.Header, objInfo ObjectInfo, copySource bool) (decryptedETag string) { 569 var ( 570 key [32]byte 571 err error 572 ) 573 // If ETag is contentMD5Sum return it as is. 574 if len(objInfo.ETag) == 32 { 575 return objInfo.ETag 576 } 577 578 if crypto.IsMultiPart(objInfo.UserDefined) { 579 return objInfo.ETag 580 } 581 582 if crypto.SSECopy.IsRequested(headers) { 583 key, err = crypto.SSECopy.ParseHTTP(headers) 584 if err != nil { 585 return objInfo.ETag 586 } 587 } 588 589 // As per AWS S3 Spec, ETag for SSE-C encrypted objects need not be MD5Sum of the data. 590 // Since server side copy with same source and dest just replaces the ETag, we save 591 // encrypted content MD5Sum as ETag for both SSE-C and SSE-S3, we standardize the ETag 592 // encryption across SSE-C and SSE-S3, and only return last 32 bytes for SSE-C 593 if crypto.SSEC.IsEncrypted(objInfo.UserDefined) && !copySource { 594 return objInfo.ETag[len(objInfo.ETag)-32:] 595 } 596 597 objectEncryptionKey, err := decryptObjectInfo(key[:], objInfo.Bucket, objInfo.Name, objInfo.UserDefined) 598 if err != nil { 599 return objInfo.ETag 600 } 601 return tryDecryptETag(objectEncryptionKey, objInfo.ETag, false) 602 } 603 604 // helper to decrypt Etag given object encryption key and encrypted ETag 605 func tryDecryptETag(key []byte, encryptedETag string, ssec bool) string { 606 // ETag for SSE-C encrypted objects need not be content MD5Sum.While encrypted 607 // md5sum is stored internally, return just the last 32 bytes of hex-encoded and 608 // encrypted md5sum string for SSE-C 609 if ssec { 610 return encryptedETag[len(encryptedETag)-32:] 611 } 612 var objectKey crypto.ObjectKey 613 copy(objectKey[:], key) 614 encBytes, err := hex.DecodeString(encryptedETag) 615 if err != nil { 616 return encryptedETag 617 } 618 etagBytes, err := objectKey.UnsealETag(encBytes) 619 if err != nil { 620 return encryptedETag 621 } 622 return hex.EncodeToString(etagBytes) 623 } 624 625 // GetDecryptedRange - To decrypt the range (off, length) of the 626 // decrypted object stream, we need to read the range (encOff, 627 // encLength) of the encrypted object stream to decrypt it, and 628 // compute skipLen, the number of bytes to skip in the beginning of 629 // the encrypted range. 630 // 631 // In addition we also compute the object part number for where the 632 // requested range starts, along with the DARE sequence number within 633 // that part. For single part objects, the partStart will be 0. 634 func (o *ObjectInfo) GetDecryptedRange(rs *HTTPRangeSpec) (encOff, encLength, skipLen int64, seqNumber uint32, partStart int, err error) { 635 if _, ok := crypto.IsEncrypted(o.UserDefined); !ok { 636 err = errors.New("Object is not encrypted") 637 return 638 } 639 640 if rs == nil { 641 // No range, so offsets refer to the whole object. 642 return 0, o.Size, 0, 0, 0, nil 643 } 644 645 // Assemble slice of (decrypted) part sizes in `sizes` 646 var sizes []int64 647 var decObjSize int64 // decrypted total object size 648 if isEncryptedMultipart(*o) { 649 sizes = make([]int64, len(o.Parts)) 650 for i, part := range o.Parts { 651 var partSize uint64 652 partSize, err = sio.DecryptedSize(uint64(part.Size)) 653 if err != nil { 654 err = errObjectTampered 655 return 656 } 657 sizes[i] = int64(partSize) 658 decObjSize += int64(partSize) 659 } 660 } else { 661 var partSize uint64 662 partSize, err = sio.DecryptedSize(uint64(o.Size)) 663 if err != nil { 664 err = errObjectTampered 665 return 666 } 667 sizes = []int64{int64(partSize)} 668 decObjSize = sizes[0] 669 } 670 671 var off, length int64 672 off, length, err = rs.GetOffsetLength(decObjSize) 673 if err != nil { 674 return 675 } 676 677 // At this point, we have: 678 // 679 // 1. the decrypted part sizes in `sizes` (single element for 680 // single part object) and total decrypted object size `decObjSize` 681 // 682 // 2. the (decrypted) start offset `off` and (decrypted) 683 // length to read `length` 684 // 685 // These are the inputs to the rest of the algorithm below. 686 687 // Locate the part containing the start of the required range 688 var partEnd int 689 var cumulativeSum, encCumulativeSum int64 690 for i, size := range sizes { 691 if off < cumulativeSum+size { 692 partStart = i 693 break 694 } 695 cumulativeSum += size 696 encPartSize, _ := sio.EncryptedSize(uint64(size)) 697 encCumulativeSum += int64(encPartSize) 698 } 699 // partStart is always found in the loop above, 700 // because off is validated. 701 702 sseDAREEncPackageBlockSize := int64(SSEDAREPackageBlockSize + SSEDAREPackageMetaSize) 703 startPkgNum := (off - cumulativeSum) / SSEDAREPackageBlockSize 704 705 // Now we can calculate the number of bytes to skip 706 skipLen = (off - cumulativeSum) % SSEDAREPackageBlockSize 707 708 encOff = encCumulativeSum + startPkgNum*sseDAREEncPackageBlockSize 709 // Locate the part containing the end of the required range 710 endOffset := off + length - 1 711 for i1, size := range sizes[partStart:] { 712 i := partStart + i1 713 if endOffset < cumulativeSum+size { 714 partEnd = i 715 break 716 } 717 cumulativeSum += size 718 encPartSize, _ := sio.EncryptedSize(uint64(size)) 719 encCumulativeSum += int64(encPartSize) 720 } 721 // partEnd is always found in the loop above, because off and 722 // length are validated. 723 endPkgNum := (endOffset - cumulativeSum) / SSEDAREPackageBlockSize 724 // Compute endEncOffset with one additional DARE package (so 725 // we read the package containing the last desired byte). 726 endEncOffset := encCumulativeSum + (endPkgNum+1)*sseDAREEncPackageBlockSize 727 // Check if the DARE package containing the end offset is a 728 // full sized package (as the last package in the part may be 729 // smaller) 730 lastPartSize, _ := sio.EncryptedSize(uint64(sizes[partEnd])) 731 if endEncOffset > encCumulativeSum+int64(lastPartSize) { 732 endEncOffset = encCumulativeSum + int64(lastPartSize) 733 } 734 encLength = endEncOffset - encOff 735 // Set the sequence number as the starting package number of 736 // the requested block 737 seqNumber = uint32(startPkgNum) 738 return encOff, encLength, skipLen, seqNumber, partStart, nil 739 } 740 741 // EncryptedSize returns the size of the object after encryption. 742 // An encrypted object is always larger than a plain object 743 // except for zero size objects. 744 func (o *ObjectInfo) EncryptedSize() int64 { 745 size, err := sio.EncryptedSize(uint64(o.Size)) 746 if err != nil { 747 // This cannot happen since AWS S3 allows parts to be 5GB at most 748 // sio max. size is 256 TB 749 reqInfo := (&logger.ReqInfo{}).AppendTags("size", strconv.FormatUint(size, 10)) 750 ctx := logger.SetReqInfo(GlobalContext, reqInfo) 751 logger.CriticalIf(ctx, err) 752 } 753 return int64(size) 754 } 755 756 // DecryptObjectInfo tries to decrypt the provided object if it is encrypted. 757 // It fails if the object is encrypted and the HTTP headers don't contain 758 // SSE-C headers or the object is not encrypted but SSE-C headers are provided. (AWS behavior) 759 // DecryptObjectInfo returns 'ErrNone' if the object is not encrypted or the 760 // decryption succeeded. 761 // 762 // DecryptObjectInfo also returns whether the object is encrypted or not. 763 func DecryptObjectInfo(info *ObjectInfo, r *http.Request) (encrypted bool, err error) { 764 // Directories are never encrypted. 765 if info.IsDir { 766 return false, nil 767 } 768 if r == nil { 769 return false, errInvalidArgument 770 } 771 772 headers := r.Header 773 774 // disallow X-Amz-Server-Side-Encryption header on HEAD and GET 775 switch r.Method { 776 case http.MethodGet, http.MethodHead: 777 if crypto.S3.IsRequested(headers) { 778 return false, errInvalidEncryptionParameters 779 } 780 } 781 782 _, encrypted = crypto.IsEncrypted(info.UserDefined) 783 if !encrypted && crypto.SSEC.IsRequested(headers) && r.Header.Get(xhttp.AmzCopySource) == "" { 784 return false, errInvalidEncryptionParameters 785 } 786 787 if encrypted { 788 if crypto.SSEC.IsEncrypted(info.UserDefined) { 789 if !(crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers)) { 790 return encrypted, errEncryptedObject 791 } 792 } 793 794 if crypto.S3.IsEncrypted(info.UserDefined) && r.Header.Get(xhttp.AmzCopySource) == "" { 795 if crypto.SSEC.IsRequested(headers) || crypto.SSECopy.IsRequested(headers) { 796 return encrypted, errEncryptedObject 797 } 798 } 799 800 if _, err = info.DecryptedSize(); err != nil { 801 return encrypted, err 802 } 803 804 if _, ok := crypto.IsEncrypted(info.UserDefined); ok && !crypto.IsMultiPart(info.UserDefined) { 805 info.ETag = getDecryptedETag(headers, *info, false) 806 } 807 } 808 809 return encrypted, nil 810 } 811 812 // The customer key in the header is used by the gateway for encryption in the case of 813 // s3 gateway double encryption. A new client key is derived from the customer provided 814 // key to be sent to the s3 backend for encryption at the backend. 815 func deriveClientKey(clientKey [32]byte, bucket, object string) [32]byte { 816 var key [32]byte 817 mac := hmac.New(sha256.New, clientKey[:]) 818 mac.Write([]byte(crypto.SSEC.String())) 819 mac.Write([]byte(path.Join(bucket, object))) 820 mac.Sum(key[:0]) 821 return key 822 }