Skip to content

Commit

Permalink
feat(http-signature-utils): add functionality to load a base64 encode…
Browse files Browse the repository at this point in the history
…d private key (#295)

* feat: add functionality to load a base64 encoded private key

* chore: add changeset

* test: add `undefined` test

* refactor: remove unnecessary consts
  • Loading branch information
sabineschaller authored Sep 21, 2023
1 parent 2ea12f0 commit 2bdca93
Show file tree
Hide file tree
Showing 5 changed files with 65 additions and 14 deletions.
5 changes: 5 additions & 0 deletions .changeset/thick-deers-sell.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
'@interledger/http-signature-utils': minor
---

adds loadBase64Key
8 changes: 8 additions & 0 deletions packages/http-signature-utils/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,14 @@ Load or generate a private Ed25519 key:
const key = parseOrProvisionKey('/PATH/TO/private-key.pem')
```

Load a base64 encoded Ed25519 private key:

```ts
const key = loadBase64Key(
'LS0tLS1CRUdJTiBQUklWQVRFIEtFWS0tLS0tCk1DNENBUUF3QlFZREsyVndCQ0lFSUkvWHBwdkZPOWltNE9odWkxNytVMnpWNUNuMDJBWXBZWFpwcUlSQ1M0UFkKLS0tLS1FTkQgUFJJVkFURSBLRVktLS0tLQo='
)
```

Create JWK from private Ed25519 key:

```ts
Expand Down
2 changes: 1 addition & 1 deletion packages/http-signature-utils/src/index.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
export { createHeaders, getKeyId, Headers } from './utils/headers'
export { generateJwk, JWK } from './utils/jwk'
export { parseOrProvisionKey } from './utils/key'
export { parseOrProvisionKey, loadBase64Key } from './utils/key'
export { createSignatureHeaders } from './utils/signatures'
export { validateSignatureHeaders, validateSignature } from './utils/validation'
export { generateTestKeys, TestKeys } from './test-utils/keys'
Expand Down
48 changes: 37 additions & 11 deletions packages/http-signature-utils/src/utils/key.test.ts
Original file line number Diff line number Diff line change
@@ -1,20 +1,20 @@
import * as assert from 'assert'
import * as crypto from 'crypto'
import * as fs from 'fs'
import { parseOrProvisionKey } from './key'
import { Buffer } from 'buffer'
import { loadBase64Key, parseOrProvisionKey } from './key'

describe('Config', (): void => {
describe('parseOrProvisionKey', (): void => {
const TMP_DIR = './tmp'

beforeEach(async (): Promise<void> => {
fs.rmSync(TMP_DIR, { recursive: true, force: true })
})
describe('Key methods', (): void => {
const TMP_DIR = './tmp'

afterEach(async (): Promise<void> => {
fs.rmSync(TMP_DIR, { recursive: true, force: true })
})
beforeEach(async (): Promise<void> => {
fs.rmSync(TMP_DIR, { recursive: true, force: true })
})

afterEach(async (): Promise<void> => {
fs.rmSync(TMP_DIR, { recursive: true, force: true })
})
describe('parseOrProvisionKey', (): void => {
test.each`
tmpDirExists
${false}
Expand Down Expand Up @@ -100,4 +100,30 @@ describe('Config', (): void => {
expect(fs.statSync(keyfile).mtimeMs).toEqual(fileStats.mtimeMs)
})
})

describe('loadBase64Key', (): void => {
test('can load base64 encoded key', (): void => {
const key = parseOrProvisionKey(undefined)
const loadedKey = loadBase64Key(
Buffer.from(key.export({ type: 'pkcs8', format: 'pem' })).toString(
'base64'
)
)
expect(loadedKey.export({ format: 'jwk' })).toEqual(
key.export({ format: 'jwk' })
)
})

test('returns undefined if not Ed25519 key', (): void => {
const key = crypto.generateKeyPairSync('rsa', {
modulusLength: 2048
}).privateKey
const loadedKey = loadBase64Key(
Buffer.from(key.export({ type: 'pkcs8', format: 'pem' })).toString(
'base64'
)
)
expect(loadedKey).toBeUndefined()
})
})
})
16 changes: 14 additions & 2 deletions packages/http-signature-utils/src/utils/key.ts
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,7 @@ export function parseOrProvisionKey(
if (keyFile) {
try {
const key = crypto.createPrivateKey(fs.readFileSync(keyFile))
const jwk = key.export({ format: 'jwk' })
if (jwk.crv === 'Ed25519') {
if (checkKey(key)) {
console.log(`Key ${keyFile} loaded.`)
return key
} else {
Expand All @@ -30,3 +29,16 @@ export function parseOrProvisionKey(
)
return keypair.privateKey
}

export function loadBase64Key(base64Key: string): crypto.KeyObject | undefined {
const privateKey = Buffer.from(base64Key, 'base64').toString('utf-8')
const key = crypto.createPrivateKey(privateKey)
if (checkKey(key)) {
return key
}
}

function checkKey(key: crypto.KeyObject): boolean {
const jwk = key.export({ format: 'jwk' })
return jwk.crv === 'Ed25519'
}

0 comments on commit 2bdca93

Please sign in to comment.