Skip to content

Commit 49ca269

Browse files
committed
New release: cardano-signer 1.18.0
* 1.17.0 General: New Verification mode to analyze and verify Governance Metadata: - verify governance metadata following CIP-100, CIP-108, CIP-119 standard via the new `verify --cip100` option
1 parent 6864312 commit 49ca269

File tree

2 files changed

+133
-2
lines changed

2 files changed

+133
-2
lines changed

src/cardano-signer.js

+132-1
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.17.0"
3+
const version = "1.18.0"
44

55
//external dependencies
66
const CardanoWasm = require("@emurgo/cardano-serialization-lib-nodejs")
@@ -142,6 +142,20 @@ switch (topic) {
142142
console.log(``)
143143
break;
144144

145+
case 'verify-cip100':
146+
console.log(``)
147+
console.log(`${Bright}${Underscore}Verify Signatures in CIP-100 governance JSON-LD metadata:${Reset}`)
148+
console.log(``)
149+
console.log(` Syntax: ${Bright}${appname} ${FgGreen}verify --cip100${Reset}`);
150+
console.log(` Params: ${FgGreen}--data${Reset} "<jsonld-text>" | ${FgGreen}--data-file${Reset} "<path_to_jsonld_file>"${Reset}`);
151+
console.log(` ${Dim}data or file in jsonld format to verify${Reset}`);
152+
console.log(` [${FgGreen}--json${Reset} |${FgGreen} --json-extended${Reset}] ${Dim}optional flag to generate output in json/json-extended format${Reset}`);
153+
console.log(` [${FgGreen}--out-file${Reset} "<path_to_file>"] ${Dim}path to an output file, default: standard-output${Reset}`);
154+
console.log(` Output: ${FgCyan}"true/false"${Reset} or ${FgCyan}JSON-Format${Reset}`);
155+
console.log(``)
156+
break;
157+
158+
145159
case 'keygen':
146160
case 'keygen-cip36':
147161
console.log(``)
@@ -185,6 +199,7 @@ switch (topic) {
185199
showUsage('sign-cip36',false)
186200
showUsage('verify',false)
187201
showUsage('verify-cip8',false)
202+
showUsage('verify-cip100',false)
188203
showUsage('keygen',false)
189204
showUsage('hash-cip100',false)
190205
console.log(``)
@@ -1688,6 +1703,122 @@ async function main() {
16881703

16891704
break;
16901705

1706+
case "verify-cip100": //CANONIZE AND HASH JSONLD GOVERNANCE METADATA, CHECKS ALL THE AUTHORS SIGNATURES
1707+
1708+
//load default variable
1709+
var result = false //will be true if all authors signatures are valid/verified and no error occurs
1710+
var errorStr = '' //will hold an explanation about an error
1711+
var authors_array = [] //holds the authors for the output with there name and verified field
1712+
1713+
//lets try to load data from the data parameter
1714+
var data = args['data'];
1715+
if ( typeof data === 'undefined' || data == '' ) {
1716+
1717+
//no data parameter present, lets try the data-file parameter
1718+
var data_file = args['data-file'];
1719+
if ( typeof data_file === 'undefined' || data_file == '' ) {console.error(`Error: Missing data / data-file to hash`); showUsage(workMode);}
1720+
1721+
//data-file present lets try to read and parse the file
1722+
try {
1723+
var jsonld_input = JSON.parse(fs.readFileSync(data_file,'utf8')); //parse the given key as a json file
1724+
} catch (error) { console.error(`Error: Can't read data-file '${data_file}' or not valid JSON-LD data`); process.exit(1); }
1725+
1726+
} else {
1727+
//data parameter present, lets see if its valid json data
1728+
try {
1729+
var jsonld_input = JSON.parse(data);
1730+
} catch (error) { console.error(`Error: Not valid JSON data (${error})`); process.exit(1); }
1731+
}
1732+
1733+
//JSON data is loaded into jsonld_input, now lets only use the @context and body key
1734+
try {
1735+
if ( jsonld_input["body"] === undefined || jsonld_input["@context"] === undefined ) { console.error(`Error: JSON-LD must contain '@context' and 'body' data`); process.exit(1); }
1736+
var jsonld_data = { "body" : jsonld_input["body"], "@context": jsonld_input["@context"] }; // var jsonld_data = {}; jsonld_data["body"] = jsonld_doc["body"]; jsonld_data["@context"] = jsonld_doc["@context"];
1737+
} catch (error) { console.error(`Error: Couldn't extract '@context' and 'body' JSON-LD data (${error})`); process.exit(1); }
1738+
1739+
//Start the async canonize process, will get triggered via the .then part once the process finished
1740+
jsonld.canonize(jsonld_data, {safe: false, algorithm: 'URDNA2015', format: 'application/n-quads'}).then( (canonized_data) =>
1741+
{
1742+
//data was successfully canonized
1743+
1744+
//get the hash of the canonized data
1745+
if (jsonld_input["hashAlgorithm"] != 'blake2b-256') { console.error(`Error: unknown hashAlgorithm ${jsonld_input["hashAlgorithm"]}`); process.exit(1); }
1746+
var canonized_hash = getHash(Buffer.from(canonized_data).toString('hex'));
1747+
1748+
//do all the testing now in the verifyAuthors block
1749+
verifyAuthors: {
1750+
1751+
//check that the authors entry is present , if not break with an errordescription
1752+
if ( jsonld_input["authors"] === undefined ) { errorStr='missing authors entry'; break verifyAuthors; }
1753+
var jsonld_authors = jsonld_input["authors"];
1754+
//check that the authors entry is an array
1755+
if ( typeof jsonld_authors !== 'object' || jsonld_authors instanceof Array == false ) { errorStr='authors entry is not an array'; break verifyAuthors; }
1756+
//check that the number of authors is not zero
1757+
if ( jsonld_authors.length == 0) { errorStr='no authors in the authors-array'; break verifyAuthors; }
1758+
//check each authors array entry
1759+
jsonld_authors.every( authorEntry => {
1760+
var authorName = authorEntry["name"]; if (typeof authorName !== 'string') { errorStr='authors.name entry is not an string'; return false; }
1761+
var authorWitness = authorEntry["witness"]; if (typeof authorWitness !== 'object') { errorStr='authors.witness entry is missing or not a json object'; return false; }
1762+
var authorWitnessAlgorithm = authorWitness["witnessAlgorithm"]; if (authorWitnessAlgorithm != 'ed25519') { errorStr=`authors.witness.algorithm entry for '${authorName}' is missing or not 'ed25519'`; return false; }
1763+
1764+
var authorWitnessPublicKey = authorWitness["publicKey"]; if (typeof authorWitnessPublicKey !== 'string' || ! regExpHex.test(authorWitnessPublicKey) ) { errorStr=`authors.witness.publickey entry for '${authorName}' is not a valid hex-string`; return false; }
1765+
//load the public key
1766+
try {
1767+
var publicKey = CardanoWasm.PublicKey.from_bytes(Buffer.from(authorWitnessPublicKey,'hex'));
1768+
} catch (error) { errorStr=`authors.witness.publickey entry for '${authorName}' error ${error}`; return false; }
1769+
1770+
var authorWitnessSignature = authorWitness["signature"]; if (typeof authorWitnessSignature !== 'string' || ! regExpHex.test(authorWitnessSignature) ) { errorStr=`authors.witness.signature entry for '${authorName}' is not a valid hex-string`; return false; }
1771+
1772+
//load the Ed25519Signature
1773+
try {
1774+
var ed25519signature = CardanoWasm.Ed25519Signature.from_hex(authorWitnessSignature);
1775+
} catch (error) { errorStr=`authors.witness.signature entry for '${authorName}' error ${error}`; return false; }
1776+
1777+
//do the verification
1778+
var verified = publicKey.verify(Buffer.from(canonized_hash,'hex'),ed25519signature);
1779+
if (!verified) { errorStr=`at least one invalid signature found`; }
1780+
1781+
//add it to the array of authors
1782+
var authorArrayEntry = { "name" : authorName, "publicKey" : authorWitnessPublicKey, "signature" : authorWitnessSignature, "valid" : verified };
1783+
authors_array.push(authorArrayEntry);
1784+
1785+
//return=true -> go to the next entry
1786+
return true;
1787+
})
1788+
1789+
}
1790+
1791+
if ( errorStr == '' ) { result = true; }
1792+
1793+
//compose the content for the output
1794+
if ( args['json'] === true ) { //generate content in json format
1795+
var content = `{ "result": ${result}, "errorMsg": "${errorStr}", "authors": ` + JSON.stringify(authors_array) + ` }`;
1796+
} else if ( args['json-extended'] === true ) { //generate content in extended json format
1797+
//split the canonized data into an array, remove the last element, do a loop for each element
1798+
var canonized_array = [];
1799+
canonized_data.split('\n').slice(0,-1).forEach( (element) => {
1800+
canonized_array.push('"' + String(element).replace(/[\"]/g, '\\"') + '"'); //replace the " with a \" while pushing new elements to the array
1801+
})
1802+
var content = `{ "workMode": "${workMode}", "result": ${result}, "errorMsg": "${errorStr}", "authors": ` + JSON.stringify(authors_array) + `, "hash": "${canonized_hash}", "body": ` + JSON.stringify(jsonld_data["body"]) + `, "canonized": [ ${canonized_array} ] }`;
1803+
} else { //generate content in text format
1804+
var content = result;
1805+
}
1806+
1807+
//output the content to the console or to a file
1808+
var out_file = args['out-file'];
1809+
//if there is no --out-file parameter specified or the parameter alone (true) then output to the console
1810+
if ( typeof out_file === 'undefined' || out_file == '' ) { console.log(content);} //Output to console
1811+
else { //else try to write the content out to the given file
1812+
try {
1813+
fs.writeFileSync(out_file,content, 'utf8')
1814+
// file written successfully
1815+
} catch (error) { console.error(`${error}`); process.exit(1); }
1816+
}
1817+
1818+
}).catch( (err) => {console.log(`Error: Could not canonize the data (${err.message})`);process.exit(1);});
1819+
1820+
break;
1821+
16911822

16921823
default:
16931824
//if workMode is not found, exit with and errormessage and showUsage

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.17.0",
3+
"version": "1.18.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)