Skip to content

Commit c37bd5c

Browse files
committed
New release: cardano-signer 1.24.0
## Release Notes / Change-Logs * 1.24.0 #### Calidus Pool-Key updates - A new path shortcut `--path calidus` was added to the `keygen` function - Using the new calidus path also switches the output description of skey/vkey files to be `Calidus Pool Signing Key` and `Calidus Pool Verification Key` - Using the new calidus path also outputs the new `Calidus-ID` in hex and bech format with the `--json-extended` flag - The `sign --cip88` function to generate Calidus Key registration data now also outputs the new `Calidus-ID` in hex and bech format. In addition it also outputs the `Pool-ID` in bech format. - The `verify --cip88` function to verify Calidus Key registration data now also outputs the new `Calidus-ID` in hex and bech format. In addition it also outputs the `Pool-ID` in bech format. #### Other updates - A new internal function was created to convert maps in to json format, this is simplifying various inputs and output in the future - The key7 entry for the CIP88v2 format was renamed from `update-key` to `calidus-key`
1 parent 4fd0a6a commit c37bd5c

File tree

2 files changed

+65
-28
lines changed

2 files changed

+65
-28
lines changed

src/cardano-signer.js

+64-27
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
//define name and version
22
const appname = "cardano-signer"
3-
const version = "1.23.0"
3+
const version = "1.24.0"
44

55
//external dependencies
66
const CardanoWasm = require("@emurgo/cardano-serialization-lib-nodejs")
@@ -17,7 +17,7 @@ const jsonld = require('jsonld'); //used for canonizing json data (governance CI
1717
//set the options for the command-line arguments. needed so that arguments like data-hex="001122" are not parsed as numbers
1818
const parse_options = {
1919
string: ['secret-key', 'public-key', 'signature', 'address', 'rewards-address', 'payment-address', 'vote-public-key', 'calidus-public-key', 'data', 'data-hex', 'data-file', 'out-file', 'out-cbor', 'out-skey', 'out-vkey', 'out-canonized', 'cose-sign1', 'cose-key', 'mnemonics', 'path', 'testnet-magic', 'mainnet', 'author-name', 'passphrase'],
20-
boolean: ['help', 'version', 'usage', 'json', 'json-extended', 'cip8', 'cip30', 'cip36', 'cip88', 'cip100', 'deregister', 'jcli', 'bech', 'hashed', 'nopayload', 'vkey-extended', 'nohashcheck', 'replace', 'ledger', 'trezor'], //all booleans are set to false per default
20+
boolean: ['help', 'version', 'usage', 'json', 'json-extended', 'cip8', 'cip30', 'cip36', 'cip88', 'cip100', 'deregister', 'jcli', 'bech', 'hashed', 'nopayload', 'vkey-extended', 'nohashcheck', 'replace', 'ledger', 'trezor', 'include-maps'], //all booleans are set to false per default
2121
//adding some aliases so users can also use variants of the original parameters. for example using --signing-key instead of --secret-key
2222
alias: { 'deregister': 'deregistration', 'cip36': 'cip-36', 'cip8': 'cip-8', 'cip30': 'cip-30', 'cip100': 'cip-100', 'secret-key': 'signing-key', 'public-key': 'verification-key', 'rewards-address': 'reward-address', 'data': 'data-text', 'jcli' : 'bech', 'mnemonic': 'mnemonics', 'vkey-extended': 'with-chain-code' },
2323
unknown: function(unknownParameter) {
@@ -166,6 +166,7 @@ switch (topic) {
166166
console.log(` ${Dim}optional data/payload/file if not present in the COSE_Sign1 signature${Reset}`);
167167
console.log(` [${FgGreen}--address${Reset} "<path_to_file>|<hex>|<bech>"] ${Dim}optional signing-address to do the verification with${Reset}`);
168168
console.log(` [${FgGreen}--nohashcheck${Reset}] ${Dim}optional flag to not perform a check that the public-key belongs to the address/hash${Reset}`);
169+
console.log(` [${FgGreen}--include-maps${Reset}] ${Dim}optional flag to include the COSE maps in the json-extended output${Reset}`);
169170
console.log(` [${FgGreen}--json${Reset} |${FgGreen} --json-extended${Reset}] ${Dim}optional flag to generate output in json/json-extended format${Reset}`);
170171
console.log(` [${FgGreen}--out-file${Reset} "<path_to_file>"] ${Dim}path to an output file, default: standard-output${Reset}`);
171172
console.log(` Output: ${FgCyan}"true/false" (exitcode 0/1)${Reset} or ${FgCyan}JSON-Format${Reset}`)
@@ -208,7 +209,8 @@ switch (topic) {
208209
console.log(``)
209210
console.log(` Syntax: ${Bright}${appname} ${FgGreen}keygen${Reset}`);
210211
console.log(` Params: [${FgGreen}--path${Reset} "<derivationpath>"] ${Dim}optional derivation path in the format like "1852H/1815H/0H/0/0" or "1852'/1815'/0'/0/0"${Reset}`);
211-
console.log(` ${Dim}or predefined names: --path payment, --path stake, --path cip36, --path drep, --path cc-cold, --path cc-hot, --path pool${Reset}`);
212+
console.log(` ${Dim}or predefined names: --path payment, --path stake, --path cip36, --path drep, --path cc-cold,${Reset}`);
213+
console.log(` ${Dim} --path cc-hot, --path pool, --path calidus${Reset}`);
212214
console.log(` [${FgGreen}--mnemonics${Reset} "word1 word2 ... word24"] ${Dim}optional mnemonic words to derive the key from (separate via space)${Reset}`);
213215
console.log(` [${FgGreen}--passphrase${Reset} "passphrase"] ${Dim}optional passphrase for --ledger or --trezor derivation method${Reset}`);
214216
console.log(` [${FgGreen}--ledger | --trezor${Reset}] ${Dim}optional flag to set the derivation type to "Ledger" or "Trezor" hardware wallet${Reset}`);
@@ -460,6 +462,7 @@ function readAddr2hex(addr, publicKey) { //reads a cardano address from a file (
460462
case '13': addr_type = 'committee-cold script cip129'; break hashcheck; break;
461463
case '22': addr_type = 'drep cip129'; break hashcheck; break;
462464
case '23': addr_type = 'drep script cip129'; break hashcheck; break;
465+
case 'a1': addr_type = 'calidus pool key'; break hashcheck; break;
463466
}
464467
}
465468

@@ -596,9 +599,15 @@ const jsToMap = (obj) => {
596599
}
597600
}
598601

599-
600-
601-
602+
const mapToJs = (obj) => {
603+
return JSON.stringify(obj, function (key, value) {
604+
if (value instanceof Map) { return Object.fromEntries(value) }
605+
else if (value instanceof Set) { const setArray = []; for( const item of value.values()){setArray.push(item)}; return setArray }
606+
else if (value == null) { return null }
607+
else if (value['type'] == 'Buffer') { return `0x${Buffer.from(value['data']).toString('hex')}` }
608+
return value
609+
})
610+
}
602611

603612
// VERIFY CIP8/30 FUNCTION -> coded as a function so it can be reused within other functions
604613
function verifyCIP8(workMode = "verify-cip8", calling_args = process.argv.slice(3)) { //default calling arguments are the same as calling the main process - can be modified by subfunctions
@@ -761,7 +770,6 @@ function verifyCIP8(workMode = "verify-cip8", calling_args = process.argv.slice(
761770
var ed25519signature = CardanoWasm.Ed25519Signature.from_hex(signature_hex);
762771
} catch (error) { throw {'msg': `${error}`}; }
763772

764-
765773
//generate the protectedHeader with the current values (the address within it might have been overwritten by a given one)
766774
// alg (1) - must be set to EdDSA (-8)
767775
// kid (4) - Optional, if present must be set to the same value as in the COSE_Key specified below. It is recommended to be set to the same value as in the "address" header.
@@ -792,8 +800,10 @@ function verifyCIP8(workMode = "verify-cip8", calling_args = process.argv.slice(
792800
if ( payload_data_hex.length <= 2000000 ) { content += `"payloadDataHex": "${payload_data_hex}", `; } //only include the payload_data_hex if it is less than 2M of chars
793801
content += `"isHashed": "${isHashed}",`;
794802
if ( Sig_structure_cbor_hex.length <= 2000000 ) { content += `"verifyDataHex": "${Sig_structure_cbor_hex}", `; } //only include the Sig_structure_cbor_hex if it is less than 2M of chars
795-
content += `"signature": "${signature_hex}",`;
796-
content += `"publicKey": "${pubKey}" }`
803+
content += `"signature": "${signature_hex}", "publicKey": "${pubKey}"`;
804+
if ( sub_args['include-maps'] === true ) { //generate content also with JSON-Maps for the COSE_Key, COSE_Sign1 and verifyData structures
805+
content += `, "maps": { "COSE_Key": ${mapToJs(COSE_Key_structure)}, "COSE_Sign1": ${mapToJs(COSE_Sign1_structure)}, "verifyData": ${mapToJs(Sig_structure)} }` }
806+
content += ` }`
797807
} else { //generate content in text format
798808
var content = `${verified}`;
799809
}
@@ -1449,6 +1459,10 @@ async function main() {
14491459
var calidusPubKey = CardanoWasm.PublicKey.from_bytes(Buffer.from(calidusPubKeyHex,'hex'));
14501460
} catch (error) { console.error(`Error: ${error}`); process.exit(1); }
14511461

1462+
//generate the calidus-id in hex and bech format
1463+
var calidusIdHex = `a1${getHash(calidusPubKeyHex, 28)}`; //hash the calidus publicKey with blake2b_224 (28bytes digest length) and add the prebyte a1=CalidusPoolKey
1464+
var calidusIdBech = bech32.encode("calidus", bech32.toWords(Buffer.from(calidusIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
1465+
14521466
//get secret key -> store it in prvKeyHex
14531467
var key_file_hex = args['secret-key'];
14541468
if ( typeof key_file_hex === 'undefined' || key_file_hex === true ) { console.error(`Error: Missing secret key parameter`); showUsage(workMode); }
@@ -1467,6 +1481,7 @@ async function main() {
14671481

14681482
//calculate the pool-id, which is just the hash of the pubKey
14691483
var poolIdHex = getHash(pubKeyHex,28)
1484+
var poolIdBech = bech32.encode("pool", bech32.toWords(Buffer.from(poolIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
14701485

14711486
//get the --nonce parameter
14721487
var nonce = args['nonce'];
@@ -1515,19 +1530,15 @@ async function main() {
15151530
//compose the content for the output as JSON registration, extended JSON data or plain registrationCBOR
15161531
if ( args['json'] === true ) { //generate content in json format
15171532

1518-
var content_payload = `{ "1": [ 1, "0x${poolIdHex}" ], "2": [], "3": [2], "4": ${nonce}, "7": "0x${calidusPubKeyHex}" }`;
1519-
var content_witness_public = `{ "1": 1, "3": -8, "-1": 6, "-2": "0x${pubKeyHex}" }`;
1520-
var content_witness_signature = `[ "0x` + Buffer.from(coseSign1Map[0]).toString('hex') + `", ${coseSign1Map[1]}, "0x` + Buffer.from(coseSign1Map[2]).toString('hex') + `", "0x` + Buffer.from(coseSign1Map[3]).toString('hex') + `" ]`;
1521-
var content = `{ "867": { "0": 2, "1": ${content_payload}, "2": [ { "1": ${content_witness_public}, "2": ${content_witness_signature} } ] } }`;
1522-
// content = JSON.stringify(JSON.parse(content_json), null, 4); //just to make a pretty json output
1533+
var content = mapToJs(registrationMap)
15231534

15241535
} else if ( args['json-extended'] === true ) { //generate content in json format with additional fields
15251536

1526-
var content_payload = `{ "1": [ 1, "0x${poolIdHex}" ], "2": [], "3": [2], "4": ${nonce}, "7": "0x${calidusPubKeyHex}" }`;
1527-
var content_witness_public = `{ "1": 1, "3": -8, "-1": 6, "-2": "0x${pubKeyHex}" }`;
1528-
var content_witness_signature = `[ "0x` + Buffer.from(coseSign1Map[0]).toString('hex') + `", ${coseSign1Map[1]}, "0x` + Buffer.from(coseSign1Map[2]).toString('hex') + `", "0x` + Buffer.from(coseSign1Map[3]).toString('hex') + `" ]`;
1529-
var content_output_json = `{ "867": { "0": 2, "1": ${content_payload}, "2": [ { "1": ${content_witness_public}, "2": ${content_witness_signature} } ] } }`;
1530-
var content = `{ "workMode": "${workMode}", "poolIdHex": "${poolIdHex}", "calidusPublicKey": "${calidusPubKeyHex}", "secretKey": "${prvKeyHex}", "publicKey": "${pubKeyHex}", "nonce": ${nonce}, "payloadCbor": "${payloadCborHex}", "payloadHash": "${payloadCborHash}", "coseSign1Hex": "${ret.cose_sign1_cbor_hex}", "coseKeyHex": "${ret.cose_key_cbor_hex}", "output": { "cbor": "${registrationCborHex}", "json": ${content_output_json} } }`;
1537+
var content = `{ "workMode": "${workMode}", "poolIdHex": "${poolIdHex}", "poolIdBech": "${poolIdBech}",`;
1538+
content += `"calidusPublicKey": "${calidusPubKeyHex}", "calidusIdHex": "${calidusIdHex}", "calidusIdBech": "${calidusIdBech}",`;
1539+
content += `"secretKey": "${prvKeyHex}", "publicKey": "${pubKeyHex}", "nonce": ${nonce}, "payloadCbor": "${payloadCborHex}",`;
1540+
content += `"payloadHash": "${payloadCborHash}", "coseSign1Hex": "${ret.cose_sign1_cbor_hex}", "coseKeyHex": "${ret.cose_key_cbor_hex}",`;
1541+
content += `"output": { "cbor": "${registrationCborHex}", "json": ${mapToJs(registrationMap)} } }`;
15311542

15321543
} else { //generate content in text format
15331544
var content = `${registrationCborHex}`;
@@ -1716,6 +1727,7 @@ async function main() {
17161727
case 'CC-COLD': derivation_path = '1852H/1815H/0H/4/0'; break;
17171728
case 'CC-HOT': derivation_path = '1852H/1815H/0H/5/0'; break;
17181729
case 'POOL': derivation_path = '1853H/1815H/0H/0H'; break;
1730+
case 'CALIDUS': derivation_path = '1852H/1815H/0H/0/0'; break;
17191731
}
17201732

17211733
if ( derivation_path.indexOf(`'`) > -1 ) { derivation_path = derivation_path.replace(/'/g,'H'); } //replace the ' char with a H char
@@ -1976,14 +1988,28 @@ async function main() {
19761988

19771989

19781990
default: //looks like a payment key
1979-
var skeyContent = `{ "type": "PaymentExtendedSigningKeyShelley_ed25519_bip32", "description": "Payment Signing Key", "cborHex": "${prvKeyCbor}" }`;
19801991

1992+
switch (args['path'].toUpperCase()) {
1993+
1994+
case 'CALIDUS': //path is --path calidus -> generate the calidusID and special description for the skey/vkey content
1995+
var calidusIdHex = `a1${getHash(pubKeyHex, 28)}`; //hash the publicKey with blake2b_224 (28bytes digest length) and add the prebyte a1=CalidusPoolKey
1996+
var calidusIdBech = bech32.encode("calidus", bech32.toWords(Buffer.from(calidusIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
1997+
var keyFileDescription = "Calidus Pool"
1998+
break;
1999+
2000+
default: //standard payment key
2001+
var keyFileDescription = "Payment"
2002+
break;
2003+
2004+
} //switch args['path']
2005+
2006+
var skeyContent = `{ "type": "PaymentExtendedSigningKeyShelley_ed25519_bip32", "description": "${keyFileDescription} Signing Key", "cborHex": "${prvKeyCbor}" }`;
19812007
if ( args['vkey-extended'] === true ) {
1982-
var vkeyContent = `{ "type": "PaymentExtendedVerificationKeyShelley_ed25519_bip32", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
2008+
var vkeyContent = `{ "type": "PaymentExtendedVerificationKeyShelley_ed25519_bip32", "description": "${keyFileDescription} Verification Key", "cborHex": "${pubKeyCbor}" }`;
19832009
} else {
1984-
var vkeyContent = `{ "type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
2010+
var vkeyContent = `{ "type": "PaymentVerificationKeyShelley_ed25519", "description": "${keyFileDescription} Verification Key", "cborHex": "${pubKeyCbor}" }`;
19852011
}
1986-
2012+
break;
19872013

19882014
} //switch (derivation_path.split('/')[3])
19892015
break;
@@ -2033,6 +2059,7 @@ async function main() {
20332059
else if ( ccColdIdHex != '' ) { content += `, "ccColdIdHex": "${ccColdIdHex}", "ccColdIdBech": "${ccColdIdBech}"`; }
20342060
else if ( ccHotIdHex != '' ) { content += `, "ccHotIdHex": "${ccHotIdHex}", "ccHotIdBech": "${ccHotIdBech}"`; }
20352061
else if ( poolIdHex != '' ) { content += `, "poolIdHex": "${poolIdHex}", "poolIdBech": "${poolIdBech}"`; }
2062+
else if ( calidusIdHex != '' ) { content += `, "calidusIdHex": "${calidusIdHex}", "calidusIdBech": "${calidusIdBech}"`; }
20362063
if ( prvKeyBech != '' ) { content += `, "secretKeyBech": "${prvKeyBech}", "publicKeyBech": "${pubKeyBech}"`; }
20372064
content += `, "output": { "skey": ${skeyContent}, "vkey": ${vkeyContent} } }`
20382065
} else { //generate content in text format
@@ -2551,7 +2578,7 @@ async function main() {
25512578
else if ( ! payloadMap.get(1) instanceof Array || payloadMap.get(1)[0] != 1 ) { console.error(`Error: PayloadMap key 1 (registraction-scope) is not an array or not of type pool-scope(1)`); process.exit(1); }
25522579
else if ( ! payloadMap.get(3) instanceof Array || isNaN(payloadMap.get(3)[0]) || payloadMap.get(3)[0] != 2) { console.error(`Error: PayloadMap key 3 (validation method) is not an array and/or not set to [2] -> CIP-0008`); process.exit(1); }
25532580
else if ( isNaN(payloadMap.get(4)) ) { console.error(`Error: PayloadMap key 4 (nonce) is not a uint number`); process.exit(1); }
2554-
else if ( ! Buffer.isBuffer(payloadMap.get(7)) || payloadMap.get(7).length != 32 ) { console.error(`Error: PayloadMap key 7 (update-public-key) is not a hex bytearray/buffer of length 32`); process.exit(1); }
2581+
else if ( ! Buffer.isBuffer(payloadMap.get(7)) || payloadMap.get(7).length != 32 ) { console.error(`Error: PayloadMap key 7 (calidus-public-key) is not a hex bytearray/buffer of length 32`); process.exit(1); }
25552582

25562583
//get the poolid from the scope=1 content and check that its a bytearray with the correct length
25572584
var scopePoolId = payloadMap.get(1)[1] // [ 1 , h'(poolid-hex)' ]
@@ -2562,7 +2589,7 @@ async function main() {
25622589
try {
25632590
var calidusPubKey = CardanoWasm.PublicKey.from_bytes(payloadMap.get(7));
25642591
var calidusPubKeyHex = payloadMap.get(7).toString('hex')
2565-
} catch (error) { console.error(`Error: PayloadMap key 7 (update-public-key) -> ${error}`); process.exit(1); }
2592+
} catch (error) { console.error(`Error: PayloadMap key 7 (calidus-public-key) -> ${error}`); process.exit(1); }
25662593

25672594
//ok, the payloadMap should be fine, lets generate the cbor representation of it and also the hash for the message verification
25682595
var payloadCborHex = cbor.encode(payloadMap).toString('hex');
@@ -2622,9 +2649,19 @@ async function main() {
26222649
if ( args['json'] === true ) { //generate content in json format
26232650
var content = `{ "result": "${json_ret['result']}" }`;
26242651
} else if ( args['json-extended'] === true ) { //generate content in json format with additional fields
2625-
var content = `{ "workMode": "${workMode}", "result": "${ret.verified}", "poolIdHex": "${scopePoolIdHex}", "calidusPublicKey": "${calidusPubKeyHex}", "publicKey": "${pubKeyHex}",`;
2626-
content += `"nonce": ${payloadMap.get(4)}, "payloadCbor": "${payloadCborHex}", "payloadHash": "${payloadCborHash}", "isHashed": "${json_ret['isHashed']}", "verifyDataHex": "${verifyDataHex}",`;
2652+
2653+
//generate the calidus-id in hex and bech format for the json-extended output
2654+
var calidusIdHex = `a1${getHash(calidusPubKeyHex, 28)}`; //hash the calidus publicKey with blake2b_224 (28bytes digest length) and add the prebyte a1=CalidusPoolKey
2655+
var calidusIdBech = bech32.encode("calidus", bech32.toWords(Buffer.from(calidusIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
2656+
2657+
//generate the bech pool-id for the json-extended output
2658+
var poolIdBech = bech32.encode("pool", bech32.toWords(Buffer.from(scopePoolIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
2659+
2660+
var content = `{ "workMode": "${workMode}", "result": "${ret.verified}", "poolIdHex": "${scopePoolIdHex}", "poolIdBech": "${poolIdBech}", `;
2661+
content += `"calidusPublicKey": "${calidusPubKeyHex}", "calidusIdHex": "${calidusIdHex}", "calidusIdBech": "${calidusIdBech}", "publicKey": "${pubKeyHex}", `;
2662+
content += `"nonce": ${payloadMap.get(4)}, "payloadCbor": "${payloadCborHex}", "payloadHash": "${payloadCborHash}", "isHashed": "${json_ret['isHashed']}", "verifyDataHex": "${verifyDataHex}", `;
26272663
content += `"coseSign1Hex": "${coseSign1Hex}", "coseKeyHex": "${coseKeyHex}", "coseSignature": "${json_ret['signature']}" }`;
2664+
26282665
} else { //generate content in text format
26292666
var content = `${json_ret['result']}`;
26302667
}

src/package.json

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
{
22
"name": "cardano-signer",
3-
"version": "1.23.0",
3+
"version": "1.24.0",
44
"description": "cardano-signer signs a given data(hex/text/file) with a signing key(hex/bech/file) or verify the signature via a public key(hex/bech/file). it can also produce a cip-8/cip-30/cip-36 conform payload signing/verification. can produce ed25519 keys from mnemonic for payment, staking, drep, constitutional commitee cold/hot keys, etc...",
55
"main": "cardano-signer.js",
66
"scripts": {

0 commit comments

Comments
 (0)