Skip to content

Commit 9ca0152

Browse files
authored
Merge branch 'master' into master
2 parents 49d69a2 + e469f00 commit 9ca0152

14 files changed

+780
-4
lines changed

Diff for: package-lock.json

+7
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

Diff for: package.json

+1
Original file line numberDiff line numberDiff line change
@@ -134,6 +134,7 @@
134134
"highlight.js": "^11.9.0",
135135
"ieee754": "^1.2.1",
136136
"jimp": "^0.22.12",
137+
"jq-web": "^0.5.1",
137138
"jquery": "3.7.1",
138139
"js-crc": "^0.2.0",
139140
"js-sha3": "^0.9.3",

Diff for: src/core/config/Categories.json

+6-1
Original file line numberDiff line numberDiff line change
@@ -72,6 +72,8 @@
7272
"Avro to JSON",
7373
"CBOR Encode",
7474
"CBOR Decode",
75+
"YAML to JSON",
76+
"JSON to YAML",
7577
"Caret/M-decode",
7678
"Rison Encode",
7779
"Rison Decode",
@@ -193,7 +195,9 @@
193195
"Parse SSH Host Key",
194196
"Parse CSR",
195197
"Public Key from Certificate",
196-
"Public Key from Private Key"
198+
"Public Key from Private Key",
199+
"SM2 Encrypt",
200+
"SM2 Decrypt"
197201
]
198202
},
199203
{
@@ -465,6 +469,7 @@
465469
"CSS Minify",
466470
"XPath expression",
467471
"JPath expression",
472+
"Jq",
468473
"CSS selector",
469474
"PHP Deserialize",
470475
"Microsoft Script Decoder",

Diff for: src/core/lib/SM2.mjs

+258
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,258 @@
1+
/**
2+
* Utilities and operations utilized for SM2 encryption and decryption
3+
* @author flakjacket95 [[email protected]]
4+
* @copyright Crown Copyright 2024
5+
* @license Apache-2.0
6+
*/
7+
8+
import OperationError from "../errors/OperationError.mjs";
9+
import { fromHex } from "../lib/Hex.mjs";
10+
import Utils from "../Utils.mjs";
11+
import Sm3 from "crypto-api/src/hasher/sm3.mjs";
12+
import {toHex} from "crypto-api/src/encoder/hex.mjs";
13+
import r from "jsrsasign";
14+
15+
/**
16+
* SM2 Class for encryption and decryption operations
17+
*/
18+
export class SM2 {
19+
/**
20+
* Constructor for SM2 class; sets up with the curve and the output format as specified in user args
21+
*
22+
* @param {*} curve
23+
* @param {*} format
24+
*/
25+
constructor(curve, format) {
26+
this.ecParams = null;
27+
this.rng = new r.SecureRandom();
28+
/*
29+
For any additional curve definitions utilized by SM2, add another block like the below for that curve, then add the curve name to the Curve selection dropdown
30+
*/
31+
r.crypto.ECParameterDB.regist(
32+
"sm2p256v1", // name / p = 2**256 - 2**224 - 2**96 + 2**64 - 1
33+
256,
34+
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFF", // p
35+
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF00000000FFFFFFFFFFFFFFFC", // a
36+
"28E9FA9E9D9F5E344D5A9E4BCF6509A7F39789F515AB8F92DDBCBD414D940E93", // b
37+
"FFFFFFFEFFFFFFFFFFFFFFFFFFFFFFFF7203DF6B21C6052B53BBF40939D54123", // n
38+
"1", // h
39+
"32C4AE2C1F1981195F9904466A39C9948FE30BBFF2660BE1715A4589334C74C7", // gx
40+
"BC3736A2F4F6779C59BDCEE36B692153D0A9877CC62A474002DF32E52139F0A0", // gy
41+
[]
42+
); // alias
43+
this.ecParams = r.crypto.ECParameterDB.getByName(curve);
44+
45+
this.format = format;
46+
}
47+
48+
/**
49+
* Set the public key coordinates for the SM2 class
50+
*
51+
* @param {string} publicKeyX
52+
* @param {string} publicKeyY
53+
*/
54+
setPublicKey(publicKeyX, publicKeyY) {
55+
/*
56+
* TODO: This needs some additional length validation; and checking for errors in the decoding process
57+
* TODO: Can probably support other public key encoding methods here as well in the future
58+
*/
59+
this.publicKey = this.ecParams.curve.decodePointHex("04" + publicKeyX + publicKeyY);
60+
61+
if (this.publicKey.isInfinity()) {
62+
throw new OperationError("Invalid Public Key");
63+
}
64+
}
65+
66+
/**
67+
* Set the private key value for the SM2 class
68+
*
69+
* @param {string} privateKey
70+
*/
71+
setPrivateKey(privateKeyHex) {
72+
this.privateKey = new r.BigInteger(privateKeyHex, 16);
73+
}
74+
75+
/**
76+
* Main encryption function; takes user input, processes encryption and returns the result in hex (with the components arranged as configured by the user args)
77+
*
78+
* @param {*} input
79+
* @returns {string}
80+
*/
81+
encrypt(input) {
82+
const G = this.ecParams.G;
83+
84+
/*
85+
* Compute a new, random public key along the same elliptic curve to form the starting point for our encryption process (record the resulting X and Y as hex to provide as part of the operation output)
86+
* k: Randomly generated BigInteger
87+
* c1: Result of dotting our curve generator point `G` with the value of `k`
88+
*/
89+
const k = this.generatePublicKey();
90+
const c1 = G.multiply(k);
91+
const [hexC1X, hexC1Y] = this.getPointAsHex(c1);
92+
93+
/*
94+
* Compute p2 (secret) using the public key, and the chosen k value above
95+
*/
96+
const p2 = this.publicKey.multiply(k);
97+
98+
/*
99+
* Compute the C3 SM3 hash before we transform the array
100+
*/
101+
const c3 = this.c3(p2, input);
102+
103+
/*
104+
* Genreate a proper length encryption key, XOR iteratively, and convert newly encrypted data to hex
105+
*/
106+
const key = this.kdf(p2, input.byteLength);
107+
for (let i = 0; i < input.byteLength; i++) {
108+
input[i] ^= Utils.ord(key[i]);
109+
}
110+
const c2 = Buffer.from(input).toString("hex");
111+
112+
/*
113+
* Check user input specs; order the output components as selected
114+
*/
115+
if (this.format === "C1C3C2") {
116+
return hexC1X + hexC1Y + c3 + c2;
117+
} else {
118+
return hexC1X + hexC1Y + c2 + c3;
119+
}
120+
}
121+
/**
122+
* Function to decrypt an SM2 encrypted message
123+
*
124+
* @param {*} input
125+
*/
126+
decrypt(input) {
127+
const c1X = input.slice(0, 64);
128+
const c1Y = input.slice(64, 128);
129+
130+
let c3 = "";
131+
let c2 = "";
132+
133+
if (this.format === "C1C3C2") {
134+
c3 = input.slice(128, 192);
135+
c2 = input.slice(192);
136+
} else {
137+
c2 = input.slice(128, -64);
138+
c3 = input.slice(-64);
139+
}
140+
c2 = Uint8Array.from(fromHex(c2));
141+
const c1 = this.ecParams.curve.decodePointHex("04" + c1X + c1Y);
142+
143+
/*
144+
* Compute the p2 (secret) value by taking the C1 point provided in the encrypted package, and multiplying by the private k value
145+
*/
146+
const p2 = c1.multiply(this.privateKey);
147+
148+
/*
149+
* Similar to encryption; compute sufficient length key material and XOR the input data to recover the original message
150+
*/
151+
const key = this.kdf(p2, c2.byteLength);
152+
153+
for (let i = 0; i < c2.byteLength; i++) {
154+
c2[i] ^= Utils.ord(key[i]);
155+
}
156+
157+
const check = this.c3(p2, c2);
158+
if (check === c3) {
159+
return c2.buffer;
160+
} else {
161+
throw new OperationError("Decryption Error -- Computed Hashes Do Not Match");
162+
}
163+
}
164+
165+
166+
/**
167+
* Generates a large random number
168+
*
169+
* @param {*} limit
170+
* @returns
171+
*/
172+
getBigRandom(limit) {
173+
return new r.BigInteger(limit.bitLength(), this.rng)
174+
.mod(limit.subtract(r.BigInteger.ONE))
175+
.add(r.BigInteger.ONE);
176+
}
177+
178+
/**
179+
* Helper function for generating a large random K number; utilized for generating our initial C1 point
180+
* TODO: Do we need to do any sort of validation on the resulting k values?
181+
*
182+
* @returns {BigInteger}
183+
*/
184+
generatePublicKey() {
185+
const n = this.ecParams.n;
186+
const k = this.getBigRandom(n);
187+
return k;
188+
}
189+
190+
/**
191+
* SM2 Key Derivation Function (KDF); Takes P2 point, and generates a key material stream large enough to encrypt all of the input data
192+
*
193+
* @param {*} p2
194+
* @param {*} len
195+
* @returns {string}
196+
*/
197+
kdf(p2, len) {
198+
const [hX, hY] = this.getPointAsHex(p2);
199+
200+
const total = Math.ceil(len / 32) + 1;
201+
let cnt = 1;
202+
203+
let keyMaterial = "";
204+
205+
while (cnt < total) {
206+
const num = Utils.intToByteArray(cnt, 4, "big");
207+
const overall = fromHex(hX).concat(fromHex(hY)).concat(num);
208+
keyMaterial += this.sm3(overall);
209+
cnt++;
210+
}
211+
return keyMaterial;
212+
}
213+
214+
/**
215+
* Calculates the C3 component of our final encrypted payload; which is the SM3 hash of the P2 point and the original, unencrypted input data
216+
*
217+
* @param {*} p2
218+
* @param {*} input
219+
* @returns {string}
220+
*/
221+
c3(p2, input) {
222+
const [hX, hY] = this.getPointAsHex(p2);
223+
224+
const overall = fromHex(hX).concat(Array.from(input)).concat(fromHex(hY));
225+
226+
return toHex(this.sm3(overall));
227+
228+
}
229+
230+
/**
231+
* SM3 setup helper function; takes input data as an array, processes the hash and returns the result
232+
*
233+
* @param {*} data
234+
* @returns {string}
235+
*/
236+
sm3(data) {
237+
const hashData = Utils.arrayBufferToStr(Uint8Array.from(data).buffer, false);
238+
const hasher = new Sm3();
239+
hasher.update(hashData);
240+
return hasher.finalize();
241+
}
242+
243+
/**
244+
* Utility function, returns an elliptic curve points X and Y values as hex;
245+
*
246+
* @param {EcPointFp} point
247+
* @returns {[]}
248+
*/
249+
getPointAsHex(point) {
250+
const biX = point.getX().toBigInteger();
251+
const biY = point.getY().toBigInteger();
252+
253+
const charlen = this.ecParams.keycharlen;
254+
const hX = ("0000000000" + biX.toString(16)).slice(- charlen);
255+
const hY = ("0000000000" + biY.toString(16)).slice(- charlen);
256+
return [hX, hY];
257+
}
258+
}

Diff for: src/core/operations/JSONtoYAML.mjs

+46
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,46 @@
1+
/**
2+
* @author ccarpo [[email protected]]
3+
* @copyright Crown Copyright 2021
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import YAML from "yaml";
10+
11+
/**
12+
* JSON to YAML operation
13+
*/
14+
class JSONtoYAML extends Operation {
15+
16+
/**
17+
* JSONtoYAML constructor
18+
*/
19+
constructor() {
20+
super();
21+
22+
this.name = "JSON to YAML";
23+
this.module = "Default";
24+
this.description = "Format a JSON object into YAML";
25+
this.infoURL = "https://en.wikipedia.org/wiki/YAML";
26+
this.inputType = "JSON";
27+
this.outputType = "string";
28+
this.args = [];
29+
}
30+
31+
/**
32+
* @param {JSON} input
33+
* @param {Object[]} args
34+
* @returns {string}
35+
*/
36+
run(input, args) {
37+
try {
38+
return YAML.stringify(input);
39+
} catch (err) {
40+
throw new OperationError("Test");
41+
}
42+
}
43+
44+
}
45+
46+
export default JSONtoYAML;

0 commit comments

Comments
 (0)