diff --git a/acme/api/handler.go b/acme/api/handler.go index 17565998..e557f33b 100644 --- a/acme/api/handler.go +++ b/acme/api/handler.go @@ -25,7 +25,7 @@ type Clock struct{} // Now returns the UTC time rounded to seconds. func (c *Clock) Now() time.Time { - return time.Now().UTC().Round(time.Second) + return time.Now().UTC().Truncate(time.Second) } var clock Clock diff --git a/acme/challenge.go b/acme/challenge.go index 94c74b74..a47fc7df 100644 --- a/acme/challenge.go +++ b/acme/challenge.go @@ -14,6 +14,7 @@ import ( "io/ioutil" "net" "net/http" + "net/url" "strings" "time" @@ -49,7 +50,7 @@ func (ch *Challenge) ToLog() (interface{}, error) { // updated. func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { // If already valid or invalid then return without performing validation. - if ch.Status == StatusValid || ch.Status == StatusInvalid { + if ch.Status != StatusPending { return nil } switch ch.Type { @@ -65,32 +66,32 @@ func (ch *Challenge) Validate(ctx context.Context, db DB, jwk *jose.JSONWebKey, } func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { - url := fmt.Sprintf("http://%s/.well-known/acme-challenge/%s", ch.Value, ch.Token) + url := &url.URL{Scheme: "http", Host: ch.Value, Path: fmt.Sprintf("/.well-known/acme-challenge/%s", ch.Token)} - resp, err := vo.HTTPGet(url) + resp, err := vo.HTTPGet(url.String()) if err != nil { - return storeError(ctx, ch, db, WrapError(ErrorConnectionType, err, + return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, "error doing http GET for url %s", url)) } + defer resp.Body.Close() if resp.StatusCode >= 400 { - return storeError(ctx, ch, db, NewError(ErrorConnectionType, + return storeError(ctx, db, ch, false, NewError(ErrorConnectionType, "error doing http GET for url %s with status code %d", url, resp.StatusCode)) } - defer resp.Body.Close() body, err := ioutil.ReadAll(resp.Body) if err != nil { return WrapErrorISE(err, "error reading "+ "response body for url %s", url) } - keyAuth := strings.Trim(string(body), "\r\n") + keyAuth := strings.TrimSpace(string(body)) expected, err := KeyAuthorization(ch.Token, jwk) if err != nil { return err } if keyAuth != expected { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expected, keyAuth)) } @@ -107,7 +108,11 @@ func http01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWeb func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebKey, vo *ValidateChallengeOptions) error { config := &tls.Config{ - NextProtos: []string{"acme-tls/1"}, + NextProtos: []string{"acme-tls/1"}, + // https://tools.ietf.org/html/rfc8737#section-4 + // ACME servers that implement "acme-tls/1" MUST only negotiate TLS 1.2 + // [RFC5246] or higher when connecting to clients for validation. + MinVersion: tls.VersionTLS12, ServerName: ch.Value, InsecureSkipVerify: true, // we expect a self-signed challenge certificate } @@ -116,7 +121,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON conn, err := vo.TLSDial("tcp", hostPort, config) if err != nil { - return storeError(ctx, ch, db, WrapError(ErrorConnectionType, err, + return storeError(ctx, db, ch, false, WrapError(ErrorConnectionType, err, "error doing TLS dial for %s", hostPort)) } defer conn.Close() @@ -125,19 +130,19 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON certs := cs.PeerCertificates if len(certs) == 0 { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "%s challenge for %s resulted in no certificates", ch.Type, ch.Value)) } if !cs.NegotiatedProtocolIsMutual || cs.NegotiatedProtocol != "acme-tls/1" { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "cannot negotiate ALPN acme-tls/1 protocol for tls-alpn-01 challenge")) } leafCert := certs[0] if len(leafCert.DNSNames) != 1 || !strings.EqualFold(leafCert.DNSNames[0], ch.Value) { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: leaf certificate must contain a single DNS name, %v", ch.Value)) } @@ -154,7 +159,7 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON for _, ext := range leafCert.Extensions { if idPeAcmeIdentifier.Equal(ext.Id) { if !ext.Critical { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: acmeValidationV1 extension not critical")) } @@ -162,12 +167,12 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON rest, err := asn1.Unmarshal(ext.Value, &extValue) if err != nil || len(rest) > 0 || len(hashedKeyAuth) != len(extValue) { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: malformed acmeValidationV1 extension value")) } if subtle.ConstantTimeCompare(hashedKeyAuth[:], extValue) != 1 { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: "+ "expected acmeValidationV1 extension value %s for this challenge but got %s", hex.EncodeToString(hashedKeyAuth[:]), hex.EncodeToString(extValue))) @@ -189,11 +194,11 @@ func tlsalpn01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSON } if foundIDPeAcmeIdentifierV1Obsolete { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: obsolete id-pe-acmeIdentifier in acmeValidationV1 extension")) } - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, true, NewError(ErrorRejectedIdentifierType, "incorrect certificate for tls-alpn-01 challenge: missing acmeValidationV1 extension")) } @@ -206,7 +211,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK txtRecords, err := vo.LookupTxt("_acme-challenge." + domain) if err != nil { - return storeError(ctx, ch, db, WrapError(ErrorDNSType, err, + return storeError(ctx, db, ch, false, WrapError(ErrorDNSType, err, "error looking up TXT records for domain %s", domain)) } @@ -224,7 +229,7 @@ func dns01Validate(ctx context.Context, ch *Challenge, db DB, jwk *jose.JSONWebK } } if !found { - return storeError(ctx, ch, db, NewError(ErrorRejectedIdentifierType, + return storeError(ctx, db, ch, false, NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expectedKeyAuth, txtRecords)) } @@ -251,8 +256,11 @@ func KeyAuthorization(token string, jwk *jose.JSONWebKey) (string, error) { } // storeError the given error to an ACME error and saves using the DB interface. -func storeError(ctx context.Context, ch *Challenge, db DB, err *Error) error { +func storeError(ctx context.Context, db DB, ch *Challenge, markInvalid bool, err *Error) error { ch.Error = err + if markInvalid { + ch.Status = StatusInvalid + } if err := db.UpdateChallenge(ctx, ch); err != nil { return WrapErrorISE(err, "failure saving error to acme challenge") } diff --git a/acme/challenge_test.go b/acme/challenge_test.go index caaca8f6..14287945 100644 --- a/acme/challenge_test.go +++ b/acme/challenge_test.go @@ -31,17 +31,19 @@ import ( func Test_storeError(t *testing.T) { type test struct { - ch *Challenge - db DB - err *Error + ch *Challenge + db DB + markInvalid bool + err *Error } err := NewError(ErrorMalformedType, "foo") tests := map[string]func(t *testing.T) test{ "fail/db.UpdateChallenge-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusValid, } return test{ ch: ch, @@ -50,6 +52,7 @@ func Test_storeError(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusValid) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -64,9 +67,10 @@ func Test_storeError(t *testing.T) { }, "fail/db.UpdateChallenge-acme-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusValid, } return test{ ch: ch, @@ -75,6 +79,7 @@ func Test_storeError(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusValid) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -89,9 +94,10 @@ func Test_storeError(t *testing.T) { }, "ok": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusValid, } return test{ ch: ch, @@ -100,6 +106,7 @@ func Test_storeError(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusValid) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) assert.Equals(t, updch.Error.Type, err.Type) @@ -111,11 +118,38 @@ func Test_storeError(t *testing.T) { }, } }, + "ok/mark-invalid": func(t *testing.T) test { + ch := &Challenge{ + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusValid, + } + return test{ + ch: ch, + db: &MockDB{ + MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { + assert.Equals(t, updch.ID, ch.ID) + assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusInvalid) + + assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) + assert.Equals(t, updch.Error.Type, err.Type) + assert.Equals(t, updch.Error.Detail, err.Detail) + assert.Equals(t, updch.Error.Status, err.Status) + assert.Equals(t, updch.Error.Detail, err.Detail) + return nil + }, + }, + markInvalid: true, + } + }, } for name, run := range tests { t.Run(name, func(t *testing.T) { tc := run(t) - if err := storeError(context.Background(), tc.ch, tc.db, err); err != nil { + if err := storeError(context.Background(), tc.db, tc.ch, tc.markInvalid, err); err != nil { if assert.NotNil(t, tc.err) { switch k := err.(type) { case *Error: @@ -499,9 +533,10 @@ func TestHTTP01Validate(t *testing.T) { tests := map[string]func(t *testing.T) test{ "fail/http-get-error-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } return test{ @@ -515,6 +550,8 @@ func TestHTTP01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) @@ -530,9 +567,10 @@ func TestHTTP01Validate(t *testing.T) { }, "ok/http-get-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } return test{ @@ -546,6 +584,8 @@ func TestHTTP01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s: force", ch.Token) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) @@ -560,9 +600,10 @@ func TestHTTP01Validate(t *testing.T) { }, "fail/http-get->=400-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } return test{ @@ -571,6 +612,7 @@ func TestHTTP01Validate(t *testing.T) { HTTPGet: func(url string) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusBadRequest, + Body: errReader(0), }, nil }, }, @@ -578,6 +620,8 @@ func TestHTTP01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) @@ -593,9 +637,10 @@ func TestHTTP01Validate(t *testing.T) { }, "ok/http-get->=400": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } return test{ @@ -604,6 +649,7 @@ func TestHTTP01Validate(t *testing.T) { HTTPGet: func(url string) (*http.Response, error) { return &http.Response{ StatusCode: http.StatusBadRequest, + Body: errReader(0), }, nil }, }, @@ -611,6 +657,8 @@ func TestHTTP01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorConnectionType, "error doing http GET for url http://zap.internal/.well-known/acme-challenge/%s with status code 400", ch.Token) assert.HasPrefix(t, updch.Error.Err.Error(), err.Err.Error()) @@ -625,9 +673,10 @@ func TestHTTP01Validate(t *testing.T) { }, "fail/read-body": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } return test{ @@ -644,9 +693,10 @@ func TestHTTP01Validate(t *testing.T) { }, "fail/key-auth-gen-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -667,9 +717,10 @@ func TestHTTP01Validate(t *testing.T) { }, "ok/key-auth-mismatch": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -692,6 +743,7 @@ func TestHTTP01Validate(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusInvalid) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) @@ -707,9 +759,10 @@ func TestHTTP01Validate(t *testing.T) { }, "fail/key-auth-mismatch-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -732,6 +785,7 @@ func TestHTTP01Validate(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusInvalid) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got foo", expKeyAuth) @@ -748,9 +802,10 @@ func TestHTTP01Validate(t *testing.T) { }, "fail/update-challenge-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -789,9 +844,10 @@ func TestHTTP01Validate(t *testing.T) { }, "ok": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: "zap.internal", + ID: "chID", + Token: "token", + Value: "zap.internal", + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -864,9 +920,10 @@ func TestDNS01Validate(t *testing.T) { tests := map[string]func(t *testing.T) test{ "fail/lookupTXT-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } return test{ @@ -880,6 +937,8 @@ func TestDNS01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) @@ -896,9 +955,10 @@ func TestDNS01Validate(t *testing.T) { }, "ok/lookupTXT-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } return test{ @@ -912,6 +972,8 @@ func TestDNS01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorDNSType, "error looking up TXT records for domain %s: force", domain) @@ -927,9 +989,10 @@ func TestDNS01Validate(t *testing.T) { }, "fail/key-auth-gen-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -949,9 +1012,10 @@ func TestDNS01Validate(t *testing.T) { }, "fail/key-auth-mismatch-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -971,6 +1035,8 @@ func TestDNS01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) @@ -988,9 +1054,10 @@ func TestDNS01Validate(t *testing.T) { }, "ok/key-auth-mismatch-store-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -1010,6 +1077,8 @@ func TestDNS01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) + assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusPending) err := NewError(ErrorRejectedIdentifierType, "keyAuthorization does not match; expected %s, but got %s", expKeyAuth, []string{"foo", "bar"}) @@ -1026,9 +1095,10 @@ func TestDNS01Validate(t *testing.T) { }, "fail/update-challenge-error": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -1051,6 +1121,7 @@ func TestDNS01Validate(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusValid) assert.Equals(t, updch.Status, StatusValid) assert.Equals(t, updch.Error, nil) @@ -1069,9 +1140,10 @@ func TestDNS01Validate(t *testing.T) { }, "ok": func(t *testing.T) test { ch := &Challenge{ - ID: "chID", - Token: "token", - Value: fulldomain, + ID: "chID", + Token: "token", + Value: fulldomain, + Status: StatusPending, } jwk, err := jose.GenerateJWK("EC", "P-256", "ES256", "sig", "", 0) @@ -1094,6 +1166,7 @@ func TestDNS01Validate(t *testing.T) { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) assert.Equals(t, updch.Value, ch.Value) + assert.Equals(t, updch.Status, StatusValid) assert.Equals(t, updch.Status, StatusValid) assert.Equals(t, updch.Error, nil) @@ -1349,7 +1422,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1379,7 +1452,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1415,7 +1488,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1452,7 +1525,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1496,7 +1569,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1539,7 +1612,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1583,7 +1656,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1626,7 +1699,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1692,7 +1765,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1731,7 +1804,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1775,7 +1848,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1818,7 +1891,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1858,7 +1931,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1897,7 +1970,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1942,7 +2015,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -1988,7 +2061,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -2034,7 +2107,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -2078,7 +2151,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusInvalid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) @@ -2123,7 +2196,7 @@ func TestTLSALPN01Validate(t *testing.T) { MockUpdateChallenge: func(ctx context.Context, updch *Challenge) error { assert.Equals(t, updch.ID, ch.ID) assert.Equals(t, updch.Token, ch.Token) - assert.Equals(t, updch.Status, ch.Status) + assert.Equals(t, updch.Status, StatusValid) assert.Equals(t, updch.Type, ch.Type) assert.Equals(t, updch.Value, ch.Value) assert.Equals(t, updch.Error, nil) diff --git a/acme/common.go b/acme/common.go index 05c909eb..26552c61 100644 --- a/acme/common.go +++ b/acme/common.go @@ -19,7 +19,7 @@ type Clock struct{} // Now returns the UTC time rounded to seconds. func (c *Clock) Now() time.Time { - return time.Now().UTC().Round(time.Second) + return time.Now().UTC().Truncate(time.Second) } var clock Clock diff --git a/acme/order.go b/acme/order.go index 7405906d..a003fe9a 100644 --- a/acme/order.go +++ b/acme/order.go @@ -24,10 +24,10 @@ type Order struct { AccountID string `json:"-"` ProvisionerID string `json:"-"` Status Status `json:"status"` - ExpiresAt time.Time `json:"expires,omitempty"` + ExpiresAt time.Time `json:"expires"` Identifiers []Identifier `json:"identifiers"` - NotBefore time.Time `json:"notBefore,omitempty"` - NotAfter time.Time `json:"notAfter,omitempty"` + NotBefore time.Time `json:"notBefore"` + NotAfter time.Time `json:"notAfter"` Error *Error `json:"error,omitempty"` AuthorizationIDs []string `json:"-"` AuthorizationURLs []string `json:"authorizations"`