1// Copyright 2017 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 fakekms provides a fake implementation of registry.KMSClient. 18// 19// Normally, a 'keyURI' identifies a key that is stored remotely by the KMS, 20// and every operation is executed remotely using a RPC call to the KMS, since 21// the key should not be sent to the client. 22// In this fake implementation we want to avoid these RPC calls. We achieve this 23// by encoding the key in the 'keyURI'. So the client simply needs to decode 24// the key and generate an AEAD out of it. This is of course insecure and should 25// only be used in testing. 26package fakekms 27 28import ( 29 "bytes" 30 "encoding/base64" 31 "fmt" 32 "strings" 33 34 "github.com/google/tink/go/aead" 35 "github.com/google/tink/go/core/registry" 36 "github.com/google/tink/go/keyset" 37 "github.com/google/tink/go/testkeyset" 38 "github.com/google/tink/go/tink" 39) 40 41const fakePrefix = "fake-kms://" 42 43var _ registry.KMSClient = (*fakeClient)(nil) 44 45type fakeClient struct { 46 uriPrefix string 47} 48 49// NewClient returns a fake KMS client which will handle keys with uriPrefix prefix. 50// keyURI must have the following format: 'fake-kms://<base64 encoded aead keyset>'. 51func NewClient(uriPrefix string) (registry.KMSClient, error) { 52 if !strings.HasPrefix(strings.ToLower(uriPrefix), fakePrefix) { 53 return nil, fmt.Errorf("uriPrefix must start with %s, but got %s", fakePrefix, uriPrefix) 54 } 55 return &fakeClient{ 56 uriPrefix: uriPrefix, 57 }, nil 58} 59 60// Supported returns true if this client does support keyURI. 61func (c *fakeClient) Supported(keyURI string) bool { 62 return strings.HasPrefix(keyURI, c.uriPrefix) 63} 64 65// GetAEAD returns an AEAD by keyURI. 66func (c *fakeClient) GetAEAD(keyURI string) (tink.AEAD, error) { 67 if !c.Supported(keyURI) { 68 return nil, fmt.Errorf("keyURI must start with prefix %s, but got %s", c.uriPrefix, keyURI) 69 } 70 encodeKeyset := strings.TrimPrefix(keyURI, fakePrefix) 71 keysetData, err := base64.RawURLEncoding.DecodeString(encodeKeyset) 72 if err != nil { 73 return nil, err 74 } 75 reader := keyset.NewBinaryReader(bytes.NewReader(keysetData)) 76 handle, err := testkeyset.Read(reader) 77 if err != nil { 78 return nil, err 79 } 80 return aead.New(handle) 81} 82 83// NewKeyURI returns a new, random fake KMS key URI. 84func NewKeyURI() (string, error) { 85 handle, err := keyset.NewHandle(aead.AES128GCMKeyTemplate()) 86 if err != nil { 87 return "", err 88 } 89 buf := new(bytes.Buffer) 90 writer := keyset.NewBinaryWriter(buf) 91 err = testkeyset.Write(handle, writer) 92 if err != nil { 93 return "", err 94 } 95 return fakePrefix + base64.RawURLEncoding.EncodeToString(buf.Bytes()), nil 96} 97