Introduce JWK data object
This commit is contained in:
Родитель
17443df066
Коммит
9f4cf7e375
|
@ -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()
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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])
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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))
|
||||
|
|
|
@ -65,7 +65,7 @@ public struct JwsToken<T: Claims> {
|
|||
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)
|
||||
}
|
||||
|
||||
|
|
|
@ -7,7 +7,7 @@ import VCCrypto
|
|||
|
||||
public protocol TokenVerifying {
|
||||
|
||||
func verify<T>(token: JwsToken<T>, usingPublicKey key: any PublicJwk) throws -> Bool
|
||||
func verify<T>(token: JwsToken<T>, usingPublicKey key: JWK) throws -> Bool
|
||||
}
|
||||
|
||||
|
||||
|
|
|
@ -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)\"}"
|
||||
}
|
||||
|
|
|
@ -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<T>(token: JwsToken<T>, 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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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<T>(token: JwsToken<T>, usingPublicKey key: any PublicJwk) throws -> Bool {
|
||||
public func verify<T>(token: JwsToken<T>, 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
|
||||
}
|
||||
}
|
||||
|
||||
|
|
Загрузка…
Ссылка в новой задаче