summaryrefslogtreecommitdiff
path: root/vendor/gopkg.in/square/go-jose.v2/jwt
diff options
context:
space:
mode:
Diffstat (limited to 'vendor/gopkg.in/square/go-jose.v2/jwt')
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/builder.go334
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/claims.go115
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/doc.go22
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/errors.go50
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go113
-rw-r--r--vendor/gopkg.in/square/go-jose.v2/jwt/validation.go89
6 files changed, 723 insertions, 0 deletions
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go
new file mode 100644
index 000000000..686ec80a4
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/builder.go
@@ -0,0 +1,334 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "bytes"
+ "reflect"
+
+ "gopkg.in/square/go-jose.v2/json"
+
+ "gopkg.in/square/go-jose.v2"
+)
+
+// Builder is a utility for making JSON Web Tokens. Calls can be chained, and
+// errors are accumulated until the final call to CompactSerialize/FullSerialize.
+type Builder interface {
+ // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
+ // into single JSON object. If you are passing private claims, make sure to set
+ // struct field tags to specify the name for the JSON key to be used when
+ // serializing.
+ Claims(i interface{}) Builder
+ // Token builds a JSONWebToken from provided data.
+ Token() (*JSONWebToken, error)
+ // FullSerialize serializes a token using the full serialization format.
+ FullSerialize() (string, error)
+ // CompactSerialize serializes a token using the compact serialization format.
+ CompactSerialize() (string, error)
+}
+
+// NestedBuilder is a utility for making Signed-Then-Encrypted JSON Web Tokens.
+// Calls can be chained, and errors are accumulated until final call to
+// CompactSerialize/FullSerialize.
+type NestedBuilder interface {
+ // Claims encodes claims into JWE/JWS form. Multiple calls will merge claims
+ // into single JSON object. If you are passing private claims, make sure to set
+ // struct field tags to specify the name for the JSON key to be used when
+ // serializing.
+ Claims(i interface{}) NestedBuilder
+ // Token builds a NestedJSONWebToken from provided data.
+ Token() (*NestedJSONWebToken, error)
+ // FullSerialize serializes a token using the full serialization format.
+ FullSerialize() (string, error)
+ // CompactSerialize serializes a token using the compact serialization format.
+ CompactSerialize() (string, error)
+}
+
+type builder struct {
+ payload map[string]interface{}
+ err error
+}
+
+type signedBuilder struct {
+ builder
+ sig jose.Signer
+}
+
+type encryptedBuilder struct {
+ builder
+ enc jose.Encrypter
+}
+
+type nestedBuilder struct {
+ builder
+ sig jose.Signer
+ enc jose.Encrypter
+}
+
+// Signed creates builder for signed tokens.
+func Signed(sig jose.Signer) Builder {
+ return &signedBuilder{
+ sig: sig,
+ }
+}
+
+// Encrypted creates builder for encrypted tokens.
+func Encrypted(enc jose.Encrypter) Builder {
+ return &encryptedBuilder{
+ enc: enc,
+ }
+}
+
+// SignedAndEncrypted creates builder for signed-then-encrypted tokens.
+// ErrInvalidContentType will be returned if encrypter doesn't have JWT content type.
+func SignedAndEncrypted(sig jose.Signer, enc jose.Encrypter) NestedBuilder {
+ if contentType, _ := enc.Options().ExtraHeaders[jose.HeaderContentType].(jose.ContentType); contentType != "JWT" {
+ return &nestedBuilder{
+ builder: builder{
+ err: ErrInvalidContentType,
+ },
+ }
+ }
+ return &nestedBuilder{
+ sig: sig,
+ enc: enc,
+ }
+}
+
+func (b builder) claims(i interface{}) builder {
+ if b.err != nil {
+ return b
+ }
+
+ m, ok := i.(map[string]interface{})
+ switch {
+ case ok:
+ return b.merge(m)
+ case reflect.Indirect(reflect.ValueOf(i)).Kind() == reflect.Struct:
+ m, err := normalize(i)
+ if err != nil {
+ return builder{
+ err: err,
+ }
+ }
+ return b.merge(m)
+ default:
+ return builder{
+ err: ErrInvalidClaims,
+ }
+ }
+}
+
+func normalize(i interface{}) (map[string]interface{}, error) {
+ m := make(map[string]interface{})
+
+ raw, err := json.Marshal(i)
+ if err != nil {
+ return nil, err
+ }
+
+ d := json.NewDecoder(bytes.NewReader(raw))
+ d.UseNumber()
+
+ if err := d.Decode(&m); err != nil {
+ return nil, err
+ }
+
+ return m, nil
+}
+
+func (b *builder) merge(m map[string]interface{}) builder {
+ p := make(map[string]interface{})
+ for k, v := range b.payload {
+ p[k] = v
+ }
+ for k, v := range m {
+ p[k] = v
+ }
+
+ return builder{
+ payload: p,
+ }
+}
+
+func (b *builder) token(p func(interface{}) ([]byte, error), h []jose.Header) (*JSONWebToken, error) {
+ return &JSONWebToken{
+ payload: p,
+ Headers: h,
+ }, nil
+}
+
+func (b *signedBuilder) Claims(i interface{}) Builder {
+ return &signedBuilder{
+ builder: b.builder.claims(i),
+ sig: b.sig,
+ }
+}
+
+func (b *signedBuilder) Token() (*JSONWebToken, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return nil, err
+ }
+
+ h := make([]jose.Header, len(sig.Signatures))
+ for i, v := range sig.Signatures {
+ h[i] = v.Header
+ }
+
+ return b.builder.token(sig.Verify, h)
+}
+
+func (b *signedBuilder) CompactSerialize() (string, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return "", err
+ }
+
+ return sig.CompactSerialize()
+}
+
+func (b *signedBuilder) FullSerialize() (string, error) {
+ sig, err := b.sign()
+ if err != nil {
+ return "", err
+ }
+
+ return sig.FullSerialize(), nil
+}
+
+func (b *signedBuilder) sign() (*jose.JSONWebSignature, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ return b.sig.Sign(p)
+}
+
+func (b *encryptedBuilder) Claims(i interface{}) Builder {
+ return &encryptedBuilder{
+ builder: b.builder.claims(i),
+ enc: b.enc,
+ }
+}
+
+func (b *encryptedBuilder) CompactSerialize() (string, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.CompactSerialize()
+}
+
+func (b *encryptedBuilder) FullSerialize() (string, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.FullSerialize(), nil
+}
+
+func (b *encryptedBuilder) Token() (*JSONWebToken, error) {
+ enc, err := b.encrypt()
+ if err != nil {
+ return nil, err
+ }
+
+ return b.builder.token(enc.Decrypt, []jose.Header{enc.Header})
+}
+
+func (b *encryptedBuilder) encrypt() (*jose.JSONWebEncryption, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ return b.enc.Encrypt(p)
+}
+
+func (b *nestedBuilder) Claims(i interface{}) NestedBuilder {
+ return &nestedBuilder{
+ builder: b.builder.claims(i),
+ sig: b.sig,
+ enc: b.enc,
+ }
+}
+
+func (b *nestedBuilder) Token() (*NestedJSONWebToken, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return nil, err
+ }
+
+ return &NestedJSONWebToken{
+ enc: enc,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
+
+func (b *nestedBuilder) CompactSerialize() (string, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.CompactSerialize()
+}
+
+func (b *nestedBuilder) FullSerialize() (string, error) {
+ enc, err := b.signAndEncrypt()
+ if err != nil {
+ return "", err
+ }
+
+ return enc.FullSerialize(), nil
+}
+
+func (b *nestedBuilder) signAndEncrypt() (*jose.JSONWebEncryption, error) {
+ if b.err != nil {
+ return nil, b.err
+ }
+
+ p, err := json.Marshal(b.payload)
+ if err != nil {
+ return nil, err
+ }
+
+ sig, err := b.sig.Sign(p)
+ if err != nil {
+ return nil, err
+ }
+
+ p2, err := sig.CompactSerialize()
+ if err != nil {
+ return nil, err
+ }
+
+ return b.enc.Encrypt([]byte(p2))
+}
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go
new file mode 100644
index 000000000..60de94002
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/claims.go
@@ -0,0 +1,115 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "encoding/json"
+ "strconv"
+ "time"
+)
+
+// Claims represents public claim values (as specified in RFC 7519).
+type Claims struct {
+ Issuer string `json:"iss,omitempty"`
+ Subject string `json:"sub,omitempty"`
+ Audience Audience `json:"aud,omitempty"`
+ Expiry NumericDate `json:"exp,omitempty"`
+ NotBefore NumericDate `json:"nbf,omitempty"`
+ IssuedAt NumericDate `json:"iat,omitempty"`
+ ID string `json:"jti,omitempty"`
+}
+
+// NumericDate represents date and time as the number of seconds since the
+// epoch, including leap seconds. Non-integer values can be represented
+// in the serialized format, but we round to the nearest second.
+type NumericDate int64
+
+// NewNumericDate constructs NumericDate from time.Time value.
+func NewNumericDate(t time.Time) NumericDate {
+ if t.IsZero() {
+ return NumericDate(0)
+ }
+
+ // While RFC 7519 technically states that NumericDate values may be
+ // non-integer values, we don't bother serializing timestamps in
+ // claims with sub-second accurancy and just round to the nearest
+ // second instead. Not convined sub-second accuracy is useful here.
+ return NumericDate(t.Unix())
+}
+
+// MarshalJSON serializes the given NumericDate into its JSON representation.
+func (n NumericDate) MarshalJSON() ([]byte, error) {
+ return []byte(strconv.FormatInt(int64(n), 10)), nil
+}
+
+// UnmarshalJSON reads a date from its JSON representation.
+func (n *NumericDate) UnmarshalJSON(b []byte) error {
+ s := string(b)
+
+ f, err := strconv.ParseFloat(s, 64)
+ if err != nil {
+ return ErrUnmarshalNumericDate
+ }
+
+ *n = NumericDate(f)
+ return nil
+}
+
+// Time returns time.Time representation of NumericDate.
+func (n NumericDate) Time() time.Time {
+ return time.Unix(int64(n), 0)
+}
+
+// Audience represents the recipents that the token is intended for.
+type Audience []string
+
+// UnmarshalJSON reads an audience from its JSON representation.
+func (s *Audience) UnmarshalJSON(b []byte) error {
+ var v interface{}
+ if err := json.Unmarshal(b, &v); err != nil {
+ return err
+ }
+
+ switch v := v.(type) {
+ case string:
+ *s = []string{v}
+ case []interface{}:
+ a := make([]string, len(v))
+ for i, e := range v {
+ s, ok := e.(string)
+ if !ok {
+ return ErrUnmarshalAudience
+ }
+ a[i] = s
+ }
+ *s = a
+ default:
+ return ErrUnmarshalAudience
+ }
+
+ return nil
+}
+
+func (s Audience) Contains(v string) bool {
+ for _, a := range s {
+ if a == v {
+ return true
+ }
+ }
+ return false
+}
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go
new file mode 100644
index 000000000..4cf97b54e
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/doc.go
@@ -0,0 +1,22 @@
+/*-
+ * Copyright 2017 Square Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/*
+
+Package jwt provides an implementation of the JSON Web Token standard.
+
+*/
+package jwt
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go
new file mode 100644
index 000000000..6507dfb28
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/errors.go
@@ -0,0 +1,50 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import "errors"
+
+// ErrUnmarshalAudience indicates that aud claim could not be unmarshalled.
+var ErrUnmarshalAudience = errors.New("square/go-jose/jwt: expected string or array value to unmarshal to Audience")
+
+// ErrUnmarshalNumericDate indicates that JWT NumericDate could not be unmarshalled.
+var ErrUnmarshalNumericDate = errors.New("square/go-jose/jwt: expected number value to unmarshal NumericDate")
+
+// ErrInvalidClaims indicates that given claims have invalid type.
+var ErrInvalidClaims = errors.New("square/go-jose/jwt: expected claims to be value convertible into JSON object")
+
+// ErrInvalidIssuer indicates invalid iss claim.
+var ErrInvalidIssuer = errors.New("square/go-jose/jwt: validation failed, invalid issuer claim (iss)")
+
+// ErrInvalidSubject indicates invalid sub claim.
+var ErrInvalidSubject = errors.New("square/go-jose/jwt: validation failed, invalid subject claim (sub)")
+
+// ErrInvalidAudience indicated invalid aud claim.
+var ErrInvalidAudience = errors.New("square/go-jose/jwt: validation failed, invalid audience claim (aud)")
+
+// ErrInvalidID indicates invalid jti claim.
+var ErrInvalidID = errors.New("square/go-jose/jwt: validation failed, invalid ID claim (jti)")
+
+// ErrNotValidYet indicates that token is used before time indicated in nbf claim.
+var ErrNotValidYet = errors.New("square/go-jose/jwt: validation failed, token not valid yet (nbf)")
+
+// ErrExpired indicates that token is used after expiry time indicated in exp claim.
+var ErrExpired = errors.New("square/go-jose/jwt: validation failed, token is expired (exp)")
+
+// ErrInvalidContentType indicated that token requires JWT cty header.
+var ErrInvalidContentType = errors.New("square/go-jose/jwt: expected content type to be JWT (cty header)")
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go
new file mode 100644
index 000000000..2155b2d27
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/jwt.go
@@ -0,0 +1,113 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import (
+ "gopkg.in/square/go-jose.v2"
+ "gopkg.in/square/go-jose.v2/json"
+ "strings"
+)
+
+// JSONWebToken represents a JSON Web Token (as specified in RFC7519).
+type JSONWebToken struct {
+ payload func(k interface{}) ([]byte, error)
+ Headers []jose.Header
+}
+
+type NestedJSONWebToken struct {
+ enc *jose.JSONWebEncryption
+ Headers []jose.Header
+}
+
+// Claims deserializes a JSONWebToken into dest using the provided key.
+func (t *JSONWebToken) Claims(key interface{}, dest ...interface{}) error {
+ b, err := t.payload(key)
+ if err != nil {
+ return err
+ }
+
+ for _, d := range dest {
+ if err := json.Unmarshal(b, d); err != nil {
+ return err
+ }
+ }
+
+ return nil
+}
+
+func (t *NestedJSONWebToken) Decrypt(decryptionKey interface{}) (*JSONWebToken, error) {
+ b, err := t.enc.Decrypt(decryptionKey)
+ if err != nil {
+ return nil, err
+ }
+
+ sig, err := ParseSigned(string(b))
+ if err != nil {
+ return nil, err
+ }
+
+ return sig, nil
+}
+
+// ParseSigned parses token from JWS form.
+func ParseSigned(s string) (*JSONWebToken, error) {
+ sig, err := jose.ParseSigned(s)
+ if err != nil {
+ return nil, err
+ }
+ headers := make([]jose.Header, len(sig.Signatures))
+ for i, signature := range sig.Signatures {
+ headers[i] = signature.Header
+ }
+
+ return &JSONWebToken{
+ payload: sig.Verify,
+ Headers: headers,
+ }, nil
+}
+
+// ParseEncrypted parses token from JWE form.
+func ParseEncrypted(s string) (*JSONWebToken, error) {
+ enc, err := jose.ParseEncrypted(s)
+ if err != nil {
+ return nil, err
+ }
+
+ return &JSONWebToken{
+ payload: enc.Decrypt,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
+
+// ParseSignedAndEncrypted parses signed-then-encrypted token from JWE form.
+func ParseSignedAndEncrypted(s string) (*NestedJSONWebToken, error) {
+ enc, err := jose.ParseEncrypted(s)
+ if err != nil {
+ return nil, err
+ }
+
+ contentType, _ := enc.Header.ExtraHeaders[jose.HeaderContentType].(string)
+ if strings.ToUpper(contentType) != "JWT" {
+ return nil, ErrInvalidContentType
+ }
+
+ return &NestedJSONWebToken{
+ enc: enc,
+ Headers: []jose.Header{enc.Header},
+ }, nil
+}
diff --git a/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go
new file mode 100644
index 000000000..fdcee371b
--- /dev/null
+++ b/vendor/gopkg.in/square/go-jose.v2/jwt/validation.go
@@ -0,0 +1,89 @@
+/*-
+ * Copyright 2016 Zbigniew Mandziejewicz
+ * Copyright 2016 Square, Inc.
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package jwt
+
+import "time"
+
+const (
+ // DefaultLeeway defines the default leeway for matching NotBefore/Expiry claims.
+ DefaultLeeway = 1.0 * time.Minute
+)
+
+// Expected defines values used for protected claims validation.
+// If field has zero value then validation is skipped.
+type Expected struct {
+ // Issuer matches the "iss" claim exactly.
+ Issuer string
+ // Subject matches the "sub" claim exactly.
+ Subject string
+ // Audience matches the values in "aud" claim, regardless of their order.
+ Audience Audience
+ // ID matches the "jti" claim exactly.
+ ID string
+ // Time matches the "exp" and "ebf" claims with leeway.
+ Time time.Time
+}
+
+// WithTime copies expectations with new time.
+func (e Expected) WithTime(t time.Time) Expected {
+ e.Time = t
+ return e
+}
+
+// Validate checks claims in a token against expected values.
+// A default leeway value of one minute is used to compare time values.
+func (c Claims) Validate(e Expected) error {
+ return c.ValidateWithLeeway(e, DefaultLeeway)
+}
+
+// ValidateWithLeeway checks claims in a token against expected values. A
+// custom leeway may be specified for comparing time values. You may pass a
+// zero value to check time values with no leeway, but you should not that
+// numeric date values are rounded to the nearest second and sub-second
+// precision is not supported.
+func (c Claims) ValidateWithLeeway(e Expected, leeway time.Duration) error {
+ if e.Issuer != "" && e.Issuer != c.Issuer {
+ return ErrInvalidIssuer
+ }
+
+ if e.Subject != "" && e.Subject != c.Subject {
+ return ErrInvalidSubject
+ }
+
+ if e.ID != "" && e.ID != c.ID {
+ return ErrInvalidID
+ }
+
+ if len(e.Audience) != 0 {
+ for _, v := range e.Audience {
+ if !c.Audience.Contains(v) {
+ return ErrInvalidAudience
+ }
+ }
+ }
+
+ if !e.Time.IsZero() && e.Time.Add(leeway).Before(c.NotBefore.Time()) {
+ return ErrNotValidYet
+ }
+
+ if !e.Time.IsZero() && e.Time.Add(-leeway).After(c.Expiry.Time()) {
+ return ErrExpired
+ }
+
+ return nil
+}