247 lines
6.2 KiB
TypeScript
247 lines
6.2 KiB
TypeScript
// Copyright 2019 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';
|
|
import * as log from '../../logging/log';
|
|
|
|
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(log, 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);
|
|
|
|
const signature = await writeSignature(
|
|
updatePath,
|
|
version,
|
|
privateKeyPath
|
|
);
|
|
|
|
const signaturePath = getSignaturePath(updatePath);
|
|
assert.strictEqual(existsSync(signaturePath), true);
|
|
|
|
const verified = await verifySignature(
|
|
updatePath,
|
|
version,
|
|
signature,
|
|
publicKey
|
|
);
|
|
assert.strictEqual(verified, true);
|
|
} finally {
|
|
if (tempDir) {
|
|
await deleteTempDir(log, 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);
|
|
|
|
const signature = await writeSignature(
|
|
updatePath,
|
|
version,
|
|
privateKeyPath
|
|
);
|
|
|
|
const verified = await verifySignature(
|
|
updatePath,
|
|
brokenVersion,
|
|
signature,
|
|
publicKey
|
|
);
|
|
assert.strictEqual(verified, false);
|
|
} finally {
|
|
if (tempDir) {
|
|
await deleteTempDir(log, 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);
|
|
|
|
const signature = await writeSignature(
|
|
updatePath,
|
|
version,
|
|
privateKeyPath
|
|
);
|
|
signature[4] += 3;
|
|
|
|
const verified = await verifySignature(
|
|
updatePath,
|
|
version,
|
|
signature,
|
|
publicKey
|
|
);
|
|
assert.strictEqual(verified, false);
|
|
} finally {
|
|
if (tempDir) {
|
|
await deleteTempDir(log, 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);
|
|
|
|
const signature = 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,
|
|
signature,
|
|
publicKey
|
|
);
|
|
assert.strictEqual(verified, false);
|
|
} finally {
|
|
if (tempDir) {
|
|
await deleteTempDir(log, 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);
|
|
|
|
const signature = await writeSignature(
|
|
updatePath,
|
|
version,
|
|
privateKeyPath
|
|
);
|
|
|
|
const verified = await verifySignature(
|
|
updatePath,
|
|
version,
|
|
signature,
|
|
publicKey
|
|
);
|
|
assert.strictEqual(verified, false);
|
|
} finally {
|
|
if (tempDir) {
|
|
await deleteTempDir(log, tempDir);
|
|
}
|
|
}
|
|
});
|
|
});
|