notnuyy
notnuyy

Reputation: 121

how to extract public key from auth data webauth in Rust and verify it?

i have stucked to extract the Auth Data from Webauth api attention into Public Key

for this case i am using coset, openssl, ciborium to extract the public key and cose key

here is how i implement to extract public key from cose

pub(super) fn extract_public_key_from_cose(&self, cose_key_value: ciborium::value::Value) -> Option<Vec<u8>> {
        if let ciborium::value::Value::Map(cose_key_map) = cose_key_value {
            let mut cose_key_builder: CoseKeyBuilder = CoseKeyBuilder::new();
            for (key, value) in cose_key_map {
                if let ciborium::value::Value::Integer(param) = key {
                    if param == Integer::from(1) {
                        if let ciborium::value::Value::Integer(kty) = value {
                            cose_key_builder = cose_key_builder.kty(KeyType::Assigned(iana::KeyType::from_i64(kty.try_into().ok()?)?));
                        }
                    } else if param == Integer::from(3) {
                        if let ciborium::value::Value::Integer(alg) = value {
                            cose_key_builder = cose_key_builder.algorithm(iana::Algorithm::from_i64(alg.try_into().ok()?)?);
                        }
                    } else if param == Integer::from(-2) {
                        if let ciborium::value::Value::Bytes(x) = value {
                            cose_key_builder = cose_key_builder.param(-2, ciborium::value::Value::Bytes(x));
                        }
                    } else if param == Integer::from(-3) {
                        if let ciborium::value::Value::Bytes(y) = value {
                            cose_key_builder = cose_key_builder.param(-3, ciborium::value::Value::Bytes(y));
                        }
                    }
                }
            }

            let cose_key: CoseKey = cose_key_builder.build();
            let cbor_key: Vec<u8> = cose_key.to_cbor_value().unwrap().to_vec().unwrap();

            return Some(cbor_key);
        }

        None
    }

and using that extract on this function

pub(super) fn extract_public_key(&self, auth_data: &[u8]) -> Option<Vec<u8>> {
        // The length of the authenticator data is at least 37 bytes
        if auth_data.len() < 37 {
            return None;
        }

        // The attestedCredentialData starts at byte 37
        let attested_credential_data = &auth_data[37..];

        // The AAGUID is 16 bytes, and the credential ID length is 2 bytes
        let aaguid_length = 16;
        let credential_id_length = 2;

        // Ensure there is enough data for the AAGUID and credential ID length
        if attested_credential_data.len() < aaguid_length + credential_id_length {
            return None;
        }

        // Extract the credential ID length
        let credential_id_length = u16::from_be_bytes([
            attested_credential_data[aaguid_length],
            attested_credential_data[aaguid_length + 1],
        ]) as usize;

        // Ensure there is enough data for the credential ID and public key
        let public_key_start = aaguid_length + 2 + credential_id_length;
        if attested_credential_data.len() <= public_key_start {
            return None;
        }
        // Extract the public key in COSE format
        let public_key_cose = &attested_credential_data[public_key_start..];

        // Manually deserialize the COSE key
        let mut cursor = Cursor::new(public_key_cose);
        let cose_key_value: ciborium::Value = match ciborium::from_reader(&mut cursor) {
            Ok(value) => value,
            Err(e) => {
                println!("Failed to deserialize public_key_cose: {:?}", e);
                return None;
            }
        };

        // Extract the public key from the COSE key
        self.extract_public_key_from_cose(cose_key_value)
    }

and than i did the core thing here

// Extract the public key from authData
        let public_key = if let ciborium::Value::Bytes(ref auth_data_bytes) = auth_data {
            self.extract_public_key(auth_data_bytes)
        } else {
            return Err("Invalid authData format".into());
        };

    let auth_data_bytes = if let ciborium::Value::Bytes(auth_data_bytes) = auth_data {
                    auth_data_bytes.clone()
                } else {
                    println!("Invalid authData format");
                    return Err("Invalid authData format".into());
                };

                let mut signature_base = Vec::new();
                signature_base.extend_from_slice(&auth_data_bytes);
                signature_base.extend_from_slice(client_data_json);

 // Verify the signature using the public key
return self.verify_signature_with_public_key(sig, &signature_base, &public_key.unwrap(), alg);

 let public_key = openssl::pkey::PKey::public_key_from_der(public_key)?;
        let message_digest = match alg {
            -7 => openssl::hash::MessageDigest::sha256(),
            -35 => openssl::hash::MessageDigest::sha384(),
            -36 => openssl::hash::MessageDigest::sha512(),
            _ => return Err("Unsupported algorithm".into()),
        };

        let mut verifier = openssl::sign::Verifier::new(message_digest, &public_key)?;
        verifier.update(data)?;
        Ok(verifier.verify(&sig)?)

i got error this

error:068000A8:asn1 encoding routines:asn1_check_tlen:wrong 
tag:crypto/asn1/tasn_dec.c:1194:, error:0688010A:asn1 encoding 
routines:asn1_item_embed_d2i:nested asn1 error:crypto/asn1/tasn_dec.c:349:Type=X509_PUBKEY

did i wrong implementation ? or how i extract_public_key_from_cose was wrong to be DER format ?

the error is from this:

 let public_key = openssl::pkey::PKey::public_key_from_der(public_key)?;

Upvotes: 0

Views: 70

Answers (1)

agl
agl

Reputation: 1682

I don't know these libraries, but the core seems to be:

let cbor_key: Vec<u8> = cose_key.to_cbor_value().unwrap().to_vec().unwrap();

So it looks like you're parsing a COSE key and then reencoding it in COSE format. public_key_from_der probably wants a public key in SubjectPublicKeyInfo format.

Perhaps the libraries that you're using offer a way to do that conversion, but if you're dealing only with COSE algorithm -7, then you can check the kty (with key 1) is 2, that the alg (with key 3) is -7, and that the curve (with key -1) is 1. If all that's true then it's algorithm -7 and you can take 32-byte x and y strings from keys -2 and -3. Then the SPKI can be formed by concatenating 3059301306072a8648ce3d020106082a8648ce3d03010703420004 followed by the x and y values.

Upvotes: 0

Related Questions