Skip to content

Commit

Permalink
Merge pull request #18 from aviarytech/witnesses
Browse files Browse the repository at this point in the history
Witnesses
  • Loading branch information
swcurran authored Oct 4, 2024
2 parents 0ee570f + 72f0cc6 commit 33ce57b
Show file tree
Hide file tree
Showing 23 changed files with 1,150 additions and 152 deletions.
4 changes: 3 additions & 1 deletion .gitignore
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
.env
test/logs
.DS_Store
.vscode
node_modules
bun.lockb

claude_chats
*.jsonl
145 changes: 136 additions & 9 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -14,11 +14,10 @@ the following features from the specification with the goal to be feature comple
| DONE | Ability to resolve the full history of the DID | Uses a verifiable chain of updates from genesis to deactivation. |
| DONE | A self-certifying identifier (SCID) for the DID | Ensures global uniqueness, derived from the initial DIDDoc for portability. |
| DONE | DIDDoc updates include a proof signed by the DID Controller(s) | Proof required for updates, authorized by the DID Controller(s). |
| TODO | Optional mechanism for publishing pre-rotation keys | Helps prevent loss of control if an active private key is compromised. |
| DONE | Optional mechanism for publishing "pre-rotation" keys | Helps prevent loss of control if an active private key is compromised. |
| TODO | DID URL path handling | Defaults to resolve <did>/path/to/file by DID-to-HTTPS translation, can be overridden. |
| TODO | A DID URL path <did>/whois | Automatically returns a Verifiable Presentation, if published by the DID controller. |


## Prerequisites

Install [bun.sh](https://bun.sh/)
Expand All @@ -33,20 +32,148 @@ curl -fsSL https://bun.sh/install | bash
bun install
```

## Run all tests
## Available Commands

The following commands are defined in the `package.json` file:

1. `dev`: Run the resolver in development mode with debugging enabled.
```bash
bun run dev
```
This command runs: `bun --watch --inspect-wait ./src/resolver.ts`

2. `server`: Run the resolver in watch mode for development.
```bash
bun run server
```
This command runs: `bun --watch ./src/resolver.ts`

3. `test`: Run all tests.
```bash
bun run test
```
This command runs: `bun test`

4. `test:watch`: Run tests in watch mode, focusing on witness tests.
```bash
bun run test:watch
```
This command runs: `bun test --watch witness`

5. `test:bail`: Run tests in watch mode, stopping on the first failure with verbose output.
```bash
bun run test:bail
```
This command runs: `bun test --watch --bail --verbose`

6. `test:log`: Run tests and save the output to a log file.
```bash
bun run test:log
```
This command runs: `mkdir -p ./test/logs && LOG_RESOLVES=true bun test &> ./test/logs/test-run.txt`

7. `cli`: Run the CLI tool.
```bash
bun run cli [command] [options]
```
This command runs: `bun run src/cli.ts --`

## CLI Documentation

```bash
bun test
```
The CLI is Experimental, buggy and beta software -- use at your own risk!
```
The trustdidweb-ts package provides a Command Line Interface (CLI) for managing Decentralized Identifiers (DIDs) using the `did:tdw` method.
### Development mode
### Usage
The general syntax for using the CLI is:
```bash
bun run test:watch
bun run cli [command] [options]
```

## Run the tests and save a log
To output the help using the CLI:

```bash
bun run test:log
bun run cli help
```

### Commands

1. **Create a DID**

```bash
bun run cli create [options]
```

Options:
- `--domain [domain]`: (Required) Domain for the DID
- `--output [file]`: (Optional) Path to save the DID log
- `--portable`: (Optional) Make the DID portable
- `--prerotation`: (Optional) Enable pre-rotation
- `--witness [witness]`: (Optional) Add a witness (can be used multiple times)
- `--witness-threshold [n]`: (Optional) Set witness threshold

Example:
```bash
bun run cli create --domain example.com --portable --witness did:tdw:QmWitness1:example.com --witness did:tdw:QmWitness2...:example.com
```

2. **Resolve a DID**

```bash
bun run cli resolve --did [did]
```

Example:
```bash
bun run cli resolve --did did:tdw:Qm...:example.com
```

3. **Update a DID**

```bash
bun run cli update [options]
```

Options:
- `--log [file]`: (Required) Path to the DID log file
- `--output [file]`: (Optional) Path to save the updated DID log
- `--prerotation`: (Optional) Enable pre-rotation
- `--witness [witness]`: (Optional) Add a witness (can be used multiple times)
- `--witness-threshold [n]`: (Optional) Set witness threshold
- `--service [service]`: (Optional) Add a service (format: type,endpoint)
- `--add-vm [type]`: (Optional) Add a verification method
- `--also-known-as [alias]`: (Optional) Add an alsoKnownAs alias

Example:
```bash
bun run cli update --log ./did.jsonl --output ./updated-did.jsonl --add-vm keyAgreement --service LinkedDomains,https://example.com
```

4. **Deactivate a DID**

```bash
bun run cli deactivate [options]
```

Options:
- `--log [file]`: (Required) Path to the DID log file
- `--output [file]`: (Optional) Path to save the deactivated DID log

Example:
```bash
bun run cli deactivate --log ./did.jsonl --output ./deactivated-did.jsonl
```

### Additional Notes

- The CLI automatically generates new authentication keys when creating or updating a DID.
- The `--portable` option in the create command allows the DID to be moved to a different domain later.
- The `--prerotation` option enables key pre-rotation, which helps prevent loss of control if an active private key is compromised.
- Witness functionality allows for third-party attestation of DID operations.
- The CLI saves the DID log to a file when the `--output` option is provided.
- For the update and deactivate commands, the existing DID log must be provided using the `--log` option.
13 changes: 7 additions & 6 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -3,16 +3,17 @@
"module": "src/index.ts",
"type": "module",
"scripts": {
"dev": "bun --watch --inspect-wait ./src/resolver.ts",
"server": "bun --watch ./src/resolver.ts",
"test": "bun test",
"test:watch": "bun test --watch",
"test:bail": "bun test --watch --bail",
"test:log": "mkdir -p ./test/logs && LOG_RESOLVES=true bun test &> ./test/logs/test-run.txt"
"test:watch": "bun test --watch witness",
"test:bail": "bun test --watch --bail --verbose",
"test:log": "mkdir -p ./test/logs && LOG_RESOLVES=true bun test &> ./test/logs/test-run.txt",
"cli": "bun run src/cli.ts"
},
"devDependencies": {
"bun-types": "latest",
"ts-node": "^10.9.2",
"tsx": "^4.7.1"
"bun-bagel": "^1.1.0",
"bun-types": "latest"
},
"peerDependencies": {
"typescript": "^5.0.0"
Expand Down
47 changes: 38 additions & 9 deletions src/assertions.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,48 @@
import * as ed from '@noble/ed25519';
import { base58btc } from "multiformats/bases/base58";
import { bytesToHex, createSCID, deriveHash } from "./utils";
import { bytesToHex, createSCID, deriveHash, resolveVM } from "./utils";
import { canonicalize } from 'json-canonicalize';
import { createHash } from 'node:crypto';

export const keyIsAuthorized = (key: string, updateKeys: string[]) => {
const isKeyAuthorized = (verificationMethod: string, updateKeys: string[]): boolean => {
if (process.env.IGNORE_ASSERTION_KEY_IS_AUTHORIZED) return true;
return updateKeys.includes(key);
}

export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys: string[]) => {
if (verificationMethod.startsWith('did:key:')) {
const key = verificationMethod.split('did:key:')[1].split('#')[0];
return updateKeys.includes(key);
}
return false;
};

const isWitnessAuthorized = (verificationMethod: string, witnesses: string[]): boolean => {
if (process.env.IGNORE_WITNESS_IS_AUTHORIZED) return true;

if (verificationMethod.startsWith('did:tdw:')) {
const didWithoutFragment = verificationMethod.split('#')[0];
return witnesses.includes(didWithoutFragment);
}
return false;
};

export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys: string[], witnesses: string[] = []) => {
if (process.env.IGNORE_ASSERTION_DOCUMENT_STATE_IS_VALID) return true;

let i = 0;
while(i < proofs.length) {
const proof = proofs[i];
if (!keyIsAuthorized(proof.verificationMethod.split('#')[0].split('did:key:').at(-1), updateKeys)) {
throw new Error(`key ${proof.verificationMethod} is not authorized to update.`)

if (proof.verificationMethod.startsWith('did:key:')) {
if (!isKeyAuthorized(proof.verificationMethod, updateKeys)) {
throw new Error(`Key ${proof.verificationMethod} is not authorized to update.`);
}
} else if (proof.verificationMethod.startsWith('did:tdw:')) {
if (witnesses.length > 0 && !isWitnessAuthorized(proof.verificationMethod, witnesses)) {
throw new Error(`Key ${proof.verificationMethod} is not from an authorized witness.`);
}
} else {
throw new Error(`Unsupported verification method: ${proof.verificationMethod}`);
}

if (proof.type !== 'DataIntegrityProof') {
throw new Error(`Unknown proof type ${proof.type}`);
}
Expand All @@ -26,7 +52,11 @@ export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys:
if (proof.cryptosuite !== 'eddsa-jcs-2022') {
throw new Error(`Unknown cryptosuite ${proof.cryptosuite}`);
}
const publicKey = base58btc.decode(proof.verificationMethod.split('did:key:')[1].split('#')[0]);
const vm = await resolveVM(proof.verificationMethod);
if (!vm) {
throw new Error(`Verification Method ${proof.verificationMethod} not found`);
}
const publicKey = base58btc.decode(vm.publicKeyMultibase!);
if (publicKey[0] !== 237 || publicKey[1] !== 1) {
throw new Error(`multiKey doesn't include ed25519 header (0xed01)`)
}
Expand All @@ -35,7 +65,6 @@ export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys:
const dataHash = createHash('sha256').update(canonicalize(doc)).digest();
const proofHash = createHash('sha256').update(canonicalize(restProof)).digest();
const input = Buffer.concat([dataHash, proofHash]);

const verified = await ed.verifyAsync(
bytesToHex(sig),
bytesToHex(input),
Expand Down
Loading

0 comments on commit 33ce57b

Please sign in to comment.