Skip to content

Support generating CMS with trusted signing time - #288

Open
emaloney wants to merge 1 commit into
apple:mainfrom
emaloney:public-cms
Open

emaloney wants to merge 1 commit into
apple:mainfrom
emaloney:public-cms

Conversation

@emaloney

@emaloney emaloney commented Dec 4, 2025

Copy link
Copy Markdown
Contributor

Motivation:

We had a need to generate CMS structures with an embedded trusted timestamp that indicates the time at which a signing operation was performed, and discovered that this was not supported by the existing Swift Certificates implementation.

Modifications:

To address this need, we adapted existing functionality, adding several functions allowing the signing time to be embedded in a CMS structure.

In order to make this work, we also needed to add public access to several entities in the code.

Result:

This new CMS SPI allows Swift Certificates to be used for generating trusted timestamp signatures from CMS structures that embed the signing time, such as those for codesigning.

We have been using this exact same code in a high-volume production environment for several months, and can confirm that the CMS structures generated by this are usable for creating valid signatures.

@Lukasa Lukasa added the 🆕 semver/minor Adds new public API. label Dec 15, 2025
/// ```
@usableFromInline
struct CMSEncapsulatedContentInfo: DERImplicitlyTaggable, BERImplicitlyTaggable, Hashable, Sendable {
public struct CMSEncapsulatedContentInfo: DERImplicitlyTaggable, BERImplicitlyTaggable, Hashable, Sendable {

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.

Let's keep this type behind the same SPI as the rest of the CMS stuff.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change is not present in the latest version of this PR.

struct CMSVersion: RawRepresentable, Hashable, Sendable {
@usableFromInline
var rawValue: Int
public struct CMSVersion: RawRepresentable, Hashable, Sendable {

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.

Let's keep this behind the same SPI as the rest of the CMS stuff.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change is not present in the latest version of this PR.


@usableFromInline
var parameters: ASN1Any?
public private(set) var parameters: ASN1Any?

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.

I don't think there's any particular reason to make these private(set).

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Changed to just plain public in latest version of this PR.

Comment thread Sources/X509/Digests.swift Outdated
@usableFromInline
@available(macOS 10.15, iOS 13, watchOS 6, tvOS 13, macCatalyst 13, visionOS 1.0, *)
enum Digest: Sendable {
public enum Digest: Sendable {

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.

Can we avoid making this enum public directly? It makes it very hard to evolve. Preferably we'd wrap it in a struct.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

I see that since my original PR was opened, Digest has gone from an enum to a struct, and the cases you were (presumably) worried about exposing are now hidden in the inner enum type Backing.

Am I correct in assuming that making Digest and the computeDigest(for:using:) function public is no longer a concern given this refactoring?


@_spi(CMS)
@inlinable
public static func createSigningTimeASN1(signingTime: Date) throws -> Data {

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.

Is this particular function pulling its weight? It isn't used by anything inside this module, and it does something quite specific, so I'm inclined to want to pull it out of this module and keep it in the code that uses it.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

This change is not present in the latest version of this PR.

@Lukasa

Lukasa commented Dec 15, 2025

Copy link
Copy Markdown
Contributor

Can I also ask that we provide some unit tests for new things with public API surface? Just to make sure they're doing what is expected.

@emaloney
emaloney force-pushed the public-cms branch 2 times, most recently from 3136a76 to 2ec894c Compare July 16, 2026 17:34
@emaloney

Copy link
Copy Markdown
Contributor Author

@Lukasa I've addressed the earlier PR comments & rebased on top of the latest swift-certificates. In the process of adding unit tests, I noticed an apparent bug (issue #306) that would need to be addressed first (PR #307).

In the meantime, I rebased #288 on top of #307 so the new tests would pass. Once #307 gets merged, I'll rebase this on the latest main again, and this PR should be ready for (final?) re-review.

@emaloney
emaloney force-pushed the public-cms branch 2 times, most recently from f794749 to ad593b9 Compare August 27, 2026 19:26

@_spi(CMS)
@inlinable
public static func getSignedAttributesBytes<Data: DataProtocol>(

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.

Is there a reason this needs to be an SPI?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

No technical reason; I will remove that.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

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

Pushed an update that removes the @_spi(CMS) — note that the signWithTrustedTimestamp function is still marked as SPI. I left that as-is b/c I wasn't sure if that signature was something we were ready to formalize & make available publicly.

In order to make `pkg-signer*` work, we needed to make some changes to `swift-certificates` exposing certain entities publicly and adding trusted timestamp support to CMS.
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.

2 participants