xref: /aosp_15_r20/external/tink/go/integration/hcvault/hcvault_aead.go (revision e7b1675dde1b92d52ec075b0a92829627f2c52a5)
1*e7b1675dSTing-Kang Chang// Copyright 2019 Google Inc.
2*e7b1675dSTing-Kang Chang//
3*e7b1675dSTing-Kang Chang// Licensed under the Apache License, Version 2.0 (the "License");
4*e7b1675dSTing-Kang Chang// you may not use this file except in compliance with the License.
5*e7b1675dSTing-Kang Chang// You may obtain a copy of the License at
6*e7b1675dSTing-Kang Chang//
7*e7b1675dSTing-Kang Chang//      http://www.apache.org/licenses/LICENSE-2.0
8*e7b1675dSTing-Kang Chang//
9*e7b1675dSTing-Kang Chang// Unless required by applicable law or agreed to in writing, software
10*e7b1675dSTing-Kang Chang// distributed under the License is distributed on an "AS IS" BASIS,
11*e7b1675dSTing-Kang Chang// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12*e7b1675dSTing-Kang Chang// See the License for the specific language governing permissions and
13*e7b1675dSTing-Kang Chang// limitations under the License.
14*e7b1675dSTing-Kang Chang//
15*e7b1675dSTing-Kang Chang////////////////////////////////////////////////////////////////////////////////
16*e7b1675dSTing-Kang Chang
17*e7b1675dSTing-Kang Changpackage hcvault
18*e7b1675dSTing-Kang Chang
19*e7b1675dSTing-Kang Changimport (
20*e7b1675dSTing-Kang Chang	"encoding/base64"
21*e7b1675dSTing-Kang Chang	"errors"
22*e7b1675dSTing-Kang Chang	"net/url"
23*e7b1675dSTing-Kang Chang	"strings"
24*e7b1675dSTing-Kang Chang
25*e7b1675dSTing-Kang Chang	"github.com/google/tink/go/tink"
26*e7b1675dSTing-Kang Chang	"github.com/hashicorp/vault/api"
27*e7b1675dSTing-Kang Chang)
28*e7b1675dSTing-Kang Chang
29*e7b1675dSTing-Kang Chang// vaultAEAD represents a HashiCorp Vault service to a particular URI.
30*e7b1675dSTing-Kang Changtype vaultAEAD struct {
31*e7b1675dSTing-Kang Chang	encKeyPath string
32*e7b1675dSTing-Kang Chang	decKeyPath string
33*e7b1675dSTing-Kang Chang	client     *api.Logical
34*e7b1675dSTing-Kang Chang}
35*e7b1675dSTing-Kang Chang
36*e7b1675dSTing-Kang Changvar _ tink.AEAD = (*vaultAEAD)(nil)
37*e7b1675dSTing-Kang Chang
38*e7b1675dSTing-Kang Changconst (
39*e7b1675dSTing-Kang Chang	encryptSegment = "encrypt"
40*e7b1675dSTing-Kang Chang	decryptSegment = "decrypt"
41*e7b1675dSTing-Kang Chang)
42*e7b1675dSTing-Kang Chang
43*e7b1675dSTing-Kang Chang// newHCVaultAEAD returns a new HashiCorp Vault service.
44*e7b1675dSTing-Kang Changfunc newHCVaultAEAD(keyURI string, client *api.Logical) (tink.AEAD, error) {
45*e7b1675dSTing-Kang Chang	encKeyPath, decKeyPath, err := getEndpointPaths(keyURI)
46*e7b1675dSTing-Kang Chang	if err != nil {
47*e7b1675dSTing-Kang Chang		return nil, err
48*e7b1675dSTing-Kang Chang	}
49*e7b1675dSTing-Kang Chang	return &vaultAEAD{
50*e7b1675dSTing-Kang Chang		encKeyPath: encKeyPath,
51*e7b1675dSTing-Kang Chang		decKeyPath: decKeyPath,
52*e7b1675dSTing-Kang Chang		client:     client,
53*e7b1675dSTing-Kang Chang	}, nil
54*e7b1675dSTing-Kang Chang}
55*e7b1675dSTing-Kang Chang
56*e7b1675dSTing-Kang Chang// Encrypt encrypts the plaintext data using a key stored in HashiCorp Vault.
57*e7b1675dSTing-Kang Chang// associatedData parameter is used as a context for key derivation, more
58*e7b1675dSTing-Kang Chang// information available https://www.vaultproject.io/docs/secrets/transit/index.html.
59*e7b1675dSTing-Kang Changfunc (a *vaultAEAD) Encrypt(plaintext, associatedData []byte) ([]byte, error) {
60*e7b1675dSTing-Kang Chang	// Create an encryption request map according to Vault REST API:
61*e7b1675dSTing-Kang Chang	// https://www.vaultproject.io/api/secret/transit/index.html#encrypt-data.
62*e7b1675dSTing-Kang Chang	req := map[string]interface{}{
63*e7b1675dSTing-Kang Chang		"plaintext": base64.StdEncoding.EncodeToString(plaintext),
64*e7b1675dSTing-Kang Chang		"context":   base64.StdEncoding.EncodeToString(associatedData),
65*e7b1675dSTing-Kang Chang	}
66*e7b1675dSTing-Kang Chang	secret, err := a.client.Write(a.encKeyPath, req)
67*e7b1675dSTing-Kang Chang	if err != nil {
68*e7b1675dSTing-Kang Chang		return nil, err
69*e7b1675dSTing-Kang Chang	}
70*e7b1675dSTing-Kang Chang	ciphertext := secret.Data["ciphertext"].(string)
71*e7b1675dSTing-Kang Chang	return []byte(ciphertext), nil
72*e7b1675dSTing-Kang Chang}
73*e7b1675dSTing-Kang Chang
74*e7b1675dSTing-Kang Chang// Decrypt decrypts the ciphertext using a key stored in HashiCorp Vault.
75*e7b1675dSTing-Kang Chang// associatedData parameter is used as a context for key derivation, more
76*e7b1675dSTing-Kang Chang// information available https://www.vaultproject.io/docs/secrets/transit/index.html.
77*e7b1675dSTing-Kang Changfunc (a *vaultAEAD) Decrypt(ciphertext, associatedData []byte) ([]byte, error) {
78*e7b1675dSTing-Kang Chang	// Create a decryption request map according to Vault REST API:
79*e7b1675dSTing-Kang Chang	// https://www.vaultproject.io/api/secret/transit/index.html#decrypt-data.
80*e7b1675dSTing-Kang Chang	req := map[string]interface{}{
81*e7b1675dSTing-Kang Chang		"ciphertext": string(ciphertext),
82*e7b1675dSTing-Kang Chang		"context":    base64.StdEncoding.EncodeToString(associatedData),
83*e7b1675dSTing-Kang Chang	}
84*e7b1675dSTing-Kang Chang	secret, err := a.client.Write(a.decKeyPath, req)
85*e7b1675dSTing-Kang Chang	if err != nil {
86*e7b1675dSTing-Kang Chang		return nil, err
87*e7b1675dSTing-Kang Chang	}
88*e7b1675dSTing-Kang Chang	plaintext64 := secret.Data["plaintext"].(string)
89*e7b1675dSTing-Kang Chang	plaintext, err := base64.StdEncoding.DecodeString(plaintext64)
90*e7b1675dSTing-Kang Chang	if err != nil {
91*e7b1675dSTing-Kang Chang		return nil, err
92*e7b1675dSTing-Kang Chang	}
93*e7b1675dSTing-Kang Chang	return plaintext, nil
94*e7b1675dSTing-Kang Chang}
95*e7b1675dSTing-Kang Chang
96*e7b1675dSTing-Kang Chang// getEndpointPaths transforms keyURL into the Vault transit encrypt and decrypt
97*e7b1675dSTing-Kang Chang// paths. The keyURL is expected to end in "/{mount}/keys/{keyName}". For
98*e7b1675dSTing-Kang Chang// example, the keyURL "hcvault:///transit/keys/key-foo" will be transformed to
99*e7b1675dSTing-Kang Chang// "transit/encrypt/key-foo" and "transit/decrypt/key-foo", and
100*e7b1675dSTing-Kang Chang// "hcvault://my-vault.example.com/teams/billing/service/cipher/keys/key-bar"
101*e7b1675dSTing-Kang Chang// will be transformed into
102*e7b1675dSTing-Kang Chang// "hcvault://my-vault.example.com/teams/billing/service/cipher/encrypt/key-bar"
103*e7b1675dSTing-Kang Chang// and
104*e7b1675dSTing-Kang Chang// "hcvault://my-vault.example.com/teams/billing/service/cipher/decrypt/key-bar".
105*e7b1675dSTing-Kang Changfunc getEndpointPaths(keyURL string) (encryptPath, decryptPath string, err error) {
106*e7b1675dSTing-Kang Chang	u, err := url.Parse(keyURL)
107*e7b1675dSTing-Kang Chang	if err != nil || u.Scheme != "hcvault" {
108*e7b1675dSTing-Kang Chang		return "", "", errors.New("malformed keyURL")
109*e7b1675dSTing-Kang Chang	}
110*e7b1675dSTing-Kang Chang
111*e7b1675dSTing-Kang Chang	parts := strings.Split(u.EscapedPath(), "/")
112*e7b1675dSTing-Kang Chang	length := len(parts)
113*e7b1675dSTing-Kang Chang	if length < 4 || parts[length-2] != "keys" {
114*e7b1675dSTing-Kang Chang		return "", "", errors.New("malformed keyURL")
115*e7b1675dSTing-Kang Chang	}
116*e7b1675dSTing-Kang Chang
117*e7b1675dSTing-Kang Chang	parts[length-2] = encryptSegment
118*e7b1675dSTing-Kang Chang	encryptPath = strings.Join(parts[1:], "/")
119*e7b1675dSTing-Kang Chang	parts[length-2] = decryptSegment
120*e7b1675dSTing-Kang Chang	decryptPath = strings.Join(parts[1:], "/")
121*e7b1675dSTing-Kang Chang	return encryptPath, decryptPath, nil
122*e7b1675dSTing-Kang Chang}
123