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

@zcloak/message: peer to peer message transport #20

Merged
merged 4 commits into from
Nov 16, 2022
Merged
Show file tree
Hide file tree
Changes from all 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 jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ module.exports = Object.assign({}, config, {
'@zcloak/did-resolver(.*)$': '<rootDir>/packages/did-resolver/src/$1',
'@zcloak/did(.*)$': '<rootDir>/packages/did/src/$1',
'@zcloak/keyring(.*)$': '<rootDir>/packages/keyring/src/$1',
'@zcloak/message(.*)$': '<rootDir>/packages/message/src/$1',
'@zcloak/vc(.*)$': '<rootDir>/packages/vc/src/$1',
'@zcloak/verify(.*)$': '<rootDir>/packages/verify/src/$1',
'@zcloak/wasm(.*)$': '<rootDir>/packages/wasm/src/$1'
Expand Down
2 changes: 1 addition & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@
},
"devDependencies": {
"@types/jest": "^27.4.0",
"@zcloak/dev": "^0.6.2"
"@zcloak/dev": "^0.6.3"
},
"resolutions": {
"typescript": "^4.8.4"
Expand Down
3 changes: 1 addition & 2 deletions packages/ctype/src/publish.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
// Copyright 2021-2022 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { DidUrl } from '@zcloak/did-resolver/types';
import type { BaseCType } from './types';

import { generateMnemonic, initCrypto } from '@zcloak/crypto';
Expand Down Expand Up @@ -39,7 +38,7 @@ describe('publish ctype', (): void => {
};

expect(getCTypeHash(base, publisher.id)).toEqual(
getCTypeHash(base, publisher.getKeyUrl('authentication') as DidUrl)
getCTypeHash(base, publisher.getKeyUrl('authentication'))
);
});

Expand Down
2 changes: 1 addition & 1 deletion packages/ctype/src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,7 @@ export function getCTypeHash(
export function getPublish(base: BaseCType, publisher: Did): CType {
const hash = getCTypeHash(base, publisher.id);

const { id, signature } = publisher.signWithKey('authentication', hash);
const { id, signature } = publisher.signWithKey(hash, 'authentication');

return {
$id: hash,
Expand Down
27 changes: 27 additions & 0 deletions packages/did-resolver/src/MockDidResolver.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,27 @@
// Copyright 2021-2022 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { DidDocument, DidUrl } from './types';

import { DidResolver } from './DidResolver';
import { DidNotFoundError } from './errors';

export class MockDidResolver extends DidResolver {
#map: Map<DidUrl, DidDocument> = new Map();

public override resolve(didUrl: string): Promise<DidDocument> {
const { did } = this.parseDid(didUrl);

const document = this.#map.get(did);

if (!document) {
throw new DidNotFoundError();
}

return Promise.resolve(document);
}

public addDocument(document: DidDocument): void {
this.#map.set(document.id, document);
}
}
2 changes: 2 additions & 0 deletions packages/did-resolver/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,3 +3,5 @@

export * from './DidResolver';
export * from './ArweaveDidResolver';

export * from './MockDidResolver';
2 changes: 1 addition & 1 deletion packages/did/src/did/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ export abstract class DidChain extends DidDetails {

const proof: DidDocumentProof[] = document.proof ?? [];

const { id, signature } = this.signWithKey('capabilityInvocation', hashDidDocument(document));
const { id, signature } = this.signWithKey(hashDidDocument(document), 'capabilityInvocation');

proof.push({ id, signature: base58Encode(signature), type: 'creation' });

Expand Down
76 changes: 63 additions & 13 deletions packages/did/src/did/details.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,13 +10,15 @@ import type {
VerificationMethodType
} from '@zcloak/did-resolver/types';
import type { KeypairType, KeyringPair } from '@zcloak/keyring/types';
import type { IDidDetails, KeyRelationship } from '../types';
import type { DidKeys, SignedData } from './types';
import type { DidKeys, EncryptedData, IDidDetails, KeyRelationship, SignedData } from '../types';

import { assert } from '@polkadot/util';

import { base58Encode } from '@zcloak/crypto';
import { DidResolver } from '@zcloak/did-resolver';
import { defaultResolver } from '@zcloak/did-resolver/defaults';

import { fromDid } from './helpers';
import { DidKeyring } from './keyring';

export function typeTransform(type: KeypairType): VerificationMethodType {
Expand Down Expand Up @@ -67,37 +69,85 @@ export abstract class DidDetails extends DidKeyring implements IDidDetails {
this.service = service;
}

public getKeyUrl(key: DidKeys): DidUrl | undefined {
return Array.from(this[key] ?? [])[0];
public getKeyUrl(key: DidKeys): DidUrl {
const didUrl = Array.from(this[key] ?? [])[0];

assert(didUrl, `Not find verification method with the key: ${key}`);

return didUrl;
}

public get(id: DidUrl): KeyRelationship {
const method = this.keyRelationship.get(id);

assert(method, `Not find verficationMethod with id ${id}`);
assert(method, `Not find verficationMethod with id: ${id}`);

return method;
}

public signWithKey(key: DidKeys, message: Uint8Array | HexString): SignedData {
public override signWithKey(
message: Uint8Array | HexString,
key: Exclude<DidKeys, 'keyAgreement'>
): SignedData {
const didUrl = this.getKeyUrl(key);

assert(didUrl, `can not find verification method with the key: ${key}`);

return this.signWithId(didUrl, message);
return this.sign(message, didUrl);
}

public signWithId(id: DidUrl, message: Uint8Array | HexString): SignedData {
const { publicKey } = this.get(id);
const signature = this.sign(publicKey, message);
public override sign(message: Uint8Array | HexString, id: DidUrl): SignedData {
const { id: _id, publicKey } = this.get(id);
const pair = this.getPair(publicKey);

const signature = pair.sign(message);

return {
signature,
type: typeTransform(this.getPair(publicKey).type),
id
id: _id
};
}

public override async encrypt(
message: Uint8Array | HexString,
receiverUrlIn: DidUrl,
senderUrl: DidUrl = this.getKeyUrl('keyAgreement'),
resolver: DidResolver = defaultResolver
): Promise<EncryptedData> {
const { id, publicKey } = this.get(senderUrl);
const pair = this.getPair(publicKey);

const receiver = await fromDid(receiverUrlIn, undefined, resolver);

const { id: receiverUrl, publicKey: receiverPublicKey } = receiver.get(receiverUrlIn);

const encrypted = pair.encrypt(message, receiverPublicKey);

return {
senderUrl: id,
receiverUrl,
type: 'X25519KeyAgreementKey2019',
data: encrypted
};
}

public override async decrypt(
encryptedMessageWithNonce: Uint8Array | HexString,
senderUrlIn: DidUrl,
receiverUrl: DidUrl,
resolver: DidResolver = defaultResolver
): Promise<Uint8Array> {
const { publicKey } = this.get(receiverUrl);
const pair = this.getPair(publicKey);

const sender = await fromDid(senderUrlIn, undefined, resolver);

const { publicKey: senderPublicKey } = sender.get(senderUrlIn);

const decrypted = pair.decrypt(encryptedMessageWithNonce, senderPublicKey);

return decrypted;
}

public getDocument(): DidDocument {
assert(this.controller.size > 0, 'Must has one controller');

Expand Down
4 changes: 2 additions & 2 deletions packages/did/src/did/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -68,7 +68,7 @@ export function parseDidDocument(document: DidDocument): IDidDetails {
*/
export async function fromDid(
did: DidUrl,
keyring: KeyringInstance = new Keyring(),
keyring?: KeyringInstance,
resolver: DidResolver = defaultResolver
): Promise<Did> {
const document = await resolver.resolve(did);
Expand All @@ -94,7 +94,7 @@ export async function fromDid(
*/
export function fromDidDocument(
document: DidDocument,
keyring: KeyringInstance = new Keyring(),
keyring?: KeyringInstance,
resolver: DidResolver = defaultResolver
): Did {
const details = parseDidDocument(document);
Expand Down
40 changes: 39 additions & 1 deletion packages/did/src/did/index.spec.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,10 @@
// Copyright 2021-2022 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import { ethereumEncode } from '@zcloak/crypto';
import { stringToU8a } from '@polkadot/util';

import { ethereumEncode, generateMnemonic } from '@zcloak/crypto';
import { MockDidResolver } from '@zcloak/did-resolver';
import { DidDocument } from '@zcloak/did-resolver/types';
import { Keyring } from '@zcloak/keyring';

Expand Down Expand Up @@ -33,7 +36,13 @@ const DOCUMENT: DidDocument = {
service: []
};

const resolver = new MockDidResolver();

describe('Did', (): void => {
beforeAll(() => {
resolver.addDocument(DOCUMENT);
});

describe('create', (): void => {
let keyring: Keyring;

Expand All @@ -59,6 +68,8 @@ describe('Did', (): void => {
'health correct setup usage father decorate curious copper sorry recycle skin equal';
const did = createEcdsaFromMnemonic(mnemonic, keyring);

resolver.addDocument(did.getDocument());

expect(did.get([...(did.authentication ?? [])][0]).publicKey).toEqual(key0);
expect(did.get([...(did.keyAgreement ?? [])][0]).publicKey).toEqual(key1);
expect([...did.controller][0]).toEqual(`did:zk:${ethereumEncode(controllerKey)}`);
Expand All @@ -84,4 +95,31 @@ describe('Did', (): void => {
expect(document.service).toEqual(DOCUMENT.service);
});
});

describe('encrypt and decrypt', (): void => {
it('encrypt and decrypt', async (): Promise<void> => {
const sender = createEcdsaFromMnemonic(generateMnemonic(12));
const receiver = createEcdsaFromMnemonic(generateMnemonic(12));

resolver.addDocument(sender.getDocument());
resolver.addDocument(receiver.getDocument());

const message = stringToU8a('abcd');

const {
data: encrypted,
receiverUrl,
senderUrl
} = await sender.encrypt(
message,
receiver.getKeyUrl('keyAgreement'),
sender.getKeyUrl('keyAgreement'),
resolver
);

const decrypted = await receiver.decrypt(encrypted, senderUrl, receiverUrl, resolver);

expect(decrypted).toEqual(message);
});
});
});
40 changes: 18 additions & 22 deletions packages/did/src/did/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '@zcloak/crypto/types';
import type { DidResolver } from '@zcloak/did-resolver';
import type { DidUrl } from '@zcloak/did-resolver/types';
import type { KeyringInstance } from '@zcloak/keyring/types';
import type { IDidKeyring } from '../types';
import type { DidKeys, EncryptedData, IDidKeyring, SignedData } from '../types';

import { assert } from '@polkadot/util';

Expand All @@ -20,30 +22,24 @@ export abstract class DidKeyring implements IDidKeyring {
return this.#keyring.getPair(publicKey);
}

public sign(publicKey: Uint8Array, message: Uint8Array | HexString): Uint8Array {
const pair = this.getPair(publicKey);
public abstract signWithKey(
message: Uint8Array | HexString,
key: Exclude<DidKeys, 'keyAgreement'>
): SignedData;

return pair.sign(message);
}
public abstract sign(message: Uint8Array | HexString, id: DidUrl): SignedData;

public encrypt(
publicKey: Uint8Array,
public abstract encrypt(
message: Uint8Array | HexString,
recipientPublicKey: Uint8Array | HexString,
nonce?: Uint8Array | HexString | undefined
): Uint8Array {
const pair = this.getPair(publicKey);

return pair.encrypt(message, recipientPublicKey, nonce);
}
receiverUrl: DidUrl,
senderUrl?: DidUrl,
resolver?: DidResolver
): Promise<EncryptedData>;

public decrypt(
publicKey: Uint8Array,
public abstract decrypt(
encryptedMessageWithNonce: Uint8Array | HexString,
senderPublicKey: Uint8Array | HexString
): Uint8Array {
const pair = this.getPair(publicKey);

return pair.decrypt(encryptedMessageWithNonce, senderPublicKey);
}
senderUrl: DidUrl,
receiverUrl: DidUrl,
resolver?: DidResolver
): Promise<Uint8Array>;
}
16 changes: 0 additions & 16 deletions packages/did/src/did/types.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,6 @@
// Copyright 2021-2022 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { DidUrl, VerificationMethodType } from '@zcloak/did-resolver/types';

// @internal generate keys
export type KeyGen = {
// the identifier publicKey
identifier: Uint8Array;
Expand All @@ -13,16 +10,3 @@ export type KeyGen = {
*/
keys: [Uint8Array, Uint8Array];
};

export type SignedData = {
id: DidUrl;
type: VerificationMethodType;
signature: Uint8Array;
};

export type DidKeys =
| 'authentication'
| 'assertionMethod'
| 'keyAgreement'
| 'capabilityInvocation'
| 'capabilityDelegation';
Loading