Skip to content

Commit

Permalink
Generic key addition (#160)
Browse files Browse the repository at this point in the history
* Add generic key addition methods

* Update ECDSA key

* Update ECDSA key

* Update RSA key

* Update HMAC

* Add `SigningAlgorithm` associatedtype to ECDSA curve

* Remove superfluous code

* Hide public enum
  • Loading branch information
ptoffy authored Apr 23, 2024
1 parent 1065f0b commit c095132
Show file tree
Hide file tree
Showing 21 changed files with 210 additions and 562 deletions.
2 changes: 1 addition & 1 deletion Sources/JWTKit/ECDSA/ECDSA.swift
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ public enum ECDSA: Sendable {}

public protocol ECDSAKey: Sendable {
associatedtype Curve: ECDSACurveType

var curve: ECDSACurve { get }
var parameters: ECDSAParameters? { get }
}
Expand Down
2 changes: 2 additions & 0 deletions Sources/JWTKit/ECDSA/ECDSACurveType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,8 @@
public protocol ECDSACurveType: Sendable {
associatedtype Signature: ECDSASignature
associatedtype PrivateKey: ECDSAPrivateKey
associatedtype SigningAlgorithm: ECDSASigningAlgorithm

static var curve: ECDSACurve { get }
static var byteRanges: (x: Range<Int>, y: Range<Int>) { get }
}
8 changes: 3 additions & 5 deletions Sources/JWTKit/ECDSA/ECDSASigner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,10 @@ import Foundation
struct ECDSASigner<Key: ECDSAKey>: JWTAlgorithm, CryptoSigner {
let privateKey: ECDSA.PrivateKey<Key.Curve>?
let publicKey: ECDSA.PublicKey<Key.Curve>
let algorithm: DigestAlgorithm
public let name: String
let algorithm: DigestAlgorithm = Key.Curve.SigningAlgorithm.digestAlgorithm
let name: String = Key.Curve.SigningAlgorithm.name

init(key: Key, algorithm: DigestAlgorithm, name: String) {
init(key: Key) {
switch key {
case let privateKey as ECDSA.PrivateKey<Key.Curve>:
self.privateKey = privateKey
Expand All @@ -18,8 +18,6 @@ struct ECDSASigner<Key: ECDSAKey>: JWTAlgorithm, CryptoSigner {
// This should never happen
fatalError("Unexpected key type: \(type(of: key))")
}
self.algorithm = algorithm
self.name = name
}

func sign(_ plaintext: some DataProtocol) throws -> [UInt8] {
Expand Down
4 changes: 4 additions & 0 deletions Sources/JWTKit/ECDSA/ECDSASigningAlgorithm.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
public protocol ECDSASigningAlgorithm {
static var name: String { get }
static var digestAlgorithm: DigestAlgorithm { get }
}
88 changes: 7 additions & 81 deletions Sources/JWTKit/ECDSA/JWTKeyCollection+ECDSA.swift
Original file line number Diff line number Diff line change
@@ -1,88 +1,16 @@
import Crypto

public extension JWTKeyCollection {
/// Adds an ES256 key to the collection.
///
/// This method configures and adds an ES256 (ECDSA using P-256 and SHA-256) key to the collection.
///
/// Example Usage:
/// ```
/// let collection = await JWTKeyCollection()
/// .addES256(key: myES256Key)
/// ```
///
/// - Parameters:
/// - key: The ``ES256Key`` to be used for signing. This key should be securely stored and not exposed.
/// - kid: An optional ``JWKIdentifier`` (Key ID). If provided, this identifier will be used in the JWT `kid`
/// header field to identify the key.
/// - jsonEncoder: An optional custom JSON encoder conforming to ``JWTJSONEncoder``, used for encoding JWTs.
/// If `nil`, a default encoder is used.
/// - jsonDecoder: An optional custom JSON decoder conforming to ``JWTJSONDecoder``, used for decoding JWTs.
/// If `nil`, a default decoder is used.
/// - Returns: The same instance of the collection (`Self`), which allows for method chaining.
@discardableResult
func addES256<Key: ECDSAKey>(
key: Key,
kid: JWKIdentifier? = nil,
parser: some JWTParser = DefaultJWTParser(),
serializer: some JWTSerializer = DefaultJWTSerializer()
) -> Self
where Key.Curve == P256
{
add(.init(
algorithm: ECDSASigner(key: key, algorithm: .sha256, name: "ES256"),
parser: parser,
serializer: serializer
), for: kid)
}

/// Adds an ES384 key to the collection.
///
/// This method configures and adds an ES384(ECDSA using P-384 and SHA-384) key to the collection.
///
/// Example Usage:
/// ```
/// let collection = await JWTKeyCollection()
/// .addES384(key: myES384Key)
/// ```
///
/// - Parameters:
/// - key: The ``ES384Key`` to be used for signing. This key should be securely stored and not exposed.
/// - kid: An optional ``JWKIdentifier`` (Key ID). If provided, this identifier will be used in the JWT `kid`
/// header field to identify the key.
/// - jsonEncoder: An optional custom JSON encoder conforming to ``JWTJSONEncoder``, used for encoding JWTs.
/// If `nil`, a default encoder is used.
/// - jsonDecoder: An optional custom JSON decoder conforming to ``JWTJSONDecoder``, used for decoding JWTs.
/// If `nil`, a default decoder is used.
/// - Returns: The same instance of the collection (`Self`), which allows for method chaining.
@discardableResult
func addES384<Key: ECDSAKey>(
key: Key,
kid: JWKIdentifier? = nil,
parser: some JWTParser = DefaultJWTParser(),
serializer: some JWTSerializer = DefaultJWTSerializer()
) -> Self
where Key.Curve == P384
{
add(.init(
algorithm: ECDSASigner(key: key, algorithm: .sha384, name: "ES384"),
parser: parser,
serializer: serializer
), for: kid)
}

/// Adds an ES512 key to the collection.
///
/// This method configures and adds an ES512 (ECDSA using P-521 and SHA-512) key to the collection.
/// Adds an ECDSA key to the collection.
///
/// Example Usage:
/// ```
/// let collection = await JWTKeyCollection()
/// .addES512(key: myES512Key)
/// .addECDSA(key: myECDSAKey)
/// ```
///
/// - Parameters:
/// - key: The ``ES512Key`` to be used for signing. This key should be securely stored and not exposed.
/// - key: The ``ECDSAKey`` to be used for signing. This key should be securely stored and not exposed.
/// - kid: An optional ``JWKIdentifier`` (Key ID). If provided, this identifier will be used in the JWT `kid`
/// header field to identify the key.
/// - jsonEncoder: An optional custom JSON encoder conforming to ``JWTJSONEncoder``, used for encoding JWTs.
Expand All @@ -91,16 +19,14 @@ public extension JWTKeyCollection {
/// If `nil`, a default decoder is used.
/// - Returns: The same instance of the collection (`Self`), which allows for method chaining.
@discardableResult
func addES512<Key: ECDSAKey>(
key: Key,
func addECDSA(
key: some ECDSAKey,
kid: JWKIdentifier? = nil,
parser: some JWTParser = DefaultJWTParser(),
serializer: some JWTSerializer = DefaultJWTSerializer()
) -> Self
where Key.Curve == P521
{
) -> Self {
add(.init(
algorithm: ECDSASigner(key: key, algorithm: .sha512, name: "ES512"),
algorithm: ECDSASigner(key: key),
parser: parser,
serializer: serializer
), for: kid)
Expand Down
5 changes: 5 additions & 0 deletions Sources/JWTKit/ECDSA/P256+CurveType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ extension P256: ECDSACurveType, @unchecked Sendable {
/// - The X coordinate spans bytes 1 through 32 (byte 0 is for the prefix).
/// - The Y coordinate spans bytes 33 through 64.
public static let byteRanges: (x: Range<Int>, y: Range<Int>) = (1 ..< 33, 33 ..< 65)

public struct SigningAlgorithm: ECDSASigningAlgorithm {
public static let name = "ES256"
public static let digestAlgorithm: DigestAlgorithm = .sha256
}
}

// TODO: Remove @unchecked Sendable when Crypto is updated to use Sendable
Expand Down
5 changes: 5 additions & 0 deletions Sources/JWTKit/ECDSA/P384+CurveType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,11 @@ extension P384: ECDSACurveType, @unchecked Sendable {
/// - The X coordinate spans bytes 1 through 48.
/// - The Y coordinate spans bytes 49 through 96.
public static let byteRanges: (x: Range<Int>, y: Range<Int>) = (1 ..< 49, 49 ..< 97)

public enum SigningAlgorithm: ECDSASigningAlgorithm {
public static let name = "ES384"
public static let digestAlgorithm: DigestAlgorithm = .sha384
}
}

// TODO: Remove @unchecked Sendable when Crypto is updated to use Sendable
Expand Down
5 changes: 5 additions & 0 deletions Sources/JWTKit/ECDSA/P521+CurveType.swift
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,11 @@ extension P521: ECDSACurveType, @unchecked Sendable {
/// - The X coordinate spans bytes 1 through 66.
/// - The Y coordinate spans bytes 67 through 132.
public static let byteRanges: (x: Range<Int>, y: Range<Int>) = (1 ..< 67, 67 ..< 133)

public enum SigningAlgorithm: ECDSASigningAlgorithm {
public static let name = "ES512"
public static let digestAlgorithm: DigestAlgorithm = .sha512
}
}

// TODO: Remove @unchecked Sendable when Crypto is updated to use Sendable
Expand Down
Loading

0 comments on commit c095132

Please sign in to comment.