You cannot select more than 25 topics
			Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
		
		
		
		
		
			
		
			
				
	
	
		
			113 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
			
		
		
	
	
			113 lines
		
	
	
		
			2.9 KiB
		
	
	
	
		
			TypeScript
		
	
| 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';
 | |
| 
 | |
| import { BinaryType, sign, verify } from './curve';
 | |
| 
 | |
| const readFile = pify(readFileCallback);
 | |
| const writeFile = pify(writeFileCallback);
 | |
| 
 | |
| export async function generateSignature(
 | |
|   updatePackagePath: string,
 | |
|   version: string,
 | |
|   privateKeyPath: string
 | |
| ) {
 | |
|   const privateKey = await loadHexFromPath(privateKeyPath);
 | |
|   const message = await generateMessage(updatePackagePath, version);
 | |
| 
 | |
|   return sign(privateKey, message);
 | |
| }
 | |
| 
 | |
| export async function verifySignature(
 | |
|   updatePackagePath: string,
 | |
|   version: string,
 | |
|   publicKey: BinaryType
 | |
| ): 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
 | |
| ): Promise<BinaryType> {
 | |
|   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
 | |
| ) {
 | |
|   const signaturePath = getSignaturePath(updatePackagePath);
 | |
|   const signature = await generateSignature(
 | |
|     updatePackagePath,
 | |
|     version,
 | |
|     privateKeyPath
 | |
|   );
 | |
|   await writeHexToPath(signaturePath, signature);
 | |
| }
 | |
| 
 | |
| export async function _getFileHash(
 | |
|   updatePackagePath: string
 | |
| ): Promise<BinaryType> {
 | |
|   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);
 | |
|     });
 | |
|   });
 | |
| }
 | |
| 
 | |
| export function getSignatureFileName(fileName: 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));
 | |
| }
 | |
| 
 | |
| export function hexToBinary(target: string): BinaryType {
 | |
|   return Buffer.from(target, 'hex');
 | |
| }
 | |
| 
 | |
| export function binaryToHex(data: BinaryType): string {
 | |
|   return Buffer.from(data).toString('hex');
 | |
| }
 | |
| 
 | |
| export async function loadHexFromPath(target: string): Promise<BinaryType> {
 | |
|   const hexString = await readFile(target, 'utf8');
 | |
| 
 | |
|   return hexToBinary(hexString);
 | |
| }
 | |
| 
 | |
| export async function writeHexToPath(target: string, data: BinaryType) {
 | |
|   await writeFile(target, binaryToHex(data));
 | |
| }
 |