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

Commit

Permalink
Did jwk creation and resolution (#8)
Browse files Browse the repository at this point in the history
  • Loading branch information
amika-sq authored Jan 4, 2024
1 parent 2721361 commit ced0e52
Show file tree
Hide file tree
Showing 9 changed files with 591 additions and 5 deletions.
6 changes: 6 additions & 0 deletions Sources/tbDEX/Dids/Did.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
import Foundation

protocol Did {
var uri: String { get }
var keyManager: KeyManager { get }
}
286 changes: 286 additions & 0 deletions Sources/tbDEX/Dids/DidDocument.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,286 @@
import Foundation

/// Decentralized Identifier (DID) Document
///
/// A set of data describing the DID subject including mechanisms such as:
/// * cryptographic public keys - used to authenticate itself and prove association
/// with the DID
/// * services - means of communicating or interacting with the DID subject or associated
/// entities via one or more service endpoints. Examples include discovery services, agent
/// services, social networking services, file storage services, and verifiable credential
/// repository services.
///
/// A DID Document can be retrieved by _resolving_ a DID URI
struct DidDocument: Codable {

let context: String?

/// The DID URI for a particular DID subject is expressed using the id property in the DID document.
let id: String

/// A DID subject can have multiple identifiers for different purposes, or at
/// different times. The assertion that two or more DIDs (or other types of URI)
/// refer to the same DID subject can be made using the alsoKnownAs property.
var alsoKnownAs: [String]?

/// A DID controller is an entity that is authorized to make changes to a
/// DID document. The process of authorizing a DID controller is defined
/// by the DID method.
var controller: DidController?

/// Cryptographic public keys, which can be used to authenticate or authorize
/// interactions with the DID subject or associated parties.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#verification-methods)
var verificationMethod: [DidVerificationMethod]?

/// Services are used in DID documents to express ways of communicating with
/// the DID subject or associated entities.
/// A service can be any type of service the DID subject wants to advertise.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#services)
var service: [DidService]?

/// The assertionMethod verification relationship is used to specify how the
/// DID subject is expected to express claims, such as for the purposes of
/// issuing a Verifiable Credential
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#assertion)
var assertionMethod: [String]?

/// The authentication verification relationship is used to specify how the
/// DID subject is expected to be authenticated, for purposes such as logging
/// into a website or engaging in any sort of challenge-response protocol.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#authentication)
var authentication: [String]?

/// The keyAgreement verification relationship is used to specify how an
/// entity can generate encryption material in order to transmit confidential
/// information intended for the DID subject, such as for the purposes of
/// establishing a secure communication channel with the recipient
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#key-agreement)
var keyAgreement: [String]?

/// The capabilityDelegation verification relationship is used to specify a
/// mechanism that might be used by the DID subject to delegate a
/// cryptographic capability to another party, such as delegating the
/// authority to access a specific HTTP API to a subordinate.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#capability-delegation)
var capabilityDelegation: [String]?

/// The capabilityInvocation verification relationship is used to specify a
/// verification method that might be used by the DID subject to invoke a
/// cryptographic capability, such as the authorization to update the
/// DID Document
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#capability-invocation)
var capabilityInvocation: [String]?

init(
context: String? = nil,
id: String,
alsoKnownAs: [String]? = nil,
controller: DidController? = nil,
verificationMethod: [DidVerificationMethod]? = nil,
service: [DidService]? = nil,
assertionMethod: [String]? = nil,
authentication: [String]? = nil,
keyAgreement: [String]? = nil,
capabilityDelegation: [String]? = nil,
capabilityInvocation: [String]? = nil
) {
self.context = context
self.id = id
self.alsoKnownAs = alsoKnownAs
self.controller = controller
self.verificationMethod = verificationMethod
self.service = service
self.assertionMethod = assertionMethod
self.authentication = authentication
self.keyAgreement = keyAgreement
self.capabilityDelegation = capabilityDelegation
self.capabilityInvocation = capabilityInvocation
}

enum CodingKeys: String, CodingKey {
case context = "@context"
case id
case alsoKnownAs
case controller
case verificationMethod
case service
case assertionMethod
case authentication
case keyAgreement
case capabilityDelegation
case capabilityInvocation
}

/// Contains metadata about the DID document contained in the didDocument
/// property. This metadata typically does not change between invocations of
/// the resolve and resolveRepresentation functions unless the DID document
/// changes, as it represents metadata about the DID document.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#dfn-diddocumentmetadata)
struct Metadata: Codable {

/// Timestamp of the Create operation. The value of the property MUST be a
/// string formatted as an XML Datetime normalized to UTC 00:00:00 and
/// without sub-second decimal precision. For example: 2020-12-20T19:17:47Z.
let created: String?

/// Timestamp of the last Update operation for the document version which was
/// resolved. The value of the property MUST follow the same formatting rules
/// as the created property. The updated property is omitted if an Update
/// operation has never been performed on the DID document. If an updated
/// property exists, it can be the same value as the created property
/// when the difference between the two timestamps is less than one second.
let updated: String?

/// If a DID has been deactivated, DID document metadata MUST include this
/// property with the boolean value true. If a DID has not been deactivated,
/// this property is OPTIONAL, but if included, MUST have the boolean value
/// false.
let deactivated: Bool?

/// Indicates the version of the last Update operation for the document version
/// which was resolved.
let versionId: String?

/// Indicates the timestamp of the next Update operation. The value of the
/// property MUST follow the same formatting rules as the created property.
let nextUpdate: String?

/// If the resolved document version is not the latest version of the document.
/// It indicates the timestamp of the next Update operation. The value of the
/// property MUST follow the same formatting rules as the created property.
let nextVersionId: String?

/// A DID method can define different forms of a DID that are logically
/// equivalent. An example is when a DID takes one form prior to registration
/// in a verifiable data registry and another form after such registration.
/// In this case, the DID method specification might need to express one or
/// more DIDs that are logically equivalent to the resolved DID as a property
/// of the DID document. This is the purpose of the equivalentId property.
let equivalentId: String?

/// The canonicalId property is identical to the equivalentId property except:
/// * It is associated with a single value rather than a set
/// * The DID is defined to be the canonical ID for the DID subject within
/// the scope of the containing DID document.
let canonicalId: String?

internal init(
created: String? = nil,
updated: String? = nil,
deactivated: Bool? = nil,
versionId: String? = nil,
nextUpdate: String? = nil,
nextVersionId: String? = nil,
equivalentId: String? = nil,
canonicalId: String? = nil
) {
self.created = created
self.updated = updated
self.deactivated = deactivated
self.versionId = versionId
self.nextUpdate = nextUpdate
self.nextVersionId = nextVersionId
self.equivalentId = equivalentId
self.canonicalId = canonicalId
}
}
}

/// DID Controller
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#did-controller)
/// This is necessary, as the controller can be either a String, or a set of strings.
/// Swift does not allow multiple types, and must handle both cases when encoding/decoding.
struct DidController: Codable {
var value: Either<String, [String]>

init(_ value: Either<String, [String]>) {
self.value = value
}

enum CodingKeys: CodingKey {
case value
}

enum Either<A, B> {
case left(A)
case right(B)
}

init(from decoder: Decoder) throws {
let container = try decoder.singleValueContainer()
if let singleValue = try? container.decode(String.self) {
self.value = .left(singleValue)
} else if let arrayValue = try? container.decode([String].self) {
self.value = .right(arrayValue)
} else {
throw DecodingError.typeMismatch(
DidController.self,
DecodingError.Context(
codingPath: decoder.codingPath,
debugDescription: "Expected either String or [String]"
)
)
}
}

func encode(to encoder: Encoder) throws {
var container = encoder.singleValueContainer()
switch value {
case .left(let singleValue):
try container.encode(singleValue)
case .right(let arrayValue):
try container.encode(arrayValue)
}
}
}

/// A DID document can express verification methods, such as cryptographic
/// public keys, which can be used to authenticate or authorize interactions
/// with the DID subject or associated parties. For example,
/// a cryptographic public key can be used as a verification method with
/// respect to a digital signature; in such usage, it verifies that the
/// signer could use the associated cryptographic private key
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#verification-methods)
struct DidVerificationMethod: Codable {
let id: String
let type: String
let controller: String
let publicKeyJwk: Jwk?
let publicKeyMultibase: String?

init(
id: String,
type: String,
controller: String,
publicKeyJwk: Jwk? = nil,
publicKeyMultibase: String? = nil
) {
self.id = id
self.type = type
self.controller = controller
self.publicKeyJwk = publicKeyJwk
self.publicKeyMultibase = publicKeyMultibase
}
}

/// Services are used in DID documents to express ways of communicating with
/// the DID subject or associated entities.
/// A service can be any type of service the DID subject wants to advertise.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#services)
struct DidService: Codable {
let id: String
let type: String
let serviceEndpoint: String
}
63 changes: 63 additions & 0 deletions Sources/tbDEX/Dids/DidJwk.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
import Foundation

struct DidJwk: Did {

struct Options {
let algorithm: Jwk.Algorithm
let curve: Jwk.Curve
}

let uri: String
let keyManager: KeyManager

init(keyManager: KeyManager, options: Options) throws {
let keyAlias = try keyManager.generatePrivateKey(algorithm: options.algorithm, curve: options.curve)
let publicKey = try keyManager.getPublicKey(keyAlias: keyAlias)
let publicKeyBase64Url = try JSONEncoder().encode(publicKey).base64UrlEncodedString()

self.uri = "did:jwk:\(publicKeyBase64Url)"
self.keyManager = keyManager
}

/// Resolves a `did:jwk` URI into a `DidResolution.Result`
/// - Parameter didUri: The DID URI to resolve
/// - Returns: `DidResolution.Result` containing the resolved DID Document.
static func resolve(didUri: String) -> DidResolution.Result {
let parsedDid: ParsedDid
do {
parsedDid = try ParsedDid(uri: didUri)
} catch {
return DidResolution.Result.invalidDid()
}

guard parsedDid.method == "jwk" else {
return DidResolution.Result.invalidDid()
}

let jwk: Jwk

do {
jwk = try JSONDecoder().decode(Jwk.self, from: try parsedDid.id.decodeBase64Url())
} catch {
return DidResolution.Result.invalidDid()
}

let verifiationMethod = DidVerificationMethod(
id: "\(didUri)#0",
type: "JsonWebKey2020",
controller: didUri,
publicKeyJwk: jwk
)

let didDocument = DidDocument(
id: didUri,
verificationMethod: [verifiationMethod],
assertionMethod: [verifiationMethod.id],
authentication: [verifiationMethod.id],
capabilityDelegation: [verifiationMethod.id],
capabilityInvocation: [verifiationMethod.id]
)

return DidResolution.Result(didDocument: didDocument)
}
}
Loading

0 comments on commit ced0e52

Please sign in to comment.