@opengovsg/formsg-sdk 0.10.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.
- package/.github/workflows/ci.yml +6 -4
- package/LICENSE.md +21 -0
- package/README.md +8 -1
- package/dist/crypto-base.d.ts +25 -0
- package/dist/crypto-base.js +93 -0
- package/dist/crypto-v3.d.ts +37 -0
- package/dist/crypto-v3.js +134 -0
- package/dist/crypto.d.ts +4 -25
- package/dist/crypto.js +43 -64
- package/dist/errors.js +4 -1
- package/dist/index.d.ts +7 -5
- package/dist/index.js +4 -2
- package/dist/resource/signing-keys.js +1 -0
- package/dist/resource/verification-keys.js +1 -0
- package/dist/types.d.ts +37 -18
- package/dist/util/crypto.d.ts +3 -3
- package/dist/util/crypto.js +22 -15
- package/dist/util/parser.d.ts +2 -2
- package/dist/util/parser.js +5 -2
- package/dist/util/publicKey.js +1 -0
- package/dist/util/signature.js +21 -4
- package/dist/util/validate.d.ts +3 -2
- package/dist/util/validate.js +13 -0
- package/dist/util/webhooks.js +21 -4
- package/dist/verification/index.js +9 -9
- package/dist/verification/utils.js +6 -3
- package/dist/webhooks.js +27 -11
- package/package.json +9 -9
package/dist/errors.js
CHANGED
|
@@ -3,16 +3,19 @@ var __extends = (this && this.__extends) || (function () {
|
|
|
3
3
|
var extendStatics = function (d, b) {
|
|
4
4
|
extendStatics = Object.setPrototypeOf ||
|
|
5
5
|
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) ||
|
|
6
|
-
function (d, b) { for (var p in b) if (
|
|
6
|
+
function (d, b) { for (var p in b) if (Object.prototype.hasOwnProperty.call(b, p)) d[p] = b[p]; };
|
|
7
7
|
return extendStatics(d, b);
|
|
8
8
|
};
|
|
9
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");
|
|
10
12
|
extendStatics(d, b);
|
|
11
13
|
function __() { this.constructor = d; }
|
|
12
14
|
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __());
|
|
13
15
|
};
|
|
14
16
|
})();
|
|
15
17
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
18
|
+
exports.AttachmentDecryptionError = exports.WebhookAuthenticateError = exports.MissingPublicKeyError = exports.MissingSecretKeyError = void 0;
|
|
16
19
|
var MissingSecretKeyError = /** @class */ (function (_super) {
|
|
17
20
|
__extends(MissingSecretKeyError, _super);
|
|
18
21
|
function MissingSecretKeyError(message) {
|
package/dist/index.d.ts
CHANGED
|
@@ -1,12 +1,8 @@
|
|
|
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';
|
|
5
|
-
declare const _default: (config?: PackageInitParams) => {
|
|
6
|
-
webhooks: Webhooks;
|
|
7
|
-
crypto: Crypto;
|
|
8
|
-
verification: Verification;
|
|
9
|
-
};
|
|
10
6
|
/**
|
|
11
7
|
* Entrypoint into the FormSG SDK
|
|
12
8
|
*
|
|
@@ -15,4 +11,10 @@ declare const _default: (config?: PackageInitParams) => {
|
|
|
15
11
|
* @param {string?} [config.webhookSecretKey] Optional. base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data.
|
|
16
12
|
* @param {VerificationOptions?} [config.verificationOptions] Optional. If provided, enables the usage of the verification module.
|
|
17
13
|
*/
|
|
14
|
+
declare const _default: (config?: PackageInitParams) => {
|
|
15
|
+
webhooks: Webhooks;
|
|
16
|
+
crypto: Crypto;
|
|
17
|
+
cryptoV3: CryptoV3;
|
|
18
|
+
verification: Verification;
|
|
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) {
|
|
@@ -13,17 +14,18 @@ module.exports = function (config) {
|
|
|
13
14
|
* Public key is used for decrypting signed verified content in the `crypto` module, and
|
|
14
15
|
* also for verifying webhook signatures' authenticity in the `wehbooks` module.
|
|
15
16
|
*/
|
|
16
|
-
var signingPublicKey = publicKey_1.getSigningPublicKey(mode || 'production');
|
|
17
|
+
var signingPublicKey = (0, publicKey_1.getSigningPublicKey)(mode || 'production');
|
|
17
18
|
/**
|
|
18
19
|
* Public key is used for verifying verified field signatures' authenticity in the `verification` module.
|
|
19
20
|
*/
|
|
20
|
-
var verificationPublicKey = publicKey_1.getVerificationPublicKey(mode || 'production');
|
|
21
|
+
var verificationPublicKey = (0, publicKey_1.getVerificationPublicKey)(mode || 'production');
|
|
21
22
|
return {
|
|
22
23
|
webhooks: new webhooks_1.default({
|
|
23
24
|
publicKey: signingPublicKey,
|
|
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
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type PackageInitParams = {
|
|
2
2
|
/** base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data. */
|
|
3
3
|
webhookSecretKey?: string;
|
|
4
4
|
/** If provided, enables the usage of the verification module. */
|
|
@@ -6,8 +6,8 @@ export declare 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
|
|
10
|
-
export
|
|
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
|
+
export type FormField = {
|
|
11
11
|
_id: string;
|
|
12
12
|
question: string;
|
|
13
13
|
fieldType: FieldType;
|
|
@@ -20,61 +20,80 @@ export declare type FormField = {
|
|
|
20
20
|
answer?: never;
|
|
21
21
|
answerArray: string[] | string[][];
|
|
22
22
|
});
|
|
23
|
-
export
|
|
24
|
-
|
|
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>;
|
|
25
35
|
export interface DecryptParams {
|
|
26
36
|
encryptedContent: EncryptedContent;
|
|
27
37
|
version: number;
|
|
28
38
|
verifiedContent?: EncryptedContent;
|
|
29
39
|
attachmentDownloadUrls?: EncryptedAttachmentRecords;
|
|
30
40
|
}
|
|
31
|
-
export
|
|
41
|
+
export interface DecryptParamsV3 {
|
|
42
|
+
encryptedContent: EncryptedContent;
|
|
43
|
+
encryptedSubmissionSecretKey: EncryptedContent;
|
|
44
|
+
version: number;
|
|
45
|
+
}
|
|
46
|
+
export type DecryptedContent = {
|
|
32
47
|
responses: FormField[];
|
|
33
48
|
verified?: Record<string, any>;
|
|
34
49
|
};
|
|
35
|
-
export
|
|
50
|
+
export type DecryptedContentV3 = {
|
|
51
|
+
submissionSecretKey: string;
|
|
52
|
+
responses: FormFieldsV3;
|
|
53
|
+
};
|
|
54
|
+
export type DecryptedFile = {
|
|
36
55
|
filename: string;
|
|
37
56
|
content: Uint8Array;
|
|
38
57
|
};
|
|
39
|
-
export
|
|
40
|
-
export
|
|
58
|
+
export type DecryptedAttachments = Record<string, DecryptedFile>;
|
|
59
|
+
export type DecryptedContentAndAttachments = {
|
|
41
60
|
content: DecryptedContent;
|
|
42
61
|
attachments: DecryptedAttachments;
|
|
43
62
|
};
|
|
44
|
-
export
|
|
63
|
+
export type EncryptedFileContent = {
|
|
45
64
|
submissionPublicKey: string;
|
|
46
65
|
nonce: string;
|
|
47
66
|
binary: Uint8Array;
|
|
48
67
|
};
|
|
49
|
-
export
|
|
68
|
+
export type EncryptedAttachmentContent = {
|
|
50
69
|
encryptedFile: {
|
|
51
70
|
submissionPublicKey: string;
|
|
52
71
|
nonce: string;
|
|
53
72
|
binary: string;
|
|
54
73
|
};
|
|
55
74
|
};
|
|
56
|
-
export
|
|
75
|
+
export type Keypair = {
|
|
57
76
|
publicKey: string;
|
|
58
77
|
secretKey: string;
|
|
59
78
|
};
|
|
60
|
-
export
|
|
61
|
-
export
|
|
79
|
+
export type PackageMode = 'staging' | 'production' | 'development' | 'test';
|
|
80
|
+
export type VerificationOptions = {
|
|
62
81
|
publicKey?: string;
|
|
63
82
|
secretKey?: string;
|
|
64
83
|
transactionExpiry?: number;
|
|
65
84
|
};
|
|
66
|
-
export
|
|
85
|
+
export type VerifiedAnswer = {
|
|
67
86
|
fieldId: string;
|
|
68
87
|
answer: string;
|
|
69
88
|
};
|
|
70
|
-
export
|
|
89
|
+
export type VerificationSignatureOptions = VerifiedAnswer & {
|
|
71
90
|
transactionId: string;
|
|
72
91
|
formId: string;
|
|
73
92
|
};
|
|
74
|
-
export
|
|
93
|
+
export type VerificationBasestringOptions = VerificationSignatureOptions & {
|
|
75
94
|
time: number;
|
|
76
95
|
};
|
|
77
|
-
export
|
|
96
|
+
export type VerificationAuthenticateOptions = VerifiedAnswer & {
|
|
78
97
|
signatureString: string;
|
|
79
98
|
submissionCreatedAt: number;
|
|
80
99
|
};
|
package/dist/util/crypto.d.ts
CHANGED
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import {
|
|
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.
|
|
@@ -11,14 +11,14 @@ export declare const generateKeypair: () => Keypair;
|
|
|
11
11
|
* @returns The encrypted basestring
|
|
12
12
|
* @throws error if any of the encrypt methods fail
|
|
13
13
|
*/
|
|
14
|
-
export declare const encryptMessage: (msg: Uint8Array, theirPublicKey: string) =>
|
|
14
|
+
export declare const encryptMessage: (msg: Uint8Array, theirPublicKey: string) => EncryptedContent;
|
|
15
15
|
/**
|
|
16
16
|
* Helper method to decrypt an encrypted submission.
|
|
17
17
|
* @param formPrivateKey base64
|
|
18
18
|
* @param encryptedContent encrypted string encoded in base64
|
|
19
19
|
* @return The decrypted content, or null if decryption failed.
|
|
20
20
|
*/
|
|
21
|
-
export declare const decryptContent: (formPrivateKey: string, encryptedContent:
|
|
21
|
+
export declare const decryptContent: (formPrivateKey: string, encryptedContent: EncryptedContent) => Uint8Array | null;
|
|
22
22
|
/**
|
|
23
23
|
* Helper method to verify a signed message.
|
|
24
24
|
* @param msg the message to verify
|
package/dist/util/crypto.js
CHANGED
|
@@ -3,19 +3,21 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.convertEncryptedAttachmentToFileContent = exports.areAttachmentFieldIdsValid = exports.verifySignedMessage = exports.decryptContent = exports.encryptMessage = exports.generateKeypair = void 0;
|
|
6
7
|
var tweetnacl_1 = __importDefault(require("tweetnacl"));
|
|
7
8
|
var tweetnacl_util_1 = require("tweetnacl-util");
|
|
8
9
|
/**
|
|
9
10
|
* Helper method to generate a new keypair for encryption.
|
|
10
11
|
* @returns The generated keypair.
|
|
11
12
|
*/
|
|
12
|
-
|
|
13
|
+
var generateKeypair = function () {
|
|
13
14
|
var kp = tweetnacl_1.default.box.keyPair();
|
|
14
15
|
return {
|
|
15
|
-
publicKey: tweetnacl_util_1.encodeBase64(kp.publicKey),
|
|
16
|
-
secretKey: tweetnacl_util_1.encodeBase64(kp.secretKey),
|
|
16
|
+
publicKey: (0, tweetnacl_util_1.encodeBase64)(kp.publicKey),
|
|
17
|
+
secretKey: (0, tweetnacl_util_1.encodeBase64)(kp.secretKey),
|
|
17
18
|
};
|
|
18
19
|
};
|
|
20
|
+
exports.generateKeypair = generateKeypair;
|
|
19
21
|
/**
|
|
20
22
|
* Helper function to encrypt input with a unique keypair for each submission.
|
|
21
23
|
* @param msg The message to encrypt
|
|
@@ -23,28 +25,30 @@ exports.generateKeypair = function () {
|
|
|
23
25
|
* @returns The encrypted basestring
|
|
24
26
|
* @throws error if any of the encrypt methods fail
|
|
25
27
|
*/
|
|
26
|
-
|
|
27
|
-
var submissionKeypair = exports.generateKeypair();
|
|
28
|
+
var encryptMessage = function (msg, theirPublicKey) {
|
|
29
|
+
var submissionKeypair = (0, exports.generateKeypair)();
|
|
28
30
|
var nonce = tweetnacl_1.default.randomBytes(24);
|
|
29
|
-
var encrypted = tweetnacl_util_1.encodeBase64(tweetnacl_1.default.box(msg, nonce, tweetnacl_util_1.decodeBase64(theirPublicKey), tweetnacl_util_1.decodeBase64(submissionKeypair.secretKey)));
|
|
30
|
-
return submissionKeypair.publicKey
|
|
31
|
+
var encrypted = (0, tweetnacl_util_1.encodeBase64)(tweetnacl_1.default.box(msg, nonce, (0, tweetnacl_util_1.decodeBase64)(theirPublicKey), (0, tweetnacl_util_1.decodeBase64)(submissionKeypair.secretKey)));
|
|
32
|
+
return "".concat(submissionKeypair.publicKey, ";").concat((0, tweetnacl_util_1.encodeBase64)(nonce), ":").concat(encrypted);
|
|
31
33
|
};
|
|
34
|
+
exports.encryptMessage = encryptMessage;
|
|
32
35
|
/**
|
|
33
36
|
* Helper method to decrypt an encrypted submission.
|
|
34
37
|
* @param formPrivateKey base64
|
|
35
38
|
* @param encryptedContent encrypted string encoded in base64
|
|
36
39
|
* @return The decrypted content, or null if decryption failed.
|
|
37
40
|
*/
|
|
38
|
-
|
|
41
|
+
var decryptContent = function (formPrivateKey, encryptedContent) {
|
|
39
42
|
try {
|
|
40
43
|
var _a = encryptedContent.split(';'), submissionPublicKey = _a[0], nonceEncrypted = _a[1];
|
|
41
44
|
var _b = nonceEncrypted.split(':').map(tweetnacl_util_1.decodeBase64), nonce = _b[0], encrypted = _b[1];
|
|
42
|
-
return tweetnacl_1.default.box.open(encrypted, nonce, tweetnacl_util_1.decodeBase64(submissionPublicKey), tweetnacl_util_1.decodeBase64(formPrivateKey));
|
|
45
|
+
return tweetnacl_1.default.box.open(encrypted, nonce, (0, tweetnacl_util_1.decodeBase64)(submissionPublicKey), (0, tweetnacl_util_1.decodeBase64)(formPrivateKey));
|
|
43
46
|
}
|
|
44
47
|
catch (err) {
|
|
45
48
|
return null;
|
|
46
49
|
}
|
|
47
50
|
};
|
|
51
|
+
exports.decryptContent = decryptContent;
|
|
48
52
|
/**
|
|
49
53
|
* Helper method to verify a signed message.
|
|
50
54
|
* @param msg the message to verify
|
|
@@ -52,28 +56,31 @@ exports.decryptContent = function (formPrivateKey, encryptedContent) {
|
|
|
52
56
|
* @returns the signed message if successful, else an error will be thrown
|
|
53
57
|
* @throws {Error} if the message cannot be verified
|
|
54
58
|
*/
|
|
55
|
-
|
|
56
|
-
var openedMessage = tweetnacl_1.default.sign.open(msg, tweetnacl_util_1.decodeBase64(publicKey));
|
|
59
|
+
var verifySignedMessage = function (msg, publicKey) {
|
|
60
|
+
var openedMessage = tweetnacl_1.default.sign.open(msg, (0, tweetnacl_util_1.decodeBase64)(publicKey));
|
|
57
61
|
if (!openedMessage)
|
|
58
62
|
throw new Error('Failed to open signed message with given public key');
|
|
59
|
-
return JSON.parse(tweetnacl_util_1.encodeUTF8(openedMessage));
|
|
63
|
+
return JSON.parse((0, tweetnacl_util_1.encodeUTF8)(openedMessage));
|
|
60
64
|
};
|
|
65
|
+
exports.verifySignedMessage = verifySignedMessage;
|
|
61
66
|
/**
|
|
62
67
|
* Helper method to check if all the field IDs given are within the filenames
|
|
63
68
|
* @param fieldIds the list of fieldIds to check
|
|
64
69
|
* @param filenames the filenames that should contain the fields
|
|
65
70
|
* @returns boolean indicating whether the fields are valid
|
|
66
71
|
*/
|
|
67
|
-
|
|
72
|
+
var areAttachmentFieldIdsValid = function (fieldIds, filenames) {
|
|
68
73
|
return fieldIds.every(function (fieldId) { return filenames[fieldId]; });
|
|
69
74
|
};
|
|
75
|
+
exports.areAttachmentFieldIdsValid = areAttachmentFieldIdsValid;
|
|
70
76
|
/**
|
|
71
77
|
* Converts an encrypted attachment to encrypted file content
|
|
72
78
|
* @param encryptedAttachment The encrypted attachment
|
|
73
79
|
* @returns EncryptedFileContent The encrypted file content
|
|
74
80
|
*/
|
|
75
|
-
|
|
81
|
+
var convertEncryptedAttachmentToFileContent = function (encryptedAttachment) { return ({
|
|
76
82
|
submissionPublicKey: encryptedAttachment.encryptedFile.submissionPublicKey,
|
|
77
83
|
nonce: encryptedAttachment.encryptedFile.nonce,
|
|
78
|
-
binary: tweetnacl_util_1.decodeBase64(encryptedAttachment.encryptedFile.binary),
|
|
84
|
+
binary: (0, tweetnacl_util_1.decodeBase64)(encryptedAttachment.encryptedFile.binary),
|
|
79
85
|
}); };
|
|
86
|
+
exports.convertEncryptedAttachmentToFileContent = convertEncryptedAttachmentToFileContent;
|
package/dist/util/parser.d.ts
CHANGED
|
@@ -1,10 +1,10 @@
|
|
|
1
|
-
export
|
|
1
|
+
export type HeaderSignature = {
|
|
2
2
|
v1: string;
|
|
3
3
|
t: number;
|
|
4
4
|
s: string;
|
|
5
5
|
f: string;
|
|
6
6
|
};
|
|
7
|
-
export
|
|
7
|
+
export type VerificationSignature = {
|
|
8
8
|
v: string;
|
|
9
9
|
t: number;
|
|
10
10
|
s: string;
|
package/dist/util/parser.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.parseVerificationSignature = exports.parseSignatureHeader = void 0;
|
|
3
4
|
/**
|
|
4
5
|
* Helper function to retrieve keys-values in a signature.
|
|
5
6
|
* @param signature The signature to convert to a keymap
|
|
@@ -20,18 +21,20 @@ var signatureToKeyMap = function (signature) {
|
|
|
20
21
|
* @param header The X-FormSG-Signature header
|
|
21
22
|
* @returns The signature header constituents
|
|
22
23
|
*/
|
|
23
|
-
|
|
24
|
+
var parseSignatureHeader = function (header) {
|
|
24
25
|
var parsedSignature = signatureToKeyMap(header);
|
|
25
26
|
parsedSignature.t = Number(parsedSignature.t);
|
|
26
27
|
return parsedSignature;
|
|
27
28
|
};
|
|
29
|
+
exports.parseSignatureHeader = parseSignatureHeader;
|
|
28
30
|
/**
|
|
29
31
|
* Parses the verification signature into its constituent
|
|
30
32
|
* @param signature The verification signature
|
|
31
33
|
* @returns The verification signature constituents
|
|
32
34
|
*/
|
|
33
|
-
|
|
35
|
+
var parseVerificationSignature = function (signature) {
|
|
34
36
|
var parsedSignature = signatureToKeyMap(signature);
|
|
35
37
|
parsedSignature.t = Number(parsedSignature.t);
|
|
36
38
|
return parsedSignature;
|
|
37
39
|
};
|
|
40
|
+
exports.parseVerificationSignature = parseVerificationSignature;
|
package/dist/util/publicKey.js
CHANGED
|
@@ -3,6 +3,7 @@ var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
|
3
3
|
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
4
|
};
|
|
5
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
6
|
+
exports.getVerificationPublicKey = exports.getSigningPublicKey = void 0;
|
|
6
7
|
var signing_keys_1 = require("../resource/signing-keys");
|
|
7
8
|
var verification_keys_1 = require("../resource/verification-keys");
|
|
8
9
|
var stage_1 = __importDefault(require("./stage"));
|
package/dist/util/signature.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
2
18
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
3
19
|
if (mod && mod.__esModule) return mod;
|
|
4
20
|
var result = {};
|
|
5
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result
|
|
6
|
-
result
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
7
23
|
return result;
|
|
8
24
|
};
|
|
9
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.verify = exports.sign = void 0;
|
|
10
27
|
var tweetnacl = __importStar(require("tweetnacl"));
|
|
11
28
|
var tweetnacl_util_1 = require("tweetnacl-util");
|
|
12
29
|
/**
|
|
@@ -16,7 +33,7 @@ var tweetnacl_util_1 = require("tweetnacl-util");
|
|
|
16
33
|
* @return base64 encoded signature
|
|
17
34
|
*/
|
|
18
35
|
function sign(basestring, secretKey) {
|
|
19
|
-
return tweetnacl_util_1.encodeBase64(tweetnacl.sign.detached(tweetnacl_util_1.decodeUTF8(basestring), tweetnacl_util_1.decodeBase64(secretKey)));
|
|
36
|
+
return (0, tweetnacl_util_1.encodeBase64)(tweetnacl.sign.detached((0, tweetnacl_util_1.decodeUTF8)(basestring), (0, tweetnacl_util_1.decodeBase64)(secretKey)));
|
|
20
37
|
}
|
|
21
38
|
exports.sign = sign;
|
|
22
39
|
/**
|
|
@@ -27,6 +44,6 @@ exports.sign = sign;
|
|
|
27
44
|
* @return True if verification checks out, false otherwise
|
|
28
45
|
*/
|
|
29
46
|
function verify(message, signature, publicKey) {
|
|
30
|
-
return tweetnacl.sign.detached.verify(tweetnacl_util_1.decodeUTF8(message), tweetnacl_util_1.decodeBase64(signature), tweetnacl_util_1.decodeBase64(publicKey));
|
|
47
|
+
return tweetnacl.sign.detached.verify((0, tweetnacl_util_1.decodeUTF8)(message), (0, tweetnacl_util_1.decodeBase64)(signature), (0, tweetnacl_util_1.decodeBase64)(publicKey));
|
|
31
48
|
}
|
|
32
49
|
exports.verify = verify;
|
package/dist/util/validate.d.ts
CHANGED
|
@@ -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
|
-
|
|
3
|
+
declare function determineIsFormFieldsV3(tbd: any): tbd is FormFieldsV3;
|
|
4
|
+
export { determineIsFormFields, determineIsFormFieldsV3 };
|
package/dist/util/validate.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.determineIsFormFieldsV3 = exports.determineIsFormFields = void 0;
|
|
3
4
|
function determineIsFormFields(tbd) {
|
|
4
5
|
if (!Array.isArray(tbd)) {
|
|
5
6
|
return false;
|
|
@@ -21,3 +22,15 @@ function determineIsFormFields(tbd) {
|
|
|
21
22
|
return filter.length === tbd.length;
|
|
22
23
|
}
|
|
23
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/dist/util/webhooks.js
CHANGED
|
@@ -1,12 +1,29 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
|
|
3
|
+
if (k2 === undefined) k2 = k;
|
|
4
|
+
var desc = Object.getOwnPropertyDescriptor(m, k);
|
|
5
|
+
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
|
|
6
|
+
desc = { enumerable: true, get: function() { return m[k]; } };
|
|
7
|
+
}
|
|
8
|
+
Object.defineProperty(o, k2, desc);
|
|
9
|
+
}) : (function(o, m, k, k2) {
|
|
10
|
+
if (k2 === undefined) k2 = k;
|
|
11
|
+
o[k2] = m[k];
|
|
12
|
+
}));
|
|
13
|
+
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
|
|
14
|
+
Object.defineProperty(o, "default", { enumerable: true, value: v });
|
|
15
|
+
}) : function(o, v) {
|
|
16
|
+
o["default"] = v;
|
|
17
|
+
});
|
|
2
18
|
var __importStar = (this && this.__importStar) || function (mod) {
|
|
3
19
|
if (mod && mod.__esModule) return mod;
|
|
4
20
|
var result = {};
|
|
5
|
-
if (mod != null) for (var k in mod) if (Object.hasOwnProperty.call(mod, k)) result
|
|
6
|
-
result
|
|
21
|
+
if (mod != null) for (var k in mod) if (k !== "default" && Object.prototype.hasOwnProperty.call(mod, k)) __createBinding(result, mod, k);
|
|
22
|
+
__setModuleDefault(result, mod);
|
|
7
23
|
return result;
|
|
8
24
|
};
|
|
9
25
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
26
|
+
exports.hasEpochExpired = exports.isSignatureHeaderValid = void 0;
|
|
10
27
|
var url = __importStar(require("url"));
|
|
11
28
|
var errors_1 = require("../errors");
|
|
12
29
|
var signature_1 = require("./signature");
|
|
@@ -23,8 +40,8 @@ var isSignatureHeaderValid = function (uri, signatureHeader, publicKey) {
|
|
|
23
40
|
if (!epoch || !signature || !submissionId || !formId) {
|
|
24
41
|
throw new errors_1.WebhookAuthenticateError('X-FormSG-Signature header is invalid');
|
|
25
42
|
}
|
|
26
|
-
var baseString = url.parse(uri).href
|
|
27
|
-
return signature_1.verify(baseString, signature, publicKey);
|
|
43
|
+
var baseString = "".concat(url.parse(uri).href, ".").concat(submissionId, ".").concat(formId, ".").concat(epoch);
|
|
44
|
+
return (0, signature_1.verify)(baseString, signature, publicKey);
|
|
28
45
|
};
|
|
29
46
|
exports.isSignatureHeaderValid = isSignatureHeaderValid;
|
|
30
47
|
/**
|
|
@@ -33,27 +33,27 @@ var Verification = /** @class */ (function () {
|
|
|
33
33
|
throw new errors_1.MissingPublicKeyError();
|
|
34
34
|
}
|
|
35
35
|
try {
|
|
36
|
-
var _b = parser_1.parseVerificationSignature(signatureString), transactionId = _b.v, time = _b.t, formId = _b.f, signature = _b.s;
|
|
36
|
+
var _b = (0, parser_1.parseVerificationSignature)(signatureString), transactionId = _b.v, time = _b.t, formId = _b.f, signature = _b.s;
|
|
37
37
|
if (!time) {
|
|
38
38
|
throw new Error('Malformed signature string was passed into function');
|
|
39
39
|
}
|
|
40
|
-
if (utils_1.isSignatureTimeValid(time, submissionCreatedAt, _this.transactionExpiry)) {
|
|
41
|
-
var data = utils_1.formatToBaseString({
|
|
40
|
+
if ((0, utils_1.isSignatureTimeValid)(time, submissionCreatedAt, _this.transactionExpiry)) {
|
|
41
|
+
var data = (0, utils_1.formatToBaseString)({
|
|
42
42
|
transactionId: transactionId,
|
|
43
43
|
formId: formId,
|
|
44
44
|
fieldId: fieldId,
|
|
45
45
|
answer: answer,
|
|
46
46
|
time: time,
|
|
47
47
|
});
|
|
48
|
-
return tweetnacl_1.default.sign.detached.verify(tweetnacl_util_1.decodeUTF8(data), tweetnacl_util_1.decodeBase64(signature), tweetnacl_util_1.decodeBase64(_this.verificationPublicKey));
|
|
48
|
+
return tweetnacl_1.default.sign.detached.verify((0, tweetnacl_util_1.decodeUTF8)(data), (0, tweetnacl_util_1.decodeBase64)(signature), (0, tweetnacl_util_1.decodeBase64)(_this.verificationPublicKey));
|
|
49
49
|
}
|
|
50
50
|
else {
|
|
51
|
-
console.info("Signature was expired for signatureString=\""
|
|
51
|
+
console.info("Signature was expired for signatureString=\"".concat(signatureString, "\" signatureDate=\"").concat(time, "\" submissionCreatedAt=\"").concat(submissionCreatedAt, "\""));
|
|
52
52
|
return false;
|
|
53
53
|
}
|
|
54
54
|
}
|
|
55
55
|
catch (error) {
|
|
56
|
-
console.error("An error occurred for signatureString=\""
|
|
56
|
+
console.error("An error occurred for signatureString=\"".concat(signatureString, "\" submissionCreatedAt=\"").concat(submissionCreatedAt, "\" fieldId=\"").concat(fieldId, "\" answer=\"").concat(answer, "\" error=\"").concat(error, "\""));
|
|
57
57
|
return false;
|
|
58
58
|
}
|
|
59
59
|
};
|
|
@@ -63,15 +63,15 @@ var Verification = /** @class */ (function () {
|
|
|
63
63
|
throw new errors_1.MissingSecretKeyError('Provide a secret key when when initializing the Verification class to use this function.');
|
|
64
64
|
}
|
|
65
65
|
var time = Date.now();
|
|
66
|
-
var data = utils_1.formatToBaseString({
|
|
66
|
+
var data = (0, utils_1.formatToBaseString)({
|
|
67
67
|
transactionId: transactionId,
|
|
68
68
|
formId: formId,
|
|
69
69
|
fieldId: fieldId,
|
|
70
70
|
answer: answer,
|
|
71
71
|
time: time,
|
|
72
72
|
});
|
|
73
|
-
var signature = tweetnacl_1.default.sign.detached(tweetnacl_util_1.decodeUTF8(data), tweetnacl_util_1.decodeBase64(_this.verificationSecretKey));
|
|
74
|
-
return "f="
|
|
73
|
+
var signature = tweetnacl_1.default.sign.detached((0, tweetnacl_util_1.decodeUTF8)(data), (0, tweetnacl_util_1.decodeBase64)(_this.verificationSecretKey));
|
|
74
|
+
return "f=".concat(formId, ",v=").concat(transactionId, ",t=").concat(time, ",s=").concat((0, tweetnacl_util_1.encodeBase64)(signature));
|
|
75
75
|
};
|
|
76
76
|
this.verificationPublicKey = params === null || params === void 0 ? void 0 : params.publicKey;
|
|
77
77
|
this.verificationSecretKey = params === null || params === void 0 ? void 0 : params.secretKey;
|
|
@@ -1,20 +1,23 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.formatToBaseString = exports.isSignatureTimeValid = void 0;
|
|
3
4
|
/**
|
|
4
5
|
* Checks if signature was made within the given expiry range before
|
|
5
6
|
* submission was created.
|
|
6
7
|
* @param signatureTime ms
|
|
7
8
|
* @param submissionCreatedAt ms
|
|
8
9
|
*/
|
|
9
|
-
|
|
10
|
+
var isSignatureTimeValid = function (signatureTime, submissionCreatedAt, transactionExpiry) {
|
|
10
11
|
var maxTime = submissionCreatedAt;
|
|
11
12
|
var minTime = maxTime - transactionExpiry * 1000;
|
|
12
13
|
return signatureTime > minTime && signatureTime < maxTime;
|
|
13
14
|
};
|
|
15
|
+
exports.isSignatureTimeValid = isSignatureTimeValid;
|
|
14
16
|
/**
|
|
15
17
|
* Formats given data into a string for signing
|
|
16
18
|
*/
|
|
17
|
-
|
|
19
|
+
var formatToBaseString = function (_a) {
|
|
18
20
|
var transactionId = _a.transactionId, formId = _a.formId, fieldId = _a.fieldId, answer = _a.answer, time = _a.time;
|
|
19
|
-
return transactionId
|
|
21
|
+
return "".concat(transactionId, ".").concat(formId, ".").concat(fieldId, ".").concat(answer, ".").concat(time);
|
|
20
22
|
};
|
|
23
|
+
exports.formatToBaseString = formatToBaseString;
|