From 3de2117e95576556dfbc30984b621a0a8e1ca60a Mon Sep 17 00:00:00 2001 From: FabioPinheiro Date: Thu, 4 Jan 2024 18:25:10 +0000 Subject: [PATCH] Fix DID Peer's key id for #162 --- .../scala/fmgp/did/method/peer/DIDPeer.scala | 43 ++++++++++++------- .../did/method/peer/DIDPeerExamples.scala | 19 +++++--- .../fmgp/did/method/peer/DIDPeerSuite.scala | 30 +++++++++++++ 3 files changed, 71 insertions(+), 21 deletions(-) diff --git a/did-method-peer/shared/src/main/scala/fmgp/did/method/peer/DIDPeer.scala b/did-method-peer/shared/src/main/scala/fmgp/did/method/peer/DIDPeer.scala index 51579d9e..c616c0b4 100644 --- a/did-method-peer/shared/src/main/scala/fmgp/did/method/peer/DIDPeer.scala +++ b/did-method-peer/shared/src/main/scala/fmgp/did/method/peer/DIDPeer.scala @@ -36,28 +36,29 @@ case class DIDPeer2(elements: Seq[DIDPeer2.Element]) extends DIDPeer { def numalgo: 2 = 2 def specificId: String = "" + numalgo + elements.map(_.encode).mkString(".", ".", "") + def indexedElements = elements.zipWithIndex + override def document: DIDDocument = DIDDocumentClass( id = this, authentication = Some( - elements - .collect { case e: DIDPeer2.ElementV => - VerificationMethodEmbeddedJWK( - id = this.string + "#" + e.mb.value.drop(1), - controller = this.did, - `type` = "JsonWebKey2020", - publicKeyJwk = OKPPublicKey( - kty = KTY.OKP, - crv = Curve.Ed25519, // TODO PLZ FIX BLACKMAGIC of did:peer! (zero documentation & unexpected logic) - x = DIDPeer.decodeKey(e.mb.value), - kid = None - ) + indexedElements.collect { case (e: DIDPeer2.ElementV, index: Int) => + VerificationMethodEmbeddedJWK( + id = this.string + "#key-" + (index + 1), + controller = this.did, + `type` = "JsonWebKey2020", + publicKeyJwk = OKPPublicKey( + kty = KTY.OKP, + crv = Curve.Ed25519, // TODO PLZ FIX BLACKMAGIC of did:peer! (zero documentation & unexpected logic) + x = DIDPeer.decodeKey(e.mb.value), + kid = None ) - } + ) + } ), keyAgreement = Some( - elements.collect { case e: DIDPeer2.ElementE => + indexedElements.collect { case (e: DIDPeer2.ElementE, index: Int) => VerificationMethodEmbeddedJWK( - id = this.string + "#" + e.mb.value.drop(1), + id = this.string + "#key-" + (index + 1), controller = this.did, `type` = "JsonWebKey2020", publicKeyJwk = OKPPublicKey( @@ -117,7 +118,15 @@ object DIDPeer2 { def makeAgent(keySeq: Seq[PrivateKey], service: Seq[DIDPeerServiceEncoded] = Seq.empty): DIDPeer.AgentDIDPeer = new DIDPeer.AgentDIDPeer { override val id: DIDPeer2 = apply(keySeq, service) - override val keyStore: KeyStore = KeyStore(keySeq.map(k => keyKidAbsolute(k, id)).toSet) + override val keyStore: KeyStore = KeyStore( + keySeq.zipWithIndex.map { case (key, index) => + key match + case k @ OKPPrivateKey(kty, crv, d, x, Some(kid)) => k + case k @ OKPPrivateKey(kty, crv, d, x, None) => k.copy(kid = Some(id.did + "#key-" + (index + 1))) + case k @ ECPrivateKey(kty, crv, d, x, y, Some(kid)) => k + case k @ ECPrivateKey(kty, crv, d, x, y, None) => k.copy(kid = Some(id.did + "#key-" + (index + 1))) + }.toSet + ) } def keyToElement(key: PrivateKey) = key match { @@ -139,6 +148,8 @@ object DIDPeer2 { case ECPrivateKey(kty, crv, d, x, y, kid) => ??? // TODO } + /** This is the old (undefined) format of kid based on the key's encoded */ + @deprecated("The new format of the kid is based on index") def keyKidAbsolute(key: PrivateKey, did: DIDPeer) = key match case k @ OKPPrivateKey(kty, crv, d, x, kid) => k.copy(kid = Some(did.did + "#" + keyToElement(k).encode.drop(2))) // FIXME .drop(2) 'Sz' diff --git a/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerExamples.scala b/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerExamples.scala index c1306b3b..1df0bd96 100644 --- a/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerExamples.scala +++ b/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerExamples.scala @@ -79,11 +79,16 @@ object DIDPeerExamples { val myExampleDID = "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19" - val myExampleDIDDocument = """{ + + /** After https://github.com/FabioPinheiro/scala-did/issues/162: + * - The id #6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE is now '#key-2' + * - The id #6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY is now '#key-1' + */ + val myExampleDIDDocument = s"""{ "id": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19", "authentication": [ { - "id": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY", + "id": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#key-2", "controller": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19", "type": "JsonWebKey2020", "publicKeyJwk": { @@ -95,7 +100,7 @@ object DIDPeerExamples { ], "keyAgreement": [ { - "id": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE", + "id": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19#key-1", "controller": "did:peer:2.Ez6LSj4X4MjeLXLi6Bnd8gp4crnUD7fBtVFH1xpUmtit5MJNE.Vz6MkwU5tsPanWKYgdEMd1oPghvAoQn41dccHUqkhxJUNdAAY.SeyJ0IjoiZG0iLCJzIjoiaHR0cDovL2xvY2FsaG9zdDo4MDgwL3Rlc3QiLCJyIjpbXSwiYSI6WyJkaWRjb21tL3YyIl19", "type": "JsonWebKey2020", "publicKeyJwk": { @@ -122,11 +127,15 @@ object DIDPeerExamples { def rootsid_ex_peer2_did = "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0" + /** After https://github.com/FabioPinheiro/scala-did/issues/162: + * - The id #6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE is now '#key-2' + * - The id #6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ is now '#key-1' + */ val rootsid_ex_peer2_didDocument = """{ | "id" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0", | "authentication" : [ | { - | "id" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0#6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE", + | "id" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0#key-2", | "controller" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0", | "type" : "JsonWebKey2020", | "publicKeyJwk" : { @@ -138,7 +147,7 @@ object DIDPeerExamples { | ], | "keyAgreement" : [ | { - | "id" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0#6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ", + | "id" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0#key-1", | "controller" : "did:peer:2.Ez6LSms555YhFthn1WV8ciDBpZm86hK9tp83WojJUmxPGk1hZ.Vz6MkmdBjMyB4TS5UbbQw54szm8yvMMf1ftGV2sQVYAxaeWhE.SeyJpZCI6Im5ldy1pZCIsInQiOiJkbSIsInMiOiJodHRwczovL21lZGlhdG9yLnJvb3RzaWQuY2xvdWQiLCJhIjpbImRpZGNvbW0vdjIiXX0", | "type" : "JsonWebKey2020", | "publicKeyJwk" : { diff --git a/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerSuite.scala b/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerSuite.scala index e3abfcee..8dec730f 100644 --- a/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerSuite.scala +++ b/did-method-peer/shared/src/test/scala/fmgp/did/method/peer/DIDPeerSuite.scala @@ -12,6 +12,7 @@ import DIDPeerExamples._ import fmgp.did.method.peer.DidPeerResolver import zio.json.ast.Json import fmgp.util.Base64 +import scala.util.matching.Regex /** didResolverPeerJVM/testOnly fmgp.did.method.peer.DIDPeerSuite */ class DIDPeerSuite extends ZSuite { @@ -267,4 +268,33 @@ class DIDPeerSuite extends ZSuite { assertEquals(service.size, 1) assertEquals(service.toJson, ex2Services) } + + test("method makeAgent must fill the 'kid' based on index") { + val alice = DIDPeer2.makeAgent( + Seq( + OKPPrivateKey( // keyAgreement + kty = KTY.OKP, + crv = Curve.X25519, + d = "Z6D8LduZgZ6LnrOHPrMTS6uU2u5Btsrk1SGs4fn8M7c", + x = "Sr4SkIskjN_VdKTn0zkjYbhGTWArdUNE4j_DmUpnQGw", + kid = None + ), + OKPPrivateKey( // keyAuthentication + kty = KTY.OKP, + crv = Curve.Ed25519, + d = "INXCnxFEl0atLIIQYruHzGd5sUivMRyQOzu87qVerug", + x = "MBjnXZxkMcoQVVL21hahWAw43RuAG-i64ipbeKKqwoA", + kid = None + ) + ) + ) + + val kidPattern: Regex = "^.*#key-[0-9]+$".r + + alice.keyStore.keys.foreach { key => + key.kid match + case None => fail("kid must be define") + case Some(kid) => assert(kidPattern.matches(kid), "'kid' must follow the kid pattern") + } + } }