Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat(http-signature-utils): move http signature related code to this package #797

Merged
merged 57 commits into from
Dec 15, 2022
Merged
Show file tree
Hide file tree
Changes from 7 commits
Commits
Show all changes
57 commits
Select commit Hold shift + click to select a range
97d10da
feat(http-signature-utils): initial commit
sabineschaller Nov 29, 2022
bd71bb0
chore(http-signature-utils): update lockfile
sabineschaller Nov 29, 2022
5a299e2
fix(http-signature-utils): fix package.json
sabineschaller Nov 29, 2022
359cbc9
fix: add http-signature-utils to tsconfig
sabineschaller Nov 29, 2022
29cd5e3
fix: include http-signature-utils in gh workflow
sabineschaller Nov 29, 2022
cc52dc6
fix: gh workflow build deps
sabineschaller Nov 29, 2022
d07b504
fix: builds
sabineschaller Nov 29, 2022
f505324
feat(HSU): pass keyId
sabineschaller Nov 29, 2022
458b88f
Merge branch 'main' into s2-http-sig-utils
sabineschaller Nov 30, 2022
ab5fe70
feat(HSU): add Dockerfile and add app to local infrastructure
sabineschaller Nov 30, 2022
f6eae9c
feat(HSU): move content digest creation into HSU
sabineschaller Nov 30, 2022
b8371bd
Revert "feat(HSU): move content digest creation into HSU"
sabineschaller Nov 30, 2022
cefd817
feat(MAP): load private key
sabineschaller Nov 30, 2022
1a111b1
chore: fix build:deps
sabineschaller Nov 30, 2022
e19a4a8
Merge branch 'main' into s2-http-sig-utils
sabineschaller Dec 1, 2022
46ab175
fix(MAP): graphql url
sabineschaller Dec 1, 2022
ceaa11c
feat(HSU): add content headers to app
sabineschaller Dec 2, 2022
e03aff4
fix(HSU): remove `conent-type` and `content-length` headers from app
sabineschaller Dec 2, 2022
c589ac7
feat(HSU): add content digest header to app
sabineschaller Dec 2, 2022
9dd1052
fix(HSU): allow for lower case headers
sabineschaller Dec 5, 2022
5bf116a
feat(HSU): move sig verification from auth to HSU
sabineschaller Dec 5, 2022
afc70f0
fix: export and imports
sabineschaller Dec 5, 2022
a027ee1
Merge branch 'main' into s2-http-sig-utils
sabineschaller Dec 5, 2022
e3b0af1
feat(HSU): move sig verification to HSU
sabineschaller Dec 6, 2022
8bcdda1
refactor(HSU): remove koa context from HSU
sabineschaller Dec 6, 2022
a293bf2
fix(backend): middleware context
sabineschaller Dec 7, 2022
019a7c5
Merge branch 'main' into s2-http-sig-utils
sabineschaller Dec 7, 2022
6664e26
fix(backend): imports
sabineschaller Dec 7, 2022
0951b09
fix(HSU): remove conent type and length headers from app response
sabineschaller Dec 7, 2022
b5dc812
fix(HSU): add missing dependency
sabineschaller Dec 7, 2022
4c8930a
fix: request URL
sabineschaller Dec 7, 2022
3764296
fix(local): readd redis network
sabineschaller Dec 7, 2022
c62549c
fix(HSU): removing whitespace
sabineschaller Dec 7, 2022
1db05d5
feat(local): enable sig validation
sabineschaller Dec 7, 2022
742af97
test(HSU): add tests
sabineschaller Dec 8, 2022
889ee01
fix: types
sabineschaller Dec 8, 2022
92d48dc
refactor(auth): remove console.log
sabineschaller Dec 8, 2022
0891bfa
fix(MAP): quote fees
sabineschaller Dec 12, 2022
8f08f94
Merge branch 'main' into s2-http-sig-utils
sabineschaller Dec 12, 2022
69a1089
docs(HSU): add Readme
sabineschaller Dec 12, 2022
21e06f0
feat(HSU): add postman scripts
sabineschaller Dec 13, 2022
1276e17
fix(HSU): formatting
sabineschaller Dec 13, 2022
6dfcb56
fix(HSU): add postman scripts to lintigonre file
sabineschaller Dec 13, 2022
7d2912d
refactor(HSU): verification
sabineschaller Dec 14, 2022
8f85413
fix(HSU): key tmp dir
sabineschaller Dec 14, 2022
5b61e5a
refactor(infrastructure): rename key files
sabineschaller Dec 14, 2022
bfa7353
chore(auth): remove jose
sabineschaller Dec 14, 2022
7658735
refactor(open-payments): use createHeaders from utils package
sabineschaller Dec 14, 2022
27d8936
style(HSU): rename validateHttpSigHeaders and verifySigAndChallenge
sabineschaller Dec 14, 2022
44d9241
refactor(auth): only store keyId, not entire key
sabineschaller Dec 14, 2022
9108d93
refactor: remove JWKWithRequired
sabineschaller Dec 14, 2022
96dcee0
refactor(HSU): remove unnecessary type cast
sabineschaller Dec 14, 2022
03a8a73
Merge branch 'main' into s2-http-sig-utils
sabineschaller Dec 14, 2022
45a51d4
chore: update gh workflow actions version
sabineschaller Dec 14, 2022
d448a24
chore(HSU): move postman scripts out of src
sabineschaller Dec 14, 2022
b759b36
test(HSU): add positive header validation tests
sabineschaller Dec 14, 2022
b5e035c
style(HSU): rename verification -> validation
sabineschaller Dec 14, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 11 additions & 1 deletion .github/workflows/lint_test_build.yml
Original file line number Diff line number Diff line change
Expand Up @@ -80,9 +80,18 @@ jobs:
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/rafiki/env-setup
- run: pnpm --filter openapi build
- run: pnpm --filter open-payments build:deps
- run: pnpm --filter open-payments test

http-signature-utils:
runs-on: ubuntu-latest
needs: checkout
timeout-minutes: 5
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/rafiki/env-setup
- run: pnpm --filter http-signature-utils test

build:
runs-on: ubuntu-latest
timeout-minutes: 5
Expand All @@ -93,6 +102,7 @@ jobs:
- openapi
- mock-account-provider
- open-payments
- http-signature-utils
steps:
- uses: actions/checkout@v2
- uses: ./.github/workflows/rafiki/env-setup
Expand Down
2 changes: 1 addition & 1 deletion packages/auth/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
],
"scripts": {
"knex": "knex",
"build:deps": "pnpm --filter open-payments build",
"build:deps": "pnpm --filter open-payments build && pnpm --filter http-signature-utils build",
"build": "pnpm build:deps && tsc --build tsconfig.json && pnpm copy-files",
"clean": "rm -fr dist/",
"fetch-schemas": "./scripts/get-op-schema.sh",
Expand Down
3 changes: 2 additions & 1 deletion packages/backend/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"test": "jest --passWithNoTests --maxWorkers=50%",
"knex": "knex",
"generate": "graphql-codegen --config codegen.yml",
"build:deps": "pnpm --filter auth build",
"build:deps": "pnpm --filter auth build && pnpm --filter http-signature-utils build",
"build": "pnpm build:deps && tsc --build tsconfig.json && pnpm copy-files",
"clean": "rm -fr dist/",
"copy-files": "cp src/graphql/schema.graphql dist/graphql/",
Expand Down Expand Up @@ -66,6 +66,7 @@
"graphql": "^16.6.0",
"graphql-scalars": "^1.18.0",
"graphql-tools": "^8.3.3",
"http-signature-utils": "workspace:../http-signature-utils",
"ilp-packet": "3.1.4-alpha.1",
"ilp-protocol-ccp": "^1.2.2",
"ilp-protocol-ildcp": "^2.2.3",
Expand Down
33 changes: 1 addition & 32 deletions packages/backend/src/config/app.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import * as crypto from 'crypto'
import * as fs from 'fs'
import { ConnectionOptions } from 'tls'
import { parseOrProvisionKey } from 'http-signature-utils'

function envString(name: string, value: string): string {
const envValue = process.env[name]
Expand All @@ -24,9 +25,6 @@ function envBool(name: string, value: boolean): boolean {

export type IAppConfig = typeof Config

const TMP_DIR = './tmp'
const PRIVATE_KEY_FILE = `${TMP_DIR}/private-key-${new Date().getTime()}.pem`

export const Config = {
logLevel: envString('LOG_LEVEL', 'info'),
// publicHost is for open payments URLs.
Expand Down Expand Up @@ -147,32 +145,3 @@ function parseRedisTlsConfig(

return Object.keys(options).length > 0 ? options : undefined
}

// exported for testing
export function parseOrProvisionKey(
keyFile: string | undefined
): crypto.KeyObject {
if (keyFile) {
try {
const key = crypto.createPrivateKey(fs.readFileSync(keyFile))
const jwk = key.export({ format: 'jwk' })
if (jwk.crv === 'Ed25519') {
return key
} else {
console.log('Private key is not EdDSA-Ed25519 key. Generating new key.')
}
} catch (err) {
console.log('Private key could not be loaded.')
throw err
}
}
const keypair = crypto.generateKeyPairSync('ed25519')
if (!fs.existsSync(TMP_DIR)) {
fs.mkdirSync(TMP_DIR)
}
fs.writeFileSync(
PRIVATE_KEY_FILE,
keypair.privateKey.export({ format: 'pem', type: 'pkcs8' })
)
return keypair.privateKey
}
30 changes: 30 additions & 0 deletions packages/http-signature-utils/package.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
{
"name": "http-signature-utils",
"main": "dist/index.js",
"types": "dist/index.d.ts",
"files": [
"dist/**/*"
],
"scripts": {
"build": "tsc --build tsconfig.json"
},
"dependencies": {
"http-message-signatures": "^0.1.2",
"koa": "^2.13.4",
"koa-bodyparser": "^4.3.0",
"koa-json": "^2.0.2",
"koa-logger": "^3.2.1",
"koa-router": "^12.0.0",
"uuid": "^8.3.2"
},
"devDependencies": {
"@types/koa": "2.13.5",
"@types/koa-bodyparser": "^4.3.7",
"@types/koa-json": "^2.0.20",
"@types/koa-logger": "^3.1.2",
"@types/koa-router": "^7.4.4",
"@types/node": "^18.7.12",
"@types/uuid": "^8.3.4",
"typescript": "^4.9.3"
}
}
45 changes: 45 additions & 0 deletions packages/http-signature-utils/src/app.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
import Koa from 'koa'
import Router from 'koa-router'

import logger from 'koa-logger'
import json from 'koa-json'
import bodyParser from 'koa-bodyparser'

import { createSignatureHeaders } from './utils/signatures'
import { parseOrProvisionKey } from './utils/key'
import { v4 as uuid } from 'uuid'
import { RequestLike } from 'http-message-signatures'

const app = new Koa()
const router = new Router()

// Load key
const privateKey = parseOrProvisionKey(process.env.KEY_FILE)
const keyId = process.env.KEY_ID || uuid()

// Router
router.get('/', async (ctx): Promise<void> => {
ctx.body = { msg: "I don't exist." }
})

router.post('/', async (ctx): Promise<void> => {
const request = ctx.request.body as RequestLike
if (!request.headers || !request.method || !request.url) {
ctx.status = 400
return
}
const headers = await createSignatureHeaders({ request, privateKey, keyId })
ctx.body = headers
})

// Middlewares
app.use(json())
app.use(logger())
app.use(bodyParser())

// Routes
app.use(router.routes()).use(router.allowedMethods())

app.listen(3000, () => {
console.log('HTTP Signature Manager started.')
})
3 changes: 3 additions & 0 deletions packages/http-signature-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
export { generateJwk } from './utils/jwk'
export { parseOrProvisionKey } from './utils/key'
export { createSignatureHeaders } from './utils/signatures'
Original file line number Diff line number Diff line change
@@ -1,5 +1,13 @@
import { createPublicKey, generateKeyPairSync, KeyObject } from 'crypto'
import { JWK } from './types'

type JWK = {
kid: string
alg: 'EdDSA'
use?: 'sig'
kty: 'OKP'
crv: 'Ed25519'
x: string
}

export const generateJwk = ({
privateKey: providedPrivateKey,
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import * as assert from 'assert'
import * as crypto from 'crypto'
import * as fs from 'fs'
import { parseOrProvisionKey } from './app'
import { parseOrProvisionKey } from './key'

describe('Config', (): void => {
describe('parseOrProvisionKey', (): void => {
Expand Down
33 changes: 33 additions & 0 deletions packages/http-signature-utils/src/utils/key.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
import * as crypto from 'crypto'
import * as fs from 'fs'

const TMP_DIR = './tmp'
const PRIVATE_KEY_FILE = `${TMP_DIR}/private-key-${new Date().getTime()}.pem`

export function parseOrProvisionKey(
keyFile: string | undefined
): crypto.KeyObject {
if (keyFile) {
try {
const key = crypto.createPrivateKey(fs.readFileSync(keyFile))
const jwk = key.export({ format: 'jwk' })
if (jwk.crv === 'Ed25519') {
return key
} else {
console.log('Private key is not EdDSA-Ed25519 key. Generating new key.')
}
} catch (err) {
console.log('Private key could not be loaded.')
throw err
}
}
const keypair = crypto.generateKeyPairSync('ed25519')
if (!fs.existsSync(TMP_DIR)) {
fs.mkdirSync(TMP_DIR)
}
fs.writeFileSync(
PRIVATE_KEY_FILE,
keypair.privateKey.export({ format: 'pem', type: 'pkcs8' })
)
return keypair.privateKey
}
11 changes: 11 additions & 0 deletions packages/http-signature-utils/tsconfig.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
{
"extends": "../../tsconfig.build.json",
"compilerOptions": {
"lib": ["ES2020"],
"outDir": "./dist",
"rootDir": "./src",
"declaration": true
},
"include": ["src/**/*"],
"exclude": ["**/*.test.ts", "src/test/*"]
}
3 changes: 2 additions & 1 deletion packages/open-payments/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@
"dist/**/*"
],
"scripts": {
"build:deps": "pnpm --filter openapi build",
"build:deps": "pnpm --filter openapi build && pnpm --filter http-signature-utils build",
"build": "pnpm build:deps && pnpm clean && tsc --build tsconfig.json",
"clean": "rm -fr dist/",
"generate:types": "npx ts-node scripts/generate-types.ts",
Expand All @@ -26,6 +26,7 @@
"dependencies": {
"axios": "^1.1.2",
"http-message-signatures": "^0.1.2",
"http-signature-utils": "workspace:../http-signature-utils",
"openapi": "workspace:../openapi",
"pino": "^8.4.2"
}
Expand Down
2 changes: 1 addition & 1 deletion packages/open-payments/src/client/requests.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios, { AxiosInstance } from 'axios'
import { KeyLike } from 'crypto'
import { ResponseValidator } from 'openapi'
import { BaseDeps } from '.'
import { createSignatureHeaders } from './signatures'
import { createSignatureHeaders } from 'http-signature-utils'

interface GetArgs {
url: string
Expand Down
2 changes: 0 additions & 2 deletions packages/open-payments/src/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,5 +16,3 @@ export {
AuthenticatedClient,
UnauthenticatedClient
} from './client'

export { generateJwk } from './jwk'
Loading