Skip to content

Commit

Permalink
cleanup and present package
Browse files Browse the repository at this point in the history
Signed-off-by: Timo Glastra <[email protected]>
  • Loading branch information
TimoGlastra committed Jan 17, 2024
1 parent abaae1d commit c8489b0
Show file tree
Hide file tree
Showing 43 changed files with 836 additions and 770 deletions.
3 changes: 2 additions & 1 deletion packages/core/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,8 @@
"dependencies": {
"@sd-jwt/decode": "workspace:*",
"@sd-jwt/types": "workspace:*",
"@sd-jwt/utils": "workspace:*"
"@sd-jwt/utils": "workspace:*",
"@sd-jwt/present": "workspace:*"
},
"devDependencies": {
"@types/node": "*",
Expand Down
7 changes: 4 additions & 3 deletions packages/core/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -19,15 +19,13 @@ export type {
} from './jwt'

export type {
Hasher,
Signer,
Verifier,
VerifyOptions,
SaltGenerator,
DisclosureItem,
DisclosureFrame,
HasherAndAlgorithm,
PresentationFrame
HasherAndAlgorithm
} from './types'

export type { SdJwtVcVerificationResult } from './sdJwtVc'
Expand All @@ -39,4 +37,7 @@ export { KeyBinding } from './keyBinding'
export { Jwt, JwtError } from './jwt'
export { SdJwtVc, SdJwtVcError } from './sdJwtVc'

// Re-export from sub-packages
export { HasherAlgorithm } from '@sd-jwt/utils'
export type { Hasher, AsyncHasher } from '@sd-jwt/types'
export type { PresentationFrame } from '@sd-jwt/present'
2 changes: 1 addition & 1 deletion packages/core/src/jwt/jwt.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Base64url } from '@sd-jwt/utils'
import { JwtError } from './error'
import { MakePropertyRequired, Signer } from '../types'
import { Verifier } from '../types'
import { getValueByKeyAnyLevel, simpleDeepEqual } from '../utils'
import { getValueByKeyAnyLevel, simpleDeepEqual } from '@sd-jwt/utils'
import { jwtFromCompact } from '@sd-jwt/decode'

type ReturnJwtWithHeaderAndPayload<
Expand Down
9 changes: 6 additions & 3 deletions packages/core/src/sdJwt/decoys.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
import { SaltGenerator, Hasher } from '../types'
import { HasherAndAlgorithm, SaltGenerator } from '../types'
import { SdJwtError } from './error'
import { Base64url } from '@sd-jwt/utils'

export const createDecoys = async (
count: number,
saltGenerator: SaltGenerator,
hasher: Hasher
hasherAndAlgorithm: HasherAndAlgorithm
) => {
if (count < 0) {
throw new SdJwtError(`Negative count of ${count} is not allowed.`)
Expand All @@ -22,7 +22,10 @@ export const createDecoys = async (
const decoys: Array<string> = []
for (let i = 0; i < count; i++) {
const salt = await saltGenerator()
const decoy = await hasher(salt)
const decoy = await hasherAndAlgorithm.hasher(
salt,
hasherAndAlgorithm.algorithm
)
const encodedDecoy = Base64url.encode(decoy)
decoys.push(encodedDecoy)
}
Expand Down
20 changes: 12 additions & 8 deletions packages/core/src/sdJwt/disclosureFrame.ts
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
import { DisclosureFrame } from '../types'
import { deleteByPath } from '../utils'
import { DisclosureFrame, HasherAndAlgorithm } from '../types'
import { deleteByPath } from '@sd-jwt/utils'
import { createDecoys } from './decoys'
import { Disclosure, DisclosureWithDigest } from './disclosures'
import { SdJwtError } from './error'
import { SaltGenerator, Hasher } from '../types'
import { SaltGenerator } from '../types'

export const applyDisclosureFrame = async <
Payload extends Record<string, unknown> = Record<string, unknown>
>(
saltGenerator: SaltGenerator,
hasher: Hasher,
hasherAndAlgorithm: HasherAndAlgorithm,
payload: Payload,
frame: DisclosureFrame<Payload>,
keys: Array<string> = [],
Expand All @@ -27,7 +27,11 @@ export const applyDisclosureFrame = async <
(payload._sd as string[]) ?? []
)

const decoys = await createDecoys(frameValue, saltGenerator, hasher)
const decoys = await createDecoys(
frameValue,
saltGenerator,
hasherAndAlgorithm
)
decoys.forEach((digest) => sd.push(digest))

// @ts-ignore
Expand All @@ -47,7 +51,7 @@ export const applyDisclosureFrame = async <
salt,
payload[key],
key
).withCalculateDigest(hasher)
).withCalculateDigest(hasherAndAlgorithm)
disclosures.push(disclosure)

const sd: Array<string> = Array.from(
Expand All @@ -66,7 +70,7 @@ export const applyDisclosureFrame = async <
) {
await applyDisclosureFrame(
saltGenerator,
hasher,
hasherAndAlgorithm,
payload[key] as Payload,
frameValue as DisclosureFrame<Payload>,
newKeys,
Expand Down Expand Up @@ -112,7 +116,7 @@ export const applyDisclosureFrame = async <
const disclosure = await new Disclosure(
salt,
payloadValue
).withCalculateDigest(hasher)
).withCalculateDigest(hasherAndAlgorithm)
disclosures.push(disclosure)

newPayloadArray.push({ '...': disclosure.digest })
Expand Down
75 changes: 58 additions & 17 deletions packages/core/src/sdJwt/disclosures.ts
Original file line number Diff line number Diff line change
@@ -1,7 +1,12 @@
import { DisclosureItem, Hasher } from '../types'
import { Base64url } from '@sd-jwt/utils'
import { disclosureCalculateDigest } from '@sd-jwt/decode'
import { AsyncHasher, Hasher } from '@sd-jwt/types'
import { Base64url, HasherAlgorithm, isPromise } from '@sd-jwt/utils'
import { DisclosureItem, HasherAndAlgorithm } from '../types'
import { SdJwtError } from './error'
import { isPromise } from '../utils'
import {
DisclosureWithDigest as DisclosureWithDigestJson,
Disclosure as DisclosureJson
} from '@sd-jwt/types'

// Make the digest property required
export type DisclosureWithDigest = Disclosure & { digest: string }
Expand Down Expand Up @@ -67,8 +72,10 @@ export class Disclosure {
return this as DisclosureWithDigest
}

public withCalculateDigest<HasherImplementation extends Hasher>(
hasher: HasherImplementation,
public withCalculateDigest<
HasherImplementation extends Hasher | AsyncHasher
>(
hasherAndAlgorithm: HasherAndAlgorithm<HasherImplementation>,
// Whether to recalculate the digest, even if it is already set
{ recalculate = false }: { recalculate?: boolean } = {}
): WithCalculateDigestReturnType<HasherImplementation> {
Expand All @@ -80,19 +87,22 @@ export class Disclosure {
return this as unknown as WithCalculateDigestReturnType<HasherImplementation>
}

// Calculate digest
const hashResult = hasher(this.encoded)
const digestResult = disclosureCalculateDigest(
this.asJson(),
// TODO: string vs HasherAlgorithm
hasherAndAlgorithm.algorithm as HasherAlgorithm,
hasherAndAlgorithm.hasher
)

// If promise, wait for it to resolve
if (isPromise(hashResult)) {
return hashResult.then((hash) => {
this.#digest = Base64url.encode(hash)
if (isPromise(digestResult)) {
return digestResult.then((digest) => {
this.#digest = digest

// We know for sure that digest is defined now
return this as DisclosureWithDigest
return this
}) as unknown as WithCalculateDigestReturnType<HasherImplementation>
} else {
this.#digest = Base64url.encode(hashResult)
this.#digest = digestResult

// We know for sure that digest is defined now
return this as unknown as WithCalculateDigestReturnType<HasherImplementation>
Expand All @@ -102,12 +112,43 @@ export class Disclosure {
public toString() {
return this.encoded
}

public asJson() {
return {
encoded: this.encoded,
salt: this.salt,
value: this.value,
key: this.key,
digest: this.digest
} as this extends DisclosureWithDigest
? DisclosureWithDigestJson
: DisclosureJson
}

public static fromJson<D extends DisclosureJson | DisclosureWithDigestJson>(
disclosureJson: D
) {
const disclosure = new Disclosure(
disclosureJson.salt,
disclosureJson.value,
disclosureJson.key
)

if ('digest' in disclosureJson) {
disclosure.withDigest(disclosureJson.digest)
}

return disclosure as D extends DisclosureWithDigestJson
? DisclosureWithDigest
: Disclosure
}
}

export type WithCalculateDigestReturnType<HasherImplementation extends Hasher> =
ReturnType<HasherImplementation> extends Promise<any>
? Promise<DisclosureWithDigest>
: DisclosureWithDigest
export type WithCalculateDigestReturnType<
HasherImplementation extends Hasher | AsyncHasher
> = ReturnType<HasherImplementation> extends Promise<any>
? Promise<DisclosureWithDigest>
: DisclosureWithDigest

export function isDisclosureWithDigest(
disclosure: Disclosure
Expand Down
1 change: 0 additions & 1 deletion packages/core/src/sdJwt/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,3 @@ export * from './disclosures'
export * from './decoys'
export * from './disclosureFrame'
export * from './types'
export * from './presentationFrame'
43 changes: 25 additions & 18 deletions packages/core/src/sdJwt/sdJwt.ts
Original file line number Diff line number Diff line change
@@ -1,26 +1,30 @@
import { Base64url } from '@sd-jwt/utils'
import { SdJwtError } from './error'
import { decodeDisclosuresInPayload, sdJwtFromCompact } from '@sd-jwt/decode'
import {
PresentationFrame,
getDisclosuresForPresentationFrame
} from '@sd-jwt/present'
import {
Base64url,
HasherAlgorithm,
getAllKeys,
getValueByKeyAnyLevel
} from '@sd-jwt/utils'
import { Jwt, JwtAdditionalOptions, JwtVerificationResult } from '../jwt/jwt'
import { KeyBinding } from '../keyBinding'
import {
DisclosureFrame,
HasherAndAlgorithm,
SaltGenerator,
Verifier
} from '../types'
import { Jwt, JwtAdditionalOptions, JwtVerificationResult } from '../jwt/jwt'
import { KeyBinding } from '../keyBinding'
import { applyDisclosureFrame } from './disclosureFrame'
import { Disclosure, DisclosureWithDigest } from './disclosures'
import { SdJwtError } from './error'
import {
ReturnSdJwtWithHeaderAndPayload,
ReturnSdJwtWithKeyBinding,
ReturnSdJwtWithPayload
} from './types'
import { Disclosure, DisclosureWithDigest } from './disclosures'
import { applyDisclosureFrame } from './disclosureFrame'
import { swapClaims } from './swapClaim'
import { getAllKeys, getValueByKeyAnyLevel } from '../utils'
import { PresentationFrame } from '../types/present'
import { getDisclosuresForPresentationFrame } from './presentationFrame'
import { sdJwtFromCompact } from '@sd-jwt/decode'
import { HasherAlgorithm } from '@sd-jwt/utils'

export type SdJwtToCompactOptions<
DisclosablePayload extends Record<string, unknown>
Expand Down Expand Up @@ -262,7 +266,7 @@ export class SdJwt<
const { payload: framedPayload, disclosures } =
await applyDisclosureFrame(
this.saltGenerator!,
this.hasherAndAlgorithm!.hasher,
this.hasherAndAlgorithm!,
this.addHasherAlgorithmToPayload().payload!,
this.disclosureFrame!
)
Expand All @@ -284,7 +288,7 @@ export class SdJwt<

return Promise.all(
this.disclosures.map((d) =>
d.withCalculateDigest(this.hasherAndAlgorithm!.hasher)
d.withCalculateDigest(this.hasherAndAlgorithm!)
)
)
}
Expand Down Expand Up @@ -403,10 +407,13 @@ export class SdJwt<
this.payload!,
presentationFrame,
await this.getPrettyClaims(),
disclosuresWithDigest
disclosuresWithDigest?.map((d) => d.asJson())
)

return await this.__toCompact(requiredDisclosures, false)
return await this.__toCompact(
requiredDisclosures.map((d) => Disclosure.fromJson(d)),
false
)
}

/**
Expand Down Expand Up @@ -514,9 +521,9 @@ export class SdJwt<
this.assertHashAndAlgorithm()

const disclosuresWithDigest = await this.disclosuresWithDigest()
const newPayload = swapClaims(
const newPayload = decodeDisclosuresInPayload(
this.payload!,
disclosuresWithDigest ?? []
disclosuresWithDigest?.map((d) => d.asJson()) ?? []
)

return newPayload as Claims
Expand Down
Loading

0 comments on commit c8489b0

Please sign in to comment.