Merge pull request #664 from letsencrypt/sig-misuse
Mitigate signature misuse vulnerability
This commit is contained in:
commit
5afb1187bf
|
@ -80,7 +80,7 @@ type RegistrationAuthority interface {
|
|||
// ValidationAuthority defines the public interface for the Boulder VA
|
||||
type ValidationAuthority interface {
|
||||
// [RegistrationAuthority]
|
||||
UpdateValidations(Authorization, int, jose.JsonWebKey) error
|
||||
UpdateValidations(Authorization, int) error
|
||||
CheckCAARecords(AcmeIdentifier) (bool, bool, error)
|
||||
}
|
||||
|
||||
|
|
|
@ -269,6 +269,16 @@ type Challenge struct {
|
|||
// Contains information about URLs used or redirected to and IPs resolved and
|
||||
// used
|
||||
ValidationRecord []ValidationRecord `json:"validationRecord,omitempty"`
|
||||
|
||||
// The account key used to create this challenge. This is not part of the
|
||||
// spec, but clients are required to ignore unknown fields, so it's harmless
|
||||
// to include.
|
||||
//
|
||||
// Boulder needs to remember what key was used to create a challenge in order
|
||||
// to prevent an attacker from re-using a validation signature with a different,
|
||||
// unauthorized key. See:
|
||||
// https://mailarchive.ietf.org/arch/msg/acme/F71iz6qq1o_QPVhJCV4dqWf-4Yc
|
||||
AccountKey *jose.JsonWebKey `json:"accountKey,omitempty"`
|
||||
}
|
||||
|
||||
// RecordsSane checks the sanity of a ValidationRecord object before sending it
|
||||
|
@ -311,6 +321,10 @@ func (ch Challenge) IsSane(completed bool) bool {
|
|||
return false
|
||||
}
|
||||
|
||||
if ch.AccountKey == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
switch ch.Type {
|
||||
case ChallengeTypeSimpleHTTP:
|
||||
// check extra fields aren't used
|
||||
|
|
|
@ -67,9 +67,22 @@ func TestRecordSanityCheck(t *testing.T) {
|
|||
}
|
||||
|
||||
func TestChallengeSanityCheck(t *testing.T) {
|
||||
// Make a temporary account key
|
||||
var accountKey *jose.JsonWebKey
|
||||
err := json.Unmarshal([]byte(`{
|
||||
"kty":"RSA",
|
||||
"n":"yNWVhtYEKJR21y9xsHV-PD_bYwbXSeNuFal46xYxVfRL5mqha7vttvjB_vc7Xg2RvgCxHPCqoxgMPTzHrZT75LjCwIW2K_klBYN8oYvTwwmeSkAz6ut7ZxPv-nZaT5TJhGk0NT2kh_zSpdriEJ_3vW-mqxYbbBmpvHqsa1_zx9fSuHYctAZJWzxzUZXykbWMWQZpEiE0J4ajj51fInEzVn7VxV-mzfMyboQjujPh7aNJxAWSq4oQEJJDgWwSh9leyoJoPpONHxh5nEE5AjE01FkGICSxjpZsF-w8hOTI3XXohUdu29Se26k2B0PolDSuj0GIQU6-W9TdLXSjBb2SpQ",
|
||||
"e":"AQAB"
|
||||
}`), &accountKey)
|
||||
test.AssertNotError(t, err, "Error unmarshaling JWK")
|
||||
|
||||
types := []string{ChallengeTypeSimpleHTTP, ChallengeTypeDVSNI, ChallengeTypeDNS}
|
||||
for _, challengeType := range types {
|
||||
chall := Challenge{Type: challengeType, Status: StatusInvalid}
|
||||
chall := Challenge{
|
||||
Type: challengeType,
|
||||
Status: StatusInvalid,
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Status = StatusPending
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
|
@ -80,6 +93,7 @@ func TestChallengeSanityCheck(t *testing.T) {
|
|||
chall.Token = "evaGxfADs6pSRb2LAv9IZf17Dt3juxGJ+PCt92wr+o!"
|
||||
test.Assert(t, !chall.IsSane(false), "IsSane should be false")
|
||||
chall.Token = "KQqLsiS5j0CONR_eUXTUSUDNVaHODtc-0pD6ACif7U4"
|
||||
test.Assert(t, chall.IsSane(false), "IsSane should be true")
|
||||
|
||||
// Post-completion tests differ by type
|
||||
if challengeType == ChallengeTypeSimpleHTTP {
|
||||
|
@ -92,7 +106,7 @@ func TestChallengeSanityCheck(t *testing.T) {
|
|||
AddressesResolved: []net.IP{net.IP{127, 0, 0, 1}},
|
||||
AddressUsed: net.IP{127, 0, 0, 1},
|
||||
}}
|
||||
test.Assert(t, chall.IsSane(false), "IsSane should be true")
|
||||
test.Assert(t, chall.IsSane(true), "IsSane should be true")
|
||||
} else if challengeType == ChallengeTypeDVSNI || challengeType == ChallengeTypeDNS {
|
||||
chall.Validation = new(jose.JsonWebSignature)
|
||||
if challengeType == ChallengeTypeDVSNI {
|
||||
|
|
|
@ -144,6 +144,9 @@ func Fingerprint256(data []byte) string {
|
|||
func KeyDigest(key crypto.PublicKey) (string, error) {
|
||||
switch t := key.(type) {
|
||||
case *jose.JsonWebKey:
|
||||
if t == nil {
|
||||
return "", fmt.Errorf("Cannot compute digest of nil key")
|
||||
}
|
||||
return KeyDigest(t.Key)
|
||||
case jose.JsonWebKey:
|
||||
return KeyDigest(t.Key)
|
||||
|
|
|
@ -132,7 +132,8 @@ func (ra *RegistrationAuthorityImpl) NewRegistration(init core.Registration) (re
|
|||
|
||||
// NewAuthorization constuct a new Authz from a request.
|
||||
func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization, regID int64) (authz core.Authorization, err error) {
|
||||
if regID <= 0 {
|
||||
reg, err := ra.SA.GetRegistration(regID)
|
||||
if err != nil {
|
||||
err = core.MalformedRequestError(fmt.Sprintf("Invalid registration ID: %d", regID))
|
||||
return authz, err
|
||||
}
|
||||
|
@ -184,6 +185,9 @@ func (ra *RegistrationAuthorityImpl) NewAuthorization(request core.Authorization
|
|||
challengeURI, _ := core.ParseAcmeURL(ra.AuthzBase + authz.ID + "?challenge=" + strconv.Itoa(i))
|
||||
authz.Challenges[i].URI = challengeURI
|
||||
|
||||
// Add the account key used to generate the challenge
|
||||
authz.Challenges[i].AccountKey = ®.Key
|
||||
|
||||
if !authz.Challenges[i].IsSane(false) {
|
||||
// InternalServerError because we generated these challenges, they should
|
||||
// be OK.
|
||||
|
@ -374,8 +378,15 @@ func (ra *RegistrationAuthorityImpl) UpdateAuthorization(base core.Authorization
|
|||
return
|
||||
}
|
||||
|
||||
// Reject the update if the challenge in question was created
|
||||
// with a different account key
|
||||
if !core.KeyDigestEquals(reg.Key, authz.Challenges[challengeIndex].AccountKey) {
|
||||
err = core.UnauthorizedError("Challenge cannot be updated with a different key")
|
||||
return
|
||||
}
|
||||
|
||||
// Dispatch to the VA for service
|
||||
ra.VA.UpdateValidations(authz, challengeIndex, reg.Key)
|
||||
ra.VA.UpdateValidations(authz, challengeIndex)
|
||||
|
||||
return
|
||||
}
|
||||
|
|
|
@ -34,7 +34,7 @@ type DummyValidationAuthority struct {
|
|||
Argument core.Authorization
|
||||
}
|
||||
|
||||
func (dva *DummyValidationAuthority) UpdateValidations(authz core.Authorization, index int, key jose.JsonWebKey) (err error) {
|
||||
func (dva *DummyValidationAuthority) UpdateValidations(authz core.Authorization, index int) (err error) {
|
||||
dva.Called = true
|
||||
dva.Argument = authz
|
||||
return
|
||||
|
@ -380,6 +380,9 @@ func TestNewAuthorization(t *testing.T) {
|
|||
test.Assert(t, authz.Challenges[0].Type == core.ChallengeTypeSimpleHTTP, "Challenge 0 not SimpleHTTP")
|
||||
test.Assert(t, authz.Challenges[1].Type == core.ChallengeTypeDVSNI, "Challenge 1 not DVSNI")
|
||||
test.Assert(t, authz.Challenges[2].Type == core.ChallengeTypeDNS, "Challenge 2 not DNS")
|
||||
test.Assert(t, authz.Challenges[0].IsSane(false), "Challenge 0 is not sane")
|
||||
test.Assert(t, authz.Challenges[1].IsSane(false), "Challenge 1 is not sane")
|
||||
test.Assert(t, authz.Challenges[2].IsSane(false), "Challenge 2 is not sane")
|
||||
|
||||
t.Log("DONE TestNewAuthorization")
|
||||
}
|
||||
|
@ -387,10 +390,12 @@ func TestNewAuthorization(t *testing.T) {
|
|||
func TestUpdateAuthorization(t *testing.T) {
|
||||
va, sa, ra, cleanUp := initAuthorities(t)
|
||||
defer cleanUp()
|
||||
AuthzInitial, _ = sa.NewPendingAuthorization(AuthzInitial)
|
||||
sa.UpdatePendingAuthorization(AuthzInitial)
|
||||
|
||||
authz, err := ra.UpdateAuthorization(AuthzInitial, ResponseIndex, Response)
|
||||
// We know this is OK because of TestNewAuthorization
|
||||
authz, err := ra.NewAuthorization(AuthzRequest, Registration.ID)
|
||||
test.AssertNotError(t, err, "NewAuthorization failed")
|
||||
|
||||
authz, err = ra.UpdateAuthorization(authz, ResponseIndex, Response)
|
||||
test.AssertNotError(t, err, "UpdateAuthorization failed")
|
||||
|
||||
// Verify that returned authz same as DB
|
||||
|
@ -408,6 +413,28 @@ func TestUpdateAuthorization(t *testing.T) {
|
|||
t.Log("DONE TestUpdateAuthorization")
|
||||
}
|
||||
|
||||
func TestUpdateAuthorizationReject(t *testing.T) {
|
||||
_, sa, ra, cleanUp := initAuthorities(t)
|
||||
defer cleanUp()
|
||||
|
||||
// We know this is OK because of TestNewAuthorization
|
||||
authz, err := ra.NewAuthorization(AuthzRequest, Registration.ID)
|
||||
test.AssertNotError(t, err, "NewAuthorization failed")
|
||||
|
||||
// Change the account key
|
||||
reg, err := sa.GetRegistration(authz.RegistrationID)
|
||||
test.AssertNotError(t, err, "GetRegistration failed")
|
||||
reg.Key = AccountKeyC // was AccountKeyA
|
||||
err = sa.UpdateRegistration(reg)
|
||||
test.AssertNotError(t, err, "UpdateRegistration failed")
|
||||
|
||||
// Verify that the RA rejected the authorization request
|
||||
_, err = ra.UpdateAuthorization(authz, ResponseIndex, Response)
|
||||
test.AssertEquals(t, err, core.UnauthorizedError("Challenge cannot be updated with a different key"))
|
||||
|
||||
t.Log("DONE TestUpdateAuthorizationReject")
|
||||
}
|
||||
|
||||
func TestOnValidationUpdate(t *testing.T) {
|
||||
_, sa, ra, cleanUp := initAuthorities(t)
|
||||
defer cleanUp()
|
||||
|
|
|
@ -124,7 +124,6 @@ type caaRequest struct {
|
|||
type validationRequest struct {
|
||||
Authz core.Authorization
|
||||
Index int
|
||||
Key jose.JsonWebKey
|
||||
}
|
||||
|
||||
type alreadyDeniedCSRReq struct {
|
||||
|
@ -449,7 +448,7 @@ func NewValidationAuthorityServer(rpc RPCServer, impl core.ValidationAuthority)
|
|||
return
|
||||
}
|
||||
|
||||
err = impl.UpdateValidations(vaReq.Authz, vaReq.Index, vaReq.Key)
|
||||
err = impl.UpdateValidations(vaReq.Authz, vaReq.Index)
|
||||
return
|
||||
})
|
||||
|
||||
|
@ -494,11 +493,10 @@ func NewValidationAuthorityClient(client RPCClient) (vac ValidationAuthorityClie
|
|||
}
|
||||
|
||||
// UpdateValidations sends an Update Validations request
|
||||
func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, index int, key jose.JsonWebKey) error {
|
||||
func (vac ValidationAuthorityClient) UpdateValidations(authz core.Authorization, index int) error {
|
||||
vaReq := validationRequest{
|
||||
Authz: authz,
|
||||
Index: index,
|
||||
Key: key,
|
||||
}
|
||||
data, err := json.Marshal(vaReq)
|
||||
if err != nil {
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
|
||||
-- +goose Up
|
||||
-- SQL in section 'Up' is executed when this migration is applied
|
||||
|
||||
ALTER TABLE `challenges` ADD COLUMN (
|
||||
`accountKey` mediumblob
|
||||
);
|
||||
|
||||
-- +goose Down
|
||||
-- SQL section 'Down' is executed when this migration is rolled back
|
||||
|
||||
ALTER TABLE `challenges` DROP COLUMN `accountKey`;
|
19
sa/model.go
19
sa/model.go
|
@ -41,6 +41,7 @@ type challModel struct {
|
|||
TLS *bool `db:"tls"`
|
||||
Validation []byte `db:"validation"`
|
||||
ValidationRecord []byte `db:"validationRecord"`
|
||||
AccountKey []byte `db:"accountKey"`
|
||||
|
||||
LockCol int64
|
||||
}
|
||||
|
@ -123,6 +124,16 @@ func challengeToModel(c *core.Challenge, authID string) (*challModel, error) {
|
|||
}
|
||||
cm.ValidationRecord = vrJSON
|
||||
}
|
||||
if c.AccountKey != nil {
|
||||
akJSON, err := json.Marshal(c.AccountKey)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if len(akJSON) > mediumBlobSize {
|
||||
return nil, fmt.Errorf("Account key object is too large to store in the database")
|
||||
}
|
||||
cm.AccountKey = akJSON
|
||||
}
|
||||
return &cm, nil
|
||||
}
|
||||
|
||||
|
@ -164,5 +175,13 @@ func modelToChallenge(cm *challModel) (core.Challenge, error) {
|
|||
}
|
||||
c.ValidationRecord = vr
|
||||
}
|
||||
if len(cm.AccountKey) > 0 {
|
||||
var ak jose.JsonWebKey
|
||||
err := json.Unmarshal(cm.AccountKey, &ak)
|
||||
if err != nil {
|
||||
return core.Challenge{}, err
|
||||
}
|
||||
c.AccountKey = &ak
|
||||
}
|
||||
return c, nil
|
||||
}
|
||||
|
|
|
@ -191,7 +191,7 @@ func (va *ValidationAuthorityImpl) resolveAndConstructDialer(name, defaultPort s
|
|||
|
||||
// Validation methods
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdentifier, input core.Challenge, accountKey jose.JsonWebKey) (core.Challenge, error) {
|
||||
func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
|
@ -357,7 +357,7 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
|
|||
"token": challenge.Token,
|
||||
"tls": (challenge.TLS == nil) || *challenge.TLS,
|
||||
}
|
||||
err = verifyValidationJWS(parsedJws, &accountKey, target)
|
||||
err = verifyValidationJWS(parsedJws, challenge.AccountKey, target)
|
||||
if err != nil {
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
@ -372,7 +372,7 @@ func (va *ValidationAuthorityImpl) validateSimpleHTTP(identifier core.AcmeIdenti
|
|||
return challenge, nil
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge, accountKey jose.JsonWebKey) (core.Challenge, error) {
|
||||
func (va *ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
if identifier.Type != "dns" {
|
||||
|
@ -392,7 +392,7 @@ func (va *ValidationAuthorityImpl) validateDvsni(identifier core.AcmeIdentifier,
|
|||
"type": core.ChallengeTypeDVSNI,
|
||||
"token": challenge.Token,
|
||||
}
|
||||
err := verifyValidationJWS((*jose.JsonWebSignature)(challenge.Validation), &accountKey, target)
|
||||
err := verifyValidationJWS(challenge.Validation, challenge.AccountKey, target)
|
||||
if err != nil {
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
@ -493,7 +493,7 @@ func parseHTTPConnError(err error) core.ProblemType {
|
|||
return core.ConnectionProblem
|
||||
}
|
||||
|
||||
func (va *ValidationAuthorityImpl) validateDNS(identifier core.AcmeIdentifier, input core.Challenge, accountKey jose.JsonWebKey) (core.Challenge, error) {
|
||||
func (va *ValidationAuthorityImpl) validateDNS(identifier core.AcmeIdentifier, input core.Challenge) (core.Challenge, error) {
|
||||
challenge := input
|
||||
|
||||
if identifier.Type != core.IdentifierDNS {
|
||||
|
@ -513,7 +513,7 @@ func (va *ValidationAuthorityImpl) validateDNS(identifier core.AcmeIdentifier, i
|
|||
"type": core.ChallengeTypeDNS,
|
||||
"token": challenge.Token,
|
||||
}
|
||||
err := verifyValidationJWS((*jose.JsonWebSignature)(challenge.Validation), &accountKey, target)
|
||||
err := verifyValidationJWS(challenge.Validation, challenge.AccountKey, target)
|
||||
if err != nil {
|
||||
va.log.Debug(err.Error())
|
||||
challenge.Status = core.StatusInvalid
|
||||
|
@ -553,7 +553,7 @@ func (va *ValidationAuthorityImpl) validateDNS(identifier core.AcmeIdentifier, i
|
|||
|
||||
// Overall validation process
|
||||
|
||||
func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeIndex int, accountKey jose.JsonWebKey) {
|
||||
func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeIndex int) {
|
||||
logEvent := verificationRequestEvent{
|
||||
ID: authz.ID,
|
||||
Requester: authz.RegistrationID,
|
||||
|
@ -571,11 +571,11 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
|
||||
switch authz.Challenges[challengeIndex].Type {
|
||||
case core.ChallengeTypeSimpleHTTP:
|
||||
authz.Challenges[challengeIndex], err = va.validateSimpleHTTP(authz.Identifier, authz.Challenges[challengeIndex], accountKey)
|
||||
authz.Challenges[challengeIndex], err = va.validateSimpleHTTP(authz.Identifier, authz.Challenges[challengeIndex])
|
||||
case core.ChallengeTypeDVSNI:
|
||||
authz.Challenges[challengeIndex], err = va.validateDvsni(authz.Identifier, authz.Challenges[challengeIndex], accountKey)
|
||||
authz.Challenges[challengeIndex], err = va.validateDvsni(authz.Identifier, authz.Challenges[challengeIndex])
|
||||
case core.ChallengeTypeDNS:
|
||||
authz.Challenges[challengeIndex], err = va.validateDNS(authz.Identifier, authz.Challenges[challengeIndex], accountKey)
|
||||
authz.Challenges[challengeIndex], err = va.validateDNS(authz.Identifier, authz.Challenges[challengeIndex])
|
||||
}
|
||||
|
||||
if err != nil {
|
||||
|
@ -599,8 +599,8 @@ func (va *ValidationAuthorityImpl) validate(authz core.Authorization, challengeI
|
|||
}
|
||||
|
||||
// UpdateValidations runs the validate() method asynchronously using goroutines.
|
||||
func (va *ValidationAuthorityImpl) UpdateValidations(authz core.Authorization, challengeIndex int, accountKey jose.JsonWebKey) error {
|
||||
go va.validate(authz, challengeIndex, accountKey)
|
||||
func (va *ValidationAuthorityImpl) UpdateValidations(authz core.Authorization, challengeIndex int) error {
|
||||
go va.validate(authz, challengeIndex)
|
||||
return nil
|
||||
}
|
||||
|
||||
|
|
|
@ -57,7 +57,7 @@ var TheKey = rsa.PrivateKey{
|
|||
Primes: []*big.Int{p, q},
|
||||
}
|
||||
|
||||
var AccountKey = jose.JsonWebKey{Key: TheKey.Public()}
|
||||
var accountKey = &jose.JsonWebKey{Key: TheKey.Public()}
|
||||
|
||||
var ident = core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "localhost"}
|
||||
|
||||
|
@ -231,7 +231,12 @@ func brokenTLSSrv() *httptest.Server {
|
|||
}
|
||||
|
||||
func TestSimpleHttpTLS(t *testing.T) {
|
||||
chall := core.Challenge{Type: core.ChallengeTypeSimpleHTTP, Token: expectedToken, ValidationRecord: []core.ValidationRecord{}}
|
||||
chall := core.Challenge{
|
||||
Type: core.ChallengeTypeSimpleHTTP,
|
||||
Token: expectedToken,
|
||||
ValidationRecord: []core.ValidationRecord{},
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
hs := simpleSrv(t, expectedToken, true)
|
||||
defer hs.Close()
|
||||
|
@ -242,7 +247,7 @@ func TestSimpleHttpTLS(t *testing.T) {
|
|||
va.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
log.Clear()
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Error validating simpleHttp")
|
||||
logs := log.GetAllMatching(`^\[AUDIT\] Attempting to validate SimpleHTTPS for `)
|
||||
|
@ -252,7 +257,13 @@ func TestSimpleHttpTLS(t *testing.T) {
|
|||
|
||||
func TestSimpleHttp(t *testing.T) {
|
||||
tls := false
|
||||
chall := core.Challenge{Type: core.ChallengeTypeSimpleHTTP, Token: expectedToken, TLS: &tls, ValidationRecord: []core.ValidationRecord{}}
|
||||
chall := core.Challenge{
|
||||
Type: core.ChallengeTypeSimpleHTTP,
|
||||
Token: expectedToken,
|
||||
TLS: &tls,
|
||||
ValidationRecord: []core.ValidationRecord{},
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
// NOTE: We do not attempt to shut down the server. The problem is that the
|
||||
// "wait-long" handler sleeps for ten seconds, but this test finishes in less
|
||||
|
@ -276,7 +287,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
va := NewValidationAuthorityImpl(&PortConfig{SimpleHTTPPort: badPort})
|
||||
va.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
invalidChall, err := va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
invalidChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
|
@ -284,14 +295,14 @@ func TestSimpleHttp(t *testing.T) {
|
|||
va = NewValidationAuthorityImpl(&PortConfig{SimpleHTTPPort: goodPort})
|
||||
va.DNSResolver = &mocks.MockDNS{}
|
||||
log.Clear()
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Error validating simpleHttp")
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`^\[AUDIT\] `)), 1)
|
||||
|
||||
log.Clear()
|
||||
chall.Token = path404
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found a 404 for the challenge.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
|
@ -301,7 +312,7 @@ func TestSimpleHttp(t *testing.T) {
|
|||
chall.Token = pathWrongToken
|
||||
// The "wrong token" will actually be the expectedToken. It's wrong
|
||||
// because it doesn't match pathWrongToken.
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Should have found the wrong token value.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnauthorizedProblem)
|
||||
|
@ -309,33 +320,33 @@ func TestSimpleHttp(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathMoved
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Failed to follow 301 redirect")
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1)
|
||||
|
||||
log.Clear()
|
||||
chall.Token = pathFound
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "Failed to follow 302 redirect")
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/302" to ".*/301"`)), 1)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1)
|
||||
|
||||
ipIdentifier := core.AcmeIdentifier{Type: core.IdentifierType("ip"), Value: "127.0.0.1"}
|
||||
invalidChall, err = va.validateSimpleHTTP(ipIdentifier, chall, AccountKey)
|
||||
invalidChall, err = va.validateSimpleHTTP(ipIdentifier, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
|
||||
invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall, AccountKey)
|
||||
invalidChall, err = va.validateSimpleHTTP(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name is invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
|
||||
chall.Token = "wait-long"
|
||||
started := time.Now()
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
invalidChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
took := time.Since(started)
|
||||
// Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds
|
||||
test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds")
|
||||
|
@ -347,7 +358,12 @@ func TestSimpleHttp(t *testing.T) {
|
|||
|
||||
func TestSimpleHttpRedirectLookup(t *testing.T) {
|
||||
tls := false
|
||||
chall := core.Challenge{Token: expectedToken, TLS: &tls, ValidationRecord: []core.ValidationRecord{}}
|
||||
chall := core.Challenge{
|
||||
Token: expectedToken,
|
||||
TLS: &tls,
|
||||
ValidationRecord: []core.ValidationRecord{},
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
hs := simpleSrv(t, expectedToken, tls)
|
||||
defer hs.Close()
|
||||
|
@ -358,7 +374,7 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathMoved
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/301" to ".*/valid"`)), 1)
|
||||
|
@ -366,7 +382,7 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathFound
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/302" to ".*/301"`)), 1)
|
||||
|
@ -375,7 +391,7 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathRedirectLookupInvalid
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, chall.Token)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1)
|
||||
|
@ -383,7 +399,7 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathRedirectLookup
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, chall.Token)
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`redirect from ".*/re-lookup" to ".*other.valid/path"`)), 1)
|
||||
|
@ -392,7 +408,7 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
chall.Token = pathRedirectPort
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err = va.validateSimpleHTTP(ident, chall)
|
||||
fmt.Println(finChall.ValidationRecord)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, chall.Token)
|
||||
|
@ -403,7 +419,11 @@ func TestSimpleHttpRedirectLookup(t *testing.T) {
|
|||
|
||||
func TestSimpleHttpRedirectLoop(t *testing.T) {
|
||||
tls := false
|
||||
chall := core.Challenge{Token: "looper", TLS: &tls, ValidationRecord: []core.ValidationRecord{}}
|
||||
chall := core.Challenge{
|
||||
Token: "looper",
|
||||
TLS: &tls,
|
||||
ValidationRecord: []core.ValidationRecord{},
|
||||
}
|
||||
|
||||
hs := simpleSrv(t, expectedToken, tls)
|
||||
defer hs.Close()
|
||||
|
@ -413,7 +433,7 @@ func TestSimpleHttpRedirectLoop(t *testing.T) {
|
|||
va.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
log.Clear()
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall, AccountKey)
|
||||
finChall, err := va.validateSimpleHTTP(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, chall.Token)
|
||||
fmt.Println(finChall)
|
||||
|
@ -447,7 +467,7 @@ func TestDvsni(t *testing.T) {
|
|||
va.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
log.Clear()
|
||||
finChall, err := va.validateDvsni(ident, chall, AccountKey)
|
||||
finChall, err := va.validateDvsni(ident, chall)
|
||||
test.AssertEquals(t, finChall.Status, core.StatusValid)
|
||||
test.AssertNotError(t, err, "")
|
||||
test.AssertEquals(t, len(log.GetAllMatching(`Resolved addresses for localhost \[using 127.0.0.1\]: \[127.0.0.1\]`)), 1)
|
||||
|
@ -456,13 +476,13 @@ func TestDvsni(t *testing.T) {
|
|||
invalidChall, err := va.validateDvsni(core.AcmeIdentifier{
|
||||
Type: core.IdentifierType("ip"),
|
||||
Value: net.JoinHostPort("127.0.0.1", fmt.Sprintf("%d", port)),
|
||||
}, chall, AccountKey)
|
||||
}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "IdentifierType IP shouldn't have worked.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.MalformedProblem)
|
||||
|
||||
log.Clear()
|
||||
invalidChall, err = va.validateDvsni(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall, AccountKey)
|
||||
invalidChall, err = va.validateDvsni(core.AcmeIdentifier{Type: core.IdentifierDNS, Value: "always.invalid"}, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Domain name was supposed to be invalid.")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.UnknownHostProblem)
|
||||
|
@ -478,7 +498,7 @@ func TestDvsni(t *testing.T) {
|
|||
|
||||
log.Clear()
|
||||
started := time.Now()
|
||||
invalidChall, err = va.validateDvsni(ident, chall, AccountKey)
|
||||
invalidChall, err = va.validateDvsni(ident, chall)
|
||||
took := time.Since(started)
|
||||
// Check that the HTTP connection times out after 5 seconds and doesn't block for 10 seconds
|
||||
test.Assert(t, (took > (time.Second * 5)), "HTTP timed out before 5 seconds")
|
||||
|
@ -490,7 +510,7 @@ func TestDvsni(t *testing.T) {
|
|||
|
||||
// Take down DVSNI validation server and check that validation fails.
|
||||
hs.Close()
|
||||
invalidChall, err = va.validateDvsni(ident, chall, AccountKey)
|
||||
invalidChall, err = va.validateDvsni(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "Server's down; expected refusal. Where did we connect?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.ConnectionProblem)
|
||||
|
@ -505,7 +525,7 @@ func TestTLSError(t *testing.T) {
|
|||
va := NewValidationAuthorityImpl(&PortConfig{DVSNIPort: port})
|
||||
va.DNSResolver = &mocks.MockDNS{}
|
||||
|
||||
invalidChall, err := va.validateDvsni(ident, chall, AccountKey)
|
||||
invalidChall, err := va.validateDvsni(ident, chall)
|
||||
test.AssertEquals(t, invalidChall.Status, core.StatusInvalid)
|
||||
test.AssertError(t, err, "What cert was used?")
|
||||
test.AssertEquals(t, invalidChall.Error.Type, core.TLSProblem)
|
||||
|
@ -516,6 +536,7 @@ func TestValidateHTTP(t *testing.T) {
|
|||
challHTTP := core.SimpleHTTPChallenge()
|
||||
challHTTP.TLS = &tls
|
||||
challHTTP.ValidationRecord = []core.ValidationRecord{}
|
||||
challHTTP.AccountKey = accountKey
|
||||
|
||||
hs := simpleSrv(t, challHTTP.Token, tls)
|
||||
port, err := getPort(hs)
|
||||
|
@ -533,7 +554,7 @@ func TestValidateHTTP(t *testing.T) {
|
|||
Identifier: ident,
|
||||
Challenges: []core.Challenge{challHTTP},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertEquals(t, core.StatusValid, mockRA.lastAuthz.Challenges[0].Status)
|
||||
}
|
||||
|
@ -545,6 +566,7 @@ func createChallenge(challengeType string) core.Challenge {
|
|||
Status: core.StatusPending,
|
||||
Token: core.NewToken(),
|
||||
ValidationRecord: []core.ValidationRecord{},
|
||||
AccountKey: accountKey,
|
||||
}
|
||||
|
||||
validationPayload, _ := json.Marshal(map[string]interface{}{
|
||||
|
@ -576,7 +598,7 @@ func TestValidateDvsni(t *testing.T) {
|
|||
Identifier: ident,
|
||||
Challenges: []core.Challenge{chall},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertEquals(t, core.StatusValid, mockRA.lastAuthz.Challenges[0].Status)
|
||||
}
|
||||
|
@ -597,7 +619,7 @@ func TestValidateDvsniNotSane(t *testing.T) {
|
|||
Identifier: ident,
|
||||
Challenges: []core.Challenge{chall},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertEquals(t, core.StatusInvalid, mockRA.lastAuthz.Challenges[0].Status)
|
||||
}
|
||||
|
@ -621,7 +643,7 @@ func TestUpdateValidations(t *testing.T) {
|
|||
}
|
||||
|
||||
started := time.Now()
|
||||
va.UpdateValidations(authz, 0, AccountKey)
|
||||
va.UpdateValidations(authz, 0)
|
||||
took := time.Since(started)
|
||||
|
||||
// Check that the call to va.UpdateValidations didn't block for 3 seconds
|
||||
|
@ -703,7 +725,7 @@ func TestDNSValidationFailure(t *testing.T) {
|
|||
Identifier: ident,
|
||||
Challenges: []core.Challenge{chalDNS},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
t.Logf("Resulting Authz: %+v", authz)
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
|
@ -731,7 +753,7 @@ func TestDNSValidationInvalid(t *testing.T) {
|
|||
mockRA := &MockRegistrationAuthority{}
|
||||
va.RA = mockRA
|
||||
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
|
@ -762,7 +784,7 @@ func TestDNSValidationNotSane(t *testing.T) {
|
|||
}
|
||||
|
||||
for i := 0; i < len(authz.Challenges); i++ {
|
||||
va.validate(authz, i, AccountKey)
|
||||
va.validate(authz, i)
|
||||
test.AssertEquals(t, authz.Challenges[i].Status, core.StatusInvalid)
|
||||
test.AssertEquals(t, authz.Challenges[i].Error.Type, core.MalformedProblem)
|
||||
}
|
||||
|
@ -786,7 +808,7 @@ func TestDNSValidationServFail(t *testing.T) {
|
|||
Identifier: badIdent,
|
||||
Challenges: []core.Challenge{chalDNS},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
|
@ -807,7 +829,7 @@ func TestDNSValidationNoServer(t *testing.T) {
|
|||
Identifier: ident,
|
||||
Challenges: []core.Challenge{chalDNS},
|
||||
}
|
||||
va.validate(authz, 0, AccountKey)
|
||||
va.validate(authz, 0)
|
||||
|
||||
test.AssertNotNil(t, mockRA.lastAuthz, "Should have gotten an authorization")
|
||||
test.Assert(t, authz.Challenges[0].Status == core.StatusInvalid, "Should be invalid.")
|
||||
|
@ -844,7 +866,7 @@ func TestDNSValidationLive(t *testing.T) {
|
|||
Challenges: []core.Challenge{goodChalDNS},
|
||||
}
|
||||
|
||||
va.validate(authzGood, 0, AccountKey)
|
||||
va.validate(authzGood, 0)
|
||||
|
||||
if authzGood.Challenges[0].Status != core.StatusValid {
|
||||
t.Logf("TestDNSValidationLive on Good did not succeed.")
|
||||
|
@ -861,7 +883,7 @@ func TestDNSValidationLive(t *testing.T) {
|
|||
Challenges: []core.Challenge{badChalDNS},
|
||||
}
|
||||
|
||||
va.validate(authzBad, 0, AccountKey)
|
||||
va.validate(authzBad, 0)
|
||||
if authzBad.Challenges[0].Status != core.StatusInvalid {
|
||||
t.Logf("TestDNSValidationLive on Bad did succeed inappropriately.")
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue