Skip to content

Commit

Permalink
Sign/Verify Operations for ECDSA
Browse files Browse the repository at this point in the history
also an Operation for ECDSA signature conversion,
as there could be multiple formats of the signature
  • Loading branch information
cplussharp committed Oct 6, 2023
1 parent 0f2e81c commit dd58568
Show file tree
Hide file tree
Showing 6 changed files with 727 additions and 0 deletions.
3 changes: 3 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -168,6 +168,9 @@
"RSA Encrypt",
"RSA Decrypt",
"Generate ECDSA Key Pair",
"ECDSA Signature Conversion",
"ECDSA Sign",
"ECDSA Verify",
"Parse SSH Host Key"
]
},
Expand Down
100 changes: 100 additions & 0 deletions src/core/operations/ECDSASign.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,100 @@
/**
* @author cplussharp
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.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",
"Concat HEX",
"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 "Concat HEX":
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
break;
case "JSON": {
const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
result = JSON.stringify(signatureRS);
break;
}
}

return result;
}
}

export default ECDSASign;
106 changes: 106 additions & 0 deletions src/core/operations/ECDSASignatureConversion.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,106 @@
/**
* @author cplussharp
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import Operation from "../Operation.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",
"Concat HEX",
"JSON"
]
},
{
name: "Output Format",
type: "option",
value: [
"ASN.1 HEX",
"Concat HEX",
"JSON"
]
}
];
}

/**
* @param {string} input
* @param {Object[]} args
* @returns {string}
*/
run(input, args) {
let inputFormat = args[0];
const outputFormat = args[1];

// detect input format
if (inputFormat === "Auto") {
if (input.substr(0, 2) === "30" && r.ASN1HEX.isASN1HEX(input)) {
inputFormat = "ASN.1 HEX";
} else if (input.indexOf("{") !== -1) {
inputFormat = "JSON";
} else {
inputFormat = "Concat HEX";
}
}

// convert input to ASN.1 hex
let signatureASN1Hex;
switch (inputFormat) {
case "ASN.1 HEX":
signatureASN1Hex = input;
break;
case "Concat HEX":
signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(input);
break;
case "JSON": {
const inputJson = JSON.parse(input);
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 "Concat HEX":
result = r.KJUR.crypto.ECDSA.asn1SigToConcatSig(signatureASN1Hex);
break;
case "JSON": {
const signatureRS = r.KJUR.crypto.ECDSA.parseSigHexInHexRS(signatureASN1Hex);
result = JSON.stringify(signatureRS);
break;
}
}

return result;
}
}

export default ECDSASignatureConversion;
120 changes: 120 additions & 0 deletions src/core/operations/ECDSAVerify.mjs
Original file line number Diff line number Diff line change
@@ -0,0 +1,120 @@
/**
* @author cplussharp
* @copyright Crown Copyright 2021
* @license Apache-2.0
*/

import Operation from "../Operation.mjs";
import OperationError from "../errors/OperationError.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",
"Concat HEX",
"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
if (inputFormat === "Auto") {
if (input.substr(0, 2) === "30" && r.ASN1HEX.isASN1HEX(input)) {
inputFormat = "ASN.1 HEX";
} else if (input.indexOf("{") !== -1) {
inputFormat = "JSON";
} else {
inputFormat = "Concat HEX";
}
}

// convert to ASN.1 signature
let signatureASN1Hex;
switch (inputFormat) {
case "ASN.1 HEX":
signatureASN1Hex = input;
break;
case "Concat HEX":
signatureASN1Hex = r.KJUR.crypto.ECDSA.concatSigToASN1Sig(input);
break;
case "JSON": {
const inputJson = JSON.parse(input);
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;
1 change: 1 addition & 0 deletions tests/operations/index.mjs
Original file line number Diff line number Diff line change
Expand Up @@ -43,6 +43,7 @@ import "./tests/ConditionalJump.mjs";
import "./tests/Crypt.mjs";
import "./tests/CSV.mjs";
import "./tests/DateTime.mjs";
import "./tests/ECDSA.mjs";
import "./tests/ExtractEmailAddresses.mjs";
import "./tests/Fork.mjs";
import "./tests/FromDecimal.mjs";
Expand Down
Loading

0 comments on commit dd58568

Please sign in to comment.