Skip to content

Instantly share code, notes, and snippets.

@ilap
Last active December 29, 2021 14:25
Show Gist options
  • Star 4 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ilap/8001dc5883d09088ee752bcf0f91be06 to your computer and use it in GitHub Desktop.
Save ilap/8001dc5883d09088ee752bcf0f91be06 to your computer and use it in GitHub Desktop.
Shelley Cold Key Generation

Shelley Cold Key Generation

Shelley Cold Key Generation Proof of Concept. Node.js is required for this PoC.

It's compatible /w cardano-node v1.16.0

Install

package.json

{
  "name": "cold-js",
  "version": "0.0.1",
  "main": "main.js",
  "author": "",
  "license": "MIT",
  "dependencies": {
    "bip39": "^3.0.2",
    "ed25519": "^0.0.4",
    "@anzerr/blake2b": "^1.0.11",
    "yargs": "^15.4.1"
  },
  "devDependencies": {},
  "description": ""
}

Example

$ mkdir cold && pushd cold
$ cat << EOF >> package.json
{
  "name": "cold-js",
  "version": "0.0.1",
  "main": "main.js",
  "author": "",
  "license": "MIT",
  "dependencies": {
    "bip39": "^3.0.2",
    "ed25519": "^0.0.4",
    "@anzerr/blake2b": "^1.0.11",
    "yargs": "^15.4.1"
  },
  "devDependencies": {},
  "description": ""
}
EOF

$ npm i
$ node gen_cold.js 
No seed nor mnemonic presented, generating randomly...
No seed nor mnemonic presented, generating randomly...
Write down the following 24-length mnemonic for recovery
24-word length mnemonic : laptop fall split foil human hazard long logic skate burden wife boy half tent bomb mosquito stool near tool unlock crucial wrap gym execute

Writing cold signing key to "cold.skey" file
Writing cold verifying key to "cold.vkey file."
Writing cold counter to "cold.counter" file.
Writing cold pool id to "cold.id" file.


$ node gen_cold.js -h
Options:
  --version       Show version number                                  [boolean]
  --mnemonic, -m  24-word length mnemonic                                [array]
  --seed, -s      hexa representation of a 32-byte long random seed     [string]
  --output, -o    Output prefix of the files. e.g. -o /tmp/UNDR: default: cold
                                                                        [string]
  --help, -h      Show help                                            [boolean]

$ popd

main.js

const bip39 = require('bip39')
const blake = require('@anzerr/blake2b')
const ed25519 = require('ed25519')
const yargs = require('yargs')
const fs = require('fs')

function toByteArray(hexString) {
  var result = new Uint8Array(hexString.length / 2)
  for (var i = 0; i < hexString.length; i += 2) {
    result[i / 2] = parseInt(hexString.substring(i, i + 2), 16)
  }
  return result
}

/**
 * Main
 * ```
 * $ node ./doCold.js -o /tmp/UNDR 
 * No seed nor mnemonic presented, generating randomly...
 * 
 * Write down the following 24-length mnemonic for recovery
 * 24-word length mnemonic : assume diesel surround alarm matter age candy message chaos moral relief fortune because heart gorilla modify surface twelve answer process avoid private pipe spot
 * 
 * Writing cold signing key to "/tmp/UNDR.skey" file
 * Writing cold verifying key to "/tmp/UNDR.vkey file."
 * Writing cold counter to "/tmp/UNDR.counter" file.
 * Writing cold pool id to "/tmp/UNDR.id" file.
 * ```
 */
const argv = yargs
  .option('mnemonic', {
    alias: 'm',
    description: '24-word length mnemonic',
    type: 'array',
  })
  .option('seed', {
    alias: 's',
    description: 'hexa representation of a 32-byte long random seed',
    type: 'string',
  })
  .option('output', {
    alias: 'o',
    description: 'Output prefix of the files. e.g. -o /tmp/UNDR: default: cold',
    type: 'string',
  })
  .help()
  .alias('help', 'h')
  .argv

var mnemonic = ''
var seed = ''
var prefix = 'cold'

if (argv.output !== undefined) {
  prefix = argv.output
}

if (argv.seed !== undefined) {
  seed = argv.seed
  try {
    mnemonic = bip39.entropyToMnemonic(seed)
  } catch (e) {
    console.log("Invalid entropy, it should be a hexa string of a 32b long entropy.")
    return -127
  }
} else if (argv.mnemonic) {
  if (argv.mnemonic.length === 24) {
    mnemonic = argv.mnemonic.join(" ")
    console.log(`MNE: ${mnemonic}`)
    seed = bip39.mnemonicToEntropy(mnemonic)
  } else {
    console.log("The mnemonic must be a 24-word length long, no apostrophes allowed.")
    return -127
  }

} else {
  console.log("No seed nor mnemonic presented, generating randomly...")
  mnemonic = bip39.generateMnemonic(256)
  seed = bip39.mnemonicToEntropy(mnemonic)
}

const coldKeyPair = ed25519.MakeKeypair(toByteArray(seed))

console.log(`Write down the following 24-length mnemonic for recovery`)
console.log(`24-word length mnemonic : ${mnemonic}\n`)


console.log(`Writing cold signing key to "${prefix}.skey" file`)

var json = {
  type: "",
  description: "",
  cborHex: ""
}

json.type = 'StakePoolSigningKey_ed25519'
json.description = 'Stake Pool Operator Signing Key'
json.cborHex = `5820${coldKeyPair.privateKey.toString('hex').slice(0,64)}`

fs.writeFile(`${prefix}.skey`, JSON.stringify(json, null, 4), 'utf8', (err) => {
  if (err) throw err;
})

console.log(`Writing cold verifying key to "${prefix}.vkey file."`)
json.type = 'StakePoolVerificationKey_ed25519'
json.description = 'Stake Pool Operator Verification Key'
json.cborHex = `5820${coldKeyPair.publicKey.toString('hex')}`

fs.writeFile(`${prefix}.vkey`, JSON.stringify(json, null, 4), 'utf8', (err) => {
  if (err) throw err;
})

console.log(`Writing cold counter to "${prefix}.counter" file.`)
json.type = 'NodeOperationalCertificateIssueCounter'
json.description = 'Next certificate issue number: 0'
json.cborHex = `82005820${coldKeyPair.publicKey.toString('hex')}`

fs.writeFile(`${prefix}.counter`, JSON.stringify(json, null, 4), 'utf8', (err) => {
  if (err) throw err;
})

// Pool Id, the blake2b_224 hash of the public key.
let context = blake.createHash({
  digestLength: 28
})

context.update(coldKeyPair.publicKey)
const data = context.digest().toString('hex')
console.log(`Writing cold pool id to "${prefix}.id" file.`)
fs.writeFile(`${prefix}.id`, data, (err) => {
  if (err) throw err;
})
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment