github.com/google/trillian-examples@v0.0.0-20240520080811-0d40d35cef0e/internal/note/note_verifier.go (about)

     1  // Copyright 2021 Google LLC. 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  
    15  // Package note provides note-compatible signature verifiers.
    16  package note
    17  
    18  import (
    19  	"crypto/ecdsa"
    20  	"crypto/sha256"
    21  	"crypto/x509"
    22  	"encoding/base64"
    23  	"encoding/binary"
    24  	"fmt"
    25  	"strconv"
    26  	"strings"
    27  
    28  	sdb_note "golang.org/x/mod/sumdb/note"
    29  )
    30  
    31  const (
    32  	// Note represents a key type that the Go SumDB note will
    33  	// know about.
    34  	Note = ""
    35  
    36  	// ECDSA is an ECDSA signature over SHA256.
    37  	// This signature type has been agreed to be represented by algo ID 2 by the note authors.
    38  	ECDSA = "ecdsa"
    39  
    40  	algECDSAWithSHA256 = 2
    41  )
    42  
    43  // NewVerifier returns a verifier for the given key type and key.
    44  func NewVerifier(keyType, key string) (sdb_note.Verifier, error) {
    45  	switch keyType {
    46  	case ECDSA:
    47  		return NewECDSAVerifier(key)
    48  	case Note:
    49  		return sdb_note.NewVerifier(key)
    50  	default:
    51  		return nil, fmt.Errorf("unknown key type %q", keyType)
    52  	}
    53  }
    54  
    55  // verifier is a note-compatible verifier.
    56  type verifier struct {
    57  	name    string
    58  	keyHash uint32
    59  	v       func(msg, sig []byte) bool
    60  }
    61  
    62  // Name returns the name associated with the key this verifier is based on.
    63  func (v *verifier) Name() string {
    64  	return v.name
    65  }
    66  
    67  // KeyHash returns a truncated hash of the key this verifier is based on.
    68  func (v *verifier) KeyHash() uint32 {
    69  	return v.keyHash
    70  }
    71  
    72  // Verify checks that the provided sig is valid over msg for the key this verifier is based on.
    73  func (v *verifier) Verify(msg, sig []byte) bool {
    74  	return v.v(msg, sig)
    75  }
    76  
    77  // NewECDSAVerifier creates a new note verifier for checking ECDSA signatures over SHA256 digests.
    78  // This implementation is compatible with the signature scheme used by the Sigstore Rékor Log.
    79  //
    80  // The key is expected to be provided as a string in the following form:
    81  //
    82  //	<key_name>+<key_hash>+<key_bytes>
    83  //
    84  // Where
    85  //
    86  //	<key_name> is a human readable identifier for the key, containing no whitespace or "+" symbols
    87  //	<key_bytes> is base64 encoded blob starting with a 0x02 (algECDSAWithSHA256) byte and followed
    88  //	    by the DER encoded public key in SPKI format.
    89  //	<key_hash> is a 32bit hash of the key DER
    90  //
    91  // e.g.:
    92  //
    93  //	"rekor.sigstore.dev+12345678+AjBZMBMGByqGSM49AgEGCCqGSM49AwEHA0IABNhtmPtrWm3U1eQXBogSMdGvXwBcK5AW5i0hrZLOC96l+smGNM7nwZ4QvFK/4sueRoVj//QP22Ni4Qt9DPfkWLc=
    94  func NewECDSAVerifier(key string) (sdb_note.Verifier, error) {
    95  	parts := strings.SplitN(key, "+", 3)
    96  	if got, want := len(parts), 3; got != want {
    97  		return nil, fmt.Errorf("key has %d parts, expected %d: %q", got, want, key)
    98  	}
    99  	keyBytes, err := base64.StdEncoding.DecodeString(parts[2])
   100  	if err != nil {
   101  		return nil, fmt.Errorf("key has invalid base64 %q: %v", parts[2], err)
   102  	}
   103  	if len(keyBytes) < 2 {
   104  		return nil, fmt.Errorf("invalid key, key bytes too short")
   105  	}
   106  	if keyBytes[0] != algECDSAWithSHA256 {
   107  		return nil, fmt.Errorf("key has incorrect type %d", keyBytes[0])
   108  	}
   109  	der := keyBytes[1:]
   110  	kh := keyHash(der)
   111  
   112  	khProvided, err := strconv.ParseUint(parts[1], 16, 32)
   113  	if err != nil {
   114  		return nil, fmt.Errorf("invalid key, couldn't parse keyhash: %v", err)
   115  	}
   116  	if uint32(khProvided) != kh {
   117  		return nil, fmt.Errorf("invalid keyhash %x, expected %x", khProvided, kh)
   118  	}
   119  
   120  	k, err := x509.ParsePKIXPublicKey(der)
   121  	if err != nil {
   122  		return nil, fmt.Errorf("couldn't parse public key: %v", err)
   123  	}
   124  	ecdsaKey, ok := k.(*ecdsa.PublicKey)
   125  	if !ok {
   126  		return nil, fmt.Errorf("key is a %T, expected an ECDSA key", k)
   127  	}
   128  
   129  	return &verifier{
   130  		name: parts[0],
   131  		v: func(msg, sig []byte) bool {
   132  			dgst := sha256.Sum256(msg)
   133  			return ecdsa.VerifyASN1(ecdsaKey, dgst[:], sig)
   134  		},
   135  		keyHash: kh,
   136  	}, nil
   137  }
   138  
   139  func keyHash(i []byte) uint32 {
   140  	h := sha256.Sum256(i)
   141  	return binary.BigEndian.Uint32(h[:])
   142  }