summaryrefslogtreecommitdiffstats
path: root/libparc/parc/security/parc_DiffieHellmanKeyShare.c
diff options
context:
space:
mode:
Diffstat (limited to 'libparc/parc/security/parc_DiffieHellmanKeyShare.c')
-rw-r--r--libparc/parc/security/parc_DiffieHellmanKeyShare.c297
1 files changed, 297 insertions, 0 deletions
diff --git a/libparc/parc/security/parc_DiffieHellmanKeyShare.c b/libparc/parc/security/parc_DiffieHellmanKeyShare.c
new file mode 100644
index 00000000..250e5664
--- /dev/null
+++ b/libparc/parc/security/parc_DiffieHellmanKeyShare.c
@@ -0,0 +1,297 @@
+/*
+ * Copyright (c) 2017 Cisco and/or its affiliates.
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at:
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+/**
+ */
+
+#include <parc/algol/parc_Object.h>
+#include <parc/algol/parc_Memory.h>
+
+#include <parc/security/parc_DiffieHellmanKeyShare.h>
+#include <parc/security/parc_CryptoHasher.h>
+#include <parc/security/parc_CryptoHash.h>
+
+#include <openssl/pem.h>
+#include <openssl/rand.h>
+
+struct parc_diffie_hellman_keyshare {
+ PARCDiffieHellmanGroup groupType;
+ EVP_PKEY *privateKey;
+};
+
+static bool
+_parcDiffieHellmanKeyShare_Destructor(PARCDiffieHellmanKeyShare **pointer)
+{
+ PARCDiffieHellmanKeyShare *share = *pointer;
+
+ if (share->privateKey != NULL) {
+ EVP_PKEY_free(share->privateKey);
+ }
+
+ return true;
+}
+
+parcObject_Override(PARCDiffieHellmanKeyShare, PARCObject,
+ .destructor = (PARCObjectDestructor *) _parcDiffieHellmanKeyShare_Destructor);
+
+parcObject_ImplementAcquire(parcDiffieHellmanKeyShare, PARCDiffieHellmanKeyShare);
+parcObject_ImplementRelease(parcDiffieHellmanKeyShare, PARCDiffieHellmanKeyShare);
+
+static EVP_PKEY *
+_parcDiffieHellmanKeyShare_CreateShare(int curveid)
+{
+ EVP_PKEY_CTX *pctx;
+ EVP_PKEY_CTX *kctx;
+
+ pctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL);
+ if (pctx == NULL) {
+ return NULL;
+ }
+
+ int result = EVP_PKEY_paramgen_init(pctx);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+ }
+
+ result = EVP_PKEY_CTX_set_ec_paramgen_curve_nid(pctx, curveid);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+ }
+
+ EVP_PKEY *params = NULL;
+ result = EVP_PKEY_paramgen(pctx, &params);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ return NULL;
+ }
+
+ kctx = EVP_PKEY_CTX_new(params, NULL);
+ if (kctx == NULL) {
+ EVP_PKEY_CTX_free(pctx);
+ EVP_PKEY_free(params);
+ return NULL;
+ }
+
+ result = EVP_PKEY_keygen_init(kctx);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ EVP_PKEY_free(params);
+ return NULL;
+ }
+
+ EVP_PKEY *pkey = NULL;
+ result = EVP_PKEY_keygen(kctx, &pkey);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(pctx);
+ EVP_PKEY_free(params);
+ EVP_PKEY_CTX_free(kctx);
+ return NULL;
+ }
+
+ EVP_PKEY_CTX_free(pctx);
+ EVP_PKEY_CTX_free(kctx);
+ EVP_PKEY_free(params);
+
+ return pkey;
+}
+
+PARCDiffieHellmanKeyShare *
+parcDiffieHellmanKeyShare_Create(PARCDiffieHellmanGroup groupType)
+{
+ PARCDiffieHellmanKeyShare *keyShare = parcObject_CreateInstance(PARCDiffieHellmanKeyShare);
+
+ if (keyShare != NULL) {
+ keyShare->groupType = groupType;
+
+ switch (groupType) {
+ case PARCDiffieHellmanGroup_Prime256v1:
+ keyShare->privateKey = _parcDiffieHellmanKeyShare_CreateShare(NID_X9_62_prime256v1);
+ break;
+ case PARCDiffieHellmanGroup_Secp521r1:
+ keyShare->privateKey = _parcDiffieHellmanKeyShare_CreateShare(NID_secp521r1);
+ break;
+ case PARCDiffieHellmanGroup_Curve2559:
+ default:
+ break;
+ }
+
+ if (keyShare->privateKey == NULL) {
+ assertTrue(false, "Unable to instantiate a private key.");
+ parcDiffieHellmanKeyShare_Release(&keyShare);
+ }
+ }
+
+ return keyShare;
+}
+
+PARCBuffer *
+parcDiffieHellmanKeyShare_SerializePublicKey(PARCDiffieHellmanKeyShare *keyShare)
+{
+ EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(keyShare->privateKey);
+
+ BN_CTX *bnctx = BN_CTX_new();
+ point_conversion_form_t form = EC_KEY_get_conv_form(ecKey);
+ const EC_POINT *point = EC_KEY_get0_public_key(ecKey);
+ const EC_GROUP *group = EC_KEY_get0_group(ecKey);
+ char *keyBuffer = EC_POINT_point2hex(group, point, form, bnctx);
+ int length = strlen(keyBuffer);
+
+ PARCBuffer *publicKey = parcBuffer_Allocate(length);
+ parcBuffer_PutArray(publicKey, length, (uint8_t *) keyBuffer);
+ parcBuffer_Flip(publicKey);
+
+ free(keyBuffer);
+ BN_CTX_free(bnctx);
+
+ return publicKey;
+}
+
+static EVP_PKEY *
+_parcDiffieHellman_DeserializePublicKeyShare(PARCDiffieHellmanKeyShare *keyShare, PARCBuffer *keyBuffer)
+{
+ EC_KEY *ecKey = EVP_PKEY_get1_EC_KEY(keyShare->privateKey);
+ const EC_GROUP *myGroup = EC_KEY_get0_group(ecKey);
+
+ EC_KEY *newKey = EC_KEY_new();
+ BN_CTX *newCtx = BN_CTX_new();
+ int result = EC_KEY_set_group(newKey, myGroup);
+ if (result != 1) {
+ BN_CTX_free(newCtx);
+ EC_KEY_free(newKey);
+ return NULL;
+ }
+
+ EC_POINT *newPoint = EC_POINT_new(myGroup);
+ char *keyString = parcBuffer_ToString(keyBuffer);
+ newPoint = EC_POINT_hex2point(myGroup, keyString, newPoint, newCtx);
+ if (newPoint == NULL) {
+ parcMemory_Deallocate(&keyString);
+ BN_CTX_free(newCtx);
+ EC_KEY_free(newKey);
+ EC_POINT_free(newPoint);
+ return NULL;
+ }
+
+ result = EC_KEY_set_public_key(newKey, newPoint);
+ if (result != 1) {
+ parcMemory_Deallocate(&keyString);
+ BN_CTX_free(newCtx);
+ EC_KEY_free(newKey);
+ EC_POINT_free(newPoint);
+ return NULL;
+ }
+
+ EVP_PKEY *peerkey = EVP_PKEY_new();
+ result = EVP_PKEY_set1_EC_KEY(peerkey, newKey);
+ if (result != 1) {
+ parcMemory_Deallocate(&keyString);
+ BN_CTX_free(newCtx);
+ EC_KEY_free(newKey);
+ EC_POINT_free(newPoint);
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ BN_CTX_free(newCtx);
+ EC_KEY_free(newKey);
+ parcMemory_Deallocate((void **) &keyString);
+ EC_POINT_free(newPoint);
+
+ return peerkey;
+}
+
+static PARCBuffer *
+_parcDiffieHellmanKeyShare_HashSharedSecret(PARCBuffer *secret)
+{
+ PARCCryptoHasher *hasher = parcCryptoHasher_Create(PARCCryptoHashType_SHA256);
+ parcCryptoHasher_Init(hasher);
+ parcCryptoHasher_UpdateBuffer(hasher, secret);
+ PARCCryptoHash *digest = parcCryptoHasher_Finalize(hasher);
+
+ PARCBuffer *sharedSecret = parcBuffer_Acquire(parcCryptoHash_GetDigest(digest));
+
+ parcCryptoHash_Release(&digest);
+ parcCryptoHasher_Release(&hasher);
+
+ return sharedSecret;
+}
+
+PARCBuffer *
+parcDiffieHellmanKeyShare_Combine(PARCDiffieHellmanKeyShare *keyShare, PARCBuffer *theirs)
+{
+ EVP_PKEY *peerkey = _parcDiffieHellman_DeserializePublicKeyShare(keyShare, theirs);
+ if (peerkey == NULL) {
+ return NULL;
+ }
+
+ EVP_PKEY *privateKey = keyShare->privateKey;
+ EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(privateKey, NULL);
+ if (ctx == NULL) {
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ int result = EVP_PKEY_derive_init(ctx);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ result = EVP_PKEY_derive_set_peer(ctx, peerkey);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ size_t secretLength = 0;
+ result = EVP_PKEY_derive(ctx, NULL, &secretLength);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ unsigned char *secret = OPENSSL_malloc(secretLength);
+ if (secret == NULL) {
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ return NULL;
+ }
+
+ result = EVP_PKEY_derive(ctx, secret, &secretLength);
+ if (result != 1) {
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ OPENSSL_free(secret);
+ return NULL;
+ }
+
+ PARCBuffer *secretBuffer = parcBuffer_Allocate(secretLength);
+ parcBuffer_PutArray(secretBuffer, secretLength, secret);
+ parcBuffer_Flip(secretBuffer);
+
+ PARCBuffer *sharedSecret = _parcDiffieHellmanKeyShare_HashSharedSecret(secretBuffer);
+ parcBuffer_Release(&secretBuffer);
+
+ EVP_PKEY_CTX_free(ctx);
+ EVP_PKEY_free(peerkey);
+ OPENSSL_free(secret);
+
+ return sharedSecret;
+}