diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index 4a436a35..19ab89d3 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -77,6 +77,8 @@
"Blowfish Decrypt",
"DES Encrypt",
"DES Decrypt",
+ "Cetacean Cipher Encode",
+ "Cetacean Cipher Decode",
"Triple DES Encrypt",
"Triple DES Decrypt",
"LS47 Encrypt",
diff --git a/src/core/operations/CetaceanCipherDecode.mjs b/src/core/operations/CetaceanCipherDecode.mjs
new file mode 100644
index 00000000..a79b98c5
--- /dev/null
+++ b/src/core/operations/CetaceanCipherDecode.mjs
@@ -0,0 +1,64 @@
+/**
+ * @author dolphinOnKeys [robin@weird.io]
+ * @copyright Crown Copyright 2022
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+
+/**
+ * Cetacean Cipher Decode operation
+ */
+class CetaceanCipherDecode extends Operation {
+
+ /**
+ * CetaceanCipherDecode constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Cetacean Cipher Decode";
+ this.module = "Ciphers";
+ this.description = "Decode Cetacean Cipher input.
e.g. EEEEEEEEEeeEeEEEEEEEEEEEEeeEeEEe
becomes hi
";
+ this.infoURL = "";
+ this.inputType = "string";
+ this.outputType = "string";
+
+ this.checks = [
+ {
+ pattern: "^(?:[eE]{16,})(?: [eE]{16,})*$",
+ flags: "",
+ args: []
+ }
+ ]
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ const binaryArray = [];
+ for ( const char of input ) {
+ if ( char === ' ' ) {
+ binaryArray.push(...[ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 0, 0 ]);
+ } else {
+ binaryArray.push( char === 'e' ? 1 : 0 );
+ }
+ }
+
+ const byteArray = [];
+
+ for ( let i = 0; i < binaryArray.length; i += 16 ) {
+ byteArray.push(binaryArray.slice(i, i + 16).join(''))
+ }
+
+ return byteArray.map( byte =>
+ String.fromCharCode(parseInt( byte , 2 )
+ )
+ ).join('');
+ }
+}
+
+export default CetaceanCipherDecode;
diff --git a/src/core/operations/CetaceanCipherEncode.mjs b/src/core/operations/CetaceanCipherEncode.mjs
new file mode 100644
index 00000000..e32e4f81
--- /dev/null
+++ b/src/core/operations/CetaceanCipherEncode.mjs
@@ -0,0 +1,54 @@
+/**
+ * @author dolphinOnKeys [robin@weird.io]
+ * @copyright Crown Copyright 2022
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+
+/**
+ * Cetacean Cipher Encode operation
+ */
+class CetaceanCipherEncode extends Operation {
+
+ /**
+ * CetaceanCipherEncode constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Cetacean Cipher Encode";
+ this.module = "Ciphers";
+ this.description = "Converts any input into Cetacean Cipher.
e.g. hi
becomes EEEEEEEEEeeEeEEEEEEEEEEEEeeEeEEe
\"";
+ this.infoURL = "";
+ this.inputType = "string";
+ this.outputType = "string";
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ let result = [];
+ let charArray = input.split('');
+
+ charArray.map( ( character ) => {
+ if ( character === ' ' ) {
+ result.push( character );
+ } else {
+ const binaryArray = this.encodeToBinary( character ).split('');
+ result.push( binaryArray.map(( str ) => str === '1' ? 'e' : 'E' ).join(''));
+ }
+ });
+
+ return result.join('');
+ }
+
+ encodeToBinary( char, padding = 16 ) {
+ return char.charCodeAt(0).toString(2).padStart( padding, '0');
+ }
+}
+
+export default CetaceanCipherEncode;
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index 0234ca86..f4294cad 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -28,6 +28,8 @@ import "./tests/Base85.mjs";
import "./tests/BitwiseOp.mjs";
import "./tests/ByteRepr.mjs";
import "./tests/CartesianProduct.mjs";
+import "./tests/CetaceanCipherEncode.mjs";
+import "./tests/CetaceanCipherDecode.mjs";
import "./tests/CharEnc.mjs";
import "./tests/ChangeIPFormat.mjs";
import "./tests/Charts.mjs";
diff --git a/tests/operations/tests/CetaceanCipherDecode.mjs b/tests/operations/tests/CetaceanCipherDecode.mjs
new file mode 100644
index 00000000..e834dc1e
--- /dev/null
+++ b/tests/operations/tests/CetaceanCipherDecode.mjs
@@ -0,0 +1,22 @@
+/**
+ * CetaceanCipher Encode tests
+ *
+ * @author dolphinOnKeys
+ * @copyright Crown Copyright 2022
+ * @licence Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Cetacean Cipher Decode",
+ input: "EEEEEEEEEeeEEEEe EEEEEEEEEeeEEEeE EEEEEEEEEeeEEEee EEeeEEEEEeeEEeee",
+ expectedOutput: "a b c で",
+ recipeConfig: [
+ {
+ op: "Cetacean Cipher Decode",
+ args: []
+ },
+ ],
+ }
+]);
diff --git a/tests/operations/tests/CetaceanCipherEncode.mjs b/tests/operations/tests/CetaceanCipherEncode.mjs
new file mode 100644
index 00000000..bef76388
--- /dev/null
+++ b/tests/operations/tests/CetaceanCipherEncode.mjs
@@ -0,0 +1,22 @@
+/**
+ * CetaceanCipher Encode tests
+ *
+ * @author dolphinOnKeys
+ * @copyright Crown Copyright 2022
+ * @licence Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+
+TestRegister.addTests([
+ {
+ name: "Cetacean Cipher Encode",
+ input: "a b c で",
+ expectedOutput: "EEEEEEEEEeeEEEEe EEEEEEEEEeeEEEeE EEEEEEEEEeeEEEee EEeeEEEEEeeEEeee",
+ recipeConfig: [
+ {
+ op: "Cetacean Cipher Encode",
+ args: []
+ },
+ ],
+ }
+]);