Skip to content

Commit

Permalink
## 1.2.0 - 2024-09-27 (#29)
Browse files Browse the repository at this point in the history
* ## 1.2.0 - 2024-09-27

### Added

- Added `_sd_decoy` option to specify number of decoy element to add.

### Deprecated

- Deprecated `_decoyCount` option.

### Fixed

- Fixed `DisclosureFrame` type definition not to use `unknown`.

* Fix src/common.spec.ts imports

---------

Co-authored-by: Linas Išganaitis <linas.isganaitis@meeco.me>
  • Loading branch information
linasi and Linas Išganaitis authored Oct 1, 2024
1 parent c5206ea commit 96057a5
Show file tree
Hide file tree
Showing 8 changed files with 324 additions and 295 deletions.
14 changes: 14 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,20 @@ All notable changes to this project will be documented in this file.
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
and this project (loosely) adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).

## 1.2.0 - 2024-09-27

### Added

- Added `_sd_decoy` option to specify number of decoy element to add.

### Deprecated

- Deprecated `_decoyCount` option.

### Fixed

- Fixed `DisclosureFrame` type definition not to use `unknown`.

## 1.1.0 - 2024-08-14

### Added
Expand Down
10 changes: 5 additions & 5 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -45,14 +45,14 @@ To issue or pack claims into a valid SD-JWT we use Disclosure Frame to define wh
It follows the following format:
```typescript
type ArrayIndex = number;
type DisclosureFrame = {
[key: string | ArrayIndex]: DisclosureFrame | unknown;
_sd?: Array<string | ArrayIndex>;
_decoyCount?: number;
type DisclosureFrameSDAttributes = { _sd?: Array<string | ArrayIndex>; _sd_decoy?: number };
export type DisclosureFrame =
| ({ [key: string | ArrayIndex]: DisclosureFrame } & DisclosureFrameSDAttributes)
| DisclosureFrameSDAttributes;
};
```

`_decoyCount` is an optional property that defines the number of decoy digests to add.
`_sd_decoy` is an optional property that defines the number of decoy digests to add.

### Examples:

Expand Down
487 changes: 226 additions & 261 deletions package-lock.json

Large diffs are not rendered by default.

20 changes: 10 additions & 10 deletions package.json
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
{
"name": "@meeco/sd-jwt",
"version": "1.1.0",
"version": "1.2.0",
"description": "SD-JWT implementation in typescript",
"scripts": {
"build": "tsc",
Expand Down Expand Up @@ -54,22 +54,22 @@
"author": "Meeco",
"devDependencies": {
"@eslint/eslintrc": "^3.1.0",
"@eslint/js": "^9.9.0",
"@types/jest": "^29.5.5",
"@typescript-eslint/eslint-plugin": "^8.1.0",
"@typescript-eslint/parser": "^8.1.0",
"esbuild": "^0.23.0",
"eslint": "^9.9.0",
"@eslint/js": "^9.11.1",
"@types/jest": "^29.5.13",
"@typescript-eslint/eslint-plugin": "^8.7.0",
"@typescript-eslint/parser": "^8.7.0",
"esbuild": "^0.24.0",
"eslint": "^9.11.1",
"eslint-config-prettier": "^9.0.0",
"eslint-plugin-prettier": "^5.2.1",
"globals": "^15.9.0",
"jest": "^29.7.0",
"jose": "^5.6.3",
"jose": "^5.9.3",
"npm-run-all": "^4.1.5",
"prettier": "^3.3.3",
"ts-jest": "^29.2.4",
"ts-jest": "^29.2.5",
"ts-node": "^10.9.1",
"tsconfig-paths": "^4.2.0",
"typescript": "^5.5.4"
"typescript": "^5.6.2"
}
}
33 changes: 25 additions & 8 deletions src/common.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import {
loadVerifiedContents,
} from './test-utils/helpers';
import { INVALID_JWT } from './test-utils/params';
import { DisclosureFrame } from './types';

const examples = getExamples();

Expand Down Expand Up @@ -74,7 +75,7 @@ describe('packSDJWT', () => {

it('should be able to pack a simple claim', async () => {
const claims = { id: 123 };
const disclosureFrame = { _sd: ['id'] };
const disclosureFrame: DisclosureFrame = { _sd: ['id'] };
const { claims: result, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, { generateSalt });

const disclosureArray = ['salt', 'id', 123];
Expand All @@ -87,7 +88,7 @@ describe('packSDJWT', () => {

it('should be able to pack an array', async () => {
const claims = { items: [1, 2, 3] };
const disclosureFrame = {
const disclosureFrame: DisclosureFrame = {
items: { _sd: [0, 1] },
};
const { claims: result, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, { generateSalt });
Expand All @@ -104,7 +105,7 @@ describe('packSDJWT', () => {

it('should be able to recursively pack an array', async () => {
const claims = { items: [1, 2, { id: 123 }] };
const disclosureFrame = {
const disclosureFrame: DisclosureFrame = {
items: {
_sd: [0, 2],
2: { _sd: ['id'] },
Expand All @@ -128,7 +129,7 @@ describe('packSDJWT', () => {

it('should be able to pack nested object', async () => {
const claims = { items: { sub: { iss: 1, aud: 2 } } };
const disclosureFrame = {
const disclosureFrame: DisclosureFrame = {
items: {
sub: {
_sd: ['iss'],
Expand Down Expand Up @@ -157,7 +158,7 @@ describe('packSDJWT', () => {
const claims = {
items: [[1, 2], 3],
};
const disclosureFrame = {
const disclosureFrame: DisclosureFrame = {
items: {
_sd: [0],
0: { _sd: [0, 1] },
Expand Down Expand Up @@ -300,7 +301,16 @@ describe('packSDJWT', () => {
describe('decoys', () => {
it('should be able to generate decoys', async () => {
const claims = { id: 123 };
const disclosureFrame = { _sd: ['id'], _decoyCount: 5 };
const disclosureFrame: DisclosureFrame = { _sd: ['id'], _sd_decoy: 5 };
const { claims: result, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, { generateSalt });

expect(disclosures.length).toBe(1);
expect(result._sd.length).toBe(6);
});

it('still supports old _decoyCount parameter', async () => {
const claims = { id: 123 };
const disclosureFrame: DisclosureFrame = { _sd: ['id'], _decoyCount: 5 };
const { claims: result, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, { generateSalt });

expect(disclosures.length).toBe(1);
Expand All @@ -309,16 +319,23 @@ describe('packSDJWT', () => {

it('should be able to generate decoys in an array', async () => {
const claims = { arr: [1, 2, 3] };
const disclosureFrame = { arr: { _sd: [0, 1, 2], _decoyCount: 5 } };
const disclosureFrame: DisclosureFrame = { arr: { _sd: [0, 1, 2], _sd_decoy: 5 } };
const { claims: result, disclosures } = await packSDJWT(claims, disclosureFrame, hasher, { generateSalt });

expect(disclosures.length).toBe(3);
expect(result.arr.length).toBe(8);
});

it('throws error if both _sd_decoy and _decoyCount are provided', () => {
const claims = { id: 123 };
const disclosureFrame: DisclosureFrame = { _sd: ['id'], _sd_decoy: 1, _decoyCount: 2 };

expect(packSDJWT(claims, disclosureFrame, hasher, { generateSalt })).rejects.toThrow();
});

it('should throw an error when provided with a negative decoy count', () => {
const claims = { id: 123 };
const disclosureFrame = { _sd: ['id'], _decoyCount: -5 };
const disclosureFrame = { _sd: ['id'], _sd_decoy: -5 };

expect(packSDJWT(claims, disclosureFrame, hasher, { generateSalt })).rejects.toThrow();
});
Expand Down
29 changes: 24 additions & 5 deletions src/common.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { DEFAULT_SD_HASH_ALG, FORMAT_SEPARATOR, SD_DECOY_COUNT, SD_DIGEST, SD_HASH_ALG } from './constants.js';
import {
DEFAULT_SD_HASH_ALG,
FORMAT_SEPARATOR,
SD_DECOY,
SD_DECOY_COUNT,
SD_DIGEST,
SD_HASH_ALG,
} from './constants.js';
import { PackSDJWTError, SDJWTInvalidFormatError } from './errors.js';
import {
createDecoy,
Expand Down Expand Up @@ -100,6 +107,10 @@ export const packSDJWT: PackSDJWT = async (claims, disclosureFrame, hasher, opti
throw new PackSDJWTError('no claims found');
}

if (SD_DECOY in disclosureFrame && SD_DECOY_COUNT in disclosureFrame) {
throw new PackSDJWTError(`${SD_DECOY} and ${SD_DECOY_COUNT} parameters cannot be used together`);
}

const sd = disclosureFrame[SD_DIGEST];

let packedClaims;
Expand All @@ -110,7 +121,7 @@ export const packSDJWT: PackSDJWT = async (claims, disclosureFrame, hasher, opti
const recursivelyPackedClaims = {};

for (const key in disclosureFrame) {
if (key !== SD_DIGEST && key !== SD_DECOY_COUNT) {
if (key !== SD_DIGEST && key !== SD_DECOY && key !== SD_DECOY_COUNT) {
const idx = parseInt(key);
const packed = await packSDJWT(claims[idx], disclosureFrame[idx] as DisclosureFrame, hasher, options);
recursivelyPackedClaims[idx] = packed.claims;
Expand All @@ -129,15 +140,19 @@ export const packSDJWT: PackSDJWT = async (claims, disclosureFrame, hasher, opti
}
}

const decoys = createDecoy(disclosureFrame[SD_DECOY_COUNT], hasher, options?.generateSalt);
const decoys = createDecoy(
disclosureFrame[SD_DECOY] || disclosureFrame[SD_DECOY_COUNT],
hasher,
options?.generateSalt,
);
decoys.forEach((decoy) => {
packedClaims.push({ '...': decoy });
});
} else {
packedClaims = {};
const recursivelyPackedClaims = {};
for (const key in disclosureFrame) {
if (key !== SD_DIGEST && key !== SD_DECOY_COUNT) {
if (key !== SD_DIGEST && key !== SD_DECOY && key !== SD_DECOY_COUNT) {
const packed = await packSDJWT(claims[key], disclosureFrame[key] as DisclosureFrame, hasher, options);
recursivelyPackedClaims[key] = packed.claims;
disclosures = disclosures.concat(packed.disclosures);
Expand All @@ -157,7 +172,11 @@ export const packSDJWT: PackSDJWT = async (claims, disclosureFrame, hasher, opti
}
}

const decoys = createDecoy(disclosureFrame[SD_DECOY_COUNT], hasher, options?.generateSalt);
const decoys = createDecoy(
disclosureFrame[SD_DECOY] || disclosureFrame[SD_DECOY_COUNT],
hasher,
options?.generateSalt,
);
decoys.forEach((decoy) => {
_sd.push(decoy);
});
Expand Down
4 changes: 4 additions & 0 deletions src/constants.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,10 @@
export const SD_DIGEST = '_sd';
export const SD_HASH_ALG = '_sd_alg';
/**
* @deprecated use SD_DECOY instead.
*/
export const SD_DECOY_COUNT = '_decoyCount';
export const SD_DECOY = '_sd_decoy';
export const DEFAULT_SD_HASH_ALG = 'sha-256';
export const FORMAT_SEPARATOR = '~';
export const KB_JWT_TYPE_HEADER = 'kb+jwt';
Expand Down
22 changes: 16 additions & 6 deletions src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -118,15 +118,21 @@ export interface DisclosureClaim {
}

type ArrayIndex = number;
export type DisclosureFrame = {
[key: string | ArrayIndex]: DisclosureFrame | unknown;
type DisclosureFrameSDAttributes = {
_sd?: Array<string | ArrayIndex>;
_sd_decoy?: number;
/**
* @deprecated use _sd_decoy instead.
*/
_decoyCount?: number;
};
export type DisclosureFrame =
| ({ [key: string | ArrayIndex]: DisclosureFrame } & DisclosureFrameSDAttributes)
| DisclosureFrameSDAttributes;

export type PackedClaims = {
_sd?: Array<string>;
[key: string]: any | unknown;
[key: string]: any;
};

export type SelectiveDiscloseableArrayItem = {
Expand All @@ -136,11 +142,15 @@ export type SelectiveDiscloseableArrayItem = {
'...': SelectiveDisclosableClaims | any;
};

export type SelectiveDisclosableClaims = {
type SelectiveDisclosableClaimsSDAttributes = {
// disclosure.string
_sd?: string;
[key: string]: SelectiveDisclosableClaims | Array<SelectiveDiscloseableArrayItem | any> | unknown;
};
export type SelectiveDisclosableClaims =
| ({
[key: string]: SelectiveDisclosableClaims | Array<SelectiveDiscloseableArrayItem | any>;
} & SelectiveDisclosableClaimsSDAttributes)
| SelectiveDisclosableClaimsSDAttributes;

export type DisclosureMap = {
[sdDigest: string]: {
Expand All @@ -159,7 +169,7 @@ export type GetHasher = (hashAlg: string) => Promise<Hasher>;
export type Signer = (header: JWTHeaderParameters, payload: JWTPayload) => Promise<string>;
export type Verifier = (data: string) => Promise<boolean>;
export type KeyBindingVerifier = (data: string, key: JWK) => Promise<boolean>;
export type SaltGenerator = (size) => string;
export type SaltGenerator = (size: number) => string;

export interface IssueSDJWTOptions {
signer: Signer;
Expand Down

0 comments on commit 96057a5

Please sign in to comment.