signal-desktop/ts/test/updater/signature_test.ts
2020-11-04 13:03:13 -06:00

209 lines
5.9 KiB
TypeScript

// Copyright 2019-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { existsSync } from 'fs';
import { join } from 'path';
import { assert } from 'chai';
import { copy } from 'fs-extra';
import {
_getFileHash,
getSignaturePath,
loadHexFromPath,
verifySignature,
writeHexToPath,
writeSignature,
} from '../../updater/signature';
import { createTempDir, deleteTempDir } from '../../updater/common';
import { keyPair } from '../../updater/curve';
describe('updater/signatures', () => {
it('_getFileHash returns correct hash', async () => {
const filePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const expected =
'7bc77f27d92d00b4a1d57c480ca86dacc43d57bc318339c92119d1fbf6b557a5';
const hash = await _getFileHash(filePath);
assert.strictEqual(expected, Buffer.from(hash).toString('hex'));
});
it('roundtrips binary file writes', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const path = join(tempDir, 'something.bin');
const { publicKey } = keyPair();
await writeHexToPath(path, publicKey);
const fromDisk = await loadHexFromPath(path);
assert.strictEqual(
Buffer.from(fromDisk).compare(Buffer.from(publicKey)),
0
);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('roundtrips signature', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const signaturePath = getSignaturePath(updatePath);
assert.strictEqual(existsSync(signaturePath), true);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, true);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if version changes', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const brokenVersion = 'v1.23.3';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const verified = await verifySignature(
updatePath,
brokenVersion,
publicKey
);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if signature tampered with', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const signaturePath = getSignaturePath(updatePath);
const signature = Buffer.from(await loadHexFromPath(signaturePath));
signature[4] += 3;
await writeHexToPath(signaturePath, signature);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if binary file tampered with', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey, privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const brokenSourcePath = join(
__dirname,
'../../../fixtures/pixabay-Soap-Bubble-7141.mp4'
);
await copy(brokenSourcePath, updatePath);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
it('fails signature verification if signed by different key', async () => {
let tempDir;
try {
tempDir = await createTempDir();
const version = 'v1.23.2';
const sourcePath = join(__dirname, '../../../fixtures/ghost-kitty.mp4');
const updatePath = join(tempDir, 'ghost-kitty.mp4');
await copy(sourcePath, updatePath);
const privateKeyPath = join(tempDir, 'private.key');
const { publicKey } = keyPair();
const { privateKey } = keyPair();
await writeHexToPath(privateKeyPath, privateKey);
await writeSignature(updatePath, version, privateKeyPath);
const verified = await verifySignature(updatePath, version, publicKey);
assert.strictEqual(verified, false);
} finally {
if (tempDir) {
await deleteTempDir(tempDir);
}
}
});
});