Skip to content

Commit 49de927

Browse files
committed
New release: cardano-signer 1.14.0
Release Notes / Change-Logs * 1.14.0 * New dRep-Key generation mode: - generate conway dRep keys via the path `--path drep` or - generate conway dRep keys from the derivation path `1852'/1815'/acc'/3/idx'` - generate conway dRep keys from mnemonics or let cardano-signer generate new mnemonics for you * Key generation mode changes: - the flag `with-chain-code` has been replaced by the new flag `vkey-extended`. this makes it easier for the users to understand the meaning - per default the public keys are now always generated as non-extended keys, the secret keys are always extended ones if derived from a path * General - code cleanup
1 parent 201a69d commit 49de927

File tree

2 files changed

+105
-54
lines changed

2 files changed

+105
-54
lines changed

src/cardano-signer.js

+103-52
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.13.0"
3+
const version = "1.14.0"
44

55
//external dependencies
66
const CardanoWasm = require("@emurgo/cardano-serialization-lib-nodejs")
@@ -17,9 +17,9 @@ const crypto = require('crypto'); //used for crypto functions like entropy gener
1717
const parse_options = {
1818
string: ['secret-key', 'public-key', 'signature', 'address', 'rewards-address', 'payment-address', 'vote-public-key', 'data', 'data-hex', 'data-file', 'out-file', 'out-cbor', 'cose-sign1', 'cose-key', 'mnemonics', 'path'],
1919
number: ['nonce', 'vote-weight', 'vote-purpose'],
20-
boolean: ['json', 'json-extended', 'cip8', 'cip30', 'cip36', 'deregister', 'bech', 'hashed', 'nopayload', 'with-chain-code'], //all booleans are set to false per default
20+
boolean: ['json', 'json-extended', 'cip8', 'cip30', 'cip36', 'deregister', 'bech', 'hashed', 'nopayload', 'vkey-extended'], //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
22-
alias: { 'deregister': 'deregistration', 'cip36': 'cip-36', 'cip8': 'cip-8', 'cip30': 'cip-30', 'secret-key': 'signing-key', 'public-key': 'verification-key', 'rewards-address': 'reward-address', 'data': 'data-text', 'jcli' : 'bech', 'mnemonic': 'mnemonics' }
22+
alias: { 'deregister': 'deregistration', 'cip36': 'cip-36', 'cip8': 'cip-8', 'cip30': 'cip-30', '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
};
2424
const args = require('minimist')(process.argv.slice(2),parse_options);
2525

@@ -138,11 +138,11 @@ switch (topic) {
138138
console.log(``)
139139
console.log(` Syntax: ${Bright}${appname} ${FgGreen}keygen${Reset}`);
140140
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}`);
141-
console.log(` ${Dim}or predefined names: --path payment, --path stake, --path cip36${Reset}`);
141+
console.log(` ${Dim}or predefined names: --path payment, --path stake, --path cip36, --path drep${Reset}`);
142142
console.log(` [${FgGreen}--mnemonics${Reset} "word1 word2 ... word24"] ${Dim}optional mnemonic words to derive the key from (separate via space)${Reset}`);
143143
console.log(` [${FgGreen}--cip36${Reset}] ${Dim}optional flag to generate CIP36 conform vote keys (also using path 1694H/1815H/0H/0/0)${Reset}`);
144144
console.log(` [${FgGreen}--vote-purpose${Reset} <unsigned_int>] ${Dim}optional vote-purpose (unsigned int) together with --cip36 flag, default: 0 (Catalyst)${Reset}`);
145-
console.log(` [${FgGreen}--with-chain-code${Reset}] ${Dim}optional flag to generate a 128byte secretKey and 64byte publicKey with chain code${Reset}`);
145+
console.log(` [${FgGreen}--vkey-extended${Reset}] ${Dim}optional flag to generate a 64byte publicKey with chain code${Reset}`);
146146
console.log(` [${FgGreen}--json${Reset} |${FgGreen} --json-extended${Reset}] ${Dim}optional flag to generate output in json/json-extended format${Reset}`);
147147
console.log(` [${FgGreen}--out-file${Reset} "<path_to_file>"] ${Dim}path to an output file, default: standard-output${Reset}`);
148148
console.log(` [${FgGreen}--out-skey${Reset} "<path_to_skey_file>"] ${Dim}path to an output skey-file${Reset}`);
@@ -1275,7 +1275,7 @@ async function main() {
12751275
case "keygen-cip36":
12761276

12771277
//setup
1278-
var XpubKeyHex = '', XpubKeyBech = ''; vote_purpose = 0;
1278+
var XpubKeyHex = '', XpubKeyBech = '', vote_purpose = -1, drepIdHex = '', drepIdBech = '', prvKeyBech = '', pubKeyBech = '';
12791279

12801280
//get the path parameter, if ok set the derivation_path variable
12811281
var derivation_path = args['path'];
@@ -1287,6 +1287,7 @@ async function main() {
12871287
case 'PAYMENT': derivation_path = '1852H/1815H/0H/0/0'; break;
12881288
case 'STAKE': derivation_path = '1852H/1815H/0H/2/0'; break;
12891289
case 'CIP36': derivation_path = '1694H/1815H/0H/0/0'; break;
1290+
case 'DREP': derivation_path = '1852H/1815H/0H/3/0'; break;
12901291
}
12911292

12921293
if ( derivation_path.indexOf(`'`) > -1 ) { derivation_path = derivation_path.replace(/'/g,'H'); } //replace the ' char with a H char
@@ -1297,21 +1298,8 @@ async function main() {
12971298

12981299

12991300
//load or overwrite derivation path if CIP36 vote keys are selected
1300-
if ( args['cip36'] === true ) { var derivation_path = '1694H/1815H/0H/0/0'
1301+
if ( args['cip36'] === true ) { var derivation_path = '1694H/1815H/0H/0/0' }
13011302

1302-
//get the --vote-purpose parameter, set default = 0
1303-
var vote_purpose_param = args['vote-purpose'];
1304-
if ( typeof vote_purpose_param === 'undefined' ) { vote_purpose = 0 } //if not defined, set it to default=0
1305-
else if ( typeof vote_purpose_param === 'number' && vote_purpose_param >= 0 ) { vote_purpose = vote_purpose_param }
1306-
else { console.error(`Error: Please specify a --vote-purpose parameter with an unsigned integer value >= 0`); process.exit(1); }
1307-
1308-
}
1309-
1310-
//get a cleartext description of the purpose (shown in the --json-extended output)
1311-
switch (vote_purpose) {
1312-
case 0: var vote_purpose_description="Catalyst"; break;
1313-
default: var vote_purpose_description="Unknown";
1314-
}
13151303

13161304
//get mnemonics parameter, if ok set the mnemonics variable
13171305
var mnemonics = args['mnemonics'];
@@ -1370,12 +1358,15 @@ async function main() {
13701358
}
13711359
});
13721360

1373-
//if the extra flag 'with-chain-code' is set, generate a 128byte private key and a 64byte public key. otherwise generate a 64byte private key and 32byte public key
1374-
if ( args['with-chain-code'] === true ) {
1375-
var prvKeyHex = Buffer.from(rootKey.to_128_xprv()).toString('hex'); //private-secret key in hex format (64bytes private + 32bytes public + 32bytes chaincode)
1361+
1362+
//if derived, we always have an extended private secret key
1363+
var prvKeyHex = Buffer.from(rootKey.to_128_xprv()).toString('hex'); //private-secret key in hex format (64bytes private + 32bytes public + 32bytes chaincode)
1364+
//var prvKeyHex = Buffer.from(rootKey.to_raw_key().as_bytes()).toString('hex'); //private-secret key in hex format (64bytes) - not used here because its always an extended one
1365+
1366+
//if the extra flag 'vkey-extended' is set, generate a 64byte public key. otherwise generate a 32byte public key
1367+
if ( args['vkey-extended'] === true ) {
13761368
var pubKeyHex = Buffer.from(rootKey.to_public().as_bytes()).toString('hex'); //public key in hex format (64bytes)
13771369
} else {
1378-
var prvKeyHex = Buffer.from(rootKey.to_raw_key().as_bytes()).toString('hex'); //private-secret key in hex format (64bytes)
13791370
var pubKeyHex = Buffer.from(rootKey.to_public().as_bytes()).toString('hex').substring(0,64); //public key in hex format (cut it to a non-extended publickey, 32bytes)
13801371
}
13811372

@@ -1385,36 +1376,99 @@ async function main() {
13851376
var prvKeyCbor = cbor.encode(Buffer.from(prvKeyHex,'hex')).toString('hex')
13861377
var pubKeyCbor = cbor.encode(Buffer.from(pubKeyHex,'hex')).toString('hex')
13871378

1388-
//generate the content of the skey/vkey files
1389-
if ( derivation_path == '' ) { //simple ed25519 keys
13901379

1391-
var skeyContent = `{ "type": "PaymentSigningKeyShelley_ed25519", "description": "Payment Signing Key", "cborHex": "${prvKeyCbor}" }`;
1392-
var vkeyContent = `{ "type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
1380+
//generate the content depending on the derivation path
1381+
switch (derivation_path.substring(0,11)) {
13931382

1394-
} else if ( derivation_path.substring(0,11) == '1694H/1815H' ) { //CIP36 voting keys
1383+
case '': //simple ed25519 keys
13951384

1396-
var skeyContent = `{ "type": "CIP36VoteExtendedSigningKey_ed25519", "description": "${vote_purpose_description} Vote Signing Key", "cborHex": "${prvKeyCbor}" }`;
1397-
if ( args['with-chain-code'] === true ) {
1398-
var vkeyContent = `{ "type": "CIP36VoteExtendedVerificationKey_ed25519", "description": "${vote_purpose_description} Vote Verification Key", "cborHex": "${pubKeyCbor}" }`;
1399-
} else {
1400-
var vkeyContent = `{ "type": "CIP36VoteVerificationKey_ed25519", "description": "${vote_purpose_description} Vote Verification Key", "cborHex": "${pubKeyCbor}" }`;
1401-
}
1385+
var skeyContent = `{ "type": "PaymentSigningKeyShelley_ed25519", "description": "Payment Signing Key", "cborHex": "${prvKeyCbor}" }`;
1386+
var vkeyContent = `{ "type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
1387+
break;
14021388

1403-
} else if ( derivation_path.substring(0,11) == '1852H/1815H' ) { //Extended Payment/Staking keys
14041389

1405-
//check if purpose is a stake key
1406-
if ( derivation_path.split('/')[3] == '2' ) {
1407-
var skeyContent = `{ "type": "StakeExtendedSigningKeyShelley_ed25519_bip32", "description": "Stake Signing Key", "cborHex": "${prvKeyCbor}" }`;
1408-
var vkeyContent = `{ "type": "StakeExtendedVerificationKeyShelley_ed25519_bip32", "description": "Stake Verification Key", "cborHex": "${pubKeyCbor}" }`;
1409-
} else { //looks like a payment key
1410-
var skeyContent = `{ "type": "PaymentExtendedSigningKeyShelley_ed25519_bip32", "description": "Payment Signing Key", "cborHex": "${prvKeyCbor}" }`;
1411-
var vkeyContent = `{ "type": "PaymentExtendedVerificationKeyShelley_ed25519_bip32", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
1412-
}
1390+
case '1694H/1815H': //CIP36 voting keys
1391+
1392+
var skeyContent = `{ "type": "CIP36VoteExtendedSigningKey_ed25519", "description": "${vote_purpose_description} Vote Signing Key", "cborHex": "${prvKeyCbor}" }`;
1393+
if ( args['vkey-extended'] === true ) {
1394+
var vkeyContent = `{ "type": "CIP36VoteExtendedVerificationKey_ed25519", "description": "${vote_purpose_description} Vote Verification Key", "cborHex": "${pubKeyCbor}" }`;
1395+
} else {
1396+
var vkeyContent = `{ "type": "CIP36VoteVerificationKey_ed25519", "description": "${vote_purpose_description} Vote Verification Key", "cborHex": "${pubKeyCbor}" }`;
1397+
}
1398+
//generate the keys also in bech format
1399+
var prvKeyBech = bech32.encode("cvote_sk", bech32.toWords(Buffer.from(prvKeyHex, "hex")), 256); //encode in bech32 with a raised limit to 256 words because of the extralong privatekey (128bytes)
1400+
var pubKeyBech = bech32.encode("cvote_vk", bech32.toWords(Buffer.from(pubKeyHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer publickey (64bytes)
1401+
1402+
//get the --vote-purpose parameter, set default = 0
1403+
var vote_purpose_param = args['vote-purpose'];
1404+
if ( typeof vote_purpose_param === 'undefined' ) { vote_purpose = 0 } //if not defined, set it to default=0
1405+
else if ( typeof vote_purpose_param === 'number' && vote_purpose_param >= 0 ) { vote_purpose = vote_purpose_param }
1406+
else { console.error(`Error: Please specify a --vote-purpose parameter with an unsigned integer value >= 0`); process.exit(1); }
1407+
1408+
//get a cleartext description of the purpose (shown in the --json-extended output)
1409+
switch (vote_purpose) {
1410+
case 0: var vote_purpose_description="Catalyst"; break;
1411+
default: var vote_purpose_description="Unknown";
1412+
}
1413+
break;
1414+
1415+
1416+
case '1852H/1815H': //Extended Payment/Staking keys and also Drep keys
1417+
1418+
//generate different key outputs depending on the path number field 4 (idx=3) 1H/2H/3H/4/5
1419+
switch (derivation_path.split('/')[3]) {
1420+
1421+
case '2': //path is a stake key
1422+
1423+
var skeyContent = `{ "type": "StakeExtendedSigningKeyShelley_ed25519_bip32", "description": "Stake Signing Key", "cborHex": "${prvKeyCbor}" }`;
1424+
1425+
if ( args['vkey-extended'] === true ) {
1426+
var vkeyContent = `{ "type": "StakeExtendedVerificationKeyShelley_ed25519_bip32", "description": "Stake Verification Key", "cborHex": "${pubKeyCbor}" }`;
1427+
} else {
1428+
var vkeyContent = `{ "type": "StakeVerificationKeyShelley_ed25519", "description": "Stake Verification Key", "cborHex": "${pubKeyCbor}" }`;
1429+
}
1430+
break;
1431+
1432+
1433+
case '3': //path is a drep key
1434+
1435+
var skeyContent = `{ "type": "DRepExtendedSigningKey_ed25519_bip32", "description": "Delegate Representative Signing Key", "cborHex": "${prvKeyCbor}" }`;
1436+
1437+
if ( args['vkey-extended'] === true ) {
1438+
var vkeyContent = `{ "type": "DRepExtendedVerificationKey_ed25519_bip32", "description": "Delegate Representative Verification Key", "cborHex": "${pubKeyCbor}" }`;
1439+
} else {
1440+
var vkeyContent = `{ "type": "DRepVerificationKey_ed25519", "description": "Delegate Representative Verification Key", "cborHex": "${pubKeyCbor}" }`;
1441+
}
1442+
1443+
//also generate the drep id in hex and bech format
1444+
var drepIdHex = getHash(pubKeyHex, 28); //hash the publicKey with blake2b_224 (28bytes digest length)
1445+
var drepIdBech = bech32.encode("drep", bech32.toWords(Buffer.from(drepIdHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer hash (56bytes)
1446+
//generate the keys also in bech format
1447+
var prvKeyBech = bech32.encode("drep_sk", bech32.toWords(Buffer.from(prvKeyHex, "hex")), 256); //encode in bech32 with a raised limit to 256 words because of the extralong privatekey (128bytes)
1448+
var pubKeyBech = bech32.encode("drep_vk", bech32.toWords(Buffer.from(pubKeyHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer publickey (64bytes)
1449+
break;
1450+
1451+
1452+
default: //looks like a payment key
1453+
var skeyContent = `{ "type": "PaymentExtendedSigningKeyShelley_ed25519_bip32", "description": "Payment Signing Key", "cborHex": "${prvKeyCbor}" }`;
1454+
1455+
if ( args['vkey-extended'] === true ) {
1456+
var vkeyContent = `{ "type": "PaymentExtendedVerificationKeyShelley_ed25519_bip32", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
1457+
} else {
1458+
var vkeyContent = `{ "type": "PaymentVerificationKeyShelley_ed25519", "description": "Payment Verification Key", "cborHex": "${pubKeyCbor}" }`;
1459+
}
1460+
1461+
1462+
} //switch (derivation_path.split('/')[3])
1463+
break;
1464+
1465+
1466+
default: //generic ones
14131467

1414-
} else { //generic ones
14151468
var skeyContent = `{ "type": "ExtendedSigningKeyShelley_ed25519_bip32", "description": "Signing Key", "cborHex": "${prvKeyCbor}" }`;
14161469
var vkeyContent = `{ "type": "ExtendedVerificationKeyShelley_ed25519_bip32", "description": "Verification Key", "cborHex": "${pubKeyCbor}" }`;
1417-
}
1470+
1471+
} //switch (derivation_path.substring(0,11))
14181472

14191473

14201474
//compose the content for the output as JSON, extended JSON data or plain hex
@@ -1423,15 +1477,12 @@ async function main() {
14231477
} else if ( args['json-extended'] === true ) { //generate content in json format with additional fields
14241478
var content = `{ "workMode": "${workMode}"`
14251479
if ( derivation_path != '' ) { content += `, "path": "${derivation_path}"`; }
1480+
if ( vote_purpose > -1 ) { content += `, "votePurpose": "${vote_purpose_description} (${vote_purpose})"`; }
14261481
if ( mnemonics != '' ) { content += `, "mnemonics": "${mnemonics}"`; }
14271482
content += `, "secretKey": "${prvKeyHex}", "publicKey": "${pubKeyHex}"`;
14281483
if ( XpubKeyHex != '' ) { content += `, "XpubKeyHex": "${XpubKeyHex}", "XpubKeyBech": "${XpubKeyBech}"`; }
1429-
if ( args['cip36'] === true ) {
1430-
content += `, "votePurpose": "${vote_purpose_description} (${vote_purpose})"`;
1431-
prvKeyBech = bech32.encode("cvote_sk", bech32.toWords(Buffer.from(prvKeyHex, "hex")), 256); //encode in bech32 with a raised limit to 256 words because of the extralong privatekey (128bytes)
1432-
pubKeyBech = bech32.encode("cvote_vk", bech32.toWords(Buffer.from(pubKeyHex, "hex")), 128); //encode in bech32 with a raised limit to 128 words because of the longer publickey (64bytes)
1433-
content += `, "secretKeyBech": "${prvKeyBech}", "publicKeyBech": "${pubKeyBech}"`;
1434-
}
1484+
if ( drepIdHex != '' ) { content += `, "drepIdHex": "${drepIdHex}", "drepIdBech": "${drepIdBech}"`; }
1485+
if ( prvKeyBech != '' ) { content += `, "secretKeyBech": "${prvKeyBech}", "publicKeyBech": "${pubKeyBech}"`; }
14351486
content += `, "output": { "skey": ${skeyContent}, "vkey": ${vkeyContent} } }`
14361487
} else { //generate content in text format
14371488
var content = `${prvKeyHex} ${pubKeyHex}`;

src/package.json

+2-2
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
{
22
"name": "cardano-signer",
3-
"version": "1.13.0",
4-
"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 and cip-36 conform payload signing/verification.",
3+
"version": "1.14.0",
4+
"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/drep conform payload signing/verification.",
55
"main": "cardano-signer.js",
66
"scripts": {
77
"test": "echo \"Error: no test specified\" && exit 1"

0 commit comments

Comments
 (0)