Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Hot to use mechanisms CKM_ECDH1_DERIVE with pkcs11interop

I bought a NitroKey HSM and wanted to derive a secret with EC. Previosly question

For this, I want to use the CKM_ECDH1_DERIVE mechanisms. Which is supported by this HSM, see:

Referring to the PKCS#11 specification this must be considered:

  1. The mechanism CKM_ECDH1_DERIVE must be used with the function Derive (Page 188)
  2. The mechanism CKM_ECDH1_DERIVE expects parameter CK_ECDH1_DERIVE_PARAMS (Page 222) with this arguments:
    1. kdf: Key derivation function used on the shared secret value (CKD)
    2. sharedData: Some data shared between the two parties
    3. publicData: Other party's EC public key value
  3. The function DeriveKey expects these arguments:
    1. Mechanism CKM.CKM_ECDH1_DERIVE
    2. ObjectHandle PrivateKey
    3. ObjectAttributes (Page 338)
      1. CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
      2. CKA.CKA_KEY_TYPE -> CKK.CKK_GENERIC_SECRET
      3. But "However, since these facts are all implicit in the mechanism, there is no need to specify any of them" so these can be null?

Problem

So with this information, I tried to implement a method.

But I get this Error:

Net.Pkcs11Interop.Common.Pkcs11Exception : Method C_DeriveKey returned CKR_TEMPLATE_INCOMPLETE

at Session.DeriveKey.

Explanation of CKR_TEMPLATE_INCOMPLETE (Page 64):

If the attribute values in the supplied template, together with any default attribute values and any attribute values contributed to the object by the object-creation function itself, are insufficient to fully specify the object to create, then the attempt should fail with the error code CKR_TEMPLATE_INCOMPLETE.

and here (Page 98)

CKR_TEMPLATE_INCOMPLETE: The template specified for creating an object is incomplete, and lacks some necessary attributes. See Section 10.1 for more information.

But I used the nesseary attributes:

  1. CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
  2. CKA.CKA_KEY_TYPE -> CKK.CKK_GENERIC_SECRET

Ideas?

Code

private const string LibraryPath = @"C:\Windows\System32\opensc-pkcs11.dll";

public static byte[] Derive(string privateEc, string publicEc)
{
    Func<string, Session, CKO, ObjectHandle> getObjectHandle = (label, session, keyType) =>
    {
        var objectAttributes = new List<ObjectAttribute>
        {
            new ObjectAttribute(CKA.CKA_CLASS, keyType),
            new ObjectAttribute(CKA.CKA_LABEL, label),
            new ObjectAttribute(CKA.CKA_TOKEN, true)
        };

        return session.FindAllObjects(objectAttributes).First();
    };

    Func<ObjectHandle, Session, CKA, byte[]> getDataFromObject = (handle, session, type) =>
    {
        var attributes = new List<ulong> {(ulong) type};
        var requiredAttributes = session.GetAttributeValue(handle, attributes);
        return requiredAttributes[0].GetValueAsByteArray();
    };

    using (Pkcs11 pk = new Pkcs11(LibraryPath, false))
    {
        var slot = pk.GetSlotList(false).First();

        using (Session session = slot.OpenSession(false))
        {
            session.Login(CKU.CKU_USER, UserPin);

            var objectPrivate = getObjectHandle(privateEc, session, CKO.CKO_PRIVATE_KEY);
            var objectPublic = getObjectHandle(publicEc, session, CKO.CKO_PUBLIC_KEY);

            var publicKey = getDataFromObject(objectPublic, session, CKA.CKA_VALUE);

            byte[] data = session.GenerateRandom(32);
            var mechanism = new Mechanism(CKM.CKM_ECDH1_DERIVE, new CkEcdh1DeriveParams(1000, data, publicKey));

            var deriveAttributes = new List<ObjectAttribute>
            {
                new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY),
                new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET),
            };

            var derivedKey = session.DeriveKey(mechanism, objectPrivate, deriveAttributes);

            var derivedSecret = getDataFromObject(derivedKey, session, CKA.CKA_VALUE);

            Console.Out.WriteLine(Convert.ToBase64String(derivedSecret));

            return derivedSecret;
        }
    }
}

See also the gist (same code) https://gist.github.com/dhcgn/4ea235cdb20155ec5ea9dc9bbf3c9887

Update

Now with updated List of ObjectAttributes (thx to the answer) I get the exception Net.Pkcs11Interop.Common.Pkcs11Exception : Method C_DeriveKey returned CKR_DATA_LEN_RANGE.

CKR_DATA_LEN_RANGE: The plaintext input data to a cryptographic operation has a bad length. Depending on the operation’s mechanism, this could mean that the plaintext data is too short, too long, or is not a multiple of some particular blocksize. This return value has higher priority than CKR_DATA_INVALID.

For CKA.CKA_VALUE_LEN I tried different values with no success:

CKA_VALUE_LEN
-------------
24 (192)
40 (320)
48 (384)

I stumble upon the public key, I not sure if I extract the public key the right way. Because it has a length of 664 Bit.

CKA.CKA_VALUE of CKO.CKO_PUBLIC_KEY (664 Bit):

BFEEelKE3TrpE3e3f5nJATxEZrU0UeauhV/dFZXeXz5gqgZjuCtkJaUTainC/Mh357x3FyO7sGoPhzokD34oj5PJs0ItvATIKYtzvwaUkdZlDc0=

Extracted with the pkcs15-tool (864 Bit)

pkcs15-tool.exe --read-public-key 20
-----BEGIN PUBLIC KEY-----
MGowFAYHKoZIzj0CAQYJKyQDAwIIAQEJA1IABHpShN066RN3t3+ZyQE8RGa1NFHm
roVf3RWV3l8+YKoGY7grZCWlE2opwvzId+e8dxcju7BqD4c6JA9+KI+TybNCLbwE
yCmLc78GlJHWZQ3N
-----END PUBLIC KEY-----
  • Why is the public key different between pkcs15-tool and CKO.CKO_PUBLIC_KEY?
  • Which format does CkEcdh1DeriveParams expect for publicData?
  • Do I extract the publicData the right way? Or what is the right way?
  • Must the value for CKA.CKA_VALUE_LEN equal to the lenght of my EC (320 Bit)?
like image 921
hdev Avatar asked Feb 21 '17 21:02

hdev


1 Answers

That's a very nice and complete description of a problem.

CKR_TEMPLATE_INCOMPLETE is always very painful to deal with because almost every library vendor expect different set of attributes to be provided and this error does not reveal which exact attributes are missing.

After a quick look at OpenSC source code I would try with following template:

var deriveAttributes = new List<ObjectAttribute>
{
    new ObjectAttribute(CKA.CKA_TOKEN, false),
    new ObjectAttribute(CKA.CKA_CLASS, CKO.CKO_SECRET_KEY),
    new ObjectAttribute(CKA.CKA_KEY_TYPE, CKK.CKK_GENERIC_SECRET),
    new ObjectAttribute(CKA.CKA_SENSITIVE, false),
    new ObjectAttribute(CKA.CKA_EXTRACTABLE, true),
    new ObjectAttribute(CKA.CKA_ENCRYPT, true),
    new ObjectAttribute(CKA.CKA_DECRYPT, true),
    new ObjectAttribute(CKA.CKA_WRAP, true),
    new ObjectAttribute(CKA.CKA_UNWRAP, true),
    new ObjectAttribute(CKA.CKA_VALUE_LEN, ???)
};

However I am not sure what is the correct value of CKA_VALUE_LEN attribute.

like image 200
jariq Avatar answered Oct 13 '22 20:10

jariq