capacitor-shamir
Version:
Provides Shamir's Secret Sharing (SSS) functionality for secure splitting and recovering secrets natively on iOS, Android, and Web.
138 lines • 6.16 kB
JavaScript
import { WebPlugin } from '@capacitor/core';
import { FileSystemMock } from './web/file-system.mock';
import { join, restorePart, split } from './web/scheme';
import { fromBase64, toBase64 } from './web/base64.utils';
export class ShamirWeb extends WebPlugin {
constructor() {
super(...arguments);
this.fs = FileSystemMock.getInstance();
}
async generateShards({ totalShards, threshold, inputDataBase64 }, callback) {
const randomBytes = (len) => crypto.getRandomValues(new Uint8Array(len));
const parts = split(randomBytes, totalShards, threshold, fromBase64(inputDataBase64));
const shards = Object.entries(parts).map(([shardIdx, shardData]) => {
const shardIdxBytes = new Uint8Array([parseInt(shardIdx)]);
const shardBytes = new Uint8Array(1 + shardData.length);
shardBytes.set(shardIdxBytes);
shardBytes.set(shardData, shardIdxBytes.length);
return shardBytes;
});
callback({ progress: 100, shardsBase64: shards.map(shard => toBase64(shard)) });
}
async restoreFromShards({ inputShardsBase64 }, callback) {
const inputShards = inputShardsBase64.map(shardBase64 => fromBase64(shardBase64));
const parts = inputShards.reduce((acc, shardBytes) => {
const shardIdx = shardBytes.subarray(0, 1)[0];
const shardData = shardBytes.subarray(1);
acc[shardIdx] = shardData;
return acc;
}, {});
const result = join(parts);
callback({ progress: 100, dataBase64: toBase64(result) });
}
async restoreShard({ shardIndex, inputShardsBase64 }, callback) {
const inputShards = inputShardsBase64.map(shardBase64 => fromBase64(shardBase64));
const parts = inputShards.reduce((acc, shardBytes) => {
const shardIdx = shardBytes.subarray(0, 1)[0];
const shardData = shardBytes.subarray(1);
acc[shardIdx] = shardData;
return acc;
}, {});
const restoredPart = restorePart(parts, shardIndex);
const restoredShard = new Uint8Array(1 + restoredPart.length);
const shardIdxBytes = new Uint8Array([shardIndex]);
restoredShard.set(shardIdxBytes);
restoredShard.set(restoredPart, shardIdxBytes.length);
callback({ progress: 100, dataBase64: toBase64(restoredShard) });
}
async generateFileShards({ totalShards, threshold, srcPath, dstPathRoot }, callback) {
const inputData = await this.fs.read(srcPath);
const inputDataBase64 = toBase64(inputData);
await this.generateShardsToFiles({ totalShards, threshold, inputDataBase64, dstPathRoot }, callback);
}
async generateShardsToFiles({ totalShards, threshold, inputDataBase64, dstPathRoot }, callback) {
await new Promise((resolve, reject) => {
this.generateShards({ totalShards, threshold, inputDataBase64 }, async ({ progress, shardsBase64 }, error) => {
if (error) {
reject(error);
}
else {
const shards = shardsBase64.map(shardBase64 => fromBase64(shardBase64));
const id = this.generateJobId();
const shardsPaths = [];
for (let i = 0; i < shards.length; i++) {
const path = this.formatShardPath(dstPathRoot, id, i);
shardsPaths.push(path);
await this.fs.write(path, shards[i]);
}
callback({ progress, shardsPaths });
resolve();
}
});
});
}
async restoreFromFileShards({ shardsPaths, dstPath }, callback) {
await new Promise((resolve, reject) => {
this.restoreFromFileShardsToData({ shardsPaths }, async ({ progress, dataBase64 }, error) => {
if (error) {
reject(error);
}
else {
await this.fs.write(dstPath, fromBase64(dataBase64));
callback({ progress, dstPath });
resolve();
}
});
});
}
async restoreFromFileShardsToData({ shardsPaths }, callback) {
const shardsBase64 = [];
for (const path of shardsPaths) {
const data = await this.fs.read(path);
shardsBase64.push(toBase64(data));
}
await new Promise((resolve, reject) => {
this.restoreFromShards({ inputShardsBase64: shardsBase64 }, async ({ progress, dataBase64 }, error) => {
if (error) {
reject(error);
}
else {
callback({ progress, dataBase64 });
resolve();
}
});
});
}
async restoreFileShard({ shardIndex, shardsPaths, dstPathRoot }, callback) {
const shardsBase64 = [];
for (const shardPath of shardsPaths) {
const data = await this.fs.read(shardPath);
shardsBase64.push(toBase64(data));
}
await new Promise((resolve, reject) => {
this.restoreShard({ shardIndex, inputShardsBase64: shardsBase64 }, async ({ progress, dataBase64 }, error) => {
if (error) {
reject(error);
}
else {
const id = this.generateJobId();
const shardPath = this.formatShardPath(dstPathRoot, id, shardIndex);
await this.fs.write(shardPath, fromBase64(dataBase64));
callback({ progress, shardPath });
resolve();
}
});
});
}
formatShardPath(dirPath, id, index) {
return `${dirPath}/${id}_${index}.bin`;
}
generateJobId() {
return Math.random().toString(36).substring(2, 15);
}
/** Web Filesystem Configuration */
updateShamirWebFsIndexedDbConfig(config) {
this.fs.updateIndexedDbConfig(config);
}
}
//# sourceMappingURL=web.js.map