@opengovsg/formsg-sdk 0.11.0 → 0.12.0-alpha.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -45,6 +45,8 @@ jobs:
45
45
  ${{ runner.OS }}-
46
46
  - run: npm ci
47
47
  - run: npm run test-ci
48
+ env:
49
+ NODE_OPTIONS: '--max-old-space-size=8192'
48
50
  - name: Submit test coverage to Coveralls
49
51
  uses: coverallsapp/github-action@v1.1.2
50
52
  with:
@@ -0,0 +1,25 @@
1
+ import { EncryptedFileContent } from './types';
2
+ export default class CryptoBase {
3
+ /**
4
+ * Generates a new keypair for encryption.
5
+ * @returns The generated keypair.
6
+ */
7
+ generate: () => import("./types").Keypair;
8
+ /**
9
+ * Encrypt given binary file with a unique keypair for each submission.
10
+ * @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary
11
+ * @param publicKey The base-64 encoded public key
12
+ * @returns Promise holding the encrypted file
13
+ * @throws error if any of the encrypt methods fail
14
+ */
15
+ encryptFile: (binary: Uint8Array, publicKey: string) => Promise<EncryptedFileContent>;
16
+ /**
17
+ * Decrypt the given encrypted file content.
18
+ * @param secretKey Secret key as a base-64 string
19
+ * @param encrypted Object returned from encryptFile function
20
+ * @param encrypted.submissionPublicKey The file's public key as a base-64 string
21
+ * @param encrypted.nonce The nonce as a base-64 string
22
+ * @param encrypted.blob The encrypted file as a Blob object
23
+ */
24
+ decryptFile: (secretKey: string, { submissionPublicKey: filePublicKey, nonce, binary: encryptedBinary, }: EncryptedFileContent) => Promise<Uint8Array | null>;
25
+ }
@@ -0,0 +1,93 @@
1
+ "use strict";
2
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
+ return new (P || (P = Promise))(function (resolve, reject) {
5
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
6
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
7
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
8
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
9
+ });
10
+ };
11
+ var __generator = (this && this.__generator) || function (thisArg, body) {
12
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g;
13
+ return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
14
+ function verb(n) { return function (v) { return step([n, v]); }; }
15
+ function step(op) {
16
+ if (f) throw new TypeError("Generator is already executing.");
17
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
18
+ if (f = 1, y && (t = op[0] & 2 ? y["return"] : op[0] ? y["throw"] || ((t = y["return"]) && t.call(y), 0) : y.next) && !(t = t.call(y, op[1])).done) return t;
19
+ if (y = 0, t) op = [op[0] & 2, t.value];
20
+ switch (op[0]) {
21
+ case 0: case 1: t = op; break;
22
+ case 4: _.label++; return { value: op[1], done: false };
23
+ case 5: _.label++; y = op[1]; op = [0]; continue;
24
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
25
+ default:
26
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
27
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
28
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
29
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
30
+ if (t[2]) _.ops.pop();
31
+ _.trys.pop(); continue;
32
+ }
33
+ op = body.call(thisArg, _);
34
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
35
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
36
+ }
37
+ };
38
+ var __importDefault = (this && this.__importDefault) || function (mod) {
39
+ return (mod && mod.__esModule) ? mod : { "default": mod };
40
+ };
41
+ Object.defineProperty(exports, "__esModule", { value: true });
42
+ var tweetnacl_1 = __importDefault(require("tweetnacl"));
43
+ var tweetnacl_util_1 = require("tweetnacl-util");
44
+ var crypto_1 = require("./util/crypto");
45
+ var CryptoBase = /** @class */ (function () {
46
+ function CryptoBase() {
47
+ var _this = this;
48
+ /**
49
+ * Generates a new keypair for encryption.
50
+ * @returns The generated keypair.
51
+ */
52
+ this.generate = crypto_1.generateKeypair;
53
+ /**
54
+ * Encrypt given binary file with a unique keypair for each submission.
55
+ * @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary
56
+ * @param publicKey The base-64 encoded public key
57
+ * @returns Promise holding the encrypted file
58
+ * @throws error if any of the encrypt methods fail
59
+ */
60
+ this.encryptFile = function (binary, publicKey) { return __awaiter(_this, void 0, void 0, function () {
61
+ var fileKeypair, nonce;
62
+ return __generator(this, function (_a) {
63
+ fileKeypair = this.generate();
64
+ nonce = tweetnacl_1.default.randomBytes(24);
65
+ return [2 /*return*/, {
66
+ //! NOTE: submissionPublicKey here is a misnomer as a new keypair is generated per file.
67
+ // The naming is only retained for backward-compatibility purposes.
68
+ submissionPublicKey: fileKeypair.publicKey,
69
+ nonce: (0, tweetnacl_util_1.encodeBase64)(nonce),
70
+ binary: tweetnacl_1.default.box(binary, nonce, (0, tweetnacl_util_1.decodeBase64)(publicKey), (0, tweetnacl_util_1.decodeBase64)(fileKeypair.secretKey)),
71
+ }];
72
+ });
73
+ }); };
74
+ /**
75
+ * Decrypt the given encrypted file content.
76
+ * @param secretKey Secret key as a base-64 string
77
+ * @param encrypted Object returned from encryptFile function
78
+ * @param encrypted.submissionPublicKey The file's public key as a base-64 string
79
+ * @param encrypted.nonce The nonce as a base-64 string
80
+ * @param encrypted.blob The encrypted file as a Blob object
81
+ */
82
+ this.decryptFile = function (secretKey, _a) {
83
+ var filePublicKey = _a.submissionPublicKey, nonce = _a.nonce, encryptedBinary = _a.binary;
84
+ return __awaiter(_this, void 0, void 0, function () {
85
+ return __generator(this, function (_b) {
86
+ return [2 /*return*/, tweetnacl_1.default.box.open(encryptedBinary, (0, tweetnacl_util_1.decodeBase64)(nonce), (0, tweetnacl_util_1.decodeBase64)(filePublicKey), (0, tweetnacl_util_1.decodeBase64)(secretKey))];
87
+ });
88
+ });
89
+ };
90
+ }
91
+ return CryptoBase;
92
+ }());
93
+ exports.default = CryptoBase;
@@ -0,0 +1,37 @@
1
+ import CryptoBase from './crypto-base';
2
+ import { DecryptedContentV3, DecryptParams, DecryptParamsV3, EncryptedContentV3 } from './types';
3
+ export default class CryptoV3 extends CryptoBase {
4
+ constructor();
5
+ /**
6
+ * Encrypt input with a unique keypair for each submission.
7
+ * @param msg The message to encrypt, will be stringified.
8
+ * @param form The base-64 encoded form public key for encrypting.
9
+ * @returns The encrypted basestring.
10
+ */
11
+ encrypt: (msg: any, formPublicKey: string) => EncryptedContentV3;
12
+ /**
13
+ * Decrypts an encrypted submission and returns it.
14
+ * @param submissionSecretKey The base-64 encoded secret key for decrypting.
15
+ * @param decryptParams The params containing encrypted content and information.
16
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
17
+ * @param decryptParams.version The version of the payload.
18
+ * @returns The decrypted content if successful. Else, null will be returned.
19
+ */
20
+ decryptFromSubmissionKey: (submissionSecretKey: string, decryptParams: DecryptParams) => DecryptedContentV3 | null;
21
+ /**
22
+ * Decrypts an encrypted submission and returns it.
23
+ * @param formSecretKey The base-64 encoded form secret key for decrypting the submission.
24
+ * @param decryptParams The params containing encrypted content, encrypted submission key and information.
25
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
26
+ * @param decryptParams.encryptedSubmissionSecretKey The encrypted submission secret key encoded with base-64.
27
+ * @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with.
28
+ * @returns The decrypted content if successful. Else, null will be returned.
29
+ */
30
+ decrypt: (formSecretKey: string, decryptParams: DecryptParamsV3) => DecryptedContentV3 | null;
31
+ /**
32
+ * Returns true if a pair of public & secret keys are associated with each other
33
+ * @param publicKey The public key to verify against.
34
+ * @param secretKey The private key to verify against.
35
+ */
36
+ valid: (publicKey: string, secretKey: string) => boolean;
37
+ }
@@ -0,0 +1,134 @@
1
+ "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
17
+ var __assign = (this && this.__assign) || function () {
18
+ __assign = Object.assign || function(t) {
19
+ for (var s, i = 1, n = arguments.length; i < n; i++) {
20
+ s = arguments[i];
21
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p))
22
+ t[p] = s[p];
23
+ }
24
+ return t;
25
+ };
26
+ return __assign.apply(this, arguments);
27
+ };
28
+ var __rest = (this && this.__rest) || function (s, e) {
29
+ var t = {};
30
+ for (var p in s) if (Object.prototype.hasOwnProperty.call(s, p) && e.indexOf(p) < 0)
31
+ t[p] = s[p];
32
+ if (s != null && typeof Object.getOwnPropertySymbols === "function")
33
+ for (var i = 0, p = Object.getOwnPropertySymbols(s); i < p.length; i++) {
34
+ if (e.indexOf(p[i]) < 0 && Object.prototype.propertyIsEnumerable.call(s, p[i]))
35
+ t[p[i]] = s[p[i]];
36
+ }
37
+ return t;
38
+ };
39
+ var __importDefault = (this && this.__importDefault) || function (mod) {
40
+ return (mod && mod.__esModule) ? mod : { "default": mod };
41
+ };
42
+ Object.defineProperty(exports, "__esModule", { value: true });
43
+ var tweetnacl_util_1 = require("tweetnacl-util");
44
+ var crypto_1 = require("./util/crypto");
45
+ var validate_1 = require("./util/validate");
46
+ var crypto_base_1 = __importDefault(require("./crypto-base"));
47
+ var CryptoV3 = /** @class */ (function (_super) {
48
+ __extends(CryptoV3, _super);
49
+ function CryptoV3() {
50
+ var _this = _super.call(this) || this;
51
+ /**
52
+ * Encrypt input with a unique keypair for each submission.
53
+ * @param msg The message to encrypt, will be stringified.
54
+ * @param form The base-64 encoded form public key for encrypting.
55
+ * @returns The encrypted basestring.
56
+ */
57
+ _this.encrypt = function (msg, formPublicKey) {
58
+ var submissionKeypair = (0, crypto_1.generateKeypair)();
59
+ var encryptedSubmissionSecretKey = (0, crypto_1.encryptMessage)((0, tweetnacl_util_1.decodeBase64)(submissionKeypair.secretKey), formPublicKey);
60
+ var processedMsg = (0, tweetnacl_util_1.decodeUTF8)(JSON.stringify(msg));
61
+ var encryptedContent = (0, crypto_1.encryptMessage)(processedMsg, submissionKeypair.publicKey);
62
+ return {
63
+ submissionPublicKey: submissionKeypair.publicKey,
64
+ submissionSecretKey: submissionKeypair.secretKey,
65
+ encryptedContent: encryptedContent,
66
+ encryptedSubmissionSecretKey: encryptedSubmissionSecretKey,
67
+ };
68
+ };
69
+ /**
70
+ * Decrypts an encrypted submission and returns it.
71
+ * @param submissionSecretKey The base-64 encoded secret key for decrypting.
72
+ * @param decryptParams The params containing encrypted content and information.
73
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
74
+ * @param decryptParams.version The version of the payload.
75
+ * @returns The decrypted content if successful. Else, null will be returned.
76
+ */
77
+ _this.decryptFromSubmissionKey = function (submissionSecretKey, decryptParams) {
78
+ try {
79
+ var encryptedContent = decryptParams.encryptedContent;
80
+ // Do not return the transformed object in `_decrypt` function as a signed
81
+ // object is not encoded in UTF8 and is encoded in Base-64 instead.
82
+ var decryptedContent = (0, crypto_1.decryptContent)(submissionSecretKey, encryptedContent);
83
+ if (!decryptedContent) {
84
+ throw new Error('Failed to decrypt content');
85
+ }
86
+ var decryptedObject = JSON.parse((0, tweetnacl_util_1.encodeUTF8)(decryptedContent));
87
+ if (!(0, validate_1.determineIsFormFieldsV3)(decryptedObject)) {
88
+ throw new Error('Decrypted object does not fit expected shape');
89
+ }
90
+ var returnedObject = {
91
+ submissionSecretKey: submissionSecretKey,
92
+ responses: decryptedObject,
93
+ };
94
+ return returnedObject;
95
+ }
96
+ catch (err) {
97
+ return null;
98
+ }
99
+ };
100
+ /**
101
+ * Decrypts an encrypted submission and returns it.
102
+ * @param formSecretKey The base-64 encoded form secret key for decrypting the submission.
103
+ * @param decryptParams The params containing encrypted content, encrypted submission key and information.
104
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
105
+ * @param decryptParams.encryptedSubmissionSecretKey The encrypted submission secret key encoded with base-64.
106
+ * @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with.
107
+ * @returns The decrypted content if successful. Else, null will be returned.
108
+ */
109
+ _this.decrypt = function (formSecretKey, decryptParams) {
110
+ var encryptedSubmissionSecretKey = decryptParams.encryptedSubmissionSecretKey, rest = __rest(decryptParams, ["encryptedSubmissionSecretKey"]);
111
+ var submissionSecretKey = (0, crypto_1.decryptContent)(formSecretKey, encryptedSubmissionSecretKey);
112
+ if (submissionSecretKey === null)
113
+ return null;
114
+ return _this.decryptFromSubmissionKey((0, tweetnacl_util_1.encodeBase64)(submissionSecretKey), rest);
115
+ };
116
+ /**
117
+ * Returns true if a pair of public & secret keys are associated with each other
118
+ * @param publicKey The public key to verify against.
119
+ * @param secretKey The private key to verify against.
120
+ */
121
+ _this.valid = function (publicKey, secretKey) {
122
+ var _a;
123
+ var testResponse = {};
124
+ var internalValidationVersion = 3;
125
+ var cipherResponse = _this.encrypt(testResponse, publicKey);
126
+ // Use toString here since the return should be an empty array.
127
+ return (testResponse.toString() ===
128
+ ((_a = _this.decrypt(secretKey, __assign(__assign({}, cipherResponse), { version: internalValidationVersion }))) === null || _a === void 0 ? void 0 : _a.responses.toString()));
129
+ };
130
+ return _this;
131
+ }
132
+ return CryptoV3;
133
+ }(crypto_base_1.default));
134
+ exports.default = CryptoV3;
package/dist/crypto.d.ts CHANGED
@@ -1,5 +1,6 @@
1
- import { DecryptedContent, DecryptedContentAndAttachments, DecryptParams, EncryptedContent, EncryptedFileContent } from './types';
2
- export default class Crypto {
1
+ import CryptoBase from './crypto-base';
2
+ import { DecryptedContent, DecryptedContentAndAttachments, DecryptParams, EncryptedContent } from './types';
3
+ export default class Crypto extends CryptoBase {
3
4
  signingPublicKey?: string;
4
5
  constructor({ signingPublicKey }?: {
5
6
  signingPublicKey?: string;
@@ -23,34 +24,12 @@ export default class Crypto {
23
24
  * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
24
25
  */
25
26
  decrypt: (formSecretKey: string, decryptParams: DecryptParams) => DecryptedContent | null;
26
- /**
27
- * Generates a new keypair for encryption.
28
- * @returns The generated keypair.
29
- */
30
- generate: () => import("./types").Keypair;
31
27
  /**
32
28
  * Returns true if a pair of public & secret keys are associated with each other
33
29
  * @param publicKey The public key to verify against.
34
30
  * @param secretKey The private key to verify against.
35
31
  */
36
32
  valid: (publicKey: string, secretKey: string) => boolean;
37
- /**
38
- * Encrypt given binary file with a unique keypair for each submission.
39
- * @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary
40
- * @param formPublicKey The base-64 encoded public key
41
- * @returns Promise holding the encrypted file
42
- * @throws error if any of the encrypt methods fail
43
- */
44
- encryptFile: (binary: Uint8Array, formPublicKey: string) => Promise<EncryptedFileContent>;
45
- /**
46
- * Decrypt the given encrypted file content.
47
- * @param formSecretKey Secret key as a base-64 string
48
- * @param encrypted Object returned from encryptFile function
49
- * @param encrypted.submissionPublicKey The submission public key as a base-64 string
50
- * @param encrypted.nonce The nonce as a base-64 string
51
- * @param encrypted.blob The encrypted file as a Blob object
52
- */
53
- decryptFile: (formSecretKey: string, { submissionPublicKey, nonce, binary: encryptedBinary, }: EncryptedFileContent) => Promise<Uint8Array | null>;
54
33
  /**
55
34
  * Decrypts an encrypted submission, and also download and decrypt any attachments alongside it.
56
35
  * @param formSecretKey Secret key as a base-64 string
package/dist/crypto.js CHANGED
@@ -1,4 +1,19 @@
1
1
  "use strict";
2
+ var __extends = (this && this.__extends) || (function () {
3
+ var extendStatics = function (d, b) {
4
+ extendStatics = Object.setPrototypeOf ||
5
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
6
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
7
+ return extendStatics(d, b);
8
+ };
9
+ return function (d, b) {
10
+ if (typeof b !== "function" && b !== null)
11
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
12
+ extendStatics(d, b);
13
+ function __() { this.constructor = d; }
14
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
15
+ };
16
+ })();
2
17
  var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
3
18
  function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
4
19
  return new (P || (P = Promise))(function (resolve, reject) {
@@ -44,11 +59,13 @@ var tweetnacl_1 = __importDefault(require("tweetnacl"));
44
59
  var tweetnacl_util_1 = require("tweetnacl-util");
45
60
  var crypto_1 = require("./util/crypto");
46
61
  var validate_1 = require("./util/validate");
62
+ var crypto_base_1 = __importDefault(require("./crypto-base"));
47
63
  var errors_1 = require("./errors");
48
- var Crypto = /** @class */ (function () {
64
+ var Crypto = /** @class */ (function (_super) {
65
+ __extends(Crypto, _super);
49
66
  function Crypto(_a) {
50
67
  var _b = _a === void 0 ? {} : _a, signingPublicKey = _b.signingPublicKey;
51
- var _this = this;
68
+ var _this = _super.call(this) || this;
52
69
  /**
53
70
  * Encrypt input with a unique keypair for each submission
54
71
  * @param encryptionPublicKey The base-64 encoded public key for encrypting.
@@ -56,7 +73,7 @@ var Crypto = /** @class */ (function () {
56
73
  * @param signingPrivateKey Optional. Must be a base-64 encoded private key. If given, will be used to signing the given msg param prior to encrypting.
57
74
  * @returns The encrypted basestring.
58
75
  */
59
- this.encrypt = function (msg, encryptionPublicKey, signingPrivateKey) {
76
+ _this.encrypt = function (msg, encryptionPublicKey, signingPrivateKey) {
60
77
  var processedMsg = (0, tweetnacl_util_1.decodeUTF8)(JSON.stringify(msg));
61
78
  if (signingPrivateKey) {
62
79
  processedMsg = tweetnacl_1.default.sign(processedMsg, (0, tweetnacl_util_1.decodeBase64)(signingPrivateKey));
@@ -73,7 +90,7 @@ var Crypto = /** @class */ (function () {
73
90
  * @returns The decrypted content if successful. Else, null will be returned.
74
91
  * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
75
92
  */
76
- this.decrypt = function (formSecretKey, decryptParams) {
93
+ _this.decrypt = function (formSecretKey, decryptParams) {
77
94
  try {
78
95
  var encryptedContent = decryptParams.encryptedContent, verifiedContent = decryptParams.verifiedContent;
79
96
  // Do not return the transformed object in `_decrypt` function as a signed
@@ -116,17 +133,12 @@ var Crypto = /** @class */ (function () {
116
133
  return null;
117
134
  }
118
135
  };
119
- /**
120
- * Generates a new keypair for encryption.
121
- * @returns The generated keypair.
122
- */
123
- this.generate = crypto_1.generateKeypair;
124
136
  /**
125
137
  * Returns true if a pair of public & secret keys are associated with each other
126
138
  * @param publicKey The public key to verify against.
127
139
  * @param secretKey The private key to verify against.
128
140
  */
129
- this.valid = function (publicKey, secretKey) {
141
+ _this.valid = function (publicKey, secretKey) {
130
142
  var _a;
131
143
  var testResponse = [];
132
144
  var internalValidationVersion = 1;
@@ -138,41 +150,6 @@ var Crypto = /** @class */ (function () {
138
150
  version: internalValidationVersion,
139
151
  })) === null || _a === void 0 ? void 0 : _a.responses.toString()));
140
152
  };
141
- /**
142
- * Encrypt given binary file with a unique keypair for each submission.
143
- * @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary
144
- * @param formPublicKey The base-64 encoded public key
145
- * @returns Promise holding the encrypted file
146
- * @throws error if any of the encrypt methods fail
147
- */
148
- this.encryptFile = function (binary, formPublicKey) { return __awaiter(_this, void 0, void 0, function () {
149
- var submissionKeypair, nonce;
150
- return __generator(this, function (_a) {
151
- submissionKeypair = this.generate();
152
- nonce = tweetnacl_1.default.randomBytes(24);
153
- return [2 /*return*/, {
154
- submissionPublicKey: submissionKeypair.publicKey,
155
- nonce: (0, tweetnacl_util_1.encodeBase64)(nonce),
156
- binary: tweetnacl_1.default.box(binary, nonce, (0, tweetnacl_util_1.decodeBase64)(formPublicKey), (0, tweetnacl_util_1.decodeBase64)(submissionKeypair.secretKey)),
157
- }];
158
- });
159
- }); };
160
- /**
161
- * Decrypt the given encrypted file content.
162
- * @param formSecretKey Secret key as a base-64 string
163
- * @param encrypted Object returned from encryptFile function
164
- * @param encrypted.submissionPublicKey The submission public key as a base-64 string
165
- * @param encrypted.nonce The nonce as a base-64 string
166
- * @param encrypted.blob The encrypted file as a Blob object
167
- */
168
- this.decryptFile = function (formSecretKey, _a) {
169
- var submissionPublicKey = _a.submissionPublicKey, nonce = _a.nonce, encryptedBinary = _a.binary;
170
- return __awaiter(_this, void 0, void 0, function () {
171
- return __generator(this, function (_b) {
172
- return [2 /*return*/, tweetnacl_1.default.box.open(encryptedBinary, (0, tweetnacl_util_1.decodeBase64)(nonce), (0, tweetnacl_util_1.decodeBase64)(submissionPublicKey), (0, tweetnacl_util_1.decodeBase64)(formSecretKey))];
173
- });
174
- });
175
- };
176
153
  /**
177
154
  * Decrypts an encrypted submission, and also download and decrypt any attachments alongside it.
178
155
  * @param formSecretKey Secret key as a base-64 string
@@ -180,7 +157,7 @@ var Crypto = /** @class */ (function () {
180
157
  * @returns A promise of the decrypted submission, including attachments (if any). Or else returns null if a decryption error decrypting any part of the submission.
181
158
  * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
182
159
  */
183
- this.decryptWithAttachments = function (formSecretKey, decryptParams) { return __awaiter(_this, void 0, void 0, function () {
160
+ _this.decryptWithAttachments = function (formSecretKey, decryptParams) { return __awaiter(_this, void 0, void 0, function () {
184
161
  var decryptedRecords, filenames, attachmentRecords, decryptedContent, fieldIds, downloadPromises, _a;
185
162
  var _this = this;
186
163
  var _b;
@@ -248,8 +225,9 @@ var Crypto = /** @class */ (function () {
248
225
  }
249
226
  });
250
227
  }); };
251
- this.signingPublicKey = signingPublicKey;
228
+ _this.signingPublicKey = signingPublicKey;
229
+ return _this;
252
230
  }
253
231
  return Crypto;
254
- }());
232
+ }(crypto_base_1.default));
255
233
  exports.default = Crypto;
package/dist/index.d.ts CHANGED
@@ -1,4 +1,5 @@
1
1
  import Crypto from './crypto';
2
+ import CryptoV3 from './crypto-v3';
2
3
  import { PackageInitParams } from './types';
3
4
  import Verification from './verification';
4
5
  import Webhooks from './webhooks';
@@ -13,6 +14,7 @@ import Webhooks from './webhooks';
13
14
  declare const _default: (config?: PackageInitParams) => {
14
15
  webhooks: Webhooks;
15
16
  crypto: Crypto;
17
+ cryptoV3: CryptoV3;
16
18
  verification: Verification;
17
19
  };
18
20
  export = _default;
package/dist/index.js CHANGED
@@ -4,6 +4,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
4
4
  };
5
5
  var publicKey_1 = require("./util/publicKey");
6
6
  var crypto_1 = __importDefault(require("./crypto"));
7
+ var crypto_v3_1 = __importDefault(require("./crypto-v3"));
7
8
  var verification_1 = __importDefault(require("./verification"));
8
9
  var webhooks_1 = __importDefault(require("./webhooks"));
9
10
  module.exports = function (config) {
@@ -24,6 +25,7 @@ module.exports = function (config) {
24
25
  secretKey: webhookSecretKey,
25
26
  }),
26
27
  crypto: new crypto_1.default({ signingPublicKey: signingPublicKey }),
28
+ cryptoV3: new crypto_v3_1.default(),
27
29
  verification: new verification_1.default({
28
30
  publicKey: verificationPublicKey,
29
31
  secretKey: verificationOptions === null || verificationOptions === void 0 ? void 0 : verificationOptions.secretKey,
package/dist/types.d.ts CHANGED
@@ -6,7 +6,7 @@ export type PackageInitParams = {
6
6
  /** Initializes public key used for verifying and decrypting in this package. If not given, will default to "production". */
7
7
  mode?: PackageMode;
8
8
  };
9
- export type FieldType = 'section' | 'radiobutton' | 'dropdown' | 'checkbox' | 'nric' | 'email' | 'table' | 'number' | 'rating' | 'yes_no' | 'decimal' | 'textfield' | 'textarea' | 'attachment' | 'date' | 'mobile' | 'homeno';
9
+ export type FieldType = 'section' | 'radiobutton' | 'dropdown' | 'checkbox' | 'nric' | 'email' | 'table' | 'number' | 'rating' | 'yes_no' | 'decimal' | 'textfield' | 'textarea' | 'attachment' | 'date' | 'mobile' | 'homeno' | 'statement' | 'image' | 'country_region' | 'uen' | 'children';
10
10
  export type FormField = {
11
11
  _id: string;
12
12
  question: string;
@@ -20,7 +20,17 @@ export type FormField = {
20
20
  answer?: never;
21
21
  answerArray: string[] | string[][];
22
22
  });
23
+ export type FormFieldsV3 = Record<string, {
24
+ fieldType: FieldType;
25
+ answer: any;
26
+ }>;
23
27
  export type EncryptedContent = string;
28
+ export type EncryptedContentV3 = {
29
+ submissionPublicKey: string;
30
+ submissionSecretKey: string;
31
+ encryptedContent: EncryptedContent;
32
+ encryptedSubmissionSecretKey: EncryptedContent;
33
+ };
24
34
  export type EncryptedAttachmentRecords = Record<string, string>;
25
35
  export interface DecryptParams {
26
36
  encryptedContent: EncryptedContent;
@@ -28,10 +38,19 @@ export interface DecryptParams {
28
38
  verifiedContent?: EncryptedContent;
29
39
  attachmentDownloadUrls?: EncryptedAttachmentRecords;
30
40
  }
41
+ export interface DecryptParamsV3 {
42
+ encryptedContent: EncryptedContent;
43
+ encryptedSubmissionSecretKey: EncryptedContent;
44
+ version: number;
45
+ }
31
46
  export type DecryptedContent = {
32
47
  responses: FormField[];
33
48
  verified?: Record<string, any>;
34
49
  };
50
+ export type DecryptedContentV3 = {
51
+ submissionSecretKey: string;
52
+ responses: FormFieldsV3;
53
+ };
35
54
  export type DecryptedFile = {
36
55
  filename: string;
37
56
  content: Uint8Array;
@@ -1,4 +1,4 @@
1
- import { Keypair, EncryptedContent, EncryptedAttachmentContent, EncryptedFileContent } from '../types';
1
+ import { EncryptedAttachmentContent, EncryptedContent, EncryptedFileContent, Keypair } from '../types';
2
2
  /**
3
3
  * Helper method to generate a new keypair for encryption.
4
4
  * @returns The generated keypair.
@@ -1,3 +1,4 @@
1
- import { FormField } from '../types';
1
+ import { FormField, FormFieldsV3 } from '../types';
2
2
  declare function determineIsFormFields(tbd: any): tbd is FormField[];
3
- export { determineIsFormFields };
3
+ declare function determineIsFormFieldsV3(tbd: any): tbd is FormFieldsV3;
4
+ export { determineIsFormFields, determineIsFormFieldsV3 };
@@ -1,6 +1,6 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.determineIsFormFields = void 0;
3
+ exports.determineIsFormFieldsV3 = exports.determineIsFormFields = void 0;
4
4
  function determineIsFormFields(tbd) {
5
5
  if (!Array.isArray(tbd)) {
6
6
  return false;
@@ -22,3 +22,15 @@ function determineIsFormFields(tbd) {
22
22
  return filter.length === tbd.length;
23
23
  }
24
24
  exports.determineIsFormFields = determineIsFormFields;
25
+ // TODO(MRF): This is currently very rudimentary, we should look at making this more specific where required.
26
+ function determineIsFormFieldsV3(tbd) {
27
+ for (var _i = 0, _a = Object.keys(tbd); _i < _a.length; _i++) {
28
+ var id = _a[_i];
29
+ var value = tbd[id];
30
+ var hasCorrectShape = value.fieldType && value.answer !== undefined;
31
+ if (!hasCorrectShape)
32
+ return false;
33
+ }
34
+ return true;
35
+ }
36
+ exports.determineIsFormFieldsV3 = determineIsFormFieldsV3;
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@opengovsg/formsg-sdk",
3
- "version": "0.11.0",
3
+ "version": "0.12.0-alpha.1",
4
4
  "repository": {
5
5
  "type": "git",
6
6
  "url": "https://github.com/opengovsg/formsg-javascript-sdk.git"
@@ -9,7 +9,7 @@
9
9
  "main": "./dist/index.js",
10
10
  "types": "./dist/index.d.ts",
11
11
  "scripts": {
12
- "test": "jest",
12
+ "test": "NODE_OPTIONS=\"--max-old-space-size=8192\" jest",
13
13
  "test-ci": "jest --coverage",
14
14
  "test-watch": "jest --watch",
15
15
  "build": "tsc",