diff --git a/VCCrypto/VCCrypto/CryptoOperations.swift b/VCCrypto/VCCrypto/CryptoOperations.swift index 82aa5b0..20cfafc 100644 --- a/VCCrypto/VCCrypto/CryptoOperations.swift +++ b/VCCrypto/VCCrypto/CryptoOperations.swift @@ -4,9 +4,10 @@ *--------------------------------------------------------------------------------------------*/ public protocol CryptoOperating { - func verify(signature: Data, forMessageHash messageHash: Data, usingPublicKey publicKey: PublicKey) throws -> Bool func sign(messageHash: Data, usingSecret secret: VCCryptoSecret) throws -> Data + func hash(message: Data, algorithm: SupportedHashAlgorithm) -> Data func getPublicKey(fromSecret secret: VCCryptoSecret) throws -> PublicKey + func verify(signature: Data, forMessageHash messageHash: Data, usingPublicKey publicKey: PublicKey) throws -> Bool } enum CryptoOperationsError: Error { @@ -24,6 +25,16 @@ public struct CryptoOperations: CryptoOperating { return try algorithm.sign(messageHash: messageHash) } + public func hash(message: Data, algorithm: SupportedHashAlgorithm) -> Data { + switch algorithm { + case .SHA256: + return Sha256().hash(data: message) + case .SHA512: + return Sha512().hash(data: message) + } + } + + /// Only support Secp256k1 public key retrieval. public func getPublicKey(fromSecret secret: VCCryptoSecret) throws -> PublicKey { return try Secp256k1(secret: secret).getPublicKey() } diff --git a/VCCrypto/VCCrypto/SupportedAlgorithms.swift b/VCCrypto/VCCrypto/SupportedAlgorithms.swift index 4252072..f4fb16b 100644 --- a/VCCrypto/VCCrypto/SupportedAlgorithms.swift +++ b/VCCrypto/VCCrypto/SupportedAlgorithms.swift @@ -3,7 +3,12 @@ * Licensed under the MIT License. See License.txt in the project root for license information. *--------------------------------------------------------------------------------------------*/ -public enum SupportedAlgorithms: String { +public enum SupportedVerificationAlgorithm: String { case ED25519 = "ED25519" case Secp256k1 = "SECP256K1" } + +public enum SupportedHashAlgorithm { + case SHA256 + case SHA512 +} diff --git a/VCEntities/VCEntities/formatters/IdentifierFormatter.swift b/VCEntities/VCEntities/formatters/IdentifierFormatter.swift index 1a8fbbb..7bc9731 100644 --- a/VCEntities/VCEntities/formatters/IdentifierFormatter.swift +++ b/VCEntities/VCEntities/formatters/IdentifierFormatter.swift @@ -26,11 +26,11 @@ struct IdentifierFormatter: IdentifierFormatting { } func createIonLongFormDid(recoveryKey: ECPublicJwk, - updateKey: ECPublicJwk, - didDocumentKeys: [ECPublicJwk], - serviceEndpoints: [IdentifierDocumentServiceEndpoint]) throws -> String { + updateKey: ECPublicJwk, + didDocumentKeys: [ECPublicJwk], + serviceEndpoints: [IdentifierDocumentServiceEndpoint]) throws -> String { - let document = IONDocumentModel(fromJwks: didDocumentKeys, andServiceEndpoints: serviceEndpoints) + let document = IONDocumentModel(fromJwks: didDocumentKeys.map { key in key.toJWK() }, andServiceEndpoints: serviceEndpoints) let patches = [IONDocumentPatch(action: IdentifierFormatter.replaceAction, document: document)] let commitmentHash = try self.createCommitmentHash(usingJwk: updateKey) diff --git a/VCEntities/VCEntities/identifier/document/IdentifierDocumentPublicKey.swift b/VCEntities/VCEntities/identifier/document/IdentifierDocumentPublicKey.swift index ac1f2b2..6783500 100644 --- a/VCEntities/VCEntities/identifier/document/IdentifierDocumentPublicKey.swift +++ b/VCEntities/VCEntities/identifier/document/IdentifierDocumentPublicKey.swift @@ -9,14 +9,14 @@ public struct IdentifierDocumentPublicKey: Codable, Equatable { let id: String? let type: String let controller: String? - let publicKeyJwk: ECPublicJwk + let publicKeyJwk: JWK let purposes: [String]? public init(id: String?, - type: String, - controller: String?, - publicKeyJwk: ECPublicJwk, - purposes: [String]?) { + type: String, + controller: String?, + publicKeyJwk: JWK, + purposes: [String]?) { self.id = id self.type = type self.controller = controller @@ -24,7 +24,7 @@ public struct IdentifierDocumentPublicKey: Codable, Equatable { self.purposes = purposes } - init(fromJwk key: ECPublicJwk) { + init(fromJwk key: JWK) { self.init(id: key.keyId, type: VCEntitiesConstants.SUPPORTED_PUBLICKEY_TYPE, controller: nil, publicKeyJwk: key, purposes: [VCEntitiesConstants.PUBLICKEY_AUTHENTICATION_PURPOSE_V1]) } } diff --git a/VCEntities/VCEntities/identifier/document/ion/IONDocumentModel.swift b/VCEntities/VCEntities/identifier/document/ion/IONDocumentModel.swift index a79e12f..4c13af0 100644 --- a/VCEntities/VCEntities/identifier/document/ion/IONDocumentModel.swift +++ b/VCEntities/VCEntities/identifier/document/ion/IONDocumentModel.swift @@ -9,7 +9,7 @@ struct IONDocumentModel: Codable { let publicKeys: [IdentifierDocumentPublicKey] let services: [IdentifierDocumentServiceEndpoint]? - init(fromJwks jwks: [ECPublicJwk], andServiceEndpoints services: [IdentifierDocumentServiceEndpoint]) { + init(fromJwks jwks: [JWK], andServiceEndpoints services: [IdentifierDocumentServiceEndpoint]) { var keys: [IdentifierDocumentPublicKey] = [] for jwk in jwks { keys.append(IdentifierDocumentPublicKey(fromJwk: jwk)) diff --git a/VCToken/VCToken/JwsToken.swift b/VCToken/VCToken/JwsToken.swift index 57b48f1..99bbe96 100644 --- a/VCToken/VCToken/JwsToken.swift +++ b/VCToken/VCToken/JwsToken.swift @@ -65,7 +65,7 @@ public struct JwsToken { self.signature = try signer.sign(token: self, withSecret: secret) } - public func verify(using verifier: TokenVerifying, withPublicKey key: any PublicJwk) throws -> Bool { + public func verify(using verifier: TokenVerifying, withPublicKey key: JWK) throws -> Bool { return try verifier.verify(token: self, usingPublicKey: key) } diff --git a/VCToken/VCToken/TokenVerifying.swift b/VCToken/VCToken/TokenVerifying.swift index 8730d18..df5724e 100644 --- a/VCToken/VCToken/TokenVerifying.swift +++ b/VCToken/VCToken/TokenVerifying.swift @@ -7,7 +7,7 @@ import VCCrypto public protocol TokenVerifying { - func verify(token: JwsToken, usingPublicKey key: any PublicJwk) throws -> Bool + func verify(token: JwsToken, usingPublicKey key: JWK) throws -> Bool } diff --git a/VCToken/VCToken/algorithms/ECPublicJwk.swift b/VCToken/VCToken/algorithms/ECPublicJwk.swift index 5163b76..b1f542e 100644 --- a/VCToken/VCToken/algorithms/ECPublicJwk.swift +++ b/VCToken/VCToken/algorithms/ECPublicJwk.swift @@ -16,6 +16,22 @@ public protocol PublicJwk: Codable, Equatable { var y: String { get } } +public struct JWK: Codable, Equatable { + let keyType: String? + public let keyId: String? + let use: String? + let keyOperations: [String]? + let algorithm: String? + let curve: String? + let x: String? + let y: String? + + enum CodingKeys: String, CodingKey { + case curve = "crv" + case keyType, keyId, use, keyOperations, algorithm, x, y + } +} + public struct ECPublicJwk: PublicJwk { public let keyType: String public let keyId: String? @@ -52,6 +68,18 @@ public struct ECPublicJwk: PublicJwk { self.init(x: x, y: y, keyId: kid) } + public func toJWK() -> JWK + { + return JWK(keyType: keyType, + keyId: keyId, + use: use, + keyOperations: keyOperations, + algorithm: algorithm, + curve: curve, + x: x, + y: y) + } + func getMinimumAlphabeticJwk() -> String { return "{\"crv\":\"\(self.curve)\",\"kty\":\"\(self.keyType)\",\"x\":\"\(self.x)\",\"y\":\"\(self.y)\"}" } diff --git a/VCToken/VCToken/algorithms/Secp256k1Signer.swift b/VCToken/VCToken/algorithms/Secp256k1Signer.swift index 98c6a4f..c034146 100644 --- a/VCToken/VCToken/algorithms/Secp256k1Signer.swift +++ b/VCToken/VCToken/algorithms/Secp256k1Signer.swift @@ -12,13 +12,10 @@ enum Secp256k1SignerError: Error { /// TODO: refactor class to be a generic signer. public struct Secp256k1Signer: TokenSigning { - private let hashAlgorithm: Sha256 - private let cryptoOperations: CryptoOperating - public init(cryptoOperations: CryptoOperating = CryptoOperations(), andHashAlgorithm hashAlg: Sha256 = Sha256()) { + public init(cryptoOperations: CryptoOperating = CryptoOperations()) { self.cryptoOperations = cryptoOperations - self.hashAlgorithm = hashAlg } public func sign(token: JwsToken, withSecret secret: VCCryptoSecret) throws -> Signature { @@ -29,7 +26,8 @@ public struct Secp256k1Signer: TokenSigning { throw VCTokenError.unableToParseData } - let hashedMessage = hashAlgorithm.hash(data: messageData) + let hashedMessage = cryptoOperations.hash(message: messageData, algorithm: .SHA256) + return try cryptoOperations.sign(messageHash: hashedMessage, usingSecret: secret) } diff --git a/VCToken/VCToken/algorithms/TokenVerifier.swift b/VCToken/VCToken/algorithms/TokenVerifier.swift index d753b5d..8ab9ffd 100644 --- a/VCToken/VCToken/algorithms/TokenVerifier.swift +++ b/VCToken/VCToken/algorithms/TokenVerifier.swift @@ -5,16 +5,20 @@ import VCCrypto +enum TokenVerifierError: Error { + case unsupportedAlgorithmFoundInJWK + case malformedJWK +} + public struct TokenVerifier: TokenVerifying { private let cryptoOperations: CryptoOperating - private let hashAlgorithm: Sha256 = Sha256() public init(cryptoOperations: CryptoOperating = CryptoOperations()) { self.cryptoOperations = cryptoOperations } - public func verify(token: JwsToken, usingPublicKey key: any PublicJwk) throws -> Bool { + public func verify(token: JwsToken, usingPublicKey key: JWK) throws -> Bool { guard let signature = token.signature else { return false @@ -24,15 +28,42 @@ public struct TokenVerifier: TokenVerifying { throw VCTokenError.unableToParseString } - guard let x = Data(base64URLEncoded: key.x), - let y = Data(base64URLEncoded: key.y), - let secpKey = Secp256k1PublicKey(x: x, y: y) else { - throw VCTokenError.unableToParseString + let hashedMessage = cryptoOperations.hash(message: encodedMessage, algorithm: .SHA256) + + return try cryptoOperations.verify(signature: signature, forMessageHash: hashedMessage, usingPublicKey: transformKey(key: key)) + } + + private func transformKey(key: JWK) throws -> PublicKey { + switch key.curve?.uppercased() { + case SupportedVerificationAlgorithm.Secp256k1.rawValue: + return try transformSecp256k1(key: key) + case SupportedVerificationAlgorithm.ED25519.rawValue: + return try transformED25519(key: key) + default: + throw TokenVerifierError.unsupportedAlgorithmFoundInJWK } - let hashedMessage = self.hashAlgorithm.hash(data: encodedMessage) + } + + private func transformSecp256k1(key: JWK) throws -> Secp256k1PublicKey { + guard let x = key.x, let y = key.y, + let encodedX = Data(base64URLEncoded: x), + let encodedY = Data(base64URLEncoded: y), + let secpKey = Secp256k1PublicKey(x: encodedX, y: encodedY) else { + throw TokenVerifierError.malformedJWK + } - return try cryptoOperations.verify(signature: signature, forMessageHash: hashedMessage, usingPublicKey: secpKey) + return secpKey + } + + private func transformED25519(key: JWK) throws -> ED25519PublicKey { + guard let x = key.x, + let encodedX = Data(base64URLEncoded: x), + let edKey = ED25519PublicKey(x: encodedX) else { + throw TokenVerifierError.malformedJWK + } + + return edKey } }