Skip to content
This repository was archived by the owner on Dec 12, 2024. It is now read-only.

Commit 08346ac

Browse files
authored
Use web5-swift JWS.sign & JWS.verify instead of CryptoUtils (#33)
1 parent 52b8848 commit 08346ac

File tree

5 files changed

+35
-172
lines changed

5 files changed

+35
-172
lines changed

Package.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -16,7 +16,7 @@ let package = Package(
1616
dependencies: [
1717
.package(url: "https://github.com/Frizlab/swift-typeid.git", from: "0.3.0"),
1818
.package(url: "https://github.com/flight-school/anycodable.git", from: "0.6.7"),
19-
.package(url: "https://github.com/TBD54566975/web5-swift", exact: "0.0.2"),
19+
.package(url: "https://github.com/TBD54566975/web5-swift", exact: "0.0.3"),
2020
.package(url: "https://github.com/allegro/swift-junit.git", from: "2.1.0"),
2121
.package(url: "https://github.com/pointfreeco/swift-custom-dump.git", from: "1.1.2"),
2222
],

Sources/tbDEX/Protocol/CryptoUtils.swift

Lines changed: 1 addition & 151 deletions
Original file line numberDiff line numberDiff line change
@@ -9,13 +9,8 @@ enum CryptoUtils {}
99
extension CryptoUtils {
1010

1111
static func digest<D: Codable, M: Codable>(data: D, metadata: M) throws -> Data {
12-
let encoder = JSONEncoder()
13-
encoder.dateEncodingStrategy = .iso8601
14-
encoder.outputFormatting = .sortedKeys
15-
1612
let payload = DigestPayload(data: data, metadata: metadata)
17-
let serializedPayload = try encoder.encode(payload)
18-
13+
let serializedPayload = try tbDEXJSONEncoder().encode(payload)
1914
let digest = SHA256.hash(data: serializedPayload)
2015
return Data(digest)
2116
}
@@ -26,148 +21,3 @@ extension CryptoUtils {
2621
}
2722

2823
}
29-
30-
// MARK: - Sign
31-
32-
extension CryptoUtils {
33-
34-
enum SigningError: Error {
35-
case assertionMethodNotFound
36-
case publicKeyJwkNotFound
37-
case algorithmNotDefined
38-
}
39-
40-
/// Signs the provided payload using the specified DID and key.
41-
/// - Parameters:
42-
/// - did: DID to use for signing
43-
/// - payload: The data to be signed
44-
/// - assertionMethodId: The alias of the key to be used for signing.
45-
/// - Returns: The signed payload as a detached payload JWT (JSON Web Token).
46-
static func sign<D>(did: BearerDID, payload: D, assertionMethodId: String? = nil) async throws -> String
47-
where D: DataProtocol {
48-
let assertionMethod = try await getAssertionMethod(did: did, assertionMethodId: assertionMethodId)
49-
guard let publicKeyJwk = assertionMethod.publicKeyJwk else {
50-
throw SigningError.publicKeyJwkNotFound
51-
}
52-
53-
let keyAlias = try did.keyManager.getDeterministicAlias(key: publicKeyJwk)
54-
let publicKey = try did.keyManager.getPublicKey(keyAlias: keyAlias)
55-
guard let algorithm = publicKey.algorithm?.jwsAlgorithm else {
56-
throw SigningError.algorithmNotDefined
57-
}
58-
59-
let jwsHeader = JWS.Header(
60-
algorithm: algorithm,
61-
keyID: assertionMethod.id
62-
)
63-
64-
let base64UrlEncodedHeader = try JSONEncoder().encode(jwsHeader).base64UrlEncodedString()
65-
let base64UrlEncodedPayload = payload.base64UrlEncodedString()
66-
67-
let toSign = "\(base64UrlEncodedHeader).\(base64UrlEncodedPayload)"
68-
let signatureBytes = try did.keyManager.sign(keyAlias: keyAlias, payload: Data(toSign.utf8))
69-
let base64UrlEncodedSignature = signatureBytes.base64UrlEncodedString()
70-
71-
return "\(base64UrlEncodedHeader)..\(base64UrlEncodedSignature)"
72-
}
73-
74-
private static func getAssertionMethod(did: BearerDID, assertionMethodId: String?) async throws
75-
-> VerificationMethod
76-
{
77-
let resolutionResult = await DIDResolver.resolve(didURI: did.uri)
78-
let assertionMethods = resolutionResult.didDocument?.assertionMethodDereferenced
79-
80-
guard
81-
let assertionMethod =
82-
if let assertionMethodId {
83-
assertionMethods?.first(where: { $0.id == assertionMethodId })
84-
} else {
85-
assertionMethods?.first
86-
}
87-
else {
88-
throw SigningError.assertionMethodNotFound
89-
}
90-
91-
return assertionMethod
92-
}
93-
94-
}
95-
96-
// MARK: - Verify
97-
98-
extension CryptoUtils {
99-
100-
struct VerifyError: Error {
101-
let reason: String
102-
}
103-
104-
// Verifies the integrity of a message or resource's signature.
105-
static func verify<D: DataProtocol>(
106-
didURI: String,
107-
signature: String?,
108-
detachedPayload: D? = nil
109-
) async throws -> Bool {
110-
guard let signature else {
111-
throw VerifyError(reason: "Signature not present")
112-
}
113-
114-
let splitJWS = signature.split(separator: ".", omittingEmptySubsequences: false)
115-
116-
guard splitJWS.count == 3 else {
117-
throw VerifyError(reason: "Excpected valid JWS with 3 parts, got \(splitJWS.count)")
118-
}
119-
120-
let decoder = JSONDecoder()
121-
decoder.dateDecodingStrategy = .iso8601
122-
123-
let jwsHeader = String(splitJWS[0])
124-
let jwsSignature = String(splitJWS[2])
125-
let jwsPayload: String
126-
127-
if let detachedPayload {
128-
guard splitJWS[1].count == 0 else {
129-
throw VerifyError(reason: "Expected valid JWS with detached payload")
130-
}
131-
jwsPayload = String(detachedPayload.base64UrlEncodedString())
132-
} else {
133-
jwsPayload = String(splitJWS[1])
134-
}
135-
136-
guard let jwsHeader = try? JSONDecoder().decode(JWS.Header.self, from: jwsHeader.decodeBase64Url()),
137-
let verificationMethodID = jwsHeader.keyID
138-
else {
139-
throw VerifyError(reason: "")
140-
}
141-
142-
let signingDID = try DID(didURI: verificationMethodID)
143-
let signingDIDURI = signingDID.uriWithoutFragment
144-
145-
guard signingDIDURI == didURI else {
146-
throw VerifyError(reason: "Was not signed by the expected DID - Expected:\(didURI) Actual:\(signingDIDURI)")
147-
}
148-
149-
let resolutionResult = await DIDResolver.resolve(didURI: signingDIDURI)
150-
if let error = resolutionResult.didResolutionMetadata.error {
151-
throw VerifyError(reason: "Failed to resolve DID \(signingDIDURI): \(error)")
152-
}
153-
154-
guard
155-
let assertionMethod =
156-
resolutionResult.didDocument?.assertionMethodDereferenced?.first(
157-
where: { $0.absoluteId == verificationMethodID }
158-
)
159-
else {
160-
throw VerifyError(reason: "Assertion method not found")
161-
}
162-
163-
let publicKeyJwk = assertionMethod.publicKeyJwk!
164-
165-
return try Crypto.verify(
166-
payload: try jwsPayload.decodeBase64Url(),
167-
signature: try jwsSignature.decodeBase64Url(),
168-
publicKey: publicKeyJwk,
169-
jwsAlgorithm: jwsHeader.algorithm
170-
)
171-
}
172-
173-
}

Sources/tbDEX/Protocol/Models/Message.swift

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -45,12 +45,21 @@ public struct Message<D: MessageData>: Codable, Equatable {
4545
try CryptoUtils.digest(data: data, metadata: metadata)
4646
}
4747

48-
mutating func sign(did: BearerDID, keyAlias: String? = nil) async throws {
49-
signature = try await CryptoUtils.sign(did: did, payload: try digest(), assertionMethodId: keyAlias)
48+
mutating func sign(did: BearerDID, keyAlias: String? = nil) throws {
49+
signature = try JWS.sign(
50+
did: did,
51+
payload: try digest(),
52+
detached: true,
53+
verificationMethodID: keyAlias
54+
)
5055
}
5156

52-
func verify() async throws {
53-
_ = try await CryptoUtils.verify(didURI: metadata.from, signature: signature, detachedPayload: try digest())
57+
func verify() async throws -> Bool {
58+
return try await JWS.verify(
59+
compactJWS: signature,
60+
detachedPayload: try digest(),
61+
expectedSigningDIDURI: metadata.from
62+
)
5463
}
5564
}
5665

Sources/tbDEX/Protocol/Models/Resource.swift

Lines changed: 12 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -39,13 +39,21 @@ public struct Resource<D: ResourceData>: Codable, Equatable {
3939
}
4040

4141
mutating func sign(did: BearerDID, keyAlias: String? = nil) async throws {
42-
self.signature = try await CryptoUtils.sign(did: did, payload: digest(), assertionMethodId: keyAlias)
42+
self.signature = try JWS.sign(
43+
did: did,
44+
payload: try digest(),
45+
detached: true,
46+
verificationMethodID: keyAlias
47+
)
4348
}
4449

45-
func verify() async throws {
46-
_ = try await CryptoUtils.verify(didURI: metadata.from, signature: signature, detachedPayload: digest())
50+
func verify() async throws -> Bool {
51+
return try await JWS.verify(
52+
compactJWS: signature,
53+
detachedPayload: try digest(),
54+
expectedSigningDIDURI: metadata.from
55+
)
4756
}
48-
4957
}
5058

5159
/// Enum containing the different types of Resources

Tests/tbDEXTests/Protocol/Models/Resources/OfferingTests.swift

Lines changed: 8 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -17,18 +17,14 @@ final class OfferingTests: XCTestCase {
1717
}
1818

1919
func test_signAndVerifySuccess() async throws {
20-
do {
21-
let did = try DIDJWK.create(keyManager: InMemoryKeyManager())
22-
var offering = createOffering(from: did.uri)
23-
24-
XCTAssertNil(offering.signature)
25-
try await offering.sign(did: did)
26-
XCTAssertNotNil(offering.signature)
27-
try await offering.verify()
28-
} catch {
29-
print("Something went wrong: \(error)")
30-
XCTFail()
31-
}
20+
let did = try DIDJWK.create(keyManager: InMemoryKeyManager())
21+
var offering = createOffering(from: did.uri)
22+
23+
XCTAssertNil(offering.signature)
24+
try await offering.sign(did: did)
25+
XCTAssertNotNil(offering.signature)
26+
let isValid = try await offering.verify()
27+
XCTAssertTrue(isValid)
3228
}
3329

3430
func test_verifyWithoutSigningFailure() async throws {

0 commit comments

Comments
 (0)