This walkthrough of the signmsg example shows you how to use SDK APIs to generate an Intel® EPID signature. Signmsg is built during the SDK build.
First, we include headers so we have access to needed declarations.
The prng.h
header provides access to a pseudo-random number generator needed for signing, while the utility headers are used by signmsg
for logging and buffer management. The epid/member/api.h
header provides access to the core member APIs. The epid/common/file_parser.h
header provides an API for parsing buffers formatted according to the various IoT Intel® EPID binary file formats.
We define a stub function responsible for checking that the CA certificate is authorized by the root CA.
IsCaCertAuthorizedByRootCa
is called from main.c
to validate the CA certificate before calling SignMsg
. In an actual implementation, you need to provide an implementation to validate the issuing CA certificate with the CA root certificate before using it in parse functions.
The core signing functionality is contained in SignMsg
.
The SignMsg
parameters are either received by the member, or they are part of the member's configuration. The exceptions are the sig
and sig_len
parameters, which are used to output the signature.
The verifier might send the message to the member or there may be another mechanism to choose the message, but the way the message is communicated is outside the scope of the Intel® EPID scheme.
We use the parameters member_precomp
and member_precomp_is_input
to pass in a pre-computation blob if provided. We can use the pre-computation blob to increase performance when verifying signatures repeatedly with the same group public key.
The member knows the group public key and the member private key.
The member and the verifier agree on the message, basename, hash algorithm, and SigRL that the member uses for signing.
Next we do basic variable setup and argument checking.
We create pointers to resources to be allocated and use the do {} while(0)
idiom so that we can reliably free resources on return from SignMsg
.
We create variables on the stack to hold the group public key and member private key.
Finally we check to make sure that sig
is a vaild pointer.
Next, we authenticate and extract the group public key using EpidParseGroupPubKeyFile.
EpidParseGroupPubKeyFile takes a buffer containing a group public key in issuer binary format and validates that the public key is signed by the private key that corresponds to the provided CA certificate, extracting the key in the process.
We authenticate and extract the signed SigRL using EpidParseSigRlFile.
We use EpidParseSigRlFile to:
To determine the required sig_rl
output buffer size, we provide a null pointer for the output buffer when calling EpidParseSigRlFile. This updates sig_rl_size
with the required size of the output buffer.
After we find out the required size of the sig_rl
, we allocate a buffer for the sig_rl
. Then we fill the buffer using EpidParseSigRlFile.
Next, we fill the member private key.
If the member private key is compressed, then we decompress it using EpidDecompressPrivKey before it can be passed to the member APIs. To determine if the member private key is compressed, we check if it is the known size of a compressed key.
If the key size is not the size of a known format, we return an error.
Next, we create a pseudo-random number generator.
prng
should be a cryptographically secure random number generator.
Now that the inputs have been prepared, we create a member context using EpidMemberCreate.
If a pre-computation blob is provided to the top level application, we use it. Otherwise, we pass in NULL
.
We serialize pre-computed member data using EpidMemberWritePrecomp.
The serialized member pre-computation blob can be used to greatly increase performance of EpidMemberCreate in future sessions if the same member private key is used.
Next, if a basename is specified, we register it with EpidRegisterBaseName so that the member can use it.
In a typical use case, to prevent loss of privacy, the member keeps a list of basenames that correspond to authorized verifiers. The member signs a message with a basename only if the basename is in the member's basename list.
Then we set the hash algorithm to be used by the member using EpidMemberSetHashAlg.
After the hash algorithm is set, future calls to EpidSign
will use the same algorithm.
Next, we sign the message, generating an Intel® EPID signature.
To create a signature, first we find out the required size of the signature using EpidGetSigSize. Then we allocate a buffer for the signature and fill the buffer using EpidSign.
It is important to compute signature size after loading sig_rl
because the signature size varies with the size of the SigRL.
Finally, we clean up and exit.
If we made it past signing without an error, we set the return code appropriately and fall out of the do-while loop. If there was an error earlier, all breaks in the do-while loop bring us to this point with an error status.
Next, we free the allocated resources. EpidMemberDelete deletes an existing member context.
We return from SignMsg with the success or error status.
This concludes the signmsg
walkthrough. Now you should be able to generate an Intel® EPID signature that proves a member's group membership to a verifier without revealing the member's identity.
To learn more about the SDK APIs see the API Reference. To learn more about the Intel® EPID scheme see Introduction to the Intel® EPID Scheme in the documentation.