BoringSSL Documentation

repository·main·Indexed 24 days ago

https://github.com/google/boringssl

BoringSSL is a fork of OpenSSL maintained by Google and used in projects such as Chrome, Chromium, and Android. It prioritizes modern security and performance over long-term API/ABI stability and is not intended for general use by third parties. The documentation covers NID and OID generation, security advisories, and Rust bindings via the bssl-crypto and bssl-sys crates.

Tokens
97K
Snippets
203
Records
485
Agent score
81%

What's inside BoringSSL

  1. Overview of the Googletest Mocking (gMock) Framework

    main

    gMock is a C++ framework for creating and using mock classes. It is designed to help developers derive better system designs and write more effective tests by providing a declarative syntax for defining mocks and controlling their behavior.

    Key features include:

    • Declarative Mock Definition: Define mock objects using a specialized syntax.
    • Partial (Hybrid) Mocks: Create objects that combine real implementation with mocked behavior.
    • Argument Validation: Uses a rich set of matchers to validate function arguments.
    • Behavior Control: Intuitive syntax for specifying how mocks should respond to calls.
    • Automatic Verification: Automatically verifies expectations without requiring a manual record-and-replay step.
    • Ordering Constraints: Supports expressing arbitrary (partial) ordering constraints on function calls.
    • Extensibility: Users can define custom matchers and actions.
    • No Exceptions: Designed to work without relying on C++ exceptions.
    • Type Support: Handles functions of arbitrary types and overloaded functions.
  2. Key features of GoogleTest

    main

    GoogleTest provides several advanced testing capabilities:

    • Test discovery: Automatically discovers and runs tests without manual registration.
    • Rich assertions: Includes equality, inequality, exception testing, and more.
    • User-defined assertions: Allows creating custom assertions specific to your domain.
    • Death tests: Verifies that code exits in a specific way (useful for error-handling).
    • Fatal and non-fatal failures: Control whether a failure stops the current test or allows it to continue.
    • Parameterized tests:
      • Value-parameterized tests: Run the same test multiple times with different input values.
      • Type-parameterized tests: Run tests with different data types.
    • Execution options: Supports running individual tests, specifying test order, and running tests in parallel.
  3. Learn Googletest testing patterns via samples

    main

    The following testing patterns are demonstrated in the Googletest sample directory:

    • Basic Testing: Testing simple C++ functions (Sample #1).
    • Class Testing: Unit testing classes with multiple member functions (Sample #2).
    • Test Fixtures: Using testing::Test to share setup/teardown logic (Sample #3).
    • Fixture Inheritance: Using a base test fixture to provide shared logic to derived fixtures (Sample #5).
    • Type-Parameterized Tests: Running the same test logic against different types (Sample #6).
    • Value-Parameterized Tests: Running tests with different input values, including the use of Combine() to create Cartesian products of parameters (Samples #7 and #8).
    • Listener and Reflection APIs: Using the listener API to modify console output or implement custom tools like a memory leak checker, and using the reflection API to inspect test results (Samples #9 and #10).
  4. Access BoringSSL security advisories

    main

    BoringSSL maintains an archive of security advisories in the docs/advisories/ directory. This archive includes:

    • Advisories issued directly by the BoringSSL team.
    • Counterparts to OpenSSL advisories that detail how specific vulnerabilities impact BoringSSL.

    Note: For OpenSSL advisories, BoringSSL will publish a corresponding document even if the advisory does not impact BoringSSL, explicitly stating that it has no impact.

  5. What is a mock object and how does it differ from a fake?

    main

    A mock object implements the same interface as a real object but allows you to specify its behavior and expectations at runtime (e.g., which methods are called, in what order, with what arguments, and what they return). Mocks are used to verify the interaction between the code under test and its dependencies.

    It is important to distinguish mocks from fakes:

    • Fakes have working implementations but use shortcuts (like an in-memory file system) to be faster or simpler, making them unsuitable for production.
    • Mocks are pre-programmed with expectations that form a specification of the calls they are expected to receive.
  6. Rules for generating failures in listeners

    main

    You can use failure-raising macros (EXPECT_*(), ASSERT_*(), FAIL(), etc.) within event listeners, but you must follow these restrictions to avoid infinite recursion or incorrect attribution:

    1. No failures in OnTestPartResult(): You cannot generate a failure inside this method, as it would trigger a recursive call to OnTestPartResult().
    2. Listener ordering:
      • Listeners that handle OnTestPartResult() should be placed before listeners that can generate failures. This ensures that failures generated by the latter are correctly attributed to the current test by the former.
      • On*Start() and OnTestPartResult() events are received in the order listeners appear in the list.
      • On*End() events are received in reverse order.
  7. Choose between typed tests and value-parameterized tests

    main

    When testing different implementations of the same interface, choose based on your setup requirements:

    • Use Typed Tests if:

      • Instances of different implementations can be created similarly (e.g., via a common default constructor or a templated factory function like CreateInstance<TypeParam>()).
      • You want the default output to include the specific type name when a test fails.
      • Note: You must ensure you are testing against the interface type (e.g., using implicit_cast<MyInterface*>(my_concrete_impl)) rather than just the concrete type.
    • Use Value-Parameterized Tests if:

      • You need different code patterns to create instances (e.g., new Foo vs new Bar(5)).
      • It is easier to wrap differences in factory function pointers passed as parameters.
      • Note: By default, these only show the iteration number on failure. To get useful output, define a function that returns the iteration name and pass it as the third parameter to INSTANTIATE_TEST_SUITE_P.
  8. Where to define mock classes

    main

    Deciding where to place your mock class depends on ownership of the interface being mocked:

    • If you own the interface: You can define the mock class directly within your test file (_test.cc).
    • If you do NOT own the interface:
      • Recommended: Define the mock class within the original interface's package (e.g., in a testing sub-directory). Provide it via a .h file and a cc_library with testonly=True. This ensures that if the interface changes, there is only one MockFoo to update, and only tests depending on the changed methods need fixing.
      • Alternative (Adaptor Pattern): Introduce a thin Adaptor layer on top of the external interface. Since you own the Adaptor, you can manage changes to the external dependency more easily and tailor the interface to your specific domain.
  9. Handling callbacks with state in BoringSSL

    main

    BoringSSL's C-based APIs do not support closures (functions with bound variables). To pass application-specific state to a callback, you must use one of two patterns depending on the API signature:

    1. APIs with an explicit arg parameter: These APIs provide a void *arg parameter that is passed back to your callback. You can use this to pass a pointer to a C++ object, such as a std::function.
    2. APIs without an explicit arg parameter: These APIs expect the callback to retrieve state from the object being operated on (e.g., an SSL object). In these cases, use ex_data to associate your application state with the BoringSSL object.

    Important: You must ensure that any state passed via arg or ex_data outlives the BoringSSL object to avoid use-after-free errors.

    // Pattern 1: Using an explicit 'arg' parameter with std::function
    int RunCertCallback(SSL *ssl, void *arg) {
      auto *f = static_cast<std::function<int(SSL*)>*>(arg);
      return (*f)(ssl);
    }
    
    std::function<int(SSL*)> cert_cb = ...;
    SSL_CTX_set_cert_cb(ctx, RunCertCallback, &cert_cb);
  10. Avoid parsing print function output for ASN.1 strings

    main

    Due to how ASN1_STRING handles NUL bytes, functions that print ASN.1 data may truncate values if an interior NUL byte is encountered. This can lead to misinterpreting names in a certificate.

    Best Practice: Do not rely on parsing the output of print functions to extract data. Instead, use the appropriate programmatic APIs to access certificate or request fields to ensure you receive the full, un-truncated value.

  11. Shard tests across multiple machines

    main

    GoogleTest supports test sharding to distribute tests across multiple machines (shards) for parallel execution. To implement this, your runner must:

    1. Set GTEST_TOTAL_SHARDS to the total number of machines (same for all shards).
    2. Set GTEST_SHARD_INDEX to the unique index of the current machine (range [0, GTEST_TOTAL_SHARDS - 1]).
    3. Run the same test program on all shards.

    GoogleTest will automatically select a subset of tests for each shard so that every test is run exactly once across the entire cluster.

    To detect if a test program supports sharding, the runner can set GTEST_SHARD_STATUS_FILE to a non-existent path; the program will create this file if it supports the protocol.

  12. Use CRYPTO_BUFFER for efficient certificate handling

    main

    To reduce memory overhead and avoid duplicating certificate data in memory, BoringSSL provides a buffer-based approach instead of using standard OpenSSL X509 structures. This is highly recommended for applications making many TLS connections.

    Key Components

    • CRYPTO_BUFFER: An opaque byte string representing certificate data.
    • CRYPTO_BUFFER_POOL: An intern table that ensures only a single copy of any given byte string is kept in a pool, enabling deduplication across connections and SSL_CTXs.

    Implementation Steps

    1. Use Buffer-based Methods: Use TLS_with_buffers_method to return an SSL_METHOD that avoids creating X509 objects.
    2. Install a Pool: Use SSL_CTX_set1_buffer_pool to install a pool on an SSL_CTX for deduplication.
    3. Avoid X509 APIs: When using buffers, do not call functions that deal with X509 or X509_NAME objects (e.g., SSL_get_peer_certificate or SSL_get_peer_cert_chain). Doing so will trigger an assert in debug mode or return NULL in release mode.
    4. Use Buffer Alternatives: Use buffer-based alternatives like SSL_get0_peer_certificates (check ssl.h for specific functions taking/returning CRYPTO_BUFFER).
    5. Implement Custom Verification: Because auto-chaining is disabled when using buffers, you must implement your own certificate verification using SSL_[CTX_]set_custom_verify, otherwise all connections will fail.

    Using these APIs allows you to eventually eliminate the OpenSSL X.509 and ASN.1 code from your binary if linked statically.