blockstack-storage
Version:
The Blockstack Javascript library for storage.
283 lines (244 loc) • 7.58 kB
JavaScript
;
Object.defineProperty(exports, "__esModule", {
value: true
});
exports.hashDataPayload = hashDataPayload;
exports.signRawData = signRawData;
exports.signDataPayload = signDataPayload;
exports.makeDataInfo = makeDataInfo;
exports.makeDataTombstone = makeDataTombstone;
exports.makeDataTombstones = makeDataTombstones;
exports.signDataTombstone = signDataTombstone;
exports.signDataTombstones = signDataTombstones;
exports.parseDataTombstone = parseDataTombstone;
exports.parseFullyQualifiedDataId = parseFullyQualifiedDataId;
var _util = require('./util');
var _blockstack = require('blockstack');
var assert = require('assert');
var crypto = require('crypto');
var EC = require('elliptic').ec;
var ec = EC('secp256k1');
var Ajv = require('ajv');
var BigInteger = require('bigi');
var bitcoinjs = require('bitcoinjs-lib');
/*
* Hash an inode payload and its length.
* Specifically hash `${payload.length}:${payload},`
*
* @param payload_buffer (String) the payload to hash
*
* Return the sha256
*/
function hashDataPayload(payload_buffer) {
var hash = crypto.createHash('sha256');
// this forces the hash to be computed over the bytestring
// (important when computing length!) which will make it
// match with the Python hash verifier
var payload_str = Buffer.from(payload_buffer);
hash.update(payload_str.length + ':');
hash.update(payload_str);
hash.update(',');
return hash.digest('hex');
}
/*
* Sign a string of data.
*
* @param payload_buffer (Buffer) the buffer to sign
* @param privkey_hex (String) the hex-encoded ECDSA private key
* @param hash (String) optional; the hash of the payload. payload_buffer can be null if hash is given.
*
* Return the base64-encoded signature
*/
function signRawData(payload_buffer, privkey_hex, hash) {
var privkey = (0, _blockstack.decodePrivateKey)(privkey_hex);
if (!hash) {
hash = (0, _util.hashRawData)(payload_buffer);
}
var sig = ec.sign(hash, privkey, { canonical: true });
// use signature encoding compatible with Blockstack
var r_array = sig.r.toArray();
var s_array = sig.s.toArray();
var r_buf = Buffer.from(r_array).toString('hex');
var s_buf = Buffer.from(s_array).toString('hex');
if (r_buf.length < 64) {
while (r_buf.length < 64) {
r_buf = "0" + r_buf;
}
}
if (s_buf.length < 64) {
while (s_buf.length < 64) {
s_buf = "0" + s_buf;
}
}
var sig_buf_hex = r_buf + s_buf;
assert(sig_buf_hex.length == 128);
var sigb64 = Buffer.from(sig_buf_hex, 'hex').toString('base64');
return sigb64;
}
/*
* Sign a data payload and its length.
* Specifically sign `${payload.length}:${payload},`
*
* @payload_string (String) the string to sign
* @privkey_hex (String) the hex-encoded private key
*
* Return the base64-encoded signature
*/
function signDataPayload(payload_string, privkey_hex) {
return signRawData(Buffer.concat([Buffer.from(payload_string.length + ':'), Buffer.from(payload_string), Buffer.from(',')]), privkey_hex);
}
/*
* Make a mutable data payload
*
* @param data_id (String) the data identifier (not fully qualified)
* @param data_payload (String) the data payload to store
* @param device_id (String) the ID of the device creating this data
*
* Returns an mutable data payload object.
*/
function makeDataInfo(data_id, data_payload, device_id) {
var fq_data_id = arguments.length > 3 && arguments[3] !== undefined ? arguments[3] : null;
if (!fq_data_id) {
fq_data_id = (0, _blockstack.makeFullyQualifiedDataId)(device_id, data_id);
}
var timestamp = new Date().getTime();
var ret = {
'fq_data_id': fq_data_id,
'data': data_payload,
'version': 1,
'timestamp': timestamp
};
return ret;
}
/*
* Make a single datum tombstone.
*
* @param tombstone_payload (String) the string that encodes the tombstone
*
* Returns the tombstone (to be fed into the storage driver)
*/
function makeDataTombstone(tombstone_payload) {
var now = parseInt(new Date().getTime());
return 'delete-' + now + ':' + tombstone_payload;
}
/*
* Make a list of data tombstones.
*
* @param device_ids (Array) the list of device IDs
* @param data_id (String) the datum ID
*
* Returns a list of tombstones.
*/
function makeDataTombstones(device_ids, data_id) {
var ts = [];
var _iteratorNormalCompletion = true;
var _didIteratorError = false;
var _iteratorError = undefined;
try {
for (var _iterator = device_ids[Symbol.iterator](), _step; !(_iteratorNormalCompletion = (_step = _iterator.next()).done); _iteratorNormalCompletion = true) {
var device_id = _step.value;
ts.push(makeDataTombstone((0, _blockstack.makeFullyQualifiedDataId)(device_id, data_id)));
}
} catch (err) {
_didIteratorError = true;
_iteratorError = err;
} finally {
try {
if (!_iteratorNormalCompletion && _iterator.return) {
_iterator.return();
}
} finally {
if (_didIteratorError) {
throw _iteratorError;
}
}
}
return ts;
}
/*
* Sign a datum tombstone
*
* @param tombstone (String) the tombstone string
* @param privkey (String) the hex-encoded private key
*
* Returns the signed tombstone as a String
*/
function signDataTombstone(tombstone, privkey) {
var sigb64 = signRawData(tombstone, privkey);
return tombstone + ':' + sigb64;
}
/*
* Sign a list of mutable data tombstones
*
* @param tobmstones (Array) the list of per-device tombstones
* @param privkey (String) the hex-encoded private key
*
* Returns the list of signed tombstones as an Array.
*/
function signDataTombstones(tombstones, privkey) {
var sts = [];
var _iteratorNormalCompletion2 = true;
var _didIteratorError2 = false;
var _iteratorError2 = undefined;
try {
for (var _iterator2 = tombstones[Symbol.iterator](), _step2; !(_iteratorNormalCompletion2 = (_step2 = _iterator2.next()).done); _iteratorNormalCompletion2 = true) {
var ts = _step2.value;
sts.push(signDataTombstone(ts, privkey));
}
} catch (err) {
_didIteratorError2 = true;
_iteratorError2 = err;
} finally {
try {
if (!_iteratorNormalCompletion2 && _iterator2.return) {
_iterator2.return();
}
} finally {
if (_didIteratorError2) {
throw _iteratorError2;
}
}
}
;
return sts;
}
/*
* Parse a (unsigned) data tombstone
*
* @param tombstone (string) the tombstone payload
*
* Returns an object with:
* .timestamp (int) the timestamp of the tombstone (in milliseconds)
* .id (string) the data ID of the tombstone
*
* Returns null on failure
*/
function parseDataTombstone(tombstone) {
var re = new RegExp(OP_TOMBSTONE_PATTERN);
var groups = re.exec(tombstone);
if (!groups) {
return null;
}
var ts = parseInt(groups[1]);
var data_id = groups[2];
return { 'timestamp': ts, 'id': data_id };
}
/*
* Parse a fully-qualified data ID
*
* @param fq_data_id (string) the fully-qualified data ID
*
* Returns an object with:
* .device_id: the device identifier
* .data_id: the device-specific data ID
*
* Returns null on failure to parse
*/
function parseFullyQualifiedDataId(fq_data_id) {
fq_data_id = unescape(fq_data_id).replace("\\x2f", "/");
var parts = fq_data_id.split(":", 2);
if (parts.length != 2) {
return null;
}
return { 'device_id': parts[0], 'data_id': parts[1] };
}