Skip to content

Commit

Permalink
Signing controller (#58)
Browse files Browse the repository at this point in the history
* add ethereum address mapping for ecdsa pair

* changeset

* 1. add controller sign key for did keyring
2. publish did document default to use controller key
3. vp presentation default to use controller key
4. publish ctype default to use controller key
5. try to use assertionMethod, if it not exist, use controller to sign vc

* changeset

* test fix
  • Loading branch information
zzcwoshizz committed Mar 7, 2023
1 parent ede41b7 commit c3ad20f
Show file tree
Hide file tree
Showing 15 changed files with 118 additions and 46 deletions.
14 changes: 14 additions & 0 deletions .changeset/hot-bulldogs-kick.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
---
'@zcloak/crypto': minor
'@zcloak/verify': minor
'@zcloak/ctype': minor
'@zcloak/did': minor
'@zcloak/vc': minor
---

add controller sign key for did keyring

1. publish did document default to use controller key
2. vp presentation default to use controller key
3. publish ctype default to use controller key
4. try to use assertionMethod, if it not exist, use controller to sign vc
5 changes: 5 additions & 0 deletions .changeset/metal-kings-tell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@zcloak/keyring': minor
---

add ethereum address mapping for ecdsa pair
10 changes: 7 additions & 3 deletions packages/crypto/src/multibase/helpers.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '../types';

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

interface Coder {
decode: (value: string) => Uint8Array;
encode: (value: Uint8Array) => string;
Expand All @@ -14,7 +18,7 @@ interface Config {
}
type DecodeFn = (value: string) => Uint8Array;

type EncodeFn = (value: Uint8Array) => string;
type EncodeFn = (value: Uint8Array | HexString) => string;

type ValidateFn = (value?: unknown) => value is string;

Expand All @@ -29,8 +33,8 @@ export function createDecode({ coder }: Config, validate: ValidateFn): DecodeFn

/** @internal */
export function createEncode({ coder, prefix }: Config): EncodeFn {
return (value: Uint8Array): string => {
const out = coder.encode(value);
return (value: Uint8Array | HexString): string => {
const out = coder.encode(u8aToU8a(value));

return `${prefix}${out}`;
};
Expand Down
6 changes: 1 addition & 5 deletions packages/ctype/src/publish.ts
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,7 @@ export async function getPublish(base: BaseCType, publisher: Did): Promise<CType

const message = signedCTypeMessage(hash, version);

const {
id,
signature,
type: signatureType
} = await publisher.signWithKey(message, 'authentication');
const { id, signature, type: signatureType } = await publisher.signWithKey(message, 'controller');

return {
$id: hash,
Expand Down
6 changes: 1 addition & 5 deletions packages/did/src/did/chain.ts
Original file line number Diff line number Diff line change
Expand Up @@ -87,11 +87,7 @@ export abstract class DidChain extends DidKeyring {

const message = signedDidDocumentMessage(hashDidDocument(document), document.version);

const {
id,
signature,
type: signatureType
} = await this.signWithKey(message, 'capabilityInvocation');
const { id, signature, type: signatureType } = await this.signWithKey(message, 'controller');

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

Expand Down
11 changes: 11 additions & 0 deletions packages/did/src/did/details.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
// Copyright 2021-2023 zcloak authors & contributors
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '@zcloak/crypto/types';
import type { DidUrl, Service } from '@zcloak/did-resolver/types';
import type { DidKeys, IDidDetails, KeyRelationship } from '../types';

Expand Down Expand Up @@ -58,8 +59,18 @@ export abstract class DidDetails implements IDidDetails {

/**
* Get [[KeyRelationship]] by `id`
* When the `id` equal to `detail.id`, it means use controller key
*/
public get(id: DidUrl): KeyRelationship {
if (id === this.id) {
return {
id: this.id,
controller: [this.id],
publicKey: this.identifier as HexString,
type: 'EcdsaSecp256k1VerificationKey2019'
};
}

const method = this.keyRelationship.get(id);

assert(method, `Not find verficationMethod with id: ${id}`);
Expand Down
2 changes: 1 addition & 1 deletion packages/did/src/did/index.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ describe('Did', (): void => {
signedDidDocumentMessage(hashDidDocument(document), document.version || '0')
),
decodeMultibase(document.proof[0].signature),
alice.get(alice.getKeyUrl('capabilityInvocation')).publicKey
alice.get(alice.getKeyUrl('controller')).publicKey
)
).toBe(true);
});
Expand Down
2 changes: 1 addition & 1 deletion packages/did/src/did/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ export abstract class DidKeyring extends DidDetails implements IDidKeyring {

public async signWithKey(
message: Uint8Array | HexString,
keyOrDidUrl: DidUrl | Exclude<DidKeys, 'keyAgreement'>
keyOrDidUrl: DidUrl | Exclude<DidKeys, 'keyAgreement'> = 'controller'
): Promise<SignedData> {
const didUrl = isDidUrl(keyOrDidUrl) ? keyOrDidUrl : this.getKeyUrl(keyOrDidUrl);
const { type } = this.get(didUrl);
Expand Down
3 changes: 2 additions & 1 deletion packages/did/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -11,6 +11,7 @@ import type {
} from '@zcloak/did-resolver/types';

export type DidKeys =
| 'controller'
| 'authentication'
| 'keyAgreement'
| 'assertionMethod'
Expand All @@ -33,7 +34,7 @@ export type EncryptedData = {
export interface KeyRelationship {
id: DidUrl;
controller: DidUrl[];
publicKey: Uint8Array;
publicKey: Uint8Array | HexString;
type: VerificationMethodType;
}

Expand Down
4 changes: 2 additions & 2 deletions packages/keyring/src/keyring.ts
Original file line number Diff line number Diff line change
Expand Up @@ -144,8 +144,8 @@ export class Keyring implements KeyringInstance {
return createPair(keypair, { type });
}

public getPair(publicKey: Uint8Array | HexString): KeyringPair {
return this.#pairs.get(publicKey);
public getPair(publicKeyOrAddress: Uint8Array | HexString): KeyringPair {
return this.#pairs.get(publicKeyOrAddress);
}

public getPairs(): KeyringPair[] {
Expand Down
26 changes: 22 additions & 4 deletions packages/keyring/src/pairs.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,14 +4,24 @@
import type { HexString } from '@zcloak/crypto/types';
import type { KeyringPair, KeyringPairs } from './types';

import { isHex, u8aToHex } from '@polkadot/util';
import { isHex, isU8a, u8aToHex } from '@polkadot/util';

type KeyringPairMap = Record<string, KeyringPair>;
import { ethereumEncode, isEthereumAddress } from '@zcloak/crypto';

type KeyringPairMap = Record<HexString, KeyringPair>;

type EthereumMapping = Record<HexString, HexString>;

export class Pairs implements KeyringPairs {
readonly #map: KeyringPairMap = {};
readonly #ethereumMapping: EthereumMapping = {};

public add(pair: KeyringPair): KeyringPair {
// save the ethereum address when `ecdsa` pair
if (pair.type === 'ecdsa') {
this.#ethereumMapping[ethereumEncode(pair.publicKey)] = u8aToHex(pair.publicKey);
}

this.#map[u8aToHex(pair.publicKey)] = pair;

return pair;
Expand All @@ -21,12 +31,20 @@ export class Pairs implements KeyringPairs {
return Object.values(this.#map);
}

public get(publicKey: HexString | Uint8Array): KeyringPair {
public get(publicKeyOrAddress: HexString | Uint8Array): KeyringPair {
const publicKey = isU8a(publicKeyOrAddress)
? publicKeyOrAddress
: isEthereumAddress(publicKeyOrAddress)
? this.#ethereumMapping[publicKeyOrAddress]
: publicKeyOrAddress;

const pair = this.#map[isHex(publicKey) ? publicKey : u8aToHex(publicKey)];

if (!pair) {
throw new Error(
`Unable to retrieve keypair '${isHex(publicKey) ? publicKey : u8aToHex(publicKey)}'`
`Unable to retrieve keypair '${
isHex(publicKeyOrAddress) ? publicKeyOrAddress : u8aToHex(publicKeyOrAddress)
}'`
);
}

Expand Down
45 changes: 36 additions & 9 deletions packages/vc/src/credential/vc.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,10 @@
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '@polkadot/util/types';
import type { CType } from '@zcloak/ctype/types';
import type { Did } from '@zcloak/did';
import type { DidKeys } from '@zcloak/did/types';
import type { DidUrl } from '@zcloak/did-resolver/types';
import type {
HashType,
Proof,
Expand All @@ -13,8 +17,6 @@ import type {
import { assert } from '@polkadot/util';

import { base58Encode } from '@zcloak/crypto';
import { CType } from '@zcloak/ctype/types';
import { Did } from '@zcloak/did';

import { DEFAULT_CONTEXT, DEFAULT_VC_VERSION } from '../defaults';
import { calcDigest, DigestPayload } from '../digest';
Expand Down Expand Up @@ -89,12 +91,26 @@ export class VerifiableCredentialBuilder {
/**
* Build to [[PublicVerifiableCredential]]
*/
public async build(issuer: Did, isPublic: true): Promise<VerifiableCredential<true>>;
public async build(
issuer: Did,
isPublic: true,
didKey?: Exclude<DidKeys, 'keyAgreement'>
): Promise<VerifiableCredential<true>>;

/**
* Build to [[PrivateVerifiableCredential]]
*/
public async build(issuer: Did, isPublic?: false): Promise<VerifiableCredential<false>>;
public async build(issuer: Did, isPublic?: boolean): Promise<VerifiableCredential<boolean>> {
public async build(
issuer: Did,
isPublic?: false,
didKey?: Exclude<DidKeys, 'keyAgreement'>
): Promise<VerifiableCredential<false>>;

public async build(
issuer: Did,
isPublic?: boolean,
didKey: Exclude<DidKeys, 'keyAgreement'> = 'assertionMethod'
): Promise<VerifiableCredential<boolean>> {
assert(this.raw.checkSubject(), `Subject check failed when use ctype ${this.raw.ctype}`);

if (
Expand Down Expand Up @@ -126,7 +142,7 @@ export class VerifiableCredentialBuilder {
this.digestHashType
);

const proof = await this._signDigest(issuer, digest, this.version);
const proof = await this._signDigest(issuer, digest, this.version, didKey);

let vc: VerifiableCredential<boolean> = {
'@context': this['@context'],
Expand Down Expand Up @@ -218,7 +234,8 @@ export class VerifiableCredentialBuilder {
private async _signDigest(
did: Did,
digest: HexString,
version: VerifiableCredentialVersion
version: VerifiableCredentialVersion,
didKey: Exclude<DidKeys, 'keyAgreement'> = 'assertionMethod'
): Promise<Proof> {
let message: Uint8Array | HexString;

Expand All @@ -228,13 +245,23 @@ export class VerifiableCredentialBuilder {
message = digest;
}

const { id, signature, type: signType } = await did.signWithKey(message, 'assertionMethod');
let signDidUrl: DidUrl;

// try to use support didKey, if it not exist, try use controller key
try {
signDidUrl = did.getKeyUrl(didKey);
} catch {
didKey = 'controller';
signDidUrl = did.getKeyUrl('controller');
}

const { id, signature, type: signType } = await did.signWithKey(message, signDidUrl);

return {
type: signType,
created: Date.now(),
verificationMethod: id,
proofPurpose: 'assertionMethod',
proofPurpose: didKey,
proofValue: base58Encode(signature)
};
}
Expand Down
14 changes: 7 additions & 7 deletions packages/vc/src/vp.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -140,7 +140,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -176,7 +176,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -216,7 +216,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -250,7 +250,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc1.digest, vc2.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -300,7 +300,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc1.digest, vc2.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -361,7 +361,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc1.digest, vc2.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down Expand Up @@ -428,7 +428,7 @@ describe('VerifiablePresentation', (): void => {
id: vpID([vc1.digest, vc2.digest, vc3.digest], vp.version, DEFAULT_VP_HASH_TYPE).hash,
proof: {
type: 'EcdsaSecp256k1SignatureEip191',
proofPurpose: 'authentication'
proofPurpose: 'controller'
},
hasher: [DEFAULT_VP_HASH_TYPE]
});
Expand Down
10 changes: 3 additions & 7 deletions packages/vc/src/vp.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
// SPDX-License-Identifier: Apache-2.0

import type { HexString } from '@zcloak/crypto/types';
import type { Did } from '@zcloak/did';
import type {
HashType,
Proof,
Expand All @@ -14,7 +15,6 @@ import type {
import { assert, isHex, objectCopy, stringToU8a, u8aConcat, u8aToHex } from '@polkadot/util';

import { base58Encode } from '@zcloak/crypto';
import { Did } from '@zcloak/did';
import { isSameUri } from '@zcloak/did/utils';

import { DEFAULT_CONTEXT, DEFAULT_VP_HASH_TYPE, DEFAULT_VP_VERSION } from './defaults';
Expand Down Expand Up @@ -179,17 +179,13 @@ export class VerifiablePresentationBuilder {
message = signedVPMessage(hash, this.version, challenge);
}

const {
id,
signature,
type: signType
} = await this.#did.signWithKey(message, 'authentication');
const { id, signature, type: signType } = await this.#did.signWithKey(message, 'controller');

return {
type: signType,
created: Date.now(),
verificationMethod: id,
proofPurpose: 'authentication',
proofPurpose: 'controller',
proofValue: base58Encode(signature),
challenge
};
Expand Down
Loading

0 comments on commit c3ad20f

Please sign in to comment.