Skip to content

Commit

Permalink
feat: signWithDid
Browse files Browse the repository at this point in the history
  • Loading branch information
rflechtner committed Oct 19, 2023
1 parent 8ca4b2e commit 0d876eb
Show file tree
Hide file tree
Showing 3 changed files with 84 additions and 40 deletions.
23 changes: 7 additions & 16 deletions packages/core/src/delegation/DelegationNode.ts
Original file line number Diff line number Diff line change
Expand Up @@ -265,26 +265,17 @@ export class DelegationNode implements IDelegationNode {
delegateDid: DidDocument,
signers: SignerInterface[]
): Promise<Did.EncodedSignature> {
const { byDid, verifiableOnChain } = Signers.select
const signer = await Signers.selectSigner(
signers,
verifiableOnChain(),
byDid(delegateDid, {
verificationRelationship: 'authentication',
})
)
if (!signer) {
throw new Error(
'no signer available for on-chain verifiable signatures by an authentication key'
)
}
const delegateSignature = await signer.sign({
const { signature, algorithm } = await Did.signWithDid({
data: this.generateHash(),
did: delegateDid,
signers,
purpose: 'authentication',
algorithms: Signers.DID_PALLET_SUPPORTED_ALGORITHMS, // signature is verified on-chain
})

return Did.didSignatureToChain({
signature: delegateSignature,
algorithm: signer.algorithm,
signature,
algorithm,
})
}

Expand Down
71 changes: 69 additions & 2 deletions packages/did/src/Did.signature.ts
Original file line number Diff line number Diff line change
Expand Up @@ -14,13 +14,14 @@ import type {
DidUri,
DidUrl,
SignatureVerificationRelationship,
SignerInterface,
SignResponseData,
} from '@kiltprotocol/types'

import { Crypto, SDKErrors } from '@kiltprotocol/utils'
import { Crypto, SDKErrors, Signers } from '@kiltprotocol/utils'

import { multibaseKeyToDidKey, parse, validateIdentifier } from './Did.utils.js'
import { dereference } from './DidResolver/DidResolver.js'
import { dereference, resolve } from './DidResolver/DidResolver.js'

export type DidSignatureVerificationInput = {
message: string | Uint8Array
Expand Down Expand Up @@ -63,6 +64,72 @@ function verifyDidSignatureDataStructure(
validateIdentifier(verificationMethodUrl, 'Url')
}

/**
* @param root0
* @param root0.data
* @param root0.did
* @param root0.signers
* @param root0.purpose
* @param root0.algorithms
*/
export async function signWithDid({
data,
did,
signers,
purpose = 'authentication',
algorithms = Signers.DID_PALLET_SUPPORTED_ALGORITHMS,
}: {
data: Uint8Array
did: DidDocument | DidUri
signers: readonly SignerInterface[]
purpose?: string
algorithms?: readonly string[]
}): Promise<{ id: DidUrl; algorithm: string; signature: Uint8Array }> {
const didDocument =
typeof did === 'string' ? (await resolve(did)).didDocument : did
if (!didDocument?.id) {
throw new Error('Unable to sign: Signer DID cannot be resolved')
}
if (!didDocument.verificationMethod?.length) {
throw new Error(
'Unable to sign: No verification methods are associated with the signer DID document. It may be that this DID has been deactivated.'
)
}
if (typeof purpose === 'string' && !didDocument[purpose]?.length) {
throw new Error(
`Unable to sign: No verification methods associated with the signer DID document match the requested purpose ("${purpose}").`
)
}

const { byAlgorithm, byDid } = Signers.select
const signerSelectors = [
byDid(didDocument, { verificationRelationship: purpose }),
]
if (Array.isArray(algorithms)) {
signerSelectors.push(byAlgorithm(algorithms))
}
const signer = await Signers.selectSigner(signers, ...signerSelectors)
if (!signer) {
const eligibleVms: string[] | undefined = purpose
? didDocument[purpose]
: didDocument.verificationMethod?.map(({ id }) => id)
const msg = `Unable to sign: No signer interface passed to this function fulfills the required criteria (eligible verification methods: [${
eligibleVms?.join(', ') ?? ''
}]${algorithms ? `, eligible algorithms: [${algorithms.join(', ')}]` : ''}).
Available signers:
${signers.map(({ id, algorithm }) => `${id} (${algorithm})`).join('\n')}
`
throw new Error(msg)
}

const signature = await signer.sign({ data })
return {
id: signer.id as DidUrl,
algorithm: signer.algorithm,
signature,
}
}

/**
* Verify a DID signature given the signer's DID URL (i.e., DID URI + verification method ID).
* A signature verification returns false if a migrated and then deleted DID is used.
Expand Down
30 changes: 8 additions & 22 deletions packages/legacy-credentials/src/Credential.ts
Original file line number Diff line number Diff line change
Expand Up @@ -21,13 +21,12 @@ import { ConfigService } from '@kiltprotocol/config'
import { Attestation, CType } from '@kiltprotocol/core'
import {
isDidSignature,
resolve,
signWithDid,
dereference,
signatureFromJson,
verifyDidSignature,
} from '@kiltprotocol/did'
import type {
DidUrl,
DidUri,
Hash,
IAttestation,
Expand Down Expand Up @@ -507,8 +506,6 @@ function getAttributes(credential: ICredential): Set<string> {
return new Set(Object.keys(credential.claim.contents))
}

const { verifiableOnChain, byDid } = Signers.select

/**
* Creates a public presentation which can be sent to a verifier.
* This presentation is signed.
Expand All @@ -530,7 +527,7 @@ export async function createPresentation({
didDocument,
}: {
credential: ICredential
signers: SignerInterface[]
signers: readonly SignerInterface[]
selectedAttributes?: string[]
challenge?: string
didDocument?: DidDocument
Expand All @@ -548,30 +545,19 @@ export async function createPresentation({
excludedClaimProperties
)

if (!didDocument) {
didDocument = (await resolve(credential.claim.owner)).didDocument
}
if (!didDocument) {
throw new Error('claimer DID cannot be resolved')
}
const signer = await Signers.selectSigner(
signers,
verifiableOnChain(),
byDid(didDocument, { verificationRelationship: 'authentication' })
)
if (!signer) {
throw new Error('no suitable signer available')
}

const signature = await signer?.sign({
const { signature, id: signerUrl } = await signWithDid({
did: didDocument ?? credential.claim.owner,
data: makeSigningData(presentation, challenge),
signers,
purpose: 'authentication',
algorithms: Signers.DID_PALLET_SUPPORTED_ALGORITHMS, // legacy presentations use Polkadot-compatible signatures exclusively
})

return {
...presentation,
claimerSignature: {
signature: Crypto.u8aToHex(signature),
signerUrl: signer.id as DidUrl,
signerUrl,
...(challenge && { challenge }),
},
}
Expand Down

0 comments on commit 0d876eb

Please sign in to comment.