121 lines
5.3 KiB
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")
|
|
})
|
|
}
|
|
}
|