blob: dfb14192308c16518e39c7d1400d21e0d432e4e2 [file] [log] [blame]
Security Library Tutorial
=========================
Key Management
--------------
Identity, Key and Certificates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
All keys, certificates and their corresponding identities are managed by
KeyChain.
Before signing a packet, you need to assure that the signing key and its
corresponding identity certificate exist in the KeyChain. The private
part of the signing key is used to generate signature, while the
identity certificate is used to constructed the KeyLocator.
In KeyChain, keys and certificates are managed in terms of identities
which are expressed by namespaces (e.g., ``/ndn/edu/ucla/irl/yingdi``, or
``/ndn/edu/ucla/boelter\_hall/room\_4805``). Each pair of keys belongs to
only one identity, and it is named by the identity name appended with a
key ID (e.g., ``/ndn/edu/ucla/irl/yingdi/ksk-1234567890``, or
``/ndn/edu/ucla/boelter\_hall/room\_4805/ksk-1357924680``). However, one
identity may have more than one pair of keys, but only one of them is
the **default key** of the identity. A key pair without any identity
certificates is not quite useful. A key pair may have more than one
identity certificates, but only one of them is the **default
certificate**. Therefore, for a given identity, there is at only one
default identity certificate, which is the default certificates of its
default key.
While keys and certificates can be created offline using NDN security
tools **ndnsec**, they can be created online using the KeyChain API. The
simplest way is to call ``KeyChain::createIdentity``.
.. code-block:: cpp
KeyChain keyChain;
Name identity("/ndn/test/alice");
Name certificateName = keyChain.createIdentity(identity);
``KeyChain::createIdentity`` returns the default certificate name of the
supplied identity, and always assures that the supplied identity has a
default key and a default certificate. If the default key of the
identity does not exist, ``createIdentity`` will create one. If the
default certificate of the identity does not exist, ``createIdentity``
will generate a self-signed certificate of the default key as the
default certificate.
System Default Identity
~~~~~~~~~~~~~~~~~~~~~~~
There is a default key for a particular identity, and a default identity
certificate for a particular key. And, there is also a **default
identity** for the system, which is the user of the system. The default
identity can be configured using ndnsec tools only. You cannot configure
through the security library API.
Get and Set Default Keys/Certificates
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you know the exact names of keys and certificates, you can call
``KeyChain::getPublicKey`` and ``KeyChain::getCertificate``.
.. code-block:: cpp
KeyChain keyChain;
Name aliceKeyName("/ndn/test/alice/ksk-1394129695025");
Name aliceCertName("/ndn/test/KEY/alice/ksk-1394129695025/ID-CERT/%FD%01D%98%9A%F2%3F");
shared_ptr<PublicKey> aliceKey = keyChain.getPublicKey(aliceKeyName);
shared_ptr<IdentityCertificate> aliceCert = keyChain.getCertificate(aliceCertName);
It might be difficult to remember the exact name of keys and
certificates, but it might be easier to remember identity names. The
security library provides a list of methods to locate the default key
name and certificate name of an identity.
.. code-block:: cpp
KeyChain keyChain;
Name alice("/ndn/test/alice");
Name aliceKeyName = keyChain.getDefaultKeyNameForIdentity(alice);
Name aliceCertName = keyChain.getDefaultCertificateNameForKey(aliceKeyName);
/* following code is equivalent to the two lines above */
Name aliceCertName2 = keyChain.getDefaultCertificateNameForIdentity(alice);
You can also manually set default key for an identity and default
certificate for a key through KeyChain.
.. code-block:: cpp
KeyChain keyChain;
Name aliceKeyName("/ndn/test/alice/ksk-1394129695025");
Name aliceCertName("/ndn/test/KEY/alice/ksk-1394129695025/ID-CERT/%FD%01D%98%9A%F2%3F");
keyChain.setDefaultKeyNameForIdentity(aliceKeyName);
keyChain.getDefaultCertificateNameForKey(aliceCertName);
Create Keys Manually
~~~~~~~~~~~~~~~~~~~~
You can call ``KeyChain::generateRSAKeyPair`` to generate an RSA key
pair. Note that generated key pair is not set as the default key of the
identity, so you need to set it manually by calling
``KeyChain::setDefaultKeyNameForIdentity``. There is also a helper
method "KeyChain::generateRSAKeyPairAsDefault", which combines the two
steps into one.
.. code-block:: cpp
KeyChain keyChain;
Name alice("/ndn/test/alice");
Name aliceKeyName = keyChain.generateRSAKeyPair(alice);
keyChain.setDefaultKeyNameForIdentity(aliceKeyName);
Name aliceKeyName2 = keyChain.generateRSAKeyPairAsDefault(alice); // Now the key with the name aliceKeyName2 becomes alice's default key
Create Identity Certificate Manually
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
If you have created a key pair, you can generate a self-signed
certificate for the key by calling ``KeyChain::selfSign``.
.. code-block:: cpp
KeyChain keyChain;
Name aliceKeyName("/ndn/test/alice/ksk-1394129695025");
shared_ptr<IdentityCertificate> aliceCert = keyChain.selfSign(aliceKeyName);
You can sign a public key using a different key, but this signing
process may take several steps. Note that this time, the signing key is
vouching for the signed key, so you need to specify more details, such
as the validity, subject descriptions. The first step is to prepare the
unsigned identity certificate by calling
``KeyChain::prepareUnsignedIdentityCertificate``. And the second step is
to sign the identity certificate. We will talk about the signing methods
in `Packet Signing <#packet_signing>`__.
.. code-block:: cpp
KeyChain keyChain;
Name signingIdentity("/ndn/test");
Name aliceKeyName("/ndn/test/alice/ksk-1394129695025");
MillisecondsSince1970 notBefore = getNow();
MillisecondsSince1970 notAfter = notBefore + 630720000;
vector<CertificateSubjectDescription> subjectDescription;
subjectDescription.push_back(CertificateSubjectDescription("2.5.4.41", "Alice")); // push any subject description into the list.
shared_ptr<IdentityCertificate> = aliceCert
keyChain.prepareUnsignedIdentityCertificate(aliceKeyName, signingIdentity, notBefore, notAftersubjectDescription);
keyChain.signByIdentity(*aliceCert, signingIdentity);
Packet Signing
--------------
When keys and certificates are ready for use, you can sign packet using
them. There are two ways to sign a packet:
1. by specifying the name of the identity certificate belonging to the
signing key.
2. by specifying the identity to which the signing key belongs
Sign With Certificate Name
~~~~~~~~~~~~~~~~~~~~~~~~~~
If we specify the exact certificate name when signing a packet, the
certificate name (without version number) is put into the KeyLocator TLV
in the SignatureInfo. KeyChain will look up the corresponding private
key in the Trusted Platform Module (TPM), and use the private key to
generate the signature.
.. code-block:: cpp
KeyChain keyChain;
Name aliceCertName("/ndn/test/KEY/alice/ksk-1394129695025/ID-CERT/%FD%01D%98%9A%F2%3F");
Data data("/ndn/test/alice/test-data");
keyChain.sign(data, aliceCertName);
When ``KeyChain::sing`` returns, the SignatureInfo and SignatureValue
TLVs of the supplied data will be set.
Sign With Identity Name
~~~~~~~~~~~~~~~~~~~~~~~
If we only specify the identity name when signing a packet, the name of
the identity's default certificate will be put into the KeyLocator TLV
in the SingatureInfo, and the identity's default key is used to sign the
packet. Please make sure the default key and certificates of the signing
identity is initialized correctly before signing, otherwise, KeyChain
will create key and self-signed certificate for signing (which is not
quite useful).
.. code-block:: cpp
KeyChain keyChain;
Name alice("/ndn/test/alice");
Data data("/ndn/test/alice/test-data");
keyChain.signByIdentity(data, alice);
Sign Interest Packet
~~~~~~~~~~~~~~~~~~~~
Signing an interest packet is the same as signing a Data packet. The
only difference is that the SignatureInfo And SignatureValue TLV are
encoded as the last two components of the interest name.
Packet Validation
-----------------
Packet validation is done through a **Validator**. Validator is a
virtual class, two pure virtual methods must be implemented in order to
construct a working validator:
.. code-block:: cpp
class Validator {
...
protected:
virtual void
checkPolicy (const Data& data,
int stepCount,
const OnDataValidated &onValidated,
const OnDataValidationFailed &onValidationFailed,
std::vector<shared_ptr<ValidationRequest> >& nextSteps) = 0;
virtual void
checkPolicy (const Interest& interest,
int stepCount,
const OnInterestValidated &onValidated,
const OnInterestValidationFailed &onValidationFailed,
std::vector<shared_ptr<ValidationRequest> >& nextSteps) = 0;
...
What you need to do inside these two methods is to check whether the
packet and signer comply with your policies, and whether their signature
can be verified. If the packet can be validated, you should call the
``onValidated`` callback function to trigger packet processing,
otherwise the ``onValidationFailed`` callback should be invoked. If you
need more information (e.g., other certificates), you can construct
several ``ValidationRequest`` and push them into nextSteps.
.. code-block:: cpp
class ValidationRequest {
public:
Interest m_interest; // An interest packet to fetch the requested data.
OnDataValidated m_onValidated; // A callback function if the requested certificate is validated.
OnDataValidationFailed m_onDataValidated; // A callback function if the requested certificate validation fails.
int m_retry; // The number of retrials when there is an interest timeout.
int m_stepCount; // The count of validation steps.
};
Security library also provides an ``Validator``, ``ValidatorRegex``
which has already implemented the two methods (basically for Data policy
checking, the Interest policy checking method always calls
``onValidationFailed``).
.. code-block:: cpp
class ValidatorRegex : public Validator
{
public:
...
void
addDataVerificationRule(shared_ptr<SecRuleRelative> rule);
void
addTrustAnchor(shared_ptr<IdentityCertificate> certificate);
...
};
With ``ValidatorRegex``, you can specify the validation rules in terms
of [[Regex\|NDN Regular Expression]] via
``ValidatorRegex::addDataVerificationRule``, and set trust anchor via
``ValidatorRegex::addTrustAnchor``.
How to specify regex-based validation rule
~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
To specify a ``SecRuleRelative``, you needs to specify two NDN regular
expressions: one for data name matching, and the other for KeyLocator
matching. For each regex, you also need to specify the back reference
pattern to extract parts of the name. Moreover, you need to specify the
relation between two extracted patterns. For example, a typical
hierarchical rule can be written as
.. code-block:: cpp
SecRuleRelative rule("^(<>*)$", "^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
">", "\\1", "\\1\\2", true);
This rule indicates that the data name must be under the signer's
namespace.
How to use validator
~~~~~~~~~~~~~~~~~~~~
Here is an example of how to use the validator
.. code-block:: cpp
class Example {
public:
Example(ndn::shared_ptr<ndn::Face> face>)
: m_face(face)
{
ndn::shared_ptr<ndn::ValidatorRegex> validator(new ndn::ValidatorRegex(m_face));
validator->addDataVerificationRule(ndn::make_shared<ndn::SecRuleRelative>("^(<>*)$",
"^([^<KEY>]*)<KEY>(<>*)<ksk-.*><ID-CERT>$",
">", "\\1", "\\1\\2", true));
ndn::shared_ptr<ndn::IdentityCertificate> anchor = ndn::io::load<IdentityCertificate>("ndn-root.cert");
validator->addTrustAnchor(anchor);
m_validator = validator;
}
virtual
~Example() {}
void
sendInterest()
{
Interest interest ("/ndn/test/data");
m_face->expressInterest(interest,
bind(&Example::onData, this, _1, _2),
bind(&Example::onTimeout, this, _1));
}
void
onData(const ndn::Interest& interest, const ndn::Data& data)
{
m_validator->validate(data,
ndn::bind(&Example::onValidated, this, _1),
ndn::bind(&Example::onValidationFailed, this, _1, _2));
}
void onTimeout(const ndn::Interest& interest) {}
void onValidated(const ndn::shared_ptr<const ndn::Data>& data) {}
void onValidationFailed(const ndn::shared_ptr<const ndn::Data>& data, const std::string& failInfo) {}
private:
ndn::shared_ptr<ndn::Face> m_face;
ndn::shared_ptr<ndn::Validator> m_validator;
};