use bao_engine::context::JsContext;
use bao_engine::value::JsValue;
fn eval_string(ctx: &mut JsContext, source: &str) -> String {
match ctx.eval(source, "<test>") {
Ok(JsValue::String(s)) => s,
Ok(JsValue::Number(n)) => format!("{}", n),
Ok(JsValue::Bool(b)) => if b { "true" } else { "false" }.to_string(),
_ => String::new(),
}
}
#[test]
fn test_realworld_crypto_workflows() {
bun_runtime::install_exit_handler();
bun_runtime::bun_api::init_process_start();
let mut ctx = JsContext::for_test().expect("JsContext");
ctx.set_global_setup(bun_runtime::globals::install_all);
let password_hash = eval_string(
&mut ctx,
r#"
var results = [];
try {
var crypto = require('crypto');
// --- Registration: hash password with random salt ---
var db = {}; // simulated user database
function hashPassword(password, salt) {
return crypto.createHash('sha256').update(password + salt).digest('hex');
}
function register(username, password) {
var saltBytes = crypto.randomBytes(16);
var salt = Buffer.from(saltBytes).toString('hex');
var hash = hashPassword(password, salt);
db[username] = { salt: salt, hash: hash };
return { user: username, saltLen: salt.length, hashLen: hash.length };
}
function login(username, password) {
var rec = db[username];
if (!rec) return { ok: false, reason: 'no_user' };
var candidate = hashPassword(password, rec.salt);
return { ok: candidate === rec.hash, reason: candidate === rec.hash ? 'match' : 'mismatch' };
}
// Register two users
var u1 = register('alice', 'correcthorse battery staple');
var u2 = register('bob', 'my-secret-pwd');
results.push('u1_user=' + u1.user);
results.push('u1_saltLen=' + u1.saltLen);
results.push('u1_hashLen=' + u1.hashLen);
results.push('u2_user=' + u2.user);
results.push('u2_saltLen=' + u2.saltLen);
// Hashes are hex sha256 = 64 chars
results.push('hash_len_correct=' + (u1.hashLen === 64 ? 'yes' : 'no'));
// Salts should be different per user (16 bytes = 32 hex chars)
results.push('salt_len_correct=' + (u1.saltLen === 32 ? 'yes' : 'no'));
// Login with correct password
var loginOk = login('alice', 'correcthorse battery staple');
results.push('login_alice_ok=' + (loginOk.ok ? 'yes' : 'no'));
results.push('login_alice_reason=' + loginOk.reason);
// Login with wrong password
var loginBad = login('alice', 'wrong-password');
results.push('login_alice_wrong=' + (loginBad.ok ? 'yes' : 'no'));
results.push('login_alice_wrong_reason=' + loginBad.reason);
// Login nonexistent user
var loginNo = login('nobody', 'x');
results.push('login_nobody=' + (loginNo.ok ? 'yes' : 'no'));
results.push('login_nobody_reason=' + loginNo.reason);
// Different passwords produce different hashes for same user (different salts)
var h1 = hashPassword('pwd', 'salt-a');
var h2 = hashPassword('pwd', 'salt-b');
results.push('same_pwd_diff_salt=' + (h1 !== h2 ? 'diff' : 'same'));
// Same password + same salt = same hash (deterministic)
var h3 = hashPassword('pwd', 'salt-a');
results.push('deterministic=' + (h1 === h3 ? 'yes' : 'no'));
results.push('SCENARIO_1_PASSED');
} catch(e) {
results.push('SCENARIO_1_ERR:' + (e.message || e));
}
results.join('|')
"#,
);
assert!(
password_hash.contains("hash_len_correct=yes"),
"sha256 hex hash is 64 chars: {}",
password_hash
);
assert!(
password_hash.contains("salt_len_correct=yes"),
"16-byte salt is 32 hex chars: {}",
password_hash
);
assert!(
password_hash.contains("login_alice_ok=yes"),
"correct password authenticates: {}",
password_hash
);
assert!(
password_hash.contains("login_alice_reason=match"),
"match reason: {}",
password_hash
);
assert!(
password_hash.contains("login_alice_wrong=no"),
"wrong password rejected: {}",
password_hash
);
assert!(
password_hash.contains("login_alice_wrong_reason=mismatch"),
"mismatch reason: {}",
password_hash
);
assert!(
password_hash.contains("login_nobody_reason=no_user"),
"unknown user rejected: {}",
password_hash
);
assert!(
password_hash.contains("same_pwd_diff_salt=diff"),
"different salts → different hashes: {}",
password_hash
);
assert!(
password_hash.contains("deterministic=yes"),
"hash is deterministic: {}",
password_hash
);
assert!(
password_hash.contains("SCENARIO_1_PASSED"),
"scenario 1 complete: {}",
password_hash
);
let hmac_sig = eval_string(
&mut ctx,
r#"
var results = [];
try {
var crypto = require('crypto');
var WEBHOOK_SECRET = 'whsec_MfKQ0rLu6hWbBnX00T4Y';
function sign(payload, secret) {
return crypto.createHmac('sha256', secret).update(payload).digest('hex');
}
// Server-side: verify signature header
function verify(payload, signatureHex, secret) {
var expected = sign(payload, secret);
// Constant-time-ish compare (length first, then char-by-char)
if (expected.length !== signatureHex.length) return false;
var diff = 0;
for (var i = 0; i < expected.length; i++) {
diff |= expected.charCodeAt(i) ^ signatureHex.charCodeAt(i);
}
return diff === 0;
}
// Stripe-like event payload
var payload = JSON.stringify({
id: 'evt_123',
object: 'event',
type: 'payment_intent.succeeded',
data: { amount: 2000, currency: 'usd' }
});
var sig = sign(payload, WEBHOOK_SECRET);
results.push('sig_len=' + sig.length);
results.push('sig_hex=' + (/^[0-9a-f]+$/.test(sig) ? 'yes' : 'no'));
// Legitimate webhook
var legit = verify(payload, sig, WEBHOOK_SECRET);
results.push('verify_legit=' + (legit ? 'yes' : 'no'));
// Tampered payload (man-in-the-middle attack)
var tampered = payload.replace('"amount":2000', '"amount":99999');
var caughtTamper = !verify(tampered, sig, WEBHOOK_SECRET);
results.push('caught_tamper=' + (caughtTamper ? 'yes' : 'no'));
// Wrong secret
var wrongSecret = !verify(payload, sig, 'wrong-secret');
results.push('caught_wrong_secret=' + (wrongSecret ? 'yes' : 'no'));
// Wrong signature length
var caughtShort = !verify(payload, sig.substring(0, 32), WEBHOOK_SECRET);
results.push('caught_short_sig=' + (caughtShort ? 'yes' : 'no'));
// Different payload → different signature
var sigA = sign('payload-a', 'key');
var sigB = sign('payload-b', 'key');
results.push('sig_diff_payloads=' + (sigA !== sigB ? 'diff' : 'same'));
// Same payload + key = same signature (deterministic)
var sigA2 = sign('payload-a', 'key');
results.push('sig_deterministic=' + (sigA === sigA2 ? 'yes' : 'no'));
results.push('SCENARIO_2_PASSED');
} catch(e) {
results.push('SCENARIO_2_ERR:' + (e.message || e));
}
results.join('|')
"#,
);
assert!(
hmac_sig.contains("sig_len=64"),
"HMAC-SHA256 hex is 64 chars: {}",
hmac_sig
);
assert!(
hmac_sig.contains("sig_hex=yes"),
"HMAC digest is hex: {}",
hmac_sig
);
assert!(
hmac_sig.contains("verify_legit=yes"),
"legitimate signature verifies: {}",
hmac_sig
);
assert!(
hmac_sig.contains("caught_tamper=yes"),
"tampered payload rejected: {}",
hmac_sig
);
assert!(
hmac_sig.contains("caught_wrong_secret=yes"),
"wrong secret rejected: {}",
hmac_sig
);
assert!(
hmac_sig.contains("caught_short_sig=yes"),
"truncated signature rejected: {}",
hmac_sig
);
assert!(
hmac_sig.contains("sig_diff_payloads=diff"),
"different payloads produce different sigs: {}",
hmac_sig
);
assert!(
hmac_sig.contains("sig_deterministic=yes"),
"HMAC is deterministic: {}",
hmac_sig
);
assert!(
hmac_sig.contains("SCENARIO_2_PASSED"),
"scenario 2 complete: {}",
hmac_sig
);
let jwt = eval_string(
&mut ctx,
r#"
var results = [];
try {
var crypto = require('crypto');
var SECRET = 'super-secret-jwt-key';
function b64encode(str) {
// Buffer base64 with URL-safe chars stripped of padding
return Buffer.from(str, 'utf8').toString('base64');
}
function b64decode(b64) {
return Buffer.from(b64, 'base64').toString('utf8');
}
function sign(data) {
return crypto.createHmac('sha256', SECRET).update(data).digest('hex');
}
function createToken(payload) {
var header = { alg: 'HS256', typ: 'JWT' };
var h = b64encode(JSON.stringify(header));
var p = b64encode(JSON.stringify(payload));
var sig = sign(h + '.' + p);
return h + '.' + p + '.' + sig;
}
function verifyToken(token) {
var parts = token.split('.');
if (parts.length !== 3) return { ok: false, reason: 'malformed' };
var h = parts[0], p = parts[1], sig = parts[2];
var expected = sign(h + '.' + p);
if (expected !== sig) return { ok: false, reason: 'bad_sig' };
try {
var payload = JSON.parse(b64decode(p));
if (payload.exp && Date.now() > payload.exp) {
return { ok: false, reason: 'expired' };
}
return { ok: true, payload: payload };
} catch(e) {
return { ok: false, reason: 'bad_json' };
}
}
var now = Date.now();
var token = createToken({
sub: 'user-42',
name: 'Alice',
iat: now,
exp: now + 3600000 // 1 hour
});
var parts = token.split('.');
results.push('token_parts=' + parts.length);
results.push('sig_part_len=' + parts[2].length);
// Decode header
var header = JSON.parse(b64decode(parts[0]));
results.push('header_alg=' + header.alg);
results.push('header_typ=' + header.typ);
// Verify legitimate token
var legit = verifyToken(token);
results.push('verify_ok=' + (legit.ok ? 'yes' : 'no'));
results.push('verify_sub=' + legit.payload.sub);
results.push('verify_name=' + legit.payload.name);
// Forged token (signature tampered)
var forged = parts[0] + '.' + parts[1] + '.deadbeef';
var caughtForged = verifyToken(forged);
results.push('forged_reason=' + caughtForged.reason);
// Malformed token
var malformed = verifyToken('not-a-jwt');
results.push('malformed_reason=' + malformed.reason);
// Expired token
var expiredToken = createToken({
sub: 'user-99',
exp: now - 1000 // already expired
});
var expired = verifyToken(expiredToken);
results.push('expired_reason=' + expired.reason);
// Payload tampering (changes payload but keeps old sig)
var tamperedParts = token.split('.');
var tamperedPayload = b64encode(JSON.stringify({ sub: 'admin', exp: now + 99999 }));
var tamperedToken = tamperedParts[0] + '.' + tamperedPayload + '.' + tamperedParts[2];
var caughtTamper = verifyToken(tamperedToken);
results.push('tamper_payload_reason=' + caughtTamper.reason);
results.push('SCENARIO_3_PASSED');
} catch(e) {
results.push('SCENARIO_3_ERR:' + (e.message || e));
}
results.join('|')
"#,
);
assert!(jwt.contains("token_parts=3"), "token has 3 parts: {}", jwt);
assert!(
jwt.contains("sig_part_len=64"),
"signature is 64 hex chars: {}",
jwt
);
assert!(
jwt.contains("header_alg=HS256"),
"header alg preserved: {}",
jwt
);
assert!(
jwt.contains("header_typ=JWT"),
"header typ preserved: {}",
jwt
);
assert!(
jwt.contains("verify_ok=yes"),
"valid token verifies: {}",
jwt
);
assert!(
jwt.contains("verify_sub=user-42"),
"payload sub preserved: {}",
jwt
);
assert!(
jwt.contains("verify_name=Alice"),
"payload name preserved: {}",
jwt
);
assert!(
jwt.contains("forged_reason=bad_sig"),
"forged signature rejected: {}",
jwt
);
assert!(
jwt.contains("malformed_reason=malformed"),
"malformed token rejected: {}",
jwt
);
assert!(
jwt.contains("expired_reason=expired"),
"expired token rejected: {}",
jwt
);
assert!(
jwt.contains("tamper_payload_reason=bad_sig"),
"payload tampering caught: {}",
jwt
);
assert!(
jwt.contains("SCENARIO_3_PASSED"),
"scenario 3 complete: {}",
jwt
);
let encryption = eval_string(
&mut ctx,
r#"
var results = [];
try {
var crypto = require('crypto');
var aesAvailable = typeof crypto.createCipheriv === 'function'
&& typeof crypto.createDecipheriv === 'function';
if (aesAvailable) {
// --- AES-256-CBC round-trip ---
try {
var key = crypto.randomBytes(32); // 256-bit key
var iv = crypto.randomBytes(16); // 128-bit IV
var cipher = crypto.createCipheriv('aes-256-cbc', key, iv);
var plaintext = 'Sensitive data: credit-card-number=4242-4242-4242-4242';
var encrypted = cipher.update(plaintext, 'utf8', 'hex') + cipher.final('hex');
var decipher = crypto.createDecipheriv('aes-256-cbc', key, iv);
var decrypted = decipher.update(encrypted, 'hex', 'utf8') + decipher.final('utf8');
results.push('aes_mode=aes-256-cbc');
results.push('aes_pt_len=' + plaintext.length);
results.push('aes_ct_len=' + encrypted.length);
results.push('aes_ct_hex=' + (/^[0-9a-f]+$/.test(encrypted) ? 'yes' : 'no'));
results.push('aes_roundtrip=' + (decrypted === plaintext ? 'match' : 'mismatch'));
results.push('aes_ct_diff_pt=' + (encrypted !== plaintext ? 'yes' : 'no'));
} catch(e) {
results.push('aes_mode=err:' + (e.message || e).substring(0, 40));
aesAvailable = false;
}
}
if (!aesAvailable) {
// --- Fallback: XOR cipher + base64 (educational) ---
function xorEncrypt(text, key) {
var out = [];
for (var i = 0; i < text.length; i++) {
out.push(text.charCodeAt(i) ^ key.charCodeAt(i % key.length));
}
// Pack into a string of char codes, then base64
var buf = Buffer.from(out);
return buf.toString('base64');
}
function xorDecrypt(b64, key) {
var buf = Buffer.from(b64, 'base64');
var out = '';
for (var i = 0; i < buf.length; i++) {
out += String.fromCharCode(buf[i] ^ key.charCodeAt(i % key.length));
}
return out;
}
var pt = 'Sensitive data: api-key=sk_live_xyz';
var key = 'secretkey';
var ct = xorEncrypt(pt, key);
var rt = xorDecrypt(ct, key);
results.push('aes_mode=xor-fallback');
results.push('aes_pt_len=' + pt.length);
results.push('aes_ct_len=' + ct.length);
results.push('aes_ct_hex=na');
results.push('aes_roundtrip=' + (rt === pt ? 'match' : 'mismatch'));
results.push('aes_ct_diff_pt=' + (ct !== pt ? 'yes' : 'no'));
}
results.push('SCENARIO_4_PASSED');
} catch(e) {
results.push('SCENARIO_4_ERR:' + (e.message || e));
}
results.join('|')
"#,
);
assert!(
encryption.contains("aes_roundtrip=match"),
"encryption round-trips: {}",
encryption
);
assert!(
encryption.contains("aes_ct_diff_pt=yes"),
"ciphertext != plaintext: {}",
encryption
);
assert!(
encryption.contains("aes_mode="),
"encryption mode reported: {}",
encryption
);
assert!(
encryption.contains("SCENARIO_4_PASSED"),
"scenario 4 complete: {}",
encryption
);
let random = eval_string(
&mut ctx,
r#"
var results = [];
try {
var crypto = require('crypto');
// Length invariants
var r16 = crypto.randomBytes(16);
var r32 = crypto.randomBytes(32);
results.push('len_16=' + r16.length);
results.push('len_32=' + r32.length);
// Hex encoding doubles length (Buffer.from wraps the array)
results.push('hex_16=' + Buffer.from(r16).toString('hex').length);
results.push('hex_32=' + Buffer.from(r32).toString('hex').length);
// base64 encoding produces a non-empty string
results.push('b64_nonempty=' + (Buffer.from(r16).toString('base64').length > 0 ? 'yes' : 'no'));
// Two consecutive randomBytes calls should produce different bytes
// (probability of collision is astronomically small)
var a = Buffer.from(crypto.randomBytes(16)).toString('hex');
var b = Buffer.from(crypto.randomBytes(16)).toString('hex');
results.push('random_different=' + (a !== b ? 'yes' : 'no'));
// Hex chars are all 0-9a-f
results.push('hex_chars_valid=' + (/^[0-9a-f]+$/.test(a) ? 'yes' : 'no'));
// Empty request returns empty buffer
results.push('empty_random=' + (crypto.randomBytes(0).length === 0 ? 'yes' : 'no'));
// Generate a session token (32 random bytes hex-encoded = 64-char token)
function genSessionToken() {
return Buffer.from(crypto.randomBytes(32)).toString('hex');
}
var tokens = [];
for (var i = 0; i < 5; i++) tokens.push(genSessionToken());
var allUnique = tokens.every(function(t, idx) {
return tokens.indexOf(t) === idx;
});
results.push('tokens_unique=' + (allUnique ? 'yes' : 'no'));
results.push('token_len=' + tokens[0].length);
// Generate a nonce (12-byte IV-style)
var nonce = Buffer.from(crypto.randomBytes(12)).toString('base64');
results.push('nonce_nonempty=' + (nonce.length > 0 ? 'yes' : 'no'));
results.push('SCENARIO_5_PASSED');
} catch(e) {
results.push('SCENARIO_5_ERR:' + (e.message || e));
}
results.join('|')
"#,
);
assert!(
random.contains("len_16=16"),
"randomBytes(16) returns 16 bytes: {}",
random
);
assert!(
random.contains("len_32=32"),
"randomBytes(32) returns 32 bytes: {}",
random
);
assert!(
random.contains("hex_16=32"),
"16-byte hex is 32 chars: {}",
random
);
assert!(
random.contains("hex_32=64"),
"32-byte hex is 64 chars: {}",
random
);
assert!(
random.contains("b64_nonempty=yes"),
"base64 encoding works: {}",
random
);
assert!(
random.contains("random_different=yes"),
"consecutive random calls differ: {}",
random
);
assert!(
random.contains("hex_chars_valid=yes"),
"hex encoding is valid: {}",
random
);
assert!(
random.contains("empty_random=yes"),
"randomBytes(0) is empty: {}",
random
);
assert!(
random.contains("tokens_unique=yes"),
"5 session tokens are unique: {}",
random
);
assert!(
random.contains("token_len=64"),
"32-byte session token is 64 hex chars: {}",
random
);
assert!(
random.contains("nonce_nonempty=yes"),
"nonce generation works: {}",
random
);
assert!(
random.contains("SCENARIO_5_PASSED"),
"scenario 5 complete: {}",
random
);
bun_runtime::shutdown_thread_sm();
}