This API allows a product to register an extra Root Certificate Repository that Opera's native SSL implementation can use to validate certificates.
At present there are implementation APIs only defined for the OpenSSL mode of Opera's libssl module.
The libssl module object contain the member LibSSL::external_repository, which is a list of all registred repositories. The items in this list are processed in order when a lookup in Opera's own repository fails to provide a known authority certificate for an issuer.
The first provider to return a certificate will be used.
The product may temporarily, or permanently disable the local repository using the function g_ssl_api->SetDisableInternalLookupRepository(). Disabling the repository will also disable certificate warnings, and any failure to locate a trusted certificate issuer will result in a fatal TLS negotiation error being reported to the server, and the connection is then closed, and if appropriate, the error reported to the user.
The fundamental base class, SSL_External_CertRepository_Base is defined in the includefile "modules/libssl/ext_certrepository.h". Only implementations starting from fundamentals will need to reference this class or header file.
The repository will be automatically unregistered when destroyed, and if there are no more repositories registered the local repository will be automatically reactivated if it was disabled.
Sets the library_type enum informing the libssl module what crypto library the API works with. Currently only SSL_External_CertRepository_Base::OpenSSL is defined
This function returns the repository's library type.
This function is called by implementations that need to asynchronously retrieve a certificate when the certificate is available, or the operation failed. If the operation failed then the implementation MUST NOT attempt another retrieval immediately, but MUST wait at least several seconds (60 is recommended) before reactivating the attempts to retrieve the certificate.
Registers the repository in the module's list of repositories.
Removes the repository from the module's list of repositories.
Insert the provided repository into the list of repositories. Does not take ownership of the object. Unregistration is done using the Unregister() function in the repository, or by deleting the repository object.
If the provided value is TRUE, only the registered repository, not the local repository, will be used to look up certificate issuers. This will also disable UI certificate warnings, and in cases where a certificate is not signed by an issuer in the external repositories, this will be reported as a handshake error due to unknown issuers.
The current version of libssl uses the OpenSSL crypto library for all certificate actions. This API is defined in the include file "modules/libssl/external/openssl/opeay_certrepository.h"
The OpenSSL_External_Repository provide the basic API, as well as some utility functions, to look up certificates in a repository, and OpenSSL_External_Trusted_Repository_Table class provides the API for using a simple table of DER (binary) encoded certificates as the source for the certificates.
Implementors should note that while the API is OpenSSL based, the actual repository the implementation is using does not have to be based on OpenSSL, but in such cases the implementation must convert the provided datastructures to/from X509 to the repository's representation using binary arrays. Utilities for this, including checking for whether the certificate was issued by the candidate is provided in the API
The API allows the provider to asynchronously retrieve a certificate that is not in the repository (all such retrievals should be non-blocking).
There is also nothing that prevents the implementation to perform independent verification of the certificate, but in such cases a negative result cannot be communicated back to Opera. Provided that the verification code uses OpenSSL, the implementation can do such verification on a copy of the context and certificate provided, and in such cases a successful signature verification for each certificate will be registered in the X509 objects, and Opera will not duplicate the signature verification.
This is the function called by Opera's SSL code to retrieve the issuer for the provided certificate. It provides the location to store the returned X509 object of the issuer certificate, and the current certificate being verified and the context in which it is done.
The function has the same API as the OpenSSL X509_STORE_CTX::get_issuer function.
The implementation may return -1 to indicate an asynchronous lookup event, in which case errors MUST NOT be registered in the OpenSSL error system. When the Lookup is completed or times out (timeout should be less than 20 seconds) the function SignalLookupCompleted must be called. If the lookup operation failed, then the implementation MUST wait several seconds (minimum 60 is recommended) before attempting a new lookup. Opera may abort the lookup without notifying the implementation, for example if the connection is closed. Implementations MUST tolerate multiple lookups for the same certificate while doing a dalayed lookup.
Converts a DER encoded certificate stored in a binary buffer to an X509 object. The caller must use X509_free() to release it.
Converts an X509 object to a DER encoded certificate stored in an allocated binary buffer. The caller must free the buffer using OPENSSL_free().
Checks if the candidate certificate issued the certificate currently being investigated
Checks if the candidate certificate (stored as a DER encoded binary buffer) issued the certificate currently being investigated, and if so returns the allocated X509 object. The caller must use X509_free() to release it. Return values are the same as for the LookupCertificate function.
This repository implementation is intialized with a table of certificate arrays, each array containing a single DER encoded certificate.
The structure (External_RepositoryItem) of the table entries is defined in "modules/libssl/repository_item.h".
Allocates and constructs a repository using the provided table of certificates.
Initializes the object with the repository in the table provided by the caller.
Loops through the repository list to find the issuer of the certificate, if it's in the list.
#include "modules/libssl/sslbase.h"
#include "modules/libssl/ssl_api.h"
#include "modules/libssl/external/openssl/eayhead.h"
#include "modules/libopeay/openssl/x509.h"
#include "modules/libssl/external/openssl/opeay_certrepository.h"
class MyCertrepository : OpenSSL_External_Repository
{
private:
// Internal structures
public:
// Constructor and destructor
// The lookup function called by the Opera verification code
virtual int LookupCertificate(X509 **issuer, X509_STORE_CTX *ctx, X509 *x)
{
OP_ASSERT(issuer) // Pointer to Return location
OP_ASSERT(ctx) // Context for the verification, contains the entire chain and utility functions
OP_ASSERT(x) // The certificate whose issuer we want to find
// Using a temporary variable to be on the safe side
X509 *temp_issuer = NULL;
// Call the specific lookup repository. The API used here is based on OpenSSL, and may vary
// return values are represented as one of
// FoundCertificate // A certificate was found and an X509 certificate is returned in temp_issuer
// WaitHaveToDownload // A certificate is known, but have to be downloaded first, will call SignalLookupCompleted when finished
// DidNotFindAMatch // No issuer for the certificate in x
int return_status = ProductSpecificLookupFunction(&temp_issuer, ctx, x);
switch(return_status)
{
case WaitHaveToDownload:
OP_ASSERT(ERR_peek_error() == 0); // No errors allowed
OP_ASSERT(temp_issuer == NULL); // No certificate returned
return -1; // return code indicating (in combination with no error) that a delayed lookup is in progress
case FoundCertificate:
OP_ASSERT(ERR_peek_error() == 0); // No errors allowed
OP_ASSERT(temp_issuer != NULL); // Must exist
OP_ASSERT(ctx->check_issued(x, temp_issuer)); // If correct certificate this will be true
*issuer = temp_issuer; // return the issuer
temp_issuer = NULL;
return 1; // Return "Yes we found one"
case DidNotFindAMatch:
OP_ASSERT(ERR_peek_error() == 0);// No errors allowed
OP_ASSERT(temp_issuer == NULL);// No certificate returned
return 0; // Return "Not found" status
case EncounteredError:
default: // Just to be on the safe side
OP_ASSERT(ERR_peek_error() != 0); // There must be an error
OP_ASSERT(temp_issuer == NULL);// No certificate returned
return -1; // Return error code
}
}
// The listener must call this when an asynchronous lookup completes, or times out
void RemoteLookupCompleted()
{
SignalLookupCompleted(); // Pass on signal to certificate verifier
}
};
// Construct the repository object
MyCertrepository repository;
repository.Register(); // Register it
repository.Unregister(); // Unregister it