Skip to content

Add "Generate UUID" and "Analyse UUID" operations #2011

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 13 commits into
base: master
Choose a base branch
from
Open
40 changes: 27 additions & 13 deletions package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -179,6 +179,7 @@
"ua-parser-js": "^1.0.38",
"unorm": "^1.6.0",
"utf8": "^3.0.0",
"uuid": "^11.1.0",
"vkbeautify": "^0.99.3",
"xpath": "0.0.34",
"xregexp": "^5.1.1",
Expand Down
1 change: 1 addition & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -544,6 +544,7 @@
"Pseudo-Random Number Generator",
"Generate De Bruijn Sequence",
"Generate UUID",
"Analyse UUID",
"Generate TOTP",
"Generate HOTP",
"Generate QR Code",
Expand Down
48 changes: 48 additions & 0 deletions src/core/operations/AnalyseUUID.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
/**
* @author n1474335 [[email protected]]
* @copyright Crown Copyright 2023
* @license Apache-2.0
*/

import * as uuid from "uuid";

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.mjs";

/**
* Analyse UUID operation
*/
class AnalyseUUID extends Operation {

/**
* AnalyseUUID constructor
*/
constructor() {
super();

this.name = "Analyse UUID";
this.module = "Crypto";
this.description = "Tries to determine information about a given UUID and suggests which version may have been used to generate it";
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
this.inputType = "string";
this.outputType = "string";
this.args = [];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
try {
const uuidVersion = uuid.version(input);
return "UUID version: " + uuidVersion;
} catch (error) {
throw new OperationError("Invalid UUID");
}
}

}

export default AnalyseUUID;
56 changes: 42 additions & 14 deletions src/core/operations/GenerateUUID.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -5,8 +5,8 @@
*/

import Operation from "../Operation.mjs";
import crypto from "crypto";

import * as uuid from "uuid";
import OperationError from "../errors/OperationError.mjs";
/**
* Generate UUID operation
*/
Expand All @@ -20,11 +20,38 @@ class GenerateUUID extends Operation {

this.name = "Generate UUID";
this.module = "Crypto";
this.description = "Generates an RFC 4122 version 4 compliant Universally Unique Identifier (UUID), also known as a Globally Unique Identifier (GUID).<br><br>A version 4 UUID relies on random numbers, in this case generated using <code>window.crypto</code> if available and falling back to <code>Math.random</code> if not.";
this.description =
"Generates an RFC 9562 (formerly RFC 4122) compliant Universally Unique Identifier (UUID), " +
"also known as a Globally Unique Identifier (GUID).<br>" +
"<br>" +
"We currently support generating the following UUID versions:<br>" +
"<ul>" +
"<li><strong>v1</strong>: Timestamp-based</li>" +
"<li><strong>v3</strong>: Namespace w/ MD5</li>" +
"<li><strong>v4</strong>: Random (default)</li>" +
"<li><strong>v5</strong>: Namespace w/ SHA-1</li>" +
"<li><strong>v6</strong>: Timestamp, reordered</li>" +
"<li><strong>v7</strong>: Unix Epoch time-based</li>" +
"</ul>" +
"UUIDs are generated using the <a href='https://npmjs.org/uuid/'><code>uuid</code><a> package.<br>";
this.infoURL = "https://wikipedia.org/wiki/Universally_unique_identifier";
this.inputType = "string";
this.outputType = "string";
this.args = [];
this.args = [
{
name: "Version",
hint: "UUID version",
type: "option",
value: ["v1", "v3", "v4", "v5", "v6", "v7"],
defaultIndex: 2,
},
{
name: "Namespace",
hint: "UUID namespace (UUID; valid for v3 and v5)",
type: "string",
value: "1b671a64-40d5-491e-99b0-da01ff1f3341"
}
];
}

/**
Expand All @@ -33,16 +60,17 @@ class GenerateUUID extends Operation {
* @returns {string}
*/
run(input, args) {
const buf = new Uint32Array(4).map(() => {
return crypto.randomBytes(4).readUInt32BE(0, true);
});
let i = 0;
return "xxxxxxxx-xxxx-4xxx-yxxx-xxxxxxxxxxxx".replace(/[xy]/g, function(c) {
const r = (buf[i >> 3] >> ((i % 8) * 4)) & 0xf,
v = c === "x" ? r : (r & 0x3 | 0x8);
i++;
return v.toString(16);
});
const [version, namespace] = args;
const hasDesiredVersion = typeof uuid[version] === "function";
if (!hasDesiredVersion) throw new OperationError("Invalid UUID version");

const requiresNamespace = ["v3", "v5"].includes(version);
if (!requiresNamespace) return uuid[version]();

const hasValidNamespace = typeof namespace === "string" && uuid.validate(namespace);
if (!hasValidNamespace) throw new OperationError("Invalid UUID namespace");

return uuid[version](input, namespace);
}

}
Expand Down
23 changes: 19 additions & 4 deletions tests/node/tests/operations.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -580,10 +580,25 @@ Password: 282760`;
assert.strictEqual(result.toString().substr(0, 37), "-----BEGIN PGP PRIVATE KEY BLOCK-----");
}),

it("Generate UUID", () => {
const result = chef.generateUUID();
assert.ok(result.toString());
assert.strictEqual(result.toString().length, 36);
...[1, 3, 4, 5, 6, 7].map(version => it(`Generate UUID v${version}`, () => {
const result = chef.generateUUID("", { "version": `v${version}` }).toString();
assert.ok(result);
assert.strictEqual(result.length, 36);
})),

...[1, 3, 4, 5, 6, 7].map(version => it(`Analyze UUID v${version}`, () => {
const uuid = chef.generateUUID("", { "version": `v${version}` }).toString();
const result = chef.analyseUUID(uuid).toString();
const expected = `UUID version: ${version}`;
assert.strictEqual(result, expected);
})),

it("Generate UUID using defaults", () => {
const uuid = chef.generateUUID();
assert.ok(uuid);

const analysis = chef.analyseUUID(uuid).toString();
assert.strictEqual(analysis, "UUID version: 4");
}),

it("Gzip, Gunzip", () => {
Expand Down
Loading