1- import { z } from 'zod'
2- import { JwkError , jwkSchema } from '@atproto/jwk'
1+ import { Jwk , JwkError , jwkSchema } from '@atproto/jwk'
32import { GenerateKeyPairOptions , JoseKey } from '@atproto/jwk-jose'
43import { fromSubtleAlgorithm , isCryptoKeyPair } from './util.js'
54
6- // Webcrypto keys are bound to a single algorithm
7- export const jwkWithAlgSchema = z . intersection (
8- jwkSchema ,
9- z . object ( { alg : z . string ( ) } ) ,
10- )
11-
12- export type JwkWithAlg = z . infer < typeof jwkWithAlgSchema >
13-
14- export class WebcryptoKey <
15- J extends JwkWithAlg = JwkWithAlg ,
16- > extends JoseKey < J > {
5+ export class WebcryptoKey < J extends Jwk = Jwk > extends JoseKey < J > {
176 // We need to override the static method generate from JoseKey because
187 // the browser needs both the private and public keys
198 static override async generate (
209 allowedAlgos : string [ ] = [ 'ES256' ] ,
2110 kid : string = crypto . randomUUID ( ) ,
2211 options ?: GenerateKeyPairOptions ,
23- ) {
12+ ) : Promise < WebcryptoKey > {
2413 const keyPair = await this . generateKeyPair ( allowedAlgos , options )
2514
2615 // Type safety only: in the browser, 'jose' always generates a CryptoKeyPair
@@ -31,14 +20,11 @@ export class WebcryptoKey<
3120 return this . fromKeypair ( keyPair , kid )
3221 }
3322
34- static async fromKeypair ( cryptoKeyPair : CryptoKeyPair , kid ?: string ) {
35- // https://datatracker.ietf.org/doc/html/rfc7517
36- // > The "use" and "key_ops" JWK members SHOULD NOT be used together; [...]
37- // > Applications should specify which of these members they use.
38-
23+ static async fromKeypair (
24+ cryptoKeyPair : CryptoKeyPair ,
25+ kid ?: string ,
26+ ) : Promise < WebcryptoKey > {
3927 const {
40- key_ops,
41- use,
4228 alg = fromSubtleAlgorithm ( cryptoKeyPair . privateKey . algorithm ) ,
4329 ...jwk
4430 } = await crypto . subtle . exportKey (
@@ -48,17 +34,8 @@ export class WebcryptoKey<
4834 : cryptoKeyPair . publicKey ,
4935 )
5036
51- if ( use && use !== 'sig' ) {
52- throw new TypeError ( `Unsupported JWK use "${ use } "` )
53- }
54-
55- if ( key_ops && ! key_ops . some ( ( o ) => o === 'sign' || o === 'verify' ) ) {
56- // Make sure that "key_ops", if present, is compatible with "use"
57- throw new TypeError ( `Invalid key_ops "${ key_ops } " for "sig" use` )
58- }
59-
60- return new WebcryptoKey (
61- jwkWithAlgSchema . parse ( { ...jwk , kid, alg, use : 'sig' } ) ,
37+ return new WebcryptoKey < Jwk > (
38+ jwkSchema . parse ( { ...jwk , kid, alg } ) ,
6239 cryptoKeyPair ,
6340 )
6441 }
@@ -67,18 +44,16 @@ export class WebcryptoKey<
6744 jwk : Readonly < J > ,
6845 readonly cryptoKeyPair : CryptoKeyPair ,
6946 ) {
47+ // Webcrypto keys are bound to a single algorithm
48+ if ( ! jwk . alg ) throw new JwkError ( 'JWK "alg" is required for Webcrypto keys' )
49+
7050 super ( jwk )
7151 }
7252
7353 get isPrivate ( ) {
7454 return true
7555 }
7656
77- get privateJwk ( ) : Readonly < J > | undefined {
78- if ( super . isPrivate ) return this . jwk
79- throw new Error ( 'Private Webcrypto Key not exportable' )
80- }
81-
8257 protected override async getKeyObj ( alg : string ) {
8358 if ( this . jwk . alg !== alg ) {
8459 throw new JwkError ( `Key cannot be used with algorithm "${ alg } "` )
0 commit comments