diff options
-rw-r--r-- | packages/server/src/helpers/iso/isoCrypto/verifyRSA.ts | 23 | ||||
-rw-r--r-- | packages/server/src/registration/verifyRegistrationResponse.test.ts | 41 |
2 files changed, 59 insertions, 5 deletions
diff --git a/packages/server/src/helpers/iso/isoCrypto/verifyRSA.ts b/packages/server/src/helpers/iso/isoCrypto/verifyRSA.ts index e22bbce..c7dc4b6 100644 --- a/packages/server/src/helpers/iso/isoCrypto/verifyRSA.ts +++ b/packages/server/src/helpers/iso/isoCrypto/verifyRSA.ts @@ -50,6 +50,10 @@ export async function verifyRSA(opts: { hash: { name: mapCoseAlgToWebCryptoAlg(alg) }, }; + const verifyAlgorithm: AlgorithmIdentifier | RsaPssParams = { + name: mapCoseAlgToWebCryptoKeyAlgName(alg), + }; + if (shaHashOverride) { keyAlgorithm.hash.name = mapCoseAlgToWebCryptoAlg(shaHashOverride); } @@ -65,13 +69,28 @@ export async function verifyRSA(opts: { keyData.alg = 'RS1'; } } else if (keyAlgorithm.name === 'RSA-PSS') { + /** + * salt length. The default value is 20 but the convention is to use hLen, the length of the + * output of the hash function in bytes. A salt length of zero is permitted and will result in + * a deterministic signature value. The actual salt length used can be determined from the + * signature value. + * + * From https://www.cryptosys.net/pki/manpki/pki_rsaschemes.html + */ + let saltLength = 0; + if (keyAlgorithm.hash.name === 'SHA-256') { keyData.alg = 'PS256'; + saltLength = 32; // 256 bits => 32 bytes } else if (keyAlgorithm.hash.name === 'SHA-384') { keyData.alg = 'PS384'; + saltLength = 48; // 384 bits => 48 bytes } else if (keyAlgorithm.hash.name === 'SHA-512') { keyData.alg = 'PS512'; + saltLength = 64; // 512 bits => 64 bytes } + + (verifyAlgorithm as RsaPssParams).saltLength = saltLength; } else { throw new Error(`Unexpected RSA key algorithm ${alg} (${keyAlgorithm.name})`); } @@ -81,10 +100,6 @@ export async function verifyRSA(opts: { algorithm: keyAlgorithm, }); - const verifyAlgorithm = { - // TODO: Determine this from `alg` so we might support the rarer RSA-PSS - name: 'RSASSA-PKCS1-v1_5', - }; if (globalThis.crypto) { return globalThis.crypto.subtle.verify(verifyAlgorithm, key, signature, data); } else { diff --git a/packages/server/src/registration/verifyRegistrationResponse.test.ts b/packages/server/src/registration/verifyRegistrationResponse.test.ts index 0927810..ad9b08f 100644 --- a/packages/server/src/registration/verifyRegistrationResponse.test.ts +++ b/packages/server/src/registration/verifyRegistrationResponse.test.ts @@ -89,7 +89,7 @@ test('should verify Packed (EC2) attestation', async () => { expect(verification.registrationInfo?.credentialID).toEqual( isoBase64URL.toBuffer( 'AYThY1csINY4JrbHyGmqTl1nL_F1zjAF3hSAIngz8kAcjugmAMNVvxZRwqpEH-bNHHAIv291OX5ko9eDf_5mu3U' + - 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q', + 'B2BvsScr2K-ppM4owOpGsqwg5tZglqqmxIm1Q', ), ); }); @@ -633,6 +633,45 @@ test('should verify FIDO U2F attestation that specifies SHA-1 in its leaf cert p }); }); +test('should verify Packed attestation with RSA-PSS SHA-256 public key', async () => { + const verified = await verifyRegistrationResponse({ + credential: { + id: "n_dmFmW9UL7678vS4A3XSQLXvxWjefEkYVzEB5cNc_Q", + rawId: "n_dmFmW9UL7678vS4A3XSQLXvxWjefEkYVzEB5cNc_Q", + response: { + attestationObject: "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzgkY3NpZ1kBAEaJQ9f_DWVWGJMJrHymDCRP7v2cOzeEA8Z1IUsd4GTq65qqg2khO05tKe6QK_NvpWbiLCRJ2E9QiMUu3xGTl7RIrIRp4T2WCjk5tLbLNwsHuFAPyjcuvIlcX2ZsKNL27tTroIz_zbzDk07vf0jhghoS3ec-qKrSZQ-B0ULgyDJf0omzgDRlH6uon7mErtunes9hVDUTn9pG9UJSL-jDptoJyu87NnBFGnlpu-Iur1lMKIEW27m5E7wYxF7IqIF2lylZGqXxh7ji93Bs7Hhik6y1T9KiGmn58rrYMxmBXzprxNQMF7rJxXbSZ9ZfjaZYamMDaoKDyKEhfAiOHXCm8AVoYXV0aERhdGFZAWZJlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0EAAAB1qWxJcH1fTWqB93Yyt64CQAAgn_dmFmW9UL7678vS4A3XSQLXvxWjefEkYVzEB5cNc_SkAQMDOCQgWQEArEwu_kUDitzDgKOTthwbNnBGfGeUEwv8ksLGvqyRbTNClHnrR9fpaffqQeNor3ndNSReFnZ_3i468d677NMJC4-qoLKu7JP2FIDpt2reDCxg7-XvsaCcDIOucvKR-KIKg9CGiNpkHMhq2auXc4aqYrRjRyuoNYkzpWGENn34govaQQqC5Gdc0yHSeFJLrc9rbQoxMiZY1Ujpe3p9me0VXL4QdNmH_NlnzRclt38Rl8HqQOhrLo6rJOuRc_Ws-BjT0xh8HL8STgTxwb9aKquFkPxylztEy4TAgmOsFv-ukfGwbGO4fszqQKtpsf5-ulO8mfszgY1VrCLmuDzBzdGsdSFDAQAB", + clientDataJSON: "eyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJjaGFsbGVuZ2UiOiI0MHZfaXpNcHpYLUxPTklHekdxMFlieER3TUtNZmRfWHhRenBlNld2NjRZIiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9", + }, + authenticatorAttachment: '', + clientExtensionResults: {}, + type: "public-key", + }, + expectedChallenge: '40v_izMpzX-LONIGzGq0YbxDwMKMfd_XxQzpe6Wv64Y', + expectedOrigin: 'http://localhost:8000', + expectedRPID: 'localhost', + }); +}); + +test('should verify Packed attestation with RSA-PSS SHA-384 public key', async () => { + const verified = await verifyRegistrationResponse({ + credential: { + id: "BCwirFmTkTdTUjVqn_uSy-UOSK-iMBgzpfFunE-Hnb0", + rawId: "BCwirFmTkTdTUjVqn_uSy-UOSK-iMBgzpfFunE-Hnb0", + response: { + attestationObject: "o2NmbXRmcGFja2VkZ2F0dFN0bXSiY2FsZzglY3NpZ1kBAB7Tn5jK2sn5U4SBuxYzmR-Rg6iU5nox23mUxw6c10RsWcCw0h3aSKaon3gcn_Sfy8cov1YSsJVeUy9jVYJSpfQSS9ZMZXD5btGPf_YKH34j9YSGyTyutquZRxJ01mou2krDIaiXJOGLFpCJfVUBe-ben68MESby_Q2VFA6u3pjayC6Tu_iUJKPwdWPPaJM2P2KwyYtPy2jGIKqn6UFekfHOKpIDInW7QmzZF6JKUXNWqmwddq0vfzBpHlcyCBRDKmbGv667lkOUz9d7h_Lw0ho2HBrqEQuXhfmog5viDsezgHjQ196JZTwIgAO20vWioXiDWwJKjXGUmQxt9OGlQ1doYXV0aERhdGFZAWZJlg3liA6MaHQ0Fw9kdmBbj-SuuaKGMseZXPO6gx2XY0EAAABjBuy6aWZcQpm9f0NUYyTRzQAgBCwirFmTkTdTUjVqn_uSy-UOSK-iMBgzpfFunE-Hnb2kAQMDOCUgWQEApgFt6NaWotNSJIfFKOsdNlOtc7vdG7b78Rrnk7oCyUYg9PFVXRhgwSNAKBwimjeRILxcra5roznykpbcv3RIWNaej-tfxG2KYINh5ts8V2I3R2PgtlgwMfSSH9tv65gAzAFRk7tyizHelODhhNUbMVPMc-qTmnBzZANd06w0PN8xnWgCHPaG2MHZkFAOqiNkL4Kv0PPFbQTpy9HZd9ofdQhpKL71iXU4pMFJSSLG8jhY-HM2EwBM2HBTqb06qDjt6UOThCqCqd-ltNRllKWfstkUKQT0XOB-NpZ88037onupO2qDaMSudwolToh3-muuGAYCSANRS3TcNPuYP-s-6yFDAQAB", + clientDataJSON: "eyJvcmlnaW4iOiJodHRwOi8vbG9jYWxob3N0OjgwMDAiLCJjaGFsbGVuZ2UiOiJwLWphWEhmWUpkbGQ2eTVucklzYTZyblpmNnJnU0MtRm8xcTdBU01VN2s4IiwidHlwZSI6IndlYmF1dGhuLmNyZWF0ZSJ9", + }, + clientExtensionResults: {}, + authenticatorAttachment: '', + type: "public-key", + }, + expectedChallenge: 'p-jaXHfYJdld6y5nrIsa6rnZf6rgSC-Fo1q7ASMU7k8', + expectedOrigin: 'http://localhost:8000', + expectedRPID: 'localhost', + }); + +}); + /** * Various Attestations Below */ |