Skip to content

Commit

Permalink
Refactor @web5/dids to support first class Key Management (#393)
Browse files Browse the repository at this point in the history
* Update Web5 Test Vectors to latest
* Fix issue with c8 not reporting code coverage and bump version
* Remove unnecessary option in crypto tsconfig and enable strict mode for dids
* Add JWK Set to crypto package
* Add did:jwk and did:web implementations
* Add utility type to @web5/common
* Improve DID Utility functions
* Add FixedLengthArray utility type
* Add support for Base32Z to Convert utility
* Improve JWK type
* Add InferKeyGeneratorAlgorithm utility type
* Remove did-resolver dependency
* Temporarily disable old implementations
* Update DID JWK and DHT implementations
* Rename createFromKeys to fromKeys and change default publishing behavior
* Update JWK and DHT implementations
* Rename DidKeySet to PortableDid and add uri
* Reorganize tests and reintroduce DidResolver
* Update package-lock.json
* Rename P256 to Secp256r1 and add minimal tests
* Add docs for undocumented methods
* Finish reintroducing DID resolvers
* Support secp256k1 as an algorithm identifier for generateKey()
* Improve support for secp256r1
* Rename to LocalKeyManager & AwsKeyManager and improve docs
* Fix Ed25519.validatePublicKey method signature
* Refactor of DID Key
* Complete implementation of DID Key
* Add KeyCompressor type to @web5/crypto
* Complete refactor of DidIon
* Complete adding secp256r1 test vectors
* Remove old dependencies and bump versions
* Resolve package dependency issue
* Remove old test-vectors directory
* Remove references to tags in the XChaCha20Poly1305 comments
* Remove duplication from DidDhtUtils.identifierToIdentityKey()
* Correct typo in comments
* Update crypto README
* Correct typo, add DHT test, and remove unused file

---------

Signed-off-by: Frank Hinek <[email protected]>
Co-authored-by: nitro-neal <[email protected]>
  • Loading branch information
frankhinek and nitro-neal authored Feb 1, 2024
1 parent 422ba93 commit c9f3661
Show file tree
Hide file tree
Showing 122 changed files with 15,858 additions and 7,050 deletions.
1,592 changes: 853 additions & 739 deletions package-lock.json

Large diffs are not rendered by default.

8 changes: 4 additions & 4 deletions packages/agent/.c8rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
".js"
],
"include": [
"tests/compiled/src/**"
"tests/compiled/**/src/**"
],
"exclude": [
"tests/compiled/src/index.js",
"tests/compiled/src/types.js",
"tests/compiled/src/types/**"
"tests/compiled/**/src/index.js",
"tests/compiled/**/src/types.js",
"tests/compiled/**/src/types/**"
],
"reporter": [
"cobertura",
Expand Down
7 changes: 4 additions & 3 deletions packages/agent/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@web5/agent",
"version": "0.2.5",
"version": "0.2.6",
"type": "module",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down Expand Up @@ -69,7 +69,7 @@
},
"dependencies": {
"@tbd54566975/dwn-sdk-js": "0.2.10",
"@web5/common": "0.2.2",
"@web5/common": "0.2.3",
"@web5/crypto": "0.2.2",
"@web5/dids": "0.2.4",
"level": "8.0.0",
Expand All @@ -81,6 +81,7 @@
"@playwright/test": "1.40.1",
"@types/chai": "4.3.6",
"@types/chai-as-promised": "7.1.5",
"@types/dns-packet": "5.6.4",
"@types/eslint": "8.44.2",
"@types/mocha": "10.0.1",
"@types/readable-stream": "4.0.6",
Expand All @@ -89,7 +90,7 @@
"@typescript-eslint/parser": "6.4.0",
"@web/test-runner": "0.18.0",
"@web/test-runner-playwright": "0.11.0",
"c8": "8.0.1",
"c8": "9.0.0",
"chai": "4.3.10",
"chai-as-promised": "7.1.1",
"esbuild": "0.19.8",
Expand Down
8 changes: 4 additions & 4 deletions packages/api/.c8rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
".js"
],
"include": [
"tests/compiled/src/**"
"tests/compiled/**/src/**"
],
"exclude": [
"tests/compiled/src/index.js",
"tests/compiled/src/types.js",
"tests/compiled/src/types/**"
"tests/compiled/**/src/index.js",
"tests/compiled/**/src/types.js",
"tests/compiled/**/src/types/**"
],
"reporter": [
"cobertura",
Expand Down
10 changes: 5 additions & 5 deletions packages/api/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -77,11 +77,11 @@
},
"dependencies": {
"@tbd54566975/dwn-sdk-js": "0.2.10",
"@web5/agent": "0.2.5",
"@web5/common": "0.2.2",
"@web5/agent": "0.2.6",
"@web5/common": "0.2.3",
"@web5/crypto": "0.2.2",
"@web5/dids": "0.2.4",
"@web5/user-agent": "0.2.5",
"@web5/user-agent": "0.2.6",
"level": "8.0.0",
"ms": "2.1.3",
"readable-stream": "4.4.2",
Expand All @@ -100,7 +100,7 @@
"@typescript-eslint/parser": "6.4.0",
"@web/test-runner": "0.18.0",
"@web/test-runner-playwright": "0.11.0",
"c8": "8.0.1",
"c8": "9.0.0",
"chai": "4.3.10",
"chai-as-promised": "7.1.1",
"esbuild": "0.19.8",
Expand All @@ -112,7 +112,7 @@
"playwright": "1.40.1",
"rimraf": "4.4.0",
"sinon": "16.1.3",
"source-map-loader": "4.0.1",
"source-map-loader": "4.0.2",
"typescript": "5.1.6"
}
}
8 changes: 4 additions & 4 deletions packages/common/.c8rc.json
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,12 @@
".js"
],
"include": [
"tests/compiled/src/**"
"tests/compiled/**/src/**"
],
"exclude": [
"tests/compiled/src/index.js",
"tests/compiled/src/types.js",
"tests/compiled/src/types/**"
"tests/compiled/**/src/index.js",
"tests/compiled/**/src/types.js",
"tests/compiled/**/src/types/**"
],
"reporter": [
"cobertura",
Expand Down
4 changes: 2 additions & 2 deletions packages/common/package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@web5/common",
"version": "0.2.2",
"version": "0.2.3",
"type": "module",
"main": "./dist/cjs/index.js",
"module": "./dist/esm/index.js",
Expand Down Expand Up @@ -83,7 +83,7 @@
"@typescript-eslint/parser": "6.4.0",
"@web/test-runner": "0.18.0",
"@web/test-runner-playwright": "0.11.0",
"c8": "8.0.1",
"c8": "9.0.0",
"chai": "4.3.10",
"chai-as-promised": "7.1.1",
"esbuild": "0.19.8",
Expand Down
21 changes: 21 additions & 0 deletions packages/common/src/convert.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import type { Multibase } from 'multiformats';

import { base32z } from 'multiformats/bases/base32';
import { base58btc } from 'multiformats/bases/base58';
import { base64url } from 'multiformats/bases/base64';

Expand Down Expand Up @@ -28,6 +29,10 @@ export class Convert {
return new Convert(data, 'AsyncIterable');
}

static base32Z(data: string): Convert {
return new Convert(data, 'Base32Z');
}

static base58Btc(data: string): Convert {
return new Convert(data, 'Base58Btc');
}
Expand Down Expand Up @@ -131,6 +136,18 @@ export class Convert {
}
}

toBase32Z(): string {
switch (this.format) {

case 'Uint8Array': {
return base32z.baseEncode(this.data);
}

default:
throw new TypeError(`Conversion from ${this.format} to Base64Z is not supported.`);
}
}

toBase58Btc(): string {
switch (this.format) {

Expand Down Expand Up @@ -357,6 +374,10 @@ export class Convert {
return new Uint8Array(this.data);
}

case 'Base32Z': {
return base32z.baseDecode(this.data);
}

case 'Base58Btc': {
return base58btc.baseDecode(this.data);
}
Expand Down
117 changes: 116 additions & 1 deletion packages/common/src/type-utils.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,67 @@
/**
* Represents an array of a fixed length, preventing modifications to its size.
*
* The `FixedLengthArray` utility type transforms a standard array into a variant where
* methods that could alter the length are omitted. It leverages TypeScript's advanced types,
* such as conditional types and mapped types, to ensure that the array cannot be resized
* through methods like `push`, `pop`, `splice`, `shift`, and `unshift`. The utility type
* maintains all other characteristics of a standard array, including indexing, iteration,
* and type checking for its elements.
*
* Note: The type does not prevent direct assignment to indices, even if it would exceed
* the original length. However, such actions would lead to TypeScript type errors.
*
* @example
* ```ts
* // Declare a variable with a type of fixed-length array of three strings.
* let myFixedLengthArray: FixedLengthArray< [string, string, string]>;
*
* // Array declaration tests
* myFixedLengthArray = [ 'a', 'b', 'c' ]; // OK
* myFixedLengthArray = [ 'a', 'b', 123 ]; // TYPE ERROR
* myFixedLengthArray = [ 'a' ]; // LENGTH ERROR
* myFixedLengthArray = [ 'a', 'b' ]; // LENGTH ERROR
*
* // Index assignment tests
* myFixedLengthArray[1] = 'foo'; // OK
* myFixedLengthArray[1000] = 'foo'; // INVALID INDEX ERROR
*
* // Methods that mutate array length
* myFixedLengthArray.push('foo'); // MISSING METHOD ERROR
* myFixedLengthArray.pop(); // MISSING METHOD ERROR
*
* // Direct length manipulation
* myFixedLengthArray.length = 123; // READ-ONLY ERROR
*
* // Destructuring
* let [ a ] = myFixedLengthArray; // OK
* let [ a, b ] = myFixedLengthArray; // OK
* let [ a, b, c ] = myFixedLengthArray; // OK
* let [ a, b, c, d ] = myFixedLengthArray; // INVALID INDEX ERROR
* ```
*
* @template T extends any[] - The array type to be transformed.
*/
export type FixedLengthArray<T extends any[]> =
Pick<T, Exclude<keyof T, ArrayLengthMutationKeys>>
& {
/**
* Custom iterator for the `FixedLengthArray` type.
*
* This iterator allows the `FixedLengthArray` to be used in standard iteration
* contexts, such as `for...of` loops and spread syntax. It ensures that even though
* the array is of a fixed length with disabled mutation methods, it still retains
* iterable behavior similar to a regular array.
*
* @returns An IterableIterator for the array items.
*/
[Symbol.iterator]: () => IterableIterator<ArrayItems<T>>
};

/** Helper types for {@link FixedLengthArray} */
type ArrayLengthMutationKeys = 'splice' | 'push' | 'pop' | 'shift' | 'unshift' | number;
type ArrayItems<T extends Array<any>> = T extends Array<infer TItems> ? TItems : never;

/**
* isArrayBufferSlice
*
Expand Down Expand Up @@ -72,6 +136,34 @@ export function isDefined<T>(arg: T): arg is Exclude<T, null | undefined> {
return arg !== null && typeof arg !== 'undefined';
}

/**
* Utility type that transforms a type `T` to have only certain keys `K` as required, while the
* rest remain optional, except for keys specified in `O`, which are omitted entirely.
*
* This type is useful when you need a variation of a type where only specific properties are
* required, and others are either optional or not included at all. It allows for more flexible type
* definitions based on existing types without the need to redefine them.
*
* @template T - The original type to be transformed.
* @template K - The keys of `T` that should be required.
* @template O - The keys of `T` that should be omitted from the resulting type (optional).
*
* @example
* ```ts
* // Given an interface
* interface Example {
* requiredProp: string;
* optionalProp?: number;
* anotherOptionalProp?: boolean;
* }
*
* // Making 'optionalProp' required and omitting 'anotherOptionalProp'
* type ModifiedExample = RequireOnly<Example, 'optionalProp', 'anotherOptionalProp'>;
* // Result: { requiredProp?: string; optionalProp: number; }
* ```
*/
export type RequireOnly<T, K extends keyof T, O extends keyof T = never> = Required<Pick<T, K>> & Omit<Partial<T>, O>;

/**
* universalTypeOf
*
Expand Down Expand Up @@ -114,4 +206,27 @@ export function universalTypeOf(value: unknown) {
const [_, type] = match as RegExpMatchArray;

return type;
}
}

/**
* Utility type to extract the type resolved by a Promise.
*
* This type unwraps the type `T` from `Promise<T>` if `T` is a Promise, otherwise returns `T` as
* is. It's useful in situations where you need to handle the type returned by a promise-based
* function in a synchronous context, such as defining types for test vectors or handling return
* types in non-async code blocks.
*
* @template T - The type to unwrap from the Promise.
*
* @example
* ```ts
* // For a Promise type, it extracts the resolved type.
* type AsyncNumber = Promise<number>;
* type UnwrappedNumber = UnwrapPromise<AsyncNumber>; // number
*
* // For a non-Promise type, it returns the type as is.
* type StringValue = string;
* type UnwrappedString = UnwrapPromise<StringValue>; // string
* ```
*/
export type UnwrapPromise<T> = T extends Promise<infer U> ? U : T;
7 changes: 1 addition & 6 deletions packages/common/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -40,9 +40,4 @@ export interface KeyValueStore<K, V> {
* @returns A promise that resolves when the value has been set.
*/
set(key: K, value: V): Promise<void>;
}

/**
* Represents an object type where a subset of keys are required and everything else is optional.
*/
export type RequireOnly<T, K extends keyof T, O extends keyof T = never> = Required<Pick<T, K>> & Omit<Partial<T>, O>;
}
36 changes: 36 additions & 0 deletions packages/common/tests/convert.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,23 @@ describe('Convert', () =>{
});
});

describe('from: Base64Z', () => {
it('to: Uint8Array', () => {
// Test Vector 1.
let input = '5umembtazeybqcd7grysfp711g1z56wzo8irzhae494hh58zguhy';
let output = new Uint8Array([
220, 214, 133, 134, 56, 186, 0, 23,
48, 125, 49, 1, 98, 183, 178, 145,
165, 125, 250, 151, 129, 234, 75, 243,
8, 215, 245, 206, 108, 247, 52, 248
]);

let result = Convert.base32Z(input).toUint8Array();

expect(result).to.deep.equal(output);
});
});

describe('from: BufferSource', () => {
it('to: ArrayBuffer', () => {
// Test Vector 1 - BufferSource is Uint8Array.
Expand Down Expand Up @@ -500,6 +517,21 @@ describe('Convert', () =>{
expect(result).to.deep.equal(output);
});

it('to: Base32Z', () => {
// Test Vector 1.
let input = new Uint8Array([
220, 214, 133, 134, 56, 186, 0, 23,
48, 125, 49, 1, 98, 183, 178, 145,
165, 125, 250, 151, 129, 234, 75, 243,
8, 215, 245, 206, 108, 247, 52, 248
]);
let output = '5umembtazeybqcd7grysfp711g1z56wzo8irzhae494hh58zguhy';

let result = Convert.uint8Array(input).toBase32Z();

expect(result).to.deep.equal(output);
});

it('to: Base58Btc', () => {
// Test Vector 1.
let input = new Uint8Array([51, 52, 53]);
Expand Down Expand Up @@ -567,6 +599,10 @@ describe('Convert', () =>{
}
});

it('toBase32Z() throw an error', () => {
expect(() => unsupported.toBase32Z()).to.throw(TypeError, 'not supported');
});

it('toBase58Btc() throw an error', () => {
expect(() => unsupported.toBase58Btc()).to.throw(TypeError, 'not supported');
});
Expand Down
Loading

0 comments on commit c9f3661

Please sign in to comment.