boulder/test/integration/bad_key_test.go

121 lines
5.3 KiB
Go

//go:build integration
package integration
import (
"crypto/rand"
"crypto/rsa"
"crypto/x509"
"crypto/x509/pkix"
"math/big"
"testing"
"github.com/eggsampler/acme/v3"
"github.com/letsencrypt/boulder/test"
)
// TestFermat ensures that a certificate public key which can be factored using
// less than 100 rounds of Fermat's Algorithm is rejected.
func TestFermat(t *testing.T) {
t.Parallel()
type testCase struct {
name string
p string
q string
}
testCases := []testCase{
{
name: "canon printer (2048 bit, 1 round)",
p: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114449",
q: "155536235030272749691472293262418471207550926406427515178205576891522284497518443889075039382254334975506248481615035474816604875321501901699955105345417152355947783063521554077194367454070647740704883461064399268622437721385112646454393005862535727615809073410746393326688230040267160616554768771412289114113",
},
{
name: "innsbruck printer (4096 bit, 1 round)",
p: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605625661",
q: "25868808535211632564072019392873831934145242707953960515208595626279836366691068618582894100813803673421320899654654938470888358089618966238341690624345530870988951109006149164192566967552401505863871260691612081236189439839963332690997129144163260418447718577834226720411404568398865166471102885763673744513186211985402019037772108416694793355840983833695882936201196462579254234744648546792097397517107797153785052856301942321429858537224127598198913168345965493941246097657533085617002572245972336841716321849601971924830462771411171570422802773095537171762650402420866468579928479284978914972383512240254605624819",
},
// Ideally we'd have a 2408-bit, nearly-100-rounds test case, but it turns
// out purposefully generating keys that require 1 < N < 100 rounds to be
// factored is surprisingly tricky.
}
for _, tc := range testCases {
t.Run(tc.name, func(t *testing.T) {
t.Parallel()
// Create a client and complete an HTTP-01 challenge for a fake domain.
c, err := makeClient()
test.AssertNotError(t, err, "creating acme client")
domain := random_domain()
order, err := c.Client.NewOrder(
c.Account, []acme.Identifier{{Type: "dns", Value: domain}})
test.AssertNotError(t, err, "creating new order")
test.AssertEquals(t, len(order.Authorizations), 1)
authUrl := order.Authorizations[0]
auth, err := c.Client.FetchAuthorization(c.Account, authUrl)
test.AssertNotError(t, err, "fetching authorization")
chal, ok := auth.ChallengeMap[acme.ChallengeTypeHTTP01]
test.Assert(t, ok, "getting HTTP-01 challenge")
err = addHTTP01Response(chal.Token, chal.KeyAuthorization)
defer delHTTP01Response(chal.Token)
test.AssertNotError(t, err, "adding HTTP-01 response")
chal, err = c.Client.UpdateChallenge(c.Account, chal)
test.AssertNotError(t, err, "updating HTTP-01 challenge")
// Reconstruct the public modulus N from the test case's prime factors.
p, ok := new(big.Int).SetString(tc.p, 10)
test.Assert(t, ok, "failed to create large prime")
q, ok := new(big.Int).SetString(tc.q, 10)
test.Assert(t, ok, "failed to create large prime")
n := new(big.Int).Mul(p, q)
// Reconstruct the private exponent D from the test case's prime factors.
p_1 := new(big.Int).Sub(p, big.NewInt(1))
q_1 := new(big.Int).Sub(q, big.NewInt(1))
field := new(big.Int).Mul(p_1, q_1)
d := new(big.Int).ModInverse(big.NewInt(65537), field)
// Create a CSR containing the reconstructed pubkey and signed with the
// reconstructed private key.
pubkey := rsa.PublicKey{
N: n,
E: 65537,
}
privkey := rsa.PrivateKey{
PublicKey: pubkey,
D: d,
Primes: []*big.Int{p, q},
}
csrDer, err := x509.CreateCertificateRequest(rand.Reader, &x509.CertificateRequest{
SignatureAlgorithm: x509.SHA256WithRSA,
PublicKeyAlgorithm: x509.RSA,
PublicKey: &pubkey,
Subject: pkix.Name{CommonName: domain},
DNSNames: []string{domain},
}, &privkey)
test.AssertNotError(t, err, "creating CSR")
csr, err := x509.ParseCertificateRequest(csrDer)
test.AssertNotError(t, err, "parsing CSR")
// Finalizing the order should fail as we reject the public key.
_, err = c.Client.FinalizeOrder(c.Account, order, csr)
test.AssertError(t, err, "finalizing order")
test.AssertContains(t, err.Error(), "urn:ietf:params:acme:error:badCSR")
test.AssertContains(t, err.Error(), "key generated with factors too close together")
})
}
}