Skip to content

Commit

Permalink
feat: generate keys when extension is installed (#93)
Browse files Browse the repository at this point in the history
* Generate keys when extension is installed

* Add comments
  • Loading branch information
raducristianpopa authored Feb 21, 2024
1 parent b944898 commit 4025f6b
Show file tree
Hide file tree
Showing 8 changed files with 100 additions and 15 deletions.
2 changes: 2 additions & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,8 @@
"test:ci": "pnpm test -- --reporters=default --reporters=github-actions"
},
"dependencies": {
"@noble/ed25519": "^2.0.0",
"@noble/hashes": "^1.3.3",
"awilix": "^10.0.1",
"axios": "^1.5.1",
"class-variance-authority": "^0.7.0",
Expand Down
15 changes: 15 additions & 0 deletions pnpm-lock.yaml

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

30 changes: 30 additions & 0 deletions src/background/Background.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
import { bytesToHex } from '@noble/hashes/utils'
import browser, { Runtime, runtime, tabs } from 'webextension-polyfill'

import { PaymentFlowService } from '@/background/grantFlow'
import { exportJWK, generateEd25519KeyPair } from '@/utils/crypto'
import { defaultData } from '@/utils/storage'

import getSendingPaymentPointerHandler from '../messageHandlers/getSendingPaymentPointerHandler'
Expand Down Expand Up @@ -72,6 +74,34 @@ class Background {
unsubscribeFromMessages() {
this.subscriptions.forEach((sub: any) => sub())
}

// TODO: Move to storage wrapper once available
private async keyExists(): Promise<boolean> {
return new Promise(res => {
chrome.storage.local.get(['privateKey', 'publicKey', 'kid'], data => {
if (data.privateKey && data.publicKey && data.kid) {
res(true)
} else {
res(false)
}
})
})
}

async onInstalled() {
chrome.runtime.onInstalled.addListener(async () => {
if (await this.keyExists()) return
const { privateKey, publicKey } = generateEd25519KeyPair()
const kid = crypto.randomUUID()
const jwk = exportJWK(publicKey, kid)

chrome.storage.local.set({
privateKey: bytesToHex(privateKey),
publicKey: btoa(JSON.stringify(jwk)),
kid,
})
})
}
}

export default Background
12 changes: 0 additions & 12 deletions src/background/BackgroundContainer.ts

This file was deleted.

14 changes: 14 additions & 0 deletions src/background/container.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
import { asClass, createContainer } from 'awilix'

import Background from './Background'

interface Cradle {
background: Background
}

export const container = createContainer<Cradle>()

container.register({
background: asClass(Background).singleton(),
// TODO - add injectable services
})
5 changes: 3 additions & 2 deletions src/background/index.ts
Original file line number Diff line number Diff line change
@@ -1,10 +1,11 @@
import BackgroundContainer from './BackgroundContainer'
import { container } from './container'

const initialize = () => {
console.log('Start initialization')

const background = BackgroundContainer.resolve('Background')
const background = container.resolve('background')

background.onInstalled()
background.subscribeToMessages()
background.subscribeToTabChanges()

Expand Down
35 changes: 35 additions & 0 deletions src/utils/crypto.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
import * as ed from '@noble/ed25519'
import { sha512 } from '@noble/hashes/sha512'

ed.etc.sha512Sync = (...m) => sha512(ed.etc.concatBytes(...m))

export function generateEd25519KeyPair() {
const rawPrivateKey = ed.utils.randomPrivateKey()
// PKCS#8 format (version + algorithm)
// Adding these values upfront solves the future import of the key using
// `crypto.subtle.importKey` once the WebCrypto API supports the Ed25519 algorithm.
// prettier-ignore
const privateKey = new Uint8Array([
48, 46, 2, 1, 0, 48, 5, 6, 3, 43, 101, 112, 4, 34, 4, 32,
...rawPrivateKey,
])
const publicKey = ed.getPublicKey(rawPrivateKey)

return { privateKey, publicKey }
}

export function exportJWK(key: Uint8Array, kid: string) {
const string = String.fromCharCode.apply(null, key)

const base64 = btoa(string)
// Based on the JWK Spec - base64url encoded.
// https://datatracker.ietf.org/doc/html/rfc7517#section-3
const base64Url = base64.replace(/\+/g, '-').replace(/\//g, '_').replace(/=+$/, '')

return {
kty: 'OKP',
crv: 'Ed25519',
x: base64Url,
kid,
}
}
2 changes: 1 addition & 1 deletion tsconfig.json
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
"baseUrl": "./src",
"esModuleInterop": true,
"module": "commonjs",
"target": "es5",
"target": "es6",
"allowJs": true,
"jsx": "react",
"sourceMap": true,
Expand Down

0 comments on commit 4025f6b

Please sign in to comment.