Add Root Of Trust Resolver Injection Support (#146)

This commit is contained in:
Sydney 2023-06-14 18:43:20 -04:00 коммит произвёл GitHub
Родитель 6475c1be46
Коммит 659ca18865
Не найден ключ, соответствующий данной подписи
Идентификатор ключа GPG: 4AEE18F83AFDEB23
8 изменённых файлов: 125 добавлений и 6 удалений

Просмотреть файл

@ -45,6 +45,7 @@
55379AD725A8DF7A0048600A /* PresentationRequestValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 55379AD625A8DF7A0048600A /* PresentationRequestValidator.swift */; };
553D4C472821D89D00FD39A6 /* IssuanceRequestValidator.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553D4C462821D89D00FD39A6 /* IssuanceRequestValidator.swift */; };
5540226428C9587600C85C6D /* VCSDKConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5540226328C9587600C85C6D /* VCSDKConfiguration.swift */; };
555089702A3289A100186885 /* RootOfTrustResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5550896F2A3289A100186885 /* RootOfTrustResolver.swift */; };
55575738251BC575009979AB /* VCEntities.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5557572E251BC575009979AB /* VCEntities.framework */; };
5557573F251BC575009979AB /* VCEntities.h in Headers */ = {isa = PBXBuildFile; fileRef = 55575731251BC575009979AB /* VCEntities.h */; settings = {ATTRIBUTES = (Public, ); }; };
55575763251BC6CF009979AB /* AttestationsDescriptor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5557574B251BC6CE009979AB /* AttestationsDescriptor.swift */; };
@ -196,6 +197,7 @@
55379AD625A8DF7A0048600A /* PresentationRequestValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PresentationRequestValidator.swift; sourceTree = "<group>"; };
553D4C462821D89D00FD39A6 /* IssuanceRequestValidator.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IssuanceRequestValidator.swift; sourceTree = "<group>"; };
5540226328C9587600C85C6D /* VCSDKConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VCSDKConfiguration.swift; sourceTree = "<group>"; };
5550896F2A3289A100186885 /* RootOfTrustResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RootOfTrustResolver.swift; sourceTree = "<group>"; };
5557572E251BC575009979AB /* VCEntities.framework */ = {isa = PBXFileReference; explicitFileType = wrapper.framework; includeInIndex = 0; path = VCEntities.framework; sourceTree = BUILT_PRODUCTS_DIR; };
55575731251BC575009979AB /* VCEntities.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VCEntities.h; sourceTree = "<group>"; };
55575732251BC575009979AB /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
@ -462,6 +464,14 @@
path = validators;
sourceTree = "<group>";
};
5550896E2A32895800186885 /* Resolvers */ = {
isa = PBXGroup;
children = (
5550896F2A3289A100186885 /* RootOfTrustResolver.swift */,
);
path = Resolvers;
sourceTree = "<group>";
};
55575724251BC575009979AB = {
isa = PBXGroup;
children = (
@ -485,6 +495,7 @@
55575730251BC575009979AB /* VCEntities */ = {
isa = PBXGroup;
children = (
5550896E2A32895800186885 /* Resolvers */,
2F848B6F282D5D25005D3176 /* backup */,
55B4D2D42787BB600086A9F1 /* oidc */,
5517D28A25B90D0A00FBD239 /* exchange */,
@ -912,6 +923,7 @@
55575766251BC6CF009979AB /* LogoDisplayDescriptor.swift in Sources */,
55B4D35F27A330460086A9F1 /* RequestedVerifiableCredentialMapping.swift in Sources */,
5557576F251BC6CF009979AB /* IdTokenDescriptor.swift in Sources */,
555089702A3289A100186885 /* RootOfTrustResolver.swift in Sources */,
55B4D2E4278F48370086A9F1 /* RequestedVPToken.swift in Sources */,
551F3057252D02AB0081D5E7 /* TokenTimeConstraints.swift in Sources */,
55B4D2DC2787BE040086A9F1 /* PresentationExchangeField.swift in Sources */,

Просмотреть файл

@ -0,0 +1,10 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
/// Allows a different way of resolving the root of trust (aka Linked Domain Result)
/// that can be injected into Issuance and Presentation Service.
public protocol RootOfTrustResolver {
func resolve(did: String) async throws -> LinkedDomainResult
}

Просмотреть файл

@ -23,6 +23,7 @@
5531D3AE255F1F360002CC0E /* IdentifierService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531D3AD255F1F360002CC0E /* IdentifierService.swift */; };
5531D3B0255F68280002CC0E /* PairwiseService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5531D3AF255F68280002CC0E /* PairwiseService.swift */; };
553D4B0F281075C200FD39A6 /* MockVCSDKConfiguration.swift in Sources */ = {isa = PBXBuildFile; fileRef = 553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */; };
555089A82A3794D500186885 /* MockRootOfTrustResolver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */; };
555BDAEC2530AEC7001E7A18 /* VerifiableCredentialDataModel.xcdatamodeld in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAEA2530AEC7001E7A18 /* VerifiableCredentialDataModel.xcdatamodeld */; };
555BDAEE2530CF27001E7A18 /* CoreDataManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAED2530CF27001E7A18 /* CoreDataManager.swift */; };
555BDAF02530E4B9001E7A18 /* CoreDataManagerTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 555BDAEF2530E4B9001E7A18 /* CoreDataManagerTests.swift */; };
@ -96,6 +97,7 @@
5531D3AD255F1F360002CC0E /* IdentifierService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IdentifierService.swift; sourceTree = "<group>"; };
5531D3AF255F68280002CC0E /* PairwiseService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PairwiseService.swift; sourceTree = "<group>"; };
553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockVCSDKConfiguration.swift; sourceTree = "<group>"; };
555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MockRootOfTrustResolver.swift; sourceTree = "<group>"; };
555BDAEB2530AEC7001E7A18 /* Identifier.xcdatamodel */ = {isa = PBXFileReference; lastKnownFileType = wrapper.xcdatamodel; path = Identifier.xcdatamodel; sourceTree = "<group>"; };
555BDAED2530CF27001E7A18 /* CoreDataManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManager.swift; sourceTree = "<group>"; };
555BDAEF2530E4B9001E7A18 /* CoreDataManagerTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CoreDataManagerTests.swift; sourceTree = "<group>"; };
@ -253,6 +255,7 @@
55AF748F252FBF5B006A8B25 /* MockVCCryptoSecret.swift */,
553D4B0E281075C200FD39A6 /* MockVCSDKConfiguration.swift */,
5525397D252FCC7D003202D5 /* SecretStoreMock.swift */,
555089A72A3794D500186885 /* MockRootOfTrustResolver.swift */,
);
path = mocks;
sourceTree = "<group>";
@ -483,6 +486,7 @@
5525397E252FCC7D003202D5 /* SecretStoreMock.swift in Sources */,
55DA770A25BE469F009C32E0 /* MockPresentationRequestValidator.swift in Sources */,
550F1E6825116E86009AF467 /* MockTokenSigner.swift in Sources */,
555089A82A3794D500186885 /* MockRootOfTrustResolver.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};

Просмотреть файл

@ -26,6 +26,7 @@ public class IssuanceService {
let sdkLog: VCSDKLog
public convenience init(correlationVector: CorrelationHeader? = nil,
rootOfTrustResolver: RootOfTrustResolver? = nil,
urlSession: URLSession = URLSession.shared) {
self.init(formatter: IssuanceResponseFormatter(),
apiCalls: IssuanceNetworkCalls(correlationVector: correlationVector,
@ -35,6 +36,7 @@ public class IssuanceService {
requestValidator: IssuanceRequestValidator(),
identifierService: IdentifierService(),
linkedDomainService: LinkedDomainService(correlationVector: correlationVector,
rootOfTrustResolver: rootOfTrustResolver,
urlSession: urlSession),
pairwiseService: PairwiseService(correlationVector: correlationVector,
urlSession: urlSession),
@ -48,6 +50,7 @@ public class IssuanceService {
identifierService: IdentifierService,
linkedDomainService: LinkedDomainService,
pairwiseService: PairwiseService,
didVerificationResolver: RootOfTrustResolver? = nil,
sdkLog: VCSDKLog = VCSDKLog.sharedInstance) {
self.formatter = formatter
self.apiCalls = apiCalls

Просмотреть файл

@ -7,32 +7,71 @@ import PromiseKit
import VCNetworking
import VCEntities
enum LinkedDomainServiceError: String, Error {
case UndefinedResolver = "Root of Trust Resolver is undefined."
}
class LinkedDomainService {
private let didDocumentDiscoveryApiCalls: DiscoveryNetworking
private let wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworking
private let validator: DomainLinkageCredentialValidating
private let rootOfTrustResolver: RootOfTrustResolver?
public convenience init(correlationVector: CorrelationHeader? = nil,
rootOfTrustResolver: RootOfTrustResolver? = nil,
urlSession: URLSession = URLSession.shared) {
self.init(didDocumentDiscoveryApiCalls: DIDDocumentNetworkCalls(correlationVector: correlationVector,
urlSession: urlSession),
wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworkCalls(correlationVector: correlationVector,
urlSession: urlSession),
domainLinkageValidator: DomainLinkageCredentialValidator())
domainLinkageValidator: DomainLinkageCredentialValidator(),
rootOfTrustResolver: rootOfTrustResolver)
}
init(didDocumentDiscoveryApiCalls: DiscoveryNetworking,
wellKnownDocumentApiCalls: WellKnownConfigDocumentNetworking,
domainLinkageValidator: DomainLinkageCredentialValidating) {
domainLinkageValidator: DomainLinkageCredentialValidating,
rootOfTrustResolver: RootOfTrustResolver? = nil) {
self.didDocumentDiscoveryApiCalls = didDocumentDiscoveryApiCalls
self.wellKnownDocumentApiCalls = wellKnownDocumentApiCalls
self.validator = domainLinkageValidator
self.rootOfTrustResolver = rootOfTrustResolver
}
/// Validate Linked Domain using injected Resolver. If error is thrown, validate using well-known configuration path.
func validateLinkedDomain(from relyingPartyDid: String) -> Promise<LinkedDomainResult> {
return validateLinkedDomainUsingResolver(did: relyingPartyDid)
.recover { error in
return self.validateLinkedDomainUsingWellknownDocument(did: relyingPartyDid)
}
}
private func validateLinkedDomainUsingResolver(did: String) -> Promise<LinkedDomainResult> {
guard let rootOfTrustResolver = rootOfTrustResolver else {
VCSDKLog.sharedInstance.logInfo(message: "DID Verification Resolver is undefined, fetching Linked Domain Status from well-known DID configuration.")
return Promise<LinkedDomainResult>(error: LinkedDomainServiceError.UndefinedResolver)
}
/// Return a Promise instead of using built-in async functionality to fix async compatibility.
return Promise { seal in
Task {
do {
let result = try await rootOfTrustResolver.resolve(did: did)
seal.fulfill(result)
} catch {
VCSDKLog.sharedInstance.logInfo(message: "DID Verification Resolver failed, fetching Linked Domain Status from well-known DID configuration.")
seal.reject(error)
}
}
}
}
private func validateLinkedDomainUsingWellknownDocument(did: String) -> Promise<LinkedDomainResult> {
return firstly {
getDidDocument(from: relyingPartyDid)
getDidDocument(from: did)
}.then { identifierDocument in
self.validateDomain(from: identifierDocument)
}

Просмотреть файл

@ -32,6 +32,7 @@ public class PresentationService {
let sdkLog: VCSDKLog
public convenience init(correlationVector: CorrelationHeader? = nil,
rootOfTrustResolver: RootOfTrustResolver? = nil,
urlSession: URLSession = URLSession.shared) {
self.init(formatter: PresentationResponseFormatter(),
presentationApiCalls: PresentationNetworkCalls(correlationVector: correlationVector,
@ -40,6 +41,7 @@ public class PresentationService {
urlSession: urlSession),
requestValidator: PresentationRequestValidator(),
linkedDomainService: LinkedDomainService(correlationVector: correlationVector,
rootOfTrustResolver: rootOfTrustResolver,
urlSession: urlSession),
identifierService: IdentifierService(),
pairwiseService: PairwiseService(correlationVector: correlationVector,

Просмотреть файл

@ -22,6 +22,33 @@ class LinkedDomainServiceTests: XCTestCase {
MockDomainLinkageCredentialValidator.wasValidateCalled = false
}
func testValidate_WithResolver_ReturnsResult() {
// Arrange
let expectedDomainUrl = "expected domain"
func mockResolve(did: String) throws -> LinkedDomainResult {
return .linkedDomainVerified(domainUrl: expectedDomainUrl)
}
let mockRootOfTrustResolver = MockRootOfTrustResolver(mockResolve: mockResolve)
service = setUpService(rootOfTrustResolver: mockRootOfTrustResolver)
let expec = self.expectation(description: "Fire")
// Act
service.validateLinkedDomain(from: validDid).done {
result in
// Assert
XCTAssertEqual(result, .linkedDomainVerified(domainUrl: expectedDomainUrl))
expec.fulfill()
}.catch { error in
XCTFail()
expec.fulfill()
}
wait(for: [expec], timeout: 5)
}
func testLinkedDomainVerifiedResult() {
let expec = self.expectation(description: "Fire")
@ -78,7 +105,8 @@ class LinkedDomainServiceTests: XCTestCase {
}
private func setUpService(serviceEndpointType: String = Constants.LINKED_DOMAINS_SERVICE_ENDPOINT_TYPE,
isValid: Bool = true) -> LinkedDomainService {
isValid: Bool = true,
rootOfTrustResolver: RootOfTrustResolver? = nil) -> LinkedDomainService {
let endpoint = IdentifierDocumentServiceEndpoint(origins: [mockDomainUrl])
@ -97,8 +125,9 @@ class LinkedDomainServiceTests: XCTestCase {
let validator = MockDomainLinkageCredentialValidator(isValid: isValid)
return LinkedDomainService(didDocumentDiscoveryApiCalls: discoveryApiCalls,
wellKnownDocumentApiCalls: wellKnownApiCalls,
domainLinkageValidator: validator)
wellKnownDocumentApiCalls: wellKnownApiCalls,
domainLinkageValidator: validator,
rootOfTrustResolver: rootOfTrustResolver)
}
let validDid = "did:ion:EiD89iut8PMhRz5zSlPl_zkHgEfRBiA9zdXYwAdrk1_JYg:eyJkZWx0YSI6eyJwYXRjaGVzIjpbeyJhY3Rpb24iOiJyZXBsYWNlIiwiZG9jdW1lbnQiOnsicHVibGljX2tleXMiOlt7ImlkIjoic2lnX2FkODRkZjIzIiwiandrIjp7ImNydiI6InNlY3AyNTZrMSIsImt0eSI6IkVDIiwieCI6IllMM2xlNC12YlNsa2ZINnJoQWJlbEJXTDByVlBieVJYTGtIUmx3REFZOTQiLCJ5IjoiYWFDZG1idjF0R1htZU1QWUdUY0ZqLUFnc2d2c2RNLXlGR2tnVU1QX04tVSJ9LCJwdXJwb3NlIjpbImF1dGgiLCJnZW5lcmFsIl0sInR5cGUiOiJFY2RzYVNlY3AyNTZrMVZlcmlmaWNhdGlvbktleTIwMTkifV0sInNlcnZpY2VfZW5kcG9pbnRzIjpbeyJlbmRwb2ludCI6eyJvcmlnaW5zIjpbImh0dHBzOi8vY29udG9zb3VuaXZlcnNpdHktZGlkZGVtby5henVyZXdlYnNpdGVzLm5ldC8iXX0sImlkIjoibGlua2VkZG9tYWlucyIsInR5cGUiOiJMaW5rZWREb21haW5zIn1dfX1dLCJ1cGRhdGVfY29tbWl0bWVudCI6IkVpQS1HSFJRVkcxV2F0bGprSVQxOGxvbWZXbTY0N2QwYmtlRmpCR2tMUjZMRXcifSwic3VmZml4X2RhdGEiOnsiZGVsdGFfaGFzaCI6IkVpQXBta0dpa2RiX3pHb1JrVU5vbmd0TXJlZS1xTjhPeEtsVmJvYUk5ZG9DdXciLCJyZWNvdmVyeV9jb21taXRtZW50IjoiRWlCb19vVkJYSEJmZGhfRFpkZjlqU3ZwQ1lnRmNkcXc3cDZFa1pPanU3bUREQSJ9fQ"

Просмотреть файл

@ -0,0 +1,20 @@
/*---------------------------------------------------------------------------------------------
* Copyright (c) Microsoft Corporation. All rights reserved.
* Licensed under the MIT License. See License.txt in the project root for license information.
*--------------------------------------------------------------------------------------------*/
import XCTest
import VCEntities
struct MockRootOfTrustResolver: RootOfTrustResolver {
let mockResolve: (String) throws -> LinkedDomainResult
init(mockResolve: @escaping (String) throws -> LinkedDomainResult) {
self.mockResolve = mockResolve
}
func resolve(did: String) async throws -> LinkedDomainResult {
return try mockResolve(did)
}
}