ECDH harness¶
Key exchange with public point¶
Naming convention¶
CC_ECDH_exchange_point_<curve>
Where curve is one of:
- P192,- P224,- P256,- P384,- P521.
- B163,- B223,- B283,- B409,- B571.
- K163,- K223,- K283,- K409,- K571.
Attention
OpenSSL 3.0 does not support B or K curves, so they are untested in harness mode.
Function signature¶
- int ECDH_exchange_point(
- uint8_t ss[512],
- size_t *ss_size,
- const uint8_t *secret,
- const size_t secret_size,
- const uint8_t *point,
- const size_t point_size,
- Performs an ECDH key exchange with an encoded uncompressed point. - Parameters:
- ss – [Out] An allocated buffer to return the shared secret. 
- ss_size – [Out] A pointer to return the actual size of the shared secret. 
- secret – [In] Peer A’s secret value as a big-endian array of bytes. 
- secret_size – [In] The size of - secretin bytes.
- point – [In] Peer B’s public key as an encoded uncompressed point. 
- point_size – [In] The size of - point_sizein bytes.
 
- Returns:
- A status code. 
- Return values:
- 1 – OK. 
- 0 – An error occurred. 
 
 
Example¶
To test that the harness integration is working correctly, we use the following OpenSSL harness:
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/objects.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int generic_exchange_point(const char *curve, uint8_t ss[512], size_t *ss_size,
                           const uint8_t *secret, const size_t secret_size,
                           const uint8_t *point, const size_t point_size);
/* Test exchange_point with P curves */
/* There are no exchange_point test vectors for secp256k1 or Brainpool curves */
/* OpenSSL 3.0 does not support NIST B or K curves */
int CC_ECDH_exchange_point_P256(uint8_t ss[512], size_t *ss_size,
                                const uint8_t *secret, const size_t secret_size,
                                const uint8_t *point, const size_t point_size) {
  return generic_exchange_point("prime256v1", ss, ss_size, secret, secret_size,
                                point, point_size);
}
int CC_ECDH_exchange_point_P384(uint8_t ss[512], size_t *ss_size,
                                const uint8_t *secret, const size_t secret_size,
                                const uint8_t *point, const size_t point_size) {
  return generic_exchange_point("secp384r1", ss, ss_size, secret, secret_size,
                                point, point_size);
}
int CC_ECDH_exchange_point_P521(uint8_t ss[512], size_t *ss_size,
                                const uint8_t *secret, const size_t secret_size,
                                const uint8_t *point, const size_t point_size) {
  return generic_exchange_point("secp521r1", ss, ss_size, secret, secret_size,
                                point, point_size);
}
/**
 * Generic exchange_point function
 *
 * Uses functions deprecated in OpenSSL 3.0.
 * The fprintf statements are mostly for debugging.
 */
int generic_exchange_point(const char *curve, uint8_t ss[512], size_t *ss_size,
                           const uint8_t *secret, const size_t secret_size,
                           const uint8_t *point, const size_t point_size) {
  int nid = OBJ_txt2nid(curve);
  if (nid == NID_undef) {
    fprintf(stderr, "Failed to get NID for %s\n", curve);
    return 0;
  }
  EC_KEY *ec_sk = EC_KEY_new_by_curve_name(nid);
  if (ec_sk == NULL) {
    fprintf(stderr, "Failed to create ec_sk by curve name\n");
    return 0;
  }
  BIGNUM *prv = BN_bin2bn(secret, secret_size, NULL);
  if (prv == NULL) {
    fprintf(stderr, "Failed to get BIGNUM from secret\n");
    return 0;
  }
  if (!EC_KEY_set_private_key(ec_sk, prv)) {
    fprintf(stderr, "Failed to set private key\n");
    return 0;
  }
  EC_KEY *ec_pk = EC_KEY_new_by_curve_name(nid);
  if (ec_pk == NULL) {
    fprintf(stderr, "Failed to create ec_pk by curve name\n");
    return 0;
  }
  const EC_GROUP *group = EC_KEY_get0_group(ec_pk);
  if (group == NULL) {
    fprintf(stderr, "Failed to get group from key\n");
    return 0;
  }
  EC_POINT *pub = EC_POINT_new(group);
  if (pub == NULL) {
    fprintf(stderr, "Failed to create point from group\n");
    return 0;
  }
  if (!EC_POINT_oct2point(group, pub, point, point_size, NULL)) {
    fprintf(stderr, "Failed to convert oct to point\n");
    return 0;
  }
  if (!EC_KEY_set_public_key(ec_pk, pub)) {
    fprintf(stderr, "Failed to set public key\n");
  }
  EVP_PKEY *sk = EVP_PKEY_new();
  if (!EVP_PKEY_set1_EC_KEY(sk, ec_sk)) {
    fprintf(stderr, "Failed to set sk\n");
    return 0;
  }
  EVP_PKEY *pk = EVP_PKEY_new();
  if (!EVP_PKEY_set1_EC_KEY(pk, ec_pk)) {
    fprintf(stderr, "Failed to set pk\n");
    return 0;
  }
  EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(sk, NULL);
  if (!ctx) {
    return 0;
  }
  if (EVP_PKEY_derive_init(ctx) <= 0) {
    fprintf(stderr, "Failed to derive_init\n");
    return 0;
  }
  if (EVP_PKEY_derive_set_peer(ctx, pk) <= 0) {
    fprintf(stderr, "Failed to derive_set_peer\n");
    return 0;
  }
  if (EVP_PKEY_derive(ctx, NULL, ss_size) <= 0) {
    fprintf(stderr, "Failed to get ss_size with derive\n");
    return 0;
  }
  if (EVP_PKEY_derive(ctx, ss, ss_size) <= 0) {
    fprintf(stderr, "Failed to derive\n");
    return 0;
  }
  return 1;
}
Compile the shared library with the -lssl -lcrypto options.
gcc -fPIC -shared ecdh_point_harness.c -o ecdh_point.so -lssl -lcrypto
Then test the harness.
crypto-condor-cli test harness ecdh_point.so
Key exchange with X.509 key¶
Naming convention¶
CC_ECDH_exchange_point_<curve>
Where curve is one of:
- P224,- P256,- P384,- P521.
- brainpoolP224r1,- brainpoolP256r1,- brainpoolP320r1,- brainpoolP384r1,- brainpoolP512r1.
- secp256k1.
- B283,- B409,- B571.
- K283,- K409,- K571.
Attention
OpenSSL 3.0 does not support B or K curves, so they are untested in harness mode.
Function signature¶
- int ECDH_exchange_x509(
- uint8_t ss[512],
- size_t *ss_size,
- const uint8_t *secret,
- const size_t secret_size,
- const uint8_t *pub,
- const size_t pub_size,
- Performs an ECDH key exchange with an encoded uncompressed point. - Parameters:
- ss – [Out] An allocated buffer to return the shared secret. 
- ss_size – [Out] A pointer to return the actual size of the shared secret. 
- secret – [In] Peer A’s secret value as a big-endian array of bytes. 
- secret_size – [In] The size of - secretin bytes.
- pub – [In] Peer B’s public key as an X.509 key. 
- point_size – [In] The size of - pub_sizein bytes.
 
- Returns:
- A status code. 
- Return values:
- 1 – OK. 
- 0 – An error occurred. 
 
 
Example¶
To test that the harness integration is working correctly, we use the following OpenSSL harness:
#include <openssl/ec.h>
#include <openssl/evp.h>
#include <openssl/obj_mac.h>
#include <openssl/objects.h>
#include <openssl/x509.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
int generic_exchange_x509(const char *curve, uint8_t ss[512], size_t *ss_size,
                          const uint8_t *secret, const size_t secret_size,
                          const uint8_t *point, const size_t point_size);
/* Test exchange_point with P curves */
/* OpenSSL 3.0 does not support NIST B or K curves */
int CC_ECDH_exchange_x509_P256(uint8_t ss[512], size_t *ss_size,
                               const uint8_t *secret, const size_t secret_size,
                               const uint8_t *pub, const size_t pub_size) {
  return generic_exchange_x509("prime256v1", ss, ss_size, secret, secret_size,
                               pub, pub_size);
}
int CC_ECDH_exchange_x509_P384(uint8_t ss[512], size_t *ss_size,
                               const uint8_t *secret, const size_t secret_size,
                               const uint8_t *pub, const size_t pub_size) {
  return generic_exchange_x509("secp384r1", ss, ss_size, secret, secret_size,
                               pub, pub_size);
}
int CC_ECDH_exchange_x509_P521(uint8_t ss[512], size_t *ss_size,
                               const uint8_t *secret, const size_t secret_size,
                               const uint8_t *pub, const size_t pub_size) {
  return generic_exchange_x509("secp521r1", ss, ss_size, secret, secret_size,
                               pub, pub_size);
}
/**
 * Generic exchange_point function
 *
 * Uses functions deprecated in OpenSSL 3.0.
 * The fprintf statements are mostly for debugging.
 */
int generic_exchange_x509(const char *curve, uint8_t ss[512], size_t *ss_size,
                          const uint8_t *secret, const size_t secret_size,
                          const uint8_t *pub, const size_t pub_size) {
  int nid = OBJ_txt2nid(curve);
  if (nid == NID_undef) {
    fprintf(stderr, "Failed to get NID for %s\n", curve);
    return 0;
  }
  EC_KEY *ec_sk = EC_KEY_new_by_curve_name(nid);
  if (ec_sk == NULL) {
    fprintf(stderr, "Failed to create ec_sk by curve name\n");
    return 0;
  }
  BIGNUM *prv = BN_bin2bn(secret, secret_size, NULL);
  if (prv == NULL) {
    fprintf(stderr, "Failed to get BIGNUM from secret\n");
    return 0;
  }
  if (!EC_KEY_set_private_key(ec_sk, prv)) {
    fprintf(stderr, "Failed to set private key\n");
    return 0;
  }
  EVP_PKEY *sk = EVP_PKEY_new();
  if (!EVP_PKEY_set1_EC_KEY(sk, ec_sk)) {
    fprintf(stderr, "Failed to set sk\n");
    return 0;
  }
  EVP_PKEY *pk = d2i_PUBKEY(NULL, &pub, pub_size);
  if (!pk) {
    fprintf(stderr, "Failed to read pubkey from pub\n");
    return 0;
  }
  EVP_PKEY_CTX *ctx = EVP_PKEY_CTX_new(sk, NULL);
  if (!ctx) {
    return 0;
  }
  if (EVP_PKEY_derive_init(ctx) <= 0) {
    fprintf(stderr, "Failed to derive_init\n");
    return 0;
  }
  if (EVP_PKEY_derive_set_peer(ctx, pk) <= 0) {
    fprintf(stderr, "Failed to derive_set_peer\n");
    return 0;
  }
  if (EVP_PKEY_derive(ctx, NULL, ss_size) <= 0) {
    fprintf(stderr, "Failed to get ss_size with derive\n");
    return 0;
  }
  if (EVP_PKEY_derive(ctx, ss, ss_size) <= 0) {
    fprintf(stderr, "Failed to derive\n");
    return 0;
  }
  return 1;
}
Compile the shared library with the -lssl -lcrypto options.
gcc -fPIC -shared ecdh_x509_harness.c -o ecdh_x509.so -lssl -lcrypto
Then test the harness.
crypto-condor-cli test harness ecdh_x509.so