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

Conversation

jiyoontbd
Copy link
Contributor

@jiyoontbd jiyoontbd commented Mar 7, 2024

Motivation

  • Align public API surface of DID creation and credential signing to be consistent with other web5 repos

Overview

  • BearerDid and PortableDid types used in place of Did(uri, keyManager) abstract class. Now all existing DID implementations' DidXyz.create() to return BearerDid.
  • Our own Jws, Jwt, Jwk types are used in place of nimbusds types. We are still creating private key internally using nimbusds (see Ed25519.kt for example), but all public surface APIs no longer expose the third party dependency.

How Has This Been Tested?

  • Ensured existing tests compiled and passed
  • Wrote new tests for new types and methods

Checklist

Before submitting this PR, please make sure:

  • I have read the CONTRIBUTING document.
  • My code is consistent with the rest of the project
  • I have tagged the relevant reviewers and/or interested parties
  • I have updated the READMEs and other documentation of affected packages

public fun sign(bearerDid: BearerDid, assertionMethodId: String? = null): String {
val payload = JwtClaimsSet.Builder()
.issuer(bearerDid.uri)
.issueTime(Date().time)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

if this is iat may need to change to seconds, currently this is milliseconds I think

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

fixed, and updated the ktdoc

public val iss: String? = null,
public val sub: String? = null,
public val aud: String? = null,
public val exp: Long? = null,
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think long is the right impl here but I think i see other libs with int

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

i agree it should stay as a long - can you tell me which lib has it as an int so we can write an issue for it? i checked js (number), go(int64) and swift (ISO8601Date)

Copy link
Contributor

@nitro-neal nitro-neal left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Wow great job ! Lots of hard work here.

Copy link

@diehuxx diehuxx left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

great work!

* @throws [AWSKMSException] for any error originating from the [AWSKMS] client
*/
override fun getPublicKey(keyAlias: String): JWK {
override fun getPublicKey(keyAlias: String): Jwk {
val getPublicKeyRequest = GetPublicKeyRequest().withKeyId(keyAlias)
val publicKeyResponse = kmsClient.getPublicKey(getPublicKeyRequest)
val publicKey = convertToJavaPublicKey(publicKeyResponse.publicKey)

val algorithmDetails = getAlgorithmDetails(publicKeyResponse.keySpec.enum())
val jwkBuilder = when (publicKey) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

does this handle uncompressing compressed EC keys?

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

good question not sure. it does handle converting what aws returns into a JWK correctly

}

override fun privateKeyToBytes(privateKey: JWK): ByteArray {
override fun privateKeyToBytes(privateKey: Jwk): ByteArray {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

it would be good to have methods that are able to represent compressed keys

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

for Ed25519 @decentralgabe ? or are you talking about Secp256k1 or more generally EC?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

would like gabe to weigh in on this / edit this issue to clarify what's missing here

#280

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

also are you talking about compressed public keys? bc presumably if you have d you can deterministically derive x and therefore y. so i'd say privateKeyToBytes already returns the most compressed key?

@jiyoontbd
Copy link
Contributor Author

jiyoontbd commented Mar 27, 2024

a reminder to myself to first merge TBD54566975/web5-spec#130 and then change the submodule commit to the merge commit

also do the same for TBD54566975/web5-js#466

Copy link
Contributor

@tomdaffurn tomdaffurn left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Approved with comment.

Huge amount of work, well done Jiyoon. Good riddance nimbus

is ECPublicKey -> ECKey.Builder(JwaCurve.toJwkCurve(algorithmDetails.curve), publicKey)
is ECPublicKey -> {
val key = ECKey.Builder(JwaCurve.toNimbusCurve(algorithmDetails.curve), publicKey).build()
Jwk.Builder("EC", key.curve.name)
Copy link
Contributor

@tomdaffurn tomdaffurn Mar 27, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Does this defs populate algorithm, keyID, keyUse? I think at least keyID was needed when first implemented

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

alg and kid is now not populated by default - relevant comment here

kid can be computed when needed via Jwk#computeThumbprint()

keyUse hmm... i'm pretty sure we're also not populating these by default, and it is optional per JWK spec
https://datatracker.ietf.org/doc/html/rfc7517#section-4.2

Values defined by this specification are:

o "sig" (signature)
o "enc" (encryption)

Other values MAY be used. The "use" value is a case-sensitive
string. Use of the "use" member is OPTIONAL, unless the application
requires its presence.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

the only fields that are required to build a Jwk are kty and crv

@jiyoontbd jiyoontbd merged commit f5a0488 into main Mar 27, 2024
6 of 7 checks passed
@jiyoontbd jiyoontbd deleted the 234-did-impl branch March 27, 2024 17:14
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
6 participants