Skip to content

Commit 9e89f28

Browse files
committed
feat: Add Ascon (NIST SP 800-232) operations: Hash, MAC, Encrypt, Decrypt
1 parent 2198a8d commit 9e89f28

File tree

10 files changed

+1016
-0
lines changed

10 files changed

+1016
-0
lines changed

package-lock.json

Lines changed: 9 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -140,6 +140,7 @@
140140
"jimp": "^0.22.12",
141141
"jq-web": "^0.5.1",
142142
"jquery": "3.7.1",
143+
"js-ascon": "^1.3.0",
143144
"js-sha3": "^0.9.3",
144145
"jsesc": "^3.0.2",
145146
"json5": "^2.2.3",

src/core/config/Categories.json

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -107,6 +107,8 @@
107107
"Rabbit",
108108
"SM4 Encrypt",
109109
"SM4 Decrypt",
110+
"Ascon Encrypt",
111+
"Ascon Decrypt",
110112
"GOST Encrypt",
111113
"GOST Decrypt",
112114
"GOST Sign",
@@ -427,6 +429,8 @@
427429
"BLAKE2b",
428430
"BLAKE2s",
429431
"BLAKE3",
432+
"Ascon Hash",
433+
"Ascon MAC",
430434
"GOST Hash",
431435
"Streebog",
432436
"SSDEEP",
Lines changed: 113 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,113 @@
1+
/**
2+
* @author Medjedtxm
3+
* @copyright Crown Copyright 2025
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import Utils from "../Utils.mjs";
10+
import { toHexFast } from "../lib/Hex.mjs";
11+
import JsAscon from "js-ascon";
12+
13+
/**
14+
* Ascon Decrypt operation
15+
*/
16+
class AsconDecrypt extends Operation {
17+
18+
/**
19+
* AsconDecrypt constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "Ascon Decrypt";
25+
this.module = "Ciphers";
26+
this.description = "Ascon-AEAD128 authenticated decryption as standardised in NIST SP 800-232. Decrypts ciphertext and verifies the authentication tag. Decryption will fail if the ciphertext or associated data has been tampered with.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>Nonce:</b> Must be exactly 16 bytes (128 bits). Must match the nonce used during encryption.<br><br><b>Associated Data:</b> Must match the associated data used during encryption. Any mismatch will cause authentication failure.";
27+
this.infoURL = "https://wikipedia.org/wiki/Ascon_(cipher)";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
"name": "Key",
33+
"type": "toggleString",
34+
"value": "",
35+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
36+
},
37+
{
38+
"name": "Nonce",
39+
"type": "toggleString",
40+
"value": "",
41+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
42+
},
43+
{
44+
"name": "Associated Data",
45+
"type": "toggleString",
46+
"value": "",
47+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
48+
},
49+
{
50+
"name": "Input",
51+
"type": "option",
52+
"value": ["Hex", "Raw"]
53+
},
54+
{
55+
"name": "Output",
56+
"type": "option",
57+
"value": ["Raw", "Hex"]
58+
}
59+
];
60+
}
61+
62+
/**
63+
* @param {string} input
64+
* @param {Object[]} args
65+
* @returns {string}
66+
* @throws {OperationError} if invalid key or nonce length, or authentication fails
67+
*/
68+
run(input, args) {
69+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
70+
nonce = Utils.convertToByteArray(args[1].string, args[1].option),
71+
ad = Utils.convertToByteArray(args[2].string, args[2].option),
72+
inputType = args[3],
73+
outputType = args[4];
74+
75+
if (key.length !== 16) {
76+
throw new OperationError(`Invalid key length: ${key.length} bytes.
77+
78+
Ascon-AEAD128 requires a key of exactly 16 bytes (128 bits).`);
79+
}
80+
81+
if (nonce.length !== 16) {
82+
throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.
83+
84+
Ascon-AEAD128 requires a nonce of exactly 16 bytes (128 bits).`);
85+
}
86+
87+
// Convert input to byte array
88+
const inputData = Utils.convertToByteArray(input, inputType);
89+
90+
// Convert to Uint8Array for js-ascon (it corrupts byte strings via TextEncoder)
91+
const keyUint8 = new Uint8Array(key);
92+
const nonceUint8 = new Uint8Array(nonce);
93+
const adUint8 = new Uint8Array(ad);
94+
const ciphertextUint8 = new Uint8Array(inputData);
95+
96+
try {
97+
// Decrypt (returns Uint8Array containing plaintext)
98+
const plaintext = JsAscon.decrypt(keyUint8, nonceUint8, adUint8, ciphertextUint8);
99+
100+
// Return in requested format
101+
if (outputType === "Hex") {
102+
return toHexFast(plaintext);
103+
} else {
104+
return Utils.arrayBufferToStr(Uint8Array.from(plaintext).buffer);
105+
}
106+
} catch (e) {
107+
throw new OperationError("Unable to decrypt: authentication failed. The ciphertext, key, nonce, or associated data may be incorrect or tampered with.");
108+
}
109+
}
110+
111+
}
112+
113+
export default AsconDecrypt;
Lines changed: 108 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,108 @@
1+
/**
2+
* @author Medjedtxm
3+
* @copyright Crown Copyright 2025
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import Utils from "../Utils.mjs";
10+
import { toHexFast } from "../lib/Hex.mjs";
11+
import JsAscon from "js-ascon";
12+
13+
/**
14+
* Ascon Encrypt operation
15+
*/
16+
class AsconEncrypt extends Operation {
17+
18+
/**
19+
* AsconEncrypt constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "Ascon Encrypt";
25+
this.module = "Ciphers";
26+
this.description = "Ascon-AEAD128 authenticated encryption as standardised in NIST SP 800-232. Ascon is a family of lightweight authenticated encryption algorithms designed for constrained devices such as IoT sensors and embedded systems.<br><br><b>Key:</b> Must be exactly 16 bytes (128 bits).<br><br><b>Nonce:</b> Must be exactly 16 bytes (128 bits). Should be unique for each encryption with the same key. Never reuse a nonce with the same key.<br><br><b>Associated Data:</b> Optional additional data that is authenticated but not encrypted. Useful for including metadata like headers or timestamps.<br><br>The output includes both the ciphertext and a 128-bit authentication tag.";
27+
this.infoURL = "https://wikipedia.org/wiki/Ascon_(cipher)";
28+
this.inputType = "string";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
"name": "Key",
33+
"type": "toggleString",
34+
"value": "",
35+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
36+
},
37+
{
38+
"name": "Nonce",
39+
"type": "toggleString",
40+
"value": "",
41+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
42+
},
43+
{
44+
"name": "Associated Data",
45+
"type": "toggleString",
46+
"value": "",
47+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
48+
},
49+
{
50+
"name": "Input",
51+
"type": "option",
52+
"value": ["Raw", "Hex"]
53+
},
54+
{
55+
"name": "Output",
56+
"type": "option",
57+
"value": ["Hex", "Raw"]
58+
}
59+
];
60+
}
61+
62+
/**
63+
* @param {string} input
64+
* @param {Object[]} args
65+
* @returns {string}
66+
* @throws {OperationError} if invalid key or nonce length
67+
*/
68+
run(input, args) {
69+
const key = Utils.convertToByteArray(args[0].string, args[0].option),
70+
nonce = Utils.convertToByteArray(args[1].string, args[1].option),
71+
ad = Utils.convertToByteArray(args[2].string, args[2].option),
72+
inputType = args[3],
73+
outputType = args[4];
74+
75+
if (key.length !== 16) {
76+
throw new OperationError(`Invalid key length: ${key.length} bytes.
77+
78+
Ascon-AEAD128 requires a key of exactly 16 bytes (128 bits).`);
79+
}
80+
81+
if (nonce.length !== 16) {
82+
throw new OperationError(`Invalid nonce length: ${nonce.length} bytes.
83+
84+
Ascon-AEAD128 requires a nonce of exactly 16 bytes (128 bits).`);
85+
}
86+
87+
// Convert input to byte array
88+
const inputData = Utils.convertToByteArray(input, inputType);
89+
90+
const keyUint8 = new Uint8Array(key);
91+
const nonceUint8 = new Uint8Array(nonce);
92+
const adUint8 = new Uint8Array(ad);
93+
const inputUint8 = new Uint8Array(inputData);
94+
95+
// Encrypt (returns Uint8Array containing ciphertext + tag)
96+
const ciphertext = JsAscon.encrypt(keyUint8, nonceUint8, adUint8, inputUint8);
97+
98+
// Return in requested format
99+
if (outputType === "Hex") {
100+
return toHexFast(ciphertext);
101+
} else {
102+
return Utils.arrayBufferToStr(Uint8Array.from(ciphertext).buffer);
103+
}
104+
}
105+
106+
}
107+
108+
export default AsconEncrypt;

src/core/operations/AsconHash.mjs

Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
/**
2+
* @author Medjedtxm
3+
* @copyright Crown Copyright 2025
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import { toHexFast } from "../lib/Hex.mjs";
9+
import JsAscon from "js-ascon";
10+
11+
/**
12+
* Ascon Hash operation
13+
*/
14+
class AsconHash extends Operation {
15+
16+
/**
17+
* AsconHash constructor
18+
*/
19+
constructor() {
20+
super();
21+
22+
this.name = "Ascon Hash";
23+
this.module = "Crypto";
24+
this.description = "Ascon-Hash256 produces a fixed 256-bit (32-byte) cryptographic hash as standardised in NIST SP 800-232. Ascon is a family of lightweight authenticated encryption and hashing algorithms designed for constrained devices such as IoT sensors and embedded systems.<br><br>The algorithm was selected by NIST in 2023 as the new standard for lightweight cryptography after a multi-year competition.";
25+
this.infoURL = "https://wikipedia.org/wiki/Ascon_(cipher)";
26+
this.inputType = "ArrayBuffer";
27+
this.outputType = "string";
28+
this.args = [];
29+
}
30+
31+
/**
32+
* @param {ArrayBuffer} input
33+
* @param {Object[]} args
34+
* @returns {string}
35+
*/
36+
run(input, args) {
37+
38+
const inputUint8 = new Uint8Array(input);
39+
40+
// Compute hash (returns Uint8Array)
41+
const hashResult = JsAscon.hash(inputUint8);
42+
43+
// Convert to hex string
44+
return toHexFast(hashResult);
45+
}
46+
47+
}
48+
49+
export default AsconHash;

src/core/operations/AsconMAC.mjs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,68 @@
1+
/**
2+
* @author Medjedtxm
3+
* @copyright Crown Copyright 2025
4+
* @license Apache-2.0
5+
*/
6+
7+
import Operation from "../Operation.mjs";
8+
import OperationError from "../errors/OperationError.mjs";
9+
import Utils from "../Utils.mjs";
10+
import { toHexFast } from "../lib/Hex.mjs";
11+
import AsconMac from "../vendor/ascon.mjs";
12+
13+
/**
14+
* Ascon MAC operation
15+
*/
16+
class AsconMAC extends Operation {
17+
18+
/**
19+
* AsconMAC constructor
20+
*/
21+
constructor() {
22+
super();
23+
24+
this.name = "Ascon MAC";
25+
this.module = "Crypto";
26+
this.description = "Ascon-Mac produces a 128-bit (16-byte) message authentication code as part of the Ascon family standardised by NIST in SP 800-232. It provides authentication for messages using a secret key, ensuring both data integrity and authenticity.<br><br>Ascon is designed for lightweight cryptography on constrained devices such as IoT sensors and embedded systems.";
27+
this.infoURL = "https://wikipedia.org/wiki/Ascon_(cipher)";
28+
this.inputType = "ArrayBuffer";
29+
this.outputType = "string";
30+
this.args = [
31+
{
32+
"name": "Key",
33+
"type": "toggleString",
34+
"value": "",
35+
"toggleValues": ["Hex", "UTF8", "Latin1", "Base64"]
36+
}
37+
];
38+
}
39+
40+
/**
41+
* @param {ArrayBuffer} input
42+
* @param {Object[]} args
43+
* @returns {string}
44+
* @throws {OperationError} if invalid key length
45+
*/
46+
run(input, args) {
47+
const keyArray = Utils.convertToByteArray(args[0].string, args[0].option);
48+
49+
if (keyArray.length !== 16) {
50+
throw new OperationError(`Invalid key length: ${keyArray.length} bytes.
51+
52+
Ascon-Mac requires a key of exactly 16 bytes (128 bits).`);
53+
}
54+
55+
// Convert to Uint8Array for vendor Ascon implementation
56+
const keyUint8 = new Uint8Array(keyArray);
57+
const inputUint8 = new Uint8Array(input);
58+
59+
// Compute MAC (returns Uint8Array)
60+
const macResult = AsconMac.mac(keyUint8, inputUint8);
61+
62+
// Convert to hex string
63+
return toHexFast(macResult);
64+
}
65+
66+
}
67+
68+
export default AsconMAC;

0 commit comments

Comments
 (0)