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