Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Can't derive ECDH secret #51

Open
pentiak opened this issue May 11, 2022 · 4 comments
Open

Can't derive ECDH secret #51

pentiak opened this issue May 11, 2022 · 4 comments

Comments

@pentiak
Copy link

pentiak commented May 11, 2022

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);
}
@pentiak
Copy link
Author

pentiak commented Sep 21, 2022

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?

@Bragolgirith
Copy link
Contributor

Bragolgirith commented Sep 25, 2022

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.

@Bragolgirith
Copy link
Contributor

Bragolgirith commented Sep 25, 2022

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?

@pentiak
Copy link
Author

pentiak commented Sep 26, 2022

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.

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)?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

2 participants