@shard-auth/client
Version:
Next-generation API authentication without secret keys - MPC-based authentication with FROST threshold signatures
175 lines • 6.89 kB
JavaScript
;
var __createBinding = (this && this.__createBinding) || (Object.create ? (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
var desc = Object.getOwnPropertyDescriptor(m, k);
if (!desc || ("get" in desc ? !m.__esModule : desc.writable || desc.configurable)) {
desc = { enumerable: true, get: function() { return m[k]; } };
}
Object.defineProperty(o, k2, desc);
}) : (function(o, m, k, k2) {
if (k2 === undefined) k2 = k;
o[k2] = m[k];
}));
var __setModuleDefault = (this && this.__setModuleDefault) || (Object.create ? (function(o, v) {
Object.defineProperty(o, "default", { enumerable: true, value: v });
}) : function(o, v) {
o["default"] = v;
});
var __importStar = (this && this.__importStar) || (function () {
var ownKeys = function(o) {
ownKeys = Object.getOwnPropertyNames || function (o) {
var ar = [];
for (var k in o) if (Object.prototype.hasOwnProperty.call(o, k)) ar[ar.length] = k;
return ar;
};
return ownKeys(o);
};
return function (mod) {
if (mod && mod.__esModule) return mod;
var result = {};
if (mod != null) for (var k = ownKeys(mod), i = 0; i < k.length; i++) if (k[i] !== "default") __createBinding(result, mod, k[i]);
__setModuleDefault(result, mod);
return result;
};
})();
Object.defineProperty(exports, "__esModule", { value: true });
exports.SecureStorage = void 0;
const errors_1 = require("./errors");
const fs = __importStar(require("fs"));
const crypto = __importStar(require("crypto"));
/**
* シェアの安全な保存・読み込みを行うクラス
*
* @example
* ```typescript
* // ファイルベースのストレージ
* const storage = new SecureStorage('./share.key', {
* password: 'strong-password'
* });
*
* // 環境変数からの読み込み
* const storage = SecureStorage.fromEnvironment();
* ```
*/
class SecureStorage {
storagePath;
options;
shareData;
constructor(storagePath, options = {}) {
this.storagePath = storagePath;
this.options = options;
}
static fromEnvironment() {
const storage = new SecureStorage();
const shareDataStr = process.env.SHARD_AUTH_SHARE;
if (!shareDataStr) {
throw new errors_1.ShareNotFoundError('SHARD_AUTH_SHARE environment variable not set');
}
try {
const shareData = JSON.parse(shareDataStr);
storage.shareData = {
index: shareData.index,
share: Buffer.from(shareData.share, 'base64'),
publicKeyShare: shareData.publicKeyShare ? Buffer.from(shareData.publicKeyShare, 'base64') : Buffer.alloc(0),
commitment: shareData.commitment ? Buffer.from(shareData.commitment, 'base64') : Buffer.alloc(0)
};
}
catch (error) {
throw new errors_1.ShareNotFoundError('Invalid share data in environment variable');
}
return storage;
}
async saveShare(share) {
if (!this.storagePath) {
throw new Error('Storage path not specified');
}
let dataToSave;
if (this.options.password) {
// ランダムなソルトを生成
const salt = crypto.randomBytes(32);
// パスワードからキーを導出(N=16384, r=8, p=1)
const key = crypto.scryptSync(this.options.password, salt, 32, {
N: 16384,
r: 8,
p: 1
});
const iv = crypto.randomBytes(16);
const cipher = crypto.createCipheriv('aes-256-gcm', key, iv);
const shareJson = JSON.stringify({
index: share.index,
share: share.share.toString('base64'),
publicKeyShare: share.publicKeyShare.toString('base64'),
commitment: share.commitment.toString('base64')
});
const encrypted = Buffer.concat([
cipher.update(shareJson, 'utf8'),
cipher.final()
]);
const authTag = cipher.getAuthTag();
// バージョン、ソルト、IV、authTag、暗号化データを結合
const version = Buffer.from([0x01]); // バージョン1
dataToSave = Buffer.concat([version, salt, iv, authTag, encrypted]);
}
else {
// 暗号化なしの場合
dataToSave = Buffer.from(JSON.stringify({
index: share.index,
share: share.share.toString('base64'),
publicKeyShare: share.publicKeyShare.toString('base64'),
commitment: share.commitment.toString('base64')
}));
}
await fs.promises.writeFile(this.storagePath, dataToSave);
}
async loadShare() {
// 環境変数から読み込み済みの場合
if (this.shareData) {
return this.shareData;
}
if (!this.storagePath) {
throw new errors_1.ShareNotFoundError('Storage path not specified');
}
if (!fs.existsSync(this.storagePath)) {
throw new errors_1.ShareNotFoundError(`Share file not found: ${this.storagePath}`);
}
const data = await fs.promises.readFile(this.storagePath);
let shareJson;
if (this.options.password) {
// 復号化
// バージョンチェック
const version = data[0];
if (version !== 0x01) {
throw new Error('Unsupported encryption version');
}
// バージョン、ソルト、IV、authTag、暗号化データを分離
const salt = data.slice(1, 33);
const iv = data.slice(33, 49);
const authTag = data.slice(49, 65);
const encrypted = data.slice(65);
// パスワードからキーを導出
const key = crypto.scryptSync(this.options.password, salt, 32, {
N: 16384,
r: 8,
p: 1
});
const decipher = crypto.createDecipheriv('aes-256-gcm', key, iv);
decipher.setAuthTag(authTag);
const decrypted = Buffer.concat([
decipher.update(encrypted),
decipher.final()
]);
shareJson = JSON.parse(decrypted.toString('utf8'));
}
else {
shareJson = JSON.parse(data.toString('utf8'));
}
return {
index: shareJson.index,
share: Buffer.from(shareJson.share, 'base64'),
publicKeyShare: Buffer.from(shareJson.publicKeyShare, 'base64'),
commitment: Buffer.from(shareJson.commitment, 'base64')
};
}
}
exports.SecureStorage = SecureStorage;
//# sourceMappingURL=secure-storage.js.map