Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
ce86cfa
refactor: update to sdk v1 alpha
rflechtner Jan 9, 2024
52957a9
refactor!: did config credential proofs
rflechtner Jan 10, 2024
e8d75e7
refactor!: move callbacks, fix errors, rename (did)resolveKey
rflechtner Jan 11, 2024
3bebcde
refactor!: select authentication signer on session creation
rflechtner Jan 15, 2024
6ade536
test: fix tests
rflechtner Jan 11, 2024
cb49b05
ci: use node 18
rflechtner Jan 15, 2024
29bf035
chore: revert to keyUri on callbacks
rflechtner Jan 15, 2024
88e4870
chore: revert to fromCredential command
rflechtner Jan 15, 2024
2dd985a
chore: update dependencies and update copyright year to 2025
BlertaSunCacti Apr 28, 2025
eb61d73
chore: add ignore pattern for license-header.js in ESLint configuration
BlertaSunCacti Apr 29, 2025
63172cc
refactor: update copyright year and improve type handling in Quote tests
BlertaSunCacti Apr 29, 2025
380635c
refactor: rename transfer operation and fix key concatenation error
BlertaSunCacti Apr 30, 2025
1a69d10
chore: fix and improve test cases
BlertaSunCacti Apr 30, 2025
fc1200d
Remove redundant dependencies and fix eslint rule for license header
BlertaSunCacti May 6, 2025
67a9ceb
Update cli issueCredential with named parameters and validate did string
BlertaSunCacti May 6, 2025
3b01384
Update terminology from "attester" to "issuer" and from "claimer" to …
BlertaSunCacti May 6, 2025
2fdd3c7
Replaced holderSignature with claimerSignature
BlertaSunCacti May 6, 2025
38ab135
Correct spelling within license
BlertaSunCacti May 6, 2025
ee28f47
refactor: improve Message test file by using helper functions
BlertaSunCacti May 7, 2025
dc0161e
refactor: use helper function instead of manual construction
BlertaSunCacti May 7, 2025
f601be3
Merge branch 'main' into rf-sdk-1-alpha
BlertaSunCacti May 7, 2025
5f0f24e
Run prettier
BlertaSunCacti May 7, 2025
7fcc0f2
Remove yarn path from yarnrc
BlertaSunCacti May 7, 2025
f2f088b
Revert "Remove yarn path from yarnrc"
BlertaSunCacti May 7, 2025
e462c46
Update package.json clean script and adjust TypeScript output directory
BlertaSunCacti May 7, 2025
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 .eslintrc.json
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
"sourceType": "module"
},
"plugins": ["@typescript-eslint", "license-header"],
"ignorePatterns": ["license-header.js", "**/*.config.js", "**/*.config.cjs", "cjs/**", "esm/**"],
"rules": {
"linebreak-style": ["error", "unix"],
"quotes": ["error", "single"],
Expand Down
6 changes: 3 additions & 3 deletions .github/workflows/publish.yml
Original file line number Diff line number Diff line change
Expand Up @@ -32,14 +32,14 @@ jobs:
id: login-ecr
uses: aws-actions/amazon-ecr-login@v1

- name: set image name
- name: Set image name
run: |
echo "IMG_NAME=${{ steps.login-ecr.outputs.registry }}/kiltprotocol/prototype-chain:latest" >> "$GITHUB_ENV"

- name: pull image
- name: Pull Docker image
run: docker pull $IMG_NAME

- name: run tests
- name: Run tests
timeout-minutes: 5
env:
TESTCONTAINERS_NODE_IMG: ${{ env.IMG_NAME }}
Expand Down
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
.yarn
/node_modules
yarn-error.log
/.vscode
Expand Down
948 changes: 948 additions & 0 deletions .yarn/releases/yarn-4.9.1.cjs

Large diffs are not rendered by default.

7 changes: 7 additions & 0 deletions .yarnrc.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
compressionLevel: 0

enableGlobalCache: false

nodeLinker: node-modules

yarnPath: .yarn/releases/yarn-4.9.1.cjs
6 changes: 2 additions & 4 deletions jest.config.cjs
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,7 @@ module.exports = {
rootDir: 'src',
coverageDirectory: 'coverage',
moduleDirectories: ['node_modules'],
globals: {
'ts-jest': {
tsconfig: 'tsconfig.cjs.json',
},
transform: {
'^.+\\.ts$': ['ts-jest', { tsconfig: 'tsconfig.cjs.json' }],
},
}
2 changes: 1 addition & 1 deletion license-header.js
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,4 @@
*
* This source code is licensed under the BSD 4-Clause "Original" license
* found in the LICENSE file in the root directory of this source tree.
*/
*/
23 changes: 12 additions & 11 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@
"main": "./index.js",
"types": "./index.d.ts",
"type": "commonjs",
"engines": {
"node": ">=20.0.0"
},
"exports": {
".": {
"import": "./esm/index.js",
Expand Down Expand Up @@ -58,39 +61,37 @@
"build": "yarn build:cjs && yarn build:esm",
"build:cjs": "tsc -p tsconfig.cjs.json",
"build:esm": "tsc -p tsconfig.esm.json && echo '{\"type\": \"module\"}' > esm/package.json",
"clean": "exec git clean -xf -e 'node_modules' '*'",
"clean": "yarn exec git clean -xf -e 'node_modules' -e '.yarn' '*'",
"test": "jest test --no-cache --forceExit",
"prepack": "yarn build"
},
"dependencies": {
"@kiltprotocol/sdk-js": "^0.35.0",
"@kiltprotocol/types": "^0.35.0",
"@kiltprotocol/vc-export": "^0.35.0",
"@polkadot/keyring": "^12.3.2",
"@polkadot/util": "^12.3.2",
"@kiltprotocol/augment-api": "^1.11405.0",
Comment thread
Dudleyneedham marked this conversation as resolved.
"@kiltprotocol/legacy-credentials": "^0.100.0",
"@kiltprotocol/sdk-js": "1.0.0",
"yargs": "^17.7.2"
},
"devDependencies": {
"@polkadot/util-crypto": "^12.3.2",
"@types/jest": "^28.0.0",
"@types/node": "^18.8.2",
"@types/react": "^18.0.21",
"@types/valid-url": "^1.0.3",
"@typescript-eslint/eslint-plugin": "^5.33.0",
"@typescript-eslint/parser": "^5.33.0",
"eslint": ">=8.14.0",
"eslint": "^8.57.0",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-license-header": "^0.6.0",
"jest": "^28.0.0",
"jest": "^29.7.0",
"prettier": "^3.0.1",
"react": "^18.2.0",
"testcontainers": "^9.5.0",
"ts-jest": "^28.0.8",
"ts-jest": "^29.3.2",
"ts-jest-resolver": "^2.0.1",
"ts-node": "^10.9.1",
"typescript": "^4.7.4"
},
"bin": {
"createDidConfig": "./cli/createDidConfig.js"
}
},
"packageManager": "yarn@4.9.1"
}
109 changes: 76 additions & 33 deletions src/cli/createDidConfig.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,17 +7,24 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import { Credential, Did, connect, disconnect } from '@kiltprotocol/sdk-js'
import { DidResourceUri, DidUri, ICredentialPresentation, SignCallback } from '@kiltprotocol/types'
import { DataIntegrity } from '@kiltprotocol/credentials'
import { multibaseKeyToDidKey, validateDid } from '@kiltprotocol/did'
import { DidResolver, connect, disconnect } from '@kiltprotocol/sdk-js'
import { Did, DidUrl } from '@kiltprotocol/types'
import { Signers } from '@kiltprotocol/utils'

import { Keyring } from '@polkadot/keyring'
import { u8aEq } from '@polkadot/util'

import { readFile, writeFile } from 'fs/promises'
import yargs from 'yargs/yargs'

import { didConfigResourceFromCredential, createCredential } from '../wellKnownDidConfiguration/index.js'
import type { DidConfigResource } from '../types/index.js'
import { DidConfigResource, DomainLinkageCredential } from '../types/Credential.js'
import {
KILT_SELF_SIGNED_PROOF_TYPE,
createCredential,
didConfigResourceFromCredentials,
} from '../wellKnownDidConfiguration/index.js'

type KeyType = 'sr25519' | 'ed25519' | 'ecdsa'

Expand All @@ -41,22 +48,39 @@ const createCredentialOpts = {
wsAddress: { alias: 'w', type: 'string', demandOption: true, default: 'wss://spiritnet.kilt.io' },
} as const

async function issueCredential(did: DidUri, origin: string, seed: string, keyType: KeyType, nodeAddress: string) {
await connect(nodeAddress)
const didDocument = await Did.resolve(did)
const assertionMethod = didDocument?.document?.assertionMethod?.[0]
async function issueCredential({
did,
origin,
seed,
keyType,
proofType,
}: {
did: Did
origin: string
seed: string
keyType: KeyType
proofType: string
}) {
const { didDocument } = await DidResolver.resolve(did, {})
const assertionMethodId = didDocument?.assertionMethod?.[0]
const assertionMethod = didDocument?.verificationMethod?.find(({ id }) => id === assertionMethodId)
if (!assertionMethod) {
throw new Error(
`Could not resolve assertionMethod of ${did}. Make sure the DID is registered to this chain and has an assertionMethod key.`
)
}
const keyUri: DidUrl = `${didDocument!.id}${assertionMethod.id}`

const keypair = new Keyring({ type: keyType }).addFromUri(seed)
if (assertionMethod.type !== keypair.type || !u8aEq(assertionMethod.publicKey, keypair.publicKey)) {
const signers = await Signers.getSignersForKeypair({ keypair, id: keyUri })

const { keyType: vmType, publicKey } = multibaseKeyToDidKey(assertionMethod.publicKeyMultibase)
if (vmType !== keypair.type || !u8aEq(publicKey, keypair.publicKey)) {
throw new Error('public key and/or key type of the DIDs assertionMethod does not match the supplied signing key')
}
const keyUri: DidResourceUri = `${didDocument!.document!.uri}${assertionMethod.id}`
const signCallback: SignCallback = async ({ data }) => ({ signature: keypair.sign(data), keyUri, keyType })
const credential = await createCredential(signCallback, origin, did)

const credential = await createCredential(signers, origin, didDocument!, { proofType } as any)

return credential
}

Expand All @@ -72,29 +96,30 @@ async function write(toWrite: unknown, outPath?: string) {
async function run() {
await yargs(process.argv.slice(2))
.command(
'fromCredential <pathToCredential>',
'create a Did Configuration Resource from an existing Kilt Credential Presentation',
'fromCredential [pathToCredential..]',
'create a Did Configuration Resource from one or more existing Domain Linkage Credentials',
(ygs) =>
ygs.options(commonOpts).positional('pathToCredential', {
describe: 'Path to a json file containing the credential presentation',
describe: 'Path to a json file containing a Domain Linkage Credential',
type: 'string',
demandOption: true,
array: true,
}),
async ({ pathToCredential, outFile }) => {
let credential: ICredentialPresentation
try {
credential = JSON.parse(await readFile(pathToCredential, { encoding: 'utf-8' }))
} catch (cause) {
throw new Error(`Cannot parse file ${pathToCredential}`, { cause })
}
if (!Credential.isPresentation(credential)) {
throw new Error(`Malformed Credential Presentation loaded from ${pathToCredential}`)
}
const credentials: DomainLinkageCredential[] = await Promise.all(
pathToCredential.map(async (path) => {
try {
return JSON.parse(await readFile(path, { encoding: 'utf-8' }))
} catch (cause) {
throw new Error(`Cannot parse file ${pathToCredential}`, { cause })
}
})
)
let didResource: DidConfigResource
try {
didResource = await didConfigResourceFromCredential(credential)
didResource = didConfigResourceFromCredentials(credentials)
} catch (cause) {
throw new Error('Credential Presentation is not suitable for use in a Did Configuration Resource', {
throw new Error('Credential is not suitable for use in a Did Configuration Resource', {
cause,
})
}
Expand All @@ -103,20 +128,38 @@ async function run() {
)
.command(
'credentialOnly',
'issue a new Kilt Credential Presentation for use in a Did Configuration Resource',
{ ...createCredentialOpts, ...commonOpts },
async ({ origin, seed, keyType, wsAddress, outFile, did }) => {
const credential = await issueCredential(did as DidUri, origin, seed, keyType, wsAddress)
'issue a new Domain Linkage Credential for use in a Did Configuration Resource',
{
...createCredentialOpts,
...commonOpts,
proofType: {
alias: 'p',
choices: [DataIntegrity.PROOF_TYPE, KILT_SELF_SIGNED_PROOF_TYPE] as const,
default: KILT_SELF_SIGNED_PROOF_TYPE,
describe:
'Which proof type to use in the credential. DataIntegrity is the more modern proof type, but might not be accepted by all extensions yet. Did Configuration Resources can contain multiple credentials, though.',
},
},
async ({ origin, seed, keyType, wsAddress, outFile, did, proofType }) => {
await connect(wsAddress)
validateDid(did)
const credential = await issueCredential({ did: did as Did, origin, seed, keyType, proofType })
await write(credential, outFile)
}
)
.command(
'$0',
'create a Did Configuration Resource from a freshly issued Kilt Credential',
'create a Did Configuration Resource containing newly issued Domain Linkage Credentials',
{ ...createCredentialOpts, ...commonOpts },
async ({ origin, seed, keyType, wsAddress, outFile, did }) => {
const credential = await issueCredential(did as DidUri, origin, seed, keyType, wsAddress)
const didResource = await didConfigResourceFromCredential(credential)
await connect(wsAddress)
validateDid(did)
const credentials = await Promise.all(
[DataIntegrity.PROOF_TYPE, KILT_SELF_SIGNED_PROOF_TYPE].map((proofType) =>
issueCredential({ did: did as Did, origin, seed, keyType, proofType })
)
)
const didResource = didConfigResourceFromCredentials(credentials)
await write(didResource, outFile)
}
)
Expand Down
19 changes: 11 additions & 8 deletions src/messaging/CredentialApiMessageType.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,8 @@
* found in the LICENSE file in the root directory of this source tree.
*/

import { Attestation, Claim, Credential, CType, Quote } from '@kiltprotocol/core'
import { Attestation, Claim, Credential } from '@kiltprotocol/legacy-credentials'
import { CType } from '@kiltprotocol/credentials'
import { DataUtils } from '@kiltprotocol/utils'
import * as Did from '@kiltprotocol/did'
import { isHex } from '@polkadot/util'
Expand All @@ -23,6 +24,8 @@ import {
import * as MessageError from './Error.js'
import type { IMessage, CredentialApiMessageBody } from '../types/index.js'
import { verifyMessageEnvelope } from './MessageEnvelope.js'
import { validateQuoteSchema } from '../quote/Quote.js'
import { QuoteSchema } from '../quote/QuoteSchema.js'

/**
* Checks if the message body is well-formed.
Expand All @@ -38,15 +41,15 @@ export function assertKnownMessageBody(message: IMessage): void {
DataUtils.verifyIsHex(body.content.delegationId)
}
if (body.content.quote) {
Quote.validateQuoteSchema(Quote.QuoteSchema, body.content.quote)
validateQuoteSchema(QuoteSchema, body.content.quote)
}
if (body.content.cTypes) {
body.content.cTypes.forEach((val) => CType.verifyDataStructure(val))
}
} else if (isRequestAttestation(message)) {
Credential.verifyDataStructure(message.body.content.credential)
if (message.body.content.quote) {
Quote.validateQuoteSchema(Quote.QuoteSchema, message.body.content.quote)
validateQuoteSchema(QuoteSchema, message.body.content.quote)
}
} else if (isSubmitAttestation(message)) {
Attestation.verifyDataStructure(message.body.content.attestation)
Expand All @@ -55,9 +58,9 @@ export function assertKnownMessageBody(message: IMessage): void {
throw new MessageError.HashMalformedError()
}
} else if (isIRequestCredential(message)) {
message.body.content.cTypes.forEach(({ cTypeHash, trustedAttesters, requiredProperties }) => {
message.body.content.cTypes.forEach(({ cTypeHash, trustedIssuers, requiredProperties }) => {
DataUtils.verifyIsHex(cTypeHash)
trustedAttesters?.forEach((did) => Did.validateUri(did, 'Did'))
trustedIssuers?.forEach((did) => Did.validateDid(did, 'Did'))
requiredProperties?.forEach((requiredProps) => {
if (typeof requiredProps !== 'string') throw new TypeError('Required properties is expected to be a string')
})
Expand Down Expand Up @@ -87,16 +90,16 @@ export function assertKnownMessageBody(message: IMessage): void {
export function ensureOwnerIsSender(message: IMessage): void {
if (isRequestAttestation(message)) {
if (!Did.isSameSubject(message.body.content.credential.claim.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Claim', 'Sender')
throw new MessageError.IdentityMismatchError('Sender not matching claim owner')
}
} else if (isSubmitAttestation(message)) {
if (!Did.isSameSubject(message.body.content.attestation.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Attestation', 'Sender')
throw new MessageError.IdentityMismatchError('Sender not matching attestation issuer')
}
} else if (isSubmitCredential(message)) {
message.body.content.forEach((presentation) => {
if (!Did.isSameSubject(presentation.claim.owner, message.sender)) {
throw new MessageError.IdentityMismatchError('Claims', 'Sender')
throw new MessageError.IdentityMismatchError('Sender not matching claim owner')
}
})
}
Expand Down
27 changes: 10 additions & 17 deletions src/messaging/Error.ts
Original file line number Diff line number Diff line change
Expand Up @@ -5,20 +5,13 @@
* found in the LICENSE file in the root directory of this source tree.
*/

export declare class MessageError extends Error {}
export declare class HashMalformedError extends MessageError {
constructor(hash?: string, type?: string)
}

export declare class SignatureMalformedError extends MessageError {}
export declare class UnknownMessageBodyTypeError extends MessageError {}
export declare class DecodingMessageError extends MessageError {}
export declare class CTypeUnknownPropertiesError extends MessageError {}
export declare class InvalidDidFormatError extends MessageError {}
export declare class KeyError extends MessageError {}
export declare class DidError extends MessageError {
constructor(context?: string, type?: string)
}
export declare class IdentityMismatchError extends MessageError {
constructor(context?: string, type?: string)
}
export class MessageError extends Error {}
export class HashMalformedError extends MessageError {}
export class SignatureMalformedError extends MessageError {}
Comment thread
Dudleyneedham marked this conversation as resolved.
export class UnknownMessageBodyTypeError extends MessageError {}
export class DecodingMessageError extends MessageError {}
export class CTypeUnknownPropertiesError extends MessageError {}
export class InvalidDidFormatError extends MessageError {}
export class KeyError extends MessageError {}
export class DidError extends MessageError {}
export class IdentityMismatchError extends MessageError {}
Loading
Loading