signal-desktop/ts/updater/signature.ts

117 lines
3 KiB
TypeScript
Raw Normal View History

2020-10-30 20:34:04 +00:00
// Copyright 2019-2020 Signal Messenger, LLC
// SPDX-License-Identifier: AGPL-3.0-only
import { createHash } from 'crypto';
import {
createReadStream,
readFile as readFileCallback,
writeFile as writeFileCallback,
} from 'fs';
import { basename, dirname, join, resolve as resolvePath } from 'path';
import pify from 'pify';
2019-08-19 22:26:45 +00:00
import { sign, verify } from './curve';
const readFile = pify(readFileCallback);
const writeFile = pify(writeFileCallback);
export async function generateSignature(
updatePackagePath: string,
version: string,
privateKeyPath: string
2020-09-16 19:31:05 +00:00
): Promise<Buffer> {
const privateKey = await loadHexFromPath(privateKeyPath);
const message = await generateMessage(updatePackagePath, version);
return sign(privateKey, message);
}
export async function verifySignature(
updatePackagePath: string,
version: string,
2019-08-19 22:26:45 +00:00
publicKey: Buffer
): Promise<boolean> {
const signaturePath = getSignaturePath(updatePackagePath);
const signature = await loadHexFromPath(signaturePath);
const message = await generateMessage(updatePackagePath, version);
return verify(publicKey, message, signature);
}
// Helper methods
async function generateMessage(
updatePackagePath: string,
version: string
2019-08-19 22:26:45 +00:00
): Promise<Buffer> {
const hash = await _getFileHash(updatePackagePath);
const messageString = `${Buffer.from(hash).toString('hex')}-${version}`;
return Buffer.from(messageString);
}
export async function writeSignature(
updatePackagePath: string,
version: string,
privateKeyPath: string
2020-09-16 19:31:05 +00:00
): Promise<void> {
const signaturePath = getSignaturePath(updatePackagePath);
const signature = await generateSignature(
updatePackagePath,
version,
privateKeyPath
);
await writeHexToPath(signaturePath, signature);
}
2019-08-19 22:26:45 +00:00
export async function _getFileHash(updatePackagePath: string): Promise<Buffer> {
const hash = createHash('sha256');
const stream = createReadStream(updatePackagePath);
return new Promise((resolve, reject) => {
stream.on('data', data => {
hash.update(data);
});
stream.on('close', () => {
resolve(hash.digest());
});
stream.on('error', error => {
reject(error);
});
});
}
2020-09-16 19:31:05 +00:00
export function getSignatureFileName(fileName: string): string {
return `${fileName}.sig`;
}
export function getSignaturePath(updatePackagePath: string): string {
const updateFullPath = resolvePath(updatePackagePath);
const updateDir = dirname(updateFullPath);
const updateFileName = basename(updateFullPath);
return join(updateDir, getSignatureFileName(updateFileName));
}
2019-08-19 22:26:45 +00:00
export function hexToBinary(target: string): Buffer {
return Buffer.from(target, 'hex');
}
2019-08-19 22:26:45 +00:00
export function binaryToHex(data: Buffer): string {
return Buffer.from(data).toString('hex');
}
2019-08-19 22:26:45 +00:00
export async function loadHexFromPath(target: string): Promise<Buffer> {
const hexString = await readFile(target, 'utf8');
return hexToBinary(hexString);
}
2020-09-16 19:31:05 +00:00
export async function writeHexToPath(
target: string,
data: Buffer
): Promise<void> {
await writeFile(target, binaryToHex(data));
}