From 363a9c4ee303a4a83d727597b8c847735c89449d Mon Sep 17 00:00:00 2001 From: Mikeal Rogers Date: Mon, 11 Jan 2021 23:52:09 +0000 Subject: [PATCH 1/5] feat: aes encryption --- package.json | 5 ++++ src/block.js | 16 ++++++++---- src/codecs/aes.js | 59 +++++++++++++++++++++++++++++++++++++++++++++ src/codecs/codec.js | 8 +++--- test/test-block.js | 28 +++++++++++++++++++++ 5 files changed, 108 insertions(+), 8 deletions(-) create mode 100644 src/codecs/aes.js diff --git a/package.json b/package.json index 77cf386e..f9d11d41 100644 --- a/package.json +++ b/package.json @@ -81,6 +81,9 @@ }, "./codecs/raw": { "import": "./src/codecs/raw.js" + }, + "./codecs/aes": { + "import": "./src/codecs/aes.js" } }, "devDependencies": { @@ -103,6 +106,8 @@ "dependencies": { "buffer": "^5.6.1", "cids": "^1.0.2", + "js-crypto-aes": "^1.0.0", + "js-crypto-random": "^1.0.0", "lodash.transform": "^4.6.0" }, "directories": { diff --git a/src/block.js b/src/block.js index 9a90ca3a..97db69d7 100644 --- a/src/block.js +++ b/src/block.js @@ -97,7 +97,7 @@ class Block { * @param {ByteView} options.bytes * @param {T} options.value */ - constructor ({ cid, bytes, value }) { + constructor ({ cid, bytes, value, codec }) { if (!cid || !bytes || typeof value === 'undefined') throw new Error('Missing required argument') this.cid = cid @@ -105,12 +105,18 @@ class Block { this.value = value this.asBlock = this + this.decrypt = null + if (codec.decrypt) { + this.decrypt = key => codec.decrypt({ value, key }) + } + // Mark all the properties immutable Object.defineProperties(this, { cid: readonly(), bytes: readonly(), value: readonly(), - asBlock: readonly() + asBlock: readonly(), + decrypt: readonly() }) } @@ -148,7 +154,7 @@ const encode = async ({ value, codec, hasher }) => { const hash = await hasher.digest(bytes) const cid = CID.create(1, codec.code, hash) - return new Block({ value, bytes, cid }) + return new Block({ value, bytes, cid, codec }) } /** @@ -169,7 +175,7 @@ const decode = async ({ bytes, codec, hasher }) => { const hash = await hasher.digest(bytes) const cid = CID.create(1, codec.code, hash) - return new Block({ value, bytes, cid }) + return new Block({ value, bytes, cid, codec }) } /** @@ -190,7 +196,7 @@ const createUnsafe = ({ bytes, cid, value: maybeValue, codec }) => { if (value === undefined) throw new Error('Missing required argument, must either provide "value" or "codec"') - return new Block({ cid, bytes, value }) + return new Block({ cid, bytes, value, codec }) } /** diff --git a/src/codecs/aes.js b/src/codecs/aes.js new file mode 100644 index 00000000..cd14de55 --- /dev/null +++ b/src/codecs/aes.js @@ -0,0 +1,59 @@ +import random from 'js-crypto-random' +import aes from 'js-crypto-aes' +import CID from '../cid.js' +import { codec } from './codec.js' + +const enc32 = value => { + value = +value + const buff = new Uint8Array(4) + buff[3] = (value >>> 24) + buff[2] = (value >>> 16) + buff[1] = (value >>> 8) + buff[0] = (value & 0xff) + return buff +} + +const readUInt32LE = (buffer) => { + const offset = buffer.byteLength - 4 + return ((buffer[offset]) | + (buffer[offset + 1] << 8) | + (buffer[offset + 2] << 16)) + + (buffer[offset + 3] * 0x1000000) +} + +let code = 0x1400 + +const concat = buffers => Uint8Array.from(buffers.map(b => [...b]).flat()) + +const mkcrypto = ({ name, code, ivsize }) => { + const encode = ({ iv, bytes }) => concat([iv, bytes]) + + const decode = bytes => { + const iv = bytes.subarray(0, ivsize) + bytes = bytes.slice(ivsize) + return { iv, bytes } + } + + const decrypt = async ({ key, value }) => { + let { bytes, iv } = value + bytes = await aes.decrypt(bytes, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + const len = readUInt32LE(bytes.subarray(0, 4)) + const cid = CID.decode(bytes.subarray(4, 4 + len)) + bytes = bytes.subarray(4 + len) + return { cid, bytes } + } + const encrypt = async ({ key, cid, bytes }) => { + const len = enc32(cid.bytes.byteLength) + const iv = random.getRandomBytes(ivsize) + const msg = concat([len, cid.bytes, bytes]) + bytes = await aes.encrypt(msg, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + return { bytes, iv } + } + return { encode, decode, encrypt, decrypt, code, name: name.toLowerCase() } +} + +const gcm = codec(mkcrypto({ name: 'aes-gcm', code: code++, ivsize: 12 })) +const cbc = codec(mkcrypto({ name: 'aes-cbc', code: code++, ivsize: 16 })) +const ctr = codec(mkcrypto({ name: 'aes-ctr', code: code++, ivsize: 12 })) + +export { gcm, cbc, ctr } diff --git a/src/codecs/codec.js b/src/codecs/codec.js index 5dd08675..e0fe23e8 100644 --- a/src/codecs/codec.js +++ b/src/codecs/codec.js @@ -11,8 +11,8 @@ * @param {(data:T) => Uint8Array} options.encode * @param {(bytes:Uint8Array) => T} options.decode */ -export const codec = ({ name, code, decode, encode }) => - new Codec(name, code, encode, decode) +export const codec = ({ name, code, decode, encode, encrypt, decrypt }) => + new Codec(name, code, encode, decode, encrypt, decrypt) /** * @template {number} Code @@ -85,11 +85,13 @@ export class Codec { * @param {(data:T) => Uint8Array} encode * @param {(bytes:Uint8Array) => T} decode */ - constructor (name, code, encode, decode) { + constructor (name, code, encode, decode, encrypt, decrypt) { this.name = name this.code = code this.encode = encode this.decode = decode + this.encrypt = encrypt + this.decrypt = decrypt } get decoder () { diff --git a/test/test-block.js b/test/test-block.js index 8521b170..2ca1368b 100644 --- a/test/test-block.js +++ b/test/test-block.js @@ -1,5 +1,7 @@ /* globals describe, it */ +import random from 'js-crypto-random' import codec from 'multiformats/codecs/json' +import * as ciphers from 'multiformats/codecs/aes' import { sha256 as hasher } from 'multiformats/hashes/sha2' import * as main from 'multiformats/block' import { CID, bytes } from 'multiformats' @@ -61,6 +63,32 @@ describe('block', () => { }) }) + describe('ciphers', () => { + const createTest = name => { + const json = codec + test(`aes-${name}`, async () => { + const block = await main.encode({ value: fixture, codec: json, hasher }) + const codec = ciphers[name] + const key = random.getRandomBytes(32) + const value = await codec.encrypt({ ...block, key }) + const eblock = await main.encode({ codec, value, hasher }) + const eeblock = await main.decode({ codec, bytes: eblock.bytes, hasher }) + same(eblock.cid.toString(), eeblock.cid.toString()) + same([...eblock.bytes], [...eeblock.bytes]) + same([...eblock.value.bytes], [...eeblock.value.bytes]) + same([...eblock.value.iv], [...eeblock.value.iv]) + const { cid, bytes } = await eblock.decrypt(key) + same(block.cid.toString(), cid.toString()) + same([...bytes], [...block.bytes]) + const dblock = await main.create({ cid, bytes, codec: json, hasher }) + same(block.value, dblock.value) + }) + } + createTest('gcm') + createTest('cbc') + createTest('ctr') + }) + test('kitchen sink', () => { const sink = { one: { two: { arr: [true, false, null], three: 3, buff, link } } } const block = main.createUnsafe({ value: sink, codec, bytes: true, cid: true }) From 85bf3c4bb1c8391eed5632ad73e935899453e60d Mon Sep 17 00:00:00 2001 From: Mikeal Rogers Date: Wed, 20 Jan 2021 19:15:11 +0000 Subject: [PATCH 2/5] wip: rename --- src/codecs/{aes.js => encrypted.js} | 0 1 file changed, 0 insertions(+), 0 deletions(-) rename src/codecs/{aes.js => encrypted.js} (100%) diff --git a/src/codecs/aes.js b/src/codecs/encrypted.js similarity index 100% rename from src/codecs/aes.js rename to src/codecs/encrypted.js From a9df0a14ae594929ff9c7cebfcf6065291806ae4 Mon Sep 17 00:00:00 2001 From: Mikeal Rogers Date: Wed, 20 Jan 2021 20:40:48 +0000 Subject: [PATCH 3/5] fix: one codec for block encryption --- package.json | 7 +++- src/block.js | 16 +++----- src/codecs/codec.js | 8 ++-- src/codecs/encrypted.js | 89 +++++++++++++++++------------------------ src/crypto/aes.js | 60 +++++++++++++++++++++++++++ test/test-block.js | 13 +++--- 6 files changed, 117 insertions(+), 76 deletions(-) create mode 100644 src/crypto/aes.js diff --git a/package.json b/package.json index f9d11d41..25fde425 100644 --- a/package.json +++ b/package.json @@ -82,8 +82,11 @@ "./codecs/raw": { "import": "./src/codecs/raw.js" }, - "./codecs/aes": { - "import": "./src/codecs/aes.js" + "./codecs/encrypted": { + "import": "./src/codecs/encrypted.js" + }, + "./crypto/aes": { + "import": "./src/crypto/aes.js" } }, "devDependencies": { diff --git a/src/block.js b/src/block.js index 97db69d7..9a90ca3a 100644 --- a/src/block.js +++ b/src/block.js @@ -97,7 +97,7 @@ class Block { * @param {ByteView} options.bytes * @param {T} options.value */ - constructor ({ cid, bytes, value, codec }) { + constructor ({ cid, bytes, value }) { if (!cid || !bytes || typeof value === 'undefined') throw new Error('Missing required argument') this.cid = cid @@ -105,18 +105,12 @@ class Block { this.value = value this.asBlock = this - this.decrypt = null - if (codec.decrypt) { - this.decrypt = key => codec.decrypt({ value, key }) - } - // Mark all the properties immutable Object.defineProperties(this, { cid: readonly(), bytes: readonly(), value: readonly(), - asBlock: readonly(), - decrypt: readonly() + asBlock: readonly() }) } @@ -154,7 +148,7 @@ const encode = async ({ value, codec, hasher }) => { const hash = await hasher.digest(bytes) const cid = CID.create(1, codec.code, hash) - return new Block({ value, bytes, cid, codec }) + return new Block({ value, bytes, cid }) } /** @@ -175,7 +169,7 @@ const decode = async ({ bytes, codec, hasher }) => { const hash = await hasher.digest(bytes) const cid = CID.create(1, codec.code, hash) - return new Block({ value, bytes, cid, codec }) + return new Block({ value, bytes, cid }) } /** @@ -196,7 +190,7 @@ const createUnsafe = ({ bytes, cid, value: maybeValue, codec }) => { if (value === undefined) throw new Error('Missing required argument, must either provide "value" or "codec"') - return new Block({ cid, bytes, value, codec }) + return new Block({ cid, bytes, value }) } /** diff --git a/src/codecs/codec.js b/src/codecs/codec.js index e0fe23e8..5dd08675 100644 --- a/src/codecs/codec.js +++ b/src/codecs/codec.js @@ -11,8 +11,8 @@ * @param {(data:T) => Uint8Array} options.encode * @param {(bytes:Uint8Array) => T} options.decode */ -export const codec = ({ name, code, decode, encode, encrypt, decrypt }) => - new Codec(name, code, encode, decode, encrypt, decrypt) +export const codec = ({ name, code, decode, encode }) => + new Codec(name, code, encode, decode) /** * @template {number} Code @@ -85,13 +85,11 @@ export class Codec { * @param {(data:T) => Uint8Array} encode * @param {(bytes:Uint8Array) => T} decode */ - constructor (name, code, encode, decode, encrypt, decrypt) { + constructor (name, code, encode, decode) { this.name = name this.code = code this.encode = encode this.decode = decode - this.encrypt = encrypt - this.decrypt = decrypt } get decoder () { diff --git a/src/codecs/encrypted.js b/src/codecs/encrypted.js index cd14de55..6abf97c4 100644 --- a/src/codecs/encrypted.js +++ b/src/codecs/encrypted.js @@ -1,59 +1,44 @@ -import random from 'js-crypto-random' -import aes from 'js-crypto-aes' -import CID from '../cid.js' +// @ts-check +import * as varint from '../varint.js' import { codec } from './codec.js' -const enc32 = value => { - value = +value - const buff = new Uint8Array(4) - buff[3] = (value >>> 24) - buff[2] = (value >>> 16) - buff[1] = (value >>> 8) - buff[0] = (value & 0xff) +const code = 0x1400 + +/** + * @template {number} Code + * @param {Object} options + * @param {Uint8Array} options.bytes + * @param {Uint8Array} options.iv + * @param {Code} options.code + * @returns {Uint8Array} + */ +const encode = ({ iv, code, bytes }) => { + const codeLength = varint.encodingLength(code) + const ivsizeLength = varint.encodingLength(iv.byteLength) + const length = codeLength + ivsizeLength + iv.byteLength + bytes.byteLength + const buff = new Uint8Array(length) + varint.encodeTo(code, buff) + let offset = codeLength + varint.encodeTo(iv.byteLength, buff, offset) + offset += ivsizeLength + buff.set(iv, offset) + offset += iv.byteLength + buff.set(bytes, offset) return buff } -const readUInt32LE = (buffer) => { - const offset = buffer.byteLength - 4 - return ((buffer[offset]) | - (buffer[offset + 1] << 8) | - (buffer[offset + 2] << 16)) + - (buffer[offset + 3] * 0x1000000) +/** + * @param {Uint8Array} bytes + */ +const decode = bytes => { + const [code, vlength] = varint.decode(bytes) + let offset = vlength + const [ivsize, ivsizeLength] = varint.decode(bytes.subarray(offset)) + offset += ivsizeLength + const iv = bytes.subarray(offset, offset + ivsize) + offset += ivsize + bytes = bytes.slice(offset) + return { iv, code, bytes } } -let code = 0x1400 - -const concat = buffers => Uint8Array.from(buffers.map(b => [...b]).flat()) - -const mkcrypto = ({ name, code, ivsize }) => { - const encode = ({ iv, bytes }) => concat([iv, bytes]) - - const decode = bytes => { - const iv = bytes.subarray(0, ivsize) - bytes = bytes.slice(ivsize) - return { iv, bytes } - } - - const decrypt = async ({ key, value }) => { - let { bytes, iv } = value - bytes = await aes.decrypt(bytes, key, { name: name.toUpperCase(), iv, tagLength: 16 }) - const len = readUInt32LE(bytes.subarray(0, 4)) - const cid = CID.decode(bytes.subarray(4, 4 + len)) - bytes = bytes.subarray(4 + len) - return { cid, bytes } - } - const encrypt = async ({ key, cid, bytes }) => { - const len = enc32(cid.bytes.byteLength) - const iv = random.getRandomBytes(ivsize) - const msg = concat([len, cid.bytes, bytes]) - bytes = await aes.encrypt(msg, key, { name: name.toUpperCase(), iv, tagLength: 16 }) - return { bytes, iv } - } - return { encode, decode, encrypt, decrypt, code, name: name.toLowerCase() } -} - -const gcm = codec(mkcrypto({ name: 'aes-gcm', code: code++, ivsize: 12 })) -const cbc = codec(mkcrypto({ name: 'aes-cbc', code: code++, ivsize: 16 })) -const ctr = codec(mkcrypto({ name: 'aes-ctr', code: code++, ivsize: 12 })) - -export { gcm, cbc, ctr } +export default codec({ encode, decode, code, name: 'encrypted' }) diff --git a/src/crypto/aes.js b/src/crypto/aes.js new file mode 100644 index 00000000..89d9785f --- /dev/null +++ b/src/crypto/aes.js @@ -0,0 +1,60 @@ +import CID from '../cid.js' +import random from 'js-crypto-random' +import aes from 'js-crypto-aes' + +const enc32 = value => { + value = +value + const buff = new Uint8Array(4) + buff[3] = (value >>> 24) + buff[2] = (value >>> 16) + buff[1] = (value >>> 8) + buff[0] = (value & 0xff) + return buff +} + +const readUInt32LE = (buffer) => { + const offset = buffer.byteLength - 4 + return ((buffer[offset]) | + (buffer[offset + 1] << 8) | + (buffer[offset + 2] << 16)) + + (buffer[offset + 3] * 0x1000000) +} + +const concat = buffers => Uint8Array.from(buffers.map(b => [...b]).flat()) + +const mkcrypto = ({ name, code, ivsize }) => { + /** + * @param {Object} options + * @param {Object} options.value + * @param {Uint8Array} options.key + * @param {Uint8Array} options.value.bytes + * @param {Uint8Array} options.value.iv + */ + const decrypt = async ({ key, value: { iv, bytes } }) => { + bytes = await aes.decrypt(bytes, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + const len = readUInt32LE(bytes.subarray(0, 4)) + const cid = CID.decode(bytes.subarray(4, 4 + len)) + bytes = bytes.subarray(4 + len) + return { cid, bytes } + } + /** + * @param {Object} options + * @param {Uint8Array} options.key + * @param {Uint8Array} options.bytes + * @param {CID} options.cid + */ + const encrypt = async ({ key, cid, bytes }) => { + const len = enc32(cid.bytes.byteLength) + const iv = random.getRandomBytes(ivsize) + const msg = concat([len, cid.bytes, bytes]) + bytes = await aes.encrypt(msg, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + return { bytes, iv, code } + } + return { encrypt, decrypt, code, name: name.toLowerCase(), ivsize } +} + +const gcm = mkcrypto({ name: 'aes-gcm', code: 0x1401, ivsize: 12 }) +const cbc = mkcrypto({ name: 'aes-cbc', code: 0x1402, ivsize: 16 }) +const ctr = mkcrypto({ name: 'aes-ctr', code: 0x1403, ivsize: 12 }) + +export { gcm, cbc, ctr } diff --git a/test/test-block.js b/test/test-block.js index 2ca1368b..195b3e07 100644 --- a/test/test-block.js +++ b/test/test-block.js @@ -1,7 +1,8 @@ /* globals describe, it */ import random from 'js-crypto-random' import codec from 'multiformats/codecs/json' -import * as ciphers from 'multiformats/codecs/aes' +import * as ciphers from 'multiformats/crypto/aes' +import encrypted from 'multiformats/codecs/encrypted' import { sha256 as hasher } from 'multiformats/hashes/sha2' import * as main from 'multiformats/block' import { CID, bytes } from 'multiformats' @@ -68,16 +69,16 @@ describe('block', () => { const json = codec test(`aes-${name}`, async () => { const block = await main.encode({ value: fixture, codec: json, hasher }) - const codec = ciphers[name] + const crypto = ciphers[name] const key = random.getRandomBytes(32) - const value = await codec.encrypt({ ...block, key }) - const eblock = await main.encode({ codec, value, hasher }) - const eeblock = await main.decode({ codec, bytes: eblock.bytes, hasher }) + const value = await crypto.encrypt({ ...block, key }) + const eblock = await main.encode({ codec: encrypted, value, hasher }) + const eeblock = await main.decode({ codec: encrypted, bytes: eblock.bytes, hasher }) same(eblock.cid.toString(), eeblock.cid.toString()) same([...eblock.bytes], [...eeblock.bytes]) same([...eblock.value.bytes], [...eeblock.value.bytes]) same([...eblock.value.iv], [...eeblock.value.iv]) - const { cid, bytes } = await eblock.decrypt(key) + const { cid, bytes } = await crypto.decrypt({ ...eblock, key }) same(block.cid.toString(), cid.toString()) same([...bytes], [...block.bytes]) const dblock = await main.create({ cid, bytes, codec: json, hasher }) From cc617bc1eccafcf7e23bbd864de9b12227857597 Mon Sep 17 00:00:00 2001 From: Irakli Gozalishvili Date: Tue, 26 Jan 2021 15:19:06 -0800 Subject: [PATCH 4/5] fix: type check --- src/crypto/aes.js | 37 +++++++++++++++++++++++++++++++++---- tsconfig.json | 2 +- 2 files changed, 34 insertions(+), 5 deletions(-) diff --git a/src/crypto/aes.js b/src/crypto/aes.js index 89d9785f..3e34d740 100644 --- a/src/crypto/aes.js +++ b/src/crypto/aes.js @@ -2,6 +2,9 @@ import CID from '../cid.js' import random from 'js-crypto-random' import aes from 'js-crypto-aes' +/** + * @param {number} value + */ const enc32 = value => { value = +value const buff = new Uint8Array(4) @@ -12,6 +15,9 @@ const enc32 = value => { return buff } +/** + * @param {Uint8Array} buffer + */ const readUInt32LE = (buffer) => { const offset = buffer.byteLength - 4 return ((buffer[offset]) | @@ -20,18 +26,32 @@ const readUInt32LE = (buffer) => { (buffer[offset + 3] * 0x1000000) } +/** + * @param {Uint8Array[]} buffers + */ const concat = buffers => Uint8Array.from(buffers.map(b => [...b]).flat()) +/** + * @template {'aes-gcm' | 'aes-cbc' | 'aes-ctr'} Name + * @template {number} Code + * @param {Object} options + * @param {Name} options.name + * @param {Code} options.code + * @param {number} options.ivsize + */ const mkcrypto = ({ name, code, ivsize }) => { + // Line below does a type cast, because type checker can't infer that + // `toUpperCase` will result in desired string literal. + const cyperType = /** @type {import('js-crypto-aes/dist/params').cipherTypes} */(name.toUpperCase()) /** * @param {Object} options - * @param {Object} options.value * @param {Uint8Array} options.key + * @param {Object} options.value * @param {Uint8Array} options.value.bytes * @param {Uint8Array} options.value.iv */ const decrypt = async ({ key, value: { iv, bytes } }) => { - bytes = await aes.decrypt(bytes, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + bytes = await aes.decrypt(bytes, key, { name: cyperType, iv, tagLength: 16 }) const len = readUInt32LE(bytes.subarray(0, 4)) const cid = CID.decode(bytes.subarray(4, 4 + len)) bytes = bytes.subarray(4 + len) @@ -47,10 +67,19 @@ const mkcrypto = ({ name, code, ivsize }) => { const len = enc32(cid.bytes.byteLength) const iv = random.getRandomBytes(ivsize) const msg = concat([len, cid.bytes, bytes]) - bytes = await aes.encrypt(msg, key, { name: name.toUpperCase(), iv, tagLength: 16 }) + bytes = await aes.encrypt(msg, key, { name: cyperType, iv, tagLength: 16 }) return { bytes, iv, code } } - return { encrypt, decrypt, code, name: name.toLowerCase(), ivsize } + + return { + code, + // Note: Do a type cast becasue `toLowerCase()` turns liternal type + // into a string. + name: /** @type {Name} */(name.toLowerCase()), + encrypt, + decrypt, + ivsize + } } const gcm = mkcrypto({ name: 'aes-gcm', code: 0x1401, ivsize: 12 }) diff --git a/tsconfig.json b/tsconfig.json index 4ce54b0e..104133d7 100644 --- a/tsconfig.json +++ b/tsconfig.json @@ -16,7 +16,7 @@ "strict": true, "alwaysStrict": true, "esModuleInterop": true, - "target": "ES2018", + "target": "ES2019", "moduleResolution": "node", "declaration": true, "declarationMap": true, From fce11b3c49361cf98cb93b267eb9d811f46addad Mon Sep 17 00:00:00 2001 From: Rod Vagg Date: Fri, 29 Jan 2021 16:22:45 +1100 Subject: [PATCH 5/5] fix: don't store CID length with encrypted, use CID.decodeFirst() --- src/crypto/aes.js | 33 +++------------------------------ test/test-block.js | 12 ++++++------ 2 files changed, 9 insertions(+), 36 deletions(-) diff --git a/src/crypto/aes.js b/src/crypto/aes.js index 3e34d740..77bea2bf 100644 --- a/src/crypto/aes.js +++ b/src/crypto/aes.js @@ -2,30 +2,6 @@ import CID from '../cid.js' import random from 'js-crypto-random' import aes from 'js-crypto-aes' -/** - * @param {number} value - */ -const enc32 = value => { - value = +value - const buff = new Uint8Array(4) - buff[3] = (value >>> 24) - buff[2] = (value >>> 16) - buff[1] = (value >>> 8) - buff[0] = (value & 0xff) - return buff -} - -/** - * @param {Uint8Array} buffer - */ -const readUInt32LE = (buffer) => { - const offset = buffer.byteLength - 4 - return ((buffer[offset]) | - (buffer[offset + 1] << 8) | - (buffer[offset + 2] << 16)) + - (buffer[offset + 3] * 0x1000000) -} - /** * @param {Uint8Array[]} buffers */ @@ -52,10 +28,8 @@ const mkcrypto = ({ name, code, ivsize }) => { */ const decrypt = async ({ key, value: { iv, bytes } }) => { bytes = await aes.decrypt(bytes, key, { name: cyperType, iv, tagLength: 16 }) - const len = readUInt32LE(bytes.subarray(0, 4)) - const cid = CID.decode(bytes.subarray(4, 4 + len)) - bytes = bytes.subarray(4 + len) - return { cid, bytes } + const [cid, remainder] = CID.decodeFirst(bytes) + return { cid, bytes: remainder } } /** * @param {Object} options @@ -64,9 +38,8 @@ const mkcrypto = ({ name, code, ivsize }) => { * @param {CID} options.cid */ const encrypt = async ({ key, cid, bytes }) => { - const len = enc32(cid.bytes.byteLength) const iv = random.getRandomBytes(ivsize) - const msg = concat([len, cid.bytes, bytes]) + const msg = concat([cid.bytes, bytes]) bytes = await aes.encrypt(msg, key, { name: cyperType, iv, tagLength: 16 }) return { bytes, iv, code } } diff --git a/test/test-block.js b/test/test-block.js index 195b3e07..a375fb8d 100644 --- a/test/test-block.js +++ b/test/test-block.js @@ -74,15 +74,15 @@ describe('block', () => { const value = await crypto.encrypt({ ...block, key }) const eblock = await main.encode({ codec: encrypted, value, hasher }) const eeblock = await main.decode({ codec: encrypted, bytes: eblock.bytes, hasher }) - same(eblock.cid.toString(), eeblock.cid.toString()) - same([...eblock.bytes], [...eeblock.bytes]) - same([...eblock.value.bytes], [...eeblock.value.bytes]) - same([...eblock.value.iv], [...eeblock.value.iv]) + same(eeblock.cid.toString(), eblock.cid.toString()) + same([...eeblock.bytes], [...eblock.bytes]) + same([...eeblock.value.bytes], [...eblock.value.bytes]) + same([...eeblock.value.iv], [...eblock.value.iv]) const { cid, bytes } = await crypto.decrypt({ ...eblock, key }) - same(block.cid.toString(), cid.toString()) + same(cid.toString(), block.cid.toString()) same([...bytes], [...block.bytes]) const dblock = await main.create({ cid, bytes, codec: json, hasher }) - same(block.value, dblock.value) + same(dblock.value, block.value) }) } createTest('gcm')