diff --git a/core/interfaces.go b/core/interfaces.go index 37e396a02..5dd9dd71d 100644 --- a/core/interfaces.go +++ b/core/interfaces.go @@ -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) } diff --git a/core/objects.go b/core/objects.go index aea2744ae..9df29e48a 100644 --- a/core/objects.go +++ b/core/objects.go @@ -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 diff --git a/core/objects_test.go b/core/objects_test.go index 053126ea8..53763f1af 100644 --- a/core/objects_test.go +++ b/core/objects_test.go @@ -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 { diff --git a/core/util.go b/core/util.go index 71b8e93ab..a419270b3 100644 --- a/core/util.go +++ b/core/util.go @@ -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) diff --git a/ra/registration-authority.go b/ra/registration-authority.go index 1cd625c90..3b6f5311d 100644 --- a/ra/registration-authority.go +++ b/ra/registration-authority.go @@ -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 } diff --git a/ra/registration-authority_test.go b/ra/registration-authority_test.go index a4aa8b724..e11411d67 100644 --- a/ra/registration-authority_test.go +++ b/ra/registration-authority_test.go @@ -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() diff --git a/rpc/rpc-wrappers.go b/rpc/rpc-wrappers.go index 437a7b447..1681f6749 100644 --- a/rpc/rpc-wrappers.go +++ b/rpc/rpc-wrappers.go @@ -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 { diff --git a/sa/_db/migrations/20150826211856_AddChallengeAccountKey.sql b/sa/_db/migrations/20150826211856_AddChallengeAccountKey.sql new file mode 100644 index 000000000..635a82ccb --- /dev/null +++ b/sa/_db/migrations/20150826211856_AddChallengeAccountKey.sql @@ -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`; diff --git a/sa/model.go b/sa/model.go index 0c932287d..e206331ee 100644 --- a/sa/model.go +++ b/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 } diff --git a/va/validation-authority.go b/va/validation-authority.go index aab6455ac..5f63137fb 100644 --- a/va/validation-authority.go +++ b/va/validation-authority.go @@ -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 } diff --git a/va/validation-authority_test.go b/va/validation-authority_test.go index 616f6cae0..d9f505e27 100644 --- a/va/validation-authority_test.go +++ b/va/validation-authority_test.go @@ -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.") }