github.com/trustbloc/kms-go@v1.1.2/kms/localkms/localkms.go (about) 1 /* 2 Copyright SecureKey Technologies Inc. All Rights Reserved. 3 4 SPDX-License-Identifier: Apache-2.0 5 */ 6 7 package localkms 8 9 import ( 10 "bytes" 11 "crypto/ecdsa" 12 "crypto/ed25519" 13 "encoding/json" 14 "errors" 15 "fmt" 16 17 "github.com/google/tink/go/aead" 18 "github.com/google/tink/go/keyset" 19 "github.com/trustbloc/bbs-signature-go/bbs12381g2pub" 20 21 kmsapi "github.com/trustbloc/kms-go/spi/kms" 22 23 "github.com/trustbloc/kms-go/spi/secretlock" 24 25 cryptoapi "github.com/trustbloc/kms-go/spi/crypto" 26 27 "github.com/trustbloc/kms-go/doc/util/jwkkid" 28 "github.com/trustbloc/kms-go/kms" 29 "github.com/trustbloc/kms-go/kms/localkms/internal/keywrapper" 30 ) 31 32 const ( 33 // Namespace is the store name used when creating a KMS store using kms.NewAriesProviderWrapper. 34 // The reason this is here in addition to kms.AriesWrapperStoreName is because 35 // the IndexedDB implementation refers to this. FOr the WASM unit tests, the aries-framework-go module import gets 36 // replaced with the local version and so in order for both to work correctly, for now we have the constant defined 37 // in both places. 38 Namespace = kms.AriesWrapperStoreName 39 40 ecdsaPrivateKeyTypeURL = "type.googleapis.com/google.crypto.tink.EcdsaPrivateKey" 41 ) 42 43 var errInvalidKeyType = errors.New("key type is not supported") 44 45 // package localkms is the default KMS service implementation of pkg/kms.KeyManager. It uses Tink keys to support the 46 // default Crypto implementation, pkg/crypto/tinkcrypto, and stores these keys in the format understood by Tink. It also 47 // uses a secretLock service to protect private key material in the storage. 48 49 // LocalKMS implements kms.KeyManager to provide key management capabilities using a local db. 50 // It uses an underlying secret lock service (default local secretLock) to wrap (encrypt) keys 51 // prior to storing them. 52 type LocalKMS struct { 53 secretLock secretlock.Service 54 primaryKeyURI string 55 store kmsapi.Store 56 primaryKeyEnvAEAD *aead.KMSEnvelopeAEAD 57 } 58 59 // New will create a new (local) KMS service. 60 func New(primaryKeyURI string, p kmsapi.Provider) (*LocalKMS, error) { 61 secretLock := p.SecretLock() 62 63 kw, err := keywrapper.New(secretLock, primaryKeyURI) 64 if err != nil { 65 return nil, fmt.Errorf("new: failed to create new keywrapper: %w", err) 66 } 67 68 // create a KMSEnvelopeAEAD instance to wrap/unwrap keys managed by LocalKMS 69 keyEnvelopeAEAD := aead.NewKMSEnvelopeAEAD2(aead.AES256GCMKeyTemplate(), kw) 70 71 return &LocalKMS{ 72 store: p.StorageProvider(), 73 secretLock: secretLock, 74 primaryKeyURI: primaryKeyURI, 75 primaryKeyEnvAEAD: keyEnvelopeAEAD, 76 }, 77 nil 78 } 79 80 // HealthCheck check kms. 81 func (l *LocalKMS) HealthCheck() error { 82 return nil 83 } 84 85 // Create a new key/keyset/key handle for the type kt 86 // Returns: 87 // - keyID of the handle 88 // - handle instance (to private key) 89 // - error if failure 90 func (l *LocalKMS) Create(kt kmsapi.KeyType, opts ...kmsapi.KeyOpts) (string, interface{}, error) { 91 if kt == "" { 92 return "", nil, fmt.Errorf("failed to create new key, missing key type") 93 } 94 95 if kt == kmsapi.ECDSASecp256k1DER { 96 return "", nil, fmt.Errorf("create: Unable to create kms key: Secp256K1 is not supported by DER format") 97 } 98 99 keyTemplate, err := getKeyTemplate(kt, opts...) 100 if err != nil { 101 return "", nil, fmt.Errorf("create: failed to getKeyTemplate: %w", err) 102 } 103 104 kh, err := keyset.NewHandle(keyTemplate) 105 if err != nil { 106 return "", nil, fmt.Errorf("create: failed to create new keyset handle: %w", err) 107 } 108 109 keyID, err := l.storeKeySet(kh, kt) 110 if err != nil { 111 return "", nil, fmt.Errorf("create: failed to store keyset: %w", err) 112 } 113 114 return keyID, kh, nil 115 } 116 117 // Get key handle for the given keyID 118 // Returns: 119 // - handle instance (to private key) 120 // - error if failure 121 func (l *LocalKMS) Get(keyID string) (interface{}, error) { 122 return l.getKeySet(keyID) 123 } 124 125 // Rotate a key referenced by keyID and return a new handle of a keyset including old key and 126 // new key with type kt. It also returns the updated keyID as the first return value 127 // Returns: 128 // - new KeyID 129 // - handle instance (to private key) 130 // - error if failure 131 func (l *LocalKMS) Rotate(kt kmsapi.KeyType, keyID string, opts ...kmsapi.KeyOpts) (string, interface{}, error) { 132 kh, err := l.getKeySet(keyID) 133 if err != nil { 134 return "", nil, fmt.Errorf("rotate: failed to getKeySet: %w", err) 135 } 136 137 keyTemplate, err := getKeyTemplate(kt, opts...) 138 if err != nil { 139 return "", nil, fmt.Errorf("rotate: failed to get GetKeyTemplate: %w", err) 140 } 141 142 km := keyset.NewManagerFromHandle(kh) 143 144 err = km.Rotate(keyTemplate) 145 if err != nil { 146 return "", nil, fmt.Errorf("rotate: failed to call Tink's keyManager rotate: %w", err) 147 } 148 149 updatedKH, err := km.Handle() 150 if err != nil { 151 return "", nil, fmt.Errorf("rotate: failed to get kms keyest handle: %w", err) 152 } 153 154 err = l.store.Delete(keyID) 155 if err != nil { 156 return "", nil, fmt.Errorf("rotate: failed to delete entry for kid '%s': %w", keyID, err) 157 } 158 159 newID, err := l.storeKeySet(updatedKH, kt) 160 if err != nil { 161 return "", nil, fmt.Errorf("rotate: failed to store keySet: %w", err) 162 } 163 164 return newID, updatedKH, nil 165 } 166 167 func (l *LocalKMS) storeKeySet(kh *keyset.Handle, kt kmsapi.KeyType) (string, error) { 168 var ( 169 kid string 170 err error 171 ) 172 173 switch kt { 174 case kmsapi.AES128GCMType, kmsapi.AES256GCMType, kmsapi.AES256GCMNoPrefixType, kmsapi.ChaCha20Poly1305Type, 175 kmsapi.XChaCha20Poly1305Type, kmsapi.HMACSHA256Tag256Type, kmsapi.CLMasterSecretType: 176 // symmetric keys will have random kid value (generated in the local storeWriter) 177 case kmsapi.CLCredDefType: 178 // ignoring custom KID generation for the asymmetric CL CredDef 179 default: 180 // asymmetric keys will use the public key's JWK thumbprint base64URL encoded as kid value 181 kid, err = l.generateKID(kh, kt) 182 if err != nil && !errors.Is(err, errInvalidKeyType) { 183 return "", fmt.Errorf("storeKeySet: failed to generate kid: %w", err) 184 } 185 } 186 187 buf := new(bytes.Buffer) 188 jsonKeysetWriter := keyset.NewJSONWriter(buf) 189 190 err = kh.Write(jsonKeysetWriter, l.primaryKeyEnvAEAD) 191 if err != nil { 192 return "", fmt.Errorf("storeKeySet: failed to write json key to buffer: %w", err) 193 } 194 195 // asymmetric keys are JWK thumbprints of the public key, base64URL encoded stored in kid. 196 // symmetric keys will have a randomly generated key ID (where kid is empty) 197 if kid != "" { 198 return writeToStore(l.store, buf, kmsapi.WithKeyID(kid)) 199 } 200 201 return writeToStore(l.store, buf) 202 } 203 204 func writeToStore(store kmsapi.Store, buf *bytes.Buffer, opts ...kmsapi.PrivateKeyOpts) (string, error) { 205 w := newWriter(store, opts...) 206 207 // write buffer to localstorage 208 _, err := w.Write(buf.Bytes()) 209 if err != nil { 210 return "", fmt.Errorf("writeToStore: failed to write buffer to store: %w", err) 211 } 212 213 return w.KeysetID, nil 214 } 215 216 func (l *LocalKMS) getKeySet(id string) (*keyset.Handle, error) { 217 localDBReader := newReader(l.store, id) 218 219 jsonKeysetReader := keyset.NewJSONReader(localDBReader) 220 221 // Read reads the encrypted keyset handle back from the io.reader implementation 222 // and decrypts it using primaryKeyEnvAEAD. 223 kh, err := keyset.Read(jsonKeysetReader, l.primaryKeyEnvAEAD) 224 if err != nil { 225 return nil, fmt.Errorf("getKeySet: failed to read json keyset from reader: %w", err) 226 } 227 228 return kh, nil 229 } 230 231 // ExportPubKeyBytes will fetch a key referenced by id then gets its public key in raw bytes and returns it. 232 // The key must be an asymmetric key. 233 // Returns: 234 // - marshalled public key []byte 235 // - error if it fails to export the public key bytes 236 func (l *LocalKMS) ExportPubKeyBytes(id string) ([]byte, kmsapi.KeyType, error) { 237 kh, err := l.getKeySet(id) 238 if err != nil { 239 return nil, "", fmt.Errorf("exportPubKeyBytes: failed to get keyset handle: %w", err) 240 } 241 242 marshalledKey, kt, err := l.exportPubKeyBytes(kh) 243 if err != nil { 244 return nil, "", fmt.Errorf("exportPubKeyBytes: failed to export marshalled key: %w", err) 245 } 246 247 // Ignore KID for CL CredDef keys 248 if kt == kmsapi.CLCredDefType { 249 return marshalledKey, kt, nil 250 } 251 252 mUpdatedKey, err := setKIDForCompositeKey(marshalledKey, id) 253 254 return mUpdatedKey, kt, err 255 } 256 257 func setKIDForCompositeKey(marshalledKey []byte, kid string) ([]byte, error) { 258 pubKey := &cryptoapi.PublicKey{} 259 260 err := json.Unmarshal(marshalledKey, pubKey) 261 if err != nil { // if unmarshalling to VerificationMethod fails, it's not a composite key, return original bytes 262 return marshalledKey, nil //nolint:nilerr 263 } 264 265 pubKey.KID = kid 266 267 return json.Marshal(pubKey) 268 } 269 270 func (l *LocalKMS) exportPubKeyBytes(kh *keyset.Handle) ([]byte, kmsapi.KeyType, error) { 271 // kh must be a private asymmetric key in order to extract its public key 272 pubKH, err := kh.Public() 273 if err != nil { 274 return nil, "", fmt.Errorf("exportPubKeyBytes: failed to get public keyset handle: %w", err) 275 } 276 277 buf := new(bytes.Buffer) 278 pubKeyWriter := NewWriter(buf) 279 280 err = pubKH.WriteWithNoSecrets(pubKeyWriter) 281 if err != nil { 282 return nil, "", fmt.Errorf("exportPubKeyBytes: failed to create keyset with no secrets (public "+ 283 "key material): %w", err) 284 } 285 286 return buf.Bytes(), pubKeyWriter.KeyType, nil 287 } 288 289 // CreateAndExportPubKeyBytes will create a key of type kt and export its public key in raw bytes and returns it. 290 // The key must be an asymmetric key. 291 // Returns: 292 // - keyID of the new handle created. 293 // - marshalled public key []byte 294 // - error if it fails to export the public key bytes 295 func (l *LocalKMS) CreateAndExportPubKeyBytes(kt kmsapi.KeyType, opts ...kmsapi.KeyOpts) (string, []byte, error) { 296 kid, _, err := l.Create(kt, opts...) 297 if err != nil { 298 return "", nil, fmt.Errorf("createAndExportPubKeyBytes: failed to create new key: %w", err) 299 } 300 301 pubKeyBytes, _, err := l.ExportPubKeyBytes(kid) 302 if err != nil { 303 return "", nil, fmt.Errorf("createAndExportPubKeyBytes: failed to export new public key bytes: %w", err) 304 } 305 306 return kid, pubKeyBytes, nil 307 } 308 309 // PubKeyBytesToHandle will create and return a key handle for pubKey of type kt 310 // it returns an error if it failed creating the key handle 311 // Note: The key handle created is not stored in the KMS, it's only useful to execute the crypto primitive 312 // associated with it. 313 func (l *LocalKMS) PubKeyBytesToHandle(pubKey []byte, kt kmsapi.KeyType, opts ...kmsapi.KeyOpts) (interface{}, error) { 314 return PublicKeyBytesToHandle(pubKey, kt, opts...) 315 } 316 317 // ImportPrivateKey will import privKey into the KMS storage for the given keyType then returns the new key id and 318 // the newly persisted Handle. 319 // 'privKey' possible types are: *ecdsa.PrivateKey and ed25519.PrivateKey 320 // 'keyType' possible types are signing key types only (ECDSA keys or Ed25519) 321 // 'opts' allows setting the keysetID of the imported key using WithKeyID() option. If the ID is already used, 322 // then an error is returned. 323 // Returns: 324 // - keyID of the handle 325 // - handle instance (to private key) 326 // - error if import failure (key empty, invalid, doesn't match keyType, unsupported keyType or storing key failed) 327 func (l *LocalKMS) ImportPrivateKey(privKey interface{}, kt kmsapi.KeyType, 328 opts ...kmsapi.PrivateKeyOpts) (string, interface{}, error) { 329 switch pk := privKey.(type) { 330 case *ecdsa.PrivateKey: 331 return l.importECDSAKey(pk, kt, opts...) 332 case ed25519.PrivateKey: 333 return l.importEd25519Key(pk, kt, opts...) 334 case *bbs12381g2pub.PrivateKey: 335 return l.importBBSKey(pk, kt, opts...) 336 default: 337 return "", nil, fmt.Errorf("import private key does not support this key type or key is public") 338 } 339 } 340 341 func (l *LocalKMS) generateKID(kh *keyset.Handle, kt kmsapi.KeyType) (string, error) { 342 keyBytes, _, err := l.exportPubKeyBytes(kh) 343 if err != nil { 344 return "", fmt.Errorf("generateKID: failed to export public key: %w", err) 345 } 346 347 return jwkkid.CreateKID(keyBytes, kt) 348 }