Comparing version 2.5.0 to 2.6.0
@@ -11,2 +11,6 @@ /// <reference types="node" /> | ||
}; | ||
export declare function createKeyStoreKey(password: string, salt: Buffer): Promise<{ | ||
secretKey: Buffer; | ||
publicKey: Buffer; | ||
}>; | ||
export declare class KeyStore { | ||
@@ -19,6 +23,8 @@ #private; | ||
checkPassword: (password: string) => Promise<boolean>; | ||
getSecretKey: (name: string, password: string) => Promise<Buffer>; | ||
addKey: (record: KeyRecord, password: string, key: Buffer) => Promise<void>; | ||
hasKey: (name: string) => boolean; | ||
getKey: (name: string) => KeyRecord | null; | ||
getSecret: (name: string, password: string) => Promise<Buffer>; | ||
addKey: (record: KeyRecord, key: Buffer) => Promise<void>; | ||
removeKey: (name: string) => void; | ||
save(): Promise<Buffer>; | ||
} |
@@ -21,4 +21,7 @@ "use strict"; | ||
}; | ||
var __importDefault = (this && this.__importDefault) || function (mod) { | ||
return (mod && mod.__esModule) ? mod : { "default": mod }; | ||
}; | ||
Object.defineProperty(exports, "__esModule", { value: true }); | ||
exports.KeyStore = void 0; | ||
exports.KeyStore = exports.createKeyStoreKey = void 0; | ||
const ton_crypto_1 = require("ton-crypto"); | ||
@@ -28,5 +31,7 @@ const t = __importStar(require("io-ts")); | ||
const __1 = require(".."); | ||
const tweetnacl_1 = __importDefault(require("tweetnacl")); | ||
const codec = t.type({ | ||
version: t.number, | ||
salt: t.string, | ||
publicKey: t.string, | ||
records: t.array(t.type({ | ||
@@ -42,9 +47,16 @@ name: t.string, | ||
}); | ||
async function createKeyStoreKey(password, salt) { | ||
let secretKey = await (0, ton_crypto_1.pbkdf2_sha512)(password, salt, 400000, 32); | ||
let r = tweetnacl_1.default.box.keyPair.fromSecretKey(secretKey); | ||
return { | ||
secretKey: Buffer.from(r.secretKey), | ||
publicKey: Buffer.from(r.publicKey) | ||
}; | ||
} | ||
exports.createKeyStoreKey = createKeyStoreKey; | ||
class KeyStore { | ||
static async createNew(password) { | ||
let encryptionSalt = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Salt', 1000000, 32); | ||
let encryptionNonce = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Nonce', 1000000, 24); | ||
let encryptionKey = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Key', 1000000, 32); | ||
let sealed = (0, ton_crypto_1.sealBox)(encryptionSalt, encryptionNonce, encryptionKey).toString('hex'); | ||
return new KeyStore({ version: 1, salt: sealed, records: [] }); | ||
let salt = await (0, ton_crypto_1.getSecureRandomBytes)(32); | ||
let key = await createKeyStoreKey(password, salt); | ||
return new KeyStore({ version: 1, salt: salt.toString('hex'), publicKey: key.publicKey.toString('hex'), records: [] }); | ||
} | ||
@@ -71,2 +83,3 @@ static async load(source) { | ||
#salt; | ||
#publicKey; | ||
#records = new Map(); | ||
@@ -78,2 +91,3 @@ constructor(src) { | ||
this.#salt = src.salt; | ||
this.#publicKey = src.publicKey; | ||
for (let r of src.records) { | ||
@@ -112,13 +126,28 @@ if (this.#records.has(r.name)) { | ||
checkPassword = async (password) => { | ||
// Check password | ||
let encryptionSalt = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Salt', 1000000, 32); | ||
let encryptionNonce = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Nonce', 1000000, 24); | ||
let encryptionKey2 = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage Key', 1000000, 32); | ||
let sealed = (0, ton_crypto_1.sealBox)(encryptionSalt, encryptionNonce, encryptionKey2); | ||
if (!sealed.equals(Buffer.from(this.#salt, 'hex'))) { | ||
let key = await createKeyStoreKey(password, Buffer.from(this.#salt, 'hex')); | ||
if (!key.publicKey.equals(Buffer.from(this.#publicKey, 'hex'))) { | ||
return false; | ||
} | ||
return true; | ||
else { | ||
return true; | ||
} | ||
}; | ||
getSecretKey = async (name, password) => { | ||
hasKey = (name) => { | ||
return this.#records.has(name); | ||
}; | ||
getKey = (name) => { | ||
let ex = this.#records.get(name); | ||
if (ex) { | ||
return { | ||
name: ex.name, | ||
address: ex.address, | ||
kind: ex.kind, | ||
config: ex.config, | ||
comment: ex.comment, | ||
publicKey: Buffer.from(ex.publicKey, 'hex') | ||
}; | ||
} | ||
return null; | ||
}; | ||
getSecret = async (name, password) => { | ||
if (!this.#records.has(name)) { | ||
@@ -130,22 +159,27 @@ throw Error('Key with name ' + name + ' does not exist'); | ||
let nonce = src.slice(0, 24); | ||
let data = src.slice(24); | ||
let encryptionKey = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage: ' + record.name, 1000000, 32); | ||
let res = (0, ton_crypto_1.openBox)(data, nonce, encryptionKey); | ||
if (!res) { | ||
let publicKey = src.slice(24, 24 + 32); | ||
let data = src.slice(24 + 32); | ||
// Derive key | ||
let key = await createKeyStoreKey(password, Buffer.from(this.#salt, 'hex')); | ||
if (!key.publicKey.equals(Buffer.from(this.#publicKey, 'hex'))) { | ||
throw Error('Invalid password'); | ||
} | ||
return res; | ||
// Decode | ||
let decoded = tweetnacl_1.default.box.open(data, nonce, publicKey, key.secretKey); | ||
if (!decoded) { | ||
throw Error('Invalid password'); | ||
} | ||
return Buffer.from(decoded); | ||
}; | ||
addKey = async (record, password, key) => { | ||
addKey = async (record, key) => { | ||
if (this.#records.has(record.name)) { | ||
throw Error('Key with name ' + record.name + ' already exists'); | ||
} | ||
// Check password | ||
if (!(await this.checkPassword(password))) { | ||
throw Error('Invalid password'); | ||
} | ||
// Create key | ||
let encryptionKey = await (0, ton_crypto_1.pbkdf2_sha512)(password, 'TON Encrypted Storage: ' + record.name, 1000000, 32); | ||
let ephemeralKeySecret = await (0, ton_crypto_1.getSecureRandomBytes)(32); | ||
let ephemeralKeyPublic = Buffer.from((tweetnacl_1.default.box.keyPair.fromSecretKey(ephemeralKeySecret)).publicKey); | ||
let nonce = await (0, ton_crypto_1.getSecureRandomBytes)(24); | ||
let encryptedSecretKey = (0, ton_crypto_1.sealBox)(key, nonce, encryptionKey); | ||
let encrypted = tweetnacl_1.default.box(key, nonce, Buffer.from(this.#publicKey, 'hex'), ephemeralKeySecret); | ||
let data = Buffer.concat([nonce, ephemeralKeyPublic, encrypted]); | ||
// Create record | ||
let rec = { | ||
@@ -158,3 +192,3 @@ name: record.name, | ||
publicKey: record.publicKey.toString('hex'), | ||
secretKey: Buffer.concat([nonce, encryptedSecretKey]).toString('hex') | ||
secretKey: data.toString('hex') | ||
}; | ||
@@ -174,2 +208,3 @@ Object.freeze(rec); | ||
salt: this.#salt, | ||
publicKey: this.#publicKey, | ||
records: Array.from(this.#records.entries()).map((v) => ({ | ||
@@ -176,0 +211,0 @@ name: v[1].name, |
@@ -23,3 +23,3 @@ "use strict"; | ||
publicKey: Buffer.from([4, 5, 6]) | ||
}, 'password', Buffer.from([1, 2, 3])); | ||
}, Buffer.from([1, 2, 3])); | ||
expect(keystore.allKeys.length).toBe(1); | ||
@@ -29,9 +29,9 @@ let saved = await keystore.save(); | ||
expect(keystore2.allKeys.length).toBe(1); | ||
let secret = await keystore.getSecretKey('key-1', 'password'); | ||
let secret = await keystore.getSecret('key-1', 'password'); | ||
expect(secret).toEqual(Buffer.from([1, 2, 3])); | ||
secret = await keystore2.getSecretKey('key-1', 'password'); | ||
secret = await keystore2.getSecret('key-1', 'password'); | ||
expect(secret).toEqual(Buffer.from([1, 2, 3])); | ||
await expect(keystore.getSecretKey('key-1', 'wrong password')).rejects.toThrowError(); | ||
await expect(keystore2.getSecretKey('key-1', 'wrong password')).rejects.toThrowError(); | ||
await expect(keystore.getSecret('key-1', 'wrong password')).rejects.toThrowError(); | ||
await expect(keystore2.getSecret('key-1', 'wrong password')).rejects.toThrowError(); | ||
}); | ||
}); |
{ | ||
"name": "ton", | ||
"version": "2.5.0", | ||
"version": "2.6.0", | ||
"repository": "https://github.com/ex3ndr/ton.git", | ||
@@ -39,5 +39,6 @@ "author": "Steve Korshakov <steve@korshakov.com>", | ||
"isomorphic-unfetch": "3.1.0", | ||
"ton-crypto": "1.3.0", | ||
"tonweb": "0.0.18" | ||
"ton-crypto": "1.3.2", | ||
"tonweb": "0.0.18", | ||
"tweetnacl": "^1.0.3" | ||
} | ||
} |
135083
3548
7
+ Addedtweetnacl@^1.0.3
+ Addedton-crypto@1.3.2(transitive)
- Removedton-crypto@1.3.0(transitive)
Updatedton-crypto@1.3.2