• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1// Copyright 2022 The Go Authors. All rights reserved.
2// Use of this source code is governed by a BSD-style
3// license that can be found in the LICENSE file.
4
5//go:build boringcrypto
6
7package x509
8
9import (
10	"crypto/ecdsa"
11	"crypto/elliptic"
12	"crypto/internal/boring/fipstls"
13	"crypto/rand"
14	"crypto/rsa"
15	"crypto/x509/pkix"
16	"fmt"
17	"math/big"
18	"strings"
19	"testing"
20	"time"
21)
22
23const (
24	boringCertCA = iota
25	boringCertLeaf
26	boringCertFIPSOK = 0x80
27)
28
29func boringRSAKey(t *testing.T, size int) *rsa.PrivateKey {
30	k, err := rsa.GenerateKey(rand.Reader, size)
31	if err != nil {
32		t.Fatal(err)
33	}
34	return k
35}
36
37func boringECDSAKey(t *testing.T, curve elliptic.Curve) *ecdsa.PrivateKey {
38	k, err := ecdsa.GenerateKey(curve, rand.Reader)
39	if err != nil {
40		t.Fatal(err)
41	}
42	return k
43}
44
45type boringCertificate struct {
46	name      string
47	org       string
48	parentOrg string
49	der       []byte
50	cert      *Certificate
51	key       interface{}
52	fipsOK    bool
53}
54
55func TestBoringAllowCert(t *testing.T) {
56	R1 := testBoringCert(t, "R1", boringRSAKey(t, 2048), nil, boringCertCA|boringCertFIPSOK)
57	R2 := testBoringCert(t, "R2", boringRSAKey(t, 512), nil, boringCertCA)
58	R3 := testBoringCert(t, "R3", boringRSAKey(t, 4096), nil, boringCertCA|boringCertFIPSOK)
59
60	M1_R1 := testBoringCert(t, "M1_R1", boringECDSAKey(t, elliptic.P256()), R1, boringCertCA|boringCertFIPSOK)
61	M2_R1 := testBoringCert(t, "M2_R1", boringECDSAKey(t, elliptic.P224()), R1, boringCertCA)
62
63	I_R1 := testBoringCert(t, "I_R1", boringRSAKey(t, 3072), R1, boringCertCA|boringCertFIPSOK)
64	testBoringCert(t, "I_R2", I_R1.key, R2, boringCertCA|boringCertFIPSOK)
65	testBoringCert(t, "I_M1", I_R1.key, M1_R1, boringCertCA|boringCertFIPSOK)
66	testBoringCert(t, "I_M2", I_R1.key, M2_R1, boringCertCA|boringCertFIPSOK)
67
68	I_R3 := testBoringCert(t, "I_R3", boringRSAKey(t, 3072), R3, boringCertCA|boringCertFIPSOK)
69	testBoringCert(t, "I_R3", I_R3.key, R3, boringCertCA|boringCertFIPSOK)
70
71	testBoringCert(t, "L1_I", boringECDSAKey(t, elliptic.P384()), I_R1, boringCertLeaf|boringCertFIPSOK)
72	testBoringCert(t, "L2_I", boringRSAKey(t, 1024), I_R1, boringCertLeaf)
73}
74
75func testBoringCert(t *testing.T, name string, key interface{}, parent *boringCertificate, mode int) *boringCertificate {
76	org := name
77	parentOrg := ""
78	if i := strings.Index(org, "_"); i >= 0 {
79		org = org[:i]
80		parentOrg = name[i+1:]
81	}
82	tmpl := &Certificate{
83		SerialNumber: big.NewInt(1),
84		Subject: pkix.Name{
85			Organization: []string{org},
86		},
87		NotBefore: time.Unix(0, 0),
88		NotAfter:  time.Unix(0, 0),
89
90		KeyUsage:              KeyUsageKeyEncipherment | KeyUsageDigitalSignature,
91		ExtKeyUsage:           []ExtKeyUsage{ExtKeyUsageServerAuth, ExtKeyUsageClientAuth},
92		BasicConstraintsValid: true,
93	}
94	if mode&^boringCertFIPSOK == boringCertLeaf {
95		tmpl.DNSNames = []string{"example.com"}
96	} else {
97		tmpl.IsCA = true
98		tmpl.KeyUsage |= KeyUsageCertSign
99	}
100
101	var pcert *Certificate
102	var pkey interface{}
103	if parent != nil {
104		pcert = parent.cert
105		pkey = parent.key
106	} else {
107		pcert = tmpl
108		pkey = key
109	}
110
111	var pub interface{}
112	var desc string
113	switch k := key.(type) {
114	case *rsa.PrivateKey:
115		pub = &k.PublicKey
116		desc = fmt.Sprintf("RSA-%d", k.N.BitLen())
117	case *ecdsa.PrivateKey:
118		pub = &k.PublicKey
119		desc = "ECDSA-" + k.Curve.Params().Name
120	default:
121		t.Fatalf("invalid key %T", key)
122	}
123
124	der, err := CreateCertificate(rand.Reader, tmpl, pcert, pub, pkey)
125	if err != nil {
126		t.Fatal(err)
127	}
128	cert, err := ParseCertificate(der)
129	if err != nil {
130		t.Fatal(err)
131	}
132
133	// Tell isBoringCertificate to enforce FIPS restrictions for this check.
134	fipstls.Force()
135	defer fipstls.Abandon()
136
137	fipsOK := mode&boringCertFIPSOK != 0
138	if boringAllowCert(cert) != fipsOK {
139		t.Errorf("boringAllowCert(cert with %s key) = %v, want %v", desc, !fipsOK, fipsOK)
140	}
141	return &boringCertificate{name, org, parentOrg, der, cert, key, fipsOK}
142}
143