An overview of NSS Internals¶
A High-Level Overview to the Internals of Network Security Services
(NSS)
Software developed by the Mozilla.org projects traditionally used its own implementation of
security protocols and cryptographic algorithms, originally called Netscape Security Services,
nowadays called Network Security Services (NSS). NSS is a library written in the C programming
language. It’s free and open source software, and many other software projects have decided to
use it. In order to support multiple operating systems (OS), it is based on a cross platform
portability layer, called the Netscape Portable Runtime (NSPR), which provides cross platform
application programming interfaces (APIs) for OS specific APIs like file system access, memory
management, network communication, and multithreaded programming.
NSS offers lots of functionality; we’ll walk through the list of modules, design principles,
and important relevant standards.
In order to allow interoperability between software and devices that perform cryptographic
operations, NSS conforms to a standard called PKCS#11. (Note that it’s important to look at the
number 11, as there are other PKCS standards with different numbers that define quite different
topics.)
A software or hardware module conforming to the PKCS#11 standard implements an interface of C
calls, which allow querying the characteristics and offered services of the module. Multiple
elements of NSS’s own modules have been implemented with this interface, and NSS makes use of
this interface when talking to those modules. This strategy allows NSS to work with many
hardware devices (e.g., to speed up the calculations required for cryptographic operations, or
to access smartcards that securely protect a secret key) and software modules (e.g., to allow
to load such modules as a plugin that provides additional algorithms or stores key or trust
information) that implement the PKCS#11 interface.
A core element of NSS is FreeBL, a base library providing hash functions, big number
calculations, and cryptographic algorithms.
Softoken is an NSS module that exposes most FreeBL functionality as a PKCS#11 module.
Some cryptography uses the same secret key for both encrypting and decrypting, for example
password based encryption (PBE). This is often sufficient if you encrypt data for yourself, but
as soon as you need to exchange signed/encrypted data with communication partners, using public
key encryption simplifies the key management. The environment that describes how to use public
key encryption is called Public Key Infrastructure (PKI). The public keys that are exchanged
between parties are transported using a container; the container is called a certificate,
following standard X.509 version 3. A certificate contains lots of other details; for example,
it contains a signature by a third party that expresses trust in the ownership relationship for
the certificate. The trust assigned by the third party might be restricted to certain uses,
which are listed in certificate extensions that are contained in the certificate.
Many (if not most) of the operations performed by NSS involve the use of X.509 certificates
(often abbreviated as “cert”, unfortunately making it easy to confuse with the term “computer
emergency response team“).
When checking whether a certificate is trusted or not, it’s necessary to find a relevant trust
anchor (root certificate) that represents the signing capability of a trusted third party,
usually called a Certificate Authority (CA). A trust anchor is just another X.509 certificate
that is already known and has been deliberately marked as trusted by a software vendor,
administrators inside an organizational infrastructure, or the software user. NSS ships a
predefined set of CA certificates. This set, including their trust assignments, is provided by
NSS as a software module, called CKBI (“built-in root certificates”), which also implements the
PKCS#11 interface. On an organizational level the contents of the set are managed according to
the Mozilla CA policy. On a technical level the set is a binary software module.
A cryptographic transaction, such as encryption or decryption related to a data exchange,
usually involves working with the X.509 certs of your communication partners (peer). It’s also
required that you safely keep your own secret keys that belong to your own certificates. You
might want to protect the storage of your secret keys with PBE. You might decide to modify the
default trust provided by NSS. All of this requires storing, looking up, and retrieving data.
NSS simplifies performing these operations by offering storage and management APIs. NSS doesn’t
require the programmer to manage individual files containing individual certificates or keys.
Instead, NSS offers to use its own database(s). Once you have imported certificates and keys
into the NSS database, you can easily look them up and use them again.
Because of NSS’s expectation to operate with an NSS database, it’s mandatory that you perform
an initialization call, where you tell NSS which database you will be using. In the most simple
scenario, the programmer will provide a directory on your filesystem as a parameter to the init
function, and NSS is designed to do the rest. It will detect and open an existing database, or
it can create a new one. Alternatively, should you decide that you don’t want to work with any
persistent recording of certificates, you may initialize NSS in a no-database mode. Usually,
NSS will flush all data to disk as soon as new data has been added to permanent storage.
Storage consists of multiple files: a key database file, which contains your secret keys, and a
certificate database file which contains the public portion of your own certificates, the
certificates of peers or CAs, and a list of trust decisions (such as to not trust a built-in
CA, or to explicitly trust other CAs). Examples for the database files are key3.db and
cert8.db, where the numbers are file version numbers. A third file contains the list of
external PKCS#11 modules that have been registered to be used by NSS. The file could be named
secmod.db, but in newer database generations a file named pkcs11.txt is used.
Only NSS is allowed to access and manipulate these database files directly; a programmer using
NSS must go through the APIs offered by NSS to manipulate the data stored in these files. The
programmer’s task is to initialize NSS with the required parameters (such as a database), and
NSS will then transparently manage the database files.
Most of the time certificates and keys are supposed to be stored in the NSS database.
Therefore, after initial import or creation, the programmer usually doesn’t deal with their raw
bytes. Instead, the programmer will use lookup functions, and NSS will provide an access handle
that will be subsequently used by the application’s code. Those handles are reference counted.
NSS will usually create an in-memory (RAM) presentation of certificates, once a certificate has
been received from the network, read from disk, or looked up from the database, and prepare
in-memory data structures that contain the certificate’s properties, as well as providing a
handle for the programmer to use. Once the application is done with a handle, it should be
released, allowing NSS to free the associated resources. When working with handles to private
keys it’s usually difficult (and undesired) that an application gets access to the raw key
data; therefore it may be difficult to extract such data from NSS. The usual minimum
requirement is that private keys must be wrapped using a protective layer (such as
password-based encryption). The intention is to make it easier to review code for security. The
less code that has access to raw secret keys, the less code that must be reviewed.
NSS has only limited functionality to look up raw keys. The preferred approach is to use
certificates, and to look up certificates by properties such as the contained subject name
(information that describes the owner of the certificate). For example, while NSS supports
random calculation (creation) of a new public/private key pair, it’s difficult to work with
such a raw key pair. The usual approach is to create a certificate signing request (CSR) as
soon as an application is done with the creation step, which will have created a handle to the
key pair, and which can be used for the necessary related operations, like producing a
proof-of-ownership of the private key, which is usually required when submitting the public key
with a CSR to a CA. The usual follow up action is receiving a signed certificate from a CA.
(However, it’s also possible to use NSS functionality to create a self-signed certificate,
which, however, usually won’t be trusted by other parties.) Once received, it’s sufficient to
tell NSS to import such a new certificate into the NSS database, and NSS will automatically
perform a lookup of the embedded public key, be able to find the associated private key, and
subsequently be able to treat it as a personal certificate. (A personal certificate is a
certificate for which the private key is in possession, and which could be used for signing
data or for decrypting data.) A unique nickname can/should be assigned to the certificate at
the time of import, which can later be used to easily identify and retrieve it.
It’s important to note that NSS requires strict cleanup for all handles returned by NSS. The
application should always call the appropriate dereference (destroy) functions once a handle is
no longer needed. This is particularly important for applications that might need to close a
database and reinitialize NSS using a different one, without restarting. Such an operation
might fail at runtime if data elements are still being referenced.
In addition to the FreeBL, Softoken, and CKBI modules, there is an utility library for general
operations (e.g., encoding/decoding between data formats, a list of standardized object
identifiers (OID)). NSS has an SSL/TLS module that implements the Secure Sockets
Layer/Transport Layer Security network protocols, an S/MIME module that implements CMS
messaging used by secure email and some instant messaging implementations, a DBM library that
implements the classic database storage, and finally a core NSS library for the big set of
“everything else”. Newer generations of the database use the SQLite database to allow
concurrent access by multiple applications.
All of the above are provided as shared libraries. The CRMF library, which is used to produce
certain kinds of certificate requests, is available as a library for static linking only.
When dealing with certificates (X.509), file formats such as PKCS#12 (certificates and keys),
PKCS#7 (signed data), and message formats as CMS, we should mention ASN.1, which is a syntax
for storing structured data in a very efficient (small sized) presentation. It was originally
developed for telecommunication systems at times where it was critical to minimize data as much
as possible (although it still makes sense to use that principle today for good performance).
In order to process data available in the ASN.1 format, the usual approach is to parse it and
transfer it to a presentation that requires more space but is easier to work with, such as
(nested) C data structures. Over the time NSS has received three different ASN.1 parser
implementations, each having their own specific properties, advantages and disadvantages, which
is why all of them are still being used (nobody has yet dared to replace the older with the
newer ones because of risks for side effects). When using the ASN.1 parser(s), a template
definition is passed to the parser, which will analyze the ASN.1 data stream accordingly. The
templates are usually closely aligned to definitions found in RFC documents.
A data block described as DER is usually in ASN.1 format. You must know which data you are
expecting, and use the correct template for parsing, based on the context of your software’s
interaction. Data described as PEM is a base64 encoded presentation of DER, usually wrapped
between human readable BEGIN/END lines. NSS prefers the binary presentation, but is often
capable to use base64 or ASCII presentations, especially when importing data from files. A
recent development adds support for loading external PEM files that contain private keys, in a
software library called nss-pem, which is separately available, but should eventually become a
core part of NSS.
Looking at the code level, NSS deals with blocks of raw data all the time. The common structure
to store such an untyped block is SECItem, which contains a size and an untyped C pointer
variable.
When dealing with memory, NSS makes use of arenas, which are an attempt to simplify management
with the limited offerings of C (because there are no destructors). The idea is to group
multiple memory allocations in order to simplify cleanup. Performing an operation often
involves allocating many individual data items, and the code might be required to abort a task
at many positions in the logic. An arena is requested once processing of a task starts, and all
memory allocations that are logically associated to that task are requested from the associated
arena. The implementation of arenas makes sure that all individual memory blocks are tracked.
Once a task is done, regardless whether it completed or was aborted, the programmer simply
needs to release the arena, and all individually allocated blocks will be released
automatically. Often freeing is combined with immediately erasing (zeroing, zfree) the memory
associated to the arena, in order to make it more difficult for attackers to extract keys from
a memory dump.
NSS uses many C data structures. Often NSS has multiple implementations for the same or similar
concepts. For example, there are multiple presentations of certificates, and the NSS internals
(and sometimes even the application using NSS) might have to convert between them.
Key responsibilites of NSS are verification of signatures and certificates. In order to verify
a digital signature, we have to look at the application data (e.g., a document that was
signed), the signature data block (the digital signature), and a public key (as found in a
certificate that is believed to be the signer, e.g., identified by metadata received together
with the signature). The signature is verified if it can be shown that the signature data block
must have been produced by the owner of the public key (because only that owner has the
associated private key).
Verifying a certificate (A) requires some additional steps. First, you must identify the
potential signer (B) of a certificate (A). This is done by reading the “issuer name” attribute
of a certificate (A), and trying to find that issuer certificate (B) (by looking for a
certificate that uses that name as its “subject name”). Then you attempt to verify the
signature found in (A) using the public key found in (B). It might be necessary to try multiple
certificates (B1, B2, …) each having the same subject name.
After succeeding, it might be necessary to repeat this procedure recursively. The goal is to
eventually find a certificate B (or C or …) that has an appropriate trust assigned (e.g.,
because it can be found in the CKBI module and the user hasn’t made any overriding trust
decisions, or it can be found in a NSS database file managed by the user or by the local
environment).
After having successfully verified the signatures in a (chain of) issuer certificate(s), we’re
still not done with verifying the certificate A. In a PKI it’s suggested/required to perform
additional checks. For example: Certificates were valid at the time the signature was made,
name in certificates matches the expected signer (check subject name, common name, email, based
on application), the trust restrictions recorded inside the certificate (extensions) permit the
use (e.g., encryption might be allowed, but not signing), and based on environment/application
policy it might be required to perform a revocation check (OCSP or CRL), that asks the
issuer(s) of the certificates whether there have been events that made it necessary to revoke
the trust (revoke the validity of the cert).
Trust anchors contained in the CKBI module are usually self signed, which is defined as having
identical subject name and issuer name fields. If a self-signed certificate is marked as
explicitly trusted, NSS will skip checking the self-signature for validity.
NSS has multiple APIs to perform verification of certificates. There is a classic engine that
is very stable and works fine in all simple scenarios, for example if all (B) candidate issuer
certificates have the same subject and issuer names and differ by validity period; however, it
works only in a limited amount of more advanced scenarios. Unfortunately, the world of
certificates has become more complex in the recent past. New Certificate Authorities enter the
global PKI market, and in order to get started with their business, they might make deals with
established CAs and receive so-called cross-signing-certificates. As a result, when searching
for a trust path from (A) to a trusted anchor (root) certificate (Z), the set of candidate
issuer certificates might have different issuer names (referring to the second or higher issuer
level). As a consequence, it will be necessary to try multiple different alternative routes
while searching for (Z), in a recursive manner. Only the newer verification engine (internally
named libPKIX) is capable of doing that properly.
It’s worth mentioning the Extended Validation (EV) principle, which is an effort by software
vendors and CAs to define a stricter set of rules for issuing certificates for web site
certificates. Instead of simply verifying that the requester of a certificate is in control of
an administrative email address at the desired web site’s domain, it’s required that the CA
performs a verification of real world identity documents (such as a company registration
document with the country’s authority), and it’s also required that a browser software performs
a revocation check with the CA, prior to granting validity to the certificate. In order to
distinguish an EV certificate, CAs will embed a policy OID in the certificate, and the browser
is expected to verify that a trust chain permits the end entity (EE) certificate to make use of
the policy. Only the APIs of the newer libPKIX engine are capable of performing a policy
verification.
That’s a good opportunity to talk about SSL/TLS connections to servers in general (not just EV,
not just websites). Whenever this document mentions SSL, it refers to either SSL or TLS. (TLS
is a newer version of SSL with enhanced features.)
When establishing an SSL connection to a server, (at least) a server certificate (and its trust
chain) is exchanged from the server to the client (e.g., the browser), and the client verifies
that the certificate can be verified (including matching the name of the expected destination
server). Another part of the handshake between both parties is a key exchange. Because public
key encryption is more expensive (more calculations required) than symmetric encryption (where
both parties use the same key), a key agreement protocol will be executed, where the public and
private keys are used to proof and verify the exchanged initial information. Once the key
agreement is done, a symmetric encryption will be used (until a potential re-handshake on an
existing channel). The combination of the hash and encryption algorithms used for a SSL
connection is called a cipher suite.
NSS ships with a set of cipher suites that it supports at a technical level. In addition, NSS
ships with a default policy that defines which cipher suites are enabled by default. An
application is able to modify the policy used at program runtime, by using function calls to
modify the set of enabled cipher suites.
If a programmer wants to influence how NSS verifies certificates or how NSS verifies the data
presented in a SSL connection handshake, it is possible to register application-defined
callback functions which will be called by NSS at the appropriate point of time, and which can
be used to override the decisions made by NSS.
If you would like to use NSS as a toolkit that implements SSL, remember that you must init NSS
first. But if you don’t care about modifying the default trust permanently (recorded on disk),
you can use the no-database init calls. When creating the network socket for data exchange,
note that you must use the operating system independent APIs provided by NSPR and NSS. It might
be interesting to mention a property of the NSPR file descriptors, which are stacked in layers.
This means you can define multiple layers that are involved in data processing. A file
descriptor has a pointer to the first layer handling the data. That layer has a pointer to a
potential second layer, which might have another pointer to a third layer, etc. Each layer
defines its own functions for the open/close/read/write/poll/select (etc.) functions. When
using an SSL network connection, you’ll already have two layers, the basic NSPR layer and an
SSL library layer. The Mozilla applications define a third layer where application specific
processing is performed. You can find more details in the NSPR reference documents.
NSS occassionally has to create outbound network connections, in addition to the connections
requested by the application. Examples are retrieving OCSP (Online Certificate Status Protocol)
information or downloading a CRL (Certificate Revocation List). However, NSS doesn’t have an
implementation to work with network proxies. If you must support proxies in your application,
you are able to register your own implementation of an http request callback interface, and NSS
can use your application code that supports proxies.
When using hashing, encryption, and decryption functions, it is possible to stream data (as
opposed to operating on a large buffer). Create a context handle while providing all the
parameters required for the operation, then call an “update” function multiple times to pass
subsets of the input to NSS. The data will be processed and either returned directly or sent to
a callback function registered in the context. When done, you call a finalization function that
will flush out any pending data and free the resources.
This line is a placeholder for future sections that should explain how libpkix works and is
designed.
If you want to work with NSS, it’s often helpful to use the command line utilities that are
provided by the NSS developers. There are tools for managing NSS databases, for dumping or
verifying certificates, for registering PKCS#11 modules with a database, for processing CMS
encrypted/signed messages, etc.
For example, if you wanted to create your own pair of keys and request a new certificate from a
CA, you could use certutil to create an empty database, then use certutil to operate on your
database and create a certificate request (which involves creating the desired key pair) and
export it to a file, submit the request file to the CA, receive the file from the CA, and
import the certificate into your database. You should assign a good nickname to a certificate
when importing it, making it easier for you to refer to it later.
It should be noted that the first database format that can be accessed simultaneously by
multiple applications is key4.db/cert9.db – database files with lower numbers will most likely
experience unrecoverable corruption if you access them with multiple applications at the same
time. In other words, if your browser or your server operates on an older NSS database format,
don’t use the NSS tools to operate on it while the other software is executing. At the time of
writing NSS and the Mozilla applications still use the older database file format by default,
where each application has its own NSS database.
If you require a copy of a certificate stored in an NSS database, including its private key,
you can use pk12util to export it to the PKCS#12 file format. If you require it in PEM format,
you could use the openssl pkcs12 command (that’s not NSS) to convert the PKCS#12 file to PEM.
This line is a placeholder for how to prepare a database, how to dump a cert, and how to
convert data.
You might have been motivated to work with NSS because it is used by the Mozilla applications
such as Firefox, Thunderbird, etc. If you build the Mozilla application, it will automatically
build the NSS library, too. However, if you want to work with the NSS command line tools, you
will have to follow the standalone NSS build instructions, and build NSS outside of the Mozilla
application sources.
The key database file will contain at least one symmetric key, which NSS will automatically
create on demand, and which will be used to protect your secret (private) keys. The symmetric
key can be protected with PBE by setting a master password on the database. As soon as you set
a master password, an attacker stealing your key database will no longer be able to get access
to your private key, unless the attacker would also succeed in stealing the master password.
Now you might be interest in how to get the
NSS sources building testing