Skip to content

Commit

Permalink
feat: JWK thumbprint URI
Browse files Browse the repository at this point in the history
feat: Arabic, Spanish and French error message translations
fix: JWK parsing using AnyJSONWebKey throws error
fix: JWS decode failure when protected header or payload is empty
chore: Direct (dynamicMember) getter/setter for ReadWriteLockedValue
chore: Direct (dynamicMember) getter/setter for TypedProtectedWebContainer/JSONProtectedWebContainer
  • Loading branch information
amosavian committed Apr 27, 2024
1 parent 20e4619 commit 3fbf686
Show file tree
Hide file tree
Showing 23 changed files with 498 additions and 88 deletions.
34 changes: 33 additions & 1 deletion Sources/JWSETKit/Base/ProtectedContainer.swift
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ extension JSONEncoder {
}

/// Data value that must be protected by JWS.
public protocol ProtectedWebContainer: Hashable, Encodable, Sendable {
public protocol ProtectedWebContainer: Hashable, Encodable, CustomDebugStringConvertible, Sendable {
/// Signed data.
var encoded: Data { get set }

Expand All @@ -30,6 +30,10 @@ public protocol ProtectedWebContainer: Hashable, Encodable, Sendable {
}

extension ProtectedWebContainer {
public var debugDescription: String {
"(Protected: \(encoded.urlBase64EncodedString())"
}

public static func == (lhs: Self, rhs: Self) -> Bool {
lhs.encoded == rhs.encoded
}
Expand Down Expand Up @@ -62,6 +66,7 @@ extension ProtectedWebContainer {
}
}

@dynamicMemberLookup
public protocol TypedProtectedWebContainer<Container>: ProtectedWebContainer {
associatedtype Container
/// Parsed value of data.
Expand All @@ -73,6 +78,33 @@ public protocol TypedProtectedWebContainer<Container>: ProtectedWebContainer {
init(value: Container) throws
}

extension TypedProtectedWebContainer {
public var debugDescription: String {
let valueDescription: String
if let value = value as? (any CustomDebugStringConvertible) {
valueDescription = value.debugDescription
} else {
valueDescription = "\(value)"
}
return "(Protected: \(encoded.urlBase64EncodedString()), Value: \(valueDescription))"
}

public subscript<T>(dynamicMember keyPath: KeyPath<Container, T>) -> T {
get {
value[keyPath: keyPath]
}
}

public subscript<T>(dynamicMember keyPath: WritableKeyPath<Container, T>) -> T {
get {
value[keyPath: keyPath]
}
mutating set {
value[keyPath: keyPath] = newValue
}
}
}

public struct ProtectedDataWebContainer: ProtectedWebContainer, Codable {
public var encoded: Data

Expand Down
6 changes: 3 additions & 3 deletions Sources/JWSETKit/Cryptography/Algorithms/Algorithms.swift
Original file line number Diff line number Diff line change
Expand Up @@ -145,11 +145,11 @@ public struct JSONWebKeyCurve: StringRepresentable {
}

extension JSONWebKeyCurve {
private static let keySizes: ReadWriteLockedValue<[Self: Int]> = .init([
private static let keySizes: ReadWriteLockedValue<[Self: Int]> = [
.p256: 32, .ed25519: 32, .x25519: 32,
.p384: 48,
.p521: 66,
])
]

/// Key size in bytes.
public var coordinateSize: Int? {
Expand All @@ -167,7 +167,7 @@ extension JSONWebKeyCurve {
/// - curve: Curve name.
/// - keySize: Uncompressed key size in bytes.
public static func register(_ curve: Self, keySize: Int) async {
keySizes.wrappedValue[curve] = keySize
keySizes[curve] = keySize
}
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/JWSETKit/Cryptography/Algorithms/Compression.swift
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,11 @@ public struct JSONWebCompressionAlgorithm: StringRepresentable {

extension JSONWebCompressionAlgorithm {
#if canImport(Compression)
private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = .init([
private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [
.deflate: AppleCompressor<DeflateCompressionCodec>.self,
])
]
#else
private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = .init([:])
private static let compressors: ReadWriteLockedValue<[Self: any JSONWebCompressor.Type]> = [:]
#endif

/// Returns provided compressor for this algorithm.
Expand All @@ -68,7 +68,7 @@ extension JSONWebCompressionAlgorithm {
/// - algorithm: Compression algorithm.
/// - compressor: Compressor instance.
public static func register<C>(_ algorithm: Self, compressor: C.Type) where C: JSONWebCompressor {
compressors.wrappedValue[algorithm] = compressor
compressors[algorithm] = compressor
}
}

Expand Down
12 changes: 6 additions & 6 deletions Sources/JWSETKit/Cryptography/Algorithms/ContentEncryption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,16 +22,16 @@ public struct JSONWebContentEncryptionAlgorithm: JSONWebAlgorithm {
}

extension JSONWebContentEncryptionAlgorithm {
private static let keyRegistryClasses: ReadWriteLockedValue<[Self: any JSONWebSealingKey.Type]> = .init([
private static let keyRegistryClasses: ReadWriteLockedValue<[Self: any JSONWebSealingKey.Type]> = [
.aesEncryptionGCM128: JSONWebKeyAESGCM.self,
.aesEncryptionGCM192: JSONWebKeyAESGCM.self,
.aesEncryptionGCM256: JSONWebKeyAESGCM.self,
.aesEncryptionCBC128SHA256: JSONWebKeyAESCBCHMAC.self,
.aesEncryptionCBC192SHA384: JSONWebKeyAESCBCHMAC.self,
.aesEncryptionCBC256SHA512: JSONWebKeyAESCBCHMAC.self,
])
]

private static let keyLengths: ReadWriteLockedValue<[Self: SymmetricKeySize]> = .init([
private static let keyLengths: ReadWriteLockedValue<[Self: SymmetricKeySize]> = [
.aesEncryptionGCM128: .bits128,
.aesEncryptionGCM192: .bits192,
.aesEncryptionGCM256: .bits256,
Expand All @@ -41,7 +41,7 @@ extension JSONWebContentEncryptionAlgorithm {
.aesEncryptionCBC128SHA256: .bits128 * 2,
.aesEncryptionCBC192SHA384: .bits192 * 2,
.aesEncryptionCBC256SHA512: .bits256 * 2,
])
]

/// Key type, either RSA, Elliptic curve, Symmetric, etc.
public var keyType: JSONWebKeyType? {
Expand Down Expand Up @@ -74,8 +74,8 @@ extension JSONWebContentEncryptionAlgorithm {
keyClass: KT.Type,
keyLength: SymmetricKeySize
) where KT: JSONWebSealingKey {
keyRegistryClasses.wrappedValue[algorithm] = keyClass
keyLengths.wrappedValue[algorithm] = keyLength
keyRegistryClasses[algorithm] = keyClass
keyLengths[algorithm] = keyLength
}
}

Expand Down
40 changes: 20 additions & 20 deletions Sources/JWSETKit/Cryptography/Algorithms/KeyEncryption.swift
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,7 @@ extension JSONWebKeyEncryptionAlgorithm {
_ cek: inout Data
) throws -> Void

private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebEncryptingKey.Type, private: any JSONWebDecryptingKey.Type)]> = .init([
private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebEncryptingKey.Type, private: any JSONWebDecryptingKey.Type)]> = [
.direct: (JSONWebDirectKey.self, JSONWebDirectKey.self),
.aesKeyWrap128: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self),
.aesKeyWrap192: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self),
Expand All @@ -56,9 +56,9 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self),
.ecdhEphemeralStaticAESKeyWrap192: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self),
.ecdhEphemeralStaticAESKeyWrap256: (JSONWebKeyAESKW.self, JSONWebKeyAESKW.self),
])
]

private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = .init([
private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = [
.direct: .symmetric,
.aesKeyWrap128: .symmetric,
.aesKeyWrap192: .symmetric,
Expand All @@ -78,9 +78,9 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: .symmetric,
.ecdhEphemeralStaticAESKeyWrap192: .symmetric,
.ecdhEphemeralStaticAESKeyWrap256: .symmetric,
])
]

private static let keyLengths: ReadWriteLockedValue<[Self: Int]> = .init([
private static let keyLengths: ReadWriteLockedValue<[Self: Int]> = [
.aesKeyWrap128: SymmetricKeySize.bits128.bitCount,
.aesKeyWrap192: SymmetricKeySize.bits192.bitCount,
.aesKeyWrap256: SymmetricKeySize.bits256.bitCount,
Expand All @@ -98,9 +98,9 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: SymmetricKeySize.bits128.bitCount,
.ecdhEphemeralStaticAESKeyWrap192: SymmetricKeySize.bits192.bitCount,
.ecdhEphemeralStaticAESKeyWrap256: SymmetricKeySize.bits256.bitCount,
])
]

private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = .init([
private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = [
.aesKeyWrap128: SHA256.self,
.aesKeyWrap192: SHA384.self,
.aesKeyWrap256: SHA512.self,
Expand All @@ -114,9 +114,9 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: SHA256.self,
.ecdhEphemeralStaticAESKeyWrap192: SHA256.self,
.ecdhEphemeralStaticAESKeyWrap256: SHA256.self,
])
]

private static let encryptedKeyHandlers: ReadWriteLockedValue<[Self: EncryptedKeyHandler]> = .init([
private static let encryptedKeyHandlers: ReadWriteLockedValue<[Self: EncryptedKeyHandler]> = [
.aesGCM128KeyWrap: aesGCMKeyWrapEncryptedKey,
.aesGCM192KeyWrap: aesGCMKeyWrapEncryptedKey,
.aesGCM256KeyWrap: aesGCMKeyWrapEncryptedKey,
Expand All @@ -127,9 +127,9 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: ecdhEsEncryptedKey,
.ecdhEphemeralStaticAESKeyWrap192: ecdhEsEncryptedKey,
.ecdhEphemeralStaticAESKeyWrap256: ecdhEsEncryptedKey,
])
]

private static let decryptionMutators: ReadWriteLockedValue<[Self: DecryptionMutatorHandler]> = .init([
private static let decryptionMutators: ReadWriteLockedValue<[Self: DecryptionMutatorHandler]> = [
.direct: directDecryptionMutator,
.aesGCM128KeyWrap: aesgcmDecryptionMutator,
.aesGCM192KeyWrap: aesgcmDecryptionMutator,
Expand All @@ -141,7 +141,7 @@ extension JSONWebKeyEncryptionAlgorithm {
.ecdhEphemeralStaticAESKeyWrap128: ecdhEsDecryptionMutator,
.ecdhEphemeralStaticAESKeyWrap192: ecdhEsDecryptionMutator,
.ecdhEphemeralStaticAESKeyWrap256: ecdhEsDecryptionMutator,
])
]

/// Key type, either RSA, Elliptic curve, Symmetric, etc.
public var keyType: JSONWebKeyType? {
Expand Down Expand Up @@ -199,12 +199,12 @@ extension JSONWebKeyEncryptionAlgorithm {
encryptedKeyHandler: EncryptedKeyHandler?,
decryptionMutating: DecryptionMutatorHandler?
) where Public: JSONWebEncryptingKey, Private: JSONWebDecryptingKey {
keyRegistryClasses.wrappedValue[algorithm] = (publicKeyClass, privateKeyClass)
keyTypes.wrappedValue[algorithm] = type
keyLengths.wrappedValue[algorithm] = keyLengthInBits
hashFunctions.wrappedValue[algorithm] = hashFunction
encryptedKeyHandlers.wrappedValue[algorithm] = encryptedKeyHandler
decryptionMutators.wrappedValue[algorithm] = decryptionMutating
keyRegistryClasses[algorithm] = (publicKeyClass, privateKeyClass)
keyTypes[algorithm] = type
keyLengths[algorithm] = keyLengthInBits
hashFunctions[algorithm] = hashFunction
encryptedKeyHandlers[algorithm] = encryptedKeyHandler
decryptionMutators[algorithm] = decryptionMutating
}

/// Generates new random key with minimum key length.
Expand Down Expand Up @@ -445,8 +445,8 @@ extension JSONWebAlgorithm where Self == JSONWebKeyEncryptionAlgorithm {
/// **Key Management**: PBES2 with HMAC SHA-512 and "A256KW" wrapping.
public static var pbes2hmac512: Self { "PBES2-HS512+A256KW" }

static func pbes2hmac(bitCount: Int) -> Self {
.init(rawValue: "PBES2-HS\(bitCount)+A\(bitCount / 2)KW")
static func pbes2hmac(keyBitCount: Int) -> Self {
.init(rawValue: "PBES2-HS\(keyBitCount * 2)+A\(keyBitCount)KW")
}

// **Key Management**:ECDH-ES using Concat KDF and CEK wrapped with "A128KW".
Expand Down
24 changes: 12 additions & 12 deletions Sources/JWSETKit/Cryptography/Algorithms/Signature.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@ public struct JSONWebSignatureAlgorithm: JSONWebAlgorithm {
}

extension JSONWebSignatureAlgorithm {
private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebValidatingKey.Type, private: any JSONWebSigningKey.Type)]> = .init([
private static let keyRegistryClasses: ReadWriteLockedValue < [Self: (public: any JSONWebValidatingKey.Type, private: any JSONWebSigningKey.Type)]> = [
.none: (JSONWebDirectKey.self, JSONWebDirectKey.self),
.hmacSHA256: (JSONWebKeyHMAC<SHA256>.self, JSONWebKeyHMAC<SHA256>.self),
.hmacSHA384: (JSONWebKeyHMAC<SHA384>.self, JSONWebKeyHMAC<SHA384>.self),
Expand All @@ -37,9 +37,9 @@ extension JSONWebSignatureAlgorithm {
.rsaSignaturePKCS1v15SHA256: (JSONWebRSAPublicKey.self, JSONWebRSAPrivateKey.self),
.rsaSignaturePKCS1v15SHA384: (JSONWebRSAPublicKey.self, JSONWebRSAPrivateKey.self),
.rsaSignaturePKCS1v15SHA512: (JSONWebRSAPublicKey.self, JSONWebRSAPrivateKey.self),
])
]

private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = .init([
private static let keyTypes: ReadWriteLockedValue<[Self: JSONWebKeyType]> = [
.none: .symmetric,
.hmacSHA256: .symmetric,
.hmacSHA384: .symmetric,
Expand All @@ -54,14 +54,14 @@ extension JSONWebSignatureAlgorithm {
.rsaSignaturePKCS1v15SHA256: .rsa,
.rsaSignaturePKCS1v15SHA384: .rsa,
.rsaSignaturePKCS1v15SHA512: .rsa,
])
]

private static let curves: ReadWriteLockedValue<[Self: JSONWebKeyCurve]> = .init([
private static let curves: ReadWriteLockedValue<[Self: JSONWebKeyCurve]> = [
.ecdsaSignatureP256SHA256: .p256, .ecdsaSignatureP384SHA384: .p384,
.ecdsaSignatureP521SHA512: .p521, .eddsaSignature: .ed25519,
])
]

private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = .init([
private static let hashFunctions: ReadWriteLockedValue<[Self: any HashFunction.Type]> = [
.hmacSHA256: SHA256.self,
.hmacSHA384: SHA384.self,
.hmacSHA512: SHA512.self,
Expand All @@ -75,7 +75,7 @@ extension JSONWebSignatureAlgorithm {
.rsaSignaturePKCS1v15SHA256: SHA256.self,
.rsaSignaturePKCS1v15SHA384: SHA384.self,
.rsaSignaturePKCS1v15SHA512: SHA512.self,
])
]

public var keyType: JSONWebKeyType? {
Self.keyTypes[self]
Expand Down Expand Up @@ -116,10 +116,10 @@ extension JSONWebSignatureAlgorithm {
publicKeyClass: Public.Type,
privateKeyClass: Private.Type
) where Public: JSONWebValidatingKey, Private: JSONWebSigningKey {
keyRegistryClasses.wrappedValue[algorithm] = (publicKeyClass, privateKeyClass)
keyTypes.wrappedValue[algorithm] = type
curves.wrappedValue[algorithm] = curve
hashFunctions.wrappedValue[algorithm] = hashFunction
keyRegistryClasses[algorithm] = (publicKeyClass, privateKeyClass)
keyTypes[algorithm] = type
curves[algorithm] = curve
hashFunctions[algorithm] = hashFunction
}
}

Expand Down
8 changes: 4 additions & 4 deletions Sources/JWSETKit/Cryptography/KeyParser.swift
Original file line number Diff line number Diff line change
Expand Up @@ -194,13 +194,13 @@ enum JSONWebKeyCertificateChainSpecializer: JSONWebKeySpecializer {
}

extension AnyJSONWebKey {
static let specializers: ReadWriteLockedValue<[any JSONWebKeySpecializer.Type]> = .init([
static let specializers: ReadWriteLockedValue<[any JSONWebKeySpecializer.Type]> = [
JSONWebKeyRSASpecializer.self,
JSONWebKeyEllipticCurveSpecializer.self,
JSONWebKeyCurve25519Specializer.self,
JSONWebKeyCertificateChainSpecializer.self,
JSONWebKeySymmetricSpecializer.self,
])
]

/// Registers a new key specializer.
///
Expand All @@ -209,8 +209,8 @@ extension AnyJSONWebKey {
///
/// - Parameter specializer: The specializer to register.
public static func registerSpecializer(_ specializer: any JSONWebKeySpecializer.Type) {
guard !specializers.wrappedValue.contains(where: { $0 == specializer }) else { return }
specializers.wrappedValue.insert(specializer, at: 0)
guard !specializers.contains(where: { $0 == specializer }) else { return }
specializers.insert(specializer, at: 0)
}
}

Expand Down

0 comments on commit 3fbf686

Please sign in to comment.