in pkg/berglas/read.go [170:282]
func (c *Client) storageRead(ctx context.Context, i *StorageReadRequest) (*Secret, error) {
bucket := i.Bucket
if bucket == "" {
return nil, fmt.Errorf("missing bucket name")
}
object := i.Object
if object == "" {
return nil, fmt.Errorf("missing object name")
}
generation := i.Generation
if generation == 0 {
generation = -1
}
logger := logging.FromContext(ctx).With(
"bucket", bucket,
"object", object,
"generation", generation,
)
logger.DebugContext(ctx, "read.start")
defer logger.DebugContext(ctx, "read.finish")
// Get attributes to find the KMS key
logger.DebugContext(ctx, "reading attributes from storage")
attrs, err := c.storageClient.
Bucket(bucket).
Object(object).
Generation(generation).
Attrs(ctx)
if errors.Is(err, storage.ErrObjectNotExist) {
return nil, errSecretDoesNotExist
}
if err != nil {
return nil, fmt.Errorf("failed to read secret metadata: %w", err)
}
if attrs.Metadata == nil || attrs.Metadata[MetadataKMSKey] == "" {
return nil, fmt.Errorf("missing kms key in secret metadata")
}
key := attrs.Metadata[MetadataKMSKey]
logger = logger.With("key", key)
logger.DebugContext(ctx, "found kms key")
// Download the file from GCS
logger.DebugContext(ctx, "downloading file from storage")
ior, err := c.storageClient.
Bucket(bucket).
Object(object).
Generation(generation).
NewReader(ctx)
if errors.Is(err, storage.ErrObjectNotExist) {
return nil, fmt.Errorf("secret object not found")
}
if err != nil {
return nil, fmt.Errorf("failed to read secret: %w", err)
}
// Read the entire response into memory
logger.DebugContext(ctx, "reading object into memory")
data, err := io.ReadAll(ior)
if err != nil {
return nil, fmt.Errorf("failed to read secret into string: %w", err)
}
if err := ior.Close(); err != nil {
return nil, fmt.Errorf("failed to close reader: %w", err)
}
// Split into parts
logger.DebugContext(ctx, "deconstructing and decoding ciphertext into parts")
parts := strings.SplitN(string(data), ":", 2)
if len(parts) < 2 {
return nil, fmt.Errorf("invalid ciphertext: not enough parts")
}
encDEK, err := base64.StdEncoding.DecodeString(parts[0])
if err != nil {
return nil, fmt.Errorf("invalid ciphertext: failed to parse dek")
}
ciphertext, err := base64.StdEncoding.DecodeString(parts[1])
if err != nil {
return nil, fmt.Errorf("invalid ciphertext: failed to parse ciphertext")
}
// Decrypt the DEK using a KMS key
logger.DebugContext(ctx, "decrypting dek using kms")
kmsResp, err := c.kmsClient.Decrypt(ctx, &kmspb.DecryptRequest{
Name: key,
Ciphertext: encDEK,
AdditionalAuthenticatedData: []byte(object),
})
if err != nil {
return nil, fmt.Errorf("failed to decrypt dek: %w", err)
}
dek := kmsResp.Plaintext
// Decrypt with the local key
logger.DebugContext(ctx, "decrypting data with deck locally")
plaintext, err := envelopeDecrypt(dek, ciphertext)
if err != nil {
return nil, fmt.Errorf("failed to decrypt envelope: %w", err)
}
return secretFromAttrs(bucket, attrs, plaintext), nil
}