Files
EdgeSSH/tests/encryption.test.ts
T

51 lines
2.5 KiB
TypeScript

import assert from 'node:assert/strict';
import { test } from 'node:test';
import {
encryptHost,
decryptHost,
base64,
HostEncryptionKeyError,
HostPayloadDecryptionError,
} from '../src/accounts/crypto.ts';
import { apiFailure } from '../src/accounts/http.ts';
const key = base64(crypto.getRandomValues(new Uint8Array(32)));
test('host credentials round trip, ciphertext never contains plaintext', async () => {
const input = { password: 'private-password-秘密', privateKey: 'private-key', host: 'server.example' };
const encrypted = await encryptHost(input, key, 'owner-a', 'host-a');
assert.deepEqual(await decryptHost(encrypted, key, 'owner-a', 'host-a'), input);
assert.equal(encrypted.includes(input.password), false);
assert.equal(encrypted.includes(input.host), false);
assert.notEqual(await encryptHost(input, key, 'owner-a', 'host-a'), encrypted);
});
test('ciphertext is bound to both Access identity and host id', async () => {
const encrypted = await encryptHost({ password: 'secret' }, key, 'owner-a', 'host-a');
await assert.rejects(decryptHost(encrypted, key, 'owner-b', 'host-a'));
await assert.rejects(decryptHost(encrypted, key, 'owner-a', 'host-b'));
});
test('wrong keys, malformed secrets and tampered ciphertext fail closed', async () => {
const encrypted = await encryptHost({ password: 'secret' }, key, 'owner', 'host');
await assert.rejects(
decryptHost(encrypted, base64(crypto.getRandomValues(new Uint8Array(32))), 'owner', 'host'),
HostPayloadDecryptionError,
);
await assert.rejects(encryptHost({}, base64(new Uint8Array(16)), 'owner', 'host'), HostEncryptionKeyError);
await assert.rejects(encryptHost({}, `${key.slice(0, -1)}A`, 'owner', 'host'), HostEncryptionKeyError);
const parts = encrypted.split('.');
parts[2] = (parts[2][0] === 'A' ? 'B' : 'A') + parts[2].slice(1);
await assert.rejects(decryptHost(parts.join('.'), key, 'owner', 'host'), HostPayloadDecryptionError);
});
test('host storage failures return actionable errors without exposing internals', async () => {
const invalidKey = apiFailure(new HostEncryptionKeyError());
assert.equal(invalidKey.status, 503);
assert.deepEqual(await invalidKey.json(), { error: '主机存储尚未正确配置,请检查 ENCRYPTION_KEY。' });
const unreadablePayload = apiFailure(new HostPayloadDecryptionError());
assert.equal(unreadablePayload.status, 503);
assert.deepEqual(await unreadablePayload.json(), { error: '主机资料无法解密,请确认 ENCRYPTION_KEY 未被更换。' });
});