import sodium from 'libsodium-wrappers';
import { BecknClient } from './lib/becknClient.js';
async function verifyCrypto() {
await sodium.ready;
console.log('๐งช Starting Crypto Verification (The "Trap" Check)...');
// 1. Generate Test Keys
const keyPair = sodium.crypto_sign_keypair();
const privateKey = sodium.to_base64(keyPair.privateKey, sodium.base64_variants.ORIGINAL);
const publicKey = sodium.to_base64(keyPair.publicKey, sodium.base64_variants.ORIGINAL);
console.log('๐ Generated Ephemeral Keys');
// 2. Initialize Client
const client = new BecknClient({
bppUri: 'http://localhost:3001',
bapId: 'test-bap',
bapUri: 'http://localhost:3000',
privateKey: privateKey,
publicKey: publicKey,
uniqueKeyId: 'key1'
});
// 3. Create a payload
const body = {
context: { action: 'search', message_id: '123' },
message: { intent: {} }
};
// 4. Sign it (Accessing private method via any cast or we can expose a helper,
// but better to just use the logic we know is inside)
// Actually, let's use the client to generate the header.
// We need to expose createAuthorizationHeader for testing or just inspect the logic.
// Since it's private, we'll use a trick or just replicate the verification logic
// by intercepting the axios call?
// Better: We'll trust the client.sendRequest to generate it, and we'll verify the OUTPUT.
// We will mock axios to capture the header
// Since we imported axios at the top (we need to add import if missing or use the one we might have)
// Actually, let's just import it at top level.
const axios = (await import('axios')).default;
let capturedHeader = '';
axios.post = async (url: string, data: any, config: any) => {
capturedHeader = config.headers['Authorization'];
return { data: { ack: { status: 'ACK' } } };
};
await client.sendRequest('search', body);
console.log('๐ Captured Authorization Header:', capturedHeader);
// 5. Verify the Signature
// Header format: Signature keyId="...",algorithm="ed25519",created="...",expires="...",headers="(created) (expires) digest",signature="..."
const signaturePart = capturedHeader.match(/signature="([^"]+)"/)?.[1];
const createdPart = capturedHeader.match(/created="([^"]+)"/)?.[1];
const expiresPart = capturedHeader.match(/expires="([^"]+)"/)?.[1];
if (!signaturePart || !createdPart || !expiresPart) {
throw new Error('โ Malformed Authorization Header');
}
// Reconstruct Signing String
const digest = sodium.crypto_generichash(64, JSON.stringify(body));
const digestBase64 = sodium.to_base64(digest, sodium.base64_variants.ORIGINAL);
const signingString = `(created): ${createdPart}\n(expires): ${expiresPart}\ndigest: BLAKE-512=${digestBase64}`;
// Verify
const signatureBytes = sodium.from_base64(signaturePart, sodium.base64_variants.ORIGINAL);
const publicKeyBytes = sodium.from_base64(publicKey, sodium.base64_variants.ORIGINAL);
const isValid = sodium.crypto_sign_verify_detached(signatureBytes, signingString, publicKeyBytes);
if (isValid) {
console.log('โ
SUCCESS: Ed25519 Signature Verified!');
} else {
console.error('โ FAILURE: Signature Verification Failed!');
process.exit(1);
}
}
verifyCrypto().catch(console.error);