@opengovsg/formsg-sdk 0.16.1 → 7.1.4

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.
Files changed (79) hide show
  1. package/README.md +8 -9
  2. package/cjs-entry.cjs +5 -0
  3. package/dist/{crypto-base.js → cjs/crypto-base.js} +7 -9
  4. package/dist/{crypto.js → cjs/crypto.js} +2 -2
  5. package/dist/{index.d.ts → cjs/index.d.ts} +2 -2
  6. package/dist/{index.js → cjs/index.js} +12 -2
  7. package/dist/{util → cjs/util}/publicKey.js +2 -3
  8. package/dist/{util → cjs/util}/signature.js +19 -10
  9. package/dist/{util → cjs/util}/validate.js +2 -3
  10. package/dist/{util → cjs/util}/webhooks.js +17 -7
  11. package/dist/{webhooks.js → cjs/webhooks.js} +17 -7
  12. package/dist/esm/crypto-base.d.ts +25 -0
  13. package/dist/esm/crypto-base.js +86 -0
  14. package/dist/esm/crypto-v3.d.ts +41 -0
  15. package/dist/esm/crypto-v3.js +168 -0
  16. package/dist/esm/crypto.d.ts +41 -0
  17. package/dist/esm/crypto.js +228 -0
  18. package/dist/esm/errors.d.ts +13 -0
  19. package/dist/esm/errors.js +64 -0
  20. package/dist/esm/index.d.ts +20 -0
  21. package/dist/esm/index.js +39 -0
  22. package/dist/esm/resource/signing-keys.d.ts +16 -0
  23. package/dist/esm/resource/signing-keys.js +19 -0
  24. package/dist/esm/resource/verification-keys.d.ts +16 -0
  25. package/dist/esm/resource/verification-keys.js +19 -0
  26. package/dist/esm/types.d.ts +101 -0
  27. package/dist/esm/types.js +1 -0
  28. package/dist/esm/util/crypto.d.ts +42 -0
  29. package/dist/esm/util/crypto.js +74 -0
  30. package/dist/esm/util/parser.d.ts +24 -0
  31. package/dist/esm/util/parser.js +35 -0
  32. package/dist/esm/util/publicKey.d.ts +14 -0
  33. package/dist/esm/util/publicKey.js +38 -0
  34. package/dist/esm/util/signature.d.ts +16 -0
  35. package/dist/esm/util/signature.js +22 -0
  36. package/dist/esm/util/stage.d.ts +5 -0
  37. package/dist/esm/util/stage.js +7 -0
  38. package/dist/esm/util/validate.d.ts +4 -0
  39. package/dist/esm/util/validate.js +32 -0
  40. package/dist/esm/util/webhooks.d.ts +20 -0
  41. package/dist/esm/util/webhooks.js +33 -0
  42. package/dist/esm/verification/index.d.ts +18 -0
  43. package/dist/esm/verification/index.js +77 -0
  44. package/dist/esm/verification/utils.d.ts +12 -0
  45. package/dist/esm/verification/utils.js +18 -0
  46. package/dist/esm/webhooks.d.ts +48 -0
  47. package/dist/esm/webhooks.js +75 -0
  48. package/package.json +48 -32
  49. package/.eslintrc +0 -75
  50. package/.github/workflows/ci.yml +0 -53
  51. package/.github/workflows/publish.yml +0 -25
  52. package/CHANGELOG.md +0 -190
  53. /package/dist/{crypto-base.d.ts → cjs/crypto-base.d.ts} +0 -0
  54. /package/dist/{crypto-v3.d.ts → cjs/crypto-v3.d.ts} +0 -0
  55. /package/dist/{crypto-v3.js → cjs/crypto-v3.js} +0 -0
  56. /package/dist/{crypto.d.ts → cjs/crypto.d.ts} +0 -0
  57. /package/dist/{errors.d.ts → cjs/errors.d.ts} +0 -0
  58. /package/dist/{errors.js → cjs/errors.js} +0 -0
  59. /package/dist/{resource → cjs/resource}/signing-keys.d.ts +0 -0
  60. /package/dist/{resource → cjs/resource}/signing-keys.js +0 -0
  61. /package/dist/{resource → cjs/resource}/verification-keys.d.ts +0 -0
  62. /package/dist/{resource → cjs/resource}/verification-keys.js +0 -0
  63. /package/dist/{types.d.ts → cjs/types.d.ts} +0 -0
  64. /package/dist/{types.js → cjs/types.js} +0 -0
  65. /package/dist/{util → cjs/util}/crypto.d.ts +0 -0
  66. /package/dist/{util → cjs/util}/crypto.js +0 -0
  67. /package/dist/{util → cjs/util}/parser.d.ts +0 -0
  68. /package/dist/{util → cjs/util}/parser.js +0 -0
  69. /package/dist/{util → cjs/util}/publicKey.d.ts +0 -0
  70. /package/dist/{util → cjs/util}/signature.d.ts +0 -0
  71. /package/dist/{util → cjs/util}/stage.d.ts +0 -0
  72. /package/dist/{util → cjs/util}/stage.js +0 -0
  73. /package/dist/{util → cjs/util}/validate.d.ts +0 -0
  74. /package/dist/{util → cjs/util}/webhooks.d.ts +0 -0
  75. /package/dist/{verification → cjs/verification}/index.d.ts +0 -0
  76. /package/dist/{verification → cjs/verification}/index.js +0 -0
  77. /package/dist/{verification → cjs/verification}/utils.d.ts +0 -0
  78. /package/dist/{verification → cjs/verification}/utils.js +0 -0
  79. /package/dist/{webhooks.d.ts → cjs/webhooks.d.ts} +0 -0
@@ -0,0 +1,41 @@
1
+ import CryptoBase from './crypto-base';
2
+ import { DecryptedContent, DecryptedContentAndAttachments, DecryptParams, EncryptedContent } from './types';
3
+ export default class Crypto extends CryptoBase {
4
+ signingPublicKey?: string;
5
+ constructor({ signingPublicKey }?: {
6
+ signingPublicKey?: string;
7
+ });
8
+ /**
9
+ * Encrypt input with a unique keypair for each submission
10
+ * @param encryptionPublicKey The base-64 encoded public key for encrypting.
11
+ * @param msg The message to encrypt, will be stringified.
12
+ * @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.
13
+ * @returns The encrypted basestring.
14
+ */
15
+ encrypt: (msg: any, encryptionPublicKey: string, signingPrivateKey?: string) => EncryptedContent;
16
+ /**
17
+ * Decrypts an encrypted submission and returns it.
18
+ * @param formSecretKey The base-64 secret key of the form to decrypt with.
19
+ * @param decryptParams The params containing encrypted content and information.
20
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
21
+ * @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with.
22
+ * @param decryptParams.verifiedContent Optional. The encrypted and signed verified content. If given, the signingPublicKey will be used to attempt to open the signed message.
23
+ * @returns The decrypted content if successful. Else, null will be returned.
24
+ * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
25
+ */
26
+ decrypt: (formSecretKey: string, decryptParams: DecryptParams) => DecryptedContent | null;
27
+ /**
28
+ * Returns true if a pair of public & secret keys are associated with each other
29
+ * @param publicKey The public key to verify against.
30
+ * @param secretKey The private key to verify against.
31
+ */
32
+ valid: (publicKey: string, secretKey: string) => boolean;
33
+ /**
34
+ * Decrypts an encrypted submission, and also download and decrypt any attachments alongside it.
35
+ * @param formSecretKey Secret key as a base-64 string
36
+ * @param decryptParams The params containing encrypted content and information.
37
+ * @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.
38
+ * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
39
+ */
40
+ decryptWithAttachments: (formSecretKey: string, decryptParams: DecryptParams) => Promise<DecryptedContentAndAttachments | null>;
41
+ }
@@ -0,0 +1,228 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ if (typeof b !== "function" && b !== null)
10
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
+ extendStatics(d, b);
12
+ function __() { this.constructor = d; }
13
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
+ };
15
+ })();
16
+ var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) {
17
+ function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); }
18
+ return new (P || (P = Promise))(function (resolve, reject) {
19
+ function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } }
20
+ function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } }
21
+ function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); }
22
+ step((generator = generator.apply(thisArg, _arguments || [])).next());
23
+ });
24
+ };
25
+ var __generator = (this && this.__generator) || function (thisArg, body) {
26
+ var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g = Object.create((typeof Iterator === "function" ? Iterator : Object).prototype);
27
+ return g.next = verb(0), g["throw"] = verb(1), g["return"] = verb(2), typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g;
28
+ function verb(n) { return function (v) { return step([n, v]); }; }
29
+ function step(op) {
30
+ if (f) throw new TypeError("Generator is already executing.");
31
+ while (g && (g = 0, op[0] && (_ = 0)), _) try {
32
+ 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;
33
+ if (y = 0, t) op = [op[0] & 2, t.value];
34
+ switch (op[0]) {
35
+ case 0: case 1: t = op; break;
36
+ case 4: _.label++; return { value: op[1], done: false };
37
+ case 5: _.label++; y = op[1]; op = [0]; continue;
38
+ case 7: op = _.ops.pop(); _.trys.pop(); continue;
39
+ default:
40
+ if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; }
41
+ if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; }
42
+ if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; }
43
+ if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; }
44
+ if (t[2]) _.ops.pop();
45
+ _.trys.pop(); continue;
46
+ }
47
+ op = body.call(thisArg, _);
48
+ } catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; }
49
+ if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true };
50
+ }
51
+ };
52
+ import axios from 'axios';
53
+ import nacl from 'tweetnacl';
54
+ import { decodeBase64, decodeUTF8, encodeUTF8 } from 'tweetnacl-util';
55
+ import { areAttachmentFieldIdsValid, convertEncryptedAttachmentToFileContent, decryptContent, encryptMessage, verifySignedMessage, } from './util/crypto';
56
+ import { determineIsFormFields } from './util/validate';
57
+ import CryptoBase from './crypto-base';
58
+ import { AttachmentDecryptionError, MissingPublicKeyError } from './errors';
59
+ var Crypto = /** @class */ (function (_super) {
60
+ __extends(Crypto, _super);
61
+ function Crypto(_a) {
62
+ var _b = _a === void 0 ? {} : _a, signingPublicKey = _b.signingPublicKey;
63
+ var _this = _super.call(this) || this;
64
+ /**
65
+ * Encrypt input with a unique keypair for each submission
66
+ * @param encryptionPublicKey The base-64 encoded public key for encrypting.
67
+ * @param msg The message to encrypt, will be stringified.
68
+ * @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.
69
+ * @returns The encrypted basestring.
70
+ */
71
+ _this.encrypt = function (msg, encryptionPublicKey, signingPrivateKey) {
72
+ var processedMsg = decodeUTF8(JSON.stringify(msg));
73
+ if (signingPrivateKey) {
74
+ processedMsg = nacl.sign(processedMsg, decodeBase64(signingPrivateKey));
75
+ }
76
+ return encryptMessage(processedMsg, encryptionPublicKey);
77
+ };
78
+ /**
79
+ * Decrypts an encrypted submission and returns it.
80
+ * @param formSecretKey The base-64 secret key of the form to decrypt with.
81
+ * @param decryptParams The params containing encrypted content and information.
82
+ * @param decryptParams.encryptedContent The encrypted content encoded with base-64.
83
+ * @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with.
84
+ * @param decryptParams.verifiedContent Optional. The encrypted and signed verified content. If given, the signingPublicKey will be used to attempt to open the signed message.
85
+ * @returns The decrypted content if successful. Else, null will be returned.
86
+ * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
87
+ */
88
+ _this.decrypt = function (formSecretKey, decryptParams) {
89
+ try {
90
+ var encryptedContent = decryptParams.encryptedContent, verifiedContent = decryptParams.verifiedContent;
91
+ // Do not return the transformed object in `_decrypt` function as a signed
92
+ // object is not encoded in UTF8 and is encoded in Base-64 instead.
93
+ var decryptedContent = decryptContent(formSecretKey, encryptedContent);
94
+ if (!decryptedContent) {
95
+ throw new Error('Failed to decrypt content');
96
+ }
97
+ var decryptedObject = JSON.parse(encodeUTF8(decryptedContent));
98
+ if (!determineIsFormFields(decryptedObject)) {
99
+ throw new Error('Decrypted object does not fit expected shape');
100
+ }
101
+ var returnedObject = {
102
+ responses: decryptedObject,
103
+ };
104
+ if (verifiedContent) {
105
+ if (!_this.signingPublicKey) {
106
+ throw new MissingPublicKeyError('Public signing key must be provided when instantiating the Crypto class in order to verify verified content');
107
+ }
108
+ // Only care if it is the correct shape if verifiedContent exists, since
109
+ // we need to append it to the end.
110
+ // Decrypted message must be able to be authenticated by the public key.
111
+ var decryptedVerifiedContent = decryptContent(formSecretKey, verifiedContent);
112
+ if (!decryptedVerifiedContent) {
113
+ // Returns null if decrypting verified content failed.
114
+ throw new Error('Failed to decrypt verified content');
115
+ }
116
+ var decryptedVerifiedObject = verifySignedMessage(decryptedVerifiedContent, _this.signingPublicKey);
117
+ returnedObject.verified = decryptedVerifiedObject;
118
+ }
119
+ return returnedObject;
120
+ }
121
+ catch (err) {
122
+ // Should only throw if MissingPublicKeyError.
123
+ // This library should be able to be used to encrypt and decrypt content
124
+ // if the content does not contain verified fields.
125
+ if (err instanceof MissingPublicKeyError) {
126
+ throw err;
127
+ }
128
+ return null;
129
+ }
130
+ };
131
+ /**
132
+ * Returns true if a pair of public & secret keys are associated with each other
133
+ * @param publicKey The public key to verify against.
134
+ * @param secretKey The private key to verify against.
135
+ */
136
+ _this.valid = function (publicKey, secretKey) {
137
+ var _a;
138
+ var testResponse = [];
139
+ var internalValidationVersion = 1;
140
+ var cipherResponse = _this.encrypt(testResponse, publicKey);
141
+ // Use toString here since the return should be an empty array.
142
+ return (testResponse.toString() ===
143
+ ((_a = _this.decrypt(secretKey, {
144
+ encryptedContent: cipherResponse,
145
+ version: internalValidationVersion,
146
+ })) === null || _a === void 0 ? void 0 : _a.responses.toString()));
147
+ };
148
+ /**
149
+ * Decrypts an encrypted submission, and also download and decrypt any attachments alongside it.
150
+ * @param formSecretKey Secret key as a base-64 string
151
+ * @param decryptParams The params containing encrypted content and information.
152
+ * @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.
153
+ * @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content.
154
+ */
155
+ _this.decryptWithAttachments = function (formSecretKey, decryptParams) { return __awaiter(_this, void 0, void 0, function () {
156
+ var decryptedRecords, filenames, attachmentRecords, decryptedContent, fieldIds, downloadPromises, _a;
157
+ var _this = this;
158
+ var _b;
159
+ return __generator(this, function (_c) {
160
+ switch (_c.label) {
161
+ case 0:
162
+ decryptedRecords = {};
163
+ filenames = {};
164
+ attachmentRecords = (_b = decryptParams.attachmentDownloadUrls) !== null && _b !== void 0 ? _b : {};
165
+ decryptedContent = this.decrypt(formSecretKey, decryptParams);
166
+ if (decryptedContent === null)
167
+ return [2 /*return*/, null
168
+ // Retrieve all original filenames for attachments for easy lookup
169
+ ];
170
+ // Retrieve all original filenames for attachments for easy lookup
171
+ decryptedContent.responses.forEach(function (response) {
172
+ if (response.fieldType === 'attachment' && response.answer) {
173
+ filenames[response._id] = response.answer;
174
+ }
175
+ });
176
+ fieldIds = Object.keys(attachmentRecords);
177
+ // Check if all fieldIds are within filenames
178
+ if (!areAttachmentFieldIdsValid(fieldIds, filenames)) {
179
+ return [2 /*return*/, null];
180
+ }
181
+ downloadPromises = fieldIds.map(function (fieldId) {
182
+ return (axios
183
+ // Retrieve all the attachments as JSON
184
+ .get(attachmentRecords[fieldId], {
185
+ responseType: 'json',
186
+ })
187
+ // Decrypt all the attachments
188
+ .then(function (_a) {
189
+ var downloadResponse = _a.data;
190
+ var encryptedFile = convertEncryptedAttachmentToFileContent(downloadResponse);
191
+ return _this.decryptFile(formSecretKey, encryptedFile);
192
+ })
193
+ .then(function (decryptedFile) {
194
+ // Check if the file exists and set the filename accordingly; otherwise, throw an error
195
+ if (decryptedFile) {
196
+ decryptedRecords[fieldId] = {
197
+ filename: filenames[fieldId],
198
+ content: decryptedFile,
199
+ };
200
+ }
201
+ else {
202
+ throw new AttachmentDecryptionError();
203
+ }
204
+ }));
205
+ });
206
+ _c.label = 1;
207
+ case 1:
208
+ _c.trys.push([1, 3, , 4]);
209
+ return [4 /*yield*/, Promise.all(downloadPromises)];
210
+ case 2:
211
+ _c.sent();
212
+ return [3 /*break*/, 4];
213
+ case 3:
214
+ _a = _c.sent();
215
+ return [2 /*return*/, null];
216
+ case 4: return [2 /*return*/, {
217
+ content: decryptedContent,
218
+ attachments: decryptedRecords,
219
+ }];
220
+ }
221
+ });
222
+ }); };
223
+ _this.signingPublicKey = signingPublicKey;
224
+ return _this;
225
+ }
226
+ return Crypto;
227
+ }(CryptoBase));
228
+ export default Crypto;
@@ -0,0 +1,13 @@
1
+ declare class MissingSecretKeyError extends Error {
2
+ constructor(message?: string);
3
+ }
4
+ declare class MissingPublicKeyError extends Error {
5
+ constructor(message?: string);
6
+ }
7
+ declare class WebhookAuthenticateError extends Error {
8
+ constructor(message: string);
9
+ }
10
+ declare class AttachmentDecryptionError extends Error {
11
+ constructor(message?: string);
12
+ }
13
+ export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, AttachmentDecryptionError, };
@@ -0,0 +1,64 @@
1
+ var __extends = (this && this.__extends) || (function () {
2
+ var extendStatics = function (d, b) {
3
+ extendStatics = Object.setPrototypeOf ||
4
+ ({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
5
+ function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
6
+ return extendStatics(d, b);
7
+ };
8
+ return function (d, b) {
9
+ if (typeof b !== "function" && b !== null)
10
+ throw new TypeError("Class extends value " + String(b) + " is not a constructor or null");
11
+ extendStatics(d, b);
12
+ function __() { this.constructor = d; }
13
+ d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
14
+ };
15
+ })();
16
+ var MissingSecretKeyError = /** @class */ (function (_super) {
17
+ __extends(MissingSecretKeyError, _super);
18
+ function MissingSecretKeyError(message) {
19
+ if (message === void 0) { message = 'Provide a secret key when initializing the FormSG SDK to use this function.'; }
20
+ var _this = _super.call(this, message) || this;
21
+ _this.name = _this.constructor.name;
22
+ // Set the prototype explicitly.
23
+ // See https://github.com/facebook/jest/issues/8279
24
+ Object.setPrototypeOf(_this, MissingSecretKeyError.prototype);
25
+ return _this;
26
+ }
27
+ return MissingSecretKeyError;
28
+ }(Error));
29
+ var MissingPublicKeyError = /** @class */ (function (_super) {
30
+ __extends(MissingPublicKeyError, _super);
31
+ function MissingPublicKeyError(message) {
32
+ if (message === void 0) { message = 'Provide a public key when initializing the FormSG SDK to use this function.'; }
33
+ var _this = _super.call(this, message) || this;
34
+ _this.name = _this.constructor.name;
35
+ // Set the prototype explicitly.
36
+ // See https://github.com/facebook/jest/issues/8279
37
+ Object.setPrototypeOf(_this, MissingPublicKeyError.prototype);
38
+ return _this;
39
+ }
40
+ return MissingPublicKeyError;
41
+ }(Error));
42
+ var WebhookAuthenticateError = /** @class */ (function (_super) {
43
+ __extends(WebhookAuthenticateError, _super);
44
+ function WebhookAuthenticateError(message) {
45
+ var _this = _super.call(this, message) || this;
46
+ _this.name = _this.constructor.name;
47
+ // Set the prototype explicitly.
48
+ // See https://github.com/facebook/jest/issues/8279
49
+ Object.setPrototypeOf(_this, WebhookAuthenticateError.prototype);
50
+ return _this;
51
+ }
52
+ return WebhookAuthenticateError;
53
+ }(Error));
54
+ var AttachmentDecryptionError = /** @class */ (function (_super) {
55
+ __extends(AttachmentDecryptionError, _super);
56
+ function AttachmentDecryptionError(message) {
57
+ if (message === void 0) { message = 'Attachment decryption with the given nonce failed.'; }
58
+ var _this = _super.call(this, message) || this;
59
+ _this.name = _this.constructor.name;
60
+ return _this;
61
+ }
62
+ return AttachmentDecryptionError;
63
+ }(Error));
64
+ export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, AttachmentDecryptionError, };
@@ -0,0 +1,20 @@
1
+ export type { DecryptedContent, DecryptedContentV3, DecryptParams, DecryptParamsV3, EncryptedAttachmentContent, EncryptedAttachmentRecords, EncryptedContent, EncryptedContentV3, EncryptedFileContent, FieldType, FormField, FormFieldsV3, Keypair, PackageMode, VerificationOptions, } from './types';
2
+ import Crypto from './crypto';
3
+ import CryptoV3 from './crypto-v3';
4
+ import { PackageInitParams } from './types';
5
+ import Verification from './verification';
6
+ import Webhooks from './webhooks';
7
+ /**
8
+ * Entrypoint into the FormSG SDK
9
+ *
10
+ * @param {PackageInitParams} config Package initialization config parameters
11
+ * @param {string?} [config.mode] Optional. Initializes public key used for verifying and decrypting in this package. If `config.signingPublicKey` is given, this param will be ignored.
12
+ * @param {string?} [config.webhookSecretKey] Optional. base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data.
13
+ * @param {VerificationOptions?} [config.verificationOptions] Optional. If provided, enables the usage of the verification module.
14
+ */
15
+ export default function (config?: PackageInitParams): {
16
+ webhooks: Webhooks;
17
+ crypto: Crypto;
18
+ cryptoV3: CryptoV3;
19
+ verification: Verification;
20
+ };
@@ -0,0 +1,39 @@
1
+ import { getSigningPublicKey, getVerificationPublicKey } from './util/publicKey';
2
+ import Crypto from './crypto';
3
+ import CryptoV3 from './crypto-v3';
4
+ import Verification from './verification';
5
+ import Webhooks from './webhooks';
6
+ /**
7
+ * Entrypoint into the FormSG SDK
8
+ *
9
+ * @param {PackageInitParams} config Package initialization config parameters
10
+ * @param {string?} [config.mode] Optional. Initializes public key used for verifying and decrypting in this package. If `config.signingPublicKey` is given, this param will be ignored.
11
+ * @param {string?} [config.webhookSecretKey] Optional. base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data.
12
+ * @param {VerificationOptions?} [config.verificationOptions] Optional. If provided, enables the usage of the verification module.
13
+ */
14
+ export default function (config) {
15
+ if (config === void 0) { config = {}; }
16
+ var webhookSecretKey = config.webhookSecretKey, mode = config.mode, verificationOptions = config.verificationOptions;
17
+ /**
18
+ * Public key is used for decrypting signed verified content in the `crypto` module, and
19
+ * also for verifying webhook signatures' authenticity in the `wehbooks` module.
20
+ */
21
+ var signingPublicKey = getSigningPublicKey(mode || 'production');
22
+ /**
23
+ * Public key is used for verifying verified field signatures' authenticity in the `verification` module.
24
+ */
25
+ var verificationPublicKey = getVerificationPublicKey(mode || 'production');
26
+ return {
27
+ webhooks: new Webhooks({
28
+ publicKey: signingPublicKey,
29
+ secretKey: webhookSecretKey,
30
+ }),
31
+ crypto: new Crypto({ signingPublicKey: signingPublicKey }),
32
+ cryptoV3: new CryptoV3({ signingPublicKey: signingPublicKey }),
33
+ verification: new Verification({
34
+ publicKey: verificationPublicKey,
35
+ secretKey: verificationOptions === null || verificationOptions === void 0 ? void 0 : verificationOptions.secretKey,
36
+ transactionExpiry: verificationOptions === null || verificationOptions === void 0 ? void 0 : verificationOptions.transactionExpiry,
37
+ }),
38
+ };
39
+ }
@@ -0,0 +1,16 @@
1
+ export declare const SIGNING_KEYS: {
2
+ staging: {
3
+ publicKey: string;
4
+ };
5
+ development: {
6
+ publicKey: string;
7
+ secretKey: string;
8
+ };
9
+ production: {
10
+ publicKey: string;
11
+ };
12
+ test: {
13
+ publicKey: string;
14
+ secretKey: string;
15
+ };
16
+ };
@@ -0,0 +1,19 @@
1
+ // keys generated using nacl.sign.keyPair() from tweetnacl
2
+ export var SIGNING_KEYS = {
3
+ staging: {
4
+ // staging must never contain secret keys
5
+ publicKey: 'rjv41kYqZwcbe3r6ymMEEKQ+Vd+DPuogN+Gzq3lP2Og=',
6
+ },
7
+ development: {
8
+ publicKey: 'Tl5gfszlKcQj99/0uafLwVpT6JAu4C0dHGvLq1cHzFE=',
9
+ secretKey: 'HDBXpu+2/gu10bLHpy8HjpN89xbA6boH9GwibPGJA8BOXmB+zOUpxCP33/S5p8vBWlPokC7gLR0ca8urVwfMUQ==',
10
+ },
11
+ production: {
12
+ // production must never contain secret keys
13
+ publicKey: '3Tt8VduXsjjd4IrpdCd7BAkdZl/vUCstu9UvTX84FWw=',
14
+ },
15
+ test: {
16
+ publicKey: 'KUY1XT30ar+XreVjsS1w/c3EpDs2oASbF6G3evvaUJM=',
17
+ secretKey: '/u+LP57Ib9y5Ytpud56FzuitSC9O6lJ4EOLOFHpsHlYpRjVdPfRqv5et5WOxLXD9zcSkOzagBJsXobd6+9pQkw==',
18
+ },
19
+ };
@@ -0,0 +1,16 @@
1
+ export declare const VERIFICATION_KEYS: {
2
+ staging: {
3
+ publicKey: string;
4
+ };
5
+ development: {
6
+ publicKey: string;
7
+ secretKey: string;
8
+ };
9
+ production: {
10
+ publicKey: string;
11
+ };
12
+ test: {
13
+ publicKey: string;
14
+ secretKey: string;
15
+ };
16
+ };
@@ -0,0 +1,19 @@
1
+ // keys generated using nacl.sign.keyPair() from tweetnacl
2
+ export var VERIFICATION_KEYS = {
3
+ staging: {
4
+ // staging must never contain secret keys
5
+ publicKey: 'bDgK1223JbrDNePFIrj7b0z02Z5nSiBzkRYRqDdVPfA=',
6
+ },
7
+ development: {
8
+ publicKey: 'SZ4pV0JXgj8dhFU69uHllqYcxTtliYmi+d6Ml56lnQU=',
9
+ secretKey: 'iGkfOuI6uxrlfw+7CZFFUZBwk86I+pu6v+g7EWA6qJpJnilXQleCPx2EVTr24eWWphzFO2WJiaL53oyXnqWdBQ==',
10
+ },
11
+ production: {
12
+ // production must never contain secret keys
13
+ publicKey: 'W/lf24kRJ9PVvSK1Ubjjhc9zHjp1amKr+3Q+Nmsy4w4=',
14
+ },
15
+ test: {
16
+ publicKey: 'ileDo328P/UApBPANuS/xO6P4BuHSgPnjRRBifgQYvs=',
17
+ secretKey: 'zLnXIV0cGjODell5w1usEHcGOJ/xsQDuDOw2BPcPEQOKV4Ojfbw/9QCkE8A25L/E7o/gG4dKA+eNFEGJ+BBi+w==',
18
+ },
19
+ };
@@ -0,0 +1,101 @@
1
+ export type PackageInitParams = {
2
+ /** base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data. */
3
+ webhookSecretKey?: string;
4
+ /** If provided, enables the usage of the verification module. */
5
+ verificationOptions?: VerificationOptions;
6
+ /** Initializes public key used for verifying and decrypting in this package. If not given, will default to "production". */
7
+ mode?: PackageMode;
8
+ };
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' | 'address' | 'signature';
10
+ export type FormField = {
11
+ _id: string;
12
+ question: string;
13
+ fieldType: FieldType;
14
+ isHeader?: boolean;
15
+ signature?: string;
16
+ } & ({
17
+ answer: string;
18
+ answerArray?: never;
19
+ } | {
20
+ answer?: never;
21
+ answerArray: string[] | string[][];
22
+ });
23
+ export type FormFieldsV3 = Record<string, {
24
+ fieldType: FieldType;
25
+ answer: any;
26
+ }>;
27
+ export type EncryptedContent = string;
28
+ export type EncryptedContentV3 = {
29
+ submissionPublicKey: string;
30
+ submissionSecretKey: string;
31
+ encryptedContent: EncryptedContent;
32
+ encryptedSubmissionSecretKey: EncryptedContent;
33
+ };
34
+ export type EncryptedAttachmentRecords = Record<string, string>;
35
+ export interface DecryptParams {
36
+ encryptedContent: EncryptedContent;
37
+ version: number;
38
+ verifiedContent?: EncryptedContent;
39
+ attachmentDownloadUrls?: EncryptedAttachmentRecords;
40
+ }
41
+ export interface DecryptParamsV3 {
42
+ encryptedContent: EncryptedContent;
43
+ encryptedSubmissionSecretKey: EncryptedContent;
44
+ verifiedContent?: EncryptedContent;
45
+ version: number;
46
+ }
47
+ export type DecryptedContent = {
48
+ responses: FormField[];
49
+ verified?: Record<string, any>;
50
+ };
51
+ export type DecryptedContentV3 = {
52
+ submissionSecretKey: string;
53
+ responses: FormFieldsV3;
54
+ verified?: Record<string, any>;
55
+ };
56
+ export type DecryptedFile = {
57
+ filename: string;
58
+ content: Uint8Array;
59
+ };
60
+ export type DecryptedAttachments = Record<string, DecryptedFile>;
61
+ export type DecryptedContentAndAttachments = {
62
+ content: DecryptedContent;
63
+ attachments: DecryptedAttachments;
64
+ };
65
+ export type EncryptedFileContent = {
66
+ submissionPublicKey: string;
67
+ nonce: string;
68
+ binary: Uint8Array;
69
+ };
70
+ export type EncryptedAttachmentContent = {
71
+ encryptedFile: {
72
+ submissionPublicKey: string;
73
+ nonce: string;
74
+ binary: string;
75
+ };
76
+ };
77
+ export type Keypair = {
78
+ publicKey: string;
79
+ secretKey: string;
80
+ };
81
+ export type PackageMode = 'staging' | 'production' | 'development' | 'test';
82
+ export type VerificationOptions = {
83
+ publicKey?: string;
84
+ secretKey?: string;
85
+ transactionExpiry?: number;
86
+ };
87
+ export type VerifiedAnswer = {
88
+ fieldId: string;
89
+ answer: string;
90
+ };
91
+ export type VerificationSignatureOptions = VerifiedAnswer & {
92
+ transactionId: string;
93
+ formId: string;
94
+ };
95
+ export type VerificationBasestringOptions = VerificationSignatureOptions & {
96
+ time: number;
97
+ };
98
+ export type VerificationAuthenticateOptions = VerifiedAnswer & {
99
+ signatureString: string;
100
+ submissionCreatedAt: number;
101
+ };
@@ -0,0 +1 @@
1
+ export {};
@@ -0,0 +1,42 @@
1
+ import { EncryptedAttachmentContent, EncryptedContent, EncryptedFileContent, Keypair } from '../types';
2
+ /**
3
+ * Helper method to generate a new keypair for encryption.
4
+ * @returns The generated keypair.
5
+ */
6
+ export declare const generateKeypair: () => Keypair;
7
+ /**
8
+ * Helper function to encrypt input with a unique keypair for each submission.
9
+ * @param msg The message to encrypt
10
+ * @param theirPublicKey The base-64 encoded public key
11
+ * @returns The encrypted basestring
12
+ * @throws error if any of the encrypt methods fail
13
+ */
14
+ export declare const encryptMessage: (msg: Uint8Array, theirPublicKey: string) => EncryptedContent;
15
+ /**
16
+ * Helper method to decrypt an encrypted submission.
17
+ * @param formPrivateKey base64
18
+ * @param encryptedContent encrypted string encoded in base64
19
+ * @return The decrypted content, or null if decryption failed.
20
+ */
21
+ export declare const decryptContent: (formPrivateKey: string, encryptedContent: EncryptedContent) => Uint8Array | null;
22
+ /**
23
+ * Helper method to verify a signed message.
24
+ * @param msg the message to verify
25
+ * @param publicKey the public key to authenticate the signed message with
26
+ * @returns the signed message if successful, else an error will be thrown
27
+ * @throws {Error} if the message cannot be verified
28
+ */
29
+ export declare const verifySignedMessage: (msg: Uint8Array, publicKey: string) => Record<string, any>;
30
+ /**
31
+ * Helper method to check if all the field IDs given are within the filenames
32
+ * @param fieldIds the list of fieldIds to check
33
+ * @param filenames the filenames that should contain the fields
34
+ * @returns boolean indicating whether the fields are valid
35
+ */
36
+ export declare const areAttachmentFieldIdsValid: (fieldIds: string[], filenames: Record<string, string>) => boolean;
37
+ /**
38
+ * Converts an encrypted attachment to encrypted file content
39
+ * @param encryptedAttachment The encrypted attachment
40
+ * @returns EncryptedFileContent The encrypted file content
41
+ */
42
+ export declare const convertEncryptedAttachmentToFileContent: (encryptedAttachment: EncryptedAttachmentContent) => EncryptedFileContent;