All files disclosure.ts

85.29% Statements 87/102
93.33% Branches 14/15
75% Functions 6/8
85.29% Lines 87/102

Press n or j to go to the next uncovered block, b, p or k for the previous block.

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 1031x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 1x 12x 12x 12x 12x 12x 12x 12x 12x 2x 2x 2x 2x 12x 8x 8x 8x 8x 8x 2x 12x 1x 1x 1x 1x 1x 4x 4x 4x 4x 4x 4x 1x 1x             1x 1x 4x 4x 4x 4x 4x 1x 1x 5x 4x 4x 4x 4x 5x 5x 1x 1x 4x 4x   4x 1x 1x 5x 5x 2x 2x 2x 5x 5x 5x 1x 1x                 1x  
import {
  Uint8ArrayToBase64Url,
  Base64urlDecode,
  Base64urlEncode,
} from './base64url';
import { SDJWTException } from './error';
import {
  HasherAndAlg,
  DisclosureData,
  HasherAndAlgSync,
} from '@hopae/sd-jwt-type';
 
export class Disclosure<T> {
  public salt: string;
  public key?: string;
  public value: T;
  private _digest: string | undefined;
  private _encoded: string | undefined;
 
  public constructor(
    data: DisclosureData<T>,
    _meta?: { digest: string; encoded: string },
  ) {
    // If the meta is provided, then we assume that the data is already encoded and digested
    this._digest = _meta?.digest;
    this._encoded = _meta?.encoded;
 
    if (data.length === 2) {
      this.salt = data[0];
      this.value = data[1];
      return;
    }
    if (data.length === 3) {
      this.salt = data[0];
      this.key = data[1] as string;
      this.value = data[2];
      return;
    }
    throw new SDJWTException('Invalid disclosure data');
  }
 
  // We need to digest of the original encoded data.
  // After decode process, we use JSON.stringify to encode the data.
  // This can be different from the original encoded data.
  public static async fromEncode<T>(s: string, hash: HasherAndAlg) {
    const { hasher, alg } = hash;
    const digest = await hasher(s, alg);
    const digestStr = Uint8ArrayToBase64Url(digest);
    const item = JSON.parse(Base64urlDecode(s)) as DisclosureData<T>;
    return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
  }
 
  public static fromEncodeSync<T>(s: string, hash: HasherAndAlgSync) {
    const { hasher, alg } = hash;
    const digest = hasher(s, alg);
    const digestStr = Uint8ArrayToBase64Url(digest);
    const item = JSON.parse(Base64urlDecode(s)) as DisclosureData<T>;
    return Disclosure.fromArray<T>(item, { digest: digestStr, encoded: s });
  }
 
  public static fromArray<T>(
    item: DisclosureData<T>,
    _meta?: { digest: string; encoded: string },
  ) {
    return new Disclosure(item, _meta);
  }
 
  public encode() {
    if (!this._encoded) {
      // we use JSON.stringify to encode the data
      // It's the most reliable and universal way to encode JSON object
      this._encoded = Base64urlEncode(JSON.stringify(this.decode()));
    }
    return this._encoded;
  }
 
  public decode(): DisclosureData<T> {
    return this.key
      ? [this.salt, this.key, this.value]
      : [this.salt, this.value];
  }
 
  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;
  }
 
  public digestSync(hash: HasherAndAlgSync): string {
    const { hasher, alg } = hash;
    if (!this._digest) {
      const hash = hasher(this.encode(), alg);
      this._digest = Uint8ArrayToBase64Url(hash);
    }

    return this._digest;
  }
}