Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 1 addition & 1 deletion WebView/CredentialManagerWebView/PasskeyWebListener.kt
Original file line number Diff line number Diff line change
Expand Up @@ -231,7 +231,7 @@ class PasskeyWebListener(
/** INJECTED_VAL is the minified version of the JavaScript code described at this class
* heading. The non minified form is found at credmanweb/javascript/encode.js.*/
const val INJECTED_VAL = """
var __webauthn_interface__,__webauthn_hooks__;!function(e){console.log("In the hook."),__webauthn_interface__.addEventListener("message",function e(n){var r=JSON.parse(n.data),t=r[2];"get"===t?o(r):"create"===t?u(r):console.log("Incorrect response format for reply")});var n=null,r=null,t=null,a=null;function o(e){if(null!==n&&null!==t){if("success"!=e[0]){var r=t;n=null,t=null,r(new DOMException(e[1],"NotAllowedError"));return}var a=i(e[1]),o=n;n=null,t=null,o(a)}}function l(e){var n=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,"+").replace(/_/g,"/").padEnd(e.length+(0===n?0:4-n),"=")),function(e){return e.charCodeAt(0)}).buffer}function s(e){return btoa(Array.from(new Uint8Array(e),function(e){return String.fromCharCode(e)}).join("")).replace(/\+/g,"-").replace(/\//g,"_").replace(/=+${'$'}/,"")}function u(e){if(null===r||null===a){console.log("Here: "+r+" and reject: "+a);return}if(console.log("Output back: "+e),"success"!=e[0]){var n=a;r=null,a=null,n(new DOMException(e[1],"NotAllowedError"));return}var t=i(e[1]),o=r;r=null,a=null,o(t)}function i(e){return console.log("Here is the response from credential manager: "+e),e.rawId=l(e.rawId),e.response.clientDataJSON=l(e.response.clientDataJSON),e.response.hasOwnProperty("attestationObject")&&(e.response.attestationObject=l(e.response.attestationObject)),e.response.hasOwnProperty("authenticatorData")&&(e.response.authenticatorData=l(e.response.authenticatorData)),e.response.hasOwnProperty("signature")&&(e.response.signature=l(e.response.signature)),e.response.hasOwnProperty("userHandle")&&(e.response.userHandle=l(e.response.userHandle)),e.getClientExtensionResults=function e(){return{}},e}e.create=function n(t){if(!("publicKey"in t))return e.originalCreateFunction(t);var o=new Promise(function(e,n){r=e,a=n}),l=t.publicKey;if(l.hasOwnProperty("challenge")){var u=s(l.challenge);l.challenge=u}if(l.hasOwnProperty("user")&&l.user.hasOwnProperty("id")){var i=s(l.user.id);l.user.id=i}var c=JSON.stringify({type:"create",request:l});return __webauthn_interface__.postMessage(c),o},e.get=function r(a){if(!("publicKey"in a))return e.originalGetFunction(a);var o=new Promise(function(e,r){n=e,t=r}),l=a.publicKey;if(l.hasOwnProperty("challenge")){var u=s(l.challenge);l.challenge=u}var i=JSON.stringify({type:"get",request:l});return __webauthn_interface__.postMessage(i),o},e.onReplyGet=o,e.CM_base64url_decode=l,e.CM_base64url_encode=s,e.onReplyCreate=u}(__webauthn_hooks__||(__webauthn_hooks__={})),__webauthn_hooks__.originalGetFunction=navigator.credentials.get,__webauthn_hooks__.originalCreateFunction=navigator.credentials.create,navigator.credentials.get=__webauthn_hooks__.get,navigator.credentials.create=__webauthn_hooks__.create,window.PublicKeyCredential=function(){},window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable=function(){return Promise.resolve(!1)};
var __webauthn_interface__,__webauthn_hooks__;!function(e){__webauthn_interface__.addEventListener('message',function e(n){var r=JSON.parse(n.data),t=r[2];'get'===t?l(r):'create'===t?u(r):console.log('Incorrect response format for reply')});var n=null,r=null,t=null,a=null;function l(e){if(null===n||null===t){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var l=t;n=null,t=null,l(new DOMException(e[1],'NotAllowedError'));return}var o=i(e[1]),s=n;n=null,t=null,s(o)}function o(e){var n=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,'+').replace(/_/g,'/').padEnd(e.length+(0===n?0:4-n),'=')),function(e){return e.charCodeAt(0)}).buffer}function s(e){return btoa(Array.from(new Uint8Array(e),function(e){return String.fromCharCode(e)}).join('')).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+${'$'}/,'')}function u(e){if(null===r||null===a){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var n=a;r=null,a=null,n(new DOMException(e[1],'NotAllowedError'));return}var t=i(e[1]),l=r;r=null,a=null,l(t)}function i(e){return e.rawId=o(e.rawId),e.response.clientDataJSON=o(e.response.clientDataJSON),e.response.hasOwnProperty('attestationObject')&&(e.response.attestationObject=o(e.response.attestationObject)),e.response.hasOwnProperty('authenticatorData')&&(e.response.authenticatorData=o(e.response.authenticatorData)),e.response.hasOwnProperty('signature')&&(e.response.signature=o(e.response.signature)),e.response.hasOwnProperty('userHandle')&&(e.response.userHandle=o(e.response.userHandle)),e.getClientExtensionResults=function e(){return{}},e}e.create=function n(t){if(!('publicKey'in t))return e.originalCreateFunction(t);var l=new Promise(function(e,n){r=e,a=n}),o=t.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}if(o.hasOwnProperty('user')&&o.user.hasOwnProperty('id')){var i=s(o.user.id);o.user.id=i}if(o.hasOwnProperty('excludeCredentials')){for(var c=0;c<o.excludeCredentials.length;c++)if(o.excludeCredentials[c].hasOwnProperty('id')){var f=s(o.excludeCredentials[c].id);o.excludeCredentials[c].id=f}}var p=JSON.stringify({type:'create',request:o});return __webauthn_interface__.postMessage(p),l},e.get=function r(a){if(!('publicKey'in a))return e.originalGetFunction(a);var l=new Promise(function(e,r){n=e,t=r}),o=a.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}var i=JSON.stringify({type:'get',request:o});return __webauthn_interface__.postMessage(i),l},e.onReplyGet=l,e.CM_base64url_decode=o,e.CM_base64url_encode=s,e.onReplyCreate=u}(__webauthn_hooks__||(__webauthn_hooks__={})),__webauthn_hooks__.originalGetFunction=navigator.credentials.get,__webauthn_hooks__.originalCreateFunction=navigator.credentials.create,navigator.credentials.get=__webauthn_hooks__.get,navigator.credentials.create=__webauthn_hooks__.create,window.PublicKeyCredential=function(){},window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable=function(){return Promise.resolve(!1)};

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

The minified JavaScript code in INJECTED_VAL should be kept in sync with the changes made to encode.js. Updating this string ensures that the robust checks for excludeCredentials are also executed by the WebView.

Suggested change
var __webauthn_interface__,__webauthn_hooks__;!function(e){__webauthn_interface__.addEventListener('message',function e(n){var r=JSON.parse(n.data),t=r[2];'get'===t?l(r):'create'===t?u(r):console.log('Incorrect response format for reply')});var n=null,r=null,t=null,a=null;function l(e){if(null===n||null===t){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var l=t;n=null,t=null,l(new DOMException(e[1],'NotAllowedError'));return}var o=i(e[1]),s=n;n=null,t=null,s(o)}function o(e){var n=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,'+').replace(/_/g,'/').padEnd(e.length+(0===n?0:4-n),'=')),function(e){return e.charCodeAt(0)}).buffer}function s(e){return btoa(Array.from(new Uint8Array(e),function(e){return String.fromCharCode(e)}).join('')).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+${'$'}/,'')}function u(e){if(null===r||null===a){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var n=a;r=null,a=null,n(new DOMException(e[1],'NotAllowedError'));return}var t=i(e[1]),l=r;r=null,a=null,l(t)}function i(e){return e.rawId=o(e.rawId),e.response.clientDataJSON=o(e.response.clientDataJSON),e.response.hasOwnProperty('attestationObject')&&(e.response.attestationObject=o(e.response.attestationObject)),e.response.hasOwnProperty('authenticatorData')&&(e.response.authenticatorData=o(e.response.authenticatorData)),e.response.hasOwnProperty('signature')&&(e.response.signature=o(e.response.signature)),e.response.hasOwnProperty('userHandle')&&(e.response.userHandle=o(e.response.userHandle)),e.getClientExtensionResults=function e(){return{}},e}e.create=function n(t){if(!('publicKey'in t))return e.originalCreateFunction(t);var l=new Promise(function(e,n){r=e,a=n}),o=t.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}if(o.hasOwnProperty('user')&&o.user.hasOwnProperty('id')){var i=s(o.user.id);o.user.id=i}if(o.hasOwnProperty('excludeCredentials')){for(var c=0;c<o.excludeCredentials.length;c++)if(o.excludeCredentials[c].hasOwnProperty('id')){var f=s(o.excludeCredentials[c].id);o.excludeCredentials[c].id=f}}var p=JSON.stringify({type:'create',request:o});return __webauthn_interface__.postMessage(p),l},e.get=function r(a){if(!('publicKey'in a))return e.originalGetFunction(a);var l=new Promise(function(e,r){n=e,t=r}),o=a.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}var i=JSON.stringify({type:'get',request:o});return __webauthn_interface__.postMessage(i),l},e.onReplyGet=l,e.CM_base64url_decode=o,e.CM_base64url_encode=s,e.onReplyCreate=u}(__webauthn_hooks__||(__webauthn_hooks__={})),__webauthn_hooks__.originalGetFunction=navigator.credentials.get,__webauthn_hooks__.originalCreateFunction=navigator.credentials.create,navigator.credentials.get=__webauthn_hooks__.get,navigator.credentials.create=__webauthn_hooks__.create,window.PublicKeyCredential=function(){},window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable=function(){return Promise.resolve(!1)};
var __webauthn_interface__,__webauthn_hooks__;!function(e){__webauthn_interface__.addEventListener('message',function e(n){var r=JSON.parse(n.data),t=r[2];'get'===t?l(r):'create'===t?u(r):console.log('Incorrect response format for reply')});var n=null,r=null,t=null,a=null;function l(e){if(null===n||null===t){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var l=t;n=null,t=null,l(new DOMException(e[1],'NotAllowedError'));return}var o=i(e[1]),s=n;n=null,t=null,s(o)}function o(e){var n=e.length%4;return Uint8Array.from(atob(e.replace(/-/g,'+').replace(/_/g,'/').padEnd(e.length+(0===n?0:4-n),'=')),function(e){return e.charCodeAt(0)}).buffer}function s(e){return btoa(Array.from(new Uint8Array(e),function(e){return String.fromCharCode(e)}).join('')).replace(/\+/g,'-').replace(/\//g,'_').replace(/=+${'$'}/,'')}function u(e){if(null===r||null===a){console.log('Reply failure: Resolve: '+r+' and reject: '+a);return}if('success'!=e[0]){var n=a;r=null,a=null,n(new DOMException(e[1],'NotAllowedError'));return}var t=i(e[1]),l=r;r=null,a=null,l(t)}function i(e){return e.rawId=o(e.rawId),e.response.clientDataJSON=o(e.response.clientDataJSON),e.response.hasOwnProperty('attestationObject')&&(e.response.attestationObject=o(e.response.attestationObject)),e.response.hasOwnProperty('authenticatorData')&&(e.response.authenticatorData=o(e.response.authenticatorData)),e.response.hasOwnProperty('signature')&&(e.response.signature=o(e.response.signature)),e.response.hasOwnProperty('userHandle')&&(e.response.userHandle=o(e.response.userHandle)),e.getClientExtensionResults=function e(){return{}},e}e.create=function n(t){if(!('publicKey'in t))return e.originalCreateFunction(t);var l=new Promise(function(e,n){r=e,a=n}),o=t.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}if(o.hasOwnProperty('user')&&o.user.hasOwnProperty('id')){var i=s(o.user.id);o.user.id=i}if(Array.isArray(o.excludeCredentials)){for(var c=0;c<o.excludeCredentials.length;c++){var f=o.excludeCredentials[c];if(f&&f.hasOwnProperty('id')){var d=s(f.id);f.id=d}}}var p=JSON.stringify({type:'create',request:o});return __webauthn_interface__.postMessage(p),l},e.get=function r(a){if(!('publicKey'in a))return e.originalGetFunction(a);var l=new Promise(function(e,r){n=e,t=r}),o=a.publicKey;if(o.hasOwnProperty('challenge')){var u=s(o.challenge);o.challenge=u}var i=JSON.stringify({type:'get',request:o});return __webauthn_interface__.postMessage(i),l},e.onReplyGet=l,e.CM_base64url_decode=o,e.CM_base64url_encode=s,e.onReplyCreate=u}(__webauthn_hooks__||(__webauthn_hooks__={})),__webauthn_hooks__.originalGetFunction=navigator.credentials.get,__webauthn_hooks__.originalCreateFunction=navigator.credentials.create,navigator.credentials.get=__webauthn_hooks__.get,navigator.credentials.create=__webauthn_hooks__.create,window.PublicKeyCredential=function(){},window.PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable=function(){return Promise.resolve(!1)};

"""
const val TAG = "PasskeyWebListener"
}
Expand Down
8 changes: 8 additions & 0 deletions WebView/CredentialManagerWebView/javascript/encode.js
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,14 @@ var __webauthn_hooks__;
var encodedString = CM_base64url_encode(temppk.user.id);
temppk.user.id = encodedString;
}
if (temppk.hasOwnProperty('excludeCredentials')) {
for (var i = 0; i < temppk.excludeCredentials.length; i++) {
if (temppk.excludeCredentials[i].hasOwnProperty('id')) {
var encodedId = CM_base64url_encode(temppk.excludeCredentials[i].id);
temppk.excludeCredentials[i].id = encodedId;
}
}
}
Comment on lines +35 to +42

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

medium

To prevent potential runtime crashes, we should use more defensive checks. If excludeCredentials is present but is null, undefined, or not an array, calling .length on it will throw a TypeError. Similarly, if any element in the excludeCredentials array is null or undefined, calling .hasOwnProperty on it will throw an error.

Using Array.isArray and checking if each element is truthy before accessing its properties makes this code much more robust.

Suggested change
if (temppk.hasOwnProperty('excludeCredentials')) {
for (var i = 0; i < temppk.excludeCredentials.length; i++) {
if (temppk.excludeCredentials[i].hasOwnProperty('id')) {
var encodedId = CM_base64url_encode(temppk.excludeCredentials[i].id);
temppk.excludeCredentials[i].id = encodedId;
}
}
}
if (Array.isArray(temppk.excludeCredentials)) {
for (var i = 0; i < temppk.excludeCredentials.length; i++) {
var cred = temppk.excludeCredentials[i];
if (cred && cred.hasOwnProperty('id')) {
var encodedId = CM_base64url_encode(cred.id);
cred.id = encodedId;
}
}
}

var jsonObj = {"type":"create", "request":temppk}

var json = JSON.stringify(jsonObj);
Expand Down