Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Probably too much #29

Merged
merged 7 commits into from
Jan 11, 2024
Merged
Show file tree
Hide file tree
Changes from 3 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions packages/web5/analysis_options.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -6,3 +6,4 @@ linter:
- prefer_final_locals
- require_trailing_commas
- unnecessary_late
- prefer_single_quotes
10 changes: 4 additions & 6 deletions packages/web5/example/web5_example.dart
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
import 'dart:convert';

import 'package:web5/web5.dart';

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

final did = await DidJwk.create(keyManager: keyManager);
print(did.uri);
// LOAD PRE-EXISTING
// final did = DidJwk(uri: "some_uri", keyManager: keyManager);

final didResolutionResult = DidJwk.resolve(did.uri);
print(jsonEncode(didResolutionResult));
// CREATE
// final did = DidJwk.create(keyManager: keyManager);
}
9 changes: 9 additions & 0 deletions packages/web5/lib/src/crypto.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
export './crypto/jwk.dart';
export './crypto/dsa.dart';
export './crypto/ed25519.dart';
export './crypto/dsa_name.dart';
export './crypto/dsa_alias.dart';
export './crypto/secp256k1.dart';
export './crypto/key_manager.dart';
export './crypto/dsa_algorithms.dart';
export './crypto/in_memory_key_manager.dart';
Comment on lines +1 to +9
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

nice!

45 changes: 1 addition & 44 deletions packages/web5/lib/src/crypto/dsa.dart
Original file line number Diff line number Diff line change
@@ -1,50 +1,7 @@
import 'dart:typed_data';

import 'package:web5/src/crypto/jwk.dart';

/// Enum [DsaName] representing supported Digital Signature Algorithm (DSA) names.
enum DsaName {
secp256k1('ES256K', 'secp256k1', 'EC'),
ed25519('EdDSA', 'Ed25519', 'OKP');

static final _aliases = {
DsaAlias(algorithm: 'EdDSA', curve: 'Ed25519'): DsaName.ed25519,
DsaAlias(algorithm: null, curve: 'Ed25519'): DsaName.ed25519,
DsaAlias(algorithm: 'ES256K', curve: 'secp256k1'): DsaName.secp256k1,
DsaAlias(algorithm: 'ES256K', curve: null): DsaName.secp256k1,
DsaAlias(algorithm: null, curve: 'secp256k1'): DsaName.secp256k1,
};

final String algorithm;
final String curve;
final String kty;

const DsaName(this.algorithm, this.curve, this.kty);

/// method that can be used to find [DsaName] using
/// [JWA](https://datatracker.ietf.org/doc/html/rfc7518.html) algorithm and
/// curve names
static DsaName? findByAlias({String? algorithm, String? curve}) =>
_aliases[DsaAlias(algorithm: algorithm, curve: curve)];
}

class DsaAlias {
String? algorithm;
String? curve;

DsaAlias({this.algorithm, this.curve});

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is DsaAlias &&
other.algorithm == algorithm &&
other.curve == curve;
}

@override
int get hashCode => algorithm.hashCode ^ curve.hashCode;
}
import 'package:web5/src/crypto/dsa_name.dart';

/// Abstract interface for Digital Signature Algorithms.
///
Expand Down
26 changes: 23 additions & 3 deletions packages/web5/lib/src/crypto/dsa_algorithms.dart
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
import 'dart:typed_data';

import 'package:web5/web5.dart';
import 'package:web5/src/crypto/jwk.dart';
import 'package:web5/src/crypto/dsa.dart';
import 'package:web5/src/crypto/ed25519.dart';
import 'package:web5/src/crypto/dsa_name.dart';
import 'package:web5/src/crypto/secp256k1.dart';

class DsaAlgorithms {
static final _supportedAlgorithms = {
Expand All @@ -12,7 +16,7 @@ class DsaAlgorithms {
final dsa = _supportedAlgorithms[alg];

if (dsa == null) {
throw Exception("${alg.name} not supported");
throw Exception('${alg.name} not supported');
}

return dsa.generatePrivateKey();
Expand All @@ -26,6 +30,22 @@ class DsaAlgorithms {
return _getDsa(privateKeyJwk).sign(privateKeyJwk, payload);
}

static Future<void> verify({
required DsaName algName,
required Jwk publicKey,
required Uint8List payload,
required Uint8List signature,
}) {
final dsa = _supportedAlgorithms[algName];
if (dsa == null) {
throw Exception(
'DSA ${algName.algorithm}:${algName.curve} not supported.',
);
}

return dsa.verify(publicKey, payload, signature);
}

static Dsa _getDsa(Jwk privateKeyJwk) {
final dsa = _supportedAlgorithms[DsaName.findByAlias(
algorithm: privateKeyJwk.alg,
Expand All @@ -34,7 +54,7 @@ class DsaAlgorithms {

if (dsa == null) {
throw Exception(
"DSA ${privateKeyJwk.alg}:${privateKeyJwk.crv} not supported.",
'DSA ${privateKeyJwk.alg}:${privateKeyJwk.crv} not supported.',
);
}
return dsa;
Expand Down
17 changes: 17 additions & 0 deletions packages/web5/lib/src/crypto/dsa_alias.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
class DsaAlias {
String? algorithm;
String? curve;

DsaAlias({this.algorithm, this.curve});

@override
bool operator ==(Object other) {
if (identical(this, other)) return true;
return other is DsaAlias &&
other.algorithm == algorithm &&
other.curve == curve;
}

@override
int get hashCode => algorithm.hashCode ^ curve.hashCode;
}
27 changes: 27 additions & 0 deletions packages/web5/lib/src/crypto/dsa_name.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
import 'package:web5/src/crypto/dsa_alias.dart';

/// Enum [DsaName] representing supported Digital Signature Algorithm (DSA) names.
enum DsaName {
secp256k1('ES256K', 'secp256k1', 'EC'),
ed25519('EdDSA', 'Ed25519', 'OKP');

static final _aliases = {
DsaAlias(algorithm: 'EdDSA', curve: 'Ed25519'): DsaName.ed25519,
DsaAlias(algorithm: null, curve: 'Ed25519'): DsaName.ed25519,
DsaAlias(algorithm: 'ES256K', curve: 'secp256k1'): DsaName.secp256k1,
DsaAlias(algorithm: 'ES256K', curve: null): DsaName.secp256k1,
DsaAlias(algorithm: null, curve: 'secp256k1'): DsaName.secp256k1,
};

final String algorithm;
final String curve;
final String kty;

const DsaName(this.algorithm, this.curve, this.kty);

/// method that can be used to find [DsaName] using
/// [JWA](https://datatracker.ietf.org/doc/html/rfc7518.html) algorithm and
/// curve names
static DsaName? findByAlias({String? algorithm, String? curve}) =>
_aliases[DsaAlias(algorithm: algorithm, curve: curve)];
}
5 changes: 3 additions & 2 deletions packages/web5/lib/src/crypto/ed25519.dart
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import 'dart:typed_data';

import 'package:cryptography/cryptography.dart' as crypto;

import 'package:web5/src/extensions/base64url.dart';
import 'package:web5/src/extensions.dart';
import 'package:web5/src/crypto/dsa.dart';
import 'package:web5/src/crypto/jwk.dart';
import 'package:web5/src/crypto/dsa_name.dart';

final ed25519 = crypto.Ed25519();
final _base64UrlCodec = Base64Codec.urlSafe();
Expand Down Expand Up @@ -107,7 +108,7 @@ class Ed25519 implements Dsa {
final isLegit = await ed25519.verify(payload, signature: signature);

if (isLegit == false) {
throw Exception("Integrity check failed");
throw Exception('Integrity check failed');
}
}

Expand Down
6 changes: 3 additions & 3 deletions packages/web5/lib/src/crypto/in_memory_key_manager.dart
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
import 'dart:typed_data';

import 'package:web5/src/crypto/dsa.dart';
import 'package:web5/src/crypto/dsa_algorithms.dart';
import 'package:web5/src/crypto/jwk.dart';
import 'package:web5/src/crypto/dsa_name.dart';
import 'package:web5/src/crypto/key_manager.dart';
import 'package:web5/src/crypto/dsa_algorithms.dart';

/// A class for managing cryptographic keys in-memory.
///
Expand Down Expand Up @@ -46,7 +46,7 @@ class InMemoryKeyManager implements KeyManager {
Jwk _retrievePrivateKeyJwk(String keyAlias) {
final privateKeyJwk = _keyStore[keyAlias];
if (privateKeyJwk == null) {
throw Exception("key with alias $keyAlias not found.");
throw Exception('key with alias $keyAlias not found.');
}

return privateKeyJwk;
Expand Down
11 changes: 6 additions & 5 deletions packages/web5/lib/src/crypto/jwk.dart
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import 'dart:convert';
import 'package:cryptography/dart.dart';
import 'package:web5/src/extensions/base64url.dart';
import 'package:web5/src/extensions/json.dart';
import 'package:web5/src/extensions.dart';

// TODO: refactor into PrivateJwk PublicJwk classes

const dartSha256 = DartSha256();
const _dartSha256 = DartSha256();
final _base64UrlEncoder = Base64Codec.urlSafe().encoder;

/// Represents a [JSON Web Key (JWK)](https://datatracker.ietf.org/doc/html/rfc7517).
///
Expand Down Expand Up @@ -135,10 +135,11 @@ class Jwk {
thumbprintPayload.removeWhere((key, value) => value == null);

final thumbprintPayloadBytes = utf8.encode(jsonEncode(thumbprintPayload));
final thumbprintPayloadDigest = dartSha256.hashSync(thumbprintPayloadBytes);
final thumbprintPayloadDigest =
_dartSha256.hashSync(thumbprintPayloadBytes);

final thumbprint =
base64UrlEncoder.convertNoPadding(thumbprintPayloadDigest.bytes);
_base64UrlEncoder.convertNoPadding(thumbprintPayloadDigest.bytes);

return thumbprint;
}
Expand Down
2 changes: 1 addition & 1 deletion packages/web5/lib/src/crypto/key_manager.dart
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import 'dart:typed_data';

import 'package:web5/src/crypto/jwk.dart';
import 'package:web5/src/crypto/dsa.dart';
import 'package:web5/src/crypto/dsa_name.dart';

/// A key management interface that provides functionality for generating,
/// storing, and utilizing private keys and their associated public keys.
Expand Down
14 changes: 7 additions & 7 deletions packages/web5/lib/src/crypto/secp256k1.dart
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
import "dart:convert";
import "dart:math";
import 'dart:math';
import 'dart:convert';
import 'dart:typed_data';

import "package:pointycastle/export.dart";
import 'package:pointycastle/export.dart';
import 'package:web5/src/crypto/dsa.dart';
import 'package:web5/src/crypto/jwk.dart';
import "package:web5/src/extensions/base64url.dart";
import 'package:web5/src/extensions/bigint.dart';
import 'package:web5/src/extensions.dart';
import 'package:web5/src/crypto/dsa_name.dart';

final _base64UrlCodec = Base64Codec.urlSafe();
final _base64UrlEncoder = _base64UrlCodec.encoder;
Expand Down Expand Up @@ -47,7 +47,7 @@ class Secp256k1 implements Dsa {
final y = Q.y!.toBigInteger();

if (x == null || y == null) {
throw Exception("Failed to generate public key from private key");
throw Exception('Failed to generate public key from private key');
}

final publicKeyJwk = Jwk(
Expand Down Expand Up @@ -166,7 +166,7 @@ class Secp256k1 implements Dsa {
final isLegit = verifier.verifySignature(payload, ecSignature);

if (!isLegit) {
throw Exception("Integrity check failed");
throw Exception('Integrity check failed');
}

return Future.value();
Expand Down
7 changes: 7 additions & 0 deletions packages/web5/lib/src/dids.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
export './dids/did.dart';
export './dids/did_uri.dart';
export './dids/data_models.dart';
export './dids/did_resolver.dart';
export './dids/did_dht/did_dht.dart';
export './dids/did_jwk/did_jwk.dart';
export './dids/did_method_resolver.dart';
11 changes: 11 additions & 0 deletions packages/web5/lib/src/dids/data_models.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
export './data_models/service.dart';
export './data_models/did_resource.dart';
export './data_models/did_document.dart';
export './data_models/resolution_result.dart';
export './data_models/dereference_result.dart';
export './data_models/verification_method.dart';
export './data_models/resolution_metadata.dart';
export './data_models/dereference_options.dart';
export './data_models/dereference_metadata.dart';
export './data_models/did_document_metadata.dart';
export './data_models/verification_relationship.dart';
53 changes: 53 additions & 0 deletions packages/web5/lib/src/dids/data_models/dereference_metadata.dart
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
import 'dart:convert';

/// A metadata structure consisting of values relating to the results of the
/// DID URL dereferencing process. This structure is REQUIRED, and in the case
/// of an error in the dereferencing process, this MUST NOT be empty. Properties
/// defined by this specification are in 7.2.2 DID URL Dereferencing Metadata.
/// If the dereferencing is not successful, this structure MUST contain an error
/// property describing the error.
///
/// [Specification Reference](https://www.w3.org/TR/did-core/#did-url-dereferencing-metadata)
class DidDereferenceMetadata {
/// The Media Type of the returned contentStream
String? contentType;

/// The error code from the dereferencing process. This property is REQUIRED
/// when there is an error in the dereferencing process.
String? error;

/// used to store properties specific to individual DID methods. properties
/// within this map will be included at the top level when json serialized
Map<String, dynamic> additionalProperties;

DidDereferenceMetadata({
this.contentType,
this.error,
Map<String, dynamic>? additionalProperties,
}) : additionalProperties = additionalProperties ?? {};

factory DidDereferenceMetadata.fromJson(Map<String, dynamic> json) {
return DidDereferenceMetadata(
contentType: json['contentType'],
error: json['error'],
additionalProperties: json
..remove('contentType')
..remove('error'),
);
}

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

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

return json;
}

@override
String toString() => json.encode(toJson());
}
Loading