| 
									
										
										
										
											2023-01-03 11:55:46 -08:00
										 |  |  | // Copyright 2019 Signal Messenger, LLC
 | 
					
						
							| 
									
										
										
										
											2020-10-30 15:34:04 -05:00
										 |  |  | // SPDX-License-Identifier: AGPL-3.0-only
 | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | import { createHash } from 'crypto'; | 
					
						
							|  |  |  | import { | 
					
						
							|  |  |  |   createReadStream, | 
					
						
							|  |  |  |   readFile as readFileCallback, | 
					
						
							|  |  |  |   writeFile as writeFileCallback, | 
					
						
							|  |  |  | } from 'fs'; | 
					
						
							| 
									
										
										
										
											2022-03-03 14:34:51 -08:00
										 |  |  | import { pipeline } from 'stream/promises'; | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | import { basename, dirname, join, resolve as resolvePath } from 'path'; | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | import pify from 'pify'; | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | import { sign, verify } from './curve'; | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | 
 | 
					
						
							|  |  |  | const readFile = pify(readFileCallback); | 
					
						
							|  |  |  | const writeFile = pify(writeFileCallback); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export async function generateSignature( | 
					
						
							|  |  |  |   updatePackagePath: string, | 
					
						
							|  |  |  |   version: string, | 
					
						
							|  |  |  |   privateKeyPath: string | 
					
						
							| 
									
										
										
										
											2020-09-16 12:31:05 -07:00
										 |  |  | ): Promise<Buffer> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   const privateKey = await loadHexFromPath(privateKeyPath); | 
					
						
							|  |  |  |   const message = await generateMessage(updatePackagePath, version); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return sign(privateKey, message); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | export async function verifySignature( | 
					
						
							|  |  |  |   updatePackagePath: string, | 
					
						
							|  |  |  |   version: string, | 
					
						
							| 
									
										
										
										
											2022-03-03 14:34:51 -08:00
										 |  |  |   signature: Buffer, | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  |   publicKey: Buffer | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | ): Promise<boolean> { | 
					
						
							|  |  |  |   const message = await generateMessage(updatePackagePath, version); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return verify(publicKey, message, signature); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | // Helper methods
 | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  | async function generateMessage( | 
					
						
							|  |  |  |   updatePackagePath: string, | 
					
						
							|  |  |  |   version: string | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | ): Promise<Buffer> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   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 | 
					
						
							| 
									
										
										
										
											2022-03-03 14:34:51 -08:00
										 |  |  | ): Promise<Buffer> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   const signaturePath = getSignaturePath(updatePackagePath); | 
					
						
							|  |  |  |   const signature = await generateSignature( | 
					
						
							|  |  |  |     updatePackagePath, | 
					
						
							|  |  |  |     version, | 
					
						
							|  |  |  |     privateKeyPath | 
					
						
							|  |  |  |   ); | 
					
						
							|  |  |  |   await writeHexToPath(signaturePath, signature); | 
					
						
							| 
									
										
										
										
											2022-03-03 14:34:51 -08:00
										 |  |  | 
 | 
					
						
							|  |  |  |   return signature; | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | export async function _getFileHash(updatePackagePath: string): Promise<Buffer> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   const hash = createHash('sha256'); | 
					
						
							| 
									
										
										
										
											2022-03-03 14:34:51 -08:00
										 |  |  |   await pipeline(createReadStream(updatePackagePath), hash); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return hash.digest(); | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 12:31:05 -07:00
										 |  |  | export function getSignatureFileName(fileName: string): string { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   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 15:26:45 -07:00
										 |  |  | export function hexToBinary(target: string): Buffer { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   return Buffer.from(target, 'hex'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | export function binaryToHex(data: Buffer): string { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   return Buffer.from(data).toString('hex'); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2019-08-19 15:26:45 -07:00
										 |  |  | export async function loadHexFromPath(target: string): Promise<Buffer> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   const hexString = await readFile(target, 'utf8'); | 
					
						
							|  |  |  | 
 | 
					
						
							|  |  |  |   return hexToBinary(hexString); | 
					
						
							|  |  |  | } | 
					
						
							|  |  |  | 
 | 
					
						
							| 
									
										
										
										
											2020-09-16 12:31:05 -07:00
										 |  |  | export async function writeHexToPath( | 
					
						
							|  |  |  |   target: string, | 
					
						
							|  |  |  |   data: Buffer | 
					
						
							|  |  |  | ): Promise<void> { | 
					
						
							| 
									
										
										
										
											2019-03-28 10:09:26 -07:00
										 |  |  |   await writeFile(target, binaryToHex(data)); | 
					
						
							|  |  |  | } |