Initial commit
This commit is contained in:
191
benchmarks/diffie-helman.ts
Normal file
191
benchmarks/diffie-helman.ts
Normal file
@@ -0,0 +1,191 @@
|
||||
/**
|
||||
* Pure TypeScript implementation of Diffie-Hellman key exchange
|
||||
*
|
||||
* Uses MODP Group 14 (RFC 3526) parameters:
|
||||
* - 2048-bit prime modulus
|
||||
* - Generator = 2
|
||||
*/
|
||||
|
||||
// MODP Group 14 parameters (RFC 3526)
|
||||
const PRIME_2048 = BigInt(
|
||||
'0x' +
|
||||
'FFFFFFFFFFFFFFFFC90FDAA22168C234C4C6628B80DC1CD1' +
|
||||
'29024E088A67CC74020BBEA63B139B22514A08798E3404DD' +
|
||||
'EF9519B3CD3A431B302B0A6DF25F14374FE1356D6D51C245' +
|
||||
'E485B576625E7EC6F44C42E9A637ED6B0BFF5CB6F406B7ED' +
|
||||
'EE386BFB5A899FA5AE9F24117C4B1FE649286651ECE45B3D' +
|
||||
'C2007CB8A163BF0598DA48361C55D39A69163FA8FD24CF5F' +
|
||||
'83655D23DCA3AD961C62F356208552BB9ED529077096966D' +
|
||||
'670C354E4ABC9804F1746C08CA237327FFFFFFFFFFFFFFFF',
|
||||
);
|
||||
|
||||
const GENERATOR = BigInt(2);
|
||||
|
||||
/**
|
||||
* Convert Uint8Array to BigInt
|
||||
*/
|
||||
function uint8ArrayToBigInt(bytes: Uint8Array): bigint {
|
||||
let result = BigInt(0);
|
||||
for (let i = 0; i < bytes.length; i++) {
|
||||
result = (result << BigInt(8)) + BigInt(bytes[i]);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Convert BigInt to Uint8Array of specified length
|
||||
*/
|
||||
function bigIntToUint8Array(value: bigint, byteLength: number): Uint8Array {
|
||||
const result = new Uint8Array(byteLength);
|
||||
let temp = value;
|
||||
|
||||
for (let i = byteLength - 1; i >= 0; i--) {
|
||||
result[i] = Number(temp & BigInt(0xff));
|
||||
temp = temp >> BigInt(8);
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Modular exponentiation: (base^exponent) mod modulus
|
||||
* Uses binary exponentiation for efficiency
|
||||
*/
|
||||
function modPow(base: bigint, exponent: bigint, modulus: bigint): bigint {
|
||||
if (modulus === BigInt(1)) return BigInt(0);
|
||||
|
||||
let result = BigInt(1);
|
||||
base = base % modulus;
|
||||
|
||||
while (exponent > BigInt(0)) {
|
||||
if (exponent % BigInt(2) === BigInt(1)) {
|
||||
result = (result * base) % modulus;
|
||||
}
|
||||
exponent = exponent >> BigInt(1);
|
||||
base = (base * base) % modulus;
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a Diffie-Hellman public key from a private key
|
||||
*
|
||||
* @param privateKey - Private key as Uint8Array (should be random and less than the prime)
|
||||
* @returns Public key as Uint8Array
|
||||
*/
|
||||
export function generatePublicKey(privateKey: Uint8Array): Uint8Array {
|
||||
const privateKeyBigInt = uint8ArrayToBigInt(privateKey);
|
||||
const publicKeyBigInt = modPow(GENERATOR, privateKeyBigInt, PRIME_2048);
|
||||
return bigIntToUint8Array(publicKeyBigInt, 256); // 2048 bits = 256 bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Compute Diffie-Hellman shared secret
|
||||
*
|
||||
* @param privateKey - Your private key as Uint8Array
|
||||
* @param publicKey - Other party's public key as Uint8Array
|
||||
* @returns Shared secret as Uint8Array
|
||||
*/
|
||||
export function diffieHellman(
|
||||
privateKey: Uint8Array,
|
||||
publicKey: Uint8Array,
|
||||
): Uint8Array {
|
||||
const privateKeyBigInt = uint8ArrayToBigInt(privateKey);
|
||||
const publicKeyBigInt = uint8ArrayToBigInt(publicKey);
|
||||
|
||||
// Validate that the public key is valid (1 < publicKey < prime-1)
|
||||
if (
|
||||
publicKeyBigInt <= BigInt(1) ||
|
||||
publicKeyBigInt >= PRIME_2048 - BigInt(1)
|
||||
) {
|
||||
throw new Error('Invalid public key: must be between 1 and prime-1');
|
||||
}
|
||||
|
||||
// Compute shared secret: (publicKey^privateKey) mod prime
|
||||
const sharedSecretBigInt = modPow(
|
||||
publicKeyBigInt,
|
||||
privateKeyBigInt,
|
||||
PRIME_2048,
|
||||
);
|
||||
|
||||
return bigIntToUint8Array(sharedSecretBigInt, 256); // 2048 bits = 256 bytes
|
||||
}
|
||||
|
||||
/**
|
||||
* Generate a random private key suitable for Diffie-Hellman
|
||||
*
|
||||
* @returns Random private key as Uint8Array
|
||||
*/
|
||||
export function generatePrivateKey(): Uint8Array {
|
||||
// Generate a random 256-byte private key
|
||||
// In practice, you'd use crypto.getRandomValues() but since this is pure TS:
|
||||
const privateKey = new Uint8Array(256);
|
||||
|
||||
// Simple pseudo-random generation (NOT cryptographically secure)
|
||||
// In real applications, use crypto.getRandomValues() or similar
|
||||
for (let i = 0; i < privateKey.length; i++) {
|
||||
privateKey[i] = Math.floor(Math.random() * 256);
|
||||
}
|
||||
|
||||
// Ensure the private key is less than the prime by clearing the most significant bit
|
||||
privateKey[0] &= 0x7f;
|
||||
|
||||
return privateKey;
|
||||
}
|
||||
|
||||
/**
|
||||
* Complete Diffie-Hellman key exchange example
|
||||
*
|
||||
* @returns Object with generated keys and shared secret
|
||||
*/
|
||||
export function demonstrateDiffieHellman() {
|
||||
// Alice generates her key pair
|
||||
const alicePrivateKey = generatePrivateKey();
|
||||
const alicePublicKey = generatePublicKey(alicePrivateKey);
|
||||
|
||||
// Bob generates his key pair
|
||||
const bobPrivateKey = generatePrivateKey();
|
||||
const bobPublicKey = generatePublicKey(bobPrivateKey);
|
||||
|
||||
// Both parties compute the same shared secret
|
||||
const aliceSharedSecret = diffieHellman(alicePrivateKey, bobPublicKey);
|
||||
const bobSharedSecret = diffieHellman(bobPrivateKey, alicePublicKey);
|
||||
|
||||
// Verify they computed the same secret
|
||||
const secretsMatch = aliceSharedSecret.every(
|
||||
(byte, index) => byte === bobSharedSecret[index],
|
||||
);
|
||||
|
||||
return {
|
||||
alicePrivateKey,
|
||||
alicePublicKey,
|
||||
bobPrivateKey,
|
||||
bobPublicKey,
|
||||
aliceSharedSecret,
|
||||
bobSharedSecret,
|
||||
secretsMatch,
|
||||
};
|
||||
}
|
||||
|
||||
// Example usage and test
|
||||
if (require.main === module) {
|
||||
console.log('Diffie-Hellman Key Exchange Demo');
|
||||
console.log('================================');
|
||||
|
||||
const demo = demonstrateDiffieHellman();
|
||||
|
||||
console.log(
|
||||
'Alice Public Key (first 8 bytes):',
|
||||
Array.from(demo.alicePublicKey.slice(0, 8)),
|
||||
);
|
||||
console.log(
|
||||
'Bob Public Key (first 8 bytes):',
|
||||
Array.from(demo.bobPublicKey.slice(0, 8)),
|
||||
);
|
||||
console.log(
|
||||
'Shared Secret (first 8 bytes):',
|
||||
Array.from(demo.aliceSharedSecret.slice(0, 8)),
|
||||
);
|
||||
console.log('Secrets Match:', demo.secretsMatch);
|
||||
}
|
||||
7
benchmarks/rng.ts
Normal file
7
benchmarks/rng.ts
Normal file
@@ -0,0 +1,7 @@
|
||||
import crypto from 'crypto';
|
||||
import { Bytes } from '../src/crypto/bytes.js';
|
||||
|
||||
const rng = crypto.getRandomValues(new Uint8Array(2048));
|
||||
const bytes = Bytes.from(rng);
|
||||
|
||||
console.log(bytes.toBase64());
|
||||
57
benchmarks/sekp256k1.ts
Normal file
57
benchmarks/sekp256k1.ts
Normal file
@@ -0,0 +1,57 @@
|
||||
import { Bytes, User } from '../src/index.js';
|
||||
|
||||
const alice = await User.fromSecret('alice');
|
||||
const bob = await User.fromSecret('bob');
|
||||
|
||||
const alicePublicKey = await alice.getPublicKey();
|
||||
const bobPublicKey = await bob.getPublicKey();
|
||||
|
||||
const keyStart = performance.now();
|
||||
const aliceSharedSecret = await alice.getSharedSecret(bobPublicKey);
|
||||
const bobSharedSecret = await bob.getSharedSecret(alicePublicKey);
|
||||
|
||||
const keyEnd = performance.now();
|
||||
|
||||
console.log(`Key generation time: ${keyEnd - keyStart}ms`);
|
||||
|
||||
const message = 'Hello, world!';
|
||||
const count = 100_000;
|
||||
|
||||
const start = performance.now();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// Encrypt key with shared secret
|
||||
const encryptedKey = await aliceSharedSecret.encrypt(Bytes.fromUtf8(message));
|
||||
|
||||
// Decrypt key with shared secret
|
||||
const decryptedKey = await bobSharedSecret.decrypt(encryptedKey);
|
||||
|
||||
if (decryptedKey.toUtf8() !== message) {
|
||||
throw new Error('Decrypted message does not match original message');
|
||||
}
|
||||
}
|
||||
|
||||
const end = performance.now();
|
||||
|
||||
const totalTimeMs = end - start;
|
||||
const operationsPerSecond = (count / totalTimeMs) * 1000;
|
||||
|
||||
console.log(
|
||||
`Aes encrypt/decrypt per second: ${operationsPerSecond} (Total time: ${totalTimeMs}ms)`,
|
||||
);
|
||||
|
||||
const startEncrypt = performance.now();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
// Encrypt key with shared secret
|
||||
const encryptedKey = await aliceSharedSecret.encrypt(Bytes.fromUtf8(message));
|
||||
}
|
||||
|
||||
const endEncrypt = performance.now();
|
||||
|
||||
const totalTimeMsEncrypt = endEncrypt - startEncrypt;
|
||||
const operationsPerSecondEncrypt = (count / totalTimeMsEncrypt) * 1000;
|
||||
|
||||
console.log(
|
||||
`Aes encrypt per second: ${operationsPerSecondEncrypt} (Total time: ${totalTimeMsEncrypt}ms)`,
|
||||
);
|
||||
25
benchmarks/sha256.ts
Normal file
25
benchmarks/sha256.ts
Normal file
@@ -0,0 +1,25 @@
|
||||
import { sha256, binToBase58 } from '@bitauth/libauth';
|
||||
|
||||
const data = 'Hello, world!';
|
||||
const count = 1_000_000;
|
||||
|
||||
let bytes = new TextEncoder().encode(data);
|
||||
const start = performance.now();
|
||||
|
||||
for (let i = 0; i < count; i++) {
|
||||
bytes = new Uint8Array(sha256.hash(bytes));
|
||||
}
|
||||
const end = performance.now();
|
||||
|
||||
const totalTimeMs = end - start;
|
||||
const operationsPerSecond = (count / totalTimeMs) * 1000;
|
||||
|
||||
console.log(
|
||||
`libauth sha256 per second: ${operationsPerSecond} (Total time: ${totalTimeMs}ms)`,
|
||||
);
|
||||
|
||||
console.log(binToBase58(bytes));
|
||||
|
||||
if (binToBase58(bytes) !== '2YZjvhWqVKZgQFDnVkadwRcpJkqW6oNjiHPXxkEpq2zP') {
|
||||
throw new Error('sha256 hash is incorrect');
|
||||
}
|
||||
38
benchmarks/storage.ts
Normal file
38
benchmarks/storage.ts
Normal file
@@ -0,0 +1,38 @@
|
||||
import { AESKey } from '../src/crypto/aes-key.js';
|
||||
import { BaseStorage } from '../src/storage/base-storage.js';
|
||||
import { StorageMemory, StorageMemorySynced, EncryptedStorage } from '../src/storage/index.js';
|
||||
|
||||
const storage = StorageMemory.from();
|
||||
// const storageSynced = StorageMemorySynced.from(storage);
|
||||
|
||||
const currentDate = new Date();
|
||||
|
||||
const data = {
|
||||
name: 'test',
|
||||
age: 20,
|
||||
email: 'test@test.com',
|
||||
password: 'test',
|
||||
createdAt: currentDate,
|
||||
updatedAt: new Date(currentDate.getTime() + 1000),
|
||||
}
|
||||
|
||||
const storageEncryptedBase = StorageMemory.from()
|
||||
const storageEncrypted = EncryptedStorage.from(storageEncryptedBase, await AESKey.fromSeed('test'));
|
||||
|
||||
storageEncryptedBase.on('insert', (event) => {
|
||||
console.log('insert', event);
|
||||
});
|
||||
|
||||
// Store data in storage
|
||||
await storage.insertOne('test', data);
|
||||
// storageSynced.insertOne('test', data);
|
||||
await storageEncrypted.insertOne('test', data);
|
||||
|
||||
// Retrieve data from storage
|
||||
const retrievedData = await storage.findOne({ name: 'test' });
|
||||
// const retrievedDataSynced = await storageSynced.findOne('test');
|
||||
const retrievedDataEncrypted = await storageEncrypted.findOne({ name: 'test' });
|
||||
|
||||
console.log(retrievedData);
|
||||
// console.log(retrievedDataSynced);
|
||||
console.log(retrievedDataEncrypted);
|
||||
Reference in New Issue
Block a user