Skip to content

Commit

Permalink
Merge pull request #5 from TBD54566975/did-resolution
Browse files Browse the repository at this point in the history
DID JWK Resolution
  • Loading branch information
mistermoe authored Dec 29, 2023
2 parents 7ba39ca + 2e784e2 commit d7337d9
Show file tree
Hide file tree
Showing 15 changed files with 664 additions and 15 deletions.
6 changes: 6 additions & 0 deletions example/tbdex_example.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,13 @@
import 'dart:convert';

import 'package:tbdex/tbdex.dart';

void main() async {
final keyManager = InMemoryKeyManager();

final did = await DidJwk.create(keyManager: keyManager);
print(did.uri);

final didResolutionResult = DidJwk.resolve(did.uri);
print(jsonEncode(didResolutionResult));
}
64 changes: 64 additions & 0 deletions lib/src/crypto/secp256k1.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,64 @@
import "dart:math";
import 'dart:typed_data';

import "package:pointycastle/pointycastle.dart";
import 'package:tbdex/src/crypto/dsa.dart';
import 'package:tbdex/src/crypto/jwk.dart';
import "package:pointycastle/ecc/curves/secp256k1.dart";
import "package:pointycastle/key_generators/ec_key_generator.dart";
import "package:pointycastle/random/fortuna_random.dart";
import 'package:tbdex/src/extensions/bigint.dart';

class Secp256k1 implements Dsa {
@override
// TODO: implement algorithm
String get algorithm => throw UnimplementedError();

@override
Future<Jwk> computePublicKey(Jwk privateKey) {
// TODO: implement computePublicKey
throw UnimplementedError();
}

@override
// TODO: implement curve
String get curve => throw UnimplementedError();

@override
Future<Jwk> generatePrivateKey() {
final generatorParams = ECKeyGeneratorParameters(ECCurve_secp256k1());
final generator = ECKeyGenerator();

final random = Random.secure();
final seed =
Uint8List.fromList(List.generate(32, (_) => random.nextInt(256)));

final rand = FortunaRandom();
rand.seed(KeyParameter(seed));

generator.init(ParametersWithRandom(generatorParams, rand));

final keyPair = generator.generateKeyPair();
final privateKey = keyPair.privateKey as ECPrivateKey;

privateKey.d!.toBytes();

throw UnimplementedError();
}

@override
// TODO: implement name
DsaName get name => throw UnimplementedError();

@override
Future<Uint8List> sign(Jwk privateKey, Uint8List payload) {
// TODO: implement sign
throw UnimplementedError();
}

@override
Future<void> verify(Jwk publicKey, Uint8List payload, Uint8List signature) {
// TODO: implement verify
throw UnimplementedError();
}
}
104 changes: 104 additions & 0 deletions lib/src/dids/did_document.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,104 @@
import 'package:tbdex/src/dids/did_service.dart';
import 'package:tbdex/src/dids/did_verification_method.dart';

/// 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
class DidDocument {
final String? context;
final String id;
final List<String>? alsoKnownAs;
final dynamic controller; // String or List<String>

/// cryptographic public keys, which can be used to authenticate or authorize
/// interactions with the DID subject or associated parties.
/// [spec reference](https://www.w3.org/TR/did-core/#verification-methods)
final List<DidVerificationMethod>? verificationMethod;

/// 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)
final List<DidService>? service;

/// 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)
final List<String>? assertionMethod;

/// 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/#key-agreement)
final List<String>? authentication;

/// 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/#authentication)
final List<String>? keyAgreement;

/// 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)
final List<String>? capabilityDelegation;

/// 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)
final List<String>? capabilityInvocation;

DidDocument({
this.context,
required this.id,
this.alsoKnownAs,
this.controller,
this.verificationMethod,
this.service,
this.assertionMethod,
this.authentication,
this.keyAgreement,
this.capabilityDelegation,
this.capabilityInvocation,
});

Map<String, dynamic> toJson() {
final json = {
'context': context,
'id': id,
'alsoKnownAs': alsoKnownAs,
'controller': controller,
'verificationMethod':
verificationMethod?.map((vm) => vm.toJson()).toList(),
'service': service?.map((s) => s.toJson()).toList(),
'assertionMethod': assertionMethod,
'authentication': authentication,
'keyAgreement': keyAgreement,
'capabilityDelegation': capabilityDelegation,
'capabilityInvocation': capabilityInvocation,
};

json.removeWhere((key, value) => value == null);

return json;
}
}
44 changes: 44 additions & 0 deletions lib/src/dids/did_document_metadata.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
/// 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)
class DidDocumentMetadata {
final String? created;
final String? updated;
final bool? deactivated;
final String? versionId;
final String? nextUpdate;
final String? nextVersionId;
final String? equivalentId;
final String? canonicalId;

DidDocumentMetadata({
this.created,
this.updated,
this.deactivated,
this.versionId,
this.nextUpdate,
this.nextVersionId,
this.equivalentId,
this.canonicalId,
});

Map<String, dynamic> toJson() {
final json = {
'created': created,
'updated': updated,
'deactivated': deactivated,
'versionId': versionId,
'nextUpdate': nextUpdate,
'nextVersionId': nextVersionId,
'equivalentId': equivalentId,
'canonicalId': canonicalId,
};

json.removeWhere((key, value) => value == null);

return json;
}
}
50 changes: 48 additions & 2 deletions lib/src/dids/did_jwk.dart
Original file line number Diff line number Diff line change
@@ -1,10 +1,14 @@
import 'dart:convert';

import 'package:tbdex/src/crypto/dsa.dart';

import 'package:tbdex/src/dids/did.dart';
import 'package:tbdex/src/crypto/dsa.dart';
import 'package:tbdex/src/crypto/jwk.dart';
import 'package:tbdex/src/dids/did_uri.dart';
import 'package:tbdex/src/extensions/json.dart';
import 'package:tbdex/src/dids/did_document.dart';
import 'package:tbdex/src/crypto/key_manager.dart';
import 'package:tbdex/src/dids/did_verification_method.dart';
import 'package:tbdex/src/dids/did_resolution_result.dart';

final base64UrlEncoder = Base64Codec.urlSafe().encoder;

Expand Down Expand Up @@ -39,4 +43,46 @@ class DidJwk implements Did {
keyManager: keyManager,
);
}

static DidResolutionResult resolve(String didUri) {
final DidUri parsedDidUri;

try {
parsedDidUri = DidUri.parse(didUri);
} on Exception {
return DidResolutionResult.invalidDid();
}

if (parsedDidUri.method != 'jwk') {
return DidResolutionResult.invalidDid();
}

final dynamic jwk;

try {
jwk = json.fromBase64Url(parsedDidUri.id);
} on FormatException {
return DidResolutionResult.invalidDid();
}

final verificationMethod = DidVerificationMethod(
id: "$didUri#0",
type: "JsonWebKey2020",
controller: didUri,
publicKeyJwk: Jwk.fromJson(jwk),
);

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

final didResolutionResult = DidResolutionResult(didDocument: didDocument);

return didResolutionResult;
}
}
39 changes: 39 additions & 0 deletions lib/src/dids/did_resolution_metadata.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
/// A metadata structure consisting of values relating to the results of the
/// DID resolution process which typically changes between invocations of the
/// resolve and resolveRepresentation functions, as it represents data about
/// the resolution process itself
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#dfn-didresolutionmetadata)
class DidResolutionMetadata {
/// The Media Type of the returned didDocumentStream. This property is
/// REQUIRED if resolution is successful and if the resolveRepresentation
/// function was called
final String? contentType;

/// The error code from the resolution process. This property is REQUIRED
/// when there is an error in the resolution process. The value of this
/// property MUST be a single keyword ASCII string. The possible property
/// values of this field SHOULD be registered in the
/// [DID Specification Registries](https://www.w3.org/TR/did-spec-registries/#error)
final String? error;

DidResolutionMetadata({
this.contentType,
this.error,
});

bool isEmpty() {
return toJson().isEmpty;
}

Map<String, dynamic> toJson() {
final json = {
'contentType': contentType,
'error': error,
};

json.removeWhere((key, value) => value == null);

return json;
}
}
Loading

0 comments on commit d7337d9

Please sign in to comment.