Skip to content

Instantly share code, notes, and snippets.

@DazWilkin
Created May 31, 2019 20:59
Show Gist options
  • Select an option

  • Save DazWilkin/a9f253fe1121ad78bd5283557bc0437d to your computer and use it in GitHub Desktop.

Select an option

Save DazWilkin/a9f253fe1121ad78bd5283557bc0437d to your computer and use it in GitHub Desktop.
Google Cloud KMS & Tink
package main
import (
"context"
"crypto/ecdsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"errors"
"flag"
"fmt"
"log"
"math/big"
"encoding/base64"
cloudkms "cloud.google.com/go/kms/apiv1"
kmspb "google.golang.org/genproto/googleapis/cloud/kms/v1"
)
var (
project = flag.String("project", "", "Project ID")
location = flag.String("location", "", "Keyring Location")
keyring = flag.String("keyring", "", "Keyring ID")
key = flag.String("key", "", "Key ID")
msg = flag.String("message", "", "Message")
)
// Google-provided sample functions
// https://cloud.google.com/kms/docs/create-validate-signatures#create_signature
func signAsymmetric(keyName string, message []byte) ([]byte, error) {
ctx := context.Background()
client, err := cloudkms.NewKeyManagementClient(ctx)
if err != nil {
return nil, err
}
digest := sha256.New()
digest.Write(message)
req := &kmspb.AsymmetricSignRequest{
Name: keyName,
Digest: &kmspb.Digest{
Digest: &kmspb.Digest_Sha256{
Sha256: digest.Sum(nil),
},
},
}
// Call the API.
rsp, err := client.AsymmetricSign(ctx, req)
if err != nil {
return nil, fmt.Errorf("asymmetric sign request failed: %+v", err)
}
return rsp.Signature, nil
}
func verifySignatureEC(keyName string, signature, message []byte) error {
ctx := context.Background()
client, err := cloudkms.NewKeyManagementClient(ctx)
if err != nil {
return err
}
// Retrieve the public key from KMS.
response, err := client.GetPublicKey(ctx, &kmspb.GetPublicKeyRequest{Name: keyName})
if err != nil {
return fmt.Errorf("failed to fetch public key: %+v", err)
}
// Parse the key.
block, _ := pem.Decode([]byte(response.Pem))
abstractKey, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return fmt.Errorf("failed to parse public key: %+v", err)
}
ecKey, ok := abstractKey.(*ecdsa.PublicKey)
if !ok {
return fmt.Errorf("key '%s' is not EC", keyName)
}
// Verify Elliptic Curve signature.
var parsedSig struct{ R, S *big.Int }
_, err = asn1.Unmarshal(signature, &parsedSig)
if err != nil {
return fmt.Errorf("failed to parse signature bytes: %+v", err)
}
hash := sha256.New()
hash.Write(message)
digest := hash.Sum(nil)
if !ecdsa.Verify(ecKey, digest, parsedSig.R, parsedSig.S) {
return errors.New("signature verification failed")
}
return nil
}
func main() {
flag.Parse()
log.Printf("Message:\n%s\n", *msg)
keyName := fmt.Sprintf("projects/%s/locations/%s/keyRings/%s/cryptoKeys/%s/cryptoKeyVersions/1", *project, *location, *keyring, *key)
message := []byte(*msg)
// Owner Signs
signature, err := signAsymmetric(keyName, message)
if err != nil {
log.Fatal(err)
}
encodedSig := base64.StdEncoding.EncodeToString(signature)
log.Printf("Encoded:\n%s\n", encodedSig)
// Prover Verifies
if err := verifySignatureEC(keyName, signature, message); err != nil {
log.Fatal(err)
}
log.Println("Message verified.")
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment