Feature #3075
openDesign support for SignatureHmacWithSha256
70%
Description
The signature format SignatureHmacWithSha256 has been approved.
Need to design support HMAC signatures where the shared key is accessed with a keyName.
Ideas:
A simple option is to support HMAC keys in SecTpm::signInTpm.
Then, if KeyChain::prepareSignatureInfo supports HMAC signatures, then the existing KeyChain::signImpl will work.
Updated by Junxiao Shi over 9 years ago
- Description updated (diff)
- Category set to Security
- Start date deleted (
07/30/2015)
With an HMAC key, there's no division of public key (lives in PIB) and private key (lives in TPM).
If we place HMAC key bits into PIB, it would be exposed, especially when PIB service backend is being used.
If we don't place HMAC key bits into PIB, the Validator doesn't have access to TPM and won't be able to validate an HMAC signature.
Since HMAC keys are short-lived, and the mapping from KeyLocatorName to key bits is managed by application, do they even need to go into PIB/TPM?
How about: let the application locate the correct key and supply it to KeyChain?
keyChain.sign(pkt, signingWithHmac(hmacKey));
// hmacKey is a object that contains KeyLocatorName and key bits; it's not a Name or iterator in PIB/TPM
I'm unfamiliar with the trust schema API so I don't have a suggestion on the Validator syntax.
Updated by Anonymous over 9 years ago
If the TPM supports signing the HMAC, then it is possible to provide the TPM object to the validator. The process to verify an HMAC signature is really the same as to sign: you create a new signature with the same key and compare the signature. Therefore, the validate method can get the keyName from the data packet, call the TPM signInTpm(keyName) and compare. If the library doesn't have a way to look up the HMAC key by keyName like with signInTpm(keyName), then it will be hard for validate(data) to work with any data packet.
Updated by Alex Afanasyev almost 9 years ago
- Related to Task #3457: Collect and notify apps about routable prefixes added
Updated by Anonymous almost 9 years ago
- Blocked by Task #2926: Refactor KeyChain added
Updated by Anonymous almost 9 years ago
- Assignee set to Yingdi Yu
After finishing issue #2926, Yingdi plans to spec out static KeyChain methods for symmetric sign and verify, where the application supplies the shared secret. (It won't use the TPM or Validator classes.)
Updated by Alex Afanasyev almost 9 years ago
- Related to deleted (Task #3457: Collect and notify apps about routable prefixes)
Updated by Anonymous over 8 years ago
This task is still assigned to Yingdi. Is someone else going to look at SignatureHmacWithSha256 support in ndn-cxx? If not, I can propose an API to use in the Common Client Libraries.
Updated by Alex Afanasyev over 8 years ago
Yes, you can try to get an API and have implementation.
Updated by Anonymous about 8 years ago
As Yingdi said, sign and verify should be static methods which take the key since the HmacWithSha256 should not be stored in the TPM. In NDN-CPP, I added signWithHmacWithSha256(Data&), signWithHmacWithSha256(Interest&), verifyDataWithHmacWithSha256 and verifyInterestWithHmacWithSha256:
https://github.com/named-data/ndn-cpp/blob/953f09dbee5ee1ea28a43f958e3b8423ec78beae/include/ndn-cpp/security/key-chain.hpp#L521
https://github.com/named-data/ndn-cpp/blob/953f09dbee5ee1ea28a43f958e3b8423ec78beae/include/ndn-cpp/security/key-chain.hpp#L538
https://github.com/named-data/ndn-cpp/blob/953f09dbee5ee1ea28a43f958e3b8423ec78beae/include/ndn-cpp/security/key-chain.hpp#L553
https://github.com/named-data/ndn-cpp/blob/953f09dbee5ee1ea28a43f958e3b8423ec78beae/include/ndn-cpp/security/key-chain.hpp#L568
I also added sign-and-verify examples for Data and Interest:
https://github.com/named-data/ndn-cpp/blob/master/examples/test-sign-verify-data-hmac.cpp
https://github.com/named-data/ndn-cpp/blob/master/examples/test-sign-verify-interest-hmac.cpp
Updated by Junxiao Shi almost 6 years ago
- Assignee set to Laqin Fan
Laqin wanted to work on this issue to support his (yet another) secure bootstrapping protocol.
Updated by Davide Pesavento almost 6 years ago
- Tracker changed from Task to Feature
- Subject changed from Design support SignatureHmacWithSha256 to Design support for SignatureHmacWithSha256
Updated by Junxiao Shi almost 6 years ago
Change 5248 patchset1 implements HMAC verification as a free function in ndn-cxx/security/verification-helpers.cpp
.
While this API is correct and necessary, two additional APIs are also needed:
- HMAC should be convertible to
PublicKey
so that it can be used inndn::security::transform::VerifierFilter
. - HMAC should be usable in
ValidationPolicyConfig
.
Updated by Davide Pesavento almost 6 years ago
Junxiao Shi wrote:
- HMAC should be convertible to
PublicKey
so that it can be used inndn::security::transform::VerifierFilter
.
I suppose you mean an HMAC *key* here, not the actual HMAC.
Two comments though:
VerifierFilter
does not support HMAC verification currently. It uses theEVP_DigestVerify*
family of functions which are for public-key-based signature schemes (sign and verify are asymmetric). For HMAC the two operations are identical, you just recalculate the signature and compare it. We already have anHmacFilter
that can do this (minus the comparison).- Not sure if it's a good idea to support HMAC keys in
PublicKey
, since... well, they're not "public".
Updated by Junxiao Shi almost 6 years ago
Not sure if it's a good idea to support HMAC keys in
PublicKey
, since... well, they're not "public".
It could be a dedicated HmacKey
type. Passing pointer around is not sustainable: (1) caller must ensure pointer is valid (2) you can’t reference a key stored in Hardware Security Module (3) who’s going to cleanse the memory?
VerifierFilter
does not support HMAC verification currently. It uses theEVP_DigestVerify*
family of functions which are for public-key-based signature schemes (sign and verify are asymmetric).
From caller point of view it is more natural to support HmacKey
type in SignerFilter
and VerifierFilter
. Internally they can invoke different OpenSSL functions.
HmacFilter
should be deprecated because (1) it passes pointer around so is not sustainable (2) it’s ambiguous on whether it does signing or verification.
Updated by Laqin Fan almost 6 years ago
Junxiao Shi wrote:
Not sure if it's a good idea to support HMAC keys in
PublicKey
, since... well, they're not "public".It could be a dedicated
HmacKey
type. Passing pointer around is not sustainable: (1) caller must ensure pointer is valid (2) you can’t reference a key stored in Hardware Security Module (3) who’s going to cleanse the memory?
So add EVP_PKEY_HMAC key type in PublicKey? Just as Davide mentions, they are not public, probably the PublicKey needs to be renamed.
Updated by Junxiao Shi almost 6 years ago
So add EVP_PKEY_HMAC key type in PublicKey? Just as Davide mentions, they are not public, probably the PublicKey needs to be renamed.
No. HmacKey is a separate type. It’s not a subclass of PublicKey or PrivateKey. Both SignerFilter and VerifierFilter should accept HmacKey in addition to PrivateKey or PublicKey.
Updated by Laqin Fan almost 6 years ago
No. HmacKey is a separate type. It’s not a subclass of PublicKey or PrivateKey. Both SignerFilter and VerifierFilter should accept HmacKey in addition to PrivateKey or PublicKey.
We can create an HMAC value with EVP_DigestSignInit, EVP_DigestSignUpdate and EVP_DigestSignFinal. And, to verify an HMAC value over the message using the same EVP_DigestSign functions. HmacFilter already does this.
And, VerifierFilter is using EVP_DigestVerify to verify a signature.
Updated by Junxiao Shi almost 6 years ago
ndn-cxx using OpenSSL is an implementation detail. What OpenSSL does is irrelevant to what ndn-cxx should expose to its callers.
Updated by Junxiao Shi over 5 years ago
- Status changed from Code review to Feedback
does the application provides the key to the SigningInfo?
Otherwise how do we retrieve the key during signing? Others do it via tpm.
TPM is one way to do it, but I'm unsure about whether storing HMAC keys should be TPM responsibility.
Neither macOS Keychain nor GNOME Keyring stores HMAC keys, so there's no precedent.
Both NDN-Lite and esp8266ndn let application manage HMAC keys.
Thus, I would suggest non-TPM APIs like this:
// ---- HmacKey construction ----
// default constructed key is invalid
ndn::security::HmacKey key;
// Attempting to use an invalid key raises an exception.
// make a key from memory (caller is responsible for cleansing the buffer)
ndn::security::HmacKey key("/key-name",
{0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA},
48);
// generate a random key
uint8_t keyBits[32]; // generated key is written into this array
auto key = ndn::security::HmacKey::generate("/key-name", keyBits, sizeof(keyBits));
// There's no public method on the HmacKey type to export the key bits.
// If HmacKey internally stores plain key bits, its destructor must cleanse memory.
// ---- transforms ----
{
namespace t = ndn::security::transform;
uint8_t input[] = {0xCC, 0xCC, 0xCC, 0xCC};
std::stringstream sigBuf;
t::bufferSource(input, sizeof(input)) >>
t::signerFilter(ndn::DigestAlgorithm::SHA256, key) >>
t::streamSink(sigBuf);
std::string sig = sigBuf.str();
bool ok;
t::bufferSource(input, sizeof(input)) >>
t::verifierFilter(ndn::DigestAlgorithm::SHA256, key, reinterpret_cast<const uint8_t*>(sig.data()), sig.size()) >>
t::boolSink(ok);
// t::hmacFilter is deprecated in favor of t::signerFilter
}
// ---- signing via KeyChain ----
// construct SigningInfo
ndn::security::SigningInfo si(key);
// create SigningInfo via helper
auto si = ndn::security::signingByHmac(key);
ndn::security::KeyChain keyChain;
ndn::Data data("/data-name");
keyChain.sign(data, si);
// ---- verification helper ----
bool ok = ndn::security::verifyHmac(data, key);
// ---- ValidatorConfig ----
ndn::security::ValidatorConfig validator;
validator.load(R"CONF(
rule {
id 0
for data
checker {
type customized
sig-type hmac-sha256
key-locator {
type name
name /key-name
relation equal
}
}
}
hmac-key {
name /key-name
type base64
base64-string "DDDD" ; base64 encoded HMAC key
}
)CONF";)
Updated by Laqin Fan over 5 years ago
// ---- signing via KeyChain ----
// construct SigningInfo
ndn::security::SigningInfo si(key);
// create SigningInfo via helper
auto si = ndn::security::signingByHmac(key);ndn::security::KeyChain keyChain;
ndn::Data data("/data-name");
keyChain.sign(data, si);
In KeyChain, prepareSignatureInfo() returns keyname and SignatureInfo, then how to retrieve hmackey using the keyname in later sign(encoder.buf(), encoder.size(), keyName, params.getDigestAlgorithm()).
Updated by Junxiao Shi over 5 years ago
In KeyChain, prepareSignatureInfo() returns keyname and SignatureInfo, then how to retrieve hmackey using the keyname in later sign(encoder.buf(), encoder.size(), keyName, params.getDigestAlgorithm()).
This means you have to refactor KeyChain
type to retain the necessary information.
Updated by Junxiao Shi over 5 years ago
One word on the benefit of having an HmacKey
type: key protection.
Suppose a private HMAC key is stored on the filesystem in a file owned and only readable by root. A program can read this file as root, construct HmacKey
instance, cleanse memory, and drop root privilege.
Pairing this with keeping only an EVP_PKEY*
within HmacKey
type instead of saving the key buffer, it reduces the risk of leaking the key.
This is also the reason existing HmacFilter
is bad API design and should be deprecated.
Updated by Laqin Fan over 5 years ago
Junxiao Shi wrote:
One word on the benefit of having anHmacKey
type: key protection.
Suppose a private HMAC key is stored on the filesystem in a file owned and only readable by root. A program can read this file as root, constructHmacKey
instance, cleanse memory, and drop root privilege.
Pairing this with keeping only anEVP_PKEY*
withinHmacKey
type instead of saving the key buffer, it reduces the risk of leaking the key.
This is also the reason existingHmacFilter
is bad API design and should be deprecated.
I think HmacFilter can still be used for hmac calculation. But the inputs can only be digest algorithm and HmacKey, similar with SignerFilter.
Updated by Junxiao Shi over 5 years ago
I think HmacFilter can still be used for hmac calculation. But the inputs can only be digest algorithm and HmacKey, similar with SignerFilter.
No. The name “HmacFilter” is ambiguous on whether it performs signing or verification. Having SignerFilter and VerifierFilter accept HmacKey avoids this ambiguity.
Updated by Junxiao Shi over 5 years ago
key name could be provided as parameter of SigningInfo, SigningInfo(*hmacKey, keyName), then we could get signerName to set keyLocator.
This looks good on signing side, but does not work well in ValidatorConfig
, because HMAC keys do not go into the PIB.
Every RSA/EC key has a name that goes with it, not with the SigningInfo
. However, they get the name at PIB layer, not transform layer.
note-21 design injects the name at transform layer. If we don't want a name in transform::HmacKey
, we'll need another class that wraps an HMAC key and a name, for use in both SigningInfo
and ValidatorConfig
.
Updated by Davide Pesavento over 5 years ago
Junxiao Shi wrote:
note-21 design injects the name at transform layer.
I don't love this approach, although not a strong opinion...
If we don't want a name in
transform::HmacKey
, we'll need another class that wraps an HMAC key and a name, for use in bothSigningInfo
andValidatorConfig
.
I can't tell if this is better or worse than having the name in transform::HmacKey
Updated by Junxiao Shi over 5 years ago
20190320 NFD call decides to store HMAC keys as "passwords" in TPM, in order to effectively support Validator
API. Given this complication, I suggest splitting into multiple commits:
-
security: HMAC transforms
HmacKey
as in note-21, except that it does not have a namesignerFilter
andverifierFilter
supportsHmacKey
- deprecated ill-designed
hmacFilter
-
security: verifyHmac helper
ndn::security::verifyHmac
as in note-21
-
security: store HMAC key in TPM
-
security: reference HMAC key in PIB
-
security: HMAC signing in KeyChain
-
security: HMAC validation
The first two can already start. The rest needs to wait for Alex's design.
Updated by Laqin Fan over 5 years ago
Junxiao Shi wrote:
20190320 NFD call decides to store HMAC keys as "passwords" in TPM, in order to effectively supportValidator
API. Given this complication, I suggest splitting into multiple commits:
security: HMAC transforms
HmacKey
as in note-21, except that it does not have a namesignerFilter
andverifierFilter
supportsHmacKey
- deprecated ill-designed
hmacFilter
security: verifyHmac helper
ndn::security::verifyHmac
as in note-21security: store HMAC key in TPM
security: reference HMAC key in PIB
security: HMAC signing in KeyChain
security: HMAC validation
The first two can already start. The rest needs to wait for Alex's design.
Based on the first two, here is a draft for hmac tpm design:
-
security: store HMAC key in TPM
- Create 'hmac key' mapping with an identity name. After generating the hmacHey, add the keyName into Identity (that is to add hmac Keyname into Pib)
-
security: reference HMAC key in PIB
- HmacKeyname is in Pib, we can use the keyName to find Hmackey in TPM for validation
Details:
//'''In Keychain class''':
// Create hmac identity
Identity
KeyChain::createHmacIdentity(identityName, params)
{ Identity id = m_pib->addIdentity(identityName);
hmacIdentity = createHmacKey(id, params);
}
Identity
KeyChain::createHmacKey(identity, params){
keyName = m_tpm->createKey(identity.getName(), params);
Identity id = m_pib->addIdentity(keyName);
}
// '''In Tpm class:'''
Name
Tpm::crerateKey(identityName, keyParam)
{ // based on KeyType (RSA, EC, or Hmac)
Keyhandle = m_backend.createKey(identityName, keyparam);
Name = keyhandle.getKeyName();
}
// In BackEnd class:(backend implementation for key handle)
KeyHandle
BackEnd::createKey( identityName, keyparams){
// based on KeyIdType (user_specified, sha256, random)
//generate Keyhandle, and setKeyName
transform::generateHmacKey(keyparams);
setKeyName(*keyHandle, identityName, params);
}
Updated by Alex Afanasyev over 5 years ago
There is no need to make changes in KeyChain class. Currently, we have some support for Aes keys, but one cannot really be created.
So, my suggestion:
Create
HmacKeyParams
or directly reuseAesKeyParams
for hmac purposes (the former is more appropriate for me; if so, AesKeyParams may need to be removed as it is unusable).Add support to generate AES key in Tpm::createKey (right now it throws exception)
Add support for AES/hmac in transform::PrivateKey. Given there is no public key, all corresponding "derivation" methods should throw error for Aes/hmac
Add support for Hmac in signedFilter (this derives from PrivateKey support). No changes in verifyFillter, as verification of hmac is done through computing signature again and comparing them.
We will need to figure out naming of the hmac identities, but I think these we can figure out as we go.
Updated by Junxiao Shi over 5 years ago
generate AES key in Tpm::createKey (right now it throws exception)
We are working on HMAC, not AES. HMAC key is similar to AES key, but not exactly the same: it needs to match hash function block length.
Hmac in signedFilter (this derives from PrivateKey support)
Yes, this is same as note-21.
No changes in verifyFilter, as verification of hmac is done through computing signature again and comparing them.
No, verifyFilter
changes are necessary, so that programmer does not need to deal with constant-time memcmp directly.
Internally it would perform a signature computation and a constant-time memcmp, and write the outcome to boolSink
.
Updated by Davide Pesavento over 5 years ago
- Status changed from Feedback to In Progress
- % Done changed from 0 to 20
Updated by Laqin Fan over 5 years ago
There is no need to make changes in KeyChain class. Currently, we have some support for Aes keys, but one cannot really be created.
If we wanna add HMAC signature support in KeyChain, we may need to change KeyChain a little to support HMAC key creation and importing. The existing KeyChain::createKey() returns Key which may not be applied to HMAC.
Updated by Davide Pesavento over 5 years ago
Should we support specifying an HMAC key inline in the SigningInfo
string representation? i.e. something like "hmac:<base64-encoded-key>"
.
One use case is being able to use HMAC signing with ndnputchunks
, which already has a command-line option (-S
) for the signing string. Otherwise HMAC signing would be unusable without some modifications to the program, given that the current direction seems to be that HMAC keys are supported only by the in-memory TPM, thus the user cannot "preload" the key into the TPM via ndnsec
and later reference it by name.
Updated by Laqin Fan over 5 years ago
Regarding HMAC validation, I think we could keep using Junxiao's validator config design.
// ---- ValidatorConfig ----
ndn::security::ValidatorConfig validator;
validator.load(R"CONF(
rule {
id 0
for data
checker {
type customized
sig-type hmac-sha256
key-locator {
type name
name /key-name
relation equal
}
}
}
hmac-key {
name /key-name
type base64
base64-string "DDDD" ; base64 encoded HMAC key
}
)CONF";)
- The base64-encoded-key provided inline in the config file, we can load it into TPM alongside with its keyname. 2. To validate data/interest, we look for HMAC key in TPM using the keyname.
Updated by Laqin Fan over 5 years ago
Regarding HMAC validation, I think we could keep using Junxiao's validator config design.
// ---- ValidatorConfig ---- ndn::security::ValidatorConfig validator; validator.load(R"CONF( rule { id 0 for data checker { type customized sig-type hmac-sha256 key-locator { type name name /key-name relation equal } } } hmac-key { name /key-name type base64 base64-string "DDDD" ; base64 encoded HMAC key } )CONF";)
- The base64-encoded-key provided inline in the config file, we can load it into TPM alongside with its keyname. 2. To validate data/interest, we look for HMAC key in TPM using the keyname.
However, Validator cannot get Tpm (tpm is only maintained by KeyChain), so there is no way to load hmac key into tpm in the beginning and later to look for key in TPM to validate data/interest.
How about we create one more container to store hmac key for validation(similar to CertificateStorage)? If so, in verification-helper, we cannot use keyhandle/tpm as the parameter in verifySignature() overloads, but use hmac key instead.
But hmac key should be private, not sure this key container works or not.
Updated by Junxiao Shi over 5 years ago
Should we support specifying an HMAC key inline in the
SigningInfo
string representation? i.e. something like"hmac:<base64-encoded-key>"
.
A question triggered by this is, whether SigningInfo
's stream insertion operator should print the HMAC key like its input format?
My opinion is no, because allowing exporting a private key would be a security vulnerability.
I'd suggest printing hmac:<redacted>
.
Just modify "createIdentity" method, so it would create HMAC "identity" (and key). for HMAC identity, you can throw if one wants to add another key, but I think it should be sufficient.
We can use a single identity /localhost/identity/hmac-sha256
as a container of all HMAC keys. The key name has an additional component that is the SHA256 digest of the key. Certificates are prohibited under this identity.
There's no concern on clashing key names because /localhost/identity
is a reserved namespace, since the era when /localhost/identity/digest-sha256
was introduced.
Updated by Davide Pesavento over 5 years ago
Junxiao Shi wrote:
A question triggered by this is, whether
SigningInfo
's stream insertion operator should print the HMAC key like its input format?
My opinion is no, because allowing exporting a private key would be a security vulnerability.
I'd suggest printinghmac:<redacted>
.
This has already been discussed on gerrit. Currently it prints hmac:/localhost/HMAC/<key-digest>
, which is inconsistent with the syntax accepted by the constructor, but I don't really care, and we don't make any promise that operator<<
follows the same format anyway.
There's no concern on clashing key names because
/localhost/identity
is a reserved namespace, since the era when/localhost/identity/digest-sha256
was introduced.
Is this documented anywhere? Does the code enforce this restriction?
Updated by Alex Afanasyev over 4 years ago
- Related to Feature #5121: Reserve and enforce restriction for /localhost/identity namespace added
Updated by Alex Afanasyev over 4 years ago
There's no concern on clashing key names because
/localhost/identity
is a reserved namespace, since the era when/localhost/identity/digest-sha256
was introduced.Is this documented anywhere? Does the code enforce this restriction?
I don't think it is documented yet and we don't have enforcement too :( (just created #5121)