Skip to content

Commit

Permalink
Merge pull request #1275 from cplussharp/ec-asn1
Browse files Browse the repository at this point in the history
  • Loading branch information
a3957273 authored Apr 15, 2024
2 parents 8ab2256 + 21ac516 commit 7538be6
Show file tree
Hide file tree
Showing 7 changed files with 978 additions and 0 deletions.
4 changes: 4 additions & 0 deletions src/core/config/Categories.json
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
107 changes: 107 additions & 0 deletions src/core/operations/ECDSASign.mjs
Original file line number Diff line number Diff line change
@@ -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;
146 changes: 146 additions & 0 deletions src/core/operations/ECDSASignatureConversion.mjs
Original file line number Diff line number Diff line change
@@ -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;
Loading

0 comments on commit 7538be6

Please sign in to comment.