crypto-keygen-suite
Version:
Key generation utilities for cryptographic operations. YES I RENAMED IT. SIX STATE PROTOCOL!!! See its folder for all <3
133 lines (119 loc) • 4.9 kB
JavaScript
// Yes i put ultra because i had lots of ideas for this one and yeah
import yargs from 'yargs/yargs';
import { hideBin } from 'yargs/helpers';
import fs from 'fs';
import crypto from 'crypto';
class SPAKE2 {
constructor(password, salt = crypto.randomBytes(16), curve = 'secp256k1') {
this.password = password;
this.salt = salt;
this.curve = curve;
}
deriveKey(iterations = 100000) {
return crypto.scryptSync(this.password, this.salt, 32, { N: iterations });
}
generateKeyPair() {
const ecdh = crypto.createECDH(this.curve);
ecdh.generateKeys();
return {
privateKey: ecdh.getPrivateKey('hex'),
publicKey: ecdh.getPublicKey('hex')
};
}
computeSharedSecret(peerPublicKey) {
const ecdh = crypto.createECDH(this.curve);
ecdh.setPrivateKey(this.deriveKey());
return ecdh.computeSecret(Buffer.from(peerPublicKey, 'hex')).toString('hex');
}
encryptMessage(message, sharedSecret) {
const key = crypto.createHash('sha256').update(sharedSecret).digest();
const nonce = crypto.randomBytes(12);
const aesgcm = crypto.createCipheriv('aes-256-gcm', key, nonce);
const encrypted = Buffer.concat([aesgcm.update(message, 'utf8'), aesgcm.final()]);
return Buffer.concat([nonce, aesgcm.getAuthTag(), encrypted]).toString('hex');
}
verifyChallenge(peerPublicKey, challenge) {
const derivedSecret = this.computeSharedSecret(peerPublicKey);
return crypto.createHash('sha256').update(derivedSecret).digest('hex') === challenge;
}
saveKeysToFile(filename, keys) {
fs.writeFileSync(filename, JSON.stringify(keys, null, 2));
}
loadKeysFromFile(filename) {
return JSON.parse(fs.readFileSync(filename));
}
}
// CLI Setup
async function main() {
const argv = yargs(hideBin(process.argv))
.option('mode', {
alias: 'm',
type: 'string',
choices: ['keygen', 'exchange', 'encrypt', 'verify'],
describe: 'Generate keys, exchange secrets, encrypt messages, or verify challenges',
})
.option('password', {
alias: 'p',
type: 'string',
demandOption: true,
describe: 'Shared password for authentication',
})
.option('peerKey', {
alias: 'k',
type: 'string',
describe: 'Peer public key for key exchange or verification',
})
.option('message', {
alias: 'msg',
type: 'string',
describe: 'Message to encrypt',
})
.option('challenge', {
alias: 'c',
type: 'string',
describe: 'Verification challenge hash',
})
.option('curve', {
alias: 'curve',
type: 'string',
choices: ['secp256k1', 'prime256v1', 'ed25519'],
default: 'secp256k1',
describe: 'Cryptographic curve for key exchange',
})
.help()
.argv;
const spake2 = new SPAKE2(argv.password, undefined, argv.curve);
if (argv.mode === 'keygen') {
const keys = spake2.generateKeyPair();
console.log(`✅ Generated SPAKE2 Key Pair`);
console.log(`🔑 Private Key: ${keys.privateKey}`);
console.log(`📡 Public Key: ${keys.publicKey}`);
spake2.saveKeysToFile('spake2_keys.json', keys);
} else if (argv.mode === 'exchange') {
if (!argv.peerKey) {
console.error("❌ Key exchange requires a peer public key");
return;
}
const sharedSecret = spake2.computeSharedSecret(argv.peerKey);
console.log(`🔐 Shared Secret: ${sharedSecret}`);
} else if (argv.mode === 'encrypt') {
if (!argv.message || !argv.peerKey) {
console.error("❌ Encryption requires a message and a peer key");
return;
}
const sharedSecret = spake2.computeSharedSecret(argv.peerKey);
const encryptedMessage = spake2.encryptMessage(argv.message, sharedSecret);
console.log(`🔐 Encrypted Message (Hex): ${encryptedMessage}`);
} else if (argv.mode === 'verify') {
if (!argv.peerKey || !argv.challenge) {
console.error("❌ Verification requires a peer key and challenge");
return;
}
const isValid = spake2.verifyChallenge(argv.peerKey, argv.challenge);
console.log(isValid ? "✅ Challenge verified successfully!" : "❌ Challenge verification failed.");
} else {
console.error("❌ Invalid mode. Use --mode keygen | exchange | encrypt | verify");
}
}
console.log("🚀 Debug: Running main function...");
main();