diff --git a/src/core/config/Categories.json b/src/core/config/Categories.json
index f0624fc7..134cae2d 100644
--- a/src/core/config/Categories.json
+++ b/src/core/config/Categories.json
@@ -181,6 +181,10 @@
"RSA Verify",
"RSA Encrypt",
"RSA Decrypt",
+ "Generate ECDSA Key Pair",
+ "ECDSA Signature Conversion",
+ "ECDSA Sign",
+ "ECDSA Verify",
"Parse SSH Host Key",
"Parse CSR",
"Public Key from Certificate",
diff --git a/src/core/operations/ECDSASign.mjs b/src/core/operations/ECDSASign.mjs
new file mode 100644
index 00000000..7b8f57f1
--- /dev/null
+++ b/src/core/operations/ECDSASign.mjs
@@ -0,0 +1,107 @@
+/**
+ * @author cplussharp
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import { fromHex } from "../lib/Hex.mjs";
+import { toBase64 } from "../lib/Base64.mjs";
+import r from "jsrsasign";
+
+/**
+ * ECDSA Sign operation
+ */
+class ECDSASign extends Operation {
+
+ /**
+ * ECDSASign constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "ECDSA Sign";
+ this.module = "Ciphers";
+ this.description = "Sign a plaintext message with a PEM encoded EC key.";
+ this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "ECDSA Private Key (PEM)",
+ type: "text",
+ value: "-----BEGIN EC PRIVATE KEY-----"
+ },
+ {
+ name: "Message Digest Algorithm",
+ type: "option",
+ value: [
+ "SHA-256",
+ "SHA-384",
+ "SHA-512",
+ "SHA-1",
+ "MD5"
+ ]
+ },
+ {
+ name: "Output Format",
+ type: "option",
+ value: [
+ "ASN.1 HEX",
+ "P1363 HEX",
+ "JSON Web Signature",
+ "Raw JSON"
+ ]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ const [keyPem, mdAlgo, outputFormat] = args;
+
+ if (keyPem.replace("-----BEGIN EC PRIVATE KEY-----", "").length === 0) {
+ throw new OperationError("Please enter a private key.");
+ }
+
+ const internalAlgorithmName = mdAlgo.replace("-", "") + "withECDSA";
+ const sig = new r.KJUR.crypto.Signature({ alg: internalAlgorithmName });
+ const key = r.KEYUTIL.getKey(keyPem);
+ if (key.type !== "EC") {
+ throw new OperationError("Provided key is not an EC key.");
+ }
+ if (!key.isPrivate) {
+ throw new OperationError("Provided key is not a private key.");
+ }
+ sig.init(key);
+ const signatureASN1Hex = sig.signString(input);
+
+ let result;
+ switch (outputFormat) {
+ case "ASN.1 HEX":
+ result = signatureASN1Hex;
+ break;
+ case "P1363 HEX":
+ result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
+ break;
+ case "JSON Web Signature":
+ result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
+ result = toBase64(fromHex(result), "A-Za-z0-9-_"); // base64url
+ break;
+ case "Raw JSON": {
+ const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
+ result = JSON.stringify(signatureRS);
+ break;
+ }
+ }
+
+ return result;
+ }
+}
+
+export default ECDSASign;
diff --git a/src/core/operations/ECDSASignatureConversion.mjs b/src/core/operations/ECDSASignatureConversion.mjs
new file mode 100644
index 00000000..3f6c6bfb
--- /dev/null
+++ b/src/core/operations/ECDSASignatureConversion.mjs
@@ -0,0 +1,146 @@
+/**
+ * @author cplussharp
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import { fromBase64, toBase64 } from "../lib/Base64.mjs";
+import { fromHex, toHexFast } from "../lib/Hex.mjs";
+import r from "jsrsasign";
+
+/**
+ * ECDSA Sign operation
+ */
+class ECDSASignatureConversion extends Operation {
+
+ /**
+ * ECDSASignatureConversion constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "ECDSA Signature Conversion";
+ this.module = "Ciphers";
+ this.description = "Convert an ECDSA signature between hex, asn1 and json.";
+ this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Input Format",
+ type: "option",
+ value: [
+ "Auto",
+ "ASN.1 HEX",
+ "P1363 HEX",
+ "JSON Web Signature",
+ "Raw JSON"
+ ]
+ },
+ {
+ name: "Output Format",
+ type: "option",
+ value: [
+ "ASN.1 HEX",
+ "P1363 HEX",
+ "JSON Web Signature",
+ "Raw JSON"
+ ]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ let inputFormat = args[0];
+ const outputFormat = args[1];
+
+ // detect input format
+ let inputJson;
+ if (inputFormat === "Auto") {
+ try {
+ inputJson = JSON.parse(input);
+ if (typeof(inputJson) === "object") {
+ inputFormat = "Raw JSON";
+ }
+ } catch {}
+ }
+
+ if (inputFormat === "Auto") {
+ const hexRegex = /^[a-f\d]{2,}$/gi;
+ if (hexRegex.test(input)) {
+ if (input.substring(0, 2) === "30" && r.ASN1HEX.isASN1HEX(input)) {
+ inputFormat = "ASN.1 HEX";
+ } else {
+ inputFormat = "P1363 HEX";
+ }
+ }
+ }
+
+ let inputBase64;
+ if (inputFormat === "Auto") {
+ try {
+ inputBase64 = fromBase64(input, "A-Za-z0-9-_", false);
+ inputFormat = "JSON Web Signature";
+ } catch {}
+ }
+
+ // convert input to ASN.1 hex
+ let signatureASN1Hex;
+ switch (inputFormat) {
+ case "Auto":
+ throw new OperationError("Signature format could not be detected");
+ case "ASN.1 HEX":
+ signatureASN1Hex = input;
+ break;
+ case "P1363 HEX":
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(input);
+ break;
+ case "JSON Web Signature":
+ if (!inputBase64) inputBase64 = fromBase64(input, "A-Za-z0-9-_");
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(toHexFast(inputBase64));
+ break;
+ case "Raw JSON": {
+ if (!inputJson) inputJson = JSON.parse(input);
+ if (!inputJson.r) {
+ throw new OperationError('No "r" value in the signature JSON');
+ }
+ if (!inputJson.s) {
+ throw new OperationError('No "s" value in the signature JSON');
+ }
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.hexRSSigToASN1Sig(inputJson.r, inputJson.s);
+ break;
+ }
+ }
+
+ // convert ASN.1 hex to output format
+ let result;
+ switch (outputFormat) {
+ case "ASN.1 HEX":
+ result = signatureASN1Hex;
+ break;
+ case "P1363 HEX":
+ result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
+ break;
+ case "JSON Web Signature":
+ result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
+ result = toBase64(fromHex(result), "A-Za-z0-9-_"); // base64url
+ break;
+ case "Raw JSON": {
+ const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
+ result = JSON.stringify(signatureRS);
+ break;
+ }
+ }
+
+ return result;
+ }
+}
+
+export default ECDSASignatureConversion;
diff --git a/src/core/operations/ECDSAVerify.mjs b/src/core/operations/ECDSAVerify.mjs
new file mode 100644
index 00000000..7e46e867
--- /dev/null
+++ b/src/core/operations/ECDSAVerify.mjs
@@ -0,0 +1,154 @@
+/**
+ * @author cplussharp
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import OperationError from "../errors/OperationError.mjs";
+import { fromBase64 } from "../lib/Base64.mjs";
+import { toHexFast } from "../lib/Hex.mjs";
+import r from "jsrsasign";
+
+/**
+ * ECDSA Verify operation
+ */
+class ECDSAVerify extends Operation {
+
+ /**
+ * ECDSAVerify constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "ECDSA Verify";
+ this.module = "Ciphers";
+ this.description = "Verify a message against a signature and a public PEM encoded EC key.";
+ this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Input Format",
+ type: "option",
+ value: [
+ "Auto",
+ "ASN.1 HEX",
+ "P1363 HEX",
+ "JSON Web Signature",
+ "Raw JSON"
+ ]
+ },
+ {
+ name: "Message Digest Algorithm",
+ type: "option",
+ value: [
+ "SHA-256",
+ "SHA-384",
+ "SHA-512",
+ "SHA-1",
+ "MD5"
+ ]
+ },
+ {
+ name: "ECDSA Public Key (PEM)",
+ type: "text",
+ value: "-----BEGIN PUBLIC KEY-----"
+ },
+ {
+ name: "Message",
+ type: "text",
+ value: ""
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ run(input, args) {
+ let inputFormat = args[0];
+ const [, mdAlgo, keyPem, msg] = args;
+
+ if (keyPem.replace("-----BEGIN PUBLIC KEY-----", "").length === 0) {
+ throw new OperationError("Please enter a public key.");
+ }
+
+ // detect input format
+ let inputJson;
+ if (inputFormat === "Auto") {
+ try {
+ inputJson = JSON.parse(input);
+ if (typeof(inputJson) === "object") {
+ inputFormat = "Raw JSON";
+ }
+ } catch {}
+ }
+
+ if (inputFormat === "Auto") {
+ const hexRegex = /^[a-f\d]{2,}$/gi;
+ if (hexRegex.test(input)) {
+ if (input.substring(0, 2) === "30" && r.ASN1HEX.isASN1HEX(input)) {
+ inputFormat = "ASN.1 HEX";
+ } else {
+ inputFormat = "P1363 HEX";
+ }
+ }
+ }
+
+ let inputBase64;
+ if (inputFormat === "Auto") {
+ try {
+ inputBase64 = fromBase64(input, "A-Za-z0-9-_", false);
+ inputFormat = "JSON Web Signature";
+ } catch {}
+ }
+
+ // convert to ASN.1 signature
+ let signatureASN1Hex;
+ switch (inputFormat) {
+ case "Auto":
+ throw new OperationError("Signature format could not be detected");
+ case "ASN.1 HEX":
+ signatureASN1Hex = input;
+ break;
+ case "P1363 HEX":
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(input);
+ break;
+ case "JSON Web Signature":
+ if (!inputBase64) inputBase64 = fromBase64(input, "A-Za-z0-9-_");
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(toHexFast(inputBase64));
+ break;
+ case "Raw JSON": {
+ if (!inputJson) inputJson = JSON.parse(input);
+ if (!inputJson.r) {
+ throw new OperationError('No "r" value in the signature JSON');
+ }
+ if (!inputJson.s) {
+ throw new OperationError('No "s" value in the signature JSON');
+ }
+ signatureASN1Hex = r.KJUR.crypto.ECDSA.hexRSSigToASN1Sig(inputJson.r, inputJson.s);
+ break;
+ }
+ }
+
+ // verify signature
+ const internalAlgorithmName = mdAlgo.replace("-", "") + "withECDSA";
+ const sig = new r.KJUR.crypto.Signature({ alg: internalAlgorithmName });
+ const key = r.KEYUTIL.getKey(keyPem);
+ if (key.type !== "EC") {
+ throw new OperationError("Provided key is not an EC key.");
+ }
+ if (!key.isPublic) {
+ throw new OperationError("Provided key is not a public key.");
+ }
+ sig.init(key);
+ sig.updateString(msg);
+ const result = sig.verify(signatureASN1Hex);
+ return result ? "Verified OK" : "Verification Failure";
+ }
+}
+
+export default ECDSAVerify;
diff --git a/src/core/operations/GenerateECDSAKeyPair.mjs b/src/core/operations/GenerateECDSAKeyPair.mjs
new file mode 100644
index 00000000..14714a02
--- /dev/null
+++ b/src/core/operations/GenerateECDSAKeyPair.mjs
@@ -0,0 +1,102 @@
+/**
+ * @author cplussharp
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+
+import Operation from "../Operation.mjs";
+import { cryptNotice } from "../lib/Crypt.mjs";
+import r from "jsrsasign";
+
+/**
+ * Generate ECDSA Key Pair operation
+ */
+class GenerateECDSAKeyPair extends Operation {
+
+ /**
+ * GenerateECDSAKeyPair constructor
+ */
+ constructor() {
+ super();
+
+ this.name = "Generate ECDSA Key Pair";
+ this.module = "Ciphers";
+ this.description = `Generate an ECDSA key pair with a given Curve.
${cryptNotice}`;
+ this.infoURL = "https://wikipedia.org/wiki/Elliptic_Curve_Digital_Signature_Algorithm";
+ this.inputType = "string";
+ this.outputType = "string";
+ this.args = [
+ {
+ name: "Elliptic Curve",
+ type: "option",
+ value: [
+ "P-256",
+ "P-384",
+ "P-521"
+ ]
+ },
+ {
+ name: "Output Format",
+ type: "option",
+ value: [
+ "PEM",
+ "DER",
+ "JWK"
+ ]
+ }
+ ];
+ }
+
+ /**
+ * @param {string} input
+ * @param {Object[]} args
+ * @returns {string}
+ */
+ async run(input, args) {
+ const [curveName, outputFormat] = args;
+
+ return new Promise((resolve, reject) => {
+ let internalCurveName;
+ switch (curveName) {
+ case "P-256":
+ internalCurveName = "secp256r1";
+ break;
+ case "P-384":
+ internalCurveName = "secp384r1";
+ break;
+ case "P-521":
+ internalCurveName = "secp521r1";
+ break;
+ }
+ const keyPair = r.KEYUTIL.generateKeypair("EC", internalCurveName);
+
+ let pubKey;
+ let privKey;
+ let result;
+ switch (outputFormat) {
+ case "PEM":
+ pubKey = r.KEYUTIL.getPEM(keyPair.pubKeyObj).replace(/\r/g, "");
+ privKey = r.KEYUTIL.getPEM(keyPair.prvKeyObj, "PKCS8PRV").replace(/\r/g, "");
+ result = pubKey + "\n" + privKey;
+ break;
+ case "DER":
+ result = keyPair.prvKeyObj.prvKeyHex;
+ break;
+ case "JWK":
+ pubKey = r.KEYUTIL.getJWKFromKey(keyPair.pubKeyObj);
+ pubKey.key_ops = ["verify"]; // eslint-disable-line camelcase
+ pubKey.kid = "PublicKey";
+ privKey = r.KEYUTIL.getJWKFromKey(keyPair.prvKeyObj);
+ privKey.key_ops = ["sign"]; // eslint-disable-line camelcase
+ privKey.kid = "PrivateKey";
+ result = JSON.stringify({keys: [privKey, pubKey]}, null, 4);
+ break;
+ }
+
+ resolve(result);
+ });
+ }
+
+}
+
+export default GenerateECDSAKeyPair;
diff --git a/tests/operations/index.mjs b/tests/operations/index.mjs
index 606675b7..c931e8c6 100644
--- a/tests/operations/index.mjs
+++ b/tests/operations/index.mjs
@@ -59,6 +59,7 @@ import "./tests/Crypt.mjs";
import "./tests/CSV.mjs";
import "./tests/DateTime.mjs";
import "./tests/DefangIP.mjs";
+import "./tests/ECDSA.mjs";
import "./tests/ELFInfo.mjs";
import "./tests/Enigma.mjs";
import "./tests/ExtractEmailAddresses.mjs";
diff --git a/tests/operations/tests/ECDSA.mjs b/tests/operations/tests/ECDSA.mjs
new file mode 100644
index 00000000..560afc5c
--- /dev/null
+++ b/tests/operations/tests/ECDSA.mjs
@@ -0,0 +1,464 @@
+/**
+ * ECDSA tests.
+ *
+ * @author cplussharp
+ * @copyright Crown Copyright 2021
+ * @license Apache-2.0
+ */
+import TestRegister from "../../lib/TestRegister.mjs";
+import { ASCII_TEXT } from "../../samples/Ciphers.mjs";
+
+const P256 = {
+ // openssl ecparam -name prime256v1 -genkey -noout -out p256.priv.key
+ privateKeyPkcs1: `-----BEGIN EC PRIVATE KEY-----
+MHcCAQEEINtTjwUkgfAiSwqgcGAXWyE0ueIW6n2k395dmQZ3vGr4oAoGCCqGSM49
+AwEHoUQDQgAEDUc8A0EDNKoCYIPWMHz1yUzqE5mJgusgcAE8H6810fkJ8ZmTNiCC
+a6sLgR2vD1VNh2diirWgKPH4PVMKav5e6Q==
+-----END EC PRIVATE KEY-----`,
+ privateKeyPkcs8: `-----BEGIN PRIVATE KEY-----
+MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg21OPBSSB8CJLCqBw
+YBdbITS54hbqfaTf3l2ZBne8avihRANCAAQNRzwDQQM0qgJgg9YwfPXJTOoTmYmC
+6yBwATwfrzXR+QnxmZM2IIJrqwuBHa8PVU2HZ2KKtaAo8fg9Uwpq/l7p
+-----END PRIVATE KEY-----`,
+
+ // openssl ec -in p256.priv.key -pubout -out p256.pub.key
+ publicKey: `-----BEGIN PUBLIC KEY-----
+MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEDUc8A0EDNKoCYIPWMHz1yUzqE5mJ
+gusgcAE8H6810fkJ8ZmTNiCCa6sLgR2vD1VNh2diirWgKPH4PVMKav5e6Q==
+-----END PUBLIC KEY-----`,
+
+ signature: {
+ sha256: {
+ asn1: "3046022100e06905608a2fa7dbda9e284c2a7959dfb68fb527a5f003b2d7975ff135145127022100b6baa253793334f8b93ea1dd622bc600124d8090babd807efe3f77b8b324388d",
+ p1363: "e06905608a2fa7dbda9e284c2a7959dfb68fb527a5f003b2d7975ff135145127b6baa253793334f8b93ea1dd622bc600124d8090babd807efe3f77b8b324388d",
+ jws: "4GkFYIovp9vanihMKnlZ37aPtSel8AOy15df8TUUUSe2uqJTeTM0-Lk-od1iK8YAEk2AkLq9gH7-P3e4syQ4jQ",
+ json: `{"r":"00e06905608a2fa7dbda9e284c2a7959dfb68fb527a5f003b2d7975ff135145127","s":"00b6baa253793334f8b93ea1dd622bc600124d8090babd807efe3f77b8b324388d"}`
+ }
+ }
+};
+
+// openssl pkcs8 -topk8 -in p256.priv.key -out p256.enc-priv.key -v2 des3 -v2prf hmacWithSHA1 -passout pass:Test1234
+/* const PEM_PRIV_P256_ENCRYPTED_PASS = "Test1234";
+const PEM_PRIV_P256_ENCRYPTED = `-----BEGIN ENCRYPTED PRIVATE KEY-----
+MIHsMFcGCSqGSIb3DQEFDTBKMCkGCSqGSIb3DQEFDDAcBAg+4ckqI9Q9ZAICCAAw
+DAYIKoZIhvcNAgkFADAdBglghkgBZQMEASoEEOnMUW15Hn/ub0OcCCj9lksEgZCk
+kxaK4d430lZHovcA4ZeKTt94QcfjnIHRk65aZt93l17l52pv6n/srs3aRo/n5RV+
+wZ5sTLF0925ZQWJB5cIhzc8KQIvguGCX1znLQJJaRHyYOUXIN77AKEfALKAinBit
+25paDnbXAqGn1CR3UwFWUZZW+c3UEhWhmpghQpS1tIl0KI6IAvnrGIdw2kKIouo=
+-----END ENCRYPTED PRIVATE KEY-----`;*/
+
+const P384 = {
+ privateKeyPkcs8: `-----BEGIN PRIVATE KEY-----
+MIG2AgEAMBAGByqGSM49AgEGBSuBBAAiBIGeMIGbAgEBBDAYo22xn2kZjN8MInom
+NDsgD/zhpUwnCYch634jUgO59fN9m2lR5ekaI1XABHz39rihZANiAAQwXoCsPOLv
+Nn2STUs/hpL41CQveSL3WUmJ4QdtD7UFCl1mBO6ME0xSUgIQTUNkHt5k9CpOq3x9
+r+LG5+GcisoLn7R54R+bRoGp/p1ZBeuBXoCgthvs+RFoT3OewUmA8oQ=
+-----END PRIVATE KEY-----`,
+ publicKey: `-----BEGIN PUBLIC KEY-----
+MHYwEAYHKoZIzj0CAQYFK4EEACIDYgAEMF6ArDzi7zZ9kk1LP4aS+NQkL3ki91lJ
+ieEHbQ+1BQpdZgTujBNMUlICEE1DZB7eZPQqTqt8fa/ixufhnIrKC5+0eeEfm0aB
+qf6dWQXrgV6AoLYb7PkRaE9znsFJgPKE
+-----END PUBLIC KEY-----`
+};
+
+const P521 = {
+ privateKeyPkcs8: `-----BEGIN PRIVATE KEY-----
+MIHuAgEAMBAGByqGSM49AgEGBSuBBAAjBIHWMIHTAgEBBEIAifBaJDqNwOtKgThc
+FU34GzPQ73ubOQg9dnighpVGwA3b/KwCifimCNKDmKnXJaE04mEcxg8yzcFKausF
+5I8o206hgYkDgYYABAGwpkwrBBlZOdx4u9mxqYxJvtzAHaFFAzl21WQVbAjyrqXe
+nFPMkhbFpEEWr1ualPYKQkHe14AX33iU3fQ9MlBkgAAripsPbiKggAaog74cUERo
+qbrUFZwMbptGgovpE6pU93h7A1wb3Vtw9DZQCgiNbwzMbdsft+p2RJ8iSxWEC6Gd
+mw==
+-----END PRIVATE KEY-----`,
+ publicKey: `-----BEGIN PUBLIC KEY-----
+MIGbMBAGByqGSM49AgEGBSuBBAAjA4GGAAQBsKZMKwQZWTnceLvZsamMSb7cwB2h
+RQM5dtVkFWwI8q6l3pxTzJIWxaRBFq9bmpT2CkJB3teAF994lN30PTJQZIAAK4qb
+D24ioIAGqIO+HFBEaKm61BWcDG6bRoKL6ROqVPd4ewNcG91bcPQ2UAoIjW8MzG3b
+H7fqdkSfIksVhAuhnZs=
+-----END PUBLIC KEY-----`
+};
+
+const PEM_PPRIV_RSA512 = `-----BEGIN RSA PRIVATE KEY-----
+MIIBOQIBAAJBAPKr0Dp6YdItzOfk6a7ma7L4BF4LnelMYKtboGLrk6ihtqFPZFRL
+NcJi68Hvnt8stMrP50t6jqwWQ2EjMdkj6fsCAwEAAQJAOJUpM0lv36MAQR3WAwsF
+F7DOy+LnigteCvaNWiNVxZ6jByB5Qb7sall/Qlu9sFI0ZwrlVcKS0kldee7JTYlL
+WQIhAP3UKEfOtpTgT1tYmdhaqjxqMfxBom0Ri+rt9ajlzs6vAiEA9L85B8/Gnb7p
+6Af7/wpmafL277OV4X4xBfzMR+TUzHUCIBq+VLQkInaTH6lXL3ZtLwyIf9W9MJjf
+RWeuRLjT5bM/AiBF7Kw6kx5Hy1fAtydEApCoDIaIjWJw/kC7WTJ0B+jUUQIgV6dw
+NSyj0feakeD890gmId+lvl/w/3oUXiczqvl/N9o=
+-----END RSA PRIVATE KEY-----`;
+const PEM_PUB_RSA512 = `-----BEGIN PUBLIC KEY-----
+MFwwDQYJKoZIhvcNAQEBBQADSwAwSAJBAPKr0Dp6YdItzOfk6a7ma7L4BF4LnelM
+YKtboGLrk6ihtqFPZFRLNcJi68Hvnt8stMrP50t6jqwWQ2EjMdkj6fsCAwEAAQ==
+-----END PUBLIC KEY-----`;
+
+TestRegister.addTests([
+ {
+ name: "ECDSA Sign/Verify: P-256 with MD5",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs1, "MD5", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "MD5", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-256 with SHA1",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs1, "SHA-1", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-1", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-256 with SHA256",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs1, "SHA-256", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-256 with SHA384",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs1, "SHA-384", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-384", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-256 with SHA512",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs1, "SHA-512", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-512", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify:: Using a private key in PKCS#8 format works",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.privateKeyPkcs8, "SHA-256", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-384 with SHA384",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P384.privateKeyPkcs8, "SHA-384", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-384", P384.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign/Verify: P-521 with SHA512",
+ input: ASCII_TEXT,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P521.privateKeyPkcs8, "SHA-512", "ASN.1 HEX"]
+ },
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-512", P521.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+
+ // ECDSA Sign
+ {
+ name: "ECDSA Sign: Using public key fails",
+ input: ASCII_TEXT,
+ expectedOutput: "Provided key is not a private key.",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [P256.publicKey, "SHA-256", "ASN.1 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Sign: Using an RSA key fails",
+ input: ASCII_TEXT,
+ expectedOutput: "Provided key is not an EC key.",
+ recipeConfig: [
+ {
+ "op": "ECDSA Sign",
+ "args": [PEM_PPRIV_RSA512, "SHA-256", "ASN.1 HEX"]
+ }
+ ]
+ },
+
+ // ECDSA Verify
+ {
+ name: "ECDSA Verify: P-256 with SHA256 (ASN.1 signature)",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: P-256 with SHA256 (P1363 signature)",
+ input: P256.signature.sha256.p1363,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: P-256 with SHA256 (JWS signature)",
+ input: P256.signature.sha256.jws,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: P-256 with SHA256 (JSON signature)",
+ input: P256.signature.sha256.json,
+ expectedOutput: "Verified OK",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: JSON signature missing r",
+ input: JSON.stringify({s: JSON.parse(P256.signature.sha256.json).s}),
+ expectedOutput: 'No "r" value in the signature JSON',
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: JSON signature missing s",
+ input: JSON.stringify({r: JSON.parse(P256.signature.sha256.json).r}),
+ expectedOutput: 'No "s" value in the signature JSON',
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["Auto", "SHA-256", P256.publicKey, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: Using private key fails",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: "Provided key is not a public key.",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-256", P256.privateKeyPkcs1, ASCII_TEXT]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Verify: Using an RSA key fails",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: "Provided key is not an EC key.",
+ recipeConfig: [
+ {
+ "op": "ECDSA Verify",
+ "args": ["ASN.1 HEX", "SHA-256", PEM_PUB_RSA512, ASCII_TEXT]
+ }
+ ]
+ },
+
+ // ECDSA Signatur Conversion
+ {
+ name: "ECDSA Signature Conversion: ASN.1 To ASN.1",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: P256.signature.sha256.asn1,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "ASN.1 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: ASN.1 To P1363",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: P256.signature.sha256.p1363,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "P1363 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: ASN.1 To JWS",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: P256.signature.sha256.jws,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "JSON Web Signature"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: ASN.1 To JSON",
+ input: P256.signature.sha256.asn1,
+ expectedOutput: P256.signature.sha256.json,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "Raw JSON"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: P1363 To ASN.1",
+ input: P256.signature.sha256.p1363,
+ expectedOutput: P256.signature.sha256.asn1,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "ASN.1 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: P1363 To P1363",
+ input: P256.signature.sha256.p1363,
+ expectedOutput: P256.signature.sha256.p1363,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "P1363 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: P1363 To JWS",
+ input: P256.signature.sha256.p1363,
+ expectedOutput: P256.signature.sha256.jws,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "JSON Web Signature"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: P1363 To JSON",
+ input: P256.signature.sha256.p1363,
+ expectedOutput: P256.signature.sha256.json,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "Raw JSON"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: JSON To ASN.1",
+ input: P256.signature.sha256.json,
+ expectedOutput: P256.signature.sha256.asn1,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "ASN.1 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: JSON To P1363",
+ input: P256.signature.sha256.json,
+ expectedOutput: P256.signature.sha256.p1363,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "P1363 HEX"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: JSON To JWS",
+ input: P256.signature.sha256.json,
+ expectedOutput: P256.signature.sha256.jws,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "JSON Web Signature"]
+ }
+ ]
+ },
+ {
+ name: "ECDSA Signature Conversion: JSON To JSON",
+ input: P256.signature.sha256.json,
+ expectedOutput: P256.signature.sha256.json,
+ recipeConfig: [
+ {
+ "op": "ECDSA Signature Conversion",
+ "args": ["Auto", "Raw JSON"]
+ }
+ ]
+ }
+]);