A Javascript library for encoding, decoding, and verifying hashlinks as defined in the IETF Hashlink draft spec.
Example Hashlinks:
hl:zm9YZpCjPLPJ4Epc
hl:zm9YZpCjPLPJ4Epc:z3TSgXTuaHxY2tsArhUreJ4ixgw9NW7DYuQ9QTPQyLHy
https://example.com/hw.txt?hl=zm9YZpCjPLPJ4Epc
Security is hard. Cryptography is harder. When in doubt, leave it to the professionals.
While the authors of this library are professionals, and they have used cryptographic primitives and libraries written by people more capable than them, bugs happen. Implementers that use this library are urged to study the code and perform a review of their own before using this library in a production system.
It is also possible to misuse this library in a variety of ways if you don't know what you are doing. If you are ever in doubt, remember that cryptography is hard. Leave it to the professionals.
When using a hyperlink to fetch a resource from the Internet, it is often useful to know if the resource has changed since the data was published. Cryptographic hashes, such as SHA-256, are often used to determine if published data has changed in unexpected ways. Due to the nature of most hyperlinks, the cryptographic hash is often published separately from the link itself. The Hashlink specification describes a data model and serialization formats for expressing cryptographically protected hyperlinks. The mechanisms described in the Hashlink specification enables a system to publish a hyperlink in a way that empowers a consuming application to determine if the resource associated with the hyperlink has changed in unexpected ways.
See also (related specs):
To use this library in the browser, you can include the latest version via a simple script tag:
<script src="https://unpkg.com/hashlink/dist/hashlink.min.js"></script>
To use the library in Node.js:
To install locally (for development):
git clone https://github.com/digitalbazaar/hashlink.git
cd hashlink
npm install
Use on the command line, or see the API section below.
There are a number of ways you can encode a hashlink. The simplest way is to provide the data directly.
./bin/hl encode hw.txt
You can encode a hashlink from any data published on the Web:
./bin/hl encode --url "https://example.com/hw.txt"
You can also encode a hashlink from data on disk and specify the location on the web that the data will be published to:
./bin/hl encode --url "https://example.com/hw.txt" hw.txt
Hashlinks are also backwards compatible with legacy URL schemes, which enables you to use query parameters to encode the hashlink information:
./bin/hl encode --legacy --url "https://example.com/hw.txt" hw.txt
To decode a hashlink, you can run the following command:
./bin/hl decode hl:zm9YZpCjPLPJ4Epc:z3TSgXTuaHxY2tsArhUreJ4ixgw9NW7DYuQ9QTPQyLHy
The command above will result in the following output:
URLs: https://example.com/hw.txt
sha256sum: 12207f83b1657ff1fc53b92dc18148a1d65dfc2d4b1fa3d677284addd200126d9069
To verify a hashlink, you can run the following command:
./bin/hl verify --file hw.txt hl:zQmNbCYUrvaVfy6w9b5W3SVTP2newPK5FoeY37QurUEUydH
The command above will result in the following output:
hashlink is valid
The API is useful when integrating this library with a larger software system.
You can use the API in the browser by including the latest version via a simple script tag:
<script src="https://unpkg.com/hashlink/dist/hashlink.min.js"></script>
The rest of the examples in this section assume a node.js environment, but all API calls listed below are also available in the browser version.
You can encode a hashlink from an existing URL (coming soon):
const hl = require('hashlink');
const url = 'https://example.com/hw.txt';
// encode a hashlink by fetching the URL content and hashing it
const hlUrl = await hl.encode({url});
// print out the hashlink
console.log(hlUrl);
You can encode a hashlink from data:
const hl = require('hashlink');
// encode a hashlink using data to be published at a URL
const data = fs.readFileSync('hw.txt');
const url = 'https://example.com/hw.txt';
const hlUrl = await hl.encode({data, url});
// print out the hashlink
console.log(hlUrl);
You can change the default options used by the hashlink function:
const {Hashlink} = require('hashlink');
const {Urdna2015} = require('hashlink-jsonld');
// setup hl library to use RDF Dataset Canonicalization codec
const hl = new Hashlink();
hl.use(new Urdna2015());
// encode a hashlink using canonicalized data published at a URL
const url = 'https://example.com/credential.jsonld';
// encode the input data using urdna2015 canonicalization algorithm and
// then hash using blake2b with a 64-bit output
const codecs = ['urdna2015', 'blake2b-64'];
const hlUrl = await hl.encode({
url,
codecs,
'content-type': 'application/ld+json'
});
// print out the hashlink
console.log(hlUrl);
You can decode a hashlink by simply calling decode (coming soon):
const hl = require('hashlink');
const url = 'hl:zm9YZpCjPLPJ4Epc:z3TSgXTuaHxY2tsArhUreJ4ixgw9NW7DYuQ9QTPQyLHy';
const hlData = hl.decode(url);
// print out the decoded hashlink information (an object)
console.log(hlData);
You can verify the integrity of a hashlink:
const hl = require('hashlink');
const hashlink = 'hl:zm9YZpCjPLPJ4Epc:z3TSgXTuaHxY2tsArhUreJ4ixgw9NW7DYuQ9QTPQyLHy';
const data = '...';
const valid = await hl.verify({hashlink, data});
// print out whether or not the hashlink is valid
console.log(valid);
In some cases, you need to be able to canonize the contents of the hashlink in order to verify it:
const {Hashlink} = require('hashlink');
const {Urdna2016} = require('hashlink-jsonld');
// setup hl library to use RDF Dataset Canonicalization codec
const hl = new Hashlink();
hl.use(new Urdna2015());
// encode a hashlink using canonicalized data published at a URL
const hlUrl = 'hl:zQmWvQxTqbG2Z9HPJgG57jjwR154cKhbtJenbyYTWkjgF3e:' +
'zuh8iaLobXC8g9tfma1CSTtYBakXeSTkHrYA5hmD4F7dCLw8XYwZ1GWyJ3zwF';
const valid = await hl.verify({hashlink: hlUrl});
// print out whether or not the hashlink is valid
console.log(valid);
The Hashlink library is built to support arbitrary transformations of input data using codecs (encoder/decoders).
The Hashlink library has an internal default instance of a Hashlink class that is provided as a convenience so that for most use cases, the defaults work just fine.
const hl = require('hashlink');
const hlUrl = await hl.encode({url: 'https://example.com/hw.txt'});
In some cases, however, a developer will need to extend the default transformations, like when input needs to be canonicalized before it is hashed.
const {Hashlink} = require('hashlink');
const jsonld = require('jsonld');
// setup URDNA2015 codec that encodes
class Urdna2015 {
constructor() {
this.algorithm = 'urdna2015';
}
async encode(input) {
const inputJsonld = JSON.parse(new TextDecoder().decode(input));
return await jsonld.canonize(
inputJsonld, {format: 'application/n-quads'});
}
}
// setup hl library to use RDF Dataset Canonicalization
const hl = new Hashlink();
hl.use(new Urdna2015());
// encode a hashlink using canonicalized data published at a URL
const url = 'https://example.com/credential.jsonld';
// encode the input data using urdna2015 canonicalization algorithm and
// then hash using blake2b with a 64-bit output
const codecs = ['urdna2015', 'blake2b-64'];
const hlUrl = await hl.encode({
url,
codecs,
'content-type': 'application/ld+json'
});
// print out the hashlink
console.log(hlUrl);
Note the use of the Hashlink
class above as well as the use()
API. Using
this API, any arbitrary number of transforms may be applied to the input
data before the final hashlink value is produced.
To run Mocha tests:
npm run mocha
To run the VC Test Suite:
npm run fetch-hl-test-suite
npm test
See the contribute file!
PRs accepted.
Small note: If editing the Readme, please conform to the standard-readme specification.
Commercial support for this library is available upon request from Digital Bazaar: support@digitalbazaar.com
New BSD License (3-clause) © Digital Bazaar
The authors of this package thank Daniel Levett
(dlevs) for contributing the hashlink
npm package
name for use with this project.