func()

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
}