sks_windows.go (103 lines of code) (raw):

// Package sks implements the Secure Key Store for Go // Copyright (c) Facebook, Inc. and its affiliates. // // Licensed under the Apache License, Version 2.0 (the "License"); // you may not use this file except in compliance with the License. // You may obtain a copy of the License at // // http://www.apache.org/licenses/LICENSE-2.0 // // Unless required by applicable law or agreed to in writing, software // distributed under the License is distributed on an "AS IS" BASIS, // WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. // See the License for the specific language governing permissions and // limitations under the License. package sks import ( "crypto/ecdsa" "crypto/elliptic" "encoding/asn1" "fmt" "math/big" "strings" "github.com/facebookincubator/sks/utils" tpm "github.com/aimeemikaelac/certtostore" ) const ( keyStorageProvider = "Microsoft Platform Crypto Provider" // keyDoesNotExistErr is the error returned by Microsoft Crypto Provider // when the requested key was not found in the provider. Taken from // ncrypt.h. errKeyDoesNotExist = "80090016" ) // genKeyPair creates a key with the given label and tag // Returns public key raw data. // tag, useBiometrics, and accessibleWhenUnlockedOnly are ignored func genKeyPair(label, tag string, _, _ bool) ([]byte, error) { certStore, err := tpm.OpenWinCertStore( keyStorageProvider, label, []string{}, []string{}, ) if err != nil { return nil, fmt.Errorf(ErrGenKeyPair, label, tag, err) } key, err := certStore.Generate(256, "ECDSA_P256") if err != nil { return nil, fmt.Errorf(ErrGenKeyPair, label, tag, err) } pubKey := key.Public().(*ecdsa.PublicKey) return elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y), nil } // signWithKey signs arbitrary data pointed to by data with the key described by // label and tag. Returns the signed data. // tag and key hash are not used. func signWithKey(label, tag string, _, digest []byte) ([]byte, error) { key, err := findPrivateKey(label) if err != nil { return nil, fmt.Errorf(ErrSignWithKey, label, tag, err) } if key == nil { return nil, fmt.Errorf("failed to find key with label %q and tag %q", label, tag) } key = key.(*tpm.EcdsaKey) sig, err := key.SignRaw(digest) if err != nil { return nil, fmt.Errorf(ErrSignWithKey, label, tag, err) } // https://stackoverflow.com/questions/38702169/c-sharp-ecdsacng-signdata-use-signature-in-openssl // windows encodes an ecdsa signature as concatenating r and s in the array. // the output sig will always be of even length r := new(big.Int).SetBytes(sig[0 : len(sig)/2]) s := new(big.Int).SetBytes(sig[len(sig)/2:]) // https://golang.org/src/crypto/ecdsa/ecdsa.go?s=2196:2295#L65 sig, err = asn1.Marshal(utils.ECCSignature{r, s}) if err != nil { return nil, fmt.Errorf(ErrSignWithKey, label, tag, err) } return sig, nil } // findPubKey returns the raw public key described by label and tag // tag and hash are not used func findPubKey(label, tag string, hash []byte) ([]byte, error) { key, err := findPrivateKey(label) if err != nil { return nil, fmt.Errorf(ErrFindPubKey, label, tag, err) } // The lib API expects that we return a nil error here with a nil key // to signify that the key was not found. if key == nil { return nil, nil } pubKey := key.Public().(*ecdsa.PublicKey) return elliptic.Marshal(pubKey.Curve, pubKey.X, pubKey.Y), nil } // removeKey tries to delete a key identified by label, tag and hash. // tag and hash are not used // Returns true if the key was found and deleted successfully func removeKey(label, tag string, _ []byte) (bool, error) { key, err := findPrivateKey(label) if err != nil { return false, fmt.Errorf(ErrRemoveKey, label, tag, err) } if key == nil { return false, fmt.Errorf("failed to find key with label %q and tag %q", label, tag) } err = key.Delete() if err != nil { return false, fmt.Errorf(ErrRemoveKey, label, tag, err) } return true, nil } func findPrivateKey(label string) (tpm.Key, error) { certStore, err := tpm.OpenWinCertStore( keyStorageProvider, label, []string{}, []string{}, ) if err != nil { return nil, err } key, err := certStore.Key() if err != nil { // Windows will return an error with the specified content if the key was not // found. if strings.Contains(err.Error(), errKeyDoesNotExist) { return nil, nil } return nil, err } return key, nil } func accessibleWhenUnlockedOnly(label, tag string, hash []byte) (bool, error) { return false, nil } func updateKeyLabel(label, tag, newLabel string, hash []byte) error { return fmt.Errorf(ErrNotImplemented, "updateKeyLabel") }