• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

hyperledger-identus / sdk-ts / 14536530200

18 Apr 2025 02:17PM UTC coverage: 82.802% (+7.3%) from 75.544%
14536530200

Pull #419

github

web-flow
Merge d20157089 into 0bddae204
Pull Request #419: test: chai and sinon removal

1711 of 2060 branches covered (83.06%)

Branch coverage included in aggregate %.

1 of 4 new or added lines in 1 file covered. (25.0%)

1047 existing lines in 114 files now uncovered.

9401 of 11360 relevant lines covered (82.76%)

33.67 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

93.85
/src/edge-agent/Agent.Backup.ts
1
import Pako from "pako";
1✔
2
import jweWasm from "jwe-wasm/jwe_rust_bg.wasm";
1✔
3
import * as Domain from "../domain";
1✔
4
import Agent from "./Agent";
5
import { Version } from "../domain/backup";
6
import { isNil, isObject, notNil, validateSafe } from "../utils";
1✔
7

8

9
/**
10
 * define Agent requirements for Backup
11
 */
12
type BackupAgent = Pick<Agent, "apollo" | "pluto" | "seed">;
13
type BackupExclude = "messages" | "mediators" | "link_secret";
14
type MasterKey = Domain.PrivateKey & Domain.ExportableKey.Common & Domain.ExportableKey.JWK & Domain.ExportableKey.PEM;
15

16
export type BackupOptions = {
17
  version?: Version;
18
  key?: MasterKey;
19
  compress?: boolean;
20
  excludes?: BackupExclude[];
21
};
22

23
export class AgentBackup {
1✔
24
  private _jwe: typeof import("jwe-wasm") | undefined;
25

26
  constructor(
1✔
27
    public readonly Agent: BackupAgent
55✔
28
  ) { }
55✔
29

30
  /**
31
   * Creates a JWE (JSON Web Encryption) containing the backup data stored in Pluto.
32
   * The data can optionally be encrypted using a custom master key, compressed, 
33
   * and filtered to exclude specified fields.
34
   * 
35
   * @param {BackupOptions} [options] - Optional settings for the backup.
36
   * @param {Version} [options.version] - Specifies the version of the backup data.
37
   * @param {MasterKey} [options.key] - Custom master key used for encrypting the backup.
38
   * @param {boolean} [options.compress] - If true, compresses the JWE using DEFLATE.
39
   * @param {BackupExclude[]} [options.excludes] - Keys to exclude from the backup data 
40
   * (e.g., "messages", "mediators", "link_secret"). Arrays are cleared, and strings are set to empty strings.
41
   * 
42
   * @returns {Promise<string>} - A promise that resolves to the JWE string.
43
   * 
44
   * @see restore - Method to restore data from a JWE string.
45
   */
46
  async createJWE(options?: BackupOptions): Promise<string> {
1✔
47
    let backup = await this.Agent.pluto.backup(options?.version);
6✔
48

49
    if (options?.excludes && Array.isArray(options.excludes)) {
6✔
50
      backup = this.applyExclusions(backup, options.excludes);
2✔
51
    }
2✔
52

53
    const backupStr = options?.compress ? this.compress(JSON.stringify(backup)) : JSON.stringify(backup);
6✔
54
    const masterSk = await this.masterSk(options);
6✔
55
    const jwk = masterSk.to.JWK();
6✔
56
    const JWE = await this.getJWE();
6✔
57
    const encrypted = JWE.encrypt(
6✔
58
      backupStr,
6✔
59
      JSON.stringify(jwk),
6✔
60
      'backup',
6✔
61
    );
6✔
62

63
    return encrypted;
6✔
64
  }
6✔
65

66
  /**
67
   * Decodes a JWE (JSON Web Encryption) string and restores the backup data to the store.
68
   * If the JWE is compressed (Base64-encoded), it will attempt to decompress it first.
69
   * 
70
   * @param {string} jwe - The JWE string containing the encrypted backup data.
71
   * @param {BackupOptions} [options] - Optional settings for the backup.
72
   * @param {Version} [options.version] - Specifies the version of the restore data.
73
   * @param {MasterKey} [options.key] - Custom master key used for decrypting the backup.
74
   * @param {boolean} [options.compress] - If true, compresses the JWE using INFLATE.
75
   * 
76
   * @returns {Promise<void>} - A promise that resolves when the data is successfully restored.
77
   * 
78
   * @see createJWE - Method to create a JWE from the stored backup data.
79
   */
80
  async restore(jwe: string, options?: BackupOptions) {
1✔
81
    const masterSk = await this.masterSk(options);
6✔
82
    const jwk = masterSk.to.JWK();
6✔
83
    const JWE = await this.getJWE();
6✔
84
    const decoded = JWE.decrypt(
6✔
85
      jwe,
6✔
86
      'backup',
6✔
87
      JSON.stringify(jwk),
6✔
88
    );
6✔
89
    let jsonStr: string;
6✔
90
    if (options?.compress) {
6✔
91
      jsonStr = this.decompress(new TextDecoder().decode(decoded));
4✔
92
    } else {
6✔
93
      jsonStr = Buffer.from(decoded).toString();
2✔
94
    }
2✔
95
    const json = JSON.parse(jsonStr);
6✔
96
    const backup = this.parseBackupJson(json);
6✔
97
    await this.Agent.pluto.restore(backup);
6✔
98
  }
6✔
99

100
  private parseBackupJson(json: unknown): Domain.Backup.Schema {
1✔
101
    if (isObject(json)) {
6✔
102
      const version = json.version ?? Domain.Backup.defaultVersion;
6!
103
      switch (version) {
6✔
104
        case "0.0.1":
6✔
105
          if (validateSafe(json, Domain.Backup.v0_0_1)) {
6✔
106
            return json;
6✔
107
          }
6!
108
          break;
×
109
      }
6✔
110
    }
6!
111
    throw new Domain.AgentError.BackupVersionError();
×
112
  }
6✔
113

114
  /**
115
 * Compresses a JSON object into a Base64-encoded string using DEFLATE.
116
 * 
117
 * - Uses `level: 9` for maximum compression and `strategy: Z_FILTERED` 
118
 *   (optimized for repetitive patterns, common in JSON data).
119
 * - Converts the JSON to a string, compresses it, and encodes it in Base64.
120
 * 
121
 * @param {unknown} json - The JSON object to compress.
122
 * @returns {string} - The Base64-encoded compressed string.
123
 */
124
  private compress(json: unknown): string {
1✔
125
    // Strategy 1 is 
126
    return Buffer.from(Pako.deflate(JSON.stringify(json), { level: 9, strategy: 1 })).toString('base64');
4✔
127
  }
4✔
128

129
  /**
130
 * Decompresses a Base64-encoded string into its original JSON representation.
131
 * 
132
 * - Decodes the Base64 string to a binary buffer.
133
 * - Uses DEFLATE to decompress the data and converts it back to a JSON string.
134
 * - Parses and returns the JSON object.
135
 * 
136
 * @param {string} data - The Base64-encoded compressed string.
137
 * @returns {string} - The decompressed JSON string.
138
 */
139
  private decompress(data: string): string {
1✔
140
    const compressedData = Buffer.from(data, 'base64');
4✔
141
    return JSON.parse(Pako.inflate(compressedData, { to: 'string' }));
4✔
142
  }
4✔
143

144
  /**
145
   * create a JWK for the MasterKey (X25519)
146
   * @returns JWK
147
   */
148
  private async masterSk(options?: BackupOptions) {
1✔
149
    const optKey = options?.key;
12✔
150
    if (notNil(optKey)) {
12✔
151
      return optKey;
4✔
152
    }
4✔
153

154
    const masterKey = this.Agent.apollo.createPrivateKey({
8✔
155
      [Domain.KeyProperties.curve]: Domain.Curve.X25519,
8✔
156
      [Domain.KeyProperties.seed]: Buffer.from(this.Agent.seed.value).toString("hex"),
8✔
157
      [Domain.KeyProperties.derivationPath]: "m/0'/0'/0'"
8✔
158
    });
8✔
159

160
    if (!masterKey.isExportable()) {
12!
161
      throw new Domain.AgentError.KeyNotExportableError();
×
UNCOV
162
    }
✔
163

164
    return masterKey;
8✔
165
  }
12✔
166

167
  /**
168
 * Modifies the backup object by applying exclusions.
169
 * Sets excluded array values to empty arrays and string values to empty strings.
170
 *
171
 * @param {Domain.Backup.Schema} backup - The backup object to be modified.
172
 * @param {BackupExclude[]} excludes - An array of keys to exclude from the backup.
173
 * @returns {Domain.Backup.Schema} The modified backup object.
174
 */
175
  private applyExclusions(backup: Domain.Backup.Schema, excludes: BackupExclude[]): Domain.Backup.Schema {
1✔
176
    const tmp = { ...backup };
2✔
177
    for (const exclude of excludes) {
2✔
178
      switch (exclude) {
6✔
179
        case "messages":
6✔
180
        case "mediators":
6✔
181
          tmp[exclude] = [];
4✔
182
          break;
4✔
183
        case "link_secret":
6✔
184
          tmp[exclude] = undefined;
2✔
185
          break;
2✔
186
      }
6✔
187
    }
6✔
188
    return tmp;
2✔
189
  }
2✔
190

191
  private async getJWE() {
1✔
192
    if (isNil(this._jwe)) {
12✔
193
      const module = await import("jwe-wasm");
9✔
194
      const wasmInstance = module.initSync({ module: jweWasm });
9✔
195
      await module.default(wasmInstance);
9✔
196
      this._jwe = module;
9✔
197
    }
9✔
198

199
    return this._jwe.JWE;
12✔
200
  }
12✔
201
}
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc