-
Notifications
You must be signed in to change notification settings - Fork 6
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Verify did_rotate attachment signature (#107)
Signed-off-by: conanoc <[email protected]>
- Loading branch information
Showing
5 changed files
with
176 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
|
||
import Foundation | ||
import os | ||
import Base58Swift | ||
|
||
public class JwsService { | ||
let agent: Agent | ||
let logger = Logger(subsystem: "AriesFramework", category: "JwsService") | ||
|
||
init(agent: Agent) { | ||
self.agent = agent | ||
} | ||
|
||
/** | ||
Creates a JWS using the given payload and verkey. | ||
- Parameters: | ||
- payload: The payload to sign. | ||
- verkey: The verkey to sign the payload for. The verkey should be created using ``Wallet.createDid(seed:)``. | ||
- Returns: A JWS object. | ||
*/ | ||
public func createJws(payload: Data, verkey: String) async throws -> JwsGeneralFormat { | ||
guard let keyEntry = try await agent.wallet.session!.fetchKey(name: verkey, forUpdate: false) else { | ||
throw AriesFrameworkError.frameworkError("Unable to find key for verkey: \(verkey)") | ||
} | ||
let key = try keyEntry.loadLocalKey() | ||
let jwkJson = try key.toJwkPublic(alg: nil).data(using: .utf8)! | ||
guard let jwk = try JSONSerialization.jsonObject(with: jwkJson) as? [String: Any] else { | ||
throw AriesFrameworkError.frameworkError("Unable to parse JWK JSON: \(jwkJson)") | ||
} | ||
let protectedHeader = [ | ||
"alg": "EdDSA", | ||
"jwk": jwk | ||
] as [String: Any] | ||
let protectedHeaderJson = try JSONSerialization.data(withJSONObject: protectedHeader) | ||
let base64ProtectedHeader = protectedHeaderJson.base64EncodedString().base64ToBase64url() | ||
let base64Payload = payload.base64EncodedString().base64ToBase64url() | ||
|
||
let message = "\(base64ProtectedHeader).\(base64Payload)".data(using: .utf8)! | ||
let signature = try key.signMessage(message: message, sigType: nil) | ||
let base64Signature = signature.base64EncodedString().base64ToBase64url() | ||
let header = [ | ||
"kid": try DIDParser.ConvertVerkeyToDidKey(verkey: verkey) | ||
] | ||
|
||
return JwsGeneralFormat(header: header, signature: base64Signature, protected: base64ProtectedHeader) | ||
} | ||
|
||
/** | ||
Verifies the given JWS against the given payload. | ||
- Parameters: | ||
- jws: The JWS to verify. | ||
- payload: The payload to verify the JWS against. | ||
- Returns: A tuple containing the validity of the JWS and the signer's verkey. | ||
*/ | ||
public func verifyJws(jws: Jws, payload: Data) throws -> (isValid: Bool, signer: String) { | ||
logger.debug("Verifying JWS...") | ||
var firstSig: JwsGeneralFormat! | ||
switch jws { | ||
case let .flattened(list): | ||
if list.signatures.count == 0 { | ||
throw AriesFrameworkError.frameworkError("No signatures found in JWS") | ||
} | ||
firstSig = list.signatures.first! | ||
case let .general(jws): | ||
firstSig = jws | ||
} | ||
guard let protectedJson = Data(base64Encoded: firstSig.protected.base64urlToBase64()), | ||
let protected = try JSONSerialization.jsonObject(with: protectedJson) as? [String: Any], | ||
let signature = Data(base64Encoded: firstSig.signature.base64urlToBase64()), | ||
let jwk = protected["jwk"] else { | ||
throw AriesFrameworkError.frameworkError("Invalid Jws: \(firstSig)") | ||
} | ||
let jwkData = try JSONSerialization.data(withJSONObject: jwk) | ||
let jwkString = String(data: jwkData, encoding: .utf8)! | ||
logger.debug("jwk: \(jwkString)") | ||
let key = try agent.wallet.keyFactory.fromJwk(jwk: jwkString) | ||
let publicBytes = try key.toPublicBytes() | ||
let signer = Base58.base58Encode([UInt8](publicBytes)) | ||
|
||
let base64Payload = payload.base64EncodedString().base64ToBase64url() | ||
let message = "\(firstSig.protected).\(base64Payload)".data(using: .utf8)! | ||
let isValid = try key.verifySignature(message: message, signature: signature, sigType: nil) | ||
|
||
return (isValid, signer) | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import XCTest | ||
@testable import AriesFramework | ||
|
||
class JwsServiceTest: XCTestCase { | ||
var agent: Agent! | ||
let seed = "00000000000000000000000000000My2" | ||
let verkey = "kqa2HyagzfMAq42H5f9u3UMwnSBPQx2QfrSyXbUPxMn" | ||
let payload = "hello".data(using: .utf8)! | ||
|
||
override func setUp() async throws { | ||
try await super.setUp() | ||
let config = try TestHelper.getBaseConfig(name: "alice") | ||
agent = Agent(agentConfig: config, agentDelegate: nil) | ||
try await agent.initialize() | ||
|
||
let (_, newKey) = try await agent.wallet.createDid(seed: seed) | ||
XCTAssertEqual(newKey, verkey) | ||
} | ||
|
||
override func tearDown() async throws { | ||
try await agent.reset() | ||
try await super.tearDown() | ||
} | ||
|
||
func testCreateAndVerify() async throws { | ||
let jws = try await agent.jwsService.createJws(payload: payload, verkey: verkey) | ||
XCTAssertEqual("did:key:z6MkfD6ccYE22Y9pHKtixeczk92MmMi2oJCP6gmNooZVKB9A", jws.header?["kid"]) | ||
let protectedJson = Data(base64Encoded: jws.protected.base64urlToBase64())! | ||
let protected = try JSONSerialization.jsonObject(with: protectedJson) as? [String: Any] | ||
XCTAssertEqual("EdDSA", protected?["alg"] as? String) | ||
XCTAssertNotNil(protected?["jwk"]) | ||
|
||
let (isValid, signer) = try agent.jwsService.verifyJws(jws: .general(jws), payload: payload) | ||
XCTAssertTrue(isValid) | ||
XCTAssertEqual(signer, verkey) | ||
} | ||
|
||
func testFlattenedJws() async throws { | ||
let jws = try await agent.jwsService.createJws(payload: payload, verkey: verkey) | ||
let list: Jws = .flattened(JwsFlattenedFormat(signatures: [jws])) | ||
|
||
let (isValid, signer) = try agent.jwsService.verifyJws(jws: list, payload: payload) | ||
XCTAssertTrue(isValid) | ||
XCTAssertEqual(signer, verkey) | ||
} | ||
|
||
func testVerifyFail() async throws { | ||
let wrongPayload = "world".data(using: .utf8)! | ||
let jws = try await agent.jwsService.createJws(payload: payload, verkey: verkey) | ||
let (isValid, signer) = try agent.jwsService.verifyJws(jws: .general(jws), payload: wrongPayload) | ||
XCTAssertFalse(isValid) | ||
XCTAssertEqual(signer, verkey) | ||
} | ||
} |