Class Relations
- class: ::nx::EnsembleObject
![[i]](/resources/acs-subsite/ZoomIn16.gif)
::nx::EnsembleObject create ::webauthn::WebAuthn::slot::__reg
Methods (to be applied on the object)
attestation_verify (scripted)
set return_url [dict get $st return_url]
set user_id [dict get $st user_id]
if {![dict exists $req response clientDataJSON]
|| ![dict exists $req response attestationObject]} {
throw {validation fields-missing} "missing required fields"
}
:assert_clientdata_json -clientData_raw [dict get $req response clientDataJSON] -expected_type "webauthn.create" -expected_challenge [dict get $st challenge] -expected_origin [dict get $st origin]
set attObj_b64u [dict get $req response attestationObject]
set attObj_bin [ns_base64urldecode -binary -- $attObj_b64u]
try {
set ao [ns_cbor parse -binary -encoding binary $attObj_bin]
} on error {e} {
throw {validation attobj-cbor} "bad attestationObject CBOR: $e"
}
if {![dict exists $ao fmt] || ![dict exists $ao authData]} {
throw {validation attobj-invalid} "attestationObject missing fmt/authData"
}
set fmt [dict get $ao fmt]
if {$fmt ne "none"} {
ns_log notice "registration attestation fmt=$fmt"
}
set authData [dict get $ao authData]
if {[string length $authData] < 55} {
throw {validation authdata-invalid} "authData too short"
}
set rpIdHash [string range $authData 0 31]
:assert_rpidhash -rpIdHash $rpIdHash -rpId ${:rp_id} -context "attestation"
binary scan [string range $authData 32 32] cu flags
if {($flags & 0x40) == 0} {
throw {validation attesteddata-missing} "no attested credential data"
}
binary scan [string range $authData 33 36] Iu signCount
set aaguid [string range $authData 37 37+15]
binary scan [string range $authData 53 53+1] S credIdLen
if {$credIdLen <= 0 || (55+$credIdLen) > [string length $authData]} {
throw {validation credidlen-invalid} "credentialId length out of range"
}
set credId [string range $authData 55 54+$credIdLen]
set coseKey [string range $authData 55+$credIdLen end]
try {
set cose [ns_cbor parse -binary -encoding binary $coseKey]
} on error {e} {
throw {validation cose-cbor} "bad COSE_Key CBOR: $e"
}
ns_log notice "DEBUG reg attestation_verify: COSE key parsed: $cose"
if {![dict exists $cose 1] || ![dict exists $cose 3]} {
throw {validation key-invalid} "COSE key missing kty/alg"
}
set kty [dict get $cose 1]
set alg [dict get $cose 3]
switch -exact -- "${kty}/${alg}" {
"1/-8" {
if {![dict exists $cose -1] || ![dict exists $cose -2]} {
throw {validation key-invalid} "OKP COSE key missing curve/public key"
}
switch -- [dict get $cose -1] {
6 - 7 {
}
4 - 5 {
throw {validation curve-unsupported} "OKP curve is for key agreement, not signatures"
}
default {
throw {validation curve-unsupported} "unsupported OKP curve \"[dict get $cose -1]\""
}
}
}
"2/-7" {
if {![dict exists $cose -1] || [dict get $cose -1] != 1} {
throw {validation curve-unsupported} "unsupported COSE crv (expected 1 P-256)"
}
}
"3/-257" {
if {![dict exists $cose -1] || ![dict exists $cose -2]} {
throw {validation key-invalid} "RSA COSE key missing modulus/exponent"
}
}
default {
throw {validation alg-unsupported} "unsupported COSE key type / algorithm ($kty/$alg)"
}
}
set credential_id [ns_base64urlencode -binary -- $credId]
set public_key [dict create format cose fmt $fmt aaguid_b64u [ns_base64urlencode -binary -- $aaguid] kty [dict get $cose 1] alg [dict get $cose 3] cose_b64u [ns_base64urlencode -binary -- $coseKey] sign_count $signCount rp_id ${:rp_id}]
if {[dict exists $cose -1]} {
dict set public_key crv [dict get $cose -1]
}
return [dict create user_id $user_id return_url $return_url credential_id $credential_id public_key $public_key origin [dict get $st origin] sign_count $signCount]