storj.io/minio@v0.0.0-20230509071714-0cbc90f649b1/cmd/config/crypto.go (about) 1 // MinIO Cloud Storage, (C) 2021 MinIO, Inc. 2 // 3 // Licensed under the Apache License, Version 2.0 (the "License"); 4 // you may not use this file except in compliance with the License. 5 // You may obtain a copy of the License at 6 // 7 // http://www.apache.org/licenses/LICENSE-2.0 8 // 9 // Unless required by applicable law or agreed to in writing, software 10 // distributed under the License is distributed on an "AS IS" BASIS, 11 // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 12 // See the License for the specific language governing permissions and 13 // limitations under the License. 14 15 package config 16 17 import ( 18 "bytes" 19 "crypto/rand" 20 "encoding/binary" 21 "encoding/json" 22 "errors" 23 "fmt" 24 "io" 25 26 "github.com/secure-io/sio-go" 27 "github.com/secure-io/sio-go/sioutil" 28 29 "storj.io/minio/pkg/kms" 30 ) 31 32 // EncryptBytes encrypts the plaintext with a key managed by KMS. 33 // The context is bound to the returned ciphertext. 34 // 35 // The same context must be provided when decrypting the 36 // ciphertext. 37 func EncryptBytes(KMS kms.KMS, plaintext []byte, context kms.Context) ([]byte, error) { 38 ciphertext, err := Encrypt(KMS, bytes.NewReader(plaintext), context) 39 if err != nil { 40 return nil, err 41 } 42 return io.ReadAll(ciphertext) 43 } 44 45 // DecryptBytes decrypts the ciphertext using a key managed by the KMS. 46 // The same context that have been used during encryption must be 47 // provided. 48 func DecryptBytes(KMS kms.KMS, ciphertext []byte, context kms.Context) ([]byte, error) { 49 plaintext, err := Decrypt(KMS, bytes.NewReader(ciphertext), context) 50 if err != nil { 51 return nil, err 52 } 53 return io.ReadAll(plaintext) 54 } 55 56 // Encrypt encrypts the plaintext with a key managed by KMS. 57 // The context is bound to the returned ciphertext. 58 // 59 // The same context must be provided when decrypting the 60 // ciphertext. 61 func Encrypt(KMS kms.KMS, plaintext io.Reader, context kms.Context) (io.Reader, error) { 62 var algorithm = sio.AES_256_GCM 63 if !sioutil.NativeAES() { 64 algorithm = sio.ChaCha20Poly1305 65 } 66 67 key, err := KMS.GenerateKey("", context) 68 if err != nil { 69 return nil, err 70 } 71 stream, err := algorithm.Stream(key.Plaintext) 72 if err != nil { 73 return nil, err 74 } 75 nonce := make([]byte, stream.NonceSize()) 76 if _, err := rand.Read(nonce); err != nil { 77 return nil, err 78 } 79 80 const ( 81 MaxMetadataSize = 1 << 20 // max. size of the metadata 82 Version = 1 83 ) 84 var ( 85 header [5]byte 86 buffer bytes.Buffer 87 ) 88 metadata, err := json.Marshal(encryptedObject{ 89 KeyID: key.KeyID, 90 KMSKey: key.Ciphertext, 91 Algorithm: algorithm, 92 Nonce: nonce, 93 }) 94 if err != nil { 95 return nil, err 96 } 97 if len(metadata) > MaxMetadataSize { 98 return nil, errors.New("config: encryption metadata is too large") 99 } 100 header[0] = Version 101 binary.LittleEndian.PutUint32(header[1:], uint32(len(metadata))) 102 buffer.Write(header[:]) 103 buffer.Write(metadata) 104 105 return io.MultiReader( 106 &buffer, 107 stream.EncryptReader(plaintext, nonce, nil), 108 ), nil 109 } 110 111 // Decrypt decrypts the ciphertext using a key managed by the KMS. 112 // The same context that have been used during encryption must be 113 // provided. 114 func Decrypt(KMS kms.KMS, ciphertext io.Reader, context kms.Context) (io.Reader, error) { 115 const ( 116 MaxMetadataSize = 1 << 20 // max. size of the metadata 117 Version = 1 118 ) 119 120 var header [5]byte 121 if _, err := io.ReadFull(ciphertext, header[:]); err != nil { 122 return nil, err 123 } 124 if header[0] != Version { 125 return nil, fmt.Errorf("config: unknown ciphertext version %d", header[0]) 126 } 127 size := binary.LittleEndian.Uint32(header[1:]) 128 if size > MaxMetadataSize { 129 return nil, errors.New("config: encryption metadata is too large") 130 } 131 132 var ( 133 metadataBuffer = make([]byte, size) 134 metadata encryptedObject 135 ) 136 if _, err := io.ReadFull(ciphertext, metadataBuffer); err != nil { 137 return nil, err 138 } 139 if err := json.Unmarshal(metadataBuffer, &metadata); err != nil { 140 return nil, err 141 } 142 143 key, err := KMS.DecryptKey(metadata.KeyID, metadata.KMSKey, context) 144 if err != nil { 145 return nil, err 146 } 147 stream, err := metadata.Algorithm.Stream(key) 148 if err != nil { 149 return nil, err 150 } 151 if stream.NonceSize() != len(metadata.Nonce) { 152 return nil, sio.NotAuthentic 153 } 154 return stream.DecryptReader(ciphertext, metadata.Nonce, nil), nil 155 } 156 157 type encryptedObject struct { 158 KeyID string `json:"keyid"` 159 KMSKey []byte `json:"kmskey"` 160 161 Algorithm sio.Algorithm `json:"algorithm"` 162 Nonce []byte `json:"nonce"` 163 }