From 5024818d59a470637df4d39d9b96bc270f2451d9 Mon Sep 17 00:00:00 2001 From: Curtis Date: Thu, 4 Jul 2024 08:41:34 +0100 Subject: [PATCH] fix(Castor): createPrismDID and resolveDID key id conflicts (#243) Signed-off-by: Curtis Harding --- src/castor/Castor.ts | 22 +- .../resolver/LongFormPrismDIDResolver.ts | 7 +- src/domain/models/keyManagement/Key.ts | 17 +- src/pollux/utils/JWT.ts | 37 +-- tests/castor/PrismDID.test.ts | 218 ++++++++++++++---- tests/fixtures/keys.ts | 9 - 6 files changed, 224 insertions(+), 86 deletions(-) diff --git a/src/castor/Castor.ts b/src/castor/Castor.ts index c10215e4a..0cfd65473 100644 --- a/src/castor/Castor.ts +++ b/src/castor/Castor.ts @@ -43,7 +43,7 @@ import { X25519PublicKey } from "../apollo/utils/X25519PublicKey"; import { Ed25519PublicKey } from "../apollo/utils/Ed25519PublicKey"; import { PrismDIDPublicKey } from "./did/prismDID/PrismDIDPublicKey"; -type ExtraResolver = new (apollo: Apollo) => DIDResolver +type ExtraResolver = new (apollo: Apollo) => DIDResolver; /** * Castor is a powerful and flexible library for working with DIDs. Whether you are building a decentralised application * or a more traditional system requiring secure and private identity management, Castor provides the tools and features @@ -136,21 +136,19 @@ export default class Castor implements CastorInterface { getUsageId(Usage.AUTHENTICATION_KEY), Usage.AUTHENTICATION_KEY, masterPublicKey, - ).toProto(); - if (issuingKeys.length) { - didPublicKeys.push(...issuingKeys.map((issuingKey) => new PrismDIDPublicKey( - getUsageId(Usage.ISSUING_KEY), + didPublicKeys.push(masterPk); + didPublicKeys.push(authenticationPk); + + if (issuingKeys.length > 0) { + didPublicKeys.push(...issuingKeys.map((issuingKey, index) => new PrismDIDPublicKey( + getUsageId(Usage.ISSUING_KEY, index), Usage.ISSUING_KEY, "publicKey" in issuingKey ? issuingKey.publicKey : issuingKey, - - ).toProto())) + ).toProto())); } - didPublicKeys.push(authenticationPk) - didPublicKeys.push(masterPk) - const didCreationData = new Protos.io.iohk.atala.prism.protos.CreateDIDOperation.DIDCreationData({ public_keys: didPublicKeys, @@ -332,7 +330,7 @@ export default class Castor implements CastorInterface { if (method.type === Curve.ED25519) { const publicKey = new Ed25519PublicKey( Buffer.from(base58.base58btc.decode(method.publicKeyMultibase)) - ) + ); if ( publicKey.canVerify() && publicKey.verify(Buffer.from(challenge), Buffer.from(signature)) @@ -341,7 +339,7 @@ export default class Castor implements CastorInterface { } } } catch (err) { - console.debug("checking next key for verification") + console.debug("checking next key for verification"); } } } else if (did.method == "peer") { diff --git a/src/castor/resolver/LongFormPrismDIDResolver.ts b/src/castor/resolver/LongFormPrismDIDResolver.ts index 29b5a1031..354c91364 100644 --- a/src/castor/resolver/LongFormPrismDIDResolver.ts +++ b/src/castor/resolver/LongFormPrismDIDResolver.ts @@ -17,7 +17,6 @@ import { PublicKey, Curve, getUsage, - getUsageId, } from "../../domain/models"; import * as DIDParser from "../parser/DIDParser"; @@ -33,7 +32,7 @@ import { PrismDIDPublicKey } from "../did/prismDID/PrismDIDPublicKey"; export class LongFormPrismDIDResolver implements DIDResolver { method = "prism"; - constructor(private apollo: Apollo) { } + constructor(private apollo: Apollo) {} async resolve(didString: string): Promise { const did = DIDParser.parse(didString); @@ -119,9 +118,9 @@ export class LongFormPrismDIDResolver implements DIDResolver { } else { throw new Error("Unsupported key type") } - const usage = getUsage(key.usage) + const usage = getUsage(key.usage); return new PrismDIDPublicKey( - getUsageId(usage), + key.id, usage, pk, ) diff --git a/src/domain/models/keyManagement/Key.ts b/src/domain/models/keyManagement/Key.ts index ebaa8d030..f6be6eadc 100644 --- a/src/domain/models/keyManagement/Key.ts +++ b/src/domain/models/keyManagement/Key.ts @@ -48,9 +48,16 @@ export function getProtosUsage( } } - -export function getUsageId(index: Usage): string { - switch (index) { +/** + * create an identifier for keys within a DID Document + * should be unique within the Document + * + * @param keyUsage - maps to a prefix word + * @param index - occurrence of this keyUsage + * @returns {string} + */ +export function getUsageId(keyUsage: Usage, index = 0): string { + switch (keyUsage) { case Usage.MASTER_KEY: return `master${index}`; case Usage.ISSUING_KEY: @@ -123,7 +130,7 @@ export function curveToAlg(curve: string) { if (curve === Curve.ED25519 || curve === Curve.X25519) { return JWT_ALG.EdDSA; } - return JWT_ALG.unknown + return JWT_ALG.unknown; } export function getKeyCurveByNameAndIndex( name: string, @@ -158,7 +165,7 @@ export abstract class Key { get alg(): JWT_ALG { // eslint-disable-next-line @typescript-eslint/no-non-null-assertion const curve = this.getProperty(KeyProperties.curve)!; - return curveToAlg(curve) + return curveToAlg(curve); } isDerivable(): this is DerivableKey { diff --git a/src/pollux/utils/JWT.ts b/src/pollux/utils/JWT.ts index 8a3a08a8c..6b8fbfcf5 100644 --- a/src/pollux/utils/JWT.ts +++ b/src/pollux/utils/JWT.ts @@ -2,15 +2,15 @@ import * as didJWT from "did-jwt"; import { JWTCredential } from "../../pollux/models/JWTVerifiableCredential"; import { JWTCore } from "./jwt/JWTCore"; import { JWTInstanceType, JWTSignOptions, JWTVerifyOptions } from "./jwt/types"; -import { PublicKey } from "../../domain"; import { decodeJWS } from "./decodeJWS"; import { base64url } from "multiformats/bases/base64"; +import { isNil } from "../../utils"; export class JWT extends JWTCore { public type = JWTInstanceType.JWT; async decode(jws: string) { - return decodeJWS(jws) + return decodeJWS(jws); } async verify( @@ -30,22 +30,27 @@ export class JWT extends JWTCore { if (jwtObject.isCredential && holderDID && holderDID.toString() !== jwtObject.subject) { throw new Error("Invalid subject (holder)"); } - const { signature, data } = await this.decode(jws); - for (const verificationMethod of verificationMethods) { - const pk: PublicKey | undefined = this.getPKInstance(verificationMethod) - if (!pk) { - throw new Error("Invalid key verification method type found") + + const decoded = await this.decode(jws); + const verified = verificationMethods.some(vm => { + try { + const pk = this.getPKInstance(vm); + + if (isNil(pk) || !pk.canVerify()) { + throw new Error("Invalid key verification method type found"); + } + + const decodedSignature = base64url.baseDecode(decoded.signature); + const passed = pk.verify(Buffer.from(decoded.data), Buffer.from(decodedSignature)); + return passed; } - if (!pk.canVerify()) { - throw new Error("Invalid key verification method type found") + catch { + return false; } - const decodedSignature = base64url.baseDecode(signature) - return pk.verify( - Buffer.from(data), Buffer.from(decodedSignature) - ) - } - return false; - } catch (err) { + }); + + return verified; + } catch { return false; } } diff --git a/tests/castor/PrismDID.test.ts b/tests/castor/PrismDID.test.ts index 418226d44..5794832d4 100644 --- a/tests/castor/PrismDID.test.ts +++ b/tests/castor/PrismDID.test.ts @@ -1,7 +1,7 @@ import chai from "chai"; import chaiAsPromised from "chai-as-promised"; import { base58btc } from "multiformats/bases/base58"; -import { Curve, getProtosUsage, getUsageId, JWT_ALG, KeyTypes, PublicKey, Usage, VerificationMethods } from "../../src/domain"; +import { Authentication, Curve, DIDDocument, getProtosUsage, getUsageId, JWT_ALG, KeyTypes, PublicKey, Services, Usage, VerificationMethod, VerificationMethods } from "../../src/domain"; import Apollo from "../../src/apollo/Apollo"; import Castor from "../../src/castor/Castor"; import * as ECConfig from "../../src/domain/models/ECConfig"; @@ -9,6 +9,7 @@ import { Secp256k1PublicKey } from "../../src/apollo/utils/Secp256k1PublicKey"; import * as Fixtures from "../fixtures"; import * as Protos from "../../src/castor/protos/node_models"; import { PrismDIDPublicKey } from "../../src/castor/did/prismDID/PrismDIDPublicKey"; +import { ed25519, x25519 } from "../fixtures/keys"; chai.use(chaiAsPromised); const expect = chai.expect; @@ -16,20 +17,26 @@ const expect = chai.expect; const apollo = new Apollo(); const castor = new Castor(apollo); -describe("PRISMDID", () => { - describe("PrismDidPublicKey", () => { +describe("PrismDID", () => { + const secpDid = "did:prism:74f299ab7e5b2d127802b6a7093f5b9dd642e95aadf858cade6ec9af6929a487:CscBCsQBElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9hdXRoZW50aWNhdGlvbjAQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-iw"; + const secpMultibase = "zSXxpYB6edvxvWxRTo3kMUoTTQVHpbNnXo2Z1AjLA78iqLdK2kVo5xw9rGg8uoEgmhxYahNur3RvV7HnaktWBqkXt"; + const ed25519Did = "did:prism:30dc8276c5facc040dab037dd8c5c6d0e3218720a1e1c241c0341fcfea83cc60:Co0CCooCElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9hdXRoZW50aWNhdGlvbjAQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJECghpc3N1aW5nMBACSjYKB0VkMjU1MTkSK2RtNWYyR2RSNUJhSHBSeEI4YlRFbHZFXzBnSUMycDQwNE1zeDlzd0o5MTQ"; + const ed25519Multibase = "z8yJizaEga14wo4pHUDBXCnrp5A9WbFDuk5DZnPq5mCHK"; + const x25519Did = "did:prism:a5cce445f3e013fc2fa76914742b8b493b436251a29727ea57a4708b80fa3a68:CowCCokCElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9hdXRoZW50aWNhdGlvbjAQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJDCghpc3N1aW5nMBACSjUKBlgyNTUxORIrX1BqSGVmRmg5SDdxSDNWdDdNTzhWRU4tRjJQbFdjWHpkeHc2TFBreEVHRQ"; + const x25519Multibase = "zJ2VmASEaRF41F4BQSydGNi7zd5ud5YhqXxTKicPGd5FN"; + describe("PrismDidPublicKey", () => { it("Should create getProtosUsageCorrectly", () => { - expect(getProtosUsage("any" as any)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.UNKNOWN_KEY) - expect(getProtosUsage(Usage.MASTER_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.MASTER_KEY) - expect(getProtosUsage(Usage.ISSUING_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.ISSUING_KEY) - expect(getProtosUsage(Usage.KEY_AGREEMENT_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.KEY_AGREEMENT_KEY) - expect(getProtosUsage(Usage.AUTHENTICATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.AUTHENTICATION_KEY) - expect(getProtosUsage(Usage.REVOCATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.REVOCATION_KEY) - expect(getProtosUsage(Usage.CAPABILITY_INVOCATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.CAPABILITY_INVOCATION_KEY) - expect(getProtosUsage(Usage.CAPABILITY_DELEGATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.CAPABILITY_DELEGATION_KEY) - expect(getProtosUsage(Usage.UNKNOWN_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.UNKNOWN_KEY) - }) + expect(getProtosUsage("any" as any)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.UNKNOWN_KEY); + expect(getProtosUsage(Usage.MASTER_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.MASTER_KEY); + expect(getProtosUsage(Usage.ISSUING_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.ISSUING_KEY); + expect(getProtosUsage(Usage.KEY_AGREEMENT_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.KEY_AGREEMENT_KEY); + expect(getProtosUsage(Usage.AUTHENTICATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.AUTHENTICATION_KEY); + expect(getProtosUsage(Usage.REVOCATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.REVOCATION_KEY); + expect(getProtosUsage(Usage.CAPABILITY_INVOCATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.CAPABILITY_INVOCATION_KEY); + expect(getProtosUsage(Usage.CAPABILITY_DELEGATION_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.CAPABILITY_DELEGATION_KEY); + expect(getProtosUsage(Usage.UNKNOWN_KEY)).to.eq(Protos.io.iohk.atala.prism.protos.KeyUsage.UNKNOWN_KEY); + }); it("Should create from and to valid key protos", () => { const unsupportedRaw = new Array(32).fill(1); @@ -43,9 +50,9 @@ describe("PRISMDID", () => { curve: unsupportedCurve, alg: JWT_ALG.unknown, getEncoded() { - return unsupportedRaw + return unsupportedRaw; } - } as any + } as any; const keys = [ Fixtures.Keys.secp256K1.publicKey, Fixtures.Keys.ed25519.publicKey, @@ -57,64 +64,196 @@ describe("PRISMDID", () => { Usage.MASTER_KEY, key ); - const masterPkProto = masterPk.toProto() - const recoveredPk = PrismDIDPublicKey.fromProto(apollo, masterPkProto) - expect(masterPk.keyData.raw).to.deep.eq(recoveredPk.keyData.raw) - expect(masterPk.usage).to.eq(recoveredPk.usage) - expect(masterPk.id).to.eq(recoveredPk.id) - }) + const masterPkProto = masterPk.toProto(); + const recoveredPk = PrismDIDPublicKey.fromProto(apollo, masterPkProto); + expect(masterPk.keyData.raw).to.deep.eq(recoveredPk.keyData.raw); + expect(masterPk.usage).to.eq(recoveredPk.usage); + expect(masterPk.id).to.eq(recoveredPk.id); + }); const masterPk = new PrismDIDPublicKey( getUsageId(Usage.MASTER_KEY), Usage.MASTER_KEY, otherTypePK ); - const masterPkProto = masterPk.toProto() + const masterPkProto = masterPk.toProto(); + + expect(() => PrismDIDPublicKey.fromProto(apollo, masterPkProto)).to.throw(`Invalid key curve: ${unsupportedCurve}. Valid options are: X25519,Ed25519,Secp256k1`); - expect(() => PrismDIDPublicKey.fromProto(apollo, masterPkProto)).to.throw(`Invalid key curve: ${unsupportedCurve}. Valid options are: X25519,Ed25519,Secp256k1`) + }); + }); - }) - }) describe("createPrismDID", () => { it("Should create a prismDID from a PublicKey (SECP256K1)", async () => { const result = await castor.createPrismDID(Fixtures.Keys.secp256K1.publicKey, []); - await castor.resolveDID(result.toString()) expect(result).not.to.be.null; - expect(result.toString()).to.equal(Fixtures.Keys.expectedDIDSecp256K1); + expect(result.toString()).to.equal(secpDid); }); it("Should create a prismDID from a KeyPair (SECP256K1)", async () => { const result = await castor.createPrismDID(Fixtures.Keys.secp256K1, []); - await castor.resolveDID(result.toString()) expect(result).not.to.be.null; - expect(result.toString()).to.equal(Fixtures.Keys.expectedDIDSecp256K1); + expect(result.toString()).to.equal(secpDid); }); it("Should create a prismDID from a KeyPair (Ed25519)", async () => { const result = await castor.createPrismDID(Fixtures.Keys.secp256K1, [], [Fixtures.Keys.ed25519]); - await castor.resolveDID(result.toString()) expect(result).not.to.be.null; - expect(result.toString()).to.equal(Fixtures.Keys.expectedDIDEd25519); + expect(result.toString()).to.equal(ed25519Did); }); it("Should create a prismDID from a KeyPair (X25519)", async () => { const result = await castor.createPrismDID(Fixtures.Keys.secp256K1, [], [Fixtures.Keys.x25519]); - await castor.resolveDID(result.toString()) - expect(result.toString()).to.equal(Fixtures.Keys.expectedDIDX25519); + expect(result.toString()).to.equal(x25519Did); }); }); describe("Integration Tests", () => { - it("Should correctly create a prismDID from an existing HexKey", async () => { + describe("DIDDocument", () => { + test("real example resolves correctly", async () => { + const didStr = "did:prism:73196107e806b084d44339c847a3ae8dd279562f23895583f62cc91a2ee5b8fe:CnsKeRI8CghtYXN0ZXItMBABSi4KCXNlY3AyNTZrMRIhArrplJNfQYxthryRU87XdODy-YWUh5mqrvIfAdoZFeJBEjkKBWtleS0wEAJKLgoJc2VjcDI1NmsxEiEC8rsFplfYvRLazdWWi3LNR1gaAQXb-adVhZacJT4ntwE"; + const sut = await castor.resolveDID(didStr); + + expect(sut).to.be.instanceOf(DIDDocument); + expect(sut.id.toString()).to.eq(didStr); + expect(sut.coreProperties).to.be.an("array").to.have.length(4); + + const cp0 = sut.coreProperties.at(0) as Authentication; + expect(cp0).to.be.instanceOf(Authentication); + expect(cp0.urls).to.include(`${didStr}#master-0`); + const cp0vm0 = cp0.verificationMethods.at(0); + expect(cp0vm0).to.be.instanceOf(VerificationMethod); + expect(cp0vm0?.controller).to.eq(didStr); + expect(cp0vm0?.id).to.eq(`${didStr}#master-0`); + expect(cp0vm0?.publicKeyJwk).to.be.undefined; + expect(cp0vm0?.publicKeyMultibase).to.eq("zRDBW15pMDuec8faAkL7oEjQhtk8S7FmFm1oE5DgPHpBLM3E5huETfpyS388WqXGsvddpmNkhA3bh3vcsASCZMTaM"); + expect(cp0vm0?.type).to.eq("Secp256k1"); + + const cp1 = sut.coreProperties.at(1) as Authentication; + expect(cp1).to.be.instanceOf(Authentication); + expect(cp1.urls).to.include(`${didStr}#key-0`); + const cp1vm0 = cp1.verificationMethods.at(0); + expect(cp1vm0).to.be.instanceOf(VerificationMethod); + expect(cp1vm0?.controller).to.eq(didStr); + expect(cp1vm0?.id).to.eq(`${didStr}#key-0`); + expect(cp1vm0?.publicKeyJwk).to.be.undefined; + expect(cp1vm0?.publicKeyMultibase).to.eq("zSKufitbSMxJ2R7o5ivGubwYHfRU1jQ6soSKzH4p7yDQP5vXmwjjnaSjXCDmtriay93hm7B4erFqxNzrSiwT9PFqZ"); + expect(cp1vm0?.type).to.eq("Secp256k1"); + + const cp2 = sut.coreProperties.at(2) as Services; + expect(cp2).to.be.instanceOf(Services); + expect(cp2.values).to.be.empty; + + // TODO why do we have duplicates of coreProperties 0 & 1 here? + const cp3 = sut.coreProperties.at(3) as VerificationMethods; + expect(cp3).to.be.instanceOf(VerificationMethods); + expect(cp3.values).to.have.length(2); + + const cp3v0 = cp3.values.at(0); + expect(cp3v0).to.be.instanceOf(VerificationMethod); + expect(cp3v0?.controller).to.eq(didStr); + expect(cp3v0?.id).to.eq(`${didStr}#master-0`); + expect(cp3v0?.publicKeyJwk).to.be.undefined; + expect(cp3v0?.publicKeyMultibase).to.eq("zRDBW15pMDuec8faAkL7oEjQhtk8S7FmFm1oE5DgPHpBLM3E5huETfpyS388WqXGsvddpmNkhA3bh3vcsASCZMTaM"); + expect(cp3v0?.type).to.eq("Secp256k1"); + + const cp3v1 = cp3.values.at(1); + expect(cp3v1).to.be.instanceOf(VerificationMethod); + expect(cp3v1?.controller).to.eq(didStr); + expect(cp3v1?.id).to.eq(`${didStr}#key-0`); + expect(cp3v1?.publicKeyJwk).to.be.undefined; + expect(cp3v1?.publicKeyMultibase).to.eq("zSKufitbSMxJ2R7o5ivGubwYHfRU1jQ6soSKzH4p7yDQP5vXmwjjnaSjXCDmtriay93hm7B4erFqxNzrSiwT9PFqZ"); + expect(cp3v1?.type).to.eq("Secp256k1"); + }); + const masterKeyId = getUsageId(Usage.MASTER_KEY); + const authKeyId = getUsageId(Usage.AUTHENTICATION_KEY); + + const testVerificationMethod = (sut: any, didStr: string, keyId: string, keyMultibase: string, curve: Curve) => { + expect(sut).to.be.instanceOf(VerificationMethod); + expect(sut?.controller).to.eq(didStr); + expect(sut?.id).to.eq(`${didStr}#${keyId}`); + expect(sut?.publicKeyJwk).to.be.undefined; + expect(sut?.publicKeyMultibase).to.eq(keyMultibase); + expect(sut?.type).to.eq(curve); + }; + + test("master key", async () => { + const prismDid = await castor.createPrismDID(Fixtures.Keys.secp256K1, [], []); + const sut = await castor.resolveDID(prismDid.toString()); + + expect(sut).not.to.be.null; + expect(sut.coreProperties).to.be.an("array").to.have.length(4); + + // master key correctly encoded > decoded + const masterProp = sut.coreProperties.at(0) as Authentication; + expect(masterProp).to.be.instanceOf(Authentication); + expect(masterProp.urls[0]).to.eq(`${secpDid}#${masterKeyId}`); + const mastervm0 = masterProp.verificationMethods.at(0); + testVerificationMethod(mastervm0, secpDid, masterKeyId, secpMultibase, Curve.SECP256K1); + + // authentication key correctly encoded > decoded + const authProp = sut.coreProperties.at(1) as Authentication; + expect(authProp).to.be.instanceOf(Authentication); + expect(authProp.urls[0]).to.eq(`${secpDid}#${authKeyId}`); + const authvm0 = authProp.verificationMethods.at(0); + testVerificationMethod(authvm0, secpDid, authKeyId, secpMultibase, Curve.SECP256K1); + + // no services given - so empty + const services = sut.coreProperties.at(2) as Services; + expect(services).to.be.instanceOf(Services); + expect(services.values).to.be.empty; + + // no issuing keys given - so only master and authentication keys duplicated + const verificationMethods = sut.coreProperties.at(3) as VerificationMethods; + expect(verificationMethods).to.be.instanceOf(VerificationMethods); + expect(verificationMethods.values).to.have.length(2); + + const vm0 = verificationMethods.values.at(0); + testVerificationMethod(vm0, secpDid, masterKeyId, secpMultibase, Curve.SECP256K1); + + const vm1 = verificationMethods.values.at(1); + testVerificationMethod(vm1, secpDid, authKeyId, secpMultibase, Curve.SECP256K1); + }); - const didExample = - "did:prism:733e594871d7700d35e6116011a08fc11e88ff9d366d8b5571ffc1aa18d249ea:Ct8BCtwBEnQKH2F1dGhlbnRpY2F0aW9uYXV0aGVudGljYXRpb25LZXkQBEJPCglzZWNwMjU2azESIDS5zeYUkLCSAJLI6aLXRTPRxstCLPUEI6TgBrAVCHkwGiDk-ffklrHIFW7pKkT8i-YksXi-XXi5h31czUMaVClcpxJkCg9tYXN0ZXJtYXN0ZXJLZXkQAUJPCglzZWNwMjU2azESIDS5zeYUkLCSAJLI6aLXRTPRxstCLPUEI6TgBrAVCHkwGiDk-ffklrHIFW7pKkT8i-YksXi-XXi5h31czUMaVClcpw"; - const resolvedDID = await castor.resolveDID(didExample); + test("issuing keys", async () => { + const expectedDid = "did:prism:44a6f5f04609666ce54770e5d9ab67801b41d8e66e4e1cfdf9a2e75ef5bda3e2:CtICCs8CElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9hdXRoZW50aWNhdGlvbjAQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJECghpc3N1aW5nMBACSjYKB0VkMjU1MTkSK2RtNWYyR2RSNUJhSHBSeEI4YlRFbHZFXzBnSUMycDQwNE1zeDlzd0o5MTQSQwoIaXNzdWluZzEQAko1CgZYMjU1MTkSK19QakhlZkZoOUg3cUgzVnQ3TU84VkVOLUYyUGxXY1h6ZHh3NkxQa3hFR0U"; + const prismDid = await castor.createPrismDID(Fixtures.Keys.secp256K1, [], [ed25519, x25519]); + const sut = await castor.resolveDID(prismDid.toString()); + + expect(sut.coreProperties).to.be.an("array").to.have.length(6); - const pubHex = - "0434b9cde61490b0920092c8e9a2d74533d1c6cb422cf50423a4e006b015087930e4f9f7e496b1c8156ee92a44fc8be624b178be5d78b9877d5ccd431a54295ca7"; + // index 2 & 3 should be issuing keys + // no services given - so empty + const services = sut.coreProperties.at(4) as Services; + expect(services).to.be.instanceOf(Services); + expect(services.values).to.be.empty; + + // 2 issuing keys given - 4 total + const verificationMethods = sut.coreProperties.at(5) as VerificationMethods; + expect(verificationMethods).to.be.instanceOf(VerificationMethods); + expect(verificationMethods.values).to.have.length(4); + + const vm0 = verificationMethods.values.at(0); + testVerificationMethod(vm0, expectedDid, masterKeyId, secpMultibase, Curve.SECP256K1); + + const vm1 = verificationMethods.values.at(1); + testVerificationMethod(vm1, expectedDid, authKeyId, secpMultibase, Curve.SECP256K1); + + const vm2 = verificationMethods.values.at(2); + testVerificationMethod(vm2, expectedDid, getUsageId(Usage.ISSUING_KEY, 0), ed25519Multibase, Curve.ED25519); + + const vm3 = verificationMethods.values.at(3); + testVerificationMethod(vm3, expectedDid, getUsageId(Usage.ISSUING_KEY, 1), x25519Multibase, Curve.X25519); + }); + }); + + it("Should correctly create a prismDID from an existing HexKey", async () => { + const didExample = "did:prism:03425669b4d84b21a323c60bb41601eb22906b1a6427be3126277420faa6d1f3:CscBCsQBElwKB21hc3RlcjAQAUJPCglzZWNwMjU2azESIDS5zeYUkLCSAJLI6aLXRTPRxstCLPUEI6TgBrAVCHkwGiDk-ffklrHIFW7pKkT8i-YksXi-XXi5h31czUMaVClcpxJkCg9hdXRoZW50aWNhdGlvbjAQBEJPCglzZWNwMjU2azESIDS5zeYUkLCSAJLI6aLXRTPRxstCLPUEI6TgBrAVCHkwGiDk-ffklrHIFW7pKkT8i-YksXi-XXi5h31czUMaVClcpw"; + const resolvedDID = await castor.resolveDID(didExample); + + const pubHex = "0434b9cde61490b0920092c8e9a2d74533d1c6cb422cf50423a4e006b015087930e4f9f7e496b1c8156ee92a44fc8be624b178be5d78b9877d5ccd431a54295ca7"; const masterPublicKey = new Secp256k1PublicKey(Buffer.from(pubHex, "hex")); const createdDID = await castor.createPrismDID(masterPublicKey, []); @@ -137,7 +276,6 @@ describe("PRISMDID", () => { }); it("Create a PrismDID and verify a signature", async () => { - const privateKey = apollo.createPrivateKey({ type: KeyTypes.EC, curve: Curve.SECP256K1, diff --git a/tests/fixtures/keys.ts b/tests/fixtures/keys.ts index 465dd2b59..247b01fa5 100644 --- a/tests/fixtures/keys.ts +++ b/tests/fixtures/keys.ts @@ -5,15 +5,6 @@ import { Secp256k1PrivateKey } from "../../src/apollo/utils/Secp256k1PrivateKey" import { X25519KeyPair } from "../../src/apollo/utils/X25519KeyPair"; import { X25519PrivateKey } from "../../src/apollo/utils/X25519PrivateKey"; -export const expectedDIDSecp256K1 = - "did:prism:da61cf65fbf04b6b9fe06fa3b577fca3e05895a13902decaad419845a20d2d78:Ct8BCtwBEnQKH2F1dGhlbnRpY2F0aW9uYXV0aGVudGljYXRpb25LZXkQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9tYXN0ZXJtYXN0ZXJLZXkQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-iw"; - -export const expectedDIDX25519 = - "did:prism:ba32baedae5964a54603f05a10ecb0939131e3160b2e92ec9eda22d202bf2ad4:Cq0CCqoCEkwKEWlzc3Vpbmdpc3N1aW5nS2V5EAJKNQoGWDI1NTE5EitfUGpIZWZGaDlIN3FIM1Z0N01POFZFTi1GMlBsV2NYemR4dzZMUGt4RUdFEnQKH2F1dGhlbnRpY2F0aW9uYXV0aGVudGljYXRpb25LZXkQBEJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-ixJkCg9tYXN0ZXJtYXN0ZXJLZXkQAUJPCglzZWNwMjU2azESIP0gMhTAVOk7SgWRluzmeJIjtm2-YMc6AbrD3ePKJQj-GiDZlsa5pQuXGzKvgK10D8SzuDvh79u5oMB7-ZeJNAh-iw"; - -export const expectedDIDEd25519 = - "did:prism:969fdc2ae434b4db3e6dd8df42231c719610bd10df93fc6139a6f9d3144e15fd:Cq4CCqsCEk0KEWlzc3Vpbmdpc3N1aW5nS2V5EAJKNgoHRWQyNTUxORIrZG01ZjJHZFI1QmFIcFJ4QjhiVEVsdkVfMGdJQzJwNDA0TXN4OXN3SjkxNBJ0Ch9hdXRoZW50aWNhdGlvbmF1dGhlbnRpY2F0aW9uS2V5EARCTwoJc2VjcDI1NmsxEiD9IDIUwFTpO0oFkZbs5niSI7ZtvmDHOgG6w93jyiUI_hog2ZbGuaULlxsyr4CtdA_Es7g74e_buaDAe_mXiTQIfosSZAoPbWFzdGVybWFzdGVyS2V5EAFCTwoJc2VjcDI1NmsxEiD9IDIUwFTpO0oFkZbs5niSI7ZtvmDHOgG6w93jyiUI_hog2ZbGuaULlxsyr4CtdA_Es7g74e_buaDAe_mXiTQIfos" - const secpPrivateKey = new Secp256k1PrivateKey( new Uint8Array([ 45, 182, 188, 189, 107, 229, 136, 180, 199, 177, 110, 84, 98, 140, 121, 84,