Skip to content

Commit

Permalink
Merge pull request #14 from aviarytech/latest-updates
Browse files Browse the repository at this point in the history
Version 0.3
  • Loading branch information
swcurran authored Aug 13, 2024
2 parents be9bbd9 + 68aa7d5 commit 0ee570f
Show file tree
Hide file tree
Showing 22 changed files with 509 additions and 2,675 deletions.
485 changes: 0 additions & 485 deletions package-lock.json

This file was deleted.

4 changes: 2 additions & 2 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -18,9 +18,9 @@
"typescript": "^5.0.0"
},
"dependencies": {
"@digitalbazaar/ed25519-multikey": "^1.1.0",
"@interop/base58-universal": "^1.0.0",
"@noble/curves": "^1.4.2",
"@noble/ed25519": "^2.1.0",
"base32": "^0.0.7",
"elysia": "^0.8.17",
"fast-json-patch": "^3.1.1",
"json-canonicalize": "^1.0.6",
Expand Down
30 changes: 23 additions & 7 deletions src/assertions.ts
Original file line number Diff line number Diff line change
@@ -1,18 +1,20 @@
import * as ed from '@noble/ed25519';
import { base58btc } from "multiformats/bases/base58";
import { bytesToHex, deriveHash } from "./utils";
import { bytesToHex, createSCID, deriveHash } from "./utils";
import { canonicalize } from 'json-canonicalize';
import { createHash } from 'node:crypto';

export const keyIsAuthorized = (verificationMethod: string, updateKeys: string[]) => {
return updateKeys.includes(verificationMethod);
export const keyIsAuthorized = (key: string, updateKeys: string[]) => {
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 (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], updateKeys)) {
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.type !== 'DataIntegrityProof') {
Expand All @@ -25,6 +27,9 @@ export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys:
throw new Error(`Unknown cryptosuite ${proof.cryptosuite}`);
}
const publicKey = base58btc.decode(proof.verificationMethod.split('did:key:')[1].split('#')[0]);
if (publicKey[0] !== 237 || publicKey[1] !== 1) {
throw new Error(`multiKey doesn't include ed25519 header (0xed01)`)
}
const {proofValue, ...restProof} = proof;
const sig = base58btc.decode(proofValue);
const dataHash = createHash('sha256').update(canonicalize(doc)).digest();
Expand All @@ -44,11 +49,17 @@ export const documentStateIsValid = async (doc: any, proofs: any[], updateKeys:
return true;
}

export const newKeysAreValid = (updateKeys: string[], previousNextKeyHashes: string[], nextKeyHashes: string[], previousPrerotate: boolean, prerotate: boolean) => {
if (prerotate && nextKeyHashes.length === 0) {
export const hashChainValid = (derivedHash: string, logEntryHash: string) => {
if (process.env.IGNORE_ASSERTION_HASH_CHAIN_IS_VALID) return true;
return derivedHash === logEntryHash;
}

export const newKeysAreValid = (updateKeys: string[], previousNextKeyHashes: string[], nextKeyHashes: string[], previousPrerotation: boolean, prerotation: boolean) => {
if (process.env.IGNORE_ASSERTION_NEW_KEYS_ARE_VALID) return true;
if (prerotation && nextKeyHashes.length === 0) {
throw new Error(`nextKeyHashes are required if prerotation enabled`);
}
if(previousPrerotate) {
if(previousPrerotation) {
const inNextKeyHashes = updateKeys.reduce((result, key) => {
const hashedKey = deriveHash(key);
return result && previousNextKeyHashes.includes(hashedKey);
Expand All @@ -59,3 +70,8 @@ export const newKeysAreValid = (updateKeys: string[], previousNextKeyHashes: str
}
return true;
}

export const scidIsFromHash = async (scid: string, hash: string) => {
if (process.env.IGNORE_ASSERTION_SCID_IS_FROM_HASH) return true;
return scid === await createSCID(hash);
}
3 changes: 2 additions & 1 deletion src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
export const PLACEHOLDER = "{SCID}";
export const METHOD = "tdw";
export const PROTOCOL = `did:${METHOD}:1`;
export const VERSION = "0.3";
export const PROTOCOL = `did:${METHOD}:${VERSION}`;
export const BASE_CONTEXT = [
"https://www.w3.org/ns/did/v1",
"https://w3id.org/security/multikey/v1"
Expand Down
62 changes: 62 additions & 0 deletions src/cryptography.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
import * as ed from '@noble/ed25519';
import { edwardsToMontgomeryPub, edwardsToMontgomeryPriv } from '@noble/curves/ed25519';

import { bytesToHex, createDate } from "./utils";
import { base58btc } from "multiformats/bases/base58"
import { canonicalize } from 'json-canonicalize';
import { createHash } from 'node:crypto';

export const createSigner = (vm: VerificationMethod) => {
return async (doc: any, challenge: string) => {
try {
const proof: any = {
type: 'DataIntegrityProof',
cryptosuite: 'eddsa-jcs-2022',
verificationMethod: `did:key:${vm.publicKeyMultibase}`,
created: createDate(),
proofPurpose: 'authentication',
challenge
}
const dataHash = createHash('sha256').update(canonicalize(doc)).digest();
const proofHash = createHash('sha256').update(canonicalize(proof)).digest();
const input = Buffer.concat([dataHash, proofHash]);
const secretKey = base58btc.decode(vm.secretKeyMultibase!);

const output = await ed.signAsync(bytesToHex(input), bytesToHex(secretKey.slice(2, 34)));

proof.proofValue = base58btc.encode(output);
return {...doc, proof};
} catch (e: any) {
console.error(e)
throw new Error(`Document signing failure: ${e.details}`)
}
}
}

export const generateEd25519VerificationMethod = async (purpose: 'authentication' | 'assertionMethod' | 'capabilityInvocation' | 'capabilityDelegation'): Promise<VerificationMethod> => {
const privKey = ed.utils.randomPrivateKey();
const pubKey = await ed.getPublicKeyAsync(privKey);
const publicKeyMultibase = base58btc.encode(Buffer.concat([new Uint8Array([0xed, 0x01]), pubKey]));
const secretKeyMultibase = base58btc.encode(Buffer.concat([new Uint8Array([0x80, 0x26]), privKey]));

return {
type: purpose,
publicKeyMultibase,
secretKeyMultibase
};
}

export const generateX25519VerificationMethod = async (purpose: 'keyAgreement'): Promise<VerificationMethod> => {
const privKey = ed.utils.randomPrivateKey();
const pubKey = await ed.getPublicKeyAsync(privKey);
const x25519PubKey = edwardsToMontgomeryPub(pubKey);
const x25519PrivKey = edwardsToMontgomeryPriv(privKey);
const publicKeyMultibase = base58btc.encode(Buffer.concat([new Uint8Array([0xec, 0x01]), x25519PubKey]));
const secretKeyMultibase = base58btc.encode(Buffer.concat([new Uint8Array([0x82, 0x26]), x25519PrivKey]));

return {
type: purpose,
publicKeyMultibase,
secretKeyMultibase
}
}
18 changes: 0 additions & 18 deletions src/gen-keys.js

This file was deleted.

3 changes: 1 addition & 2 deletions src/global.d.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,2 @@
declare module '@digitalbazaar/ed25519-multikey';
declare module 'fast-json-patch/index.mjs';
declare module 'base32';
declare module '@interop/base58-universal';
2 changes: 1 addition & 1 deletion src/index.ts
Original file line number Diff line number Diff line change
@@ -1,2 +1,2 @@
export { createSigner } from './signing';
export { createSigner, generateEd25519VerificationMethod } from './cryptography';
export { resolveDID, createDID, updateDID } from './method';
15 changes: 8 additions & 7 deletions src/interfaces.d.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,15 +18,15 @@ interface DIDOperation {
}

type DIDLogEntry = [
logEntryHash: string,
versionId: number,
versionId: string,
timestamp: string,
params: {
method?: string,
scid?: string,
updateKeys?: string[],
prerotate?: boolean,
nextKeyHashes?: string[]
prerotation?: boolean,
nextKeyHashes?: string[],
portable?: boolean
},
data: {value: any} | {patch: DIDOperation[]},
proof?: any
Expand Down Expand Up @@ -57,8 +57,9 @@ interface CreateDIDInterface {
context?: string | string[];
verificationMethods?: VerificationMethod[];
created?: Date;
prerotate?: boolean;
prerotation?: boolean;
nextKeyHashes?: string[];
portable?: boolean;
}

interface SignDIDDocInterface {
Expand All @@ -77,9 +78,9 @@ interface UpdateDIDInterface {
services?: ServiceEndpoint[];
alsoKnownAs?: string[];
domain?: string;
updated?: Date;
updated?: Date | string;
deactivated?: boolean;
prerotate?: boolean;
prerotation?: boolean;
nextKeyHashes?: string[];
}

Expand Down
Loading

0 comments on commit 0ee570f

Please sign in to comment.