npm package discovery and stats viewer.

Discover Tips

  • General search

    [free text search, go nuts!]

  • Package details

    pkg:[package-name]

  • User packages

    @[username]

Sponsor

Optimize Toolset

I’ve always been into building performant and accessible sites, but lately I’ve been taking it extremely seriously. So much so that I’ve been building a tool to help me optimize and monitor the sites that I build to make sure that I’m making an attempt to offer the best experience to those who visit them. If you’re into performant, accessible and SEO friendly sites, you might like it too! You can check it out at Optimize Toolset.

About

Hi, 👋, I’m Ryan Hefner  and I built this site for me, and you! The goal of this site was to provide an easy way for me to check the stats on my npm packages, both for prioritizing issues and updates, and to give me a little kick in the pants to keep up on stuff.

As I was building it, I realized that I was actually using the tool to build the tool, and figured I might as well put this out there and hopefully others will find it to be a fast and useful way to search and browse npm packages as I have.

If you’re interested in other things I’m working on, follow me on Twitter or check out the open source projects I’ve been publishing on GitHub.

I am also working on a Twitter bot for this site to tweet the most popular, newest, random packages from npm. Please follow that account now and it will start sending out packages soon–ish.

Open Software & Tools

This site wouldn’t be possible without the immense generosity and tireless efforts from the people who make contributions to the world and share their work via open source initiatives. Thank you 🙏

© 2024 – Pkg Stats / Ryan Hefner

dashmessage

v1.0.3

Published

Verify messages signed via DASH addresses.

Downloads

258

Readme

DashMessage.js

Electrum-Style Message Signing (a.k.a. Satoshi-style Signing), implemented in JavaScript.

Used by the Dash network to Sign and Verify voting messages by DASH addresses.

// JS
let equal = await DashMessage.magicVerify(addr, msg, recSig, verifyFn, pkhFn);
if (equal === true) {
  console.log("verified");
}
# Dev CLI
printf 'Hello, World!' | base64
# SGVsbG8sIFdvcmxkIQ==

./bin/dashmessage verify 'XyBmeuLa8y3D3XmzPvCTj5PVh7WvMPkLn1' 'SGVsbG8sIFdvcmxkIQ==' '<signature>'

Table of Contents

JS

Works in Browsers, Node, and Bundlers.

npm install --save [email protected]
// node versions < v24.0 may require `node --experimental-require-module`
let DashMessage = require(`dashmessage`);

Overview

  • Core (Sign, Verify, Hash, Recover)

    await DashMessage.magicSign(privBytes, msgBytes, sign); // recSigBytes
    await DashMessage.magicVerify(pkh, msg, recSig, recoverPubkey, toPkh); // equal
    
    DashMessage.DASH_MAGIC_BYTES; // bytes for "DarkCoin Signed Message:\n"
    
    await DashMessage.magicHash(messageBytes, magicBytes); // hashBytes
    await DashMessage.magicRecoverPubkey(msg, recSig, recoverPubkey); // pubBytes
    DashMessage.encodeRecoverySig({ bytes, recovery, compressed }); // recSigBytes
    DashMessage.decodeRecoverySig(recoverySigBytes); // { bytes, recovery, compressed }

    (note: most of these have an optional magicBytes parameter)

  • Convenience Utils (base64, etc)

    DashMessage.utils.textEncoder.encode(str); // bytes
    DashMessage.utils.base64ToBytes(base64); // bytes
    DashMessage.utils.rfcBase64ToBytes(rfcBase64); // bytes
    DashMessage.utils.urlBase64ToRfcBase64(urlBase64); // urlBase64
    DashMessage.utils.bytesToRfcBase64(bytes); // rfcBase64
    
    await DashMessage.utils.doubleSha256(bytes); // hashBytes
    DashMessage.utils.concatBytes(bytesList, totalLen); // bytes
    DashMessage.utils.toVarInt32(n); // bytes

Boilerplate (you need this)

Here's some copy-pasta boilerplate that will Just Work™:
(it's not "baked in" due to dependency management issues with some bundlers and transpilers)

import DashKeys from "dashkeys";
import Secp256k1 from "@dashincubator/secp256k1";

let boilerplate = {
  signMessage: async function (privkeyBytes, hashBytes) {
    let compressed = true;
    let [bytes, recovery] = await Secp256k1.sign(hashBytes, privkeyBytes, {
      extraEntropy: null, // for testing only, otherwise should be 'true'
      canonical: true,
      der: false,
      recovered: true,
    });
    return { bytes, compressed, recovery };
  },
  recoverPubkey: async function (msgHash, signature, recovery, isCompressed) {
    let pubkeyBytes = await Secp256k1.recoverPublicKey(
      msgHash,
      signature,
      recovery,
      isCompressed,
    );
    return pubkeyBytes;
  },
  toPubKeyHash: async function (pubkeyBytes) {
    let pkhBytes = await DashKeys.pubkeyToPkh(pubkeyBytes);
    return pkhBytes;
  },
};

Signing

See the working example in ./examples/sign.js.

import DashKeys from "dashkeys";
import DashMessage from "dashmessage";

async function sign(wif, messageText) {
  let networkOpts = { version: "mainnet" };
  let privateKeyBytes = await DashKeys.wifToPrivKey(wif, networkOpts);
  let messageBytes = DashMessage.utils.textEncoder.encode(messageText);

  let recoverySigBytes = await DashMessage.magicSign(
    privateKeyBytes,
    messageBytes,
    boilerplate.signMessage,
    DashMessage.DASH_MAGIC_BYTES,
  );

  let sig = DashMessage.utils.bytesToRfcBase64(recoverySigBytes);
  return sig;
}

let messageText = "dte2022-akerdemelidis|estoever|mmason";
let wif = "XK5DHnAiSj6HQNsNcDkawd9qdp8UFMdYftdVZFuRreTMJtbJhk8i";

let sig = await sign(wif, messageText);
console.info(`Signature (base64):\n${sig}`);
// H2Opy9NX72iPZRcDVEHrFn2qmVwWMgc+DKILdVxl1yfmcL2qcpu9esw9wcD7RH0/dJHnIISe5j39EYahorWQM7I=

Note: you'll also need the boilerplate functions in the section above.

Verifying

See the working example in ./examples/verify.js.

import DashKeys from "dashkeys";
import DashMessage from "dashmessage";

async function mustVerify(address, messageText, sig) {
  let pkhBytes = await DashKeys.addrToPkh(address);
  let messageBytes = DashMessage.utils.textEncoder.encode(messageText);
  let recoverySigBytes = DashMessage.utils.base64ToBytes(sig);

  let isEqual = await DashMessage.magicVerify(
    pkhBytes,
    messageBytes,
    recoverySigBytes,
    boilerplate.recoverPubkey,
    boilerplate.pubkeyToPkh,
  );
  if (isEqual !== true) {
    throw new Error("invalid signature");
  }
}

let messageText = "dte2022-akerdemelidis|estoever|mmason";
let address = "XyBmeuLa8y3D3XmzPvCTj5PVh7WvMPkLn1";
let sig =
  "H2Opy9NX72iPZRcDVEHrFn2qmVwWMgc+DKILdVxl1yfmcL2qcpu9esw9wcD7RH0/dJHnIISe5j39EYahorWQM7I=";

await mustVerify(address, messageText, sig);

console.info(`verified`);

Note: you'll also need the boilerplate functions above.

Utils

Use DashKeys to convert between bytes and key formats - WIF (Private Key), Address (PubKeyHash, PKH), Public Key, etc.

The built-in Base64 utils are provided for working with Command Line and UI tools.

Dev CLI

These can be run from a local copy of the repo, but are not installed via npm.

git clone https://github.com/digitalcashdev/DashMessage.js
pushd ./DashMessage/

npm clean-install

See ./bin/dashmessage to see how the CLI is implemented.

Signing

  1. Create a WIF file containing the private key
    cat ./XyBmeuLa8y3D3XmzPvCTj5PVh7WvMPkLn1.wif
    # XK5DHnAiSj6HQNsNcDkawd9qdp8UFMdYftdVZFuRreTMJtbJhk8i
  2. Encode the message as Base64
    (this is done as to allow for text or binary messages)
    printf 'dte2022-akerdemelidis|estoever|mmason' | base64
  3. Sign the message
    ./bin/dashmessage sign \
        ./XyBmeuLa8y3D3XmzPvCTj5PVh7WvMPkLn1.wif \
        'ZHRlMjAyMi1ha2VyZGVtZWxpZGlzfGVzdG9ldmVyfG1tYXNvbg=='

Note: the random salt which would normally be used for ECDSA signatures is turned off in the CLI as to match other tools in the ecosystem and to make for easier testing. It's recommended to turn the extra entropy on in production.

Verifying

  1. Encode the message as Base64
    (this is done as to allow for text or binary messages)
    printf 'dte2022-akerdemelidis|estoever|mmason' | base64
  2. Verify the message against the recovery signature
    ./bin/dashmessage verify \
        'XyBmeuLa8y3D3XmzPvCTj5PVh7WvMPkLn1' \
        'ZHRlMjAyMi1ha2VyZGVtZWxpZGlzfGVzdG9ldmVyfG1tYXNvbg==' \
        'H2Opy9NX72iPZRcDVEHrFn2qmVwWMgc+DKILdVxl1yfmcL2qcpu9esw9wcD7RH0/dJHnIISe5j39EYahorWQM7I='

Specification

As of 2024 this bespoke message algorithm has no formal specification. However, it has many implementations, most often referred to as Electrum-Style Message Signing.

Key details about this implementation:

  • the default magic string is "DarkCoin Signed Message:\n"
  • the hash format is magicStrVarSize + magicStr + msgVarSize + msg
  • the hash algorithm is double-sha256
  • the recID is the first byte of the signature ("compressed"), either:
    • 27 + 4 + 0 (for 0x02)
    • OR 27 + 4 + 1 (for 0x03)
  • the "magic" naming convention is used to differentiate from standard hashes, signatures, etc

References