Skip to content

Use current time by default during validation in RFC5280Policy and OCSPPolicy - #266

Merged
Lukasa merged 1 commit into
apple:mainfrom
aryan-25:add-fixed-validation-time
Jul 15, 2025
Merged

Use current time by default during validation in RFC5280Policy and OCSPPolicy#266
Lukasa merged 1 commit into
apple:mainfrom
aryan-25:add-fixed-validation-time

Conversation

@aryan-25

@aryan-25 aryan-25 commented Jul 15, 2025

Copy link
Copy Markdown
Contributor

Motivation:

The initializers of RFC5280Policy and OCSPPolicy require a validation time as an argument. If this argument is set to Date.now, the timestamp would be captured at the point of initialization. As such, if the validation method is invoked on the policy instance long after initialization, the timestamp can become stale at that point. This means that the validation function could incorrectly determine a certificate (RFC5280Policy) or an OCSP response (OCSPPolicy) to be valid, when it has actually expired by the time the validation is performed.

This is not an issue in common use-cases where the policy is initialized (with validationTime set to .now) inside a PolicyBuilder closure, because the closure is evaluated at the point of validation. However, some use-cases may initialize the policy independently; the issue can potentially arise here.

Modifications:

This patch deprecates the current initializers of RFC5280Policy and OCSPPolicy to guide users toward using a safer initializer which requires a fixedValidationTime: Date? argument:

  • Passing nil (default) will lead to the current time being obtained at the point of validation. This will prevent the bug described above.
  • Passing a specific Date instance preserves the ability to validate against a fixed point in time, i.e. a time in the past or future, but not the current time.

Result:

Users now have the option to use a safer initializer to construct RFC5280Policy and OCSPPolicy.

…dation* and used as the validation time in `RFC5280Policy` and `OCSPPolicy`.
@aryan-25 aryan-25 changed the title Add support for the current time to be obtained *at the point of validation* in RFC5280Policy and OCSPPolicy. Add support for the current time to be obtained at the point of validation in RFC5280Policy and OCSPPolicy. Jul 15, 2025

@simonjbeaumont simonjbeaumont left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This LGTM—nice work 👏

@simonjbeaumont simonjbeaumont added the 🆕 semver/minor Adds new public API. label Jul 15, 2025
@aryan-25 aryan-25 changed the title Add support for the current time to be obtained at the point of validation in RFC5280Policy and OCSPPolicy. Use current time during validation in RFC5280Policy and OCSPPolicy Jul 15, 2025
@aryan-25 aryan-25 changed the title Use current time during validation in RFC5280Policy and OCSPPolicy Use current time by default during validation in RFC5280Policy and OCSPPolicy Jul 15, 2025

@Lukasa Lukasa left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice, thank you! ✨

@Lukasa
Lukasa merged commit a78498b into apple:main Jul 15, 2025
35 of 36 checks passed
aryan-25 added a commit to aryan-25/swift-certificates that referenced this pull request Oct 8, 2025
New initializers for `ExpiryPolicy`, `RFC5280Policy`, and `OCSPVerifierPolicy` were introduced in apple#266. These initializers were added to prevent users from specifying `Date.now` as the time to validate certificate expiry against. For context, the problem was that `Date.now` would be evaluated at initialization, but validation could occur at a later stage, at which point the validation time would become stale.

These initializers had a `fixedValidationTime: Date? = nil` argument, where a non-`nil` value denoted a *fixed* time to validate against, and a `nil` value denoted that the current time, evaluated at the point of validation, would be used for validation (the common case). Although the dangers of specifying `fixedValidationTime = .now` were documented, in practice, it is very easy for users to miss this and continue using the API in an incorrect way.

Given that most users will validate certificate expiry against the current time, we want to default to this and remove the validation time argument from the public initializers entirely. Users who want to specify a fixed predetermined time must explicitly opt-in by using `@_spi(FixedValidationTime)` to access the initializers that have a *non-optional* `fixedValidationTime` argument.

Modifications:
- Deprecated the initializers of `RFC5280Policy` and `OCSPVerifierPolicy` and introduced two new initializers for each: one without a validation time argument (common case), and one with a *non-optional* validation time argument backed behind an SPI
- Updated the initializers of `ExpiryPolicy` (an internal type)
- Additional changes:
  - Updated test cases to use new initializers
  - Updated examples in documentation to use new initializers

Result:
`RFC5280Policy` and `OCSPVerifierPolicy` can be used more safely
Lukasa pushed a commit that referenced this pull request Oct 13, 2025
### Motivation:
New initializers for `ExpiryPolicy`, `RFC5280Policy`, and
`OCSPVerifierPolicy` were introduced in #266. These initializers were
added to prevent users from specifying `Date.now` as the time to
validate certificate expiry against. For context, the problem was that
`Date.now` would be evaluated at initialization, but validation could
occur at a later stage, at which point the validation time would become
stale.

These initializers had a `fixedValidationTime: Date? = nil` argument,
where a non-`nil` value denoted a *fixed* time to validate against, and
a `nil` value denoted that the current time, evaluated at the point of
validation, would be used for validation (the common case). Although the
dangers of specifying `fixedValidationTime = .now` were documented, in
practice, it is very easy for users to miss this and continue using the
API in an incorrect way.

Given that most users will validate certificate expiry against the
current time, we want to default to this and remove the validation time
argument from the public initializers entirely. Users who want to
specify a fixed predetermined time **must explicitly opt-in** by using
`@_spi(FixedExpiryValidationTime)` to access the initializers that have
a *non-optional* `fixedExpiryValidationTime` argument.

### Modifications:
- Deprecated the initializers of `RFC5280Policy` and
`OCSPVerifierPolicy` and introduced two new initializers for each: one
without a validation time argument (common case), and one with a
*non-optional* validation time argument guarded behind an SPI
- Updated the initializers of `ExpiryPolicy` (an internal type)
- Additional changes:
  - Updated test cases to use new initializers
  - Updated examples in documentation to use new initializers

### Result:
`RFC5280Policy` and `OCSPVerifierPolicy` can be used more safely
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

🆕 semver/minor Adds new public API.

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants