@julesl23/s5js
Version:
Enhanced TypeScript SDK for S5 decentralized storage with path-based API, media processing, and directory utilities
93 lines • 3.98 kB
JavaScript
import { bytesToUtf8, utf8ToBytes } from "@noble/ciphers/utils";
import { decryptMutableBytes, encryptMutableBytes } from "../encryption/mutable.js";
import { BlobIdentifier } from "../identifier/blob.js";
import { createRegistryEntry } from "../registry/entry.js";
import { deriveHashInt, deriveHashString } from "../util/derive_hash.js";
const pathKeyDerivationTweak = 1;
const writeKeyDerivationTweak = 2;
const encryptionKeyDerivationTweak = 3;
class HiddenDBProvider {
}
export class TrustedHiddenDBProvider extends HiddenDBProvider {
hiddenRootKey;
api;
cidMap = new Map();
constructor(hiddenRootKey, api) {
super();
this.hiddenRootKey = hiddenRootKey;
this.api = api;
}
async getRawData(path) {
const pathKey = this.derivePathKeyForPath(path);
const res = await this.getHiddenRawDataImplementation(pathKey);
if (res.cid) {
this.cidMap.set(path, res.cid);
}
return res;
}
async setRawData(path, data, revision) {
const pathKey = this.derivePathKeyForPath(path);
const newCID = await this.setHiddenRawDataImplementation(pathKey, data, revision);
if (this.cidMap.has(path)) {
await this.api.unpinHash(this.cidMap.get(path).hash);
}
this.cidMap.set(path, newCID);
}
derivePathKeyForPath(path) {
const pathSegments = path
.split('/')
.map(e => e.trim())
.filter(element => element.length > 0);
const key = this.deriveKeyForPathSegments(pathSegments);
return deriveHashInt(key, pathKeyDerivationTweak, this.api.crypto);
}
deriveKeyForPathSegments(pathSegments) {
if (pathSegments.length === 0) {
return this.hiddenRootKey;
}
const parentKey = this.deriveKeyForPathSegments(pathSegments.slice(0, pathSegments.length - 1));
return deriveHashString(parentKey, utf8ToBytes(pathSegments[pathSegments.length - 1]), this.api.crypto);
}
async getJSON(path) {
const res = await this.getRawData(path);
if (!res.data) {
return { cid: res.cid, revision: res.revision };
}
return {
data: JSON.parse(bytesToUtf8(res.data)),
revision: res.revision,
cid: res.cid
};
}
async setJSON(path, data, revision) {
return this.setRawData(path, utf8ToBytes(JSON.stringify(data)), revision);
}
async setHiddenRawDataImplementation(pathKey, data, revision) {
const encryptionKey = deriveHashInt(pathKey, encryptionKeyDerivationTweak, this.api.crypto);
const cipherText = await encryptMutableBytes(data, encryptionKey, this.api.crypto);
const cid = await this.api.uploadBlob(new Blob([cipherText]));
const writeKey = deriveHashInt(pathKey, writeKeyDerivationTweak, this.api.crypto);
const keyPair = await this.api.crypto.newKeyPairEd25519(writeKey);
const sre = await createRegistryEntry(keyPair, cid.hash, revision, this.api.crypto);
await this.api.registrySet(sre);
return cid;
}
async getHiddenRawDataImplementation(pathKey) {
const encryptionKey = deriveHashInt(pathKey, encryptionKeyDerivationTweak, this.api.crypto);
const writeKey = deriveHashInt(pathKey, writeKeyDerivationTweak, this.api.crypto);
const keyPair = await this.api.crypto.newKeyPairEd25519(writeKey);
const sre = await this.api.registryGet(keyPair.publicKey);
if (sre === undefined) {
return { revision: -1 };
}
const hash = sre.data.subarray(0, 33);
const bytes = await this.api.downloadBlobAsBytes(hash);
const plaintext = await decryptMutableBytes(bytes, encryptionKey, this.api.crypto);
return {
data: plaintext,
cid: new BlobIdentifier(hash, 0),
revision: sre.revision,
};
}
}
//# sourceMappingURL=hidden_db.js.map