wallix / webauthn

node.js webauthn framework
Apache License 2.0
105 stars 23 forks source link

Add support for Android SafetyNet #26

Open eheikes opened 4 years ago

eheikes commented 4 years ago

What It Does

This PR adds support for the android-safetynet attestation format to the server package. It should close #24.

Details

parseAndroidSafetyNetKey() follows the process outlined in the WebAuthn specs and the SafetyNet docs. validateAndroidSafetyNetKey() and parseAttestationData() basically duplicates the code from the other formats, since it's the same routine for all.

The original implementation in our fork has been tested and will be used in production soon. This PR takes code & ideas from @tnokin's fork to improve upon the original code.

How To Test

You can test against a real-world example:

const parseRegisterRequest = require('./packages/server/src/registration').parseRegisterRequest
const body = {
    "id": "Acy1doxPENsOlmfL8xOLpWEIrjYQ67ADfnL5P0Nzjszt-QGl_x4-48MP1yiMVOxKZqGLti4Ppkr3WYF-OXcv840",
    "rawId": "Acy1doxPENsOlmfL8xOLpWEIrjYQ67ADfnL5P0Nzjszt-QGl_x4-48MP1yiMVOxKZqGLti4Ppkr3WYF-OXcv840",
    "type": "public-key",
    "response": {
        "attestationObject": "o2NmbXRxYW5kcm9pZC1zYWZldHluZXRnYXR0U3RtdKJjdmVyaDE5NDIwMDQ4aHJlc3BvbnNlWRTCZXlKaGJHY2lPaUpTVXpJMU5pSXNJbmcxWXlJNld5Sk5TVWxHYkVSRFEwSkllV2RCZDBsQ1FXZEpVa0ZOYTJKNk1tOUdhaXRuYVVOQlFVRkJRVUZXWkdZd2QwUlJXVXBMYjFwSmFIWmpUa0ZSUlV4Q1VVRjNVV3BGVEUxQmEwZEJNVlZGUW1oTlExWldUWGhJYWtGalFtZE9Wa0pCYjFSR1ZXUjJZakprYzFwVFFsVmpibFo2WkVOQ1ZGcFlTakpoVjA1c1kzcEZWRTFDUlVkQk1WVkZRWGhOUzFJeFVsUkpSVTVDU1VSR1VFMVVRV1ZHZHpCNFQxUkJOVTFxUVhkT2VsVXlUVlJvWVVaM01IaFBWRVY1VFZScmQwNTZWVEpOVkdoaFRVZDNlRU42UVVwQ1owNVdRa0ZaVkVGc1ZsUk5VazEzUlZGWlJGWlJVVWxGZDNCRVdWZDRjRnB0T1hsaWJXeG9UVkpaZDBaQldVUldVVkZJUlhjeFRtSXpWblZrUjBad1ltbENWMkZYVmpOTlVrMTNSVkZaUkZaUlVVdEZkM0JJWWpJNWJtSkhWV2RVUlhoRVRWSnpkMGRSV1VSV1VWRkVSWGhLYUdSSVVteGpNMUYxV1ZjMWEyTnRPWEJhUXpWcVlqSXdkMmRuUldsTlFUQkhRMU54UjFOSllqTkVVVVZDUVZGVlFVRTBTVUpFZDBGM1oyZEZTMEZ2U1VKQlVVTmtjbXRwV0VnemEyZE1UR0ZTZUdJMFprWXlhV1YwUWpsdFFqZG1URE55VnpGVlZ5dDFVR1o0ZERCaWQyVlZaWHByVjI5MFJrdHJjVlZKVUdsUmVWaExlak5OTVZZeFMxaDZSSFkyWm1Ob05rNDFPRE5SYW1nNGFsbFhaMEYyYzNGSGRtRTFSbWxxYUZkWWNXdGtVRVJrT0dSaWFUZGpiMU5yVGpsVFdUbFhOVTh5Y0hOMFJWUXpZMlJrUzNwMGNrczJORUpQY1U1U1VHUkRlRWMyYUZKd0syOVZVa2xYVHpoRFUwMXhWMDh5VFZkU1RuQXdSbHBPYUZOTGNHZ3ZheTlNZDJwVk4ybFVZMVpWV1dKdVJIRkxXbVExV1ZGc2NsRllNVEJLZGt3MmNFTmFkVVpPU3pSMlZsQkpSWGxHTDA5NFUyeEpRemN2Um1kb1JHUjNNRWRNV0hwbFpuRjBWMm93UkZCaGEydGlTR1J6VFROUVMxaHhkbnBQZEdwaUx6Wm9lVlJEY0ZaUWJuTTFMMVpYUmpRNVMzRTNVaTlTZVROd1RrWmhhRFIyVG1ob2FtNXRkMDF2Tm1aUVoyMUxRMU5DTldsRUwwRm5UVUpCUVVkcVoyZEtXazFKU1VOV1ZFRlBRbWRPVmtoUk9FSkJaamhGUWtGTlEwSmhRWGRGZDFsRVZsSXdiRUpCZDNkRFoxbEpTM2RaUWtKUlZVaEJkMFYzUkVGWlJGWlNNRlJCVVVndlFrRkpkMEZFUVdSQ1owNVdTRkUwUlVablVWVXplWHBYWms1UWRYUmxhelI2UkVwSmQyZFVaV2xFT0dnMVYxRjNTSGRaUkZaU01HcENRbWQzUm05QlZXMU9TRFJpYUVSeWVqVjJjMWxLT0ZsclFuVm5Oak13U2k5VGMzZGFRVmxKUzNkWlFrSlJWVWhCVVVWRlYwUkNWMDFEWTBkRFEzTkhRVkZWUmtKNlFVSm9hSFJ2WkVoU2QwOXBPSFppTWs1NlkwTTFkMkV5YTNWYU1qbDJXbms1Ym1SSVRYaGlla1YzUzNkWlNVdDNXVUpDVVZWSVRVRkxSMGd5YURCa1NFRTJUSGs1ZDJFeWEzVmFNamwyV25rNWJtTXpTWGxNTUdSVlZYcEdVRTFUTldwamJsRjNTRkZaUkZaU01GSkNRbGwzUmtsSlUxbFlVakJhV0U0d1RHMUdkVnBJU25aaFYxRjFXVEk1ZEUxRFJVZEJNVlZrU1VGUllVMUNaM2REUVZsSFdqUkZUVUZSU1VOTlFYZEhRMmx6UjBGUlVVSXhibXREUWxGTmQweDNXVVJXVWpCbVFrTm5kMHBxUVd0dlEwdG5TVWxaWldGSVVqQmpSRzkyVERKT2VXSkROWGRoTW10MVdqSTVkbHA1T1VoV1JrMTRWSHBGZFZrelNuTk5TVWxDUWxGWlMwdDNXVUpDUVVoWFpWRkpSVUZuVTBJNVoxTkNPSGRFZUVGSVdVRlpMMHhpZW1Wbk4zcERlbEJETTB0RlNqRmtjazAyVTA1WldHVlFkbGhYYlU5TVNFaGhSbEpNTWtrd1FVRkJSblJVWlV4dU1IZEJRVUpCVFVGU2VrSkdRV2xGUVdsdWVrMXZOM0owVXpKakx6TktRbWRSZERKRFN5dG9hR012WXpOMFNWZzVjVXd5V1c5eGNURTFSVkZEU1VOYWVrTjZSVkZQTUU1WWRXbDRiU3MyTjJ4VWNsa3pjbUZRTjJ0MUswOUNUbFZ1YTFGWFYwUTVaRWxCU0dOQlpFZzNZV2Q2UjNSTmVFTlNTVnA2VDBwVk9VTmpUVXN2TDFZMVEwbEJha2RPZWxZMU5XaENOM3BHV1VGQlFVWjBWR1ZNYmpSQlFVRkNRVTFCVTBSQ1IwRnBSVUV4UmpSWU4wcHZTV1p1VFZKNWFsVmxVMXBZWmxBck1uaGhhR2wzUTBSMVYyRnBRa1ZrWW5KV01uSkZRMGxSUkZkWWFTdEdVVUZKTW5CdmEyaDFSMnBEVFhWa0sxZE1NbUZGT0RjeFJIVlJRemRLZFZKNmRHUjFWM3BCVGtKbmEzRm9hMmxIT1hjd1FrRlJjMFpCUVU5RFFWRkZRVmhITlVoeGJVTlNUekpDU2preFZHSlpNRWgzUVdjeVl6RkhVVll6ZDFOV01uQlBiRFZTYmpKcldqTnNiSEJIUkhSc2VsaFRRVFZoYUVWSE9XZFdaMHhHU1RjNFMxWnhkVlJtZWxkVk9VWmhNSGxsU2pWSmJGRlNVRkpPTTBaWGNHRkxOMVJtTWtjM2JGWjFUeXR3VUZNdk1qVjJVbG95TjNoelowZ3dNRmg0YmxwbVJWTnZNR3hoV1hkMGVtbDBVRlZEV1M5VVNrbDZibUoxU2xFMlFtNXhiR2xDZGsweE4wcDFlR1ZXY2tnNU1qWm5ValJHTW5wS2JraGlZMWRxUkZvMWMwSkZRWG81YlM5VU16WmFPRzk1ZGpSMGVFRXZUMnhHUVZKUlVETnFjMjFGSzJnMmNFZzFSRU5UU1U4M1NYZ3daMlZOZW5FMlVsTmlOVEp0VFRSc2VtUmpSRW81YzFZd1FscGhWbmRRZUU5bFUycGFXVzgyYW5sMFJHaFdMekY0VDFabFpWVmFMekJFYTJnMVpYVmlUblZaT1dFck5IRkxUVE5GU3pZeFpHcHVaMkp2WldWelVVcHRTamRKVWt0dmVrbzBVVDA5SWl3aVRVbEpSVk5xUTBOQmVrdG5RWGRKUWtGblNVNUJaVTh3YlhGSFRtbHhiVUpLVjJ4UmRVUkJUa0puYTNGb2EybEhPWGN3UWtGUmMwWkJSRUpOVFZOQmQwaG5XVVJXVVZGTVJYaGtTR0pIT1dsWlYzaFVZVmRrZFVsR1NuWmlNMUZuVVRCRloweFRRbE5OYWtWVVRVSkZSMEV4VlVWRGFFMUxVako0ZGxsdFJuTlZNbXh1WW1wRlZFMUNSVWRCTVZWRlFYaE5TMUl5ZUhaWmJVWnpWVEpzYm1KcVFXVkdkekI0VG5wQk1rMVVWWGROUkVGM1RrUktZVVozTUhsTlZFVjVUVlJWZDAxRVFYZE9SRXBoVFVWSmVFTjZRVXBDWjA1V1FrRlpWRUZzVmxSTlVqUjNTRUZaUkZaUlVVdEZlRlpJWWpJNWJtSkhWV2RXU0VveFl6TlJaMVV5Vm5sa2JXeHFXbGhOZUVWNlFWSkNaMDVXUWtGTlZFTnJaRlZWZVVKRVVWTkJlRlI2UlhkblowVnBUVUV3UjBOVGNVZFRTV0l6UkZGRlFrRlJWVUZCTkVsQ1JIZEJkMmRuUlV0QmIwbENRVkZFVVVkTk9VWXhTWFpPTURWNmExRlBPU3QwVGpGd1NWSjJTbnA2ZVU5VVNGYzFSSHBGV21oRU1tVlFRMjUyVlVFd1VXc3lPRVpuU1VObVMzRkRPVVZyYzBNMFZESm1WMEpaYXk5cVEyWkRNMUl6VmxwTlpGTXZaRTQwV2t0RFJWQmFVbkpCZWtSemFVdFZSSHBTY20xQ1FrbzFkM1ZrWjNwdVpFbE5XV05NWlM5U1IwZEdiRFY1VDBSSlMyZHFSWFl2VTBwSUwxVk1LMlJGWVd4MFRqRXhRbTF6U3l0bFVXMU5SaXNyUVdONFIwNW9jalU1Y1Uwdk9XbHNOekZKTW1ST09FWkhabU5rWkhkMVlXVnFOR0pZYUhBd1RHTlJRbUpxZUUxalNUZEtVREJoVFROVU5Fa3JSSE5oZUcxTFJuTmlhbnBoVkU1RE9YVjZjRVpzWjA5Slp6ZHlVakkxZUc5NWJsVjRkamgyVG0xcmNUZDZaRkJIU0ZocmVGZFpOMjlIT1dvclNtdFNlVUpCUW1zM1dISktabTkxWTBKYVJYRkdTa3BUVUdzM1dFRXdURXRYTUZremVqVnZlakpFTUdNeGRFcExkMGhCWjAxQ1FVRkhhbWRuUlhwTlNVbENUSHBCVDBKblRsWklVVGhDUVdZNFJVSkJUVU5CV1ZsM1NGRlpSRlpTTUd4Q1FsbDNSa0ZaU1V0M1dVSkNVVlZJUVhkRlIwTkRjMGRCVVZWR1FuZE5RMDFDU1VkQk1WVmtSWGRGUWk5M1VVbE5RVmxDUVdZNFEwRlJRWGRJVVZsRVZsSXdUMEpDV1VWR1NtcFNLMGMwVVRZNEsySTNSME5tUjBwQlltOVBkRGxEWmpCeVRVSTRSMEV4VldSSmQxRlpUVUpoUVVaS2RtbENNV1J1U0VJM1FXRm5ZbVZYWWxOaFRHUXZZMGRaV1hWTlJGVkhRME56UjBGUlZVWkNkMFZDUWtOcmQwcDZRV3hDWjJkeVFtZEZSa0pSWTNkQldWbGFZVWhTTUdORWIzWk1NamxxWXpOQmRXTkhkSEJNYldSMllqSmpkbG96VG5sTmFrRjVRbWRPVmtoU09FVkxla0Z3VFVObFowcGhRV3BvYVVadlpFaFNkMDlwT0haWk0wcHpURzVDY21GVE5XNWlNamx1VERKa2VtTnFTWFphTTA1NVRXazFhbU50ZDNkUWQxbEVWbEl3WjBKRVozZE9ha0V3UW1kYWJtZFJkMEpCWjBsM1MycEJiMEpuWjNKQ1owVkdRbEZqUTBGU1dXTmhTRkl3WTBoTk5reDVPWGRoTW10MVdqSTVkbHA1T1hsYVdFSjJZekpzTUdJelNqVk1la0ZPUW1kcmNXaHJhVWM1ZHpCQ1FWRnpSa0ZCVDBOQlVVVkJSMjlCSzA1dWJqYzRlVFp3VW1wa09WaHNVVmRPWVRkSVZHZHBXaTl5TTFKT1IydHRWVzFaU0ZCUmNUWlRZM1JwT1ZCRllXcDJkMUpVTW1sWFZFaFJjakF5Wm1WemNVOXhRbGt5UlZSVmQyZGFVU3RzYkhSdlRrWjJhSE5QT1hSMlFrTlBTV0Y2Y0hOM1YwTTVZVW81ZUdwMU5IUlhSRkZJT0U1V1ZUWlpXbG92V0hSbFJGTkhWVGxaZWtweFVHcFpPSEV6VFVSNGNucHRjV1Z3UWtObU5XODRiWGN2ZDBvMFlUSkhObmg2VlhJMlJtSTJWRGhOWTBSUE1qSlFURkpNTm5VelRUUlVlbk16UVRKTk1XbzJZbmxyU2xscE9IZFhTVkprUVhaTFRGZGFkUzloZUVKV1lucFpiWEZ0ZDJ0dE5YcE1VMFJYTlc1SlFVcGlSVXhEVVVOYWQwMUlOVFowTWtSMmNXOW1lSE0yUWtKalEwWkpXbFZUY0hoMU5uZzJkR1F3VmpkVGRrcERRMjl6YVhKVGJVbGhkR292T1dSVFUxWkVVV2xpWlhRNGNTODNWVXMwZGpSYVZVNDRNR0YwYmxwNk1YbG5QVDBpWFgwLmV5SnViMjVqWlNJNkluTkRVek5hY1c1WWVVUnNXVGhEZVZKUmFFZEdiSEJMVW1oWWFHeHpRME0zUWtOc2VtUldTbmcyWTFVOUlpd2lkR2x0WlhOMFlXMXdUWE1pT2pFMU56RXpOREl6TVRJNU5ETXNJbUZ3YTFCaFkydGhaMlZPWVcxbElqb2lZMjl0TG1kdmIyZHNaUzVoYm1SeWIybGtMbWR0Y3lJc0ltRndhMFJwWjJWemRGTm9ZVEkxTmlJNkltcDRNM2x0VVZwSFJFdE1XRVZWVFVkMFNrdzRPWEJKYVcxcGVIQXdOM1UxTlU5elJXMXFVSFlyWWpBOUlpd2lZM1J6VUhKdlptbHNaVTFoZEdOb0lqcDBjblZsTENKaGNHdERaWEowYVdacFkyRjBaVVJwWjJWemRGTm9ZVEkxTmlJNld5STRVREZ6VnpCRlVFcGpjMngzTjFWNlVuTnBXRXcyTkhjclR6VXdSV1FyVWtKSlEzUmhlVEZuTWpSTlBTSmRMQ0ppWVhOcFkwbHVkR1ZuY21sMGVTSTZkSEoxWlgwLmpsc25yb1g3bXRPLXZROUM4SGNhaFFZRHJENHdJZ1RUNFBzRmI5bWthcml5dUNBV1BwWDNNYlhILVhNZ1VGMmdWdEVFX3B5S2prMUhiOXJKdk1LS0JCb2FoOTR3dTlwbEt3akVPa2xmaWtuelp5VzgyNUh2T05GMmhaTDlsazd2NXZUeUhMeGVpMGxUejZmSTBILUR6b0RXeDF6QXFzOC1vbF9PekhBMzU2R3pRcFg2WlJ0Sk43aUF0QUkzOXVoeGVhSkk5ZWQwM2lGaFRxY1BfYXJSZnBDWjJHUEp0QWVBRFFQWWRhdDNYQ05VYm4zWEpnZ0xfZHNTaUlwazFxSFduNlZvcXpUMk9QTFF6NjdYYXp1RGV4bGF5SXFQLWFhTFc5TjJKYXE4NDBXVWdLZGFLNE1FSWxvVFBVWmNnSi1nRjFMdjJxUm9fUjRvVkhNVEt6M3EzZ2hhdXRoRGF0YVjFSZYN5YgOjGh0NBcPZHZgW4/krrmihjLHmVzzuoMdl2NFAAAAALk/2WHy5kYvsSKCACJH3ngAQQHMtXaMTxDbDpZny/MTi6VhCK42EOuwA35y+T9Dc47M7fkBpf8ePuPDD9cojFTsSmahi7YuD6ZK91mBfjl3L/ONpQECAyYgASFYIDbKMSzPmXLUnE5smSCzWtDjYqxbrUcjaqjLteSBPxylIlggKShBQQRSLimme/nNu3dxm8K1YH+1VA3/CgisT1Ck8Yw=",
        "clientDataJSON": "eyJ0eXBlIjoid2ViYXV0aG4uY3JlYXRlIiwiY2hhbGxlbmdlIjoiMk4zelNBWFZLdTJtQkdxV3VWQnozd2xFODdjT1Q2RHdPNUJoTnZ6amJSQSIsIm9yaWdpbiI6Imh0dHBzOlwvXC9sb2NhbGhvc3Q6ODQ0MyIsImFuZHJvaWRQYWNrYWdlTmFtZSI6ImNvbS5hbmRyb2lkLmNocm9tZSJ9"
    }
}
console.log(parseRegisterRequest(body))

Running this should result in:

{ challenge: '2N3zSAXVKu2mBGqWuVBz3wlE87cOT6DwO5BhNvzjbRA=',
  key:
   { fmt: 'android-safetynet',
     publicKey:
      'BDbKMSzPmXLUnE5smSCzWtDjYqxbrUcjaqjLteSBPxylKShBQQRSLimme/nNu3dxm8K1YH+1VA3/CgisT1Ck8Yw=',
     counter: 0,
     credID:
      'Acy1doxPENsOlmfL8xOLpWEIrjYQ67ADfnL5P0Nzjszt+QGl/x4+48MP1yiMVOxKZqGLti4Ppkr3WYF+OXcv840=' } }
eheikes commented 4 years ago

@Kmaschta Can this be merged?

gustavobini commented 4 years ago

Any follow up on this?