Repository: honojs/hono
File: src/utils/jwt/jws.ts (TypeScript)

Code before:
  const cryptoKey = await importPrivateKey(privateKey, algorithm)
  return await crypto.subtle.sign(algorithm, cryptoKey, data)
}

export async function verifying(
  publicKey: SignatureKey,
  alg: SignatureAlgorithm,
  signature: BufferSource,
  data: BufferSource
): Promise<boolean> {
  const algorithm = getKeyAlgorithm(alg)
  const cryptoKey = await importPublicKey(publicKey, algorithm)
  return await crypto.subtle.verify(algorithm, cryptoKey, signature, data)
}

function pemToBinary(pem: string): Uint8Array {
  return decodeBase64(pem.replace(/-+(BEGIN|END).*/g, '').replace(/\s/g, ''))
}

async function importPrivateKey(key: SignatureKey, alg: KeyImporterAlgorithm): Promise<CryptoKey> {
  if (!crypto.subtle || !crypto.subtle.importKey) {
    throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
  }
  if (isCryptoKey(key)) {
    if (key.type !== 'private' && key.type !== 'secret') {
      throw new Error(`unexpected non private key: CryptoKey.type is ${key.type}`)
    }
    return key
  }
  const usages = [CryptoKeyUsage.Sign]
  if (typeof key === 'object') {
    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#json_web_key_import
    return await crypto.subtle.importKey('jwk', key, alg, false, usages)
  }
  if (key.includes('PRIVATE')) {
    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#pkcs_8_import
    return await crypto.subtle.importKey('pkcs8', pemToBinary(key), alg, false, usages)
  }
  return await crypto.subtle.importKey('raw', utf8Encoder.encode(key), alg, false, usages)
}

async function importPublicKey(key: SignatureKey, alg: KeyImporterAlgorithm): Promise<CryptoKey> {
  if (!crypto.subtle || !crypto.subtle.importKey) {
    throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
  }
  if (isCryptoKey(key)) {
    if (key.type === 'public' || key.type === 'secret') {
      return key
    }
    key = await exportPublicJwkFrom(key)
  }

Review comment (security):
How about changing the error message to clarify the `secret` is also expected? Like this:

```ts
throw new Error(`unexpected key type: CryptoKey.type is ${key.type}, expected 'private' or 'secret'`)
```

Code after:
  const cryptoKey = await importPrivateKey(privateKey, algorithm)
  return await crypto.subtle.sign(algorithm, cryptoKey, data)
}

export async function verifying(
  publicKey: SignatureKey,
  alg: SignatureAlgorithm,
  signature: BufferSource,
  data: BufferSource
): Promise<boolean> {
  const algorithm = getKeyAlgorithm(alg)
  const cryptoKey = await importPublicKey(publicKey, algorithm)
  return await crypto.subtle.verify(algorithm, cryptoKey, signature, data)
}

function pemToBinary(pem: string): Uint8Array {
  return decodeBase64(pem.replace(/-+(BEGIN|END).*/g, '').replace(/\s/g, ''))
}

async function importPrivateKey(key: SignatureKey, alg: KeyImporterAlgorithm): Promise<CryptoKey> {
  if (!crypto.subtle || !crypto.subtle.importKey) {
    throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
  }
  if (isCryptoKey(key)) {
    if (key.type !== 'private' && key.type !== 'secret') {
      throw new Error(
        `unexpected key type: CryptoKey.type is ${key.type}, expected private or secret`
      )
    }
    return key
  }
  const usages = [CryptoKeyUsage.Sign]
  if (typeof key === 'object') {
    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#json_web_key_import
    return await crypto.subtle.importKey('jwk', key, alg, false, usages)
  }
  if (key.includes('PRIVATE')) {
    // https://developer.mozilla.org/en-US/docs/Web/API/SubtleCrypto/importKey#pkcs_8_import
    return await crypto.subtle.importKey('pkcs8', pemToBinary(key), alg, false, usages)
  }
  return await crypto.subtle.importKey('raw', utf8Encoder.encode(key), alg, false, usages)
}

async function importPublicKey(key: SignatureKey, alg: KeyImporterAlgorithm): Promise<CryptoKey> {
  if (!crypto.subtle || !crypto.subtle.importKey) {
    throw new Error('`crypto.subtle.importKey` is undefined. JWT auth middleware requires it.')
  }
  if (isCryptoKey(key)) {
    if (key.type === 'public' || key.type === 'secret') {
      return key
    }