1// Copyright 2019 Google LLC 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//////////////////////////////////////////////////////////////////////////////// 16 17package aead 18 19import ( 20 "encoding/binary" 21 "errors" 22 "fmt" 23 24 "github.com/google/tink/go/core/registry" 25 "github.com/google/tink/go/tink" 26 tinkpb "github.com/google/tink/go/proto/tink_go_proto" 27) 28 29const ( 30 lenDEK = 4 31 maxUint32Size = 4294967295 32) 33 34// KMSEnvelopeAEAD represents an instance of Envelope AEAD. 35type KMSEnvelopeAEAD struct { 36 dekTemplate *tinkpb.KeyTemplate 37 remote tink.AEAD 38 // if err != nil, then the primitive will always fail with this error. 39 // this is needed because NewKMSEnvelopeAEAD2 doesn't return an error. 40 err error 41} 42 43var tinkAEADKeyTypes map[string]bool = map[string]bool{ 44 aesCTRHMACAEADTypeURL: true, 45 aesGCMTypeURL: true, 46 chaCha20Poly1305TypeURL: true, 47 xChaCha20Poly1305TypeURL: true, 48 aesGCMSIVTypeURL: true, 49} 50 51func isSupporedKMSEnvelopeDEK(dekKeyTypeURL string) bool { 52 _, found := tinkAEADKeyTypes[dekKeyTypeURL] 53 return found 54} 55 56// NewKMSEnvelopeAEAD2 creates an new instance of KMSEnvelopeAEAD. 57// 58// dekTemplate must be a KeyTemplate for any of these Tink AEAD key types (any 59// other key template will be rejected): 60// - AesCtrHmacAeadKey 61// - AesGcmKey 62// - ChaCha20Poly1305Key 63// - XChaCha20Poly1305 64// - AesGcmSivKey 65func NewKMSEnvelopeAEAD2(dekTemplate *tinkpb.KeyTemplate, remote tink.AEAD) *KMSEnvelopeAEAD { 66 if !isSupporedKMSEnvelopeDEK(dekTemplate.GetTypeUrl()) { 67 return &KMSEnvelopeAEAD{ 68 remote: nil, 69 dekTemplate: nil, 70 err: fmt.Errorf("unsupported DEK key type %s", dekTemplate.GetTypeUrl()), 71 } 72 } 73 return &KMSEnvelopeAEAD{ 74 remote: remote, 75 dekTemplate: dekTemplate, 76 err: nil, 77 } 78} 79 80// Encrypt implements the tink.AEAD interface for encryption. 81func (a *KMSEnvelopeAEAD) Encrypt(pt, aad []byte) ([]byte, error) { 82 if a.err != nil { 83 return nil, a.err 84 } 85 dekKeyData, err := registry.NewKeyData(a.dekTemplate) 86 if err != nil { 87 return nil, err 88 } 89 dek := dekKeyData.GetValue() 90 encryptedDEK, err := a.remote.Encrypt(dek, []byte{}) 91 if err != nil { 92 return nil, err 93 } 94 p, err := registry.Primitive(a.dekTemplate.TypeUrl, dek) 95 if err != nil { 96 return nil, err 97 } 98 primitive, ok := p.(tink.AEAD) 99 if !ok { 100 return nil, errors.New("kms_envelope_aead: failed to convert AEAD primitive") 101 } 102 103 payload, err := primitive.Encrypt(pt, aad) 104 if err != nil { 105 return nil, err 106 } 107 if len(encryptedDEK) > maxUint32Size { 108 return nil, errors.New("kms_envelope_aead: encrypted dek too large") 109 } 110 res := make([]byte, 0, lenDEK+len(encryptedDEK)+len(payload)) 111 res = binary.BigEndian.AppendUint32(res, uint32(len(encryptedDEK))) 112 res = append(res, encryptedDEK...) 113 res = append(res, payload...) 114 return res, nil 115} 116 117// Decrypt implements the tink.AEAD interface for decryption. 118func (a *KMSEnvelopeAEAD) Decrypt(ct, aad []byte) ([]byte, error) { 119 if a.err != nil { 120 return nil, a.err 121 } 122 // Verify we have enough bytes for the length of the encrypted DEK. 123 if len(ct) <= lenDEK { 124 return nil, errors.New("kms_envelope_aead: invalid ciphertext") 125 } 126 127 // Extract length of encrypted DEK and advance past that length. 128 ed := int(binary.BigEndian.Uint32(ct[:lenDEK])) 129 ct = ct[lenDEK:] 130 131 // Verify we have enough bytes for the encrypted DEK. 132 if ed <= 0 || len(ct) < ed { 133 return nil, errors.New("kms_envelope_aead: invalid ciphertext") 134 } 135 136 // Extract the encrypted DEK and the payload. 137 encryptedDEK := ct[:ed] 138 payload := ct[ed:] 139 ct = nil 140 141 // Decrypt the DEK. 142 dek, err := a.remote.Decrypt(encryptedDEK, []byte{}) 143 if err != nil { 144 return nil, err 145 } 146 147 // Get an AEAD primitive corresponding to the DEK. 148 p, err := registry.Primitive(a.dekTemplate.TypeUrl, dek) 149 if err != nil { 150 return nil, fmt.Errorf("kms_envelope_aead: %s", err) 151 } 152 primitive, ok := p.(tink.AEAD) 153 if !ok { 154 return nil, errors.New("kms_envelope_aead: failed to convert AEAD primitive") 155 } 156 157 // Decrypt the payload. 158 return primitive.Decrypt(payload, aad) 159} 160