diff --git a/package-lock.json b/package-lock.json
index 6fa9016e..8eacd9e7 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -8234,6 +8234,11 @@
"resolved": "https://registry.npmjs.org/jsrsasign/-/jsrsasign-10.4.0.tgz",
"integrity": "sha512-C8qLhiAssh/b74KJpGhWuFGG9cFhJqMCVuuHXRibb3Z5vPuAW0ue0jUirpoExCdpdhv4nD3sZ1DAwJURYJTm9g=="
},
+ "jssha": {
+ "version": "3.2.0",
+ "resolved": "https://registry.npmjs.org/jssha/-/jssha-3.2.0.tgz",
+ "integrity": "sha512-QuruyBENDWdN4tZwJbQq7/eAK85FqrI4oDbXjy5IBhYD+2pTJyBUWZe8ctWaCkrV0gy6AaelgOZZBMeswEa/6Q=="
+ },
"jszip": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/jszip/-/jszip-2.5.0.tgz",
@@ -12973,6 +12978,14 @@
"integrity": "sha512-gduQwd1rOdDMGxFG1gEvhV88Oirdo2p+KjoYFU7k2g+i7n6AFFbDQ5kMPUsW0pNbfQsB/cwXvT1i4Bue0s9g5g==",
"dev": true
},
+ "totp-generator": {
+ "version": "0.0.13",
+ "resolved": "https://registry.npmjs.org/totp-generator/-/totp-generator-0.0.13.tgz",
+ "integrity": "sha512-/Sg4eXvaLfaXNJJWv/OLPQm1GVKQXOe6mbDNXFBRZdYnaN9iY6Grk6uPYLph9MDn+cGn6GLNaqpak34jfu1CQQ==",
+ "requires": {
+ "jssha": "^3.1.2"
+ }
+ },
"tough-cookie": {
"version": "2.5.0",
"resolved": "https://registry.npmjs.org/tough-cookie/-/tough-cookie-2.5.0.tgz",
diff --git a/package.json b/package.json
index 78a8696b..f6b94f12 100644
--- a/package.json
+++ b/package.json
@@ -156,6 +156,7 @@
"stream-browserify": "^3.0.0",
"terser": "^5.7.1",
"tesseract.js": "2.1.5",
+ "totp-generator": "0.0.13",
"ua-parser-js": "^0.7.28",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 09ee8d15..2d98300d 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -453,6 +453,7 @@
"Pseudo-Random Number Generator",
"Generate UUID",
"Generate TOTP",
+ "Generate TOTP Code",
"Generate HOTP",
"Generate QR Code",
"Parse QR Code",
diff --git a/src/core/operations/GenerateTOTPCode.mjs b/src/core/operations/GenerateTOTPCode.mjs
new file mode 100644
index 00000000..23d9c130
--- /dev/null
+++ b/src/core/operations/GenerateTOTPCode.mjs
@@ -0,0 +1,89 @@
+/**
+ * @author hellodword
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import totp from "totp-generator";
+
+/**
+ * Generate TOTP Code operation
+ */
+class GenerateTOTPCode extends Operation {
+
+ /**
+ * GenerateTOTPCode constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Generate TOTP Code";
+ this.module = "Default";
+ this.description = "The Time-based One-Time Password algorithm (TOTP) is an algorithm that computes a one-time password from a shared secret key and the current time. It has been adopted as Internet Engineering Task Force standard RFC 6238, is the cornerstone of Initiative For Open Authentication (OAUTH), and is used in a number of two-factor authentication systems. A TOTP is an HOTP where the counter is the current time.
Enter the Secret to generate a TOTP code. Timestamp is in milliseconds.";
+ this.infoURL = "https://wikipedia.org/wiki/Time-based_One-time_Password_algorithm";
+ this.inputType = "ArrayBuffer";
+ this.outputType = "string";
+ this.args = [
+ {
+ "name": "Secret",
+ "type": "string",
+ "value": ""
+ },
+ {
+ "name": "Algorithm",
+ "type": "editableOption",
+ "value": [
+ {
+ name: "SHA-1",
+ value: "SHA-1",
+ },
+ {
+ name: "SHA-512",
+ value: "SHA-512",
+ },
+ ]
+ },
+ {
+ "name": "Period",
+ "type": "number",
+ "value": 30
+ },
+ {
+ "name": "Digits",
+ "type": "number",
+ "value": 6
+ },
+ {
+ "name": "Timestamp",
+ "type": "number",
+ "value": 0
+ }
+ ];
+ }
+
+ /**
+ * @param {ArrayBuffer} input
+ * @param {Object[]} args
+ * @returns {string}
+ *
+ * @throws {OperationError} if invalid secret
+ */
+ run(input, args) {
+ const secret = args[0];
+ if (secret.length === 0) {
+ throw new OperationError(`Invalid Secret`);
+ }
+ const token = totp(secret, {
+ algorithm: args[1],
+ period: args[2],
+ digits: args[3],
+ timestamp: args[4] === 0 ? +new Date() : args[4],
+ });
+ return token;
+ }
+
+}
+
+export default GenerateTOTPCode;
diff --git a/tests/operations/tests/GenerateTOTPCode.mjs b/tests/operations/tests/GenerateTOTPCode.mjs
new file mode 100644
index 00000000..7563f60f
--- /dev/null
+++ b/tests/operations/tests/GenerateTOTPCode.mjs
@@ -0,0 +1,23 @@
+/**
+ * Generate TOTP Code tests.
+ *
+ * @author hellodword
+ *
+ * @copyright Crown Copyright 2017
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Generate TOTP Code",
+ input: "",
+ expectedOutput: "871328",
+ recipeConfig: [
+ {
+ op: "Generate TOTP Code",
+ args: ["KJLTAORSKZGV4RBMHNFDETRVKQUFMLZTHZYFOW2UFAYXIP23JVNQ", "SHA-1", 30, 6, 1465324707000],
+ },
+ ],
+ },
+]);