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:
- The mechanism
CKM_ECDH1_DERIVE
must be used with the functionDerive
(Page 188) - The mechanism
CKM_ECDH1_DERIVE
expects parameterCK_ECDH1_DERIVE_PARAMS
(Page 222) with this arguments:- kdf: Key derivation function used on the shared secret value (CKD)
- sharedData: Some data shared between the two parties
- publicData: Other party's EC public key value
- The function
DeriveKey
expects these arguments:- Mechanism CKM.CKM_ECDH1_DERIVE
- ObjectHandle PrivateKey
- ObjectAttributes (Page 338)
- CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
- CKA.CKA_KEY_TYPE -> CKK.CKK_GENERIC_SECRET
- 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:
- CKA.CKA_CLASS -> CKO.CKO_SECRET_KEY
- 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
andCKO.CKO_PUBLIC_KEY
? - Which format does
CkEcdh1DeriveParams
expect forpublicData
? - 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
)?