jacknji11
jacknji11 copied to clipboard
Can't derive ECDH secret
I am trying to derive a shared secret between two EC parties, but I have difficulties providing correct CKM. As there is no CK_ECDH1_DERIVE_PARAMS struct defined, I am trying to allocate native memory myself, but without luck so far.
Can you advise what I am doing wrong?
public long deriveECDH(long session, byte[] otherPublicKey, long privateKeyHandle) {
CKA[] secretTemplate = new CKA[]{
new CKA(CKA.TOKEN, true),
new CKA(CKA.CLASS, CKO.SECRET_KEY),
new CKA(CKA.KEY_TYPE, CKK.GENERIC_SECRET),
new CKA(CKA.SENSITIVE, false),
new CKA(CKA.EXTRACTABLE, true)
};
Memory deriveParam = new Memory((long) NativeLong.SIZE + NativeLong.SIZE + Native.POINTER_SIZE + NativeLong.SIZE + Native.POINTER_SIZE);
int offset = 0;
deriveParam.setLong(offset, CKD.NULL);
offset += NativeLong.SIZE;
deriveParam.setLong(offset, 0L);
offset += NativeLong.SIZE;
deriveParam.setPointer(offset, Pointer.NULL);
offset += Native.POINTER_SIZE;
deriveParam.setLong(offset, otherPublicKey.length);
offset += NativeLong.SIZE;
Memory pubKeyPointer = new Memory(otherPublicKey.length);
pubKeyPointer.write(0, otherPublicKey, 0, otherPublicKey.length);
deriveParam.setPointer(offset, pubKeyPointer);
return CE.DeriveKey(session, new CKM(CKM.ECDH1_DERIVE, deriveParam, (int) deriveParam.size()), privateKeyHandle, secretTemplate);
}
I solved the issue using this struct I created:
package org.pkcs11.jacknji11.jna;
import com.sun.jna.Memory;
import com.sun.jna.NativeLong;
import com.sun.jna.Pointer;
import com.sun.jna.Structure;
import org.pkcs11.jacknji11.CKD;
import java.util.Arrays;
import java.util.List;
/**
* typedef struct CK_ECDH1_DERIVE_PARAMS {
* unsigned long kdf;
* unsigned long ulSharedDataLen;
* unsigned char * pSharedData;
* unsigned long ulPublicDataLen;
* unsigned char * pPublicData;
* } CK_ECDH1_DERIVE_PARAMS;
*/
public class JNA_CK_ECDH1_DERIVE_PARAMS extends Structure {
public NativeLong kdf;
public NativeLong ulSharedDataLen;
public Pointer pSharedData;
public NativeLong ulPublicDataLen;
public Pointer pPublicData;
public JNA_CK_ECDH1_DERIVE_PARAMS(byte[] otherPublicKey) {
super();
this.kdf = new NativeLong(CKD.NULL);
this.ulSharedDataLen = new NativeLong(0);
this.pSharedData = null;
Memory pubKeyPointer = new Memory(otherPublicKey.length);
pubKeyPointer.write(0, otherPublicKey, 0, otherPublicKey.length);
this.ulPublicDataLen = new NativeLong(pubKeyPointer.size());
this.pPublicData = pubKeyPointer;
write();
}
@Override
protected List<String> getFieldOrder() {
return Arrays.asList("kdf", "ulSharedDataLen", "pSharedData", "ulPublicDataLen", "pPublicData");
}
}
It is working on linux an macos machines, but is failing under windows with MECHANISM_PARAM_INVALID error. Any advice what might be at fault?
This is most likely due to the structure packing alignment issue described here. In short, although the PKCS11 v2.40 spec explicitly says:
Cryptoki structures are packed to occupy as little space as is possible. Cryptoki structures SHALL be packed with 1-byte alignment.
in practice (most) Linux libraries don't do this, while (most) Windows libraries do.
Depending on the library used, calling super(ALIGN_NONE);
instead of super();
might make it work on Windows.
The C# Pkcs11Interop library solves both this (structure packing alignment) and another common Cryptoki interop issue (the C ulong
type size) on the library level by doing runtime feature recognition:
public Pkcs11LibraryFactory()
{
if (Platform.NativeULongSize == 4)
{
if (Platform.StructPackingSize == 0)
_factory = new HighLevelAPI40.Factories.Pkcs11LibraryFactory();
else
_factory = new HighLevelAPI41.Factories.Pkcs11LibraryFactory();
}
else
{
if (Platform.StructPackingSize == 0)
_factory = new HighLevelAPI80.Factories.Pkcs11LibraryFactory();
else
_factory = new HighLevelAPI81.Factories.Pkcs11LibraryFactory();
}
}
We could maybe consider doing something similar in the future?
This is most likely due to the structure packing alignment issue described here. In short, although the PKCS11 v2.40 spec explicitly says:
Cryptoki structures are packed to occupy as little space as is possible. Cryptoki structures SHALL be packed with 1-byte alignment.
in practice (most) Linux libraries don't do this, while (most) Windows libraries do.
Depending on the library used, calling
super(ALIGN_NONE);
instead ofsuper();
might make it work on Windows.
I've tried it and it still fails with same error. Maybe the fact that the struct for JNA_CKM
has still the default alignment is at fault (pParameter
- JNA_CK_ECDH1_DERIVE_PARAMS
has alignment changed)?