1// Copyright 2016-2017 VMware, Inc. All Rights Reserved. 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 15package extraconfig 16 17import ( 18 "crypto/rand" 19 "encoding/base64" 20 "errors" 21 "fmt" 22 23 log "github.com/sirupsen/logrus" 24 25 "golang.org/x/crypto/nacl/secretbox" 26) 27 28// The value of this key is hidden from API requests, but visible within the guest 29// #nosec: Potential hardcoded credentials 30const GuestInfoSecretKey = GuestInfoPrefix + "ovfEnv" 31 32// SecretKey provides helpers to encrypt/decrypt extraconfig values 33type SecretKey struct { 34 key [32]byte 35} 36 37// NewSecretKey generates a new secret key 38func NewSecretKey() (*SecretKey, error) { 39 s := new(SecretKey) 40 41 if _, err := rand.Read(s.key[:]); err != nil { 42 return nil, err 43 } 44 45 return s, nil 46} 47 48// FromString base64 decodes an existing SecretKey 49func (s *SecretKey) FromString(key string) error { 50 b, err := base64.StdEncoding.DecodeString(key) 51 if err != nil { 52 return err 53 } 54 55 if len(b) != 32 { 56 return errors.New("invalid secret key") 57 } 58 59 copy(s.key[:], b) 60 61 return nil 62} 63 64// String base64 encodes a SecretKey 65func (s *SecretKey) String() string { 66 return base64.StdEncoding.EncodeToString(s.key[:]) 67} 68 69// Source wraps the given DataSource, decrypting any secret values 70func (s *SecretKey) Source(ds DataSource) DataSource { 71 // If GuestInfoSecretKey has a value, it should be our secret key. 72 // #nosec: Errors unhandled. 73 if val, _ := ds(GuestInfoSecretKey); val != "" { 74 if err := s.FromString(val); err != nil { 75 log.Errorf("failed to decode %s: %s", GuestInfoSecretKey, err) 76 } else { 77 log.Debugf("secret key decoded from %s", GuestInfoSecretKey) 78 } 79 } 80 81 return func(key string) (string, error) { 82 val, err := ds(key) 83 84 if err == nil && isSecret(key) { 85 b, err := base64.StdEncoding.DecodeString(val) 86 if err != nil { 87 return "", err 88 } 89 90 var nonce [24]byte 91 copy(nonce[:], b[:24]) 92 93 plaintext, ok := secretbox.Open([]byte{}, b[24:], &nonce, &s.key) 94 if !ok { 95 return "", fmt.Errorf("failed to decrypt value for %s", key) 96 } 97 98 val = string(plaintext) 99 } 100 101 return val, err 102 } 103} 104 105// Sink wraps the given DataSink, encrypting any secret values 106func (s *SecretKey) Sink(ds DataSink) DataSink { 107 // Store our secret key. 108 if err := ds(GuestInfoSecretKey, s.String()); err != nil { 109 log.Errorf("failed to store %s: %s", GuestInfoSecretKey, err) 110 } 111 112 return func(key, value string) error { 113 if isSecret(key) { 114 var nonce [24]byte 115 116 if _, err := rand.Read(nonce[:]); err != nil { 117 return err 118 } 119 120 ciphertext := secretbox.Seal(nonce[:], []byte(value), &nonce, &s.key) 121 122 value = base64.StdEncoding.EncodeToString(ciphertext) 123 } 124 125 return ds(key, value) 126 } 127} 128