fn create_inner()

in key/aziot-keys/src/key.rs [559:683]


fn create_inner(
    locations: &[crate::implementation::Location],
    create_method: CreateMethod<'_>,
    usage: crate::AZIOT_KEYS_KEY_USAGE,
) -> Result<(), crate::AZIOT_KEYS_RC> {
    for location in locations {
        match location {
            crate::implementation::Location::Filesystem(path) => {
                let result = match create_method {
                    CreateMethod::Generate => {
                        // Filesystem uses AES-256-GCM for encryption keys and HMAC-SHA256 for hash keys,
                        // so it uses 256-bit == 32-byte keys for both.

                        let mut bytes = vec![0_u8; 32];
                        openssl::rand::rand_bytes(&mut bytes)?;
                        std::fs::write(path, bytes)
                    }

                    CreateMethod::Import(bytes) => std::fs::write(path, bytes),
                };
                let () = result.map_err(crate::implementation::err_external)?;
                return Ok(());
            }

            crate::implementation::Location::Pkcs11 { lib_path, uri } => {
                let usage = match usage {
                    #[allow(unreachable_patterns)] // DERIVE and SIGN are the same constant
                    crate::AZIOT_KEYS_KEY_USAGE_DERIVE | crate::AZIOT_KEYS_KEY_USAGE_SIGN => {
                        pkcs11::KeyUsage::Hmac
                    }
                    crate::AZIOT_KEYS_KEY_USAGE_ENCRYPT => pkcs11::KeyUsage::Aes,
                    _ => {
                        return Err(crate::implementation::err_invalid_parameter(
                            "usage",
                            "unrecognized value",
                        ))
                    }
                };

                let pkcs11_context = pkcs11::Context::load(lib_path.clone())
                    .map_err(crate::implementation::err_external)?;
                let pkcs11_slot = pkcs11_context
                    .find_slot(&uri.slot_identifier)
                    .map_err(crate::implementation::err_external)?;
                let pkcs11_session = pkcs11_context
                    .open_session(pkcs11_slot, uri.pin.clone())
                    .map_err(crate::implementation::err_external)?;

                let pkcs11_object = match create_method {
                    CreateMethod::Generate => {
                        let result = pkcs11_session
                            .clone()
                            .generate_key(uri.object_label.as_deref(), usage);

                        #[allow(clippy::unnested_or_patterns)]
                        match result {
                            Ok(pkcs11_key) => pkcs11_key,

                            Err(pkcs11::GenerateKeyError::GenerateKeyFailed(
                                pkcs11_sys::CKR_FUNCTION_NOT_SUPPORTED,
                            )) |
                            // Some PKCS#11 implementations like Cryptoauthlib don't support `C_GenerateKey(CKM_GENERIC_SECRET_KEY_GEN)`
                            Err(pkcs11::GenerateKeyError::GenerateKeyFailed(
                                pkcs11_sys::CKR_MECHANISM_INVALID,
                            )) => continue,

                            Err(err) => return Err(crate::implementation::err_external(err)),
                        }
                    }

                    CreateMethod::Import(bytes) => {
                        // TODO: Verify if CAL actually smashes the stack for keys that are too large,
                        // and if not, if it returns a better error than CKR_GENERAL_ERROR
                        let result = pkcs11_session.clone().import_key(
                            bytes,
                            uri.object_label.as_deref(),
                            usage,
                        );
                        match result {
                            Ok(pkcs11_key) => pkcs11_key,

                            // No better error from some PKCS#11 implementations like Cryptoauthlib than CKR_GENERAL_ERROR
                            Err(pkcs11::ImportKeyError::CreateObjectFailed(_)) => continue,

                            Err(err) => return Err(crate::implementation::err_external(err)),
                        }
                    }
                };

                if let pkcs11::KeyUsage::Aes = usage {
                    // At this point we've successfully created an AES key but we don't know if the token supports AES-GCM specifically or not.
                    // So we do a dummy encryption.

                    let iv: &[u8; 12] = b"123456789012";
                    let aad = b"someaad";
                    let plaintext = b"someplaintext";
                    // We can use the block size of AES-256-GCM from openssl even though the token didn't necessarily use AES-256-GCM,
                    // because all AES ciphers have the same block size by definition.
                    let cipher = openssl::symm::Cipher::aes_256_gcm();
                    let mut ciphertext = vec![0_u8; plaintext.len() + cipher.block_size() + 16];

                    if pkcs11_object
                        .encrypt(iv, aad, plaintext, &mut ciphertext)
                        .is_ok()
                    {
                        return Ok(());
                    }

                    // Delete the object we just created...
                    if let Some(object_label) = &uri.object_label {
                        let _ = pkcs11_session.clone().delete_key(object_label);
                    }

                    // ... and continue to the next location.
                } else {
                    return Ok(());
                }
            }
        }
    }

    Err(crate::implementation::err_external(
        "no valid location for symmetric key",
    ))
}