https://github.com/dozyio/js-blockstore-encrypt
Encrypted Blockstore for IPFS / Helia
https://github.com/dozyio/js-blockstore-encrypt
blockstore encryption helia ipfs
Last synced: about 1 year ago
JSON representation
Encrypted Blockstore for IPFS / Helia
- Host: GitHub
- URL: https://github.com/dozyio/js-blockstore-encrypt
- Owner: dozyio
- License: mit
- Created: 2024-09-16T23:36:45.000Z (almost 2 years ago)
- Default Branch: main
- Last Pushed: 2024-10-13T17:12:13.000Z (over 1 year ago)
- Last Synced: 2025-04-14T12:12:00.850Z (about 1 year ago)
- Topics: blockstore, encryption, helia, ipfs
- Language: TypeScript
- Homepage:
- Size: 232 KB
- Stars: 4
- Watchers: 1
- Forks: 0
- Open Issues: 0
-
Metadata Files:
- Readme: README.md
- License: LICENSE
Awesome Lists containing this project
README
# blockstore-enc
A JS/TS transparent encrypted wrapper around any existing Blockstore implementation.
## Features
* Data Security: Encrypts blocks to ensure privacy and security. CIDs are stored as secure hashes.
* Compatibility: Works with any Blockstore implementation conforming to the `interface-blockstore` specification.
* Strong Cryptography:
* Uses AES-GCM for block encryption.
* Uses HMAC-SHA256 for CID hashing.
* Master key is derived from a password using PBKDF2.
* Derives encryption and MAC keys from a master key using HKDF.
## Installation
```sh
npm install blockstore-enc
```
Or with Yarn:
```sh
yarn add blockstore-enc
```
## Usage
```js
import { EncBlockstore } from 'blockstore-enc';
import { FsBlockstore } from 'blockstore-fs';
import { CID } from 'multiformats/cid';
(async () => {
try {
const password = 'strong-password-is-strong'; // Must be at least 16 bytes long
const masterSalt = new TextEncoder().encode('4d1A2eF42C9F09BF8ba6141D3dBA3521') // Must be at least 16 bytes long
const store = new EncBlockstore(new FsBlockstore('./data'));
await store.init(password, masterSalt);
await store.open();
// Use the store as you would use any Blockstore
const someCid = CID.parse('bafkreigh2akiscaildc6en5ynpwp45fucjk64o4uqa5fmsrzc4i4vqveae')
const someData = new Uint8Array([1, 2, 3, 4, 5]);
await store.put(someCid, someData);
const data = await store.get(someCid);
console.log('Retrieved data:', data);
} catch (err) {
console.error(err);
}
})();
```
## API
### Class: `EncBlockstore`
#### Constructor
```js
new EncBlockstore(blockstore: Blockstore, init?: EncBlockstoreInit)
```
Creates a new instance of EncBlockstore wrapping the provided blockstore.
* blockstore: The underlying Blockstore to wrap.
* init (optional): Initialization options.
#### Methods
```js
init(password: string, masterSalt: Uint8Array): Promise
```
Initializes the encryption and MAC keys. Must be called before using the blockstore.
* password: The password to derive the master key from. Must be at least 16 bytes long.
* masterSalt: The master salt used for key derivation. Must be at least 16 bytes long.
```js
open(): Promise
```
Opens the underlying blockstore.
```js
close(): Promise
```
Closes the underlying blockstore.
```js
put(key: CID, val: Uint8Array): Promise
```
Encrypts and stores a block under the original CID.
* `key`: The original CID.
* `val`: The plaintext data to encrypt and store.
* *Returns*: The original CID.
```js
get(key: CID): Promise
```
Retrieves and decrypts a block by its original CID.
* `key`: The original CID.
* *Returns*: The decrypted plaintext data.
Deletes a block by its original CID.
* `key`: The original CID.
```js
has(key: CID): Promise
```
Checks if a block exists by its original CID.
* `key`: The original CID.
* *Returns*: A boolean indicating existence.
```js
putMany(source: AwaitIterable): AsyncIterable
```
Stores multiple blocks in parallel.
* `source`: An iterable of { cid, block } pairs.
* *Returns*: An async iterable of CIDs.
```js
getMany(source: AwaitIterable): AsyncIterable
```
Retrieves multiple blocks in parallel.
* ```source```: An iterable of CIDs.
* *Returns*: An async iterable of `{ cid, block }` pairs.
```js
deleteMany(source: AwaitIterable): AsyncIterable
```
Deletes multiple blocks in parallel.
* `source`: An iterable of CIDs.
* *Returns*: An async iterable of deleted CIDs.
### Initialization Options (`EncBlockstoreInit`)
You can configure the behavior of the `EncBlockstore` using the `init` parameter.
* `pbkdf2Iterations` (number): The number of PBKDF2 iterations to use. Default: `210000` for SHA-512.
* `pbkdf2hash` ('SHA-512' | 'SHA-256'): The hash algorithm to use for PBKDF2. Default: `'SHA-512'`.
* `saltByteLength` (number): The length of the salt to use for PBKDF2. Default: `16` bytes.
* `putManyConcurrency` (number): How many blocks to put in parallel when `.putMany` is called. Default: `50`.
* `getManyConcurrency` (number): How many blocks to read in parallel when `.getMany` is called. Default: `50`.
* `deleteManyConcurrency` (number): How many blocks to delete in parallel when `.deleteMany` is called. Default: `50`.
## Cryptography Details
* **Key Derivation**:
* Uses PBKDF2 to derive a master key from the password and master salt.
* Default PBKDF2 settings: 210,000 iterations and SHA-512 hash function.
* **Key Expansion**:
* Uses HKDF with SHA-256 to derive separate encryption and MAC keys from the master key.
* The encryption key is used for per-block key derivation.
* **Per-block Encryption**:
* For each block, a unique per-block salt is generated.
* Uses HKDF with the per-block salt to derive a per-block encryption key.
* The block data is encrypted using AES-GCM with a random IV.
* The salt and IV are stored alongside the encrypted data.
* **CID Hashing**:
* The original CID is transformed using HMAC-SHA256 with the MAC key to compute a storage CID.
* The storage CID serves as a mapping to the original CID - it is **not** a content addressed hash of the encrypted block.
* The storage CID is used to store and retrieve the encrypted block.
## Security Considerations
* Password and Salt:
* The security of the encrypted blockstore depends critically on the secrecy of the password and the master salt.
* **If either is compromised, the encrypted data may be at risk.**
* **If either is lost, the data cannot be recovered.**
## Limitations
* No `getAll` Support:
* Due to the one-way mapping of CIDs, the getAll() method is not supported.
* Performance Overhead:
* Encryption and decryption operations introduce computational overhead.