How to use C to perform ECC operations with the eHSM module

Last updated November 28, 2018

This example demonstrates how to use C in order to perform elliptic curve cryptography with the eHSM module. Compared to its counterpart in Java, the C example is significantly more work, but with a few helper functions can be quite manageable. It’s also way more flexible than the Java interface.

For the full source code, refer to the github repository.

For simplicity, we’re leaving out the boilerplate dynamic library loading code.

First, we need to create a session and login to the eHSM:

    CK_SESSION_HANDLE hSessionRW;
    rv = EHSM_FPTR(C_OpenSession(slotId, CKF_SERIAL_SESSION | CKF_RW_SESSION, NULL_PTR, NULL_PTR, &hSessionRW));
    if (rv == CKR_OK) {
        rv = EHSM_FPTR(C_Login(hSessionRW, CKU_USER, (CK_UTF8CHAR_PTR) argv[1], strlen(argv[1])));
        ...

This session is then used as a parameter in most the cryptographic function calls.

Generate a test key pair, in this case using the NIST curve secp256r1 identified by its standard object id.

    CK_MECHANISM mechanism = {CKM_EC_KEY_PAIR_GEN, NULL_PTR, 0};
    CK_KEY_TYPE keyType = CKK_EC;
    CK_BYTE oidP256[] = {0x06, 0x08, 0x2A, 0x86, 0x48, 0xCE, 0x3D, 0x03, 0x01, 0x07};
    CK_BYTE label[] = "example1_test";
    CK_BYTE id[] = {99};
    CK_BBOOL bFalse = CK_FALSE;
    CK_BBOOL bTrue = CK_TRUE;

    CK_ATTRIBUTE pukAttribs[] = {
            {CKA_EC_PARAMS, NULL,            0},
            {CKA_LABEL,    &label[0],        sizeof(label)},
            {CKA_ID,       &id[0],           sizeof(id)},
            {CKA_KEY_TYPE, &keyType,         sizeof(keyType)},
            {CKA_VERIFY,   &bTrue,           sizeof(bTrue)},
            {CKA_ENCRYPT,  &bFalse,          sizeof(bFalse)},
            {CKA_WRAP,     &bFalse,          sizeof(bFalse)},
            {CKA_TOKEN,    &bTokenPuk,       sizeof(bTokenPuk)},
            {CKA_PRIVATE,  &isPubKeyPrivate, sizeof(isPubKeyPrivate)}
    };
    CK_ATTRIBUTE prkAttribs[] = {
            {CKA_LABEL,       &label[0],         sizeof(label)},
            {CKA_ID,          &id[0],            sizeof(id)},
            {CKA_KEY_TYPE,    &keyType,          sizeof(keyType)},
            {CKA_SIGN,        &bTrue,            sizeof(bTrue)},
            {CKA_DECRYPT,     &bFalse,           sizeof(bFalse)},
            {CKA_UNWRAP,      &bFalse,           sizeof(bFalse)},
            {CKA_SENSITIVE,   &bTrue,            sizeof(bTrue)},
            {CKA_TOKEN,       &bTokenPrk,        sizeof(bTokenPrk)},
            {CKA_PRIVATE,     &isPrivKeyPrivate, sizeof(isPrivKeyPrivate)},
            {CKA_EXTRACTABLE, &bFalse,           sizeof(bFalse)}
    };

    pukAttribs[0].pValue = oidP256;
    pukAttribs[0].ulValueLen = sizeof(oidP256);
    *hPuk = CK_INVALID_HANDLE;
    *hPrk = CK_INVALID_HANDLE;
    return EHSM_FPTR(C_GenerateKeyPair(hSession, &mechanism,
                                       pukAttribs, sizeof(pukAttribs) / sizeof(CK_ATTRIBUTE),
                                       prkAttribs, sizeof(prkAttribs) / sizeof(CK_ATTRIBUTE),
                                       hPuk, hPrk));

Attributes are properties set on objects stored in the PKCS#11 HSM. Their values depend on what the intended use of the object will be and does require some thought to get it right. See some of our other posts on PKCS#11 Attributes and recommendations for them.

Once the key pair is generated, we use their handles to sign and verify (using the CKM_ECDSA mechanism in this example):

CK_RV rvSignVerifyData(CK_SESSION_HANDLE hSession, CK_MECHANISM_TYPE mechanismType, CK_OBJECT_HANDLE hPrivateKey,
                       CK_OBJECT_HANDLE hPublicKey) {
    CK_MECHANISM mechanism = {mechanismType, NULL_PTR, 0};
    CK_BYTE data[] = {"helloworld"};
    CK_BYTE signature[256];
    CK_ULONG ulSignatureLen = 0;

    CK_RV rv = EHSM_FPTR(C_SignInit(hSession, &mechanism, hPrivateKey));
    if (rv == CKR_OK) {
        ulSignatureLen = sizeof(signature);
        rv = EHSM_FPTR(C_Sign(hSession, data, sizeof(data), signature, &ulSignatureLen));
        if (rv == CKR_OK) {
            fprintf(stdout, "Signed data\n");
            rv = EHSM_FPTR(C_VerifyInit(hSession, &mechanism, hPublicKey));
            if (rv == CKR_OK) {
                rv = EHSM_FPTR(C_Verify(hSession, data, sizeof(data), signature, ulSignatureLen));
                if (rv == CKR_OK) {
                    fprintf(stdout, "Verified data\n");
                }
            }
        }
    }
    return rv;
}

Finally it’s good practice to cleanup once we’re done:

    EHSM_FPTR(C_Finalize(NULL_PTR));
    vCloseLibrary(pModule);

C_Finalize will close open sessions and logout from the device.

Subscribe to receive updates

* indicates required