Skip to content

Commit

Permalink
✍️ Signing binaries and validating them
Browse files Browse the repository at this point in the history
  • Loading branch information
Ashu11-A committed Jun 12, 2024
1 parent 5986ca7 commit bb795a0
Show file tree
Hide file tree
Showing 6 changed files with 177 additions and 24 deletions.
6 changes: 6 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,13 @@
<div align="left">


## Generate Keys

```sh
openssl genpkey -algorithm RSA -out private_key.pem -pkeyopt rsa_keygen_bits:4096
openssl rsa -pubout -in private_key.pem -out public_key.pem

```

## 📃 | License

Expand Down
14 changes: 14 additions & 0 deletions core/public_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0jWw8pMioj0qA+B71kHp
yxcln9ELZ+GWfIQN/EofTZ9OCm3Ka3O+wu2iS38UzF9OEi08eCHxwxEB/Hfhijwc
pGoAvF+NOTbb3Q6uqgOj9DRRftV4QrTDb/IuFNSolTc/6PU3ZtTAb1mDDPcv/pYy
WWZleom6s2swuRmsjGkVVb0ylatxtt9JxssEaNVmdWwtptSl2tg4iFRBe7aKREu0
ohouohev6V4NNkl7gvZ83c+QqC3VN9F9Gxq7yf3gdhL9u06WOJrYQxyDTt/OQ1v5
THkWg04cLMHHrnYS9wGUS6gexoKm1x6O5SvCc62VFKlOfiHxp6l5FLTZIkAy4Vt9
sMF44nsTlCUCxp6jbGAK6UWTcFhqI7qnguw+YKwOxNcmq+S5v1AYUNN9+viFXlRO
asIMIysKv5GqpgVjOxaMpsyY8ythUagaUpxZVZ1gL67NiKzHOlgfyBkR1Lx2Ij3m
yFgcDX0dYcIoqKncQqcxsdejv1XVOsvnHx8XbSEedN5qXmSo4BY13/ImPnolCGDK
WrThg8jnC7+FNyNvHIRjUpM/39HIUaVA97yIHa27yFpum+hSqiclPL7wTHTOrCoy
4G/tWqF7qA+dXiCDsSiCk77koyw8EtggLAHrBHGP3C6KyUJsElNuDCk77ph8hNt9
Aspz8pOgA779IGKDRGi0h4kCAwEAAQ==
-----END PUBLIC KEY-----
28 changes: 27 additions & 1 deletion core/src/controller/plugins.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { Discord } from '@/discord/Client.js'
import { Command, CommandData } from '@/discord/Commands.js'
import { ChildProcessWithoutNullStreams, spawn } from 'child_process'
import { existsSync } from 'fs'
import { mkdir, writeFile } from 'fs/promises'
import { mkdir, readFile, writeFile } from 'fs/promises'
import { watch } from 'chokidar'
import { glob } from 'glob'
import { isBinaryFile } from 'isbinaryfile'
Expand All @@ -14,6 +14,7 @@ import { PKG_MODE, RootPATH } from '@/index.js'
import { Config, ConfigOptions } from './config.js'
import { Database, EntityImport } from './database.js'
import { fileURLToPath } from 'url'
import { createVerify } from 'crypto'

const __dirname = dirname(fileURLToPath(import.meta.url))
interface Metadata {
Expand Down Expand Up @@ -65,6 +66,7 @@ export class Plugins {
const valid = []
for (const filePath of plugins) {
if (!(await isBinaryFile(filePath))) continue
if (!(await this.validate(filePath))) continue
valid.push(filePath)
}
Plugins.plugins = valid.length
Expand Down Expand Up @@ -114,6 +116,28 @@ export class Plugins {
})
}

async validate (filePath: string): Promise<boolean> {
const binary = await readFile(filePath)
const publicKey = await readFile(join(__dirname, '../../public_key.pem'), 'utf8')

const data = binary.subarray(0, binary.length - 512)
const signature = binary.subarray(binary.length - 512)


const verifier = createVerify('sha512')
verifier.update(data)
verifier.end()

const isValid = verifier.verify(publicKey, signature)

if (isValid) {
console.log('Plugin Valido!')
} else {
console.log(`Falha na verificação da assinatura. ${filePath} não é um arquivo valido!`)
}
return isValid
}

async load(): Promise<void> {
const plugins = await this.list()
if (plugins.length === 0) {
Expand All @@ -135,6 +159,8 @@ export class Plugins {
return
}

if (!(await this.validate(filePath))) return

console.log('\n✨ Novo plugin adicionado!')
this.start(filePath)
})
Expand Down
52 changes: 52 additions & 0 deletions private_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
-----BEGIN PRIVATE KEY-----
MIIJQgIBADANBgkqhkiG9w0BAQEFAASCCSwwggkoAgEAAoICAQDSNbDykyKiPSoD
4HvWQenLFyWf0Qtn4ZZ8hA38Sh9Nn04Kbcprc77C7aJLfxTMX04SLTx4IfHDEQH8
d+GKPBykagC8X405NtvdDq6qA6P0NFF+1XhCtMNv8i4U1KiVNz/o9Tdm1MBvWYMM
9y/+ljJZZmV6ibqzazC5GayMaRVVvTKVq3G230nGywRo1WZ1bC2m1KXa2DiIVEF7
topES7SiGi6iF6/pXg02SXuC9nzdz5CoLdU30X0bGrvJ/eB2Ev27TpY4mthDHINO
385DW/lMeRaDThwswceudhL3AZRLqB7GgqbXHo7lK8JzrZUUqU5+IfGnqXkUtNki
QDLhW32wwXjiexOUJQLGnqNsYArpRZNwWGojuqeC7D5grA7E1yar5Lm/UBhQ0336
+IVeVE5qwgwjKwq/kaqmBWM7FoymzJjzK2FRqBpSnFlVnWAvrs2IrMc6WB/IGRHU
vHYiPebIWBwNfR1hwiioqdxCpzGx16O/VdU6y+cfHxdtIR503mpeZKjgFjXf8iY+
eiUIYMpatOGDyOcLv4U3I28chGNSkz/f0chRpUD3vIgdrbvIWm6b6FKqJyU8vvBM
dM6sKjLgb+1aoXuoD51eIIOxKIKTvuSjLDwS2CAsAesEcY/cLorJQmwSU24MKTvu
mHyE230CynPyk6ADvv0gYoNEaLSHiQIDAQABAoICAAdp6hqWDp7E9DhEiK2Q781Q
PM0XBvVUFxX/ZMxg81tuGscioeTaBVBz59lMizfdhR/+PS+Z9+A6t3SXaEqTLent
DKdYId+SlBzG5alcnUHRvg2QexA0Np/aPV6R9PulQhFl450yc/KBaE4y4hwTIUW2
py3UEXP+O+7NTWD63EwksvXlIOqzk5MpqhdF7noF9ZK4lXxjS5QgT5pswNHhjyZ3
8KzaE8PR47qmSw8+gP8kMMyDqE1Si7xi3Bxmwi274/vBDOqEaIvuY6GqokhNJDza
mO0K2lLIR1H7e1X/leK4tFQpO7fizaSev1oL1VIQGhL4AME6aIkuyykqdTjuPH+o
3ETMwV6BYwklznQq/EPyeKzCVdt0uQa72frOsMQFoI/TOa5M+lMX3eRMDfURg7Tt
P3+HDeivuqr3Ljlse8ZP+l0b3InmJXqpig35LSH6SdsgaB059OBVosf7RovCDxVH
w6xcga9phwY4FxR9MzV+B4nNvYppRaQKL9YY6cfP8Df1WJjYiVs4+QKsKFFScq4P
JmaGPJuU/HlOCNTI8pzEl8oWkhc6g3JWt07HiQK0NmrXY32XCHxXTQyyCiMZzuVg
8lJnjGu2hwGwzPkT0SSdyW8j0p2WJNpqgRSjDtfyoRW3zXjR5opbOkBSNc9LRnU3
ZyF6I9EPze2FGu09MytxAoIBAQDXHm7ww723zRz75b3ie2T2otRc2dXqbk/x5jCE
aFNxs33j69EP/L0yoi9wlRK7I6f+j4VukKpOVxnKthW2h48fMPDTjpCETv7CDJ7P
tP3NeGYlBaugZQf7z6rXCm8OsF/OddXSj9vhoLkOaUBckD6JItL4yWAqPreHWA+W
B85eEI2THDJv97WFYrO2DtzgnEY+ZCkP6RcvIXspSZP6+cq+aB5VAsC+xtxNxMrU
o5jWIi8uT0LzWxUsf1lPs7F3jR2cHam6ESsXMiHBYjbkHonXreNwW2at7GV8P/Fb
+Xo6L9zDVyxa/MFO4RwfYfwi3z37hcAiz/1Nv8Q6M+Bsva9xAoIBAQD6KG1Ou9oN
n0PlREgIlNOnL35XHmn24SN8Sy8uRgwfCn4I1Wdi/vrRhh7ahWNDBffq/mDluhZp
H9SgG70dw/Zol9UhInvzq4/FHwMBi+OZDfux4tpQqLvuj84LhJa44v+uk/FJxiuL
nYw33BuxZ5mvYnEy7zv+UFUdkaytz3NSSVoiveb3B/2rokj/3lnSqm5cU/CWudUZ
R06Md/vi9Noynpzweoz4gspMnrD+ovNyuu0WWPcH6/MDk8wGLmpVK1cpZ2cLLphj
bU1dzCOqOIviQH1j01YeqZ0khXF2FhWXQroA/e4xxkiRDLgzSOm08n+dXrYZq4W6
0yLdbkRyNP2ZAoIBAQDTapSLyUQ2VL6sjiDtampSvrrJOkmzaZDmY13Bc6q2rzoF
e2hvNuxWy/Cly8yp4SQk2bbAcCsAbTfq8sMqLdYqSHYW5SEA94YG/lJCPlXFzxNM
RWNkcobDI1h6mUWGgS7XgRWRKFNoTwYwtJ5NmnHrYy9G1NrPCe5jdNd0kt1tswUA
1JB5XFCo89U9EyRfFxcuEFAO0/02E8iwTiDCYyavtTJCSD9X/6baQ5M4IkR+BwCp
mbpp41VO8yW6YBcFMMot+TUqqd7YoCtunHDoB19fegVyElI6kn82+rzhsLFAaHOm
A5GTJp9xP2ndKfab6x1Gii/HxvPiqppWvU9Ob6nxAoIBADAMi9JPqrZrHPNwhhV/
4JeAO9gn81qEK/i3yDwvICzmKwcvVkhhiXOtO0flzwcUomX0rk/xylQu1Pxb15tc
BHxajYlrwjsvfNHz/vPRFspTMUEdvak4f8B2xrDXqsugblPt/q6Y7cxHWqNwF7Gf
oJlbYzrRUMisTWz8ihpoelIX3MRHjZFta4AID9V1s/MO4VvCk7efwhq045V/hlY1
iaOna6WVJHanSIwm/wAp9HDZDqj4NZrD602ImeumBq6NQJSnU9eF/6c5W17k0wTj
D5tJn8wIz8Xb9l8Jcp2n6EXBf1CpdZMB7vZNZyQSoTEsUu67Pmb31xvnag72GxeE
riECggEAUsYmIyyviVad6a0hNl1crw6tzG6KQ+UjffO4NQQ/2WQoo5uEaO2i5b/D
vXqBFMYcgDnWi3ZhPq8Z9m+ppwOna6wb8+ZmTYnxH1ooZVZ/S1QEw1GV2AFLljP6
D94F0yTwQ3Yu+6iBba4z4x2hgzPL1YqTuAe0vVQbjuD9ubBu/O0beCog9frlUNG+
bUGo8OBoh1bzfo6E6HxOo+2VME5RwZHKiAagMVPe5TpmoLg3p+PO/iYm4skNRDFK
hXAn9KCVahKWlIG5Tgsnbdv9Prvh9hw1afDL1Fr+Q7LV32/136klAU2KOB3HeA67
mKRktz06mNA6/LbR+AfSEYrbmgIJEg==
-----END PRIVATE KEY-----
14 changes: 14 additions & 0 deletions public_key.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
-----BEGIN PUBLIC KEY-----
MIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA0jWw8pMioj0qA+B71kHp
yxcln9ELZ+GWfIQN/EofTZ9OCm3Ka3O+wu2iS38UzF9OEi08eCHxwxEB/Hfhijwc
pGoAvF+NOTbb3Q6uqgOj9DRRftV4QrTDb/IuFNSolTc/6PU3ZtTAb1mDDPcv/pYy
WWZleom6s2swuRmsjGkVVb0ylatxtt9JxssEaNVmdWwtptSl2tg4iFRBe7aKREu0
ohouohev6V4NNkl7gvZ83c+QqC3VN9F9Gxq7yf3gdhL9u06WOJrYQxyDTt/OQ1v5
THkWg04cLMHHrnYS9wGUS6gexoKm1x6O5SvCc62VFKlOfiHxp6l5FLTZIkAy4Vt9
sMF44nsTlCUCxp6jbGAK6UWTcFhqI7qnguw+YKwOxNcmq+S5v1AYUNN9+viFXlRO
asIMIysKv5GqpgVjOxaMpsyY8ythUagaUpxZVZ1gL67NiKzHOlgfyBkR1Lx2Ij3m
yFgcDX0dYcIoqKncQqcxsdejv1XVOsvnHx8XbSEedN5qXmSo4BY13/ImPnolCGDK
WrThg8jnC7+FNyNvHIRjUpM/39HIUaVA97yIHa27yFpum+hSqiclPL7wTHTOrCoy
4G/tWqF7qA+dXiCDsSiCk77koyw8EtggLAHrBHGP3C6KyUJsElNuDCk77ph8hNt9
Aspz8pOgA779IGKDRGi0h4kCAwEAAQ==
-----END PUBLIC KEY-----
87 changes: 64 additions & 23 deletions release.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import babel from "@babel/core"
import { formatBytes } from 'bytes-formatter'
import { exec } from 'child_process'
import { Presets, SingleBar } from 'cli-progress'
import { createHash } from 'crypto'
import { createHash, createSign, createVerify, sign } from 'crypto'
import { build } from 'esbuild'
import { createWriteStream, existsSync } from 'fs'
import { exists } from 'fs-extra'
Expand All @@ -15,14 +15,16 @@ import { cwd } from 'process'
import { Readable } from 'stream'
import { minify } from 'terser'

const SIGNATURE_LENGTH = 512

interface BuildInfo {
path: string
platform: string
arch: string
size: string
timeBuild: string
hashMD5: string
hashSHA: string
size?: string
hashMD5?: string
hashSHA?: string
}

type BuildManifest = Record<string, BuildInfo>
Expand Down Expand Up @@ -92,6 +94,8 @@ interface Asset {

class Build {
public readonly options: BuildConstructor
private outputPath?: string
private manifests: ({ [key: string]: BuildInfo })[] = []

private readonly progressBar = new SingleBar({}, Presets.rect)
private readonly startTime = Date.now()
Expand All @@ -110,6 +114,9 @@ class Build {
await this.convertToCJS(`${this.options.outBuild}/node_modules`, `${this.options.outBuild}/node_modules`)
await this.compress({ directory: `${this.options.outBuild}/node_modules`, outBuild: `${this.options.outBuild}/node_modules` })
await this.pkgbuild()
await this.sign()
await this.singCheck()
await this.saveManifest()
}

async build() {
Expand Down Expand Up @@ -161,10 +168,6 @@ class Build {
this.progressBar.stop()
}

async sign(): Promise<void> {

}

async compress(options?: { directory: string, outBuild: string }): Promise<void> {
const { directory, outBuild } = options ?? { directory: `${this.options.outBuild}/src`, outBuild: `${this.options.outBuild}/src` }
const paths = await glob([`${directory}/**/*.js`])
Expand Down Expand Up @@ -253,7 +256,8 @@ class Build {
"src/**/*.js",
"src/**/*.json",
"package.json",
"LICENSE.md"
"LICENSE.md",
"public_key.pem"
],
assets: [
"node_modules/**/*.js",
Expand All @@ -267,6 +271,7 @@ class Build {

for (const name of remove) delete packageJson?.[name]
await cp('LICENSE.md', `${this.options.outBuild}/LICENSE.md`)
await cp('public_key.pem', `${this.options.outBuild}/public_key.pem`)
await writeFile(path.join(process.cwd(), `${this.options.outBuild}/package.json`), JSON.stringify(packageJson, null, 2))
}

Expand Down Expand Up @@ -321,7 +326,6 @@ class Build {
async pkgbuild(): Promise<void> {
const args = ['.', '--compress', 'Brotli', '--no-bytecode', '--public-packages', '"*"', '--public']
const builds: string[] = []
const manifests: BuildManifest[] = []

if (os.platform() !== 'win32') {
for (const platform of this.options.platforms) {
Expand All @@ -348,39 +352,76 @@ class Build {

console.debug(`\n\nIniciando Build ${nameSplit[2]}...`)
newArg.push(...args, '-t', build, '--output', `${this.options.outRelease}/${buildName}`)
this.outputPath = `${this.options.outRelease}/${buildName}`

await execProcess(`cd ${this.options.outBuild} && PKG_CACHE_PATH=${join(process.cwd(), 'pkg-cache')} PKG_IGNORE_TAG=true npx pkg ${newArg.join(" ")}`)

const timeSpent = (Date.now() - this.startTime) / 1000 + 's'
console.info(`Build | ${nameSplit[1]}-${nameSplit[2]} | ${timeSpent}`)

const file = await readFile(`${this.options.outRelease}/${buildName}`)
const hashMD5 = createHash('md5').update(file).digest('hex')
const hashSHA = createHash('sha256').update(file).digest('hex')

manifests.push({
this.manifests.push({
[manifestName]: {
path: buildName.replace('./release/', ''),
platform: nameSplit[1],
arch: nameSplit[2],
size: formatBytes(file.byteLength),
timeBuild: timeSpent,
hashMD5,
hashSHA
}
})
}
}

async sign(): Promise<void> {
if (this.outputPath === undefined) throw new Error('Não foi definido o path de output, tente buildar com PKG antes!')
const binary = await readFile(this.outputPath)
const privateKey = await readFile('private_key.pem', { encoding: 'utf8' })

const signer = createSign(`sha${SIGNATURE_LENGTH}`)
signer.update(binary)
signer.end()

const signature = signer.sign(privateKey)
const output = Buffer.concat([binary, signature])
await writeFile(this.outputPath, output)
}

async singCheck(): Promise<void> {
if (this.outputPath === undefined) throw new Error('Não foi definido o path de output, tente buildar com PKG antes!')
const binary = await readFile(this.outputPath)
const publicKey = await readFile('public_key.pem', 'utf8')

console.log(...manifests)
const data = binary.subarray(0, binary.length - SIGNATURE_LENGTH)
const signature = binary.subarray(binary.length - SIGNATURE_LENGTH)

for (const manifest of manifests) {

const verifier = createVerify(`sha${SIGNATURE_LENGTH}`)
verifier.update(data)
verifier.end()

const isValid = verifier.verify(publicKey, signature)

if (isValid) {
console.log('Assinatura verificada com sucesso!')
} else {
throw new Error('Falha na verificação da assinatura. O arquivo pode ter sido alterado.')
}
}

async saveManifest() {
if (this.outputPath === undefined) throw new Error('Não foi definido o path de output, tente buildar com PKG antes!')

for (const manifest of this.manifests) {
for (const [key, value] of Object.entries(manifest)) {
const file = await readFile(`release/${value.path}`)
const hashMD5 = createHash('md5').update(file).digest('hex')
const hashSHA = createHash('sha512').update(file).digest('hex')

const values = [value]
if (existsSync(`release/manifest-${key}.json`)) {
const existContent = JSON.parse(await readFile(`release/manifest-${key}.json`, { encoding: 'utf-8' })) as BuildInfo[]
values.push(...existContent)
}
await writeFile(`release/manifest-${key}.json`, JSON.stringify(values, null, 4))
const data = JSON.stringify(Object.assign(values, Object.assign(value, { size: formatBytes(file.byteLength), hashMD5: hashMD5, hashSHA: hashSHA })), null, 4)
await writeFile(`release/manifest-${key}.json`, data)
}
}
}
Expand Down Expand Up @@ -595,7 +636,7 @@ release [options] <input>
break
}
case 'pkg': {
buildFunctions.push(() => build.pkgbuild())
buildFunctions.push(async () => { await build.pkgbuild(); await build.sign(); await build.singCheck() })
break
}
case 'esbuild': {
Expand All @@ -604,7 +645,7 @@ release [options] <input>
}
}
}

buildFunctions.push(() => build.saveManifest())
exec.push(async () => await execQueue(buildFunctions))
}

Expand Down

0 comments on commit bb795a0

Please sign in to comment.