1// Copyright 2020 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 streamingaead_test 18 19// [START streaming-aead-example] 20 21import ( 22 "bytes" 23 "fmt" 24 "io" 25 "log" 26 "os" 27 "path/filepath" 28 29 "github.com/google/tink/go/insecurecleartextkeyset" 30 "github.com/google/tink/go/keyset" 31 "github.com/google/tink/go/streamingaead" 32) 33 34func Example() { 35 // A keyset created with "tinkey create-keyset --key-template=AES256_CTR_HMAC_SHA256_1MB". Note 36 // that this keyset has the secret key information in cleartext. 37 jsonKeyset := `{ 38 "primaryKeyId": 1720777699, 39 "key": [{ 40 "keyData": { 41 "typeUrl": "type.googleapis.com/google.crypto.tink.AesCtrHmacStreamingKey", 42 "keyMaterialType": "SYMMETRIC", 43 "value": "Eg0IgCAQIBgDIgQIAxAgGiDtesd/4gCnQdTrh+AXodwpm2b6BFJkp043n+8mqx0YGw==" 44 }, 45 "outputPrefixType": "RAW", 46 "keyId": 1720777699, 47 "status": "ENABLED" 48 }] 49 }` 50 51 // Create a keyset handle from the cleartext keyset in the previous 52 // step. The keyset handle provides abstract access to the underlying keyset to 53 // limit the exposure of accessing the raw key material. WARNING: In practice, 54 // it is unlikely you will want to use an insecurecleartextkeyset, as it implies 55 // that your key material is passed in cleartext, which is a security risk. 56 // Consider encrypting it with a remote key in Cloud KMS, AWS KMS or HashiCorp Vault. 57 // See https://github.com/google/tink/blob/master/docs/GOLANG-HOWTO.md#storing-and-loading-existing-keysets. 58 keysetHandle, err := insecurecleartextkeyset.Read( 59 keyset.NewJSONReader(bytes.NewBufferString(jsonKeyset))) 60 if err != nil { 61 log.Fatal(err) 62 } 63 64 // Retrieve the StreamingAEAD primitive we want to use from the keyset handle. 65 primitive, err := streamingaead.New(keysetHandle) 66 if err != nil { 67 log.Fatal(err) 68 } 69 70 // Create a file with the plaintext. 71 dir, err := os.MkdirTemp("", "streamingaead") 72 if err != nil { 73 log.Fatal(err) 74 } 75 defer os.RemoveAll(dir) 76 plaintextPath := filepath.Join(dir, "plaintext") 77 if err := os.WriteFile(plaintextPath, []byte("this data needs to be encrypted"), 0666); err != nil { 78 log.Fatal(err) 79 } 80 plaintextFile, err := os.Open(plaintextPath) 81 if err != nil { 82 log.Fatal(err) 83 } 84 85 // associatedData defines the context of the encryption. Here, we include the path of the 86 // plaintext file. 87 associatedData := []byte("associatedData for " + plaintextPath) 88 89 // Encrypt the plaintext file and write the output to the ciphertext file. In this case the 90 // primary key of the keyset will be used (which is also the only key in this example). 91 ciphertextPath := filepath.Join(dir, "ciphertext") 92 ciphertextFile, err := os.Create(ciphertextPath) 93 if err != nil { 94 log.Fatal(err) 95 } 96 w, err := primitive.NewEncryptingWriter(ciphertextFile, associatedData) 97 if err != nil { 98 log.Fatal(err) 99 } 100 if _, err := io.Copy(w, plaintextFile); err != nil { 101 log.Fatal(err) 102 } 103 if err := w.Close(); err != nil { 104 log.Fatal(err) 105 } 106 if err := ciphertextFile.Close(); err != nil { 107 log.Fatal(err) 108 } 109 if err := plaintextFile.Close(); err != nil { 110 log.Fatal(err) 111 } 112 113 // Decrypt the ciphertext file and write the output to the decrypted file. The 114 // decryption finds the correct key in the keyset and decrypts the ciphertext. 115 // If no key is found or decryption fails, it returns an error. 116 ciphertextFile, err = os.Open(ciphertextPath) 117 if err != nil { 118 log.Fatal(err) 119 } 120 decryptedPath := filepath.Join(dir, "decrypted") 121 decryptedFile, err := os.Create(decryptedPath) 122 if err != nil { 123 log.Fatal(err) 124 } 125 r, err := primitive.NewDecryptingReader(ciphertextFile, associatedData) 126 if err != nil { 127 log.Fatal(err) 128 } 129 if _, err := io.Copy(decryptedFile, r); err != nil { 130 log.Fatal(err) 131 } 132 if err := decryptedFile.Close(); err != nil { 133 log.Fatal(err) 134 } 135 if err := ciphertextFile.Close(); err != nil { 136 log.Fatal(err) 137 } 138 139 // Print the content of the decrypted file. 140 b, err := os.ReadFile(decryptedPath) 141 if err != nil { 142 log.Fatal(err) 143 } 144 fmt.Println(string(b)) 145 // Output: this data needs to be encrypted 146} 147 148// [END streaming-aead-example] 149