rust-security-framework

repository·main·Indexed 43 days ago

https://github.com/kornelski/rust-security-framework

A Rust wrapper for Apple's Security.framework, providing access to TLS and Keychain services on macOS and iOS. It includes FFI bindings for managing authorization references, code signing validation, CMS encoding/decoding, and security item import/export.

Tokens
28.8K
Snippets
91
Records
106
Agent score
55%

What's inside rust-security-framework

  1. Use the macOS/iOS Security framework in Rust

    main
    The security-framework crate provides Rust bindings to Apple's Security.framework. It enables Rust applications running on macOS and iOS to interact with system-level security services, specifically for managing TLS (Transport Layer Security) and accessing the system Keychain.
  2. Low-level Secure Transport FFI bindings

    main

    This module provides raw FFI bindings to Apple's Secure Transport API, used for managing SSL/TLS contexts, connections, and protocols on macOS and iOS. It exposes types for SSLContextRef, SSLConnectionRef, and various protocol/session constants.

    Warning: These are low-level extern "C" functions. Users should generally use the high-level security-framework crate instead of calling these sys functions directly to ensure memory safety and proper resource management.

  3. Explore the security-framework modules

    main

    The security-framework crate provides high-level Rust wrappers around the macOS/iOS Security Framework. The API is organized into several functional modules. Note that some modules are platform-specific and only available on macOS.

    Core Modules

    • base: Error handling and core types.
    • certificate: Handling of X.509 certificates.
    • cipher_suite: Access to supported cipher suites.
    • identity: Managing identities (certificate + private key pairs).
    • import_export: Importing and exporting security items.
    • item: General security item management.
    • key: Cryptographic key management.
    • os: Operating system integration.
    • passwords: Password management.
    • policy: Security policies.
    • random: Secure random number generation.
    • secure_transport: TLS/SSL secure transport services.
    • trust: Certificate trust evaluation.

    macOS-Specific Modules

    • authorization: Authorization services.
    • cms: Cryptographic Message Syntax (CMS).
    • trust_settings: Managing system-wide trust settings.
  4. How Authorization, Rights, and ItemSets work together

    main

    The macOS Authorization service operates on a hierarchy of abstractions:

    1. Authorization: The primary session/handle (Authorization) representing a specific request for privileges.
    2. Rights: Named permissions (e.g., system.privilege.admin) that the Security Server evaluates.
    3. AuthorizationItemSet: A collection of AuthorizationItems. An item consists of a name (the right) and an optional value (data associated with that right, like a username or a specific configuration).

    To request access, you build an AuthorizationItemSet containing the rights you need, then pass that set to Authorization::new. The Security Server then evaluates these rights against the current user context and policy database, potentially prompting the user if Flags::INTERACTION_ALLOWED is set.

  5. Choose keychain storage location with `Location`

    main

    The Location enum determines where an item is stored. This is used in ItemAddOptions and ItemUpdateOptions.

    • Location::DataProtectionKeychain: The modern, consistent keychain implementation. Required for keys stored in the Secure Enclave. This is the only keychain available on iOS.
    • Location::DefaultFileKeychain: The default file-based keychain. On macOS, this defaults to the Login keychain.
    • Location::FileKeychain(SecKeychain): (macOS only) A specific file-based keychain.
  6. Handle keychain search results with `SearchResult`

    main

    A search operation returns a Vec<SearchResult>. Each SearchResult can be one of the following:

    • SearchResult::Ref(Reference): A reference to a Security Framework object (e.g., SecKey, SecCertificate, SecIdentity, or SecKeychainItem on macOS).
    • SearchResult::Dict(CFDictionary): A dictionary of attributes (returned if .load_attributes(true) was used).
    • SearchResult::Data(Vec<u8>): The raw data of the item (returned if .load_data(true) was used).
    • SearchResult::Other: An unknown representation.

    If you receive a SearchResult::Dict, you can use .simplify_dict() to convert it into a HashMap<String, String> for easier access to common types like CFString, CFData, and CFDate.

  7. Query and manage certificate trust settings

    main

    The TrustSettings struct allows you to access and modify certificate trust settings within specific domains on macOS/iOS. You can iterate over certificates that have explicit trust settings in a domain and query their aggregate TLS trust status.

    Domains

    When creating a TrustSettings instance, specify one of the following domains:

    • Domain::User: Per-user trust settings.
    • Domain::Admin: Locally administered, system-wide trust settings.
    • Domain::System: System trust settings.

    Workflow

    1. Create a TrustSettings instance for a specific Domain.
    2. Use .iter() to discover certificates that have explicit settings in that domain.
    3. Use .tls_trust_settings_for_certificate(&cert) to check the aggregate trust status of a specific certificate for TLS usage.
    use security_framework::trust_settings::{TrustSettings, Domain};
    use security_framework::certificate::SecCertificate;
    
    let settings = TrustSettings::new(Domain::System);
    
    // Iterate over certificates with explicit settings in the System domain
    if let Ok(iter) = settings.iter() {
        for cert in iter {
            match settings.tls_trust_settings_for_certificate(&cert) {
                Ok(Some(status)) => println!("Cert status: {:?}", status),
                Ok(None) => println!("Cert is trusted by default (e.g., in system store)"),
                Err(e) => eprintln!("Error querying cert: {}", e),
            }
        }
    }
  8. Manage cloud synchronization with set_access_synchronized

    main

    Use set_access_synchronized to determine if a password should be stored in the cloud-synchronized keychain or the local-only keychain.

    Important Behavior: Cloud-synchronized and non-synchronized passwords are kept in different stores and are uniquely identified by their synchronization status.

    • Some(true): Affects only the cloud-synchronized store.
    • Some(false): Affects only the local (not cloud-synchronized) store.
    • None:
      • Delete: Deletes from both stores.
      • Get: Returns values from both stores (but only returns one value).
      • Set: Attempts to create a value in the local store first. If successful, it does not update the cloud store. It only updates the cloud store if an existing value in the local store is found.
    // Only affect the cloud-synchronized store
    options.set_access_synchronized(Some(true));
    
    // Affect only the local store
    options.set_access_synchronized(Some(false));
    
    // Affect both (delete) or attempt both (set)
    options.set_access_synchronized(None);
  9. Manage generic passwords on macOS

    main

    You can manage generic passwords (service/account pairs) using the SecKeychain methods or the standalone find_generic_password function.

    • SecKeychain::set_generic_password: Updates an existing password if it exists, or adds a new one if it doesn't.
    • SecKeychain::add_generic_password: Adds a new password without checking for duplicates.
    • find_generic_password: Searches for a password. If keychains is None, it searches the default keychain. If Some(&[SecKeychain]) is provided, it searches only those specific keychains.

    Passwords are returned as SecKeychainItemPassword, which can be accessed as a byte slice &[u8] via .as_ref() or converted to a Vec<u8> via .to_owned().

    // Using the SecKeychain instance method to set/update a password
    keychain.set_generic_password("my-service", "my-account", b"my-password")?;
    
    // Finding a generic password
    let (password, item) = find_generic_password(None, "my-service", "my-account")?;
    let bytes = password.as_ref();
  10. Manage internet passwords on macOS

    main

    Internet passwords (server/account/path/etc.) can be managed via SecKeychain methods or the standalone find_internet_password function.

    • SecKeychain::set_internet_password: Updates an existing internet password or adds a new one.
    • SecKeychain::add_internet_password: Adds a new internet password without checking for duplicates.
    • find_internet_password: Searches for an internet password using specific network parameters like server, path, port, protocol, and authentication_type.
    keychain.set_internet_password(
        "example.com",
        None, // security_domain
        "user",
        "/path",
        Some(443),
        SecProtocolType::HTTPS,
        SecAuthenticationType::Basic,
        b"password"
    )?;
  11. Configure `SecTrust` evaluation options

    main

    You can customize how a SecTrust object evaluates certificates using set_options. This is available on macOS via the TrustOptions bitflags.

    Available flags:

    • ALLOW_EXPIRED: Allow expired certificates (except for the root).
    • ALLOW_EXPIRED_ROOT: Allow expired root certificates.
    • LEAF_IS_CA: Allow CA certificates to act as leaf certificates.
    • FETCH_ISSUER_FROM_NET: Allow network downloads of CA certificates.
    • REQUIRE_REVOCATION_PER_CERT: Require a positive revocation check for each certificate.
    • USE_TRUST_SETTINGS: Use TrustSettings instead of anchors.
    • IMPLICIT_ANCHORS: Treat properly self-signed certificates as anchors implicitly.
    #[cfg(target_os = "macos")]
    use security_framework::trust::TrustOptions;
    
    // Example: Allow expired certificates and network fetching
    trust.set_options(TrustOptions::ALLOW_EXPIRED | TrustOptions::FETCH_ISSUER_FROM_NET)?;