-
Notifications
You must be signed in to change notification settings - Fork 10
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
33 changed files
with
1,104 additions
and
1 deletion.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -80,7 +80,6 @@ web_modules/ | |
|
||
# Nuxt.js build / generate output | ||
.nuxt | ||
dist | ||
|
||
# Gatsby files | ||
.cache/ | ||
|
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
import { DecryptParams, DecryptedContent, EncryptedFileContent } from './types'; | ||
export default class Crypto { | ||
signingPublicKey?: string; | ||
constructor({ signingPublicKey }?: { | ||
signingPublicKey?: string; | ||
}); | ||
/** | ||
* Encrypt input with a unique keypair for each submission | ||
* @param encryptionPublicKey The base-64 encoded public key for encrypting. | ||
* @param msg The message to encrypt, will be stringified. | ||
* @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. | ||
* @returns The encrypted basestring. | ||
*/ | ||
encrypt: (msg: any, encryptionPublicKey: string, signingPrivateKey?: string | undefined) => string; | ||
/** | ||
* Decrypts an encrypted submission and returns it. | ||
* @param formSecretKey The base-64 secret key of the form to decrypt with. | ||
* @param decryptParams The params containing encrypted content and information. | ||
* @param decryptParams.encryptedContent The encrypted content encoded with base-64. | ||
* @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with. | ||
* @param decryptParams.verifiedContent Optional. The encrypted and signed verified content. If given, the signingPublicKey will be used to attempt to open the signed message. | ||
* @returns The decrypted content if successful. Else, null will be returned. | ||
* @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content. | ||
*/ | ||
decrypt: (formSecretKey: string, decryptParams: DecryptParams) => DecryptedContent | null; | ||
/** | ||
* Generates a new keypair for encryption. | ||
* @returns The generated keypair. | ||
*/ | ||
generate: () => import("./types").Keypair; | ||
/** | ||
* Returns true if a pair of public & secret keys are associated with each other | ||
* @param publicKey The public key to verify against. | ||
* @param secretKey The private key to verify against. | ||
*/ | ||
valid: (publicKey: string, secretKey: string) => boolean; | ||
/** | ||
* Encrypt given binary file with a unique keypair for each submission. | ||
* @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary | ||
* @param formPublicKey The base-64 encoded public key | ||
* @returns Promise holding the encrypted file | ||
* @throws error if any of the encrypt methods fail | ||
*/ | ||
encryptFile: (binary: Uint8Array, formPublicKey: string) => Promise<EncryptedFileContent>; | ||
/** | ||
* Decrypt the given encrypted file content. | ||
* @param formSecretKey Secret key as a base-64 string | ||
* @param encrypted Object returned from encryptFile function | ||
* @param encrypted.submissionPublicKey The submission public key as a base-64 string | ||
* @param encrypted.nonce The nonce as a base-64 string | ||
* @param encrypted.blob The encrypted file as a Blob object | ||
*/ | ||
decryptFile: (formSecretKey: string, { submissionPublicKey, nonce, binary: encryptedBinary, }: EncryptedFileContent) => Promise<Uint8Array | null>; | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,178 @@ | ||
"use strict"; | ||
var __awaiter = (this && this.__awaiter) || function (thisArg, _arguments, P, generator) { | ||
function adopt(value) { return value instanceof P ? value : new P(function (resolve) { resolve(value); }); } | ||
return new (P || (P = Promise))(function (resolve, reject) { | ||
function fulfilled(value) { try { step(generator.next(value)); } catch (e) { reject(e); } } | ||
function rejected(value) { try { step(generator["throw"](value)); } catch (e) { reject(e); } } | ||
function step(result) { result.done ? resolve(result.value) : adopt(result.value).then(fulfilled, rejected); } | ||
step((generator = generator.apply(thisArg, _arguments || [])).next()); | ||
}); | ||
}; | ||
var __generator = (this && this.__generator) || function (thisArg, body) { | ||
var _ = { label: 0, sent: function() { if (t[0] & 1) throw t[1]; return t[1]; }, trys: [], ops: [] }, f, y, t, g; | ||
return g = { next: verb(0), "throw": verb(1), "return": verb(2) }, typeof Symbol === "function" && (g[Symbol.iterator] = function() { return this; }), g; | ||
function verb(n) { return function (v) { return step([n, v]); }; } | ||
function step(op) { | ||
if (f) throw new TypeError("Generator is already executing."); | ||
while (_) try { | ||
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; | ||
if (y = 0, t) op = [op[0] & 2, t.value]; | ||
switch (op[0]) { | ||
case 0: case 1: t = op; break; | ||
case 4: _.label++; return { value: op[1], done: false }; | ||
case 5: _.label++; y = op[1]; op = [0]; continue; | ||
case 7: op = _.ops.pop(); _.trys.pop(); continue; | ||
default: | ||
if (!(t = _.trys, t = t.length > 0 && t[t.length - 1]) && (op[0] === 6 || op[0] === 2)) { _ = 0; continue; } | ||
if (op[0] === 3 && (!t || (op[1] > t[0] && op[1] < t[3]))) { _.label = op[1]; break; } | ||
if (op[0] === 6 && _.label < t[1]) { _.label = t[1]; t = op; break; } | ||
if (t && _.label < t[2]) { _.label = t[2]; _.ops.push(op); break; } | ||
if (t[2]) _.ops.pop(); | ||
_.trys.pop(); continue; | ||
} | ||
op = body.call(thisArg, _); | ||
} catch (e) { op = [6, e]; y = 0; } finally { f = t = 0; } | ||
if (op[0] & 5) throw op[1]; return { value: op[0] ? op[1] : void 0, done: true }; | ||
} | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var tweetnacl_1 = __importDefault(require("tweetnacl")); | ||
var tweetnacl_util_1 = require("tweetnacl-util"); | ||
var validate_1 = require("./util/validate"); | ||
var errors_1 = require("./errors"); | ||
var crypto_1 = require("./util/crypto"); | ||
var Crypto = /** @class */ (function () { | ||
function Crypto(_a) { | ||
var _this = this; | ||
var signingPublicKey = (_a === void 0 ? {} : _a).signingPublicKey; | ||
/** | ||
* Encrypt input with a unique keypair for each submission | ||
* @param encryptionPublicKey The base-64 encoded public key for encrypting. | ||
* @param msg The message to encrypt, will be stringified. | ||
* @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. | ||
* @returns The encrypted basestring. | ||
*/ | ||
this.encrypt = function (msg, encryptionPublicKey, signingPrivateKey) { | ||
var processedMsg = tweetnacl_util_1.decodeUTF8(JSON.stringify(msg)); | ||
if (signingPrivateKey) { | ||
processedMsg = tweetnacl_1.default.sign(processedMsg, tweetnacl_util_1.decodeBase64(signingPrivateKey)); | ||
} | ||
return crypto_1.encryptMessage(processedMsg, encryptionPublicKey); | ||
}; | ||
/** | ||
* Decrypts an encrypted submission and returns it. | ||
* @param formSecretKey The base-64 secret key of the form to decrypt with. | ||
* @param decryptParams The params containing encrypted content and information. | ||
* @param decryptParams.encryptedContent The encrypted content encoded with base-64. | ||
* @param decryptParams.version The version of the payload. Used to determine the decryption process to decrypt the content with. | ||
* @param decryptParams.verifiedContent Optional. The encrypted and signed verified content. If given, the signingPublicKey will be used to attempt to open the signed message. | ||
* @returns The decrypted content if successful. Else, null will be returned. | ||
* @throws {MissingPublicKeyError} if a public key is not provided when instantiating this class and is needed for verifying signed content. | ||
*/ | ||
this.decrypt = function (formSecretKey, decryptParams) { | ||
try { | ||
var encryptedContent = decryptParams.encryptedContent, verifiedContent = decryptParams.verifiedContent, version = decryptParams.version; | ||
// Do not return the transformed object in `_decrypt` function as a signed | ||
// object is not encoded in UTF8 and is encoded in Base-64 instead. | ||
var decryptedContent = crypto_1.decryptContent(formSecretKey, encryptedContent); | ||
if (!decryptedContent) { | ||
throw new Error('Failed to decrypt content'); | ||
} | ||
var decryptedObject = JSON.parse(tweetnacl_util_1.encodeUTF8(decryptedContent)); | ||
if (!validate_1.determineIsFormFields(decryptedObject)) { | ||
throw new Error('Decrypted object does not fit expected shape'); | ||
} | ||
var returnedObject = { | ||
responses: decryptedObject, | ||
}; | ||
if (verifiedContent) { | ||
if (!_this.signingPublicKey) { | ||
throw new errors_1.MissingPublicKeyError('Public signing key must be provided when instantiating the Crypto class in order to verify verified content'); | ||
} | ||
// Only care if it is the correct shape if verifiedContent exists, since | ||
// we need to append it to the end. | ||
// Decrypted message must be able to be authenticated by the public key. | ||
var decryptedVerifiedContent = crypto_1.decryptContent(formSecretKey, verifiedContent); | ||
if (!decryptedVerifiedContent) { | ||
// Returns null if decrypting verified content failed. | ||
throw new Error('Failed to decrypt verified content'); | ||
} | ||
var decryptedVerifiedObject = crypto_1.verifySignedMessage(decryptedVerifiedContent, _this.signingPublicKey); | ||
returnedObject.verified = decryptedVerifiedObject; | ||
} | ||
return returnedObject; | ||
} | ||
catch (err) { | ||
// Should only throw if MissingPublicKeyError. | ||
// This library should be able to be used to encrypt and decrypt content | ||
// if the content does not contain verified fields. | ||
if (err instanceof errors_1.MissingPublicKeyError) { | ||
throw err; | ||
} | ||
return null; | ||
} | ||
}; | ||
/** | ||
* Generates a new keypair for encryption. | ||
* @returns The generated keypair. | ||
*/ | ||
this.generate = crypto_1.generateKeypair; | ||
/** | ||
* Returns true if a pair of public & secret keys are associated with each other | ||
* @param publicKey The public key to verify against. | ||
* @param secretKey The private key to verify against. | ||
*/ | ||
this.valid = function (publicKey, secretKey) { | ||
var _a; | ||
var testResponse = []; | ||
var internalValidationVersion = 1; | ||
var cipherResponse = _this.encrypt(testResponse, publicKey); | ||
// Use toString here since the return should be an empty array. | ||
return (testResponse.toString() === ((_a = _this.decrypt(secretKey, { | ||
encryptedContent: cipherResponse, | ||
version: internalValidationVersion, | ||
})) === null || _a === void 0 ? void 0 : _a.responses.toString())); | ||
}; | ||
/** | ||
* Encrypt given binary file with a unique keypair for each submission. | ||
* @param binary The file to encrypt, should be a blob that is converted to Uint8Array binary | ||
* @param formPublicKey The base-64 encoded public key | ||
* @returns Promise holding the encrypted file | ||
* @throws error if any of the encrypt methods fail | ||
*/ | ||
this.encryptFile = function (binary, formPublicKey) { return __awaiter(_this, void 0, void 0, function () { | ||
var submissionKeypair, nonce; | ||
return __generator(this, function (_a) { | ||
submissionKeypair = this.generate(); | ||
nonce = tweetnacl_1.default.randomBytes(24); | ||
return [2 /*return*/, { | ||
submissionPublicKey: submissionKeypair.publicKey, | ||
nonce: tweetnacl_util_1.encodeBase64(nonce), | ||
binary: tweetnacl_1.default.box(binary, nonce, tweetnacl_util_1.decodeBase64(formPublicKey), tweetnacl_util_1.decodeBase64(submissionKeypair.secretKey)), | ||
}]; | ||
}); | ||
}); }; | ||
/** | ||
* Decrypt the given encrypted file content. | ||
* @param formSecretKey Secret key as a base-64 string | ||
* @param encrypted Object returned from encryptFile function | ||
* @param encrypted.submissionPublicKey The submission public key as a base-64 string | ||
* @param encrypted.nonce The nonce as a base-64 string | ||
* @param encrypted.blob The encrypted file as a Blob object | ||
*/ | ||
this.decryptFile = function (formSecretKey, _a) { | ||
var submissionPublicKey = _a.submissionPublicKey, nonce = _a.nonce, encryptedBinary = _a.binary; | ||
return __awaiter(_this, void 0, void 0, function () { | ||
return __generator(this, function (_b) { | ||
return [2 /*return*/, tweetnacl_1.default.box.open(encryptedBinary, tweetnacl_util_1.decodeBase64(nonce), tweetnacl_util_1.decodeBase64(submissionPublicKey), tweetnacl_util_1.decodeBase64(formSecretKey))]; | ||
}); | ||
}); | ||
}; | ||
this.signingPublicKey = signingPublicKey; | ||
} | ||
return Crypto; | ||
}()); | ||
exports.default = Crypto; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
declare class MissingSecretKeyError extends Error { | ||
constructor(message?: string); | ||
} | ||
declare class MissingPublicKeyError extends Error { | ||
constructor(message?: string); | ||
} | ||
declare class WebhookAuthenticateError extends Error { | ||
constructor(message: string); | ||
} | ||
export { MissingSecretKeyError, MissingPublicKeyError, WebhookAuthenticateError, }; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,56 @@ | ||
"use strict"; | ||
var __extends = (this && this.__extends) || (function () { | ||
var extendStatics = function (d, b) { | ||
extendStatics = Object.setPrototypeOf || | ||
({ __proto__: [] } instanceof Array && function (d, b) { d.__proto__ = b; }) || | ||
function (d, b) { for (var p in b) if (b.hasOwnProperty(p)) d[p] = b[p]; }; | ||
return extendStatics(d, b); | ||
}; | ||
return function (d, b) { | ||
extendStatics(d, b); | ||
function __() { this.constructor = d; } | ||
d.prototype = b === null ? Object.create(b) : (__.prototype = b.prototype, new __()); | ||
}; | ||
})(); | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var MissingSecretKeyError = /** @class */ (function (_super) { | ||
__extends(MissingSecretKeyError, _super); | ||
function MissingSecretKeyError(message) { | ||
if (message === void 0) { message = 'Provide a secret key when initializing the FormSG SDK to use this function.'; } | ||
var _this = _super.call(this, message) || this; | ||
_this.name = _this.constructor.name; | ||
// Set the prototype explicitly. | ||
// See https://github.com/facebook/jest/issues/8279 | ||
Object.setPrototypeOf(_this, MissingSecretKeyError.prototype); | ||
return _this; | ||
} | ||
return MissingSecretKeyError; | ||
}(Error)); | ||
exports.MissingSecretKeyError = MissingSecretKeyError; | ||
var MissingPublicKeyError = /** @class */ (function (_super) { | ||
__extends(MissingPublicKeyError, _super); | ||
function MissingPublicKeyError(message) { | ||
if (message === void 0) { message = 'Provide a public key when initializing the FormSG SDK to use this function.'; } | ||
var _this = _super.call(this, message) || this; | ||
_this.name = _this.constructor.name; | ||
// Set the prototype explicitly. | ||
// See https://github.com/facebook/jest/issues/8279 | ||
Object.setPrototypeOf(_this, MissingPublicKeyError.prototype); | ||
return _this; | ||
} | ||
return MissingPublicKeyError; | ||
}(Error)); | ||
exports.MissingPublicKeyError = MissingPublicKeyError; | ||
var WebhookAuthenticateError = /** @class */ (function (_super) { | ||
__extends(WebhookAuthenticateError, _super); | ||
function WebhookAuthenticateError(message) { | ||
var _this = _super.call(this, message) || this; | ||
_this.name = _this.constructor.name; | ||
// Set the prototype explicitly. | ||
// See https://github.com/facebook/jest/issues/8279 | ||
Object.setPrototypeOf(_this, WebhookAuthenticateError.prototype); | ||
return _this; | ||
} | ||
return WebhookAuthenticateError; | ||
}(Error)); | ||
exports.WebhookAuthenticateError = WebhookAuthenticateError; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
import { PackageInitParams } from './types'; | ||
import Verification from './verification'; | ||
import Webhooks from './webhooks'; | ||
import Crypto from './crypto'; | ||
/** | ||
* Entrypoint into the FormSG SDK | ||
* | ||
* @param {PackageInitParams} config Package initialization config parameters | ||
* @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. | ||
* @param {string?} [config.webhookSecretKey] Optional. base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data. | ||
* @param {VerificationOptions?} [config.verificationOptions] Optional. If provided, enables the usage of the verification module. | ||
*/ | ||
export default function (config?: PackageInitParams): { | ||
webhooks: Webhooks; | ||
crypto: Crypto; | ||
verification: Verification; | ||
}; |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
"use strict"; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
var publicKey_1 = require("./util/publicKey"); | ||
var verification_1 = __importDefault(require("./verification")); | ||
var webhooks_1 = __importDefault(require("./webhooks")); | ||
var crypto_1 = __importDefault(require("./crypto")); | ||
/** | ||
* Entrypoint into the FormSG SDK | ||
* | ||
* @param {PackageInitParams} config Package initialization config parameters | ||
* @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. | ||
* @param {string?} [config.webhookSecretKey] Optional. base64 secret key for signing webhooks. If provided, enables generating signature and headers to authenticate webhook data. | ||
* @param {VerificationOptions?} [config.verificationOptions] Optional. If provided, enables the usage of the verification module. | ||
*/ | ||
function default_1(config) { | ||
if (config === void 0) { config = {}; } | ||
var webhookSecretKey = config.webhookSecretKey, mode = config.mode, verificationOptions = config.verificationOptions; | ||
var signingPublicKey = publicKey_1.getSigningPublicKey(mode || 'production'); | ||
var verificationPublicKey = publicKey_1.getVerificationPublicKey(mode || 'production'); | ||
return { | ||
webhooks: new webhooks_1.default({ | ||
publicKey: signingPublicKey, | ||
secretKey: webhookSecretKey, | ||
}), | ||
crypto: new crypto_1.default({ signingPublicKey: signingPublicKey }), | ||
verification: new verification_1.default({ | ||
publicKey: verificationPublicKey, | ||
secretKey: verificationOptions === null || verificationOptions === void 0 ? void 0 : verificationOptions.secretKey, | ||
transactionExpiry: verificationOptions === null || verificationOptions === void 0 ? void 0 : verificationOptions.transactionExpiry, | ||
}), | ||
}; | ||
} | ||
exports.default = default_1; |
Oops, something went wrong.