diff options
author | pukkamustard <pukkamustard@posteo.net> | 2020-06-06 14:10:29 +0200 |
---|---|---|
committer | pukkamustard <pukkamustard@posteo.net> | 2020-06-06 14:10:29 +0200 |
commit | ffe7fe6f45e3a0b6d06da49d8498193d10f3e820 (patch) | |
tree | 12328a77bad7c68eda7b2575a4c582afa984d7b4 | |
parent | b2abf771a80ba297e654fc4eed3245fb51f0995f (diff) |
towards decoding
-rw-r--r-- | src/crypto.js | 5 | ||||
-rw-r--r-- | src/eris.js | 92 |
2 files changed, 91 insertions, 6 deletions
diff --git a/src/crypto.js b/src/crypto.js index 17b0f21..a0119dd 100644 --- a/src/crypto.js +++ b/src/crypto.js @@ -29,6 +29,11 @@ module.exports = { derive_verification_key: async function (readKey) { await sodium.ready return sodium.crypto_kdf_derive_from_key(32, 1, 'eris.key', readKey) + }, + + is_zero: async function (buf) { + await sodium.ready + return sodium.is_zero(buf) } } diff --git a/src/eris.js b/src/eris.js index 474f8bc..787f0d2 100644 --- a/src/eris.js +++ b/src/eris.js @@ -19,9 +19,27 @@ function NullContentAddressableStorage () { ) } +/* A in-memory content-addressable storage backed by a Map + * */ +function MapContentAddressableStorage () { + this._map = new Map() + ContentAddressableStorage.call( + this, + async function (block) { + const ref = await crypto.hash(block) + const key = base32.encode(ref) + this._map.set(key, block) + return ref + }, async function (ref) { + const key = base32.encode(ref) + return this._map.get(key) + } + ) +} + /* Helper to read blocks from a buffer */ -async function * blockGenerator (buffer, blockSize = 4096) { +function * blockGenerator (buffer, blockSize = 4096) { // yield blocks while (buffer.byteLength >= blockSize) { const block = buffer.slice(0, blockSize) @@ -140,7 +158,7 @@ async function buildMerkleTree (input, verificationKey, cas) { } // Get data blocks from input - for await (const dataBlock of blockGenerator(input)) { + for (const dataBlock of blockGenerator(input)) { // put data block in content-addressable storage const dataBlockRef = await cas.put(dataBlock) @@ -161,8 +179,8 @@ function makeReadCapability (level, rootReference, readKey) { // Set version to 0 cap.set([0], 0) - // Set type to 1 (for read capability) - cap.set([1], 1) + // Set type to 0 (for read capability) + cap.set([0], 1) // Set level cap.set([level], 2) @@ -173,7 +191,23 @@ function makeReadCapability (level, rootReference, readKey) { // Set key cap.set(readKey, 35) - return "urn:erisx:".concat(base32.encode(cap)) + return 'urn:erisx:'.concat(base32.encode(cap)) +} + +function decodeCapability (cap) { + if (cap.substring(0, 10) === 'urn:erisx:') { + const buffer = base32.decode(cap.substring(10)) + const view = new Uint8Array(buffer) + return { + version: view[0], + type: view[1], + level: view[2], + rootReference: view.slice(3, 35), + key: view.slice(35, 67) + } + } else { + throw new Error('Can not decode ERIS URN.') + } } async function put (content, cas = new NullContentAddressableStorage()) { @@ -195,8 +229,54 @@ async function put (content, cas = new NullContentAddressableStorage()) { return makeReadCapability(tree.level, tree.rootReference, readKey) } +async function * decodeTree (cas, verificationKey, ref, nodeLevel, nodeCount) { + + // Get block from cas + const block = await cas.get(ref) + + if (nodeLevel === 0) { + // if level 0, then it is a data block + yield block + } else { + // decode node + const nonce = nodeNonce(nodeLevel, nodeCount) + const decodedBlock = await crypto.stream_xor(block, nonce, verificationKey) + + // Counter for children + var i = 0 + + // read child refs from decoded blocks + for (const childRef of blockGenerator(decodedBlock, 32)) { + if (!(await crypto.is_zero(childRef))) { + const childLevel = nodeLevel - 1 + const childCount = (128 * nodeCount) + i + + // decode sub-tree + yield * decodeTree(cas, verificationKey, childRef, childLevel, childCount) + + // increment the child counter + i++ + } + } + } +} + +async function * get (capability, cas) { + capability = decodeCapability(capability) + + if (capability.type !== 0) { + throw new Error('Not a read capability') + } + + const verificationKey = await crypto.derive_verification_key(capability.key) + + yield * decodeTree(cas, verificationKey, capability.rootReference, capability.level, 0) +} + module.exports = { ContentAddressableStorage: ContentAddressableStorage, NullContentAddressableStorage: NullContentAddressableStorage, - put: put + MapContentAddressableStorage: MapContentAddressableStorage, + put: put, + get: get } |