-
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.
feat(pollux): add jwt credential revocation support
Fixes ATL-7034 Signed-off-by: goncalo-frade-iohk <goncalo.frade@iohk.io>
- Loading branch information
1 parent
068acdd
commit 3f2a698
Showing
14 changed files
with
241 additions
and
10 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
31 changes: 31 additions & 0 deletions
31
EdgeAgentSDK/Domain/Sources/Models/Credentials/RevocableCredential.swift
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,31 @@ | ||
import Foundation | ||
|
||
/// `RevocableCredential` is a protocol that defines the attributes and behaviors | ||
/// of a credential that can be revoked or suspended. | ||
public protocol RevocableCredential { | ||
/// Indicates whether the credential can be revoked. | ||
var canBeRevoked: Bool { get } | ||
|
||
/// Indicates whether the credential can be suspended. | ||
var canBeSuspended: Bool { get } | ||
|
||
/// Checks if the credential is currently revoked. | ||
/// | ||
/// - Returns: A Boolean value indicating whether the credential is revoked. | ||
/// - Throws: An error if the status cannot be determined. | ||
var isRevoked: Bool { get async throws } | ||
|
||
/// Checks if the credential is currently suspended. | ||
/// | ||
/// - Returns: A Boolean value indicating whether the credential is suspended. | ||
/// - Throws: An error if the status cannot be determined. | ||
var isSuspended: Bool { get async throws } | ||
} | ||
|
||
public extension Credential { | ||
/// A Boolean value indicating whether the credential can verify revocability. | ||
var isRevocable: Bool { self is RevocableCredential } | ||
|
||
/// Returns the revocable representation of the credential. | ||
var revocable: RevocableCredential? { self as? RevocableCredential } | ||
} |
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
26 changes: 26 additions & 0 deletions
26
EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTCredential+RevocableCredential.swift
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,26 @@ | ||
import Domain | ||
import Foundation | ||
|
||
extension JWTCredential: RevocableCredential { | ||
public var canBeRevoked: Bool { | ||
self.jwtVerifiableCredential.verifiableCredential.credentialStatus?.statusPurpose == .revocation | ||
} | ||
|
||
public var canBeSuspended: Bool { | ||
self.jwtVerifiableCredential.verifiableCredential.credentialStatus?.statusPurpose == .suspension | ||
} | ||
|
||
public var isRevoked: Bool { | ||
get async throws { | ||
guard canBeRevoked else { return false } | ||
return try await JWTRevocationCheck(credential: self).checkIsRevoked() | ||
} | ||
} | ||
|
||
public var isSuspended: Bool { | ||
get async throws { | ||
guard canBeSuspended else { return false } | ||
return try await JWTRevocationCheck(credential: self).checkIsRevoked() | ||
} | ||
} | ||
} |
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
76 changes: 76 additions & 0 deletions
76
EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationCheck.swift
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,76 @@ | ||
import Domain | ||
import Foundation | ||
import Gzip | ||
import JSONWebSignature | ||
|
||
struct JWTRevocationCheck { | ||
let credential: JWTCredential | ||
|
||
init(credential: JWTCredential) { | ||
self.credential = credential | ||
} | ||
|
||
func checkIsRevoked() async throws -> Bool { | ||
guard let status = credential.jwtVerifiableCredential.verifiableCredential.credentialStatus else { | ||
return false | ||
} | ||
|
||
guard status.type == "StatusList2021Entry" else { | ||
throw UnknownError.somethingWentWrongError(customMessage: nil, underlyingErrors: nil) | ||
} | ||
|
||
let listData = try await DownloadDataWithResolver() | ||
.downloadFromEndpoint(urlOrDID: status.statusListCredential) | ||
let statusList = try JSONDecoder.didComm().decode(JWTRevocationStatusListCredential.self, from: listData) | ||
let encodedList = statusList.credentialSubject.encodedList | ||
let index = status.statusListIndex | ||
return try verifyRevocationOnEncodedList(encodedList.tryToData(), index: index) | ||
} | ||
|
||
func verifyRevocationOnEncodedList(_ list: Data, index: Int) throws -> Bool { | ||
let encodedListData = try list.gunzipped() | ||
let bitList = encodedListData.bytes.flatMap { $0.toBits() } | ||
guard index < bitList.count else { | ||
throw UnknownError.somethingWentWrongError(customMessage: "Revocation index out of bounds", underlyingErrors: nil) | ||
} | ||
return bitList[index] | ||
} | ||
} | ||
|
||
extension UInt8 { | ||
func toBits() -> [Bool] { | ||
var bits = [Bool](repeating: false, count: 8) | ||
for i in 0..<8 { | ||
bits[7 - i] = (self & (1 << i)) != 0 | ||
} | ||
return bits | ||
} | ||
} | ||
|
||
fileprivate struct DownloadDataWithResolver: Downloader { | ||
|
||
public func downloadFromEndpoint(urlOrDID: String) async throws -> Data { | ||
let url: URL | ||
|
||
if let validUrl = URL(string: urlOrDID.replacingOccurrences(of: "host.docker.internal", with: "localhost")) { | ||
url = validUrl | ||
} else { | ||
throw CommonError.invalidURLError(url: urlOrDID) | ||
} | ||
|
||
let (data, urlResponse) = try await URLSession.shared.data(from: url) | ||
|
||
guard | ||
let code = (urlResponse as? HTTPURLResponse)?.statusCode, | ||
200...299 ~= code | ||
else { | ||
throw CommonError.httpError( | ||
code: (urlResponse as? HTTPURLResponse)?.statusCode ?? 500, | ||
message: String(data: data, encoding: .utf8) ?? "" | ||
) | ||
} | ||
|
||
return data | ||
} | ||
} | ||
|
32 changes: 32 additions & 0 deletions
32
EdgeAgentSDK/Pollux/Sources/Models/JWT/JWTRevocationStatus.swift
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,32 @@ | ||
import Foundation | ||
|
||
struct JWTRevocationStatus: Codable { | ||
enum CredentialStatusListType: String, Codable { | ||
case statusList2021Entry = "StatusList2021Entry" | ||
} | ||
|
||
enum CredentialStatusPurpose: String, Codable { | ||
case revocation | ||
case suspension | ||
} | ||
|
||
let id: String | ||
let type: String | ||
let statusPurpose: CredentialStatusPurpose | ||
let statusListIndex: Int | ||
let statusListCredential: String | ||
} | ||
|
||
struct JWTRevocationStatusListCredential: Codable { | ||
struct StatusListCredentialSubject: Codable { | ||
let type: String | ||
let statusPurpose: String | ||
let encodedList: String | ||
} | ||
let context: [String] | ||
let type: [String] | ||
let id: String | ||
let issuer: String | ||
let issuanceDate: String | ||
let credentialSubject: StatusListCredentialSubject | ||
} |
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
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