-
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.
- Loading branch information
Showing
19 changed files
with
1,401 additions
and
1,363 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
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,66 @@ | ||
/* | ||
Copyright (c) 2023 European Commission | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
|
||
import Foundation | ||
|
||
/// This structure is used to store document claim values and associated metadata. | ||
/// It provides functionality for generating string representations for | ||
/// debugging and display purposes. | ||
@DebugDescription | ||
public struct DocClaim: Equatable, CustomStringConvertible, CustomDebugStringConvertible, Sendable { | ||
public init(name: String, displayName: String? = nil, dataValue: DocDataValue, stringValue: String, valueType: String? = nil, isOptional: Bool = false, order: Int = 0, namespace: String? = nil, children: [DocClaim]? = nil) { | ||
self.name = name | ||
self.displayName = displayName | ||
self.dataValue = dataValue | ||
self.valueType = valueType | ||
self.stringValue = stringValue | ||
self.isOptional = isOptional | ||
self.order = order | ||
self.namespace = namespace | ||
self.children = children | ||
} | ||
/// The namespace of the claim (if document is a mso-mdoc) | ||
public let namespace: String? | ||
/// The name of the claim. | ||
public let name: String | ||
/// The display name of the claim, originated from VCI metadata/claims. | ||
public let displayName: String? | ||
/// The value of the claim as a string. | ||
public let stringValue: String | ||
/// The value of the claim as a `DocDataValue` (enum with associated values) | ||
public let dataValue: DocDataValue | ||
/// The type of the value of the claim, originated from VCI metadata/claims. | ||
public let valueType: String? | ||
/// A flag indicating whether the claim is optional, originated from VCI metadata/claims. | ||
public var isOptional: Bool = false | ||
/// The order of the claim in the document. | ||
public var order: Int = 0 | ||
/// A string for Wallet UI usage to define the style of the claim. | ||
public var style: String? | ||
/// The children of the claim. | ||
public var children: [DocClaim]? | ||
/// Description of the claim. | ||
public var description: String { "\(name): \(stringValue)" } | ||
/// Debug description of the claim. | ||
public var debugDescription: String { "\(order). \t\(name): \(stringValue)" } | ||
|
||
/// Adds a child to the claim. | ||
public mutating func add(child: DocClaim) { | ||
if children == nil { children = [] } | ||
children!.append(child) | ||
} | ||
} |
158 changes: 158 additions & 0 deletions
158
Sources/MdocDataModel18013/DocumentClaims/DocClaimsDecodable.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,158 @@ | ||
/* | ||
Copyright (c) 2023 European Commission | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
// ClaimsDecodable.swift | ||
|
||
import Foundation | ||
import SwiftCBOR | ||
|
||
/// A conforming type represents claims data. | ||
/// | ||
/// Can be decoded by CBOR or SD-JWT data | ||
public protocol DocClaimsDecodable: Sendable, AgeAttesting { | ||
/// The unique identifier of the document. | ||
var id: String { get } | ||
/// The date and time the document was created. | ||
var createdAt: Date { get } | ||
/// The date and time the document was last modified. | ||
var modifiedAt: Date? { get } | ||
/// The display name of the document. | ||
var displayName: String? { get } | ||
// The document type. For CBOR (mso_mdoc) documents is native, for SD-JWT (vc+sd-jwt) documents is the type of the document. | ||
var docType: String? { get } | ||
// document claims in a format agnostic way | ||
var docClaims: [DocClaim] { get } | ||
/// The format of the document data. | ||
var docDataFormat: DocDataFormat { get } | ||
} // end protocol | ||
|
||
/// Methods to extract CBOR values. | ||
extension DocClaimsDecodable { | ||
|
||
public static func getCborItemValue<T>(_ issuerSigned: IssuerSigned, _ s: String) -> T? { | ||
let nameSpaceItems = Self.getCborSignedItems(issuerSigned) | ||
guard let nameSpaceItems else { return nil } | ||
return Self.getCborItemValue(nameSpaceItems, string: s) | ||
} | ||
|
||
static func getCborItemValue<T>(_ nameSpaceItems: [NameSpace: [IssuerSignedItem]], string name: String) -> T? { | ||
for (_,v) in nameSpaceItems { | ||
if let item = v.first(where: { name == $0.elementIdentifier }) { return item.getTypedValue() } | ||
} | ||
return nil | ||
} | ||
|
||
public static func getCborSignedItems(_ issuerSigned: IssuerSigned, _ ns: [NameSpace]? = nil) -> [String: [IssuerSignedItem]]? { | ||
guard var nameSpaces = issuerSigned.issuerNameSpaces?.nameSpaces else { return nil } | ||
if let ns { nameSpaces = nameSpaces.filter { ns.contains($0.key) } } | ||
return nameSpaces | ||
} | ||
|
||
|
||
/// Extracts age-over values from the provided namespaces and updates the given dictionary with the results. | ||
/// | ||
/// - Parameters: | ||
/// - nameSpaces: A dictionary where the key is a `NameSpace` and the value is an array of `IssuerSignedItem`. | ||
/// - ageOverXX: An inout parameter that is a dictionary where the key is an integer representing the age and the value is a boolean indicating whether the age condition is met. | ||
public static func extractAgeOverValues(_ nameSpaces: [NameSpace: [IssuerSignedItem]], _ ageOverXX: inout [Int: Bool]) { | ||
for (_, items) in nameSpaces { | ||
for item in items { | ||
let k = item.elementIdentifier | ||
if !k.hasPrefix("age_over_") { continue } | ||
if let age = Int(k.suffix(k.count - 9)) { | ||
let b: Bool? = item.getTypedValue() | ||
if let b { ageOverXX[age] = b } | ||
} | ||
} | ||
} | ||
} | ||
|
||
/// Determines if there are more than two age-over element identifiers in the provided list. | ||
/// | ||
/// - Parameters: | ||
/// - reqDocType: The required document type. | ||
/// - reqNamespace: The required namespace.pa | ||
/// - ageAttest: An instance conforming to the `AgeAttesting` protocol.s | ||
/// - reqElementIdentifiers: A list of data element identifiers. | ||
/// - Returns: A set of strings representing the identifiers that meet the criteria. } | ||
|
||
public static func moreThan2AgeOverElementIdentifiers(_ reqDocType: DocType, _ reqNamespace: NameSpace, _ ageAttest: any AgeAttesting, _ reqElementIdentifiers: [DataElementIdentifier]) -> Set<String> { | ||
// special case for maximum two age_over_NN data elements shall be returned | ||
guard reqDocType == IsoMdlModel.isoDocType, reqNamespace == IsoMdlModel.isoNamespace else { return Set() } | ||
let ages = reqElementIdentifiers.filter { $0.hasPrefix("age_over_")}.compactMap { k in Int(k.suffix(k.count - 9)) } | ||
let agesDict = ageAttest.max2AgesOver(ages: ages) | ||
return Set( agesDict.filter { $1 == false }.keys.map { "age_over_\($0)" }) | ||
} | ||
|
||
/// Extracts a CBOR value. | ||
/// | ||
/// - Parameters: | ||
/// - name: The name associated with the CBOR value. | ||
/// - cborValue: The CBOR value to be processed.pa | ||
/// - bDebugDisplay: A boolean flag indicating whether to enable debug display.s | ||
/// - ns: The namespace associated with the CBOR value. | ||
/// - order: The order in which the value should be processed. | ||
/// - labels: A dictionary where the key is the elementIdentifier and the value is a string representing the label. | ||
/// - Returns: A `DocClaim` object containing the extracted display string or image. | ||
public static func extractCborClaim(_ name: String, _ cborValue: CBOR, _ bDebugDisplay: Bool, _ namespace: NameSpace, _ order: Int, _ displayNames: [String:String]? = nil, _ mandatory: [String:Bool]? = nil, _ valueTypes: [String:String]? = nil) -> DocClaim { | ||
var stringValue = bDebugDisplay ? cborValue.debugDescription : cborValue.description | ||
let dt = cborValue.mdocDataValue ?? .string(stringValue) | ||
if name == "sex", let isex = Int(stringValue), isex <= 2 { stringValue = NSLocalizedString(isex == 1 ? "male" : "female", comment: "") } | ||
let isMandatory = mandatory?[name] ?? true | ||
var node = DocClaim(name: name, displayName: displayNames?[name], dataValue: dt, stringValue: stringValue, valueType: valueTypes?[name], isOptional: !isMandatory, order: order, namespace: namespace) | ||
if case let .map(m) = cborValue { | ||
let innerJsonMap = CBOR.decodeDictionary(m, unwrap: false) | ||
for (o2,(k,v)) in innerJsonMap.enumerated() { | ||
guard let cv = v as? CBOR else { continue } | ||
node.add(child: extractCborClaim(k, cv, bDebugDisplay, namespace, o2, displayNames, mandatory, valueTypes)) | ||
} | ||
} else if case let .array(a) = cborValue { | ||
let innerJsonArray = CBOR.decodeList(a, unwrap: false) | ||
for (o2,v) in innerJsonArray.enumerated() { | ||
guard let cv = v as? CBOR else { continue } | ||
let k = "\(name)[\(o2)]" | ||
node.add(child: extractCborClaim(k, cv, bDebugDisplay, namespace, o2, displayNames, mandatory, valueTypes)) | ||
} | ||
} | ||
return node | ||
} | ||
|
||
/// Extracts display strings and images from the provided namespaces and populates the given arrays. | ||
/// | ||
/// - Parameters: | ||
/// - nameSpaces: A dictionary where the key is a `NameSpace` and the value is an array of `IssuerSignedItem`. | ||
/// - docClaims: An inout parameter that will be populated with `DocClaim` items extracted from the namespaces. | ||
/// - claimDisplayNames: A dictionary where the key is the elementIdentifier and the value is a string representing the label. | ||
/// - mandatoryClaims: A dictionary where the key is the elementIdentifier and the value is a boolean indicating whether the claim is mandatory. | ||
/// - claimValueTypes: A dictionary where the key is the elementIdentifier and the value is a string representing the value type. | ||
/// - nsFilter: An optional array of `NameSpace` to filter/sort the extraction. Defaults to `nil`. | ||
public static func extractCborClaims(_ nameSpaces: [NameSpace: [IssuerSignedItem]], _ docClaims: inout [DocClaim], _ claimDisplayNames: [NameSpace: [String: String]]? = nil, _ mandatoryClaims: [NameSpace: [String: Bool]]? = nil, _ claimValueTypes: [NameSpace: [String: String]]? = nil, nsFilter: [NameSpace]? = nil) { | ||
let bDebugDisplay = UserDefaults.standard.bool(forKey: "DebugDisplay") | ||
var order = 0 | ||
let nsFilterUsed = nsFilter ?? Array(nameSpaces.keys) | ||
for ns in nsFilterUsed { | ||
let items = nameSpaces[ns] ?? [] | ||
for item in items { | ||
let n = extractCborClaim(item.elementIdentifier, item.elementValue, bDebugDisplay, ns, order, claimDisplayNames?[ns], mandatoryClaims?[ns], claimValueTypes?[ns]) | ||
docClaims.append(n) | ||
order = order + 1 | ||
} | ||
} | ||
} | ||
|
||
|
||
} // end extension | ||
|
43 changes: 22 additions & 21 deletions
43
.../MdocKnownDocTypes/MdocModelFactory.swift → ...entClaims/DocClaimsDecodableFactory.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 |
---|---|---|
@@ -1,21 +1,22 @@ | ||
/* | ||
Copyright (c) 2023 European Commission | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
public protocol MdocModelFactory: Sendable { | ||
func makeMdocDecodable(id: String, createdAt: Date, issuerSigned: IssuerSigned, devicePrivateKey: CoseKeyPrivate, docType: String, displayName: String?, statusDescription: String?) -> (any MdocDecodable)? | ||
} | ||
/* | ||
Copyright (c) 2023 European Commission | ||
|
||
Licensed under the Apache License, Version 2.0 (the "License"); | ||
you may not use this file except in compliance with the License. | ||
You may obtain a copy of the License at | ||
|
||
http://www.apache.org/licenses/LICENSE-2.0 | ||
|
||
Unless required by applicable law or agreed to in writing, software | ||
distributed under the License is distributed on an "AS IS" BASIS, | ||
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. | ||
See the License for the specific language governing permissions and | ||
limitations under the License. | ||
*/ | ||
|
||
import Foundation | ||
|
||
/// A protocol to create a ``DocClaimsDecodable`` from a cbor encoded ``IssuerSigned`` struct | ||
public protocol DocClaimsDecodableFactory: Sendable { | ||
func makeClaimsDecodableFromCbor(id: String, createdAt: Date, issuerSigned: IssuerSigned, displayName: String?, claimDisplayNames: [NameSpace: [String: String]]?, mandatoryClaims: [NameSpace: [String: Bool]]?, claimValueTypes: [NameSpace: [String: String]]?) -> (any DocClaimsDecodable)? | ||
} |
22 changes: 22 additions & 0 deletions
22
Sources/MdocDataModel18013/DocumentClaims/DocDataFormat.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,22 @@ | ||
import Foundation | ||
/// Format of document data | ||
/// ``cbor``: IssuerSigned struct cbor encoded | ||
/// ``sdjwt``: vc+sd-jwt encoded | ||
/// | ||
/// Raw value must be a 4-length string due to keychain requirements | ||
public enum DocDataFormat: String, Sendable, CustomStringConvertible, CustomDebugStringConvertible, Codable { | ||
case cbor = "cbor" | ||
case sdjwt = "sjwt" | ||
|
||
/// A description to display | ||
public var description: String { | ||
switch self { | ||
case .cbor: return "mso_mdoc" | ||
case .sdjwt: return "vc+sd-jwt" | ||
} | ||
} | ||
|
||
public var debugDescription: String { | ||
description | ||
} | ||
} |
This file was deleted.
Oops, something went wrong.
Oops, something went wrong.