Skip to content

Add PEM representation init and property to EdDSA keys#237

Merged
ptoffy merged 5 commits into
mainfrom
eddsa-pem
Oct 30, 2025
Merged

Add PEM representation init and property to EdDSA keys#237
ptoffy merged 5 commits into
mainfrom
eddsa-pem

Conversation

@ptoffy

@ptoffy ptoffy commented Oct 27, 2025

Copy link
Copy Markdown
Member

This adds PEM representation support for EdDSA keys, it's now possible to create EdDSA keys with PEM and get their PEM representation.
This also adds the missing Equatable conformance to EdDSA keys and fixes some tests.

@codecov

codecov Bot commented Oct 27, 2025

Copy link
Copy Markdown

Codecov Report

✅ All modified and coverable lines are covered by tests.
✅ Project coverage is 82.00%. Comparing base (0ea1fd2) to head (f5f0de3).
⚠️ Report is 1 commits behind head on main.

Additional details and impacted files
@@            Coverage Diff             @@
##             main     #237      +/-   ##
==========================================
- Coverage   82.13%   82.00%   -0.13%     
==========================================
  Files          61       61              
  Lines        1422     1434      +12     
==========================================
+ Hits         1168     1176       +8     
- Misses        254      258       +4     
Files with missing lines Coverage Δ
Sources/JWTKit/EdDSA/EdDSA.swift 88.88% <100.00%> (-6.12%) ⬇️
Sources/JWTKit/EdDSA/EdDSASigner.swift 86.36% <100.00%> (-1.14%) ⬇️
🚀 New features to boost your workflow:
  • ❄️ Test Analytics: Detect flaky tests, report on failures, and find test suite problems.

@tkrajacic tkrajacic 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 should now refer to the latest swift-crypto release 4.1.0 that includes the required APIs

Comment on lines -27 to +29
let keys = try await JWTKeyCollection()
.add(eddsa: EdDSA.PublicKey(x: eddsaPublicKeyBase64, curve: .ed25519), kid: "public")
.add(eddsa: EdDSA.PrivateKey(d: eddsaPrivateKeyBase64, curve: .ed25519), kid: "private")
let signingCollection = try await JWTKeyCollection()
.add(eddsa: EdDSA.PrivateKey(d: eddsaPrivateKeyBase64, curve: .ed25519))
let verifyingCollection = try await JWTKeyCollection()
.add(eddsa: EdDSA.PublicKey(x: eddsaPublicKeyBase64, curve: .ed25519))

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Why have these tests changed?

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

Because before it seemed like it was using the public key to verify but it wasn't. It wasn't wrong per se but a bit misleading

/// In JWT, EdDSA public keys are represented as a single x-coordinate and are used for verifying signatures.
/// Currently, only the ``EdDSACurve/ed25519`` curve is supported.
public struct PublicKey: EdDSAKey {
public struct PublicKey: EdDSAKey, Equatable {

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.

Out of curiosity, why can't we also make them Hashable?

@ptoffy ptoffy Oct 30, 2025

Copy link
Copy Markdown
Member Author

Choose a reason for hiding this comment

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

We could if there's a use case for it. I wouldn't have made them Equatable either if

  1. We didn't need it in the tests
  2. RSA and ECDSA weren't also already Equatable

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 think it is generally useful to make all your types Hashable, if they are. We can't foresee every usecase, but if someone wants to put them in a Set… there you go.

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 think all keys should conform to Hashable because… they are

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

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

Equatable means "represents a value which can be meaningfully compared for sameness with another value".
Identifiable means "represents a value which has unique identity even if it compares the same with another value".
Hashable means "is Equatable and represents a value which can be quickly and efficiently reduced to a hashing key with appropriate properties".

There are very few things which are semantically Equatable without being semantically Hashable. In Swift, "not Hashable" is effectively used to mean "this type should not be treated as having sufficiently unique representation to be used as a dictionary key or to be uniqued by set inclusion".

As regards asymmetric encryption keys, Hashable is by this definition not semantically appropriate. You should not be keying a dictionary by a crypto key or trying to construct unique sets of crypto keys. In the infosec world, these would definitely qualify as potentially dangerous behaviors (in terms of reducing security).

@ptoffy ptoffy requested a review from 0xTim October 30, 2025 15:02
@ptoffy ptoffy requested a review from gwynne as a code owner October 30, 2025 15:41
@ptoffy ptoffy merged commit b5f82fb into main Oct 30, 2025
15 checks passed
@ptoffy ptoffy deleted the eddsa-pem branch October 30, 2025 15:59
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants