Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implementing BearerDid / PortableDid, exposing our own JWS, JWT, JWK types #262

Merged
merged 50 commits into from
Mar 27, 2024
Merged
Show file tree
Hide file tree
Changes from 8 commits
Commits
Show all changes
50 commits
Select commit Hold shift + click to select a range
9eca633
create bearerdid and portable did, keyexporter and keyimporter
jiyoontbd Mar 7, 2024
12cb845
changing Did to ChangemeDid to make it easier to spot and replace w B…
jiyoontbd Mar 7, 2024
28e683a
removing didmethod interface
jiyoontbd Mar 7, 2024
0106920
trying on BearerDID for did jwk
jiyoontbd Mar 7, 2024
9c5fbbf
removing create options
jiyoontbd Mar 9, 2024
f348127
getting did:dht and did:key to return BearerDID. next step: figure ou…
jiyoontbd Mar 9, 2024
cb309ee
moved bearerdid import() to companion object so i can call bearerdid.…
jiyoontbd Mar 10, 2024
1f32e5a
updated codeowners doc
jiyoontbd Mar 10, 2024
a3ac9b5
changing type names to be true CamelCase. implemented Jws methods and…
jiyoontbd Mar 11, 2024
90fa517
wrote jwt class. modify jws class to include builder for jwsheader
jiyoontbd Mar 11, 2024
584a36b
sub out JwtUtils.sign() for Jwt.sign()
jiyoontbd Mar 11, 2024
a9e1494
implemented jwt. removed jwtutil class
jiyoontbd Mar 12, 2024
d387f2c
using hand rolled JwsHeader and JwtClaimsSet instead of nimbusds
jiyoontbd Mar 12, 2024
b681b7c
adding jwk impl, in progress
jiyoontbd Mar 12, 2024
33852b9
adding jose package. fixing some todos
jiyoontbd Mar 14, 2024
9a77e89
goodbye, changemedid
jiyoontbd Mar 14, 2024
9945824
updating tests to test import instead of load
jiyoontbd Mar 14, 2024
f522197
wrote kt docs, fixed didresolvers, removed resolvedidoptions because …
jiyoontbd Mar 14, 2024
e92dbd1
removing unnecessary tests
jiyoontbd Mar 14, 2024
275d6f8
fixing tests
jiyoontbd Mar 15, 2024
e2a3cb7
requiring keytype and curve when building Jwk
jiyoontbd Mar 18, 2024
f0fbc80
adding default keyUse of sig to Jwk when computing public key.
jiyoontbd Mar 18, 2024
46e3d5e
fixing some tests
jiyoontbd Mar 18, 2024
f4c418c
adding '$.misc.vc....' path to all test vector jsons
jiyoontbd Mar 19, 2024
a3e7688
fixing test vectors for credentials package tests
jiyoontbd Mar 19, 2024
81c470d
reverting back adding $.misc.vc... for valid paths, instead using jwt…
jiyoontbd Mar 19, 2024
fca2488
removing todos, removing extraneous Convert() call in Jwt.decode()
jiyoontbd Mar 20, 2024
f56860c
removing jwk serializer/deserializer
jiyoontbd Mar 21, 2024
129483b
tests pass. writing tests for jwt in process. jwk still needs tests w…
jiyoontbd Mar 21, 2024
68d9c6b
Merge branch 'main' of github.com:TBD54566975/web5-kt into 234-did-impl
jiyoontbd Mar 21, 2024
0b7dc33
fixing tests
jiyoontbd Mar 21, 2024
149ed9a
new web5-spec sha
jiyoontbd Mar 21, 2024
711f9d7
adding more jwttest
jiyoontbd Mar 22, 2024
2f3c98d
added jwktest
jiyoontbd Mar 22, 2024
c5d3b91
fixing detekt error
jiyoontbd Mar 22, 2024
c1ea33d
adding bearerdid tests
jiyoontbd Mar 22, 2024
eb52fd3
removing portabledidtest class because there is no method inside port…
jiyoontbd Mar 22, 2024
5cdfec3
fixing detekt error
jiyoontbd Mar 22, 2024
3937376
moving nimbusds lib to implementation instead of api
jiyoontbd Mar 22, 2024
cda57a0
adding wording around not supporting did web creation
jiyoontbd Mar 23, 2024
58db293
adding uri as a property to BearerDid as it was missed. addressing co…
jiyoontbd Mar 24, 2024
55e652c
Merge branch 'main' of github.com:TBD54566975/web5-kt into 234-did-impl
jiyoontbd Mar 24, 2024
5ab978d
changing how portabledid is constructed in vc test vector test
jiyoontbd Mar 27, 2024
030678d
addressing review comments
jiyoontbd Mar 27, 2024
6004707
Update dids/src/test/kotlin/web5/sdk/dids/methods/jwk/DidJwkTest.kt
jiyoontbd Mar 27, 2024
b472990
making error in testvectorinput to default to false, added portabledi…
jiyoontbd Mar 27, 2024
aa90cd4
Merge branch '234-did-impl' of github.com:TBD54566975/web5-kt into 23…
jiyoontbd Mar 27, 2024
c5bba53
adding comment about supporting keys that use ECC
jiyoontbd Mar 27, 2024
1d7e0a5
reverting back to include portabledid concept in vc create test vecto…
jiyoontbd Mar 27, 2024
870cc12
bumping web5-spec commit
jiyoontbd Mar 27, 2024
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
2 changes: 1 addition & 1 deletion CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@
# The format is described: https://github.blog/2017-07-06-introducing-code-owners/

# These owners will be the default owners for everything in the repo.
* @amika-sq @mistermoe @nitro-neal @tomdaffurn @phoebe-lew @diehuxx @kirahsapong @jiyoontbd @frankhinek
* @mistermoe @nitro-neal @tomdaffurn @phoebe-lew @diehuxx @kirahsapong @jiyoontbd @frankhinek

/crypto/src/main/kotlin/web5/sdk/crypto/AwsKeyManager.kt @tomdaffurn
# -----------------------------------------------
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.JWTParser
import com.nimbusds.jwt.SignedJWT
import web5.sdk.credentials.util.JwtUtil
import web5.sdk.dids.Did
import web5.sdk.dids.ChangemeDid
import java.net.URI
import java.security.SignatureException
import java.util.Date
Expand Down Expand Up @@ -53,7 +53,7 @@ public class VerifiableCredential internal constructor(public val vcDataModel: V
* If the [assertionMethodId] is null, the function will attempt to use the first available verification method from
* the [did]. The result is a String in a JWT format.
*
* @param did The [Did] used to sign the credential.
* @param did The [ChangemeDid] used to sign the credential.
* @param assertionMethodId An optional identifier for the assertion method that will be used for verification of the
* produced signature.
* @return The JWT representing the signed verifiable credential.
Expand All @@ -64,7 +64,7 @@ public class VerifiableCredential internal constructor(public val vcDataModel: V
* ```
*/
@JvmOverloads
public fun sign(did: Did, assertionMethodId: String? = null): String {
public fun sign(did: ChangemeDid, assertionMethodId: String? = null): String {
val payload = JWTClaimsSet.Builder()
.issuer(vcDataModel.issuer.toString())
.issueTime(vcDataModel.issuanceDate)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import com.nimbusds.jwt.JWTClaimsSet
import com.nimbusds.jwt.JWTParser
import com.nimbusds.jwt.SignedJWT
import web5.sdk.credentials.util.JwtUtil
import web5.sdk.dids.Did
import web5.sdk.dids.ChangemeDid
import java.net.URI
import java.security.SignatureException
import java.util.Date
Expand Down Expand Up @@ -47,7 +47,7 @@ public class VerifiablePresentation internal constructor(public val vpDataModel:
* If the [assertionMethodId] is null, the function will attempt to use the first available verification method from
* the [did]. The result is a String in a JWT format.
*
* @param did The [Did] used to sign the credential.
* @param did The [ChangemeDid] used to sign the credential.
* @param assertionMethodId An optional identifier for the assertion method that will be used for verification of the
* produced signature.
* @return The JWT representing the signed verifiable credential.
Expand All @@ -58,7 +58,7 @@ public class VerifiablePresentation internal constructor(public val vpDataModel:
* ```
*/
@JvmOverloads
public fun sign(did: Did, assertionMethodId: String? = null): String {
public fun sign(did: ChangemeDid, assertionMethodId: String? = null): String {
val payload = JWTClaimsSet.Builder()
.issuer(did.uri)
.issueTime(Date())
Expand Down
20 changes: 10 additions & 10 deletions credentials/src/main/kotlin/web5/sdk/credentials/util/JwtUtil.kt
Original file line number Diff line number Diff line change
Expand Up @@ -9,9 +9,9 @@ import com.nimbusds.jwt.JWTParser
import com.nimbusds.jwt.SignedJWT
import web5.sdk.common.Convert
import web5.sdk.crypto.Crypto
import web5.sdk.dids.Did
import web5.sdk.dids.ChangemeDid
import web5.sdk.dids.DidResolvers
import web5.sdk.dids.didcore.DidUri
import web5.sdk.dids.didcore.Did
import web5.sdk.dids.exceptions.DidResolutionException
import web5.sdk.dids.exceptions.PublicKeyJwkMissingException
import java.net.URI
Expand All @@ -31,18 +31,18 @@ public object JwtUtil {
* If the [assertionMethodId] is null, the function will attempt to use the first available verification method from
* the [did]. The result is a String in a JWT format.
*
* @param did The [Did] used to sign the credential.
* @param did The [ChangemeDid] used to sign the credential.
* @param assertionMethodId An optional identifier for the assertion method
* that will be used for verification of the produced signature.
* @param jwtPayload the payload that is getting signed by the [Did]
* @param jwtPayload the payload that is getting signed by the [ChangemeDid]
* @return The JWT representing the signed verifiable credential.
*
* Example:
* ```
* val signedVc = verifiableCredential.sign(myDid)
* ```
*/
public fun sign(did: Did, assertionMethodId: String?, jwtPayload: JWTClaimsSet): String {
public fun sign(did: ChangemeDid, assertionMethodId: String?, jwtPayload: JWTClaimsSet): String {
val didResolutionResult = DidResolvers.resolve(did.uri)
val didDocument = didResolutionResult.didDocument
if (didResolutionResult.didResolutionMetadata.error != null || didDocument == null) {
Expand Down Expand Up @@ -108,13 +108,13 @@ public object JwtUtil {
}

val verificationMethodId = jwt.header.keyID
val didUri = DidUri.Parser.parse(verificationMethodId)
val did = Did.Parser.parse(verificationMethodId)

val didResolutionResult = DidResolvers.resolve(didUri.url)
val didResolutionResult = DidResolvers.resolve(did.url)
if (didResolutionResult.didResolutionMetadata.error != null) {
throw SignatureException(
"Signature verification failed: " +
"Failed to resolve DID ${didUri.url}. " +
"Failed to resolve DID ${did.url}. " +
"Error: ${didResolutionResult.didResolutionMetadata.error}"
)
}
Expand All @@ -123,8 +123,8 @@ public object JwtUtil {
// or just `#fragment`. See: https://www.w3.org/TR/did-core/#relative-did-urls.
// using a set for fast string comparison. DIDs can be lonnng.
val verificationMethodIds = setOf(
didUri.url,
"#${didUri.fragment}"
did.url,
"#${did.fragment}"
)

didResolutionResult.didDocument?.assertionMethod?.firstOrNull {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,7 @@ import org.junit.jupiter.api.assertDoesNotThrow
import web5.sdk.crypto.AlgorithmId
import web5.sdk.crypto.AwsKeyManager
import web5.sdk.crypto.InMemoryKeyManager
import web5.sdk.dids.Did
import web5.sdk.dids.ChangemeDid
import web5.sdk.dids.didcore.Purpose
import web5.sdk.dids.extensions.load
import web5.sdk.dids.methods.dht.CreateDidDhtOptions
Expand Down Expand Up @@ -252,8 +252,8 @@ class Web5TestVectorsCredentials {

val keyManager = InMemoryKeyManager()
keyManager.import(listOf(vector.input.signerPrivateJwk!!))
val issuerDid = Did.load(vector.input.signerDidUri!!, keyManager)
val vcJwt = vc.sign(issuerDid)
val issuerChangemeDid = ChangemeDid.load(vector.input.signerDidUri!!, keyManager)
val vcJwt = vc.sign(issuerChangemeDid)

assertEquals(vector.output, vcJwt, vector.description)
}
Expand Down
8 changes: 8 additions & 0 deletions crypto/src/main/kotlin/web5/sdk/crypto/KeyManager.kt
Original file line number Diff line number Diff line change
Expand Up @@ -59,4 +59,12 @@ public interface KeyManager {
* @throws IllegalArgumentException if the key is not known to the [KeyManager]
*/
public fun getDeterministicAlias(publicKey: JWK): String
}

public interface KeyExporter {
public fun exportKey(keyId: String): JWK
}

public interface KeyImporter {
public fun importKey(jwk: JWK): String
}
201 changes: 101 additions & 100 deletions dids/src/main/kotlin/web5/sdk/dids/DidMethod.kt
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package web5.sdk.dids

import web5.sdk.crypto.KeyManager
import web5.sdk.dids.didcore.DidUri
import web5.sdk.dids.didcore.Did
import web5.sdk.dids.exceptions.PublicKeyJwkMissingException

/**
Expand All @@ -26,7 +26,7 @@ import web5.sdk.dids.exceptions.PublicKeyJwkMissingException
* Implementers should adhere to the respective DID method specifications ensuring both compliance
* and interoperability across different DID networks.
*/
public abstract class Did(public val uri: String, public val keyManager: KeyManager) {
public abstract class ChangemeDid(public val uri: String, public val keyManager: KeyManager) {
public companion object {
// static helper methods here
}
Expand Down Expand Up @@ -84,101 +84,102 @@ public interface CreationMetadata
*/
public interface ResolveDidOptions

/**
* An interface defining operations for DID methods in accordance with the W3C DID standard.
*
* A DID method is a specific set of rules for creating, updating, and revoking DIDs,
* specified in a DID method specification. Different DID methods utilize different
* consensus mechanisms, cryptographic algorithms, and registries (or none at all).
* The purpose of `DidMethod` implementations is to provide logic tailored to a
* particular method while adhering to the broader operations outlined in the W3C DID standard.
*
* Implementations of this interface should provide method-specific logic for
* creating and resolving DIDs under a particular method.
*
* @param T The type of DID that this method can create and resolve, extending [Did].
*
* ### Example of a Custom DID Method Implementation:
* ```
* class ExampleDidMethod : DidMethod<ExampleDid, ExampleCreateDidOptions> {
* override val methodName: String = "example"
*
* override fun create(keyManager: KeyManager, options: ExampleCreateDidOptions?): ExampleDid {
* // Implementation-specific logic for creating DIDs.
* }
*
* override fun resolve(didUrl: String, opts: ResolveDidOpts?): DidResolutionResult {
* // Implementation-specific logic for resolving DIDs.
* }
* }
* ```
*
* ### Notes:
* - Ensure conformance with the relevant DID method specification for accurate and
* interoperable functionality.
* - Ensure that cryptographic operations utilize secure and tested libraries, ensuring
* the reliability and security of DIDs managed by this method.
*/
public interface DidMethod<T : Did, O : CreateDidOptions> {
/**
* A string that specifies the name of the DID method.
*
* For instance, in the DID `did:example:123456`, "example" would be the method name.
*/
public val methodName: String

/**
* Creates a new DID.
*
* This function should generate a new DID according to the rules of the specific
* method being implemented, using the provided [KeyManager] and optionally considering
* any provided [CreateDidOptions].
*
* @param keyManager An instance of [KeyManager] responsible for cryptographic operations.
* @param options Optionally, an instance of [CreateDidOptions] providing additional options
* or requirements for DID creation.
* @return A new instance of type [T], representing the created DID.
*/
public fun create(keyManager: KeyManager, options: O? = null): T

/**
* Resolves a DID to its associated DID Document.
*
* This function should retrieve and return the DID Document associated with the provided
* DID URI, in accordance with the rules and mechanisms of the specific DID method being
* implemented, and optionally considering any provided [ResolveDidOptions].
*
* @param did A string containing the DID URI to be resolved.
* @param options Optionally, an instance of [ResolveDidOptions] providing additional options
* or requirements for DID resolution.
* @return An instance of [DidResolutionResult] containing the resolved DID Document and
* any associated metadata.
*/
public fun resolve(did: String, options: ResolveDidOptions? = null): DidResolutionResult

/**
* Returns an instance of [T] for [uri]. This function validates that all the key material needed for signing and
* managing the passed in [uri] exists within the provided [keyManager].
*
* @param uri A string containing the DID URI to load.
* @param keyManager An instance of [KeyManager] that should contain all the key material needed for signing and
* managing the passed in [did].
* @return An instance of [T] representing the loaded DID.
*/
public fun load(uri: String, keyManager: KeyManager): T
}


internal fun <T : Did, O : CreateDidOptions> DidMethod<T, O>.validateKeyMaterialInsideKeyManager(
did: String, keyManager: KeyManager) {
require(DidUri.parse(did).method == methodName) {
"did must start with the prefix \"did:$methodName\", but got $did"
}
val didResolutionResult = resolve(did)

didResolutionResult.didDocument!!.verificationMethod?.forEach {
val publicKeyJwk = it.publicKeyJwk ?: throw PublicKeyJwkMissingException("publicKeyJwk is null")
val keyAlias = keyManager.getDeterministicAlias(publicKeyJwk)
keyManager.getPublicKey(keyAlias)
}
}
///**
// * An interface defining operations for DID methods in accordance with the W3C DID standard.
// *
// * A DID method is a specific set of rules for creating, updating, and revoking DIDs,
// * specified in a DID method specification. Different DID methods utilize different
// * consensus mechanisms, cryptographic algorithms, and registries (or none at all).
// * The purpose of `DidMethod` implementations is to provide logic tailored to a
// * particular method while adhering to the broader operations outlined in the W3C DID standard.
// *
// * Implementations of this interface should provide method-specific logic for
// * creating and resolving DIDs under a particular method.
// *
// * @param T The type of DID that this method can create and resolve, extending [ChangemeDid].
// *
// * ### Example of a Custom DID Method Implementation:
// * ```
// * class ExampleDidMethod : DidMethod<ExampleDid, ExampleCreateDidOptions> {
// * override val methodName: String = "example"
// *
// * override fun create(keyManager: KeyManager, options: ExampleCreateDidOptions?): ExampleDid {
// * // Implementation-specific logic for creating DIDs.
// * }
// *
// * override fun resolve(didUrl: String, opts: ResolveDidOpts?): DidResolutionResult {
// * // Implementation-specific logic for resolving DIDs.
// * }
// * }
// * ```
// *
// * ### Notes:
// * - Ensure conformance with the relevant DID method specification for accurate and
// * interoperable functionality.
// * - Ensure that cryptographic operations utilize secure and tested libraries, ensuring
// * the reliability and security of DIDs managed by this method.
// */
//public interface DidMethod<T : ChangemeDid, O : CreateDidOptions> {
// /**
// * A string that specifies the name of the DID method.
// *
// * For instance, in the DID `did:example:123456`, "example" would be the method name.
// */
// public val methodName: String
//
// /**
// * Creates a new DID.
// *
// * This function should generate a new DID according to the rules of the specific
// * method being implemented, using the provided [KeyManager] and optionally considering
// * any provided [CreateDidOptions].
// *
// * @param keyManager An instance of [KeyManager] responsible for cryptographic operations.
// * @param options Optionally, an instance of [CreateDidOptions] providing additional options
// * or requirements for DID creation.
// * @return A new instance of type [T], representing the created DID.
// */
// public fun create(keyManager: KeyManager, options: O? = null): T
//
// /**
// * Resolves a DID to its associated DID Document.
// *
// * This function should retrieve and return the DID Document associated with the provided
// * DID URI, in accordance with the rules and mechanisms of the specific DID method being
// * implemented, and optionally considering any provided [ResolveDidOptions].
// *
// * @param did A string containing the DID URI to be resolved.
// * @param options Optionally, an instance of [ResolveDidOptions] providing additional options
// * or requirements for DID resolution.
// * @return An instance of [DidResolutionResult] containing the resolved DID Document and
// * any associated metadata.
// */
// public fun resolve(did: String, options: ResolveDidOptions? = null): DidResolutionResult
//
// /**
// * Returns an instance of [T] for [uri]. This function validates that all the key material needed for signing and
// * managing the passed in [uri] exists within the provided [keyManager].
// *
// * @param uri A string containing the DID URI to load.
// * @param keyManager An instance of [KeyManager] that should contain all the key material needed for signing and
// * managing the passed in [did].
// * @return An instance of [T] representing the loaded DID.
// */
// public fun load(uri: String, keyManager: KeyManager): T
//}
//
//
//// todo do i just remove this?
//internal fun <T : ChangemeDid, O : CreateDidOptions> DidMethod<T, O>.validateKeyMaterialInsideKeyManager(
// did: String, keyManager: KeyManager) {
// require(Did.parse(did).method == methodName) {
// "did must start with the prefix \"did:$methodName\", but got $did"
// }
// val didResolutionResult = resolve(did)
//
// didResolutionResult.didDocument!!.verificationMethod?.forEach {
// val publicKeyJwk = it.publicKeyJwk ?: throw PublicKeyJwkMissingException("publicKeyJwk is null")
// val keyAlias = keyManager.getDeterministicAlias(publicKeyJwk)
// keyManager.getPublicKey(keyAlias)
// }
//}
Loading
Loading