GithubHelp home page GithubHelp logo

openwallet-foundation-labs / sd-jwt-js Goto Github PK

View Code? Open in Web Editor NEW
32.0 32.0 10.0 821 KB

A JavaScript implementation of the Selective Disclosure JWT (SD-JWT) spec.

Home Page: https://sdjwt.js.org/

License: Apache License 2.0

TypeScript 99.79% JavaScript 0.21%
jwt sd-jwt

sd-jwt-js's People

Contributors

aceshim avatar berendsliedrecht avatar charsleysa avatar cre8 avatar dependabot[bot] avatar lukasjhan avatar ryjones avatar tkuhrt avatar y12studio avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar

sd-jwt-js's Issues

Proposal: add crypto implementations as optional import

To make the usage easier for developers, I would suggest also to publish an implementation for the signet and verifier functions like key generation, hashing etc.

I would like to include it in the src folder and not an example folder so a developer can easily import and use it.

We are not forcing someone to use it, but we offer a challenged implementation that is ready to be used in the browser or in nodejs.

Too much type casting

There seems to be quite some type casting (also introduced by #80). This makes working within the library quite a bit more difficult. Most, if not all, type casts can be avoided but do require some restructuring of the functions. Over the coming weekend I will try to find some time to add some functionality to avoid the type casting and include some safety checks.

Host image to server and revise link

Overview

We currently upload our architecture image to our GitHub repository, but it appears that npm is unable to fetch it.
We should upload our image to a server and use it's URL

Fix: Create key binding JWT in present

In standard version 07, we can find details of key binding JWT(https://www.ietf.org/archive/id/draft-ietf-oauth-selective-disclosure-jwt-07.html#name-key-binding)

Key Binding aims to ensure that the presenter of an SD-JWT credential is actually the legitimate Holder of the credential

Note that there may be other ways to send a Key Binding JWT to the Verifier or for the Holder to prove possession of the key material included in an SD-JWT. In these cases, inclusion of the Key Binding JWT in the SD-JWT is not required.

It looks like key binding is created in present time

Create CODEOWNERS file

A CODEOWNERS file is required to ensure that approvals are completed via the CODEOWNERS.

Change the util/base64 function names to start with lowercase

By following the common javascript convention, I suggest all function names to be camelCase (or at least start with lowercase).
Especially when remarks can be misunderstood as a type instead of a function when imported somewhere else.

current

import { Base64 } from 'js-base64';

export const Base64urlEncode = Base64.encodeURI;

export const Base64urlDecode = Base64.decode;

export const Uint8ArrayToBase64Url = (input: Uint8Array): string =>
  Base64.fromUint8Array(input, true);

to-be

import { Base64 } from 'js-base64';

export const base64UrlEncode = Base64.encodeURI;

export const base64UrlDecode = Base64.decode;

export const uint8ArrayToBase64Url = (input: Uint8Array): string =>
  Base64.fromUint8Array(input, true);

Revise READMEs

  • Fix the root README.

    • Write about packages
    • Revise example code
  • Add README.md in package folder

e2e test not included

in vitest.shared.js e2e test file is excluded.
Removing exclude value lead to activate e2e test

import { defineProject, mergeConfig } from 'vitest/config';
export const browserConfig = defineProject({
  test: {
    globals: true,
    coverage: {
      exclude: ['examples/**'],
      reporter: ['json'],
    },
    environment: 'jsdom',
  },
});

export const nodeConfig = defineProject({
  test: {
    globals: true,
    coverage: {
      //TODO: the exclude is not working, therefore the coverage result are not correct.
      exclude: ['examples/**'],
      reporter: ['json'],
    },
    environment: 'node',
  },
});

export const allEnvs = mergeConfig(browserConfig, nodeConfig);

@cre8 Please share your thoughts :)

Add correct payload to SDJwtVcInstance

When decoding an sd-jwt-vc credential with

const sdjwtvc = await sdjwt.decode(credential.credential as string);
console.log(sdjwtvc.jwt!.payload!.jti);

it will throw an error that jti comes from an index signature. It would be correct that we pass SdJwtVcPayload to the decode function so it knows that the default values from a JWT can be set.

Current solution is to cast it manually like console.log((sdjwtvc.jwt!.payload as SdJwtVcPayload).jti);

According to the SD-JWT-VC spec, jti is not a known value, but according to the JWT spec it is. Should we also add all values that are in a JWT also to the payload of a Sdjwtvc? Of course marking them all as optional.

Change default branch to next

To be aligned with the new publish strategy in #89 we need to change the default branch from main to next

@ryjones can you do this for us please, we do not have the privileges for this action? :)

Add key binding example

Overview

Add key binding example in example project.

  • Issue with key binding
  • verify sd jwt with key binding

feat: Examples / Implementations for key request

Section 3.5 lists 3 options to receive the key of an issuer:

  • JWT VC Issuer Metadata
  • X.509 Certificates
  • DID Document resolution

Right now the library defines "bring your own crypto". Meaning the the user has to implement the getVerifier(publicKeyJWK: object): Promise<(data: string, signatureBase64url: string) => Promise<boolean>>; by himself. The data field includes the encoded header and payload field, therefore all three options can be implemented.

@lukasjhan do you think it makes sense? We can not implement to resolve all did elements, but we could give some examples for this.

bug: add biome format after release

When the release job gets executed. It will update the package.json files to add the new version number. Unfortunately it will be formatted in a way biome will fail after the changes get pushed to the main branch.

bug: Replace object with Extensible

some type are using the object type to extend a type like type DisclosureFrame<T extends object> = Frame<T>;

This could be the reason for some errors we have in the veramo implementation. One of their devs suggested to use an extensible like

interface Extensible {
  [key: string]?: unknown;
}

type DisclosureFrame<T extends Extensible> = Frame<T>;

Bug: Missing kid in the header

Right now we have only the reference to the issuer in either the iss or issuer field. But to determine the used key, we need to place a reference into the header

Discussion: presentation definition as object and not only sting list

Right now a presentation request is defined like ['name', 'address.city', 'address.street'] defining the fields that should be disclosed. The equivalent object oriented presentation looks like this:

const obj = {
      name: true,
      address: {
        city: true,
        street: true,
};

However I have some concerns allowing the object oriented approach:

  • adding types to a definition is a unnecessary feature. It it's set to true, it should be disclosed. If it's not present, it should not. So we don't even have a use case to set value to false
  • Disclosing array elements is not possible. With the object oriented way I can not create the case like [names.2.value].

Because of this I suggest to only allow the list presentation approach and not add or replace it with the object one.

feat: kbVerify improvment

right now the kbVerify uses the same input as Verify (payload and signature of the jwt). However the payload does not include a reference to the holder public key, therefore we are not able to verify the signature

Define minimal node version

We should define the minimal node version in the package json.

Additional goal:
In this step, we should also consider to run the tests as matrix to validate that it is running for multiple node versions. Doing so we need to check which step should upload the codecov report since the test run in parallel (or check if we can split it up and merge the different coverage reports laster). Right now vitest is doing browser and node test in sequence and is merging the report in one final file.

Some of features in decode, present package change the input object.

What I found out while implementing PEX is that there are functions that directly modify objects received as input. I think this will confuse users, so I think I'll have to copy the object internally and use it.

I proposed:

  • use after deep clone inside

selectDisclosures in @sd-jwt/present

  • comments or write a docs that this function change the input.

unpack, unpackSync, unpackObj and unpackArray in @sd-jwt/decode

feat: Update signer-function logic to be more dynamic

Right now I can pass a signer function to request the required key on demand like this example with veramo:

    const signer: Signer = async (data: string) => context.agent.keyManagerSign({ keyRef: key.kid, data })
    const sdjwt = new SDJwtInstance({
      signer,
      hasher: this.algorithms.hasher,
      saltGenerator: this.algorithms.salltGenerator,
      signAlg: alg,
  })

The problem is, that the signAlg is a static value so I am not able to use any other sign algorithm in my signer.

Suggestion

The signer will return not just the signature, but also the used signAlgorithm. This would allow a more dynamic approach
We could think about using this approach also for the hasher, beside the specification of sd-jwt is limited to sha-256. But It would make this approach more generalised when we change it now for the signature

Verification for jwt and kb+jwt reconstruct base64 encoding

Verification right now takes a JSON Object and re-encodes this for validation (see https://github.com/openwallet-foundation-labs/sd-jwt-js/blob/d9eaf4465bb5f50f21d90549678d6c426fea0ae8/packages/core/src/kbjwt.ts#L39C1-L41C41):

const header = Base64urlEncode(JSON.stringify(this.header));
const payload = Base64urlEncode(JSON.stringify(this.payload));
const data = `${header}.${payload}`;

This means that the original encoding of header/payload does not get preserved and signature validation might fail. A good example would be the sd-jwt-vc spec example created by the python reference implementation of sd-jwt. When debugging the calls for verification, header and payload change and signature validation fails (because the original encoding is not minified and the new encoding is minified).

Test Vectors to reproduce the problem with:

	const publicKeyExampleJwt: JWK = {
		kty: "EC",
		crv: "P-256",
		x: "b28d4MwZMjw8-00CG4xfnn9SLMVMM19SlqZpVb_uNtQ",
		y: "Xv5zWwuoaTgdS6hV43yI6gBwTnjukmFQQnJ_kCxzqk8",
	};
	let encodedJwt: string | undefined =
		"eyJhbGciOiAiRVMyNTYiLCAidHlwIjogInZjK3NkLWp3dCIsICJraWQiOiAiZG9jLXNpZ25lci0wNS0yNS0yMDIyIn0.eyJfc2QiOiBbIjA5dktySk1PbHlUV00wc2pwdV9wZE9CVkJRMk0xeTNLaHBINTE1blhrcFkiLCAiMnJzakdiYUMwa3k4bVQwcEpyUGlvV1RxMF9kYXcxc1g3NnBvVWxnQ3diSSIsICJFa084ZGhXMGRIRUpidlVIbEVfVkNldUM5dVJFTE9pZUxaaGg3WGJVVHRBIiwgIklsRHpJS2VpWmREd3BxcEs2WmZieXBoRnZ6NUZnbldhLXNONndxUVhDaXciLCAiSnpZakg0c3ZsaUgwUjNQeUVNZmVadTZKdDY5dTVxZWhabzdGN0VQWWxTRSIsICJQb3JGYnBLdVZ1Nnh5bUphZ3ZrRnNGWEFiUm9jMkpHbEFVQTJCQTRvN2NJIiwgIlRHZjRvTGJnd2Q1SlFhSHlLVlFaVTlVZEdFMHc1cnREc3JaemZVYW9tTG8iLCAiamRyVEU4WWNiWTRFaWZ1Z2loaUFlX0JQZWt4SlFaSUNlaVVRd1k5UXF4SSIsICJqc3U5eVZ1bHdRUWxoRmxNXzNKbHpNYVNGemdsaFFHMERwZmF5UXdMVUs0Il0sICJpc3MiOiAiaHR0cHM6Ly9leGFtcGxlLmNvbS9pc3N1ZXIiLCAiaWF0IjogMTY4MzAwMDAwMCwgImV4cCI6IDE4ODMwMDAwMDAsICJ2Y3QiOiAiaHR0cHM6Ly9jcmVkZW50aWFscy5leGFtcGxlLmNvbS9pZGVudGl0eV9jcmVkZW50aWFsIiwgIl9zZF9hbGciOiAic2hhLTI1NiIsICJjbmYiOiB7Imp3ayI6IHsia3R5IjogIkVDIiwgImNydiI6ICJQLTI1NiIsICJ4IjogIlRDQUVSMTladnUzT0hGNGo0VzR2ZlNWb0hJUDFJTGlsRGxzN3ZDZUdlbWMiLCAieSI6ICJaeGppV1diWk1RR0hWV0tWUTRoYlNJaXJzVmZ1ZWNDRTZ0NGpUOUYySFpRIn19fQ.QXgzrePAdq_WZVGCwDxP-l8h0iyckrHBNidxVqGtKJ0LMzObqgaXUD1cgGEf7d9TexPkBcgQYqjuzlfbeCxxuA~WyJRZ19PNjR6cUF4ZTQxMmExMDhpcm9BIiwgImFkZHJlc3MiLCB7InN0cmVldF9hZGRyZXNzIjogIjEyMyBNYWluIFN0IiwgImxvY2FsaXR5IjogIkFueXRvd24iLCAicmVnaW9uIjogIkFueXN0YXRlIiwgImNvdW50cnkiOiAiVVMifV0~WyI2SWo3dE0tYTVpVlBHYm9TNXRtdlZBIiwgImVtYWlsIiwgImpvaG5kb2VAZXhhbXBsZS5jb20iXQ~WyJlbHVWNU9nM2dTTklJOEVZbnN4QV9BIiwgImZhbWlseV9uYW1lIiwgIkRvZSJd~WyIyR0xDNDJzS1F2ZUNmR2ZyeU5STjl3IiwgImdpdmVuX25hbWUiLCAiSm9obiJd~eyJhbGciOiAiRVMyNTYiLCAidHlwIjogImtiK2p3dCJ9.eyJub25jZSI6ICIxMjM0NTY3ODkwIiwgImF1ZCI6ICJodHRwczovL2V4YW1wbGUuY29tL3ZlcmlmaWVyIiwgImlhdCI6IDE3MDk5OTYxODUsICJzZF9oYXNoIjogIjc4cFFEazJOblNEM1dKQm5SN015aWpmeUVqcGJ5a01yRnlpb2ZYSjlsN0kifQ.7k4goAlxM4a3tHnvCBCe70j_I-BCwtzhBRXQNk9cWJnQWxxt2kIqCyzcwzzUc0gTwtbGWVQoeWCiL5K6y3a4VQ";

Bug in checking validate in sdjwt instance

Overview

await keyword is missing at index.ts:138

const validated = this.validate(encodedSDJwt, publicKey);
if (!validated) {    ^
  return false;
}

It should be

const validated = await this.validate(encodedSDJwt, publicKey);
if (!validated) {    
  return false;
}

Improve Examples

Overview

  • I think the example should have comments to make it more understandable.
  • Fix example in readme
  • Add basic example in example projects

Disclosure digest should be accessible without providing Hasher

For a holder, you receive the Disclosures with digests and right now can't easily access the digests of disclosures (private member). The only way to access a digest, is via calling digest which requires a HaserAndAlg as input

  public async digest(hash: HasherAndAlg): Promise<string> {
    const { hasher, alg } = hash;
    if (!this._digest) {
      const hash = await hasher(this.encode(), alg);
      this._digest = Uint8ArrayToBase64Url(hash);
    }
    return this._digest;
  }

I do think it would be beneficial to make either the variable directly accessible (public) or change the signature of the function to have hash as an optional argument as it is only used when computing a new digest.

Support 07 version of standard

  • support new version: 07 standard
  • revise verify, validate return type (boolean -> { header, payload }
  • support custom signer, verifier function

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.