Add ECDH for X25519 (#123)

* Add ECDH for X25519

* Fix gnu compiler error

It doesn't like jumping things it should not care about.
Try using a different checkout code
Still track down how to do OpenSSL 1.1 on appveyor

* Fix errors

Fix compiler error - context error
Fix crash from missing - error ptr == null

* Fix MBEDTLS crash

Need to clear a pointer in one case and change the return error for unknown curves.

* Remove support for Openssl pre 1.1

* Deal with flag for doing compressed vs uncompressed points
* Use EVP everywhere but ECDSA signing - should do that too

* Correct no context compiler error

* Run clang-format
diff --git a/.appveyor.yml b/.appveyor.yml
index 3bc14b3..0666f66 100644
--- a/.appveyor.yml
+++ b/.appveyor.yml
@@ -29,6 +29,7 @@
 before_build:
   - set PATH=c:\OpenSSL-v111-Win64\bin;%PATH%
   - set OPENSSL_ROOT_DIR=c:\OpenSSL-v111-Win64
+  - set CMAKE_FRAMEWORK_PATH=c:\OpenSSL-v111-Win64
   - cmake --version
   - mkdir build
   - cd build
diff --git a/.github/workflows/cancel.yml b/.github/workflows/cancel.yml
new file mode 100644
index 0000000..3cf5cc5
--- /dev/null
+++ b/.github/workflows/cancel.yml
@@ -0,0 +1,18 @@
+name: Cancel
+on:
+  push:
+    branches:
+      - master
+  pull_request:
+    branches:    
+      - '*'
+jobs:
+  cancel:
+    name: 'Cancel Previous Runs'
+    runs-on: ubuntu-latest
+    timeout-minutes: 3
+    steps:
+      - uses: styfle/cancel-workflow-action@0.3.1
+        with:
+          workflow_id: 479426
+          access_token: ${{ github.token }}
diff --git a/.github/workflows/main.yml b/.github/workflows/main.yml
index 907265a..8981477 100644
--- a/.github/workflows/main.yml
+++ b/.github/workflows/main.yml
@@ -41,7 +41,7 @@
             COMPILER: gcc

 

     steps:

-      - uses: actions/checkout@v1

+      - uses: actions/checkout@v2

 

       - name: setup (windows)

         if: startsWith(matrix.os, 'windows')

diff --git a/CMakeLists.txt b/CMakeLists.txt
index b1eb816..0b57348 100644
--- a/CMakeLists.txt
+++ b/CMakeLists.txt
@@ -331,6 +331,9 @@
 message(STATUS "COSE_C_USE_LEAK_SANITIZER:.......${COSE_C_USE_LEAK_SANITIZER}")
 message(STATUS "COSE_C_USE_UNDEFINED_SANITIZER:..${COSE_C_USE_UNDEFINED_SANITIZER}")
 message(STATUS "COSE_C_VALGRIND_MEMORY_CHECK:....${COSE_C_VALGRIND_MEMORY_CHECK}")
+if(COSE_C_USE_OPENSSL)
+message(STATUS "OpenSSL .........................${OPENSSL_LIBRARIES}")
+endif()
 message(STATUS "project_cn_cbor_SOURCE_DIR:......${project_cn_cbor_SOURCE_DIR}")
 message(STATUS "project_cn_cbor_BINARY_DIR:......${project_cn_cbor_BINARY_DIR}")
 message(STATUS "project_mbedtls_SOURCE_DIR:......${project_mbedtls_SOURCE_DIR}")
diff --git a/include/cose/cose.h b/include/cose/cose.h
index 0cc424c..c9a5783 100644
--- a/include/cose/cose.h
+++ b/include/cose/cose.h
@@ -222,9 +222,9 @@
  * Functions dealing with keys
  */
 
-const int COSE_KEY_FL_OWN = 0x1;	// Cede ownership of the key to the libraray
-									// Only neede for MBEDTLS as OpenSSL does reference counts
-	
+const int COSE_KEY_FL_OWN =
+	0x1;  // Cede ownership of the key to the libraray
+		  // Only neede for MBEDTLS as OpenSSL does reference counts
 
 HCOSE_KEY COSE_KEY_FromCbor(cn_cbor* pcborKey,
 	CBOR_CONTEXT_COMMA cose_errback* perror);
@@ -235,8 +235,8 @@
 	CBOR_CONTEXT_COMMA cose_errback* perror);
 #endif
 #ifdef COSE_C_USE_MBEDTLS
-HCOSE_KEY COSE_KEY_FromMbedKeypair(mbedtls_ecp_keypair *,
-	cn_cbor *  pcborKey,
+HCOSE_KEY COSE_KEY_FromMbedKeypair(mbedtls_ecp_keypair*,
+	cn_cbor* pcborKey,
 	int flags,
 	CBOR_CONTEXT_COMMA cose_errback* perror);
 #endif
diff --git a/src/CoseKey.cpp b/src/CoseKey.cpp
index 236c05e..1becefc 100644
--- a/src/CoseKey.cpp
+++ b/src/CoseKey.cpp
@@ -138,7 +138,7 @@
 #endif
 
 #ifdef COSE_C_USE_MBEDTLS
-HCOSE_KEY COSE_KEY_FromMbedKeypair(mbedtls_ecp_keypair * mbedtls_keypair,
+HCOSE_KEY COSE_KEY_FromMbedKeypair(mbedtls_ecp_keypair *mbedtls_keypair,
 	cn_cbor *pcborKey,
 	int flags,
 	CBOR_CONTEXT_COMMA cose_errback *perror)
diff --git a/src/Encrypt.cpp b/src/Encrypt.cpp
index 37f74bf..07a506a 100644
--- a/src/Encrypt.cpp
+++ b/src/Encrypt.cpp
@@ -533,6 +533,11 @@
 	CHECK_CONDITION(
 		pcose->m_recipientFirst != nullptr, COSE_ERR_INVALID_HANDLE);
 
+	cose_errback coseError;
+	if (perr == nullptr) {
+		perr = &coseError;
+	}
+
 	return _COSE_Enveloped_encrypt(pcose, nullptr, 0, "Encrypt", perr);
 
 errorReturn:
diff --git a/src/Recipient.cpp b/src/Recipient.cpp
index 2682079..cea8db5 100644
--- a/src/Recipient.cpp
+++ b/src/Recipient.cpp
@@ -211,20 +211,22 @@
 	if (fECDH) {
 #ifdef USE_ECDH
 
-		if (pKeyPrivate != nullptr) {
+		if (pKeyPrivate != nullptr && pKeyPrivate->m_cborKey != nullptr) {
 			cn = cn_cbor_mapget_int(pKeyPrivate->m_cborKey, COSE_Key_Type);
 			CHECK_CONDITION((cn != nullptr) && (cn->type == CN_CBOR_UINT),
 				COSE_ERR_INVALID_PARAMETER);
-			CHECK_CONDITION(
-				cn->v.uint == COSE_Key_Type_EC2, COSE_ERR_INVALID_PARAMETER);
+			CHECK_CONDITION(cn->v.uint == COSE_Key_Type_EC2 ||
+								cn->v.uint == COSE_Key_Type_OKP,
+				COSE_ERR_INVALID_PARAMETER);
 		}
 
-		if (pKeyPublic != nullptr) {
+		if (pKeyPublic != nullptr && pKeyPublic->m_cborKey != nullptr) {
 			cn = cn_cbor_mapget_int(pKeyPublic->m_cborKey, COSE_Key_Type);
 			CHECK_CONDITION((cn != nullptr) && (cn->type == CN_CBOR_UINT),
 				COSE_ERR_INVALID_PARAMETER);
-			CHECK_CONDITION(
-				cn->v.uint == COSE_Key_Type_EC2, COSE_ERR_INVALID_PARAMETER);
+			CHECK_CONDITION(cn->v.uint == COSE_Key_Type_EC2 ||
+								cn->v.uint == COSE_Key_Type_OKP,
+				COSE_ERR_INVALID_PARAMETER);
 		}
 
 		if (fSend) {
@@ -1643,7 +1645,7 @@
 		case 0:
 			break;
 
-		case 1:
+		case COSE_PROTECT_ONLY:
 			cn = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_ID);
 			CHECK_CONDITION(cn != nullptr, COSE_ERR_INVALID_PARAMETER);
 			cn2 = cn_cbor_clone(cn, CBOR_CONTEXT_PARAM_COMMA & cbor_err);
@@ -1655,7 +1657,21 @@
 			cn2 = nullptr;
 			break;
 
-		case 2:
+		case COSE_UNPROTECT_ONLY:
+			if (pKey->m_cborKey == nullptr) {
+#ifdef COSE_C_USE_OPENSSL
+				pKey->m_cborKey = EVP_ToCBOR(pKey->m_opensslKey, true,
+#ifdef USE_CBOR_CONTEXT
+					&pKey->m_allocContext,
+#endif
+					perr);
+				if (pKey->m_cborKey == nullptr) {
+					return false;
+				}
+#else
+				return false;
+#endif
+			}
 			cn2 = cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & cbor_err);
 			CHECK_CONDITION_CBOR(cn2 != nullptr, cbor_err);
 			cn = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_Type);
diff --git a/src/cose_int.h b/src/cose_int.h
index 88d9907..d097464 100644
--- a/src/cose_int.h
+++ b/src/cose_int.h
@@ -39,7 +39,7 @@
 	EVP_PKEY *m_opensslKey;
 #endif
 #ifdef COSE_C_USE_MBEDTLS
-	mbedtls_ecp_keypair * m_mbedtls_keypair;
+	mbedtls_ecp_keypair *m_mbedtls_keypair;
 #endif
 } COSE_KEY;
 
@@ -476,13 +476,16 @@
 #define COSE_CounterSign_object 1000
 #define COSE_CounterSign1_object 1001
 
-
 #if defined(COSE_C_USE_OPENSSL) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
 EC_KEY *ECKey_From(COSE_KEY *pKey, int *cbGroup, cose_errback *perr);
+cn_cbor *EVP_ToCBOR(EVP_PKEY *pKey,
+	bool fCompressPoints,
+	CBOR_CONTEXT_COMMA cose_errback *perr);
+EVP_PKEY *EVP_FromKey(COSE_KEY *pKey, CBOR_CONTEXT_COMMA cose_errback *perr);
 #endif
 
 #ifdef COSE_C_USE_MBEDTLS
-mbedtls_ecp_keypair * ECKey_From(COSE_KEY *pKey,
+mbedtls_ecp_keypair *ECKey_From(COSE_KEY *pKey,
 	mbedtls_ecp_keypair *keypair,
 	cose_errback *perr);
 #endif
diff --git a/src/mbedtls.cpp b/src/mbedtls.cpp
index 5626192..f7598eb 100644
--- a/src/mbedtls.cpp
+++ b/src/mbedtls.cpp
@@ -666,14 +666,14 @@
 #define COSE_Key_EC_Y -3
 #define COSE_Key_EC_d -4
 
-mbedtls_ecp_keypair * ECKey_From(COSE_KEY *pKey,
+mbedtls_ecp_keypair *ECKey_From(COSE_KEY *pKey,
 	mbedtls_ecp_keypair *keypair,
 	cose_errback *perr)
 {
 	if (pKey->m_mbedtls_keypair != nullptr) {
 		return pKey->m_mbedtls_keypair;
 	}
-	
+
 	byte rgbKey[MBEDTLS_ECP_MAX_PT_LEN];
 	int cbKey = 0;
 	int cbGroup = 0;
@@ -869,7 +869,7 @@
 	cose_errback *perr)
 {
 	mbedtls_ecp_keypair keypair;
-	mbedtls_ecp_keypair* useKey = nullptr;
+	mbedtls_ecp_keypair *useKey = nullptr;
 	mbedtls_mpi r;
 	mbedtls_mpi s;
 	mbedtls_md_type_t mdType;
@@ -1188,7 +1188,8 @@
 			break;
 
 		default:
-			FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
+			p = nullptr;
+			FAIL_CONDITION(COSE_ERR_UNKNOWN_ALGORITHM);
 	}
 	p = nullptr;
 
diff --git a/src/openssl.cpp b/src/openssl.cpp
index f376fb4..a64a8c7 100644
--- a/src/openssl.cpp
+++ b/src/openssl.cpp
@@ -22,44 +22,60 @@
 #include <openssl/rand.h>
 #include <openssl/bn.h>
 
-static bool FUseCompressed = true;
+/*******************************************/
 
-#if (OPENSSL_VERSION_NUMBER < 0x10100000)
+#define Safe_OPENSSL(handleName, freeFunction)          \
+	class Safe_##handleName {                           \
+		handleName *h;                                  \
+                                                        \
+	   public:                                          \
+		Safe_##handleName() { h = nullptr; }            \
+		Safe_##handleName(handleName *hIn) { h = hIn; } \
+		~Safe_##handleName() { freeFunction(h); }       \
+		handleName *Set(handleName *hIn)                \
+		{                                               \
+			if (h != nullptr) {                         \
+				freeFunction(h);                        \
+			}                                           \
+			h = hIn;                                    \
+			if (hIn != nullptr) {                       \
+				handleName##_up_ref(hIn);               \
+			}                                           \
+			return hIn;                                 \
+		}                                               \
+		bool IsNull() { return h == NULL; }             \
+		operator handleName *() { return h; }           \
+		handleName *operator=(handleName *pIn)          \
+		{                                               \
+			Set(pIn);                                   \
+			return pIn;                                 \
+		}                                               \
+		handleName *Transfer(Safe_##handleName *hIn)    \
+		{                                               \
+			if (h != nullptr) {                         \
+				freeFunction(h);                        \
+			}                                           \
+			h = hIn->h;                                 \
+			hIn->h = nullptr;                           \
+			return h;                                   \
+		}                                               \
+		handleName *operator=(Safe_##handleName hIn)    \
+		{                                               \
+			Set(hIn.h);                                 \
+			return h;                                   \
+		}                                               \
+		handleName *Release()                           \
+		{                                               \
+			handleName *h2 = h;                         \
+			h = nullptr;                                \
+			return h2;                                  \
+		}                                               \
+	};
 
-HMAC_CTX *HMAC_CTX_new()
-{
-	HMAC_CTX *foo = (HMAC_CTX *)malloc(sizeof(HMAC_CTX));
-	if (foo != nullptr) {
-		HMAC_CTX_init(foo);
-	}
-	return foo;
-}
+Safe_OPENSSL(EC_KEY, EC_KEY_free);
+Safe_OPENSSL(EVP_PKEY, EVP_PKEY_free);
 
-void HMAC_CTX_free(HMAC_CTX *foo)
-{
-	if (foo != nullptr)
-		free(foo);
-}
-
-void ECDSA_SIG_get0(const ECDSA_SIG *sig, const BIGNUM **pr, const BIGNUM **ps)
-{
-	if (pr != nullptr)
-		*pr = sig->r;
-	if (ps != nullptr)
-		*ps = sig->s;
-}
-
-int ECDSA_SIG_set0(ECDSA_SIG *sig, BIGNUM *r, BIGNUM *s)
-{
-	if (r == nullptr || s == nullptr)
-		return 0;
-	BN_clear_free(sig->r);
-	BN_clear_free(sig->s);
-	sig->r = r;
-	sig->s = s;
-	return 1;
-}
-#endif
+/**********************************************/
 
 bool AES_CCM_Decrypt(COSE_Enveloped *pcose,
 	int TSize,
@@ -1068,10 +1084,12 @@
 	CHECK_CONDITION(cn != nullptr, COSE_ERR_CBOR);
 
 	if (cn->length > cbOut) {
-		return false;
+		f = false;
 	}
-	for (unsigned int i = 0; i < (unsigned int)TSize / 8; i++) {
-		f |= (cn->v.bytes[i] != rgbOut[i]);
+	else {
+		for (unsigned int i = 0; i < (unsigned int)TSize / 8; i++) {
+			f |= (cn->v.bytes[i] != rgbOut[i]);
+		}
 	}
 
 	COSE_FREE(rgbOut, context);
@@ -1084,21 +1102,96 @@
 #define COSE_Key_EC_Y -3
 #define COSE_Key_EC_d -4
 
-
-EC_KEY *ECKey_From(COSE_KEY *pKey, int *cbGroup, cose_errback *perr)
+EVP_PKEY *EVP_FromKey(COSE_KEY *pKey, CBOR_CONTEXT_COMMA cose_errback *perr)
 {
-	EC_KEY *pNewKey = nullptr;
+	if (pKey->m_opensslKey != nullptr) {
+		return pKey->m_opensslKey;
+	}
 
 	if (false) {
 	errorReturn:
-		if (pNewKey != nullptr) {
-			EC_KEY_free(pNewKey);
+		return nullptr;
+	}
+
+	cn_cbor *keyType = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_Type);
+	CHECK_CONDITION(keyType != NULL && keyType->type == CN_CBOR_UINT,
+		COSE_ERR_INVALID_PARAMETER);
+
+	switch (keyType->v.uint) {
+		case COSE_Key_Type_EC2: {
+			int cbSize;
+			Safe_EC_KEY ecKey = ECKey_From(pKey, &cbSize, perr);
+			CHECK_CONDITION(ecKey != nullptr, perr->err);
+			Safe_EVP_PKEY evpKey = EVP_PKEY_new();
+			CHECK_CONDITION(evpKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
+			CHECK_CONDITION(
+				EVP_PKEY_set1_EC_KEY(evpKey, ecKey) == 1, COSE_ERR_CRYPTO_FAIL);
+			pKey->m_opensslKey = evpKey;
+			EVP_PKEY_up_ref(pKey->m_opensslKey);
+			return evpKey.Release();
 		}
+
+		case COSE_Key_Type_OKP: {
+			int type;
+			cn_cbor *p =
+				cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_OPK_Curve);
+			CHECK_CONDITION(p != nullptr, COSE_ERR_INVALID_PARAMETER);
+
+			switch (p->v.uint) {
+				case COSE_Curve_Ed25519:
+					type = EVP_PKEY_ED25519;
+					break;
+
+				case COSE_Curve_Ed448:
+					type = EVP_PKEY_ED448;
+					break;
+
+				case COSE_Curve_X25519:
+					type = EVP_PKEY_X25519;
+					break;
+
+				case COSE_Curve_X448:
+					type = EVP_PKEY_X448;
+					break;
+
+				default:
+					FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
+			}
+
+			Safe_EVP_PKEY evpKey;
+
+			p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_EC_d);
+			if (p != nullptr) {
+				evpKey = EVP_PKEY_new_raw_private_key(
+					type, nullptr, p->v.bytes, p->length);
+				CHECK_CONDITION(evpKey != nullptr, COSE_ERR_CRYPTO_FAIL);
+			}
+			else {
+				p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_OPK_X);
+				CHECK_CONDITION(p != nullptr, COSE_ERR_INVALID_PARAMETER);
+				evpKey = EVP_PKEY_new_raw_public_key(
+					type, nullptr, p->v.bytes, p->length);
+			}
+
+			pKey->m_opensslKey = evpKey;
+			EVP_PKEY_up_ref(pKey->m_opensslKey);
+			return evpKey.Release();
+		}
+
+		default:
+			FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
+	}
+}
+
+EC_KEY *ECKey_From(COSE_KEY *pKey, int *cbGroup, cose_errback *perr)
+{
+	if (false) {
+	errorReturn:
 		return nullptr;
 	}
 
 	if (pKey->m_opensslKey != nullptr) {
-		EC_KEY *pKeyNew = EVP_PKEY_get1_EC_KEY(pKey->m_opensslKey);
+		Safe_EC_KEY pKeyNew = EVP_PKEY_get1_EC_KEY(pKey->m_opensslKey);
 		CHECK_CONDITION(pKeyNew != nullptr, COSE_ERR_INVALID_PARAMETER);
 		int gid = EC_GROUP_get_curve_name(EC_KEY_get0_group(pKeyNew));
 		switch (gid) {
@@ -1116,9 +1209,9 @@
 
 			default:
 				FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
-		}			
+		}
 
-		return pKeyNew;
+		return pKeyNew.Release();
 	}
 
 	byte rgbKey[512 + 1];
@@ -1127,7 +1220,7 @@
 	int nidGroup = -1;
 	EC_POINT *pPoint = nullptr;
 
-	pNewKey = EC_KEY_new();
+	Safe_EC_KEY pNewKey = EC_KEY_new();
 	CHECK_CONDITION(pNewKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
 
 	p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_EC_Curve);
@@ -1165,7 +1258,7 @@
 	memcpy(rgbKey + 1, p->v.str, p->length);
 
 	p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_EC_Y);
-	CHECK_CONDITION((p != nullptr), COSE_ERR_INVALID_PARAMETER);
+	CHECK_CONDITION(p != nullptr, COSE_ERR_INVALID_PARAMETER);
 	if (p->type == CN_CBOR_BYTES) {
 		rgbKey[0] = POINT_CONVERSION_UNCOMPRESSED;
 		cbKey = (*cbGroup * 2) + 1;
@@ -1181,8 +1274,9 @@
 		cbKey = (*cbGroup) + 1;
 		rgbKey[0] = POINT_CONVERSION_COMPRESSED;
 	}
-	else
+	else {
 		FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
+	}
 
 	pPoint = EC_POINT_new(ecgroup);
 	CHECK_CONDITION(pPoint != nullptr, COSE_ERR_CRYPTO_FAIL);
@@ -1194,9 +1288,7 @@
 
 	p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_EC_d);
 	if (p != nullptr) {
-		BIGNUM *pbn;
-
-		pbn = BN_bin2bn(p->v.bytes, (int)p->length, nullptr);
+		BIGNUM *pbn = BN_bin2bn(p->v.bytes, (int)p->length, nullptr);
 		CHECK_CONDITION(pbn != nullptr, COSE_ERR_CRYPTO_FAIL);
 		CHECK_CONDITION(
 			EC_KEY_set_private_key(pNewKey, pbn) == 1, COSE_ERR_CRYPTO_FAIL);
@@ -1208,24 +1300,24 @@
 	CHECK_CONDITION(EVP_PKEY_set1_EC_KEY(pKey->m_opensslKey, pNewKey) == 1,
 		COSE_ERR_CRYPTO_FAIL);
 
-	return pNewKey;
+	return pNewKey.Release();
 }
 
-COSE_KEY *EC_FromKey(const EC_KEY *pKey, CBOR_CONTEXT_COMMA cose_errback *perr)
+cn_cbor *EC_ToCBOR(const EC_KEY *pKey,
+	bool fUseCompressed,
+	CBOR_CONTEXT_COMMA cose_errback *perr)
 {
 	cn_cbor *pkey = nullptr;
-	const EC_GROUP *pgroup;
 	int cose_group;
 	cn_cbor *p = nullptr;
 	cn_cbor_errback cbor_error;
-	const EC_POINT *pPoint;
 	byte *pbPoint = nullptr;
 	size_t cbSize;
 	byte *pbOut = nullptr;
-	COSE_KEY *coseKey = nullptr;
 	size_t cbX;
+	const EC_POINT *pPoint = nullptr;
 
-	pgroup = EC_KEY_get0_group(pKey);
+	const EC_GROUP *pgroup = EC_KEY_get0_group(pKey);
 	CHECK_CONDITION(pgroup != nullptr, COSE_ERR_INVALID_PARAMETER);
 
 	switch (EC_GROUP_get_curve_name(pgroup)) {
@@ -1256,7 +1348,7 @@
 	pPoint = EC_KEY_get0_public_key(pKey);
 	CHECK_CONDITION(pPoint != nullptr, COSE_ERR_INVALID_PARAMETER);
 
-	if (FUseCompressed) {
+	if (fUseCompressed) {
 		cbSize = EC_POINT_point2oct(
 			pgroup, pPoint, POINT_CONVERSION_COMPRESSED, nullptr, 0, nullptr);
 		CHECK_CONDITION(cbSize > 0, COSE_ERR_CRYPTO_FAIL);
@@ -1293,7 +1385,7 @@
 		cbor_error);
 	p = nullptr;
 
-	if (FUseCompressed) {
+	if (fUseCompressed) {
 		p = cn_cbor_bool_create(
 			pbPoint[0] & 1, CBOR_CONTEXT_PARAM_COMMA & cbor_error);
 		CHECK_CONDITION_CBOR(p != nullptr, cbor_error);
@@ -1324,11 +1416,6 @@
 		cbor_error);
 	p = nullptr;
 
-	coseKey =
-		(COSE_KEY *)COSE_KEY_FromCbor(pkey, CBOR_CONTEXT_PARAM_COMMA perr);
-	CHECK_CONDITION(coseKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
-	pkey = nullptr;
-
 returnHere:
 	if (pbPoint != nullptr) {
 		COSE_FREE(pbPoint, context);
@@ -1339,10 +1426,7 @@
 	if (p != nullptr) {
 		CN_CBOR_FREE(p, context);
 	}
-	if (pkey != nullptr) {
-		CN_CBOR_FREE(pkey, context);
-	}
-	return coseKey;
+	return pkey;
 
 errorReturn:
 	CN_CBOR_FREE(pkey, context);
@@ -1350,20 +1434,110 @@
 	goto returnHere;
 }
 
-/*
-bool ECDSA_Sign(const cn_cbor * pKey)
+cn_cbor *EVP_ToCBOR(EVP_PKEY *pKey,
+	bool fCompressPoints,
+	CBOR_CONTEXT_COMMA cose_errback *perr)
 {
-	byte * digest = nullptr;
-	int digestLen = 0;
-	ECDSA_SIG * sig;
+	cn_cbor_errback cborErr;
+	int type = EVP_PKEY_base_id(pKey);
 
-	EC_KEY * eckey = ECKey_From(pKey);
+	switch (type) {
+		case EVP_PKEY_EC:
+			return EC_ToCBOR(EVP_PKEY_get1_EC_KEY(pKey), fCompressPoints,
+				CBOR_CONTEXT_PARAM_COMMA perr);
 
-	sig = ECDSA_do_sign(digest, digestLen, eckey);
+		case EVP_PKEY_X25519:
+		case EVP_PKEY_X448: {
+			cn_cbor *pkey = nullptr;
+			cn_cbor *temp = nullptr;
+			unsigned char *pbKey = nullptr;
+			if (false) {
+			errorReturn:
+				if (pkey != nullptr) {
+					CN_CBOR_FREE(pkey, context);
+				}
+				if (temp != nullptr) {
+					CN_CBOR_FREE(temp, context);
+				}
+				if (pbKey != nullptr) {
+					COSE_FREE(pbKey, context);
+				}
+				return nullptr;
+			}
+			pkey = cn_cbor_map_create(CBOR_CONTEXT_PARAM_COMMA & cborErr);
+			CHECK_CONDITION_CBOR(pkey != nullptr, cborErr);
+			temp = cn_cbor_int_create(
+				COSE_Key_Type_OKP, CBOR_CONTEXT_PARAM_COMMA & cborErr);
+			CHECK_CONDITION_CBOR(temp != nullptr, cborErr);
+			CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_Type, temp,
+									 CBOR_CONTEXT_PARAM_COMMA & cborErr),
+				cborErr);
+			temp = nullptr;
+			temp = cn_cbor_int_create(
+				type == EVP_PKEY_X25519 ? COSE_Curve_X25519 : COSE_Curve_X448,
+				CBOR_CONTEXT_PARAM_COMMA & cborErr);
+			CHECK_CONDITION_CBOR(temp != nullptr, cborErr);
+			CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_OPK_Curve,
+									 temp, CBOR_CONTEXT_PARAM_COMMA & cborErr),
+				cborErr);
+			temp = nullptr;
+			size_t cbKey;
+			CHECK_CONDITION(
+				EVP_PKEY_get_raw_public_key(pKey, nullptr, &cbKey) == 1,
+				COSE_ERR_CRYPTO_FAIL);
+			pbKey = (unsigned char *)COSE_CALLOC(cbKey, 1, context);
+			CHECK_CONDITION(pbKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
+			CHECK_CONDITION(
+				EVP_PKEY_get_raw_public_key(pKey, pbKey, &cbKey) == 1,
+				COSE_ERR_CRYPTO_FAIL);
+			temp = cn_cbor_data_create2(
+				pbKey, cbKey, 0, CBOR_CONTEXT_PARAM_COMMA & cborErr);
+			CHECK_CONDITION(temp != nullptr, COSE_ERR_OUT_OF_MEMORY);
+			pbKey = nullptr;
+			CHECK_CONDITION_CBOR(cn_cbor_mapput_int(pkey, COSE_Key_OPK_X, temp,
+									 CBOR_CONTEXT_PARAM_COMMA & cborErr),
+				cborErr);
+			temp = nullptr;
+			return pkey;
+		} break;
 
-	return true;
+		default:
+			perr->err = COSE_ERR_INVALID_PARAMETER;
+			return nullptr;
+	}
 }
-*/
+
+#if false
+
+COSE_KEY *EC_FromKey(EC_KEY *pKey, bool fUseCompressed, CBOR_CONTEXT_COMMA cose_errback *perr)
+{
+	COSE_KEY *coseKey = nullptr;
+	cn_cbor *pkey =
+		EC_ToCBOR(pKey, fUseCompressed, CBOR_CONTEXT_PARAM_COMMA perr);
+	if (pkey == nullptr) {
+		return nullptr;
+	}
+
+	Safe_EVP_PKEY evpKey = EVP_PKEY_new();
+	CHECK_CONDITION(evpKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
+	
+	CHECK_CONDITION(EVP_PKEY_set1_EC_KEY(evpKey, pKey) == 1, COSE_ERR_CRYPTO_FAIL);	
+
+	coseKey =
+		(COSE_KEY *)COSE_KEY_FromEVP(evpKey, pkey, CBOR_CONTEXT_PARAM_COMMA perr);
+	CHECK_CONDITION(coseKey != nullptr, COSE_ERR_OUT_OF_MEMORY);
+	pkey = nullptr;
+
+returnHere:
+	if (pkey != nullptr) {
+		CN_CBOR_FREE(pkey, context);
+	}
+	return coseKey;
+
+errorReturn:
+	goto returnHere;
+}
+#endif
 
 bool ECDSA_Sign(COSE *pSigner,
 	int index,
@@ -1550,7 +1724,7 @@
 	cn_cbor_errback cbor_error;
 	EVP_PKEY_CTX *keyCtx = nullptr;
 	EVP_MD_CTX *mdCtx = nullptr;
-	EVP_PKEY *pkey = nullptr;
+	Safe_EVP_PKEY pkey;
 	byte *pbSig = nullptr;
 	int cbSig;
 
@@ -1563,9 +1737,6 @@
 		if (keyCtx != nullptr) {
 			EVP_PKEY_CTX_free(keyCtx);
 		}
-		if (pkey != nullptr) {
-			EVP_PKEY_free(pkey);
-		}
 		if (pbSig != nullptr) {
 			COSE_FREE(pbSig, context);
 		}
@@ -1589,11 +1760,10 @@
 			FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
 	}
 
-	p = cn_cbor_mapget_int(pKeyIn->m_cborKey, COSE_Key_EC_d);
-	CHECK_CONDITION(p != nullptr, COSE_ERR_INVALID_PARAMETER);
-
-	pkey = EVP_PKEY_new_raw_private_key(type, nullptr, p->v.bytes, p->length);
-	CHECK_CONDITION(pkey != nullptr, COSE_ERR_CRYPTO_FAIL);
+	pkey = EVP_FromKey(pKeyIn, CBOR_CONTEXT_PARAM_COMMA perr);
+	if (pkey == nullptr) {
+		goto errorReturn;
+	}
 
 	keyCtx = EVP_PKEY_CTX_new_id(type, nullptr);
 	CHECK_CONDITION(keyCtx != nullptr, COSE_ERR_OUT_OF_MEMORY);
@@ -1629,9 +1799,6 @@
 	if (keyCtx != nullptr) {
 		EVP_PKEY_CTX_free(keyCtx);
 	}
-	if (pkey != nullptr) {
-		EVP_PKEY_free(pkey);
-	}
 	if (pbSig != nullptr) {
 		COSE_FREE(pbSig, context);
 	}
@@ -1646,14 +1813,18 @@
 	size_t cbToSign,
 	cose_errback *perr)
 {
+#ifdef USE_CBOR_CONTEXT
+	cn_cbor_context *context = &pSigner->m_allocContext;
+#endif
 	cn_cbor *pSig;
-	EVP_PKEY *pkey = nullptr;
+	Safe_EVP_PKEY pkey = nullptr;
+	EVP_MD_CTX *pmdCtx = nullptr;
 
 	cn_cbor *p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_OPK_Curve);
 	if (p == nullptr) {
 	errorReturn:
-		if (pkey != nullptr) {
-			EVP_PKEY_free(pkey);
+		if (pmdCtx != nullptr) {
+			EVP_MD_CTX_free(pmdCtx);
 		}
 		return false;
 	}
@@ -1673,16 +1844,15 @@
 			FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
 	}
 
-	p = cn_cbor_mapget_int(pKey->m_cborKey, COSE_Key_OPK_X);
-	CHECK_CONDITION(p != nullptr, COSE_ERR_INVALID_PARAMETER);
-
-	pkey = EVP_PKEY_new_raw_public_key(type, nullptr, p->v.bytes, p->length);
-	CHECK_CONDITION(pkey != nullptr, COSE_ERR_CBOR);
+	pkey = EVP_FromKey(pKey, CBOR_CONTEXT_PARAM_COMMA perr);
+	if (pkey == nullptr) {
+		goto errorReturn;
+	}
 
 	pSig = _COSE_arrayget_int(pSigner, index);
 	CHECK_CONDITION(pSig != nullptr, COSE_ERR_INVALID_PARAMETER);
 
-	EVP_MD_CTX *pmdCtx = EVP_MD_CTX_new();
+	pmdCtx = EVP_MD_CTX_new();
 	EVP_PKEY_CTX *keyCtx = EVP_PKEY_CTX_new_id(type, nullptr);
 
 	CHECK_CONDITION(
@@ -1696,9 +1866,6 @@
 	if (pmdCtx != nullptr) {
 		EVP_MD_CTX_free(pmdCtx);
 	}
-	if (pkey != nullptr) {
-		EVP_PKEY_free(pkey);
-	}
 
 	return true;
 }
@@ -1799,71 +1966,112 @@
 	size_t *pcbSecret,
 	CBOR_CONTEXT_COMMA cose_errback *perr)
 {
-	EC_KEY *peckeyPrivate = nullptr;
-	EC_KEY *peckeyPublic = nullptr;
-	int cbGroup;
-	int cbsecret;
-	byte *pbsecret = nullptr;
-	bool fRet = false;
+	EVP_PKEY *evpPublic = nullptr;
+	EVP_PKEY *evpPrivate = nullptr;
+	EVP_PKEY_CTX *ctx = nullptr;
 
-	peckeyPublic = ECKey_From(pKeyPublic, &cbGroup, perr);
-	if (peckeyPublic == nullptr) {
+	if (false) {
+	errorReturn:
+		if (ctx != nullptr) {
+			EVP_PKEY_CTX_free(ctx);
+		}
+		if (evpPublic != nullptr) {
+			EVP_PKEY_free(evpPublic);
+		}
+		return false;
+	}
+
+	evpPublic = EVP_FromKey(pKeyPublic, CBOR_CONTEXT_PARAM_COMMA perr);
+	if (evpPublic == nullptr) {
 		goto errorReturn;
 	}
 
+	bool fCompressPoints = true;
+
 	if (*ppKeyPrivate == nullptr) {
-		{
-			cn_cbor *pCompress = _COSE_map_get_int(
-				pRecipient, COSE_Header_UseCompressedECDH, COSE_BOTH, perr);
-			if (pCompress == nullptr) {
-				FUseCompressed = false;
-			}
-			else {
-				FUseCompressed = (pCompress->type == CN_CBOR_TRUE);
-			}
+		// Generate an ephemeral key for the key agreement.
+
+		int type = EVP_PKEY_base_id(evpPublic);
+		cn_cbor *pCompress = _COSE_map_get_int(
+			pRecipient, COSE_Header_UseCompressedECDH, COSE_DONT_SEND, perr);
+		if (pCompress == nullptr) {
+			fCompressPoints = true;
 		}
-		peckeyPrivate = EC_KEY_new();
-		EC_KEY_set_group(peckeyPrivate, EC_KEY_get0_group(peckeyPublic));
-		CHECK_CONDITION(
-			EC_KEY_generate_key(peckeyPrivate) == 1, COSE_ERR_CRYPTO_FAIL);
-		*ppKeyPrivate =
-			EC_FromKey(peckeyPrivate, CBOR_CONTEXT_PARAM_COMMA perr);
-		if (*ppKeyPrivate == nullptr) {
+		else {
+			fCompressPoints = (pCompress->type == CN_CBOR_TRUE);
+		}
+
+		switch (type) {
+			case EVP_PKEY_EC: {
+				EC_KEY *peckeyPrivate = EC_KEY_new();
+				EC_KEY *peckeyPublic = EVP_PKEY_get0_EC_KEY(evpPublic);
+				EC_KEY_set_group(
+					peckeyPrivate, EC_KEY_get0_group(peckeyPublic));
+				CHECK_CONDITION(EC_KEY_generate_key(peckeyPrivate) == 1,
+					COSE_ERR_CRYPTO_FAIL);
+				evpPrivate = EVP_PKEY_new();
+				EVP_PKEY_set1_EC_KEY(evpPrivate, peckeyPrivate);
+			} break;
+
+			case EVP_PKEY_X25519:
+			case EVP_PKEY_X448: {
+				EVP_PKEY_CTX *ctx2 = EVP_PKEY_CTX_new_id(type, nullptr);
+				CHECK_CONDITION(ctx2 != nullptr, COSE_ERR_OUT_OF_MEMORY);
+				// CHECK_CONDITION(
+				//	EVP_PKEY_paramgen_init(ctx2) == 1, COSE_ERR_CRYPTO_FAIL);
+				CHECK_CONDITION(
+					EVP_PKEY_keygen_init(ctx2) == 1, COSE_ERR_CRYPTO_FAIL);
+				CHECK_CONDITION(
+					EVP_PKEY_keygen(ctx2, &evpPrivate), COSE_ERR_CRYPTO_FAIL);
+			} break;
+
+			default:
+				FAIL_CONDITION(COSE_ERR_INVALID_PARAMETER);
+		}
+
+		cn_cbor *pcborPrivate = EVP_ToCBOR(
+			evpPrivate, fCompressPoints, CBOR_CONTEXT_PARAM_COMMA perr);
+		if (pcborPrivate == nullptr) {
 			goto errorReturn;
 		}
+		COSE_KEY *pPrivateKey = (COSE_KEY *)COSE_KEY_FromEVP(
+			evpPrivate, pcborPrivate, CBOR_CONTEXT_PARAM_COMMA perr);
+		if (pPrivateKey == nullptr) {
+			CN_CBOR_FREE(pcborPrivate, context);
+			goto errorReturn;
+		}
+		*ppKeyPrivate = pPrivateKey;
 	}
 	else {
-		peckeyPrivate = ECKey_From(*ppKeyPrivate, &cbGroup, perr);
-		if (peckeyPrivate == nullptr) {
+		//  Use the passed in sender key
+		evpPrivate = EVP_FromKey(*ppKeyPrivate, CBOR_CONTEXT_PARAM_COMMA perr);
+		if (evpPrivate == nullptr) {
 			goto errorReturn;
 		}
 	}
 
-	pbsecret = (byte *)COSE_CALLOC(cbGroup, 1, context);
-	CHECK_CONDITION(pbsecret != nullptr, COSE_ERR_OUT_OF_MEMORY);
+	ctx = EVP_PKEY_CTX_new(evpPrivate, nullptr);
+	CHECK_CONDITION(ctx != nullptr, COSE_ERR_OUT_OF_MEMORY);
 
-	cbsecret = ECDH_compute_key(pbsecret, cbGroup,
-		EC_KEY_get0_public_key(peckeyPublic), peckeyPrivate, nullptr);
-	CHECK_CONDITION(cbsecret > 0, COSE_ERR_CRYPTO_FAIL);
+	CHECK_CONDITION(EVP_PKEY_derive_init(ctx) > 0, COSE_ERR_CRYPTO_FAIL);
+	CHECK_CONDITION(
+		EVP_PKEY_derive_set_peer(ctx, evpPublic) > 0, COSE_ERR_CRYPTO_FAIL);
+	size_t skeylen;
+	CHECK_CONDITION(
+		EVP_PKEY_derive(ctx, nullptr, &skeylen) > 0, COSE_ERR_CRYPTO_FAIL);
+	byte *skey = static_cast<byte *>(COSE_CALLOC(skeylen, 1, context));
+	CHECK_CONDITION(skey != nullptr, COSE_ERR_OUT_OF_MEMORY);
+	CHECK_CONDITION(
+		EVP_PKEY_derive(ctx, skey, &skeylen) > 0, COSE_ERR_CRYPTO_FAIL);
 
-	*ppbSecret = pbsecret;
-	*pcbSecret = cbsecret;
-	pbsecret = nullptr;
-
-	fRet = true;
-
-errorReturn:
-	if (pbsecret != nullptr) {
-		COSE_FREE(pbsecret, context);
-	}
-	if (peckeyPublic != nullptr) {
-		EC_KEY_free(peckeyPublic);
-	}
-	if (peckeyPrivate != nullptr) {
-		EC_KEY_free(peckeyPrivate);
+	if (ctx != nullptr) {
+		EVP_PKEY_CTX_free(ctx);
 	}
 
-	return fRet;
+	*ppbSecret = skey;
+	*pcbSecret = skeylen;
+
+	return true;
 }
 
 #endif	// COSE_C_USE_OPENSSL
diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt
index 85b893b..112c2ed 100644
--- a/test/CMakeLists.txt
+++ b/test/CMakeLists.txt
@@ -110,6 +110,10 @@
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
   COMMAND cose_test --dir Examples/ecdh-wrap-examples)
 add_test(
+  NAME ecdh-x25519
+  WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
+  COMMAND cose_test --dir Examples/X25519-tests)
+add_test(
   NAME sign
   WORKING_DIRECTORY ${CMAKE_SOURCE_DIR}
   COMMAND cose_test --dir Examples/sign-tests)
diff --git a/test/test.cpp b/test/test.cpp
index f515018..a995d22 100644
--- a/test/test.cpp
+++ b/test/test.cpp
@@ -21,7 +21,6 @@
 #include "json.h"
 #include "test.h"
 
-
 #ifdef COSE_C_USE_OPENSSL
 #include <openssl/ec.h>
 #endif
@@ -322,7 +321,7 @@
 	int kty;
 	int operation;
 	int keyNew;
-} RgStringKeys[10] = {{"kty", 0, OPERATION_IGNORE, COSE_Key_Type},
+} RgStringKeys[12] = {{"kty", 0, OPERATION_IGNORE, COSE_Key_Type},
 	{"kid", 0, OPERATION_NONE, COSE_Key_ID},
 	{"crv", 2, OPERATION_STRING, COSE_Key_EC2_Curve},
 	{"x", 2, OPERATION_BASE64, COSE_Key_EC2_X},
@@ -330,7 +329,9 @@
 	{"k", 4, OPERATION_BASE64, -1},
 	{"crv", COSE_Key_Type_OKP, OPERATION_STRING, COSE_Key_OPK_Curve},
 	{"x_hex", COSE_Key_Type_OKP, OPERATION_HEX, COSE_Key_OPK_X},
-	{"d_hex", COSE_Key_Type_OKP, OPERATION_HEX, -4}};
+	{"x", COSE_Key_Type_OKP, OPERATION_BASE64, COSE_Key_OPK_X},
+	{"d_hex", COSE_Key_Type_OKP, OPERATION_HEX, -4},
+	{"d", COSE_Key_Type_OKP, OPERATION_BASE64, -4}};
 
 bool SetAttributes(HCOSE hHandle,
 	const cn_cbor* pAttributes,
@@ -867,6 +868,7 @@
 		COSE_KEY_FromCbor(pKeyOut, CBOR_CONTEXT_PARAM_COMMA & coseError);
 	if (key == nullptr) {
 		CN_CBOR_FREE(pKeyOut, context);
+		return nullptr;
 	}
 	if (KeyFormat == 1) {
 		return key.Release();
@@ -878,7 +880,7 @@
 	}
 
 #ifdef COSE_C_USE_OPENSSL
-	EVP_PKEY* opensslKey = EVP_PKEY_new();
+	EVP_PKEY* opensslKey = nullptr;
 #endif
 #ifdef COSE_C_USE_MBEDTLS
 	mbedtls_ecp_keypair* keypair = nullptr;
@@ -886,32 +888,22 @@
 
 	switch (keyType->v.uint) {
 		case COSE_Key_Type_EC2:
-#if defined(COSE_C_USE_OPENSSL) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
+#if defined(COSE_C_USE_OPENSSL)
 		{
-			int cbR = 0;
-			EC_KEY* ecKey =
-				ECKey_From((COSE_KEY*)(HCOSE_KEY)key, &cbR, &coseError);
-			if (ecKey == nullptr) {
-				return nullptr;
-			}
-
-			if (EVP_PKEY_set1_EC_KEY(opensslKey, ecKey) == 0) {
-				EC_KEY_free(ecKey);
-				return nullptr;
-			}
-			EC_KEY_free(ecKey);
+			opensslKey = EVP_FromKey((COSE_KEY*)(HCOSE_KEY)key,
+				CBOR_CONTEXT_PARAM_COMMA & coseError);
 		}
 #endif
 #ifdef COSE_C_USE_MBEDTLS
 			{
-				keypair =
-					static_cast<mbedtls_ecp_keypair*>(COSE_CALLOC(sizeof(*keypair), 1, context)
-					);
+				keypair = static_cast<mbedtls_ecp_keypair*>(
+					COSE_CALLOC(sizeof(*keypair), 1, context));
 				if (keypair == nullptr) {
 					return nullptr;
 				}
 				mbedtls_ecp_keypair_init(keypair);
-				if (!ECKey_From((COSE_KEY *) (HCOSE_KEY) key, keypair, &coseError)) {
+				if (!ECKey_From(
+						(COSE_KEY*)(HCOSE_KEY)key, keypair, &coseError)) {
 					mbedtls_ecp_keypair_free(keypair);
 					COSE_FREE(keypair, context);
 					return nullptr;
@@ -922,7 +914,8 @@
 
 		case COSE_Key_Type_OKP:
 #ifdef COSE_C_USE_OPENSSL
-
+			opensslKey = EVP_FromKey((COSE_KEY*)(HCOSE_KEY)key,
+				CBOR_CONTEXT_PARAM_COMMA & coseError);
 #endif
 			break;
 
@@ -930,7 +923,7 @@
 			break;
 	}
 
-#if defined(COSE_C_USE_OPENSSL) && (OPENSSL_VERSION_NUMBER > 0x10100000L)
+#if defined(COSE_C_USE_OPENSSL)
 
 	if (opensslKey == nullptr) {
 		return key.Release();
@@ -1527,7 +1520,6 @@
 }
 #endif	// _MSCVER
 
-
 int main(int argc, char** argv)
 {
 	int i;