| //go:build go1.18 |
| // +build go1.18 |
| |
| package cose_test |
| |
| import ( |
| "bytes" |
| "crypto" |
| "crypto/ecdsa" |
| "crypto/ed25519" |
| "crypto/elliptic" |
| "crypto/rand" |
| "crypto/rsa" |
| "encoding/json" |
| "os" |
| "path/filepath" |
| "strings" |
| "testing" |
| |
| _ "google3/go/tools/nogo/allowlist/crypto/elliptic" |
| |
| "google3/third_party/golang/github_com/fxamacker/cbor/v/v2/cbor" |
| "google3/third_party/golang/github_com/veraison/go_cose/v/v0/cose" |
| ) |
| |
| func FuzzSign1Message_UnmarshalCBOR(f *testing.F) { |
| testdata, err := os.ReadDir("testdata") |
| if err != nil { |
| f.Fatalf("failed to read testdata directory: %s", err) |
| } |
| for _, de := range testdata { |
| if de.IsDir() || !strings.HasPrefix(de.Name(), "sign1-") || !strings.HasSuffix(de.Name(), ".json") { |
| continue |
| } |
| b, err := os.ReadFile(filepath.Join("testdata", de.Name())) |
| if err != nil { |
| f.Fatalf("failed to read testdata: %s", err) |
| } |
| type testCase struct { |
| Sign1 *Sign1 `json:"sign1::sign"` |
| Verify1 *Verify1 `json:"sign1::verify"` |
| } |
| var tc testCase |
| err = json.Unmarshal(b, &tc) |
| if err != nil { |
| f.Fatal(err) |
| } |
| if tc.Sign1 != nil { |
| f.Add(mustHexToBytes(tc.Sign1.Output.CBORHex)) |
| } else if tc.Verify1 != nil { |
| f.Add(mustHexToBytes(tc.Verify1.TaggedCOSESign1.CBORHex)) |
| } |
| } |
| enc, _ := cbor.CanonicalEncOptions().EncMode() |
| dec, _ := cbor.DecOptions{IntDec: cbor.IntDecConvertSigned}.DecMode() |
| isCanonical := func(b []byte) bool { |
| var tmp interface{} |
| err := dec.Unmarshal(b, &tmp) |
| if err != nil { |
| return false |
| } |
| b1, err := enc.Marshal(tmp) |
| return bytes.Equal(b, b1) |
| } |
| f.Fuzz(func(t *testing.T, b []byte) { |
| var msg cose.Sign1Message |
| if err := msg.UnmarshalCBOR(b); err != nil { |
| return |
| } |
| got, err := msg.MarshalCBOR() |
| if err != nil { |
| t.Fatalf("failed to marshal valid message: %s", err) |
| } |
| if !isCanonical(b) { |
| return |
| } |
| if len(b) > len(got) { |
| b = b[:len(got)] |
| } |
| if !bytes.Equal(b, got) { |
| t.Fatalf("roundtripped message has changed, got: %v, want: %v", got, b) |
| } |
| }) |
| } |
| |
| func FuzzSign1(f *testing.F) { |
| testdata, err := os.ReadDir("testdata") |
| if err != nil { |
| f.Fatalf("failed to read testdata directory: %s", err) |
| } |
| for _, de := range testdata { |
| if de.IsDir() || !strings.HasPrefix(de.Name(), "sign1-sign") || !strings.HasSuffix(de.Name(), ".json") { |
| continue |
| } |
| b, err := os.ReadFile(filepath.Join("testdata", de.Name())) |
| if err != nil { |
| f.Fatalf("failed to read testdata: %s", err) |
| } |
| type testCase struct { |
| Sign1 *Sign1 `json:"sign1::sign"` |
| } |
| var tc testCase |
| err = json.Unmarshal(b, &tc) |
| if err != nil { |
| f.Fatal(err) |
| } |
| if tc.Sign1 != nil { |
| hdr, _ := encMode.Marshal(mustHexToBytes(tc.Sign1.ProtectedHeaders.CBORHex)) |
| f.Add(hdr, mustHexToBytes(tc.Sign1.Payload), mustHexToBytes(tc.Sign1.External)) |
| } |
| } |
| f.Fuzz(func(t *testing.T, hdr_data, payload, external []byte) { |
| hdr := make(cose.ProtectedHeader) |
| err := hdr.UnmarshalCBOR(hdr_data) |
| if err != nil { |
| return |
| } |
| alg, err := hdr.Algorithm() |
| if err != nil { |
| return |
| } |
| pkey, err := newSignerWithEphemeralKey(alg) |
| if err != nil { |
| return |
| } |
| signer, err := cose.NewSigner(alg, pkey) |
| if err != nil { |
| t.Fatal(err) |
| } |
| verifier, err := cose.NewVerifier(alg, pkey.Public()) |
| if err != nil { |
| t.Fatal(err) |
| } |
| msg, err := cose.Sign1(rand.Reader, signer, hdr, payload, external) |
| if err != nil { |
| t.Fatal(err) |
| } |
| err = cose.Verify1(msg, external, verifier) |
| if err != nil { |
| t.Fatal(err) |
| } |
| err = cose.Verify1(msg, append(external, []byte{0}...), verifier) |
| if err == nil { |
| t.Fatal("verification error expected") |
| } |
| }) |
| } |
| |
| func newSignerWithEphemeralKey(alg cose.Algorithm) (crypto.Signer, error) { |
| var key crypto.Signer |
| var err error |
| switch alg { |
| case cose.AlgorithmPS256: |
| key, err = rsa.GenerateKey(rand.Reader, 2048) |
| case cose.AlgorithmPS384: |
| key, err = rsa.GenerateKey(rand.Reader, 3072) |
| case cose.AlgorithmPS512: |
| key, err = rsa.GenerateKey(rand.Reader, 4096) |
| case cose.AlgorithmES256: |
| key, err = ecdsa.GenerateKey(elliptic.P256(), rand.Reader) |
| case cose.AlgorithmES384: |
| key, err = ecdsa.GenerateKey(elliptic.P384(), rand.Reader) |
| case cose.AlgorithmES512: |
| key, err = ecdsa.GenerateKey(elliptic.P521(), rand.Reader) |
| case cose.AlgorithmEd25519: |
| _, key, err = ed25519.GenerateKey(rand.Reader) |
| default: |
| return nil, cose.ErrAlgorithmNotSupported |
| } |
| if err != nil { |
| return nil, err |
| } |
| return key, nil |
| } |