-
Notifications
You must be signed in to change notification settings - Fork 71
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
0b94248
commit 08b2761
Showing
3 changed files
with
115 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,71 @@ | ||
import { hexToBytes, base58ToBytes, base64ToBytes } from '../util' | ||
import { ES256Signer } from '../signers/ES256Signer' | ||
|
||
describe('Secp256r1 Signer', () => { | ||
it('signs data, given a hex private key', async () => { | ||
expect.assertions(1) | ||
const privateKey = '040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a' | ||
const signer = ES256Signer(hexToBytes(privateKey)) | ||
const plaintext = 'thequickbrownfoxjumpedoverthelazyprogrammer' | ||
await expect(signer(plaintext)).resolves.toEqual( | ||
'vOTe64WujVUjEiQrAlwaPJtNADx4usSlCfe8OXHS6Np1BqJdqdJX912pVwVlAjmbqR_TMVE5i5TWB_GJVgrHgg' | ||
) | ||
}) | ||
|
||
it('signs data: privateKey with 0x prefix', async () => { | ||
expect.assertions(1) | ||
const privateKey = '0x040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a' | ||
const signer = ES256Signer(hexToBytes(privateKey)) | ||
const plaintext = 'thequickbrownfoxjumpedoverthelazyprogrammer' | ||
await expect(signer(plaintext)).resolves.toEqual( | ||
'vOTe64WujVUjEiQrAlwaPJtNADx4usSlCfe8OXHS6Np1BqJdqdJX912pVwVlAjmbqR_TMVE5i5TWB_GJVgrHgg' | ||
) | ||
}) | ||
|
||
it('signs data: privateKey base58', async () => { | ||
expect.assertions(1) | ||
const privateKey = 'Gqzym8nfnxR5ZYZ3wZo8rvTwKTqGn5cJsbHnEhUZDPo' | ||
const signer = ES256Signer(base58ToBytes(privateKey)) | ||
const plaintext = 'thequickbrownfoxjumpedoverthelazyprogrammer' | ||
await expect(signer(plaintext)).resolves.toEqual( | ||
'vOTe64WujVUjEiQrAlwaPJtNADx4usSlCfe8OXHS6Np1BqJdqdJX912pVwVlAjmbqR_TMVE5i5TWB_GJVgrHgg' | ||
) | ||
}) | ||
|
||
it('signs data: privateKey base64url', async () => { | ||
expect.assertions(1) | ||
const privateKey = 'BA8dvwosqGh1RHp8AQsPxtOddoWcRY--jyv3daQK10o' | ||
const signer = ES256Signer(base64ToBytes(privateKey)) | ||
const plaintext = 'thequickbrownfoxjumpedoverthelazyprogrammer' | ||
await expect(signer(plaintext)).resolves.toEqual( | ||
'vOTe64WujVUjEiQrAlwaPJtNADx4usSlCfe8OXHS6Np1BqJdqdJX912pVwVlAjmbqR_TMVE5i5TWB_GJVgrHgg' | ||
) | ||
}) | ||
|
||
it('signs data: privateKey base64', async () => { | ||
expect.assertions(1) | ||
const privateKey = 'BA8dvwosqGh1RHp8AQsPxtOddoWcRY++jyv3daQK10o' | ||
const signer = ES256Signer(base64ToBytes(privateKey)) | ||
const plaintext = 'thequickbrownfoxjumpedoverthelazyprogrammer' | ||
await expect(signer(plaintext)).resolves.toEqual( | ||
'vOTe64WujVUjEiQrAlwaPJtNADx4usSlCfe8OXHS6Np1BqJdqdJX912pVwVlAjmbqR_TMVE5i5TWB_GJVgrHgg' | ||
) | ||
}) | ||
|
||
it('refuses wrong key size (too short)', async () => { | ||
expect.assertions(1) | ||
const privateKey = '040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad7' | ||
expect(() => { | ||
ES256Signer(hexToBytes(privateKey)) | ||
}).toThrowError(/^bad_key: Invalid private key format.*/) | ||
}) | ||
|
||
it('refuses wrong key size (double)', async () => { | ||
expect.assertions(1) | ||
const privateKey = | ||
'040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a040f1dbf0a2ca86875447a7c010b0fc6d39d76859c458fbe8f2bf775a40ad74a' | ||
expect(() => { | ||
ES256Signer(hexToBytes(privateKey)) | ||
}).toThrowError(/^bad_key: Invalid private key format.*/) | ||
}) | ||
}) |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,42 @@ | ||
import { leftpad } from '../util' | ||
import { toJose } from '../util' | ||
import { Signer } from '../JWT' | ||
import { sha256 } from '../Digest' | ||
import elliptic from 'elliptic' | ||
|
||
const secp256r1 = new elliptic.ec('p256') | ||
|
||
/** | ||
* Creates a configured signer function for signing data using the ES256K (secp256r1 + sha256) algorithm. | ||
* | ||
* The signing function itself takes the data as a `Uint8Array` or `string` and returns a `base64Url`-encoded signature | ||
* | ||
* @example | ||
* ```typescript | ||
* const sign: Signer = ES256Signer(process.env.PRIVATE_KEY) | ||
* const signature: string = await sign(data) | ||
* ``` | ||
* | ||
* @param {String} privateKey a private key as `Uint8Array` | ||
* @param {Boolean} recoverable an optional flag to add the recovery param to the generated signatures | ||
* @return {Function} a configured signer function `(data: string | Uint8Array): Promise<string>` | ||
*/ | ||
export function ES256Signer(privateKey: Uint8Array, recoverable = false): Signer { | ||
const privateKeyBytes: Uint8Array = privateKey | ||
if (privateKeyBytes.length !== 32) { | ||
throw new Error(`bad_key: Invalid private key format. Expecting 32 bytes, but got ${privateKeyBytes.length}`) | ||
} | ||
const keyPair: elliptic.ec.KeyPair = secp256r1.keyFromPrivate(privateKeyBytes) | ||
|
||
return async (data: string | Uint8Array): Promise<string> => { | ||
const { r, s, recoveryParam }: elliptic.ec.Signature = keyPair.sign(sha256(data)) | ||
return toJose( | ||
{ | ||
r: leftpad(r.toString('hex')), | ||
s: leftpad(s.toString('hex')), | ||
recoveryParam, | ||
}, | ||
recoverable | ||
) | ||
} | ||
} |