@lo-fi/webauthn-local-client
Advanced tools
Comparing version 0.999.7 to 0.1000.0
@@ -28,3 +28,3 @@ # Deploying WebAuthn-Local-Client WITH A Bundler | ||
**Note:** The [`ASN1` dependency](https://github.com/yoursunny/asn1.js) is [licensed under MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/), which is generally compatible with this library's [MIT license](LICENSE.txt). However, MPL 2.0 specifically requires preservation of the copyright/license header (block comment at top of `asn1.all.min.js`). To comply with this licensing requirement, ensure your tooling does not remove this comment from the bundle file. | ||
**Note:** The [`ASN1` dependency](https://github.com/root/asn1.js) is [licensed under MPL 2.0](https://www.mozilla.org/en-US/MPL/2.0/), which is generally compatible with this library's [MIT license](LICENSE.txt). However, MPL 2.0 specifically requires preservation of the copyright/license header (block comment at top of `asn1.all.min.js`). To comply with this licensing requirement, ensure your tooling does not remove this comment from the bundle file. | ||
@@ -31,0 +31,0 @@ ### Astro Plugin (aka Integration) |
/*! WebAuthn-Local-Client: external.js | ||
v0.999.7 (c) 2024 Kyle Simpson | ||
v0.1000.0 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
@@ -4,0 +4,0 @@ */ |
@@ -9,5 +9,5 @@ /*! | ||
https://www.npmjs.com/package/@yoursunny/asn1 | ||
https://github.com/yoursunny/asn1.js | ||
https://www.npmjs.com/package/@root/asn1 | ||
https://github.com/therootcompany/asn1.js | ||
*/ | ||
(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i<len;i+=1){h=u8[i].toString(16);if(2!==h.length){h="0"+h}hex.push(h)}return hex.join("").toLowerCase()};Enc.numToHex=function(d){d=d.toString(16);if(d.length%2){return"0"+d}return d};Enc.strToHex=function(str){return Enc._binToHex(Enc.strToBin(str))};Enc._binToHex=function(bin){return bin.split("").map(function(ch){var h=ch.charCodeAt(0).toString(16);if(2!==h.length){h="0"+h}return h}).join("")};Enc.hexToBuf=function(hex){var arr=[];hex.match(/.{2}/g).forEach(function(h){arr.push(parseInt(h,16))});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.hexToStr=function(hex){return Enc.binToStr(_hexToBin(hex))};function _hexToBin(hex){return hex.replace(/([0-9A-F]{2})/gi,function(_,p1){return String.fromCharCode("0x"+p1)})}Enc._hexToBin=_hexToBin;Enc.bufToBin=function(buf){var bin="";buf.forEach(function(ch){bin+=String.fromCharCode(ch)});return bin};Enc.strToBin=function(str){var escstr=encodeURIComponent(str);var binstr=escstr.replace(/%([0-9A-F]{2})/g,function(_,p1){return String.fromCharCode("0x"+p1)});return binstr};Enc.binToBuf=function(bin){var arr=bin.split("").map(function(ch){return ch.charCodeAt(0)});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.strToBuf=function(str){return Enc.binToBuf(Enc.strToBin(str))};Enc.binToStr=function(binstr){var escstr=binstr.replace(/(.)/g,function(m,p){var code=p.charCodeAt(0).toString(16).toUpperCase();if(code.length<2){code="0"+code}return"%"+code});return decodeURIComponent(escstr)};Enc.bufToStr=function(buf){return Enc.binToStr(Enc.bufToBin(buf))};Enc.base64ToHex=function(b64){return Enc.bufToHex(Enc.base64ToBuf(b64))};Enc.hexToBase64=function(hex){return btoa(Enc._hexToBin(hex))}})();(function(){"use strict";var ASN1=window.ASN1={};var Enc=window.Encoding;ASN1.ELOOPN=102;ASN1.ELOOP="uASN1.js Error: iterated over "+ASN1.ELOOPN+"+ elements (probably a malformed file)";ASN1.EDEEPN=60;ASN1.EDEEP="uASN1.js Error: element nested "+ASN1.EDEEPN+"+ layers deep (probably a malformed file)";ASN1.CTYPES=[48,49,160,161];ASN1.VTYPES=[1,2,5,6,12,130];ASN1.parseVerbose=function parseAsn1Helper(buf,opts){if(!opts){opts={}}function parseAsn1(buf,depth,eager){if(depth.length>=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters<ASN1.ELOOPN&&index<2+asn1.length+asn1.lengthSize){iters+=1;depth.length+=1;child=parseAsn1(buf.slice(index,index+adjustedLen),depth,eager);depth.length-=1;index+=2+child.lengthSize+child.length;if(index>2+asn1.lengthSize+asn1.length){throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})(); | ||
(function(){"use strict";var Enc=window.Encoding={};Enc.bufToBase64=function(u8){var bin="";u8.forEach(function(i){bin+=String.fromCharCode(i)});return btoa(bin)};Enc.strToBase64=function(str){return btoa(Enc.strToBin(str))};function _base64ToBin(b64){return atob(Enc.urlBase64ToBase64(b64))}Enc._base64ToBin=_base64ToBin;Enc.base64ToBuf=function(b64){return Enc.binToBuf(_base64ToBin(b64))};Enc.base64ToStr=function(b64){return Enc.binToStr(_base64ToBin(b64))};Enc.urlBase64ToBase64=function(u64){var r=u64%4;if(2===r){u64+="=="}else if(3===r){u64+="="}return u64.replace(/-/g,"+").replace(/_/g,"/")};Enc.base64ToUrlBase64=function(b64){return b64.replace(/\+/g,"-").replace(/\//g,"_").replace(/=/g,"")};Enc.bufToUrlBase64=function(buf){return Enc.base64ToUrlBase64(Enc.bufToBase64(buf))};Enc.strToUrlBase64=function(str){return Enc.bufToUrlBase64(Enc.strToBuf(str))};Enc.bufToHex=function(u8){var hex=[];var i,h;var len=u8.byteLength||u8.length;for(i=0;i<len;i+=1){h=u8[i].toString(16);if(2!==h.length){h="0"+h}hex.push(h)}return hex.join("").toLowerCase()};Enc.numToHex=function(d){d=d.toString(16);if(d.length%2){return"0"+d}return d};Enc.strToHex=function(str){return Enc._binToHex(Enc.strToBin(str))};Enc._binToHex=function(bin){return bin.split("").map(function(ch){var h=ch.charCodeAt(0).toString(16);if(2!==h.length){h="0"+h}return h}).join("")};Enc.hexToBuf=function(hex){var arr=[];hex.match(/.{2}/g).forEach(function(h){arr.push(parseInt(h,16))});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.hexToStr=function(hex){return Enc.binToStr(_hexToBin(hex))};function _hexToBin(hex){return hex.replace(/([0-9A-F]{2})/gi,function(_,p1){return String.fromCharCode("0x"+p1)})}Enc._hexToBin=_hexToBin;Enc.bufToBin=function(buf){var bin="";buf.forEach(function(ch){bin+=String.fromCharCode(ch)});return bin};Enc.strToBin=function(str){var escstr=encodeURIComponent(str);var binstr=escstr.replace(/%([0-9A-F]{2})/g,function(_,p1){return String.fromCharCode("0x"+p1)});return binstr};Enc.binToBuf=function(bin){var arr=bin.split("").map(function(ch){return ch.charCodeAt(0)});return"undefined"!==typeof Uint8Array?new Uint8Array(arr):arr};Enc.strToBuf=function(str){return Enc.binToBuf(Enc.strToBin(str))};Enc.binToStr=function(binstr){var escstr=binstr.replace(/(.)/g,function(m,p){var code=p.charCodeAt(0).toString(16).toUpperCase();if(code.length<2){code="0"+code}return"%"+code});return decodeURIComponent(escstr)};Enc.bufToStr=function(buf){return Enc.binToStr(Enc.bufToBin(buf))};Enc.base64ToHex=function(b64){return Enc.bufToHex(Enc.base64ToBuf(b64))};Enc.hexToBase64=function(hex){return btoa(Enc._hexToBin(hex))}})();(function(){"use strict";var ASN1=window.ASN1={};var Enc=window.Encoding;ASN1.ELOOPN=102;ASN1.ELOOP="uASN1.js Error: iterated over "+ASN1.ELOOPN+"+ elements (probably a malformed file)";ASN1.EDEEPN=60;ASN1.EDEEP="uASN1.js Error: element nested "+ASN1.EDEEPN+"+ layers deep (probably a malformed file)";ASN1.CTYPES=[48,49,160,161];ASN1.VTYPES=[1,2,5,6,12,130];ASN1.parseVerbose=function parseAsn1Helper(buf,opts){if(!opts){opts={}}function parseAsn1(buf,depth,eager){if(depth.length>=ASN1.EDEEPN){throw new Error(ASN1.EDEEP)}var index=2;var asn1={type:buf[0],lengthSize:0,length:buf[1]};var child;var iters=0;var adjust=0;var adjustedLen;if(128&asn1.length){asn1.lengthSize=127&asn1.length;asn1.length=parseInt(Enc.bufToHex(buf.slice(index,index+asn1.lengthSize)),16);index+=asn1.lengthSize}if(0===buf[index]&&(2===asn1.type||3===asn1.type)){if(asn1.length>1){index+=1;adjust=-1}}adjustedLen=asn1.length+adjust;function parseChildren(eager){asn1.children=[];while(iters<ASN1.ELOOPN&&index<2+asn1.length+asn1.lengthSize){iters+=1;depth.length+=1;child=parseAsn1(buf.slice(index,index+adjustedLen),depth,eager);depth.length-=1;index+=2+child.lengthSize+child.length;if(index>2+asn1.lengthSize+asn1.length){throw new Error("Parse error: child value length ("+child.length+") is greater than remaining parent length ("+(asn1.length-index)+" = "+asn1.length+" - "+index+")")}asn1.children.push(child)}if(index!==2+asn1.lengthSize+asn1.length){throw new Error("premature end-of-file")}if(iters>=ASN1.ELOOPN){throw new Error(ASN1.ELOOP)}delete asn1.value;return asn1}if(-1!==ASN1.CTYPES.indexOf(asn1.type)){return parseChildren(eager)}asn1.value=buf.slice(index,index+adjustedLen);if(opts.json){asn1.value=Enc.bufToHex(asn1.value)}if(-1!==ASN1.VTYPES.indexOf(asn1.type)){return asn1}try{return parseChildren(true)}catch(e){asn1.children.length=0;return asn1}}var asn1=parseAsn1(buf,[]);var len=buf.byteLength||buf.length;if(len!==2+asn1.lengthSize+asn1.length){throw new Error("Length of buffer does not match length of ASN.1 sequence.")}return asn1};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1.parse=function(opts){var opts2={json:false!==opts.json};var verbose=ASN1.parseVerbose(opts.der,opts2);if(opts.verbose){return verbose}return ASN1._toArray(verbose,opts2)};ASN1._replacer=function(k,v){if("type"===k){return"0x"+Enc.numToHex(v)}if(v&&"value"===k){return"0x"+Enc.bufToHex(v.data||v)}return v};function Any(){var args=Array.prototype.slice.call(arguments);var typ=args.shift();var str=args.join("").replace(/\s+/g,"").toLowerCase();var len=str.length/2;var lenlen=0;var hex=typ;if("number"===typeof hex){hex=Enc.numToHex(hex)}if(len!==Math.round(len)){throw new Error("invalid hex")}if(len>127){lenlen+=1;while(len>255){lenlen+=1;len=len>>8}}if(lenlen){hex+=Enc.numToHex(128+lenlen)}return hex+Enc.numToHex(str.length/2)+str}ASN1.Any=Any;ASN1.UInt=function UINT(){var str=Array.prototype.slice.call(arguments).join("");var first=parseInt(str.slice(0,2),16);if(128&first){str="00"+str}return Any("02",str)};ASN1.BitStr=function BITSTR(){var str=Array.prototype.slice.call(arguments).join("");return Any("03","00"+str)};ASN1._toArray=function toArray(next,opts){var typ=opts.json?Enc.numToHex(next.type):next.type;var val=next.value;if(val){if("string"!==typeof val&&opts.json){val=Enc.bufToHex(val)}return[typ,val]}return[typ,next.children.map(function(child){return toArray(child,opts)})]};ASN1._pack=function(arr){var typ=arr[0];if("number"===typeof arr[0]){typ=Enc.numToHex(arr[0])}var str="";if(Array.isArray(arr[1])){arr[1].forEach(function(a){str+=ASN1._pack(a)})}else if("string"===typeof arr[1]){str=arr[1]}else if(arr[1].byteLength){str=Enc.bufToHex(arr[1])}else{throw new Error("unexpected array")}if("03"===typ){return ASN1.BitStr(str)}else if("02"===typ){return ASN1.UInt(str)}else{return Any(typ,str)}};ASN1.pack=function(asn1,opts){if(!opts){opts={}}if(!Array.isArray(asn1)){asn1=ASN1._toArray(asn1,{json:true})}var result=ASN1._pack(asn1);if(opts.json){return result}return Enc.hexToBuf(result)}})(); |
/*! WebAuthn-Local-Client: walc.js | ||
v0.999.7 (c) 2024 Kyle Simpson | ||
v0.1000.0 (c) 2024 Kyle Simpson | ||
MIT License: http://getify.mit-license.org | ||
*/ | ||
import{sodium as e,CBOR as t,ASN1 as r}from"./external.js";const n=[{name:"Ed25519",COSEID:-8,cipherOpts:{name:"Ed25519",hash:{name:"SHA-512"}}},{name:"ES256",COSEID:-7,cipherOpts:{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}}},{name:"RSASSA-PSS",COSEID:-37,cipherOpts:{name:"RSA-PSS",hash:{name:"SHA-256"}}},{name:"RS256",COSEID:-257,cipherOpts:{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}}}],i=Object.fromEntries(n.flatMap((e=>[[e.name,e],[e.COSEID,e]]))),a=Symbol("credential-type"),o=Symbol("reset-abort"),s="undefined"!=typeof navigator&&void 0!==navigator.credentials&&void 0!==navigator.credentials.create&&void 0!==navigator.credentials.get&&"undefined"!=typeof PublicKeyCredential&&void 0!==PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable&&await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),l=s&&void 0!==PublicKeyCredential.isConditionalMediationAvailable&&await PublicKeyCredential.isConditionalMediationAvailable();export{s as supportsWebAuthn,l as supportsConditionalMediation,regDefaults,register,authDefaults,auth,verifyAuthResponse,packPublicKeyJSON,unpackPublicKeyJSON,toBase64String,fromBase64String,toUTF8String,fromUTF8String,o as resetAbortReason};var u={supportsWebAuthn:s,supportsConditionalMediation:l,regDefaults:regDefaults,register:register,authDefaults:authDefaults,auth:auth,verifyAuthResponse:verifyAuthResponse,packPublicKeyJSON:packPublicKeyJSON,unpackPublicKeyJSON:unpackPublicKeyJSON,toBase64String:toBase64String,fromBase64String:fromBase64String,toUTF8String:toUTF8String,fromUTF8String:fromUTF8String,resetAbortReason:o};export default u;async function register(n=regDefaults()){try{if(s){n[n[a]].excludeCredentials=normalizeCredentialsList(n[n[a]].excludeCredentials);let i=await navigator.credentials.create(n),o=new Uint8Array(i.response.clientDataJSON),s=JSON.parse(toUTF8String(o));if("webauthn.create"!=s.type)throw new Error("Invalid registration response");let l=e.to_base64(n[n[a]].challenge,e.base64_variants.URLSAFE_NO_PADDING);if(s.challenge!=l)throw new Error("Challenge not accepted");let u=i.response.getPublicKeyAlgorithm(),c=new Uint8Array(i.response.getPublicKey()),{algo:g,raw:f}=function parsePublicKeySPKI(t){var n=r.parseVerbose(new Uint8Array(t));return{algo:e.to_hex(findValue(n.children[0])),raw:findValue(n.children[1])};function findValue(e){if(e.value&&e.value instanceof Uint8Array)return e.value;if(e.children)for(let t of e.children){let e=findValue(t);if(null!=e)return e}return null}}(c),y=parseAuthenticatorData(void 0!==i.response.getAuthenticatorData?new Uint8Array(i.response.getAuthenticatorData()):t.decode(i.response.attestationObject).authData);if(!checkRPID(y.rpIdHash,n.relyingPartyID))throw new Error("Unexpected relying-party ID");return 0==y.signCount&&delete y.signCount,{request:{credentialType:i.type,...n[n[a]],challenge:toBase64String(n[n[a]].challenge),...Object.fromEntries(Object.entries(s).filter((([e,t])=>["origin","crossOrigin"].includes(e))))},response:{credentialID:toBase64String(new Uint8Array(i.rawId)),credentialType:i.type,authenticatorAttachment:i.authenticatorAttachment,publicKey:{algoCOSE:u,algoOID:g,spki:c,raw:f},...Object.fromEntries(Object.entries(y).filter((([e,t])=>["flags","signCount","userPresence","userVerification"].includes(e)))),raw:i.response}}}throw new Error("WebAuthentication not supported on this device")}catch(e){if(e!=o)throw new Error("Credential registration failed",{cause:e})}}function regDefaults({credentialType:t="publicKey",authenticatorSelection:{authenticatorAttachment:r="platform",userVerification:i="required",residentKey:o="required",requireResidentKey:s=!0,...l}={},relyingPartyID:u=document.location.hostname,relyingPartyName:c="wacg",attestation:g="none",challenge:f=e.randombytes_buf(20),excludeCredentials:y=[],user:{name:d="wacg-user",displayName:p=d,id:h=e.randombytes_buf(5)}={},publicKeyCredentialParams:S=n.map((e=>({type:"public-key",alg:e.COSEID}))),signal:b,...m}={}){var w={[t]:{authenticatorSelection:{authenticatorAttachment:r,userVerification:i,residentKey:o,requireResidentKey:s,...l},attestation:g,rp:{id:u,name:c},user:{name:d,displayName:p,id:h},challenge:f,excludeCredentials:y,pubKeyCredParams:S,...m},...null!=b?{signal:b}:null};return Object.defineProperty(w,a,{enumerable:!1,writable:!1,configurable:!1,value:t}),w}async function auth(t=authDefaults()){try{if(s){t[t[a]].allowCredentials=normalizeCredentialsList(t[t[a]].allowCredentials);let r=await navigator.credentials.get(t),n=new Uint8Array(r.response.clientDataJSON),i=JSON.parse(toUTF8String(n));if("webauthn.get"!=i.type)throw new Error("Invalid auth response");let o=e.to_base64(t[t[a]].challenge,e.base64_variants.URLSAFE_NO_PADDING);if(i.challenge!=o)throw new Error("Challenge not accepted");let s=parseAuthenticatorData(new Uint8Array(r.response.authenticatorData));if(!checkRPID(s.rpIdHash,t.relyingPartyID))throw new Error("Unexpected relying-party ID");0==s.signCount&&delete s.signCount;let l=new Uint8Array(r.response.signature);return{request:{credentialType:r.type,mediation:t.mediation,...t[t[a]],...Object.fromEntries(Object.entries(i).filter((([e,t])=>["origin","crossOrigin"].includes(e))))},response:{credentialID:toBase64String(new Uint8Array(r.rawId)),signature:l,...Object.fromEntries(Object.entries(s).filter((([e,t])=>["flags","signCount","userPresence","userVerification"].includes(e)))),...null!=r.response.userHandle?{userID:new Uint8Array(r.response.userHandle)}:null,raw:r.response}}}throw new Error("WebAuthentication not supported on this device")}catch(e){if(e!=o)throw new Error("Credential auth failed",{cause:e})}}function authDefaults({credentialType:t="publicKey",relyingPartyID:r=document.location.hostname,userVerification:n="required",challenge:i=e.randombytes_buf(20),allowCredentials:o=[],mediation:s="optional",signal:l,...u}={}){var c={[t]:{rpId:r,userVerification:n,challenge:i,allowCredentials:o},mediation:s,...null!=l?{signal:l}:null,...u};return Object.defineProperty(c,a,{enumerable:!1,writable:!1,configurable:!1,value:t}),c}async function verifyAuthResponse({signature:t,raw:{clientDataJSON:n,authenticatorData:a}}={},{algoCOSE:o,spki:s,raw:l}={}){try{if(t&&n&&a&&s&&l&&Number.isInteger(o)){let u=function parseSignature(e,t){if(isPublicKeyAlgorithm("ES256",e)){let e=r.parseVerbose(t);return new Uint8Array([...e.children[0].value,...e.children[1].value])}return t}(o,t),c=await async function computeVerificationData(e,t){var r=await computeSHA256Hash(t),n=new Uint8Array(e.byteLength+r.byteLength);return n.set(new Uint8Array(e),0),n.set(r,e.byteLength),n}(a,n),g=await(isPublicKeyAlgorithm("Ed25519",o)?function verifySignatureSodium(t,r,n,i){if(isPublicKeyAlgorithm("Ed25519",r))try{return e.crypto_sign_verify_detached(n,i,t)}catch(e){return console.log(e),!1}throw new Error("Unrecognized signature for sodium verification")}(l,o,u,c):isPublicKeyAlgorithm("ES256",o)||isPublicKeyAlgorithm("RS256",o)||isPublicKeyAlgorithm("RSASSA-PSS",o)?async function verifySignatureSubtle(e,t,r,n){if(isPublicKeyAlgorithm("ES256",t)||isPublicKeyAlgorithm("RSASSA-PSS",t)||isPublicKeyAlgorithm("RS256",t))try{let a=await crypto.subtle.importKey("spki",e,i[t].cipherOpts,!1,["verify"]);return await crypto.subtle.verify(i[t].cipherOpts,a,r,n)}catch(e){return console.log(e),!1}throw new Error("Unrecognized signature for subtle-crypto verification")}(s,o,u,c):null);if(null==g)throw new Error("Unrecognized signature, failed validation");return g}throw new Error("Auth verification missing required inputs")}catch(e){throw new Error("Auth verification failed",{cause:e})}}function parseAuthenticatorData(e){return{rpIdHash:e.slice(0,32),flags:e[32],userPresence:!(1&~e[32]),userVerification:!(4&~e[32]),signCount:byteArrayTo32Int(e.slice(33,37))}}async function checkRPID(e,t){var r=await computeSHA256Hash(fromUTF8String(t));return e.length>0&&e.byteLength==r.byteLength&&e.toString()==r.toString()}function byteArrayTo32Int(e){if(e.byteLength<4){let t=new Uint8Array(4);t.set(e,4-e.byteLength),e=t}return new DataView(e.buffer).getInt32(0)}async function computeSHA256Hash(e){return new Uint8Array(await window.crypto.subtle.digest("SHA-256",new Uint8Array(e)))}function isPublicKeyAlgorithm(e,t){return i[e]==i[t]}function packPublicKeyJSON(e,t=!1){return e={...e,spki:"string"!=typeof e.spki?toBase64String(e.spki):e.spki,raw:"string"!=typeof e.raw?toBase64String(e.raw):e.raw},t?JSON.stringify(e):e}function unpackPublicKeyJSON(e){var t="string"==typeof e?JSON.parse(e):e;return{...t,spki:"string"==typeof t.spki?fromBase64String(t.spki):t.spki,raw:"string"==typeof t.raw?fromBase64String(t.raw):t.raw}}function normalizeCredentialsList(e){if(Array.isArray(e))return e.map((e=>({...e,id:"string"==typeof e.id?fromBase64String(e.id):e.id})))}function toBase64String(t){return e.to_base64(t,e.base64_variants.ORIGINAL)}function fromBase64String(t){return e.from_base64(t,e.base64_variants.ORIGINAL)}function toUTF8String(t){return e.to_string(t)}function fromUTF8String(t){return e.from_string(t)} | ||
import{sodium as e,CBOR as t,ASN1 as r}from"./external.js";const n=[{name:"Ed25519",COSEID:-8,cipherOpts:{name:"Ed25519",hash:{name:"SHA-512"}}},{name:"ES256",COSEID:-7,cipherOpts:{name:"ECDSA",namedCurve:"P-256",hash:{name:"SHA-256"}}},{name:"RSASSA-PSS",COSEID:-37,cipherOpts:{name:"RSA-PSS",hash:{name:"SHA-256"}}},{name:"RS256",COSEID:-257,cipherOpts:{name:"RSASSA-PKCS1-v1_5",hash:{name:"SHA-256"}}}],i=Object.fromEntries(n.flatMap((e=>[[e.name,e],[e.COSEID,e]]))),a=Symbol("credential-type"),o=Symbol("reset-abort"),s="undefined"!=typeof navigator&&void 0!==navigator.credentials&&void 0!==navigator.credentials.create&&void 0!==navigator.credentials.get&&"undefined"!=typeof PublicKeyCredential&&void 0!==PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable&&await PublicKeyCredential.isUserVerifyingPlatformAuthenticatorAvailable(),l=s&&void 0!==PublicKeyCredential.isConditionalMediationAvailable&&await PublicKeyCredential.isConditionalMediationAvailable();export{s as supportsWebAuthn,l as supportsConditionalMediation,regDefaults,register,authDefaults,auth,verifyAuthResponse,packPublicKeyJSON,unpackPublicKeyJSON,toBase64String,fromBase64String,toUTF8String,fromUTF8String,o as resetAbortReason};var u={supportsWebAuthn:s,supportsConditionalMediation:l,regDefaults:regDefaults,register:register,authDefaults:authDefaults,auth:auth,verifyAuthResponse:verifyAuthResponse,packPublicKeyJSON:packPublicKeyJSON,unpackPublicKeyJSON:unpackPublicKeyJSON,toBase64String:toBase64String,fromBase64String:fromBase64String,toUTF8String:toUTF8String,fromUTF8String:fromUTF8String,resetAbortReason:o};export default u;async function register(n=regDefaults()){try{if(s){n[n[a]].excludeCredentials=normalizeCredentialsList(n[n[a]].excludeCredentials);let i=await navigator.credentials.create(n),o=new Uint8Array(i.response.clientDataJSON),s=JSON.parse(toUTF8String(o));if("webauthn.create"!=s.type)throw new Error("Invalid registration response");let l=e.to_base64(n[n[a]].challenge,e.base64_variants.URLSAFE_NO_PADDING);if(s.challenge!=l)throw new Error("Challenge not accepted");let u=i.response.getPublicKeyAlgorithm(),c=new Uint8Array(i.response.getPublicKey()),{algo:g,raw:f}=function parsePublicKeySPKI(t){var n=r.parseVerbose(t);return{algo:e.to_hex(findValue(n.children[0])),raw:findValue(n.children[1])};function findValue(e){if(e.value&&e.value instanceof Uint8Array)return e.value;if(e.children)for(let t of e.children){let e=findValue(t);if(null!=e)return e}return null}}(c),y=parseAuthenticatorData(void 0!==i.response.getAuthenticatorData?new Uint8Array(i.response.getAuthenticatorData()):t.decode(i.response.attestationObject).authData);if(!checkRPID(y.rpIdHash,n.relyingPartyID))throw new Error("Unexpected relying-party ID");return 0==y.signCount&&delete y.signCount,{request:{credentialType:i.type,...n[n[a]],challenge:toBase64String(n[n[a]].challenge),...Object.fromEntries(Object.entries(s).filter((([e,t])=>["origin","crossOrigin"].includes(e))))},response:{credentialID:toBase64String(new Uint8Array(i.rawId)),credentialType:i.type,authenticatorAttachment:i.authenticatorAttachment,publicKey:{algoCOSE:u,algoOID:g,spki:c,raw:f},...Object.fromEntries(Object.entries(y).filter((([e,t])=>["flags","signCount","userPresence","userVerification"].includes(e)))),raw:i.response}}}throw new Error("WebAuthentication not supported on this device")}catch(e){if(e!=o)throw new Error("Credential registration failed",{cause:e})}}function regDefaults({credentialType:t="publicKey",authenticatorSelection:{authenticatorAttachment:r="platform",userVerification:i="required",residentKey:o="required",requireResidentKey:s=!0,...l}={},relyingPartyID:u=document.location.hostname,relyingPartyName:c="wacl",attestation:g="none",challenge:f=e.randombytes_buf(20),excludeCredentials:y=[],user:{name:d="wacl-user",displayName:p=d,id:h=e.randombytes_buf(5)}={},publicKeyCredentialParams:S=n.map((e=>({type:"public-key",alg:e.COSEID}))),signal:b,...m}={}){var w={[t]:{authenticatorSelection:{authenticatorAttachment:r,userVerification:i,residentKey:o,requireResidentKey:s,...l},attestation:g,rp:{id:u,name:c},user:{name:d,displayName:p,id:h},challenge:f,excludeCredentials:y,pubKeyCredParams:S,...m},...null!=b?{signal:b}:null};return Object.defineProperty(w,a,{enumerable:!1,writable:!1,configurable:!1,value:t}),w}async function auth(t=authDefaults()){try{if(s){t[t[a]].allowCredentials=normalizeCredentialsList(t[t[a]].allowCredentials);let r=await navigator.credentials.get(t),n=new Uint8Array(r.response.clientDataJSON),i=JSON.parse(toUTF8String(n));if("webauthn.get"!=i.type)throw new Error("Invalid auth response");let o=e.to_base64(t[t[a]].challenge,e.base64_variants.URLSAFE_NO_PADDING);if(i.challenge!=o)throw new Error("Challenge not accepted");let s=parseAuthenticatorData(new Uint8Array(r.response.authenticatorData));if(!checkRPID(s.rpIdHash,t.relyingPartyID))throw new Error("Unexpected relying-party ID");0==s.signCount&&delete s.signCount;let l=new Uint8Array(r.response.signature);return{request:{credentialType:r.type,mediation:t.mediation,...t[t[a]],...Object.fromEntries(Object.entries(i).filter((([e,t])=>["origin","crossOrigin"].includes(e))))},response:{credentialID:toBase64String(new Uint8Array(r.rawId)),signature:l,...Object.fromEntries(Object.entries(s).filter((([e,t])=>["flags","signCount","userPresence","userVerification"].includes(e)))),...null!=r.response.userHandle?{userID:new Uint8Array(r.response.userHandle)}:null,raw:r.response}}}throw new Error("WebAuthentication not supported on this device")}catch(e){if(e!=o)throw new Error("Credential auth failed",{cause:e})}}function authDefaults({credentialType:t="publicKey",relyingPartyID:r=document.location.hostname,userVerification:n="required",challenge:i=e.randombytes_buf(20),allowCredentials:o=[],mediation:s="optional",signal:l,...u}={}){var c={[t]:{rpId:r,userVerification:n,challenge:i,allowCredentials:o},mediation:s,...null!=l?{signal:l}:null,...u};return Object.defineProperty(c,a,{enumerable:!1,writable:!1,configurable:!1,value:t}),c}async function verifyAuthResponse({signature:t,raw:{clientDataJSON:n,authenticatorData:a}}={},{algoCOSE:o,spki:s,raw:l}={}){try{if(t&&n&&a&&s&&l&&Number.isInteger(o)){let u=function parseSignature(e,t){if(isPublicKeyAlgorithm("ES256",e)){let e=r.parseVerbose(t);return new Uint8Array([...e.children[0].value,...e.children[1].value])}return t}(o,t),c=await async function computeVerificationData(e,t){var r=await computeSHA256Hash(t),n=new Uint8Array(e.byteLength+r.byteLength);return n.set(new Uint8Array(e),0),n.set(r,e.byteLength),n}(a,n),g=await(isPublicKeyAlgorithm("Ed25519",o)?function verifySignatureSodium(t,r,n,i){if(isPublicKeyAlgorithm("Ed25519",r))try{return e.crypto_sign_verify_detached(n,i,t)}catch(e){return console.log(e),!1}throw new Error("Unrecognized signature for sodium verification")}(l,o,u,c):isPublicKeyAlgorithm("ES256",o)||isPublicKeyAlgorithm("RS256",o)||isPublicKeyAlgorithm("RSASSA-PSS",o)?async function verifySignatureSubtle(e,t,r,n){if(isPublicKeyAlgorithm("ES256",t)||isPublicKeyAlgorithm("RSASSA-PSS",t)||isPublicKeyAlgorithm("RS256",t))try{let a=await crypto.subtle.importKey("spki",e,i[t].cipherOpts,!1,["verify"]);return await crypto.subtle.verify(i[t].cipherOpts,a,r,n)}catch(e){return console.log(e),!1}throw new Error("Unrecognized signature for subtle-crypto verification")}(s,o,u,c):null);if(null==g)throw new Error("Unrecognized signature, failed validation");return g}throw new Error("Auth verification missing required inputs")}catch(e){throw new Error("Auth verification failed",{cause:e})}}function parseAuthenticatorData(e){return{rpIdHash:e.slice(0,32),flags:e[32],userPresence:!(1&~e[32]),userVerification:!(4&~e[32]),signCount:byteArrayTo32Int(e.slice(33,37))}}async function checkRPID(e,t){var r=await computeSHA256Hash(fromUTF8String(t));return e.length>0&&e.byteLength==r.byteLength&&e.toString()==r.toString()}function byteArrayTo32Int(e){if(e.byteLength<4){let t=new Uint8Array(4);t.set(e,4-e.byteLength),e=t}return new DataView(e.buffer).getInt32(0)}async function computeSHA256Hash(e){return new Uint8Array(await window.crypto.subtle.digest("SHA-256",new Uint8Array(e)))}function isPublicKeyAlgorithm(e,t){return i[e]==i[t]}function packPublicKeyJSON(e,t=!1){return e={...e,spki:"string"!=typeof e.spki?toBase64String(e.spki):e.spki,raw:"string"!=typeof e.raw?toBase64String(e.raw):e.raw},t?JSON.stringify(e):e}function unpackPublicKeyJSON(e){var t="string"==typeof e?JSON.parse(e):e;return{...t,spki:"string"==typeof t.spki?fromBase64String(t.spki):t.spki,raw:"string"==typeof t.raw?fromBase64String(t.raw):t.raw}}function normalizeCredentialsList(e){if(Array.isArray(e))return e.map((e=>({...e,id:"string"==typeof e.id?fromBase64String(e.id):e.id})))}function toBase64String(t){return e.to_base64(t,e.base64_variants.ORIGINAL)}function fromBase64String(t){return e.from_base64(t,e.base64_variants.ORIGINAL)}function toUTF8String(t){return e.to_string(t)}function fromUTF8String(t){return e.from_string(t)} |
{ | ||
"name": "@lo-fi/webauthn-local-client", | ||
"description": "Browser-only utils for locally managing WebAuthn (passkey) API", | ||
"version": "0.999.7", | ||
"version": "0.1000.0", | ||
"exports": { | ||
@@ -24,3 +24,3 @@ ".": "./dist/bundlers/walc.mjs", | ||
"dependencies": { | ||
"@yoursunny/asn1": "~0.0.20200718", | ||
"@root/asn1": "~1.0.2", | ||
"cbor-js": "~0.1.0", | ||
@@ -27,0 +27,0 @@ "libsodium": "~0.7.13", |
@@ -268,13 +268,12 @@ # WebAuthn Local Client | ||
To locally run the tests, start the simple static server (no server-side logic): | ||
To instead run the tests locally, first make sure you've [already run the build](#re-building-dist), then: | ||
```cmd | ||
# only needed one time | ||
npm install | ||
npm run test:start | ||
npm test | ||
``` | ||
Then visit `http://localhost:8080/` in a browser. | ||
This will start a static file webserver (no server logic), serving the interactive test page from `http://localhost:8080/`; visit this page in your browser to perform tests. | ||
By default, the `test/test.js` file imports the code from the `src/*` directly. However, to test against the `dist/auto/*` files (as included in the npm package), you can modify `test/test.js`, updating the `/src` in its `import` statement to `/dist` (see the import-map in `test/index.html` for more details). | ||
## License | ||
@@ -281,0 +280,0 @@ |
@@ -9,4 +9,4 @@ /*! | ||
https://www.npmjs.com/package/@yoursunny/asn1 | ||
https://github.com/yoursunny/asn1.js | ||
https://www.npmjs.com/package/@root/asn1 | ||
https://github.com/therootcompany/asn1.js | ||
*/ |
@@ -18,3 +18,3 @@ #!/usr/bin/env node | ||
const NODE_MODULES_DIR = path.join(PKG_ROOT_DIR,"node_modules"); | ||
const ASN1_SRC = path.join(NODE_MODULES_DIR,"@yoursunny","asn1","dist","asn1.all.min.js"); | ||
const ASN1_SRC = path.join(NODE_MODULES_DIR,"@root","asn1","dist","asn1.all.min.js"); | ||
const ASN1_COPYRIGHT_HEADER = path.join(__dirname,"asn1-copyright-header.txt"); | ||
@@ -21,0 +21,0 @@ const CBOR_SRC = path.join(NODE_MODULES_DIR,"cbor-js","cbor.js"); |
@@ -245,3 +245,3 @@ // dynamically load external dependencies (non-bundlers only) | ||
relyingPartyID = document.location.hostname, | ||
relyingPartyName = "wacg", | ||
relyingPartyName = "wacl", | ||
attestation = "none", | ||
@@ -253,3 +253,3 @@ challenge = sodium.randombytes_buf(20), | ||
user: { | ||
name: userName = "wacg-user", | ||
name: userName = "wacl-user", | ||
displayName: userDisplayName = userName, | ||
@@ -535,3 +535,3 @@ id: userID = sodium.randombytes_buf(5), | ||
function parsePublicKeySPKI(publicKeySPKI) { | ||
var der = ASN1.parseVerbose(new Uint8Array(publicKeySPKI)); | ||
var der = ASN1.parseVerbose(publicKeySPKI); | ||
return { | ||
@@ -538,0 +538,0 @@ algo: sodium.to_hex(findValue(der.children[0])), |
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
1620798
7596
283
+ Added@root/asn1@~1.0.2
+ Added@root/asn1@1.0.2(transitive)
- Removed@yoursunny/asn1@~0.0.20200718
- Removed@yoursunny/asn1@0.0.20200718(transitive)