Skip to content

Commit

Permalink
feat: add serializeSignature + parseSignature
Browse files Browse the repository at this point in the history
  • Loading branch information
jxom committed Jul 1, 2024
1 parent 1e90195 commit ffc1b57
Show file tree
Hide file tree
Showing 3 changed files with 207 additions and 1 deletion.
47 changes: 46 additions & 1 deletion src/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -301,7 +301,7 @@ Serializes a public key into a hex string or bytes.
#### Usage

```ts
import { parsePublicKey } from 'webauthn-p256'
import { serializePublicKey } from 'webauthn-p256'

const publicKey = serializePublicKey({
x: 12341...,
Expand All @@ -319,7 +319,52 @@ console.log(publicKey)
| `publicKey` | P256 Credential public key. | `PublicKey` |
| returns | Serialized public key. | `string` |

### `parseSignature`

Parses a serialized signature into r and s coordinates.

#### Usage

```ts
import { parseSignature } from 'webauthn-p256'

const signature = parseSignature('0x...')

console.log(signature)
// { r: 1231..., s: 12412... }
```

#### API

| Name | Description | Type |
| ----------- | -------------------------------------- | ------------- |
| `signature` | Serialized P256 signature. | `0x${string}` |
| returns | Parsed P256 signature. | `Signature` |

### `serializeSignature`

Serializes a signature into a hex string or bytes.

#### Usage

```ts
import { serializeSignature } from 'webauthn-p256'

const signature = serializeSignature({
r: 12341...,
s: 12341...,
})

console.log(signature)
// '0x...'
```

#### API

| Name | Description | Type |
| ----------- | --------------------------- | ----------- |
| `signature` | P256 signature. | `Signature` |
| returns | Serialized signature. | `string` |

## Authors

Expand Down
127 changes: 127 additions & 0 deletions src/sign.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@ import { describe, expect, test } from 'vitest'
import {
getCredentialSignRequestOptions,
parseAsn1Signature,
parseSignature,
serializeSignature,
sign,
} from './sign.js'

Expand Down Expand Up @@ -307,3 +309,128 @@ describe('parseAsn1Signature', async () => {
`)
})
})

describe('parseSignature', () => {
test('default', () => {
const signature =
'0x16d6f4bd3231c71c5e58927b9cf2ee701df03b52e3db71efc03d1139122f854f67f32a4fcb17b07ab9b7755b61e999b99139074fc8e1aa6d33d25beccbb2fbd4'
expect(parseSignature(signature)).toMatchInlineSnapshot(
`
{
"r": 10330677067519063752777069525326520293658884904426299601620960859195372963151n,
"s": 47017859265388077754498411591757867926785106410894171160067329762716841868244n,
}
`,
)
expect(serializeSignature(parseSignature(signature))).toEqual(signature)
})

test('bytes', () => {
const signature = new Uint8Array([
22, 214, 244, 189, 50, 49, 199, 28, 94, 88, 146, 123, 156, 242, 238, 112,
29, 240, 59, 82, 227, 219, 113, 239, 192, 61, 17, 57, 18, 47, 133, 79,
103, 243, 42, 79, 203, 23, 176, 122, 185, 183, 117, 91, 97, 233, 153, 185,
145, 57, 7, 79, 200, 225, 170, 109, 51, 210, 91, 236, 203, 178, 251, 212,
])
expect(parseSignature(signature)).toMatchInlineSnapshot(
`
{
"r": 10330677067519063752777069525326520293658884904426299601620960859195372963151n,
"s": 47017859265388077754498411591757867926785106410894171160067329762716841868244n,
}
`,
)
})
})

describe('serializeSignature', () => {
test('default', () => {
const signature = {
r: 10330677067519063752777069525326520293658884904426299601620960859195372963151n,
s: 47017859265388077754498411591757867926785106410894171160067329762716841868244n,
} as const
expect(serializeSignature(signature)).toMatchInlineSnapshot(
`"0x16d6f4bd3231c71c5e58927b9cf2ee701df03b52e3db71efc03d1139122f854f67f32a4fcb17b07ab9b7755b61e999b99139074fc8e1aa6d33d25beccbb2fbd4"`,
)
expect(parseSignature(serializeSignature(signature))).toEqual(signature)
})

test('bytes', () => {
const signature = {
r: 10330677067519063752777069525326520293658884904426299601620960859195372963151n,
s: 47017859265388077754498411591757867926785106410894171160067329762716841868244n,
} as const
expect(
serializeSignature(signature, { to: 'bytes' }),
).toMatchInlineSnapshot(
`
Uint8Array [
22,
214,
244,
189,
50,
49,
199,
28,
94,
88,
146,
123,
156,
242,
238,
112,
29,
240,
59,
82,
227,
219,
113,
239,
192,
61,
17,
57,
18,
47,
133,
79,
103,
243,
42,
79,
203,
23,
176,
122,
185,
183,
117,
91,
97,
233,
153,
185,
145,
57,
7,
79,
200,
225,
170,
109,
51,
210,
91,
236,
203,
178,
251,
212,
]
`,
)
})
})
34 changes: 34 additions & 0 deletions src/sign.ts
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
import { numberToBytesBE } from '@noble/curves/abstract/utils'
import { p256 } from '@noble/curves/p256'
import type { Credential, Hex, Signature, WebAuthnData } from './types.js'
import {
Expand Down Expand Up @@ -142,3 +143,36 @@ export function parseAsn1Signature(bytes: Uint8Array) {
s: s > n / 2n ? n - s : s,
}
}

/**
* Parses a serialized signature into r and s values.
*/
export function parseSignature(signature: Hex | Uint8Array): Signature {
const bytes =
typeof signature === 'string' ? hexToBytes(signature) : signature
const r = bytes.slice(0, 32)
const s = bytes.slice(32, 64)
return {
r: BigInt(bytesToHex(r)),
s: BigInt(bytesToHex(s)),
}
}

export type SerializeSignatureOptions<to extends 'hex' | 'bytes' = 'hex'> = {
to?: to | 'bytes' | 'hex' | undefined
}

/**
* Serializes a signature into a hex string or bytes.
*/
export function serializeSignature<to extends 'hex' | 'bytes' = 'hex'>(
signature: Signature,
options: SerializeSignatureOptions<to> = {},
): to extends 'hex' ? Hex : Uint8Array {
const { to = 'hex' } = options
const result = new Uint8Array([
...numberToBytesBE(signature.r, 32),
...numberToBytesBE(signature.s, 32),
])
return (to === 'hex' ? bytesToHex(result) : result) as any
}

0 comments on commit ffc1b57

Please sign in to comment.