Skip to content

Commit ac937aa

Browse files
committed
Merge pull request google#886 from AlCutter/verify_sct
Add signature verification for SCTs
2 parents 429eaca + a942709 commit ac937aa

File tree

5 files changed

+472
-7
lines changed

5 files changed

+472
-7
lines changed

go/serialization.go

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -321,9 +321,7 @@ func serializeV1PrecertSCTSignatureInput(timestamp uint64, issuerKeyHash [issuer
321321
return buf.Bytes(), nil
322322
}
323323

324-
// SerializeV1SCTSignatureInput serializes the passed in sct and log entry into
325-
// the correct format for signing.
326-
func SerializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
324+
func serializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
327325
if sct.SCTVersion != V1 {
328326
return nil, fmt.Errorf("unsupported SCT version, expected V1, but got %s", sct.SCTVersion)
329327
}
@@ -341,3 +339,14 @@ func SerializeV1SCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry
341339
return nil, fmt.Errorf("unknown TimestampedEntryLeafType %s", entry.Leaf.TimestampedEntry.EntryType)
342340
}
343341
}
342+
343+
// SerializeSCTSignatureInput serializes the passed in sct and log entry into
344+
// the correct format for signing.
345+
func SerializeSCTSignatureInput(sct SignedCertificateTimestamp, entry LogEntry) ([]byte, error) {
346+
switch sct.SCTVersion {
347+
case V1:
348+
return serializeV1SCTSignatureInput(sct, entry)
349+
default:
350+
return nil, fmt.Errorf("unknown SCT version %d", sct.SCTVersion)
351+
}
352+
}

go/serialization_test.go

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -293,7 +293,7 @@ func DefaultPrecertLogEntry() LogEntry {
293293
}
294294

295295
func TestSerializeV1SCTSignatureInputForCertificateKAT(t *testing.T) {
296-
serialized, err := SerializeV1SCTSignatureInput(DefaultSCT(), DefaultCertificateLogEntry())
296+
serialized, err := SerializeSCTSignatureInput(DefaultSCT(), DefaultCertificateLogEntry())
297297
if err != nil {
298298
t.Fatalf("Failed to serialize SCT for signing: %v", err)
299299
}
@@ -303,7 +303,7 @@ func TestSerializeV1SCTSignatureInputForCertificateKAT(t *testing.T) {
303303
}
304304

305305
func TestSerializeV1SCTSignatureInputForPrecertKAT(t *testing.T) {
306-
serialized, err := SerializeV1SCTSignatureInput(DefaultSCT(), DefaultPrecertLogEntry())
306+
serialized, err := SerializeSCTSignatureInput(DefaultSCT(), DefaultPrecertLogEntry())
307307
if err != nil {
308308
t.Fatalf("Failed to serialize SCT for signing: %v", err)
309309
}

go/signatures.go

Lines changed: 120 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,120 @@
1+
package ct
2+
3+
import (
4+
"crypto"
5+
"crypto/ecdsa"
6+
"crypto/elliptic"
7+
"crypto/rsa"
8+
"crypto/x509"
9+
"encoding/asn1"
10+
"encoding/pem"
11+
"errors"
12+
"flag"
13+
"fmt"
14+
"log"
15+
"math/big"
16+
)
17+
18+
var allowVerificationWithNonCompliantKeys = flag.Bool("allow_verification_with_non_compliant_keys", false,
19+
"Allow a SignatureVerifier to use keys which are technically non-compliant with RFC6962.")
20+
21+
// PublicKeyFromPEM parses a PEM formatted block and returns the public key contained within.
22+
func PublicKeyFromPEM(b []byte) (crypto.PublicKey, error) {
23+
p, _ := pem.Decode(b)
24+
if p == nil {
25+
return nil, fmt.Errorf("no PEM block found in %s", string(b))
26+
}
27+
return x509.ParsePKIXPublicKey(p.Bytes)
28+
}
29+
30+
// SignatureVerifier can verify signatures on SCTs and STHs
31+
type SignatureVerifier struct {
32+
pubKey crypto.PublicKey
33+
}
34+
35+
// NewSignatureVerifier creates a new SignatureVerifier using the passed in PublicKey.
36+
func NewSignatureVerifier(pk crypto.PublicKey) (*SignatureVerifier, error) {
37+
switch pkType := pk.(type) {
38+
case *rsa.PublicKey:
39+
if pkType.N.BitLen() < 2048 {
40+
e := fmt.Errorf("public key is RSA with < 2048 bits (size:%d)", pkType.N.BitLen())
41+
if !(*allowVerificationWithNonCompliantKeys) {
42+
return nil, e
43+
}
44+
log.Printf("WARNING: %v", e)
45+
}
46+
case *ecdsa.PublicKey:
47+
params := *(pkType.Params())
48+
if params != *elliptic.P256().Params() {
49+
e := fmt.Errorf("public is ECDSA, but not on the P256 curve")
50+
if !(*allowVerificationWithNonCompliantKeys) {
51+
return nil, e
52+
}
53+
log.Printf("WARNING: %v", e)
54+
55+
}
56+
default:
57+
return nil, fmt.Errorf("Unsupported public key type %v", pkType)
58+
}
59+
60+
return &SignatureVerifier{
61+
pubKey: pk,
62+
}, nil
63+
}
64+
65+
// verifySignature verifies that the passed in signature over data was created by our PublicKey.
66+
// Currently, only SHA256 is supported as a HashAlgorithm, and only ECDSA and RSA signatures are supported.
67+
func (s SignatureVerifier) verifySignature(data []byte, sig DigitallySigned) error {
68+
if sig.HashAlgorithm != SHA256 {
69+
return fmt.Errorf("unsupported HashAlgorithm in signature: %v", sig.HashAlgorithm)
70+
}
71+
72+
hasherType := crypto.SHA256
73+
hasher := hasherType.New()
74+
if _, err := hasher.Write(data); err != nil {
75+
return fmt.Errorf("failed to write to hasher: %v", err)
76+
}
77+
hash := hasher.Sum([]byte{})
78+
79+
switch sig.SignatureAlgorithm {
80+
case RSA:
81+
rsaKey, ok := s.pubKey.(*rsa.PublicKey)
82+
if !ok {
83+
return fmt.Errorf("cannot verify RSA signature with %T key", s.pubKey)
84+
}
85+
if err := rsa.VerifyPKCS1v15(rsaKey, hasherType, hash, sig.Signature); err != nil {
86+
return fmt.Errorf("failed to verify rsa signature: %v", err)
87+
}
88+
case ECDSA:
89+
ecdsaKey, ok := s.pubKey.(*ecdsa.PublicKey)
90+
if !ok {
91+
return fmt.Errorf("cannot verify ECDSA signature with %T key", s.pubKey)
92+
}
93+
var ecdsaSig struct {
94+
R, S *big.Int
95+
}
96+
rest, err := asn1.Unmarshal(sig.Signature, &ecdsaSig)
97+
if err != nil {
98+
return fmt.Errorf("failed to unmarshal ECDSA signature: %v", err)
99+
}
100+
if len(rest) != 0 {
101+
log.Printf("Garbage following signature %v", rest)
102+
}
103+
104+
if !ecdsa.Verify(ecdsaKey, hash, ecdsaSig.R, ecdsaSig.S) {
105+
return errors.New("failed to verify ecdsa signature")
106+
}
107+
default:
108+
return fmt.Errorf("unsupported signature type %v", sig.SignatureAlgorithm)
109+
}
110+
return nil
111+
}
112+
113+
// VerifySCTSignature verifies that the SCT's signature is valid for the given LogEntry
114+
func (s SignatureVerifier) VerifySCTSignature(sct SignedCertificateTimestamp, entry LogEntry) error {
115+
sctData, err := SerializeSCTSignatureInput(sct, entry)
116+
if err != nil {
117+
return err
118+
}
119+
return s.verifySignature(sctData, sct.Signature)
120+
}

0 commit comments

Comments
 (0)