Botan 2.x to 3.x Migration¶
This is a guide on migrating applications from Botan 2.x to 3.0.
This guide attempts to be, but is not, complete. If you run into a problem while converting code that does not seem to be described here, please open an issue on GitHub.
Headers¶
Many headers have been removed from the public API.
In some cases, such as datastor.h
or tls_blocking.h
, the functionality
presented was entirely deprecated, in which case it has been removed.
In other cases (such as loadstor.h
or rotate.h
) the header was really an
implementation header of the library and not intended to be consumed as a public
API. In these cases the header is still used internally, but not installed for
application use.
However in most cases there is a better way of performing the same operations,
which usually works in both 2.x and 3.x. For example, in 3.0 all of the
algorithm headers (such as aes.h
) have been removed. Instead you should
create objects via the factory methods (in the case of AES,
BlockCipher::create
) which works in both 2.x and 3.0
Errata: pk_ops.h
¶
Between Botan 3.0 and 3.2 the public header pk_ops.h
was removed
accidentally. This header is typically required for specialized applications
that interface with dedicated crypto hardware. If you are migrating such an
application, please make sure to use Botan 3.3 or newer.
Build Artifacts¶
For consistency with other platforms the DLL is now suffixed with the library’s major version on Windows as well.
TLS¶
Starting with Botan 3.0 TLS 1.3 is supported. This development required a number of backward-incompatible changes to accomodate the protocol differences to TLS 1.2, which is still supported.
Build modules¶
The build module tls
is now internal and contains common TLS helpers. Users
have to explicitly enable tls12
and/or tls13
. Note that for Botan 3.0 it
is not (yet) possible to exclusively enable TLS 1.3 at build time.
Removed Functionality¶
Functionality removed from the TLS implementation includes
TLS 1.0, 1.1 and DTLS 1.0
DSA ciphersuites
anonymous ciphersuites
SRP ciphersuites
SEED ciphersuites
Camellia CBC ciphersuites
AES-128 OCB ciphersuites
DHE_PSK ciphersuites
CECPQ1 ciphersuites
enum classes¶
The publicly available C++ enums in the TLS namespace are now enum class and their member naming scheme was converted from SHOUTING_SNAKE_CASE to CamelCase.
Callbacks¶
A number of new callbacks were added with TLS 1.3. None of those new callbacks is mandatory to implement by applications, though. Additionally there are a few backward incompatible changes in callbacks that might require attention by some applications:
tls_record_received() / tls_emit_data()¶
Those callbacks now take std::span<const uint8_t> instead of const uint8_t* with a size_t buffer length.
tls_session_established()¶
This callback provides a summary of the just-negotiated connection. It used to have a bool return value letting an application decide to store or discard the connection’s resumption information. This use case is now provided via: tls_should_persist_resumption_information() which might be called more than once for a single TLS 1.3 connection.
tls_session_established is not a mandatory callback anymore but still allows applications to abort a connection given a summary of the negotiated characteristics. Note that this summary is not a persistable Session anymore.
tls_verify_cert_chain()¶
The parameter ocsp_responses, which was previously std::shared_ptr<OCSP::Response>, is now std::optional<OCSP::Response>.
tls_modify_extensions() / tls_examine_extensions()¶
These callbacks now have an additional parameter of type Handshake_Type that identify the TLS handshake message the extensions in question are residing in. TLS 1.3 makes much heavier use of such extensions in a wider range of messages to implement core protocol functionality.
tls_dh_agree() / tls_ecdh_agree() / tls_decode_group_param()¶
These callbacks were used as customization points for the TLS 1.2 key exchange in the TLS client. To allow similar (and more) customizations with the introduction of TLS 1.3, these callbacks were replaced with a more generic approach.
Key agreement is split into two callbacks, namely tls_generate_ephemeral_key() and tls_ephemeral_key_agreement(). Those are used in both clients and servers and in all protocol versions. tls_decode_group_param() is removed as it became obsolete by the replacement of the other two callbacks.
Policy¶
choose_key_exchange_group()¶
The new parameter offered_by_peer identifies the key exchange groups a peer has sent public exchange offerings for (in TLS 1.3 handshakes only). Choosing a key exchange group that is not listed is legal but will result in an additional network round trip (cf. “Hello Retry Request”). In TLS 1.2, this vector is always empty and can be ignored.
session_ticket_lifetime()¶
Now returns std::chrono::seconds rather than a bare uint32_t.
Credentials Manager¶
find_cert_chain(), cert_chain() and cert_chain_single_type()¶
These methods now have a cert_signature_schemes parameter that identifies a list of signature schemes the peer is willing to accept for signatures in certificates. Notably, this does not necessarily mean that the leaf certificate must feature a public key type able to generate one of those schemes.
private_key_for()¶
Applications must now provide a std::shared_ptr<> to the requested private key object instead of a raw pointer to better communicate the implementation’s life-time expectations of this private key object.
Session and Ticket Handling¶
Old (pre-Botan 3.0) sessions won’t load in Botan 3.0 anymore and should be discarded. For applications using Session_Manager_SQL or Session_Manager_SQLite discarding happens automatically on first access after the update.
With Botan 3.0 the session manager now is responsible for stateful session handling (backed by a database) and creation and management of stateless session tickets. The latter was previously handled transparently by the TLS implementation itself.
Therefore, TLS server applications that relied on Botan’s default session management implementations (most notably Session_Manager_SQLite or Session_Manager_In_Memory) are advised to re-evaluate their choice. Have a look at Session_Manager_Hybrid to retain support for both stateful and stateless TLS sessions. TLS client applications may safely keep relying on the above-mentioned default implementations.
Applications implementing their own Session_Manager will need to adapt to the new base class API.
New API of Session Manager¶
TLS 1.3 removed the legacy resumption procedures based on session IDs or session tickets and combined them under the protocol’s Pre-Shared Key mechanism. This new approach allows TLS servers to handle sessions both stateless (as self-contained encrypted and authenticated tickets) and stateful (identified with unique database handles).
To accomodates this flexibility the Session_Manager base class API has changed drastically and is now responsible for creation, storage and management of both stateful sessions and stateless session tickets. Sub-classes therefore gain full control over the session ticket’s structure and content.
API details are documented in the class’ doxygen comments.
The Session Object and its Handle¶
Objects of class Session are not aware of their “session ID” or their “session ticket” anymore. Instead, the new class Session_Handle encapsulates the session’s identifier or ticket and accompanies the Session object where necessary.
Algorithms Removed¶
The algorithms CAST-256, MISTY1, Kasumi, DESX, XTEA, PBKDF1, MCEIES, CBC-MAC, Tiger, CECPQ1, and NewHope have been removed.
All Or Nothing Package Transform¶
This code was deprecated and has been removed.
Exception Changes¶
Several exceptions, mostly ones not used by the library, were removed.
A few others that were very specific (such as Illegal_Point) were replaced by throws of their immediate base class exception type.
The base class of Encoding_Error and Decoding_Error changed from Invalid_Argument to Exception. If you are explicitly catching Invalid_Argument, verify that you do not need to now also explicitly catch Encoding_Error and/or Decoding_Error.
X.509 Certificate Info Access¶
Previously X509_Certificate::subject_info
and issuer_info
could be used
to query information about extensions. This is not longer the case; instead you
should either call a specific function on X509_Certificate
which returns the
same information, or lacking that, iterate over the result of
X509_Certificate::v3_extensions
.
OCSP Response Validation¶
After mitigating CVE-2022-43705 the OCSP response signature validation was refactored. This led to the removal of the OCSP::Response::check_signature() method. If you must validate OCSP responses directly in your application please use the new method OCSP::Response::find_signing_certificate() and OCSP::Response::verify_signature().
Use of enum class
¶
Several enumerations where modified to become enum class
, including
DL_Group::Format
, CRL_Code
, EC_Group_Encoding
, Signature_Format
,
Cipher_Dir
, TLS::Extension_Code
, TLS::Connection_Side
,
TLS::Record_Type
, and TLS::Handshake_Type
In many cases the enumeration values were renamed from SHOUTING_CASE
to
CamelCase
. In some cases where the enumeration was commonly used by
applications (for example Signature_Format
and Cipher_Dir
) the old
enumeration names are retained as deprecated variants.
ASN.1 enums¶
The enum ASN1_Tag
has been split into ASN1_Type
and ASN1_Class
.
Unlike ASN1_Tag
, these new enums are enum class
. The members of the
enums have changed from SHOUTING_CASE
to CamelCase
, eg CONSTRUCTED
is now Constructed
.
Also an important change related to ASN1_Tag::PRIVATE
. This enum value was
incorrect, and actually was used for explicitly tagged context specific values.
Now, ASN1_Class::Private
refers to the correct class, but would lead to a
different encoding vs 2.x’s ASN1_Tag::PRIVATE
. The correct value to use in
3.0 to match ASN1_Tag::PRIVATE
is ASN1_Class::ExplicitContextSpecific
.
Cipher Mode Granularity¶
Previously Cipher_Mode::update_granularity specified the minimum buffer size that must be provided during processing. However the value returned was often much larger than what was strictly required. In particular some modes can easily accept inputs as small as 1 byte, but their update_granularity was much larger to encourage best performance.
Now update_granularity returns the true minimum value, and the new Cipher_Mode::ideal_granularity returns a value which is a multiple of update_granularity sized for good performance.
If you are sizing buffers on the basis of update_granularity consider using ideal_granularity instead. Otherwise you may encounter performance regressions due to creating and processing very small buffers.
“SHA-160” and “SHA1”¶
Previously the library accepted “SHA-160” and “SHA1” alternative names for “SHA-1”. This is no longer the case, you must use “SHA-1”. Botan 2.x also recognizes “SHA-1”.
PointGFp¶
This type is now named EC_Point
X509::load_key¶
Previously these functions returned a raw pointer. They now return a std::unique_ptr
PKCS11_Request::subject_public_key and X509_Certificate::subject_public_key¶
These functions now return a unique_ptr
choose_sig_format removed¶
The freestanding functions choose_sig_format have been removed. Use X509_Object::choose_sig_format
DLIES Constructors¶
Previously the constructors to the DLIES classes took raw pointers, and retained ownership of them. They now consume std::unique_ptrs
Credentials_Manager::private_key_for¶
Previously this function returned a raw pointer, which the Credentials_Manager implementation had to keep alive “forever”, since there was no way for it to know when or if the TLS layer had completed using the returned key.
Now this function returns std::shared_ptr<Private_Key>
OID operator+¶
OID operator+ allowed concatenating new fields onto an object identifier. This was not used at all within the library or the tests, and seems of marginal value, so it was removed.
If necessary in your application, this can be done by retrieving the vector of components from your source OID, push the new element onto the vector and create an OID from the result.
RSA with “EMSA1” padding¶
EMSA1 indicates that effectively the plain hash is signed, with no other padding. It is typically used for algorithms like ECSDA, but was allowed for RSA. This is now no longer implemented.
If you must generate such signatures for some horrible reason, you can pre-hash the message using a hash function as usual, and then sign using a “Raw” padding, which will allow you to sign any arbitrary bits with no preprocessing.
ECDSA/DSA with “EMSA1” padding¶
Previous versions of Botan required using a hash specifier like “EMSA1(SHA-256)” when generating or verifying ECDSA/DSA signatures, with the specified hash. The “EMSA1” was a reference to a now obsolete IEEE standard.
In Botan 3 the “EMSA1” notation is still accepted, but now also it is possible to simply use the name of the hash, eg “EMSA1(SHA-256)” becomes “SHA-256”.
Signature Algorithm OIDs¶
In line with the previous entries, previously Botan used a string like
“ECDSA/EMSA1(SHA-256)” to identify the OID 1.2.840.10045.4.3.2. Now it
uses the string “ECDSA/SHA-256” instead, and does not recognize the
EMSA1 variant at all (for example in OID::from_string
).
Public Key Signature Padding¶
In previous versions Botan was somewhat lenient about allowing the application to specify using a hash which was in fact incompatible with the algorithm. For example, Ed25519 signatures are always generated using SHA-512; there is no choice in the matter. In the past, requesting using some other hash, say SHA-256, would be silently ignored. Now an exception is thrown, indicating the desired hash is not compatible with the algorithm.
In previous versions, various APIs required that the application specify the hash function to be used. In most cases this can now be omitted (passing an empty string) and a suitable default will be chosen.
Discrete Logarithm Key Changes¶
Keys based on the discrete logarithm problem no longer derive from the DL_Scheme_PrivateKey and DL_Scheme_PublicKey classes; these classes have been removed.
Functions to access DL algorithm internal fields (such as the integer value of
the private key using get_x
) have been removed. If you need access to this
information you can use the new get_int_field
function.
The constructors of the DL scheme private keys have changed. Previously, loading
and creating a key used the same constructor, namely one taking arguments
(DL_Group, RandomNumberGenerator&, BigInt x = 0)
and then the behavior of
the constructor depend on if x
was zero (in which case a new key was
created) or otherwise if x
was non-zero then it was taken as the private
key. Now there are two constructors, one taking a random number generator and a
group, which generates a new key, and a second taking a group and an integer,
which loads an existing key.
XMSS Signature Changes¶
The logic to derive WOTS+ private keys from the seed contained in the XMSS
private key has been updated according to the recommendations in
NIST SP 800-208. While signatures created with old private keys are still valid using
the old public key, new valid signatures cannot be created. To still support legacy
private XMSS keys, they can be used by passing WOTS_Derivation_Method::Botan2x
to
the constructor of the XMSS_PrivateKey
.
Private XMSS keys created this way use the old derivation logic and can therefore
generate new valid signatures. It is recommended to use
WOTS_Derivation_Method::NIST_SP800_208
(default) when creating new XMSS keys.
Random Number Generator¶
Fetching a large number of bytes via randomize_with_input() from a stateful
RNG will now incorporate the provided “input” data in the first request to the
underlying DRBG only. This applies to such DRBGs that pose a limit on the number
of bytes per request (most notable HMAC_DRBG
with a 64kB default). Botan 2.x
(erroneously) applied the input to all underlying DRBG requests in such cases.
Applications that rely on a static seed for deterministic RNG output might observe a different byte stream in such cases. As a workaround, users are advised to “mimick” the legacy behaviour by manually pulling from the RNG in “byte limit”-sized chunks and provide the “input” with each invocation.