1// Copyright 2019 Google Inc. 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 17// Package hcvault provides integration with the [HashiCorp Vault]. 18// 19// [HashiCorp Vault]: https://www.vaultproject.io/. 20package hcvault 21 22import ( 23 "crypto/tls" 24 "errors" 25 "fmt" 26 "net/http" 27 "net/url" 28 "strings" 29 30 "github.com/google/tink/go/core/registry" 31 "github.com/google/tink/go/tink" 32 "github.com/hashicorp/vault/api" 33) 34 35const ( 36 vaultPrefix = "hcvault://" 37) 38 39// vaultClient represents a client that connects to the HashiCorp Vault backend. 40type vaultClient struct { 41 keyURIPrefix string 42 client *api.Logical 43} 44 45var _ registry.KMSClient = (*vaultClient)(nil) 46 47// NewClient returns a new client to HashiCorp Vault. 48// uriPrefix parameter is a valid URI which must have "hcvault" scheme and 49// vault server address and port. Specific key URIs will be matched against this 50// prefix to determine if the client supports the key or not. 51// tlsCfg represents tls.Config which will be used to communicate with Vault 52// server via HTTPS protocol. If not specified a default tls.Config{} will be 53// used. 54func NewClient(uriPrefix string, tlsCfg *tls.Config, token string) (registry.KMSClient, error) { 55 if !strings.HasPrefix(strings.ToLower(uriPrefix), vaultPrefix) { 56 return nil, fmt.Errorf("key URI must start with %s", vaultPrefix) 57 } 58 59 httpClient := api.DefaultConfig().HttpClient 60 transport := httpClient.Transport.(*http.Transport) 61 if tlsCfg == nil { 62 tlsCfg = &tls.Config{} 63 } else { 64 tlsCfg = tlsCfg.Clone() 65 } 66 transport.TLSClientConfig = tlsCfg 67 68 vurl, err := url.Parse(uriPrefix) 69 if err != nil { 70 return nil, err 71 } 72 73 cfg := &api.Config{ 74 Address: "https://" + vurl.Host, 75 HttpClient: httpClient, 76 } 77 78 client, err := api.NewClient(cfg) 79 if err != nil { 80 return nil, err 81 } 82 83 client.SetToken(token) 84 return &vaultClient{ 85 keyURIPrefix: uriPrefix, 86 client: client.Logical(), 87 }, nil 88 89} 90 91// Supported returns true if this client does support keyURI. 92func (c *vaultClient) Supported(keyURI string) bool { 93 return strings.HasPrefix(keyURI, c.keyURIPrefix) 94} 95 96// GetAEAD gets an AEAD backend by keyURI. 97func (c *vaultClient) GetAEAD(keyURI string) (tink.AEAD, error) { 98 if !c.Supported(keyURI) { 99 return nil, errors.New("unsupported keyURI") 100 } 101 102 return newHCVaultAEAD(keyURI, c.client) 103} 104