tink-envelope-encryption-sample/encrypted_keyset_cli.go (103 lines of code) (raw):
// Copyright 2024 Google LLC
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// http://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS-IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.
package main
import (
"context"
"errors"
"flag"
"log"
"os"
"github.com/google/tink/go/aead"
"github.com/google/tink/go/core/registry"
"github.com/google/tink/go/integration/gcpkms"
"github.com/google/tink/go/keyset"
"github.com/google/tink/go/tink"
"google.golang.org/api/option"
)
var (
mode = flag.String("mode", "", "The operation to perform: generate, encrypt, or decrypt")
keysetPath = flag.String("keyset_path", "", "Path to the keyset used for encryption")
kekURI = flag.String("kek_uri", "", "The Cloud KMS URI of the key encryption key")
gcpCredential = flag.String("gcp_credential_path", "", "Path to the GCP credentials JSON file")
inputPath = flag.String("input_path", "", "Path to the input file")
outputPath = flag.String("output_path", "", "Path to the output file")
associatedData = flag.String("associated_data", "", "Optional associated data to use with the encryption operation")
)
func main() {
flag.Parse()
// load master key
ctx := context.Background()
gcpClient, err := gcpkms.NewClientWithOptions(ctx, *kekURI, option.WithCredentialsFile(*gcpCredential))
if err != nil {
log.Fatal(err)
}
registry.RegisterKMSClient(gcpClient)
masterKey, err := gcpClient.GetAEAD(*kekURI)
if err != nil {
log.Fatal(err)
}
switch *mode {
case "generate":
if err := generateKeyset(*outputPath, masterKey); err != nil {
log.Fatalf("Error generating keyset: %v", err)
}
case "encrypt", "decrypt":
if err := processFile(*mode, *outputPath, *inputPath, *keysetPath, *associatedData, masterKey); err != nil {
log.Fatalf("Error: %v", err)
}
default:
log.Fatalf("Unsupported mode %s. Please choose 'generate', 'encrypt', or 'decrypt'", *mode)
}
}
func generateKeyset(outputPath string, masterKey tink.AEAD) error {
var err error
// create output file
_, err = os.Stat(outputPath)
if err == nil {
log.Fatal(errors.New("output file must not exist"))
}
f, err := os.Create(outputPath)
if err != nil {
log.Fatal(err)
}
defer f.Close()
// generate a new key
keyTemplate := aead.AES256GCMKeyTemplate()
handle, err := keyset.NewHandle(keyTemplate)
// write the new key
keyWriter := keyset.NewJSONWriter(f)
if err := handle.Write(keyWriter, masterKey); err != nil {
log.Fatal(err)
}
return nil
}
func processFile(mode string, outputPath string, inputPath string, keysetPath string, associatedData string, masterKey tink.AEAD) error {
// Read the encrypted keyset
keysetFile, err := os.Open(keysetPath)
if err != nil {
log.Fatal("error opening keyset file: %v", err)
}
defer keysetFile.Close()
keysetHandle, err := keyset.Read(keyset.NewJSONReader(keysetFile), masterKey)
if err != nil {
log.Fatal("error reading encrypted keyset: %v", err)
}
// Get the primitive
cipher, err := aead.New(keysetHandle)
if err != nil {
log.Fatal("error getting primitive: %v", err)
}
// Read the input file
inputData, err := os.ReadFile(inputPath)
if err != nil {
log.Fatal("error reading input file: %v", err)
}
var outputData []byte
if mode == "encrypt" {
outputData, err = cipher.Encrypt(inputData, []byte(associatedData))
if err != nil {
log.Fatal("error encrypting data: %v", err)
}
}
if mode == "decrypt" {
outputData, err = cipher.Decrypt(inputData, []byte(associatedData))
if err != nil {
log.Fatal("error decrypting data: %v", err)
}
}
// Write the output file
if err := os.WriteFile(outputPath, outputData, 0644); err != nil {
log.Fatal("error writing output file: %v", err)
}
return nil
}