Re: decoder memory-management question

[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

 



To give a bit more context: The attached beta openssl_ed25519_lib25519.c
is an OpenSSL provider that uses lib25519 to speed up Ed25519. On, e.g.,
a 3.5GHz Haswell core, openssl speed with OpenSSL's built-in provider
says 21239.0 sign/s and 8272.0 verify/s, while openssl speed with this
provider says 73708.1 sign/s and 22853.0 verify/s. I used a different
secret-key format (and maybe cheated a bit on the OIDs for that) but the
public keys are compatible.

I'm planning similar X25519 integration. For an idea of the speedups on
various Intel/AMD platforms, see https://lib25519.cr.yp.to/speed.html.
There's also ARM code coming soon. An important caveat is that the code
isn't formally verified yet; but there's some fully verified code from
s2n-bignum that drops straightforwardly into lib25519, and reusing that
code helps security even for applications that don't care about speed.

I haven't dug this deeply into OpenSSL before. I could easily be doing
major things wrong in this provider, maybe security-relevant things,
perhaps most importantly regarding memory management. The documentation
that I looked at had occasional comments on who was responsible for each
piece of memory at each moment, but most of the time I found myself
running tests and studying code samples (in the OpenSSL core, OpenSSL's
default provider, and a few providers I found online) to try to figure
out the expectations.

Some memory-management issues that I bumped into were easily avoidable.
For example, bad memory management in the OpenSSL core, specifically a
bad free() for openssl req, occurs if a provider returns bad _bytes_
inside an OSSL_SIGNATURE_PARAM_ALGORITHM_ID octet string. Well, okay,
obviously I should get the ID right. (The core should be fixed too:
string contents should never be able to corrupt pointer structures.)

As another example, I think I understand the layering that's expected to
be used inside encoders and decoders, but the layers for public-key
decoding were turning into such a data-structure mess that I ended up
just tossing that code and doing a trivial parse of the known DER prefix
followed by the Ed25519 byte string. It's tempting to simplify the other
encoding/decoding steps in the same way.

But obviously a provider can't avoid some issues. In particular, there's
a three-way dance between decode() and its callback and load(), and I
want to make sure that I'm handling memory in the expected way for all
possible failure cases. It would be nice if OpenSSL included a test
harness for providers---not just basic functionality tests as in the
attached test.sh but also testing how various types of internal failures
are handled.

---D. J. Bernstein
/*
Basic requirements on the underlying signature library:
- Must follow a SUPERCOP-style interface.
- The secret key must end with the public key.
- The signed message must be a signature followed by the message.
- Signatures must be constant length.

Overall structure of this file:
glue code at the top, then the painful part.

The glue code wraps the signature library:
- Sizes:
  - PKBYTES is the number of public-key bytes.
  - SKBYTES is the number of secret-key bytes.
  - SIGBYTES is the number of signature bytes.
- Data structure:
  - struct glue_key stores data[SKBYTES] and state.
  - state GLUE_KEY_EMPTY: data is all 0.
  - state GLUE_KEY_PK: data is zeros followed by public key.
  - state GLUE_KEY_SK: data is secret key.
  - GLUE_KEY_EMPTY is guaranteed to be 0.
- Functions:
  - glue_key_generate(&K): stores a new secret key in K.
  - glue_key_sign(sig,m,mlen,&K): signs a message using K.
  - glue_key_verify(sig,m,mlen,&K): verifies a signature using K.
  - OpenSSL-style return values: 1 for success, 0 for failure.
- Constants to interface with OpenSSL:
  - glue_pubkey_asn1_prefix is DER prefix for public keys
  - glue_algorithm_asn1 is DER encoding of just the system name
  - glue_algorithm_name is main command-line name for the system
  - glue_algorithm_names is name:altname1:altname2:...
    (see openssl/providers/implementations/include/prov/names.h)
  - glue_algorithm_nid is the OpenSSL NID for the signature system (as NID_...)
  - glue_provider_name
  - glue_provider_version
*/

#include <openssl/crypto.h> // to use OPENSSL_zalloc in the glue code
#include <inttypes.h> // for SIZE_MAX, LLONG_MAX
#include <string.h> // for memset
#include <lib25519.h> // the wrapped library

#define glue_provider_name "openssl_ed25519_lib25519"
#define glue_provider_version "20240301"
#define glue_algorithm_name "ED25519"
#define glue_algorithm_names "ED25519:1.3.101.112"
#define glue_algorithm_nid NID_ED25519

// XXX: must be correct DER; otherwise triggers double-free in openssl req
static const unsigned char glue_algorithm_asn1[7] = {
  0x30,0x05,0x06,0x03,0x2b,0x65,0x70,
} ;

static const unsigned char glue_pubkey_asn1_prefix[12] = {
  0x30,0x2a,
  0x30,0x05,0x06,0x03,0x2b,0x65,0x70,
  0x03,0x21,0x00
} ;

#define PKBYTES lib25519_sign_PUBLICKEYBYTES
#define SKBYTES lib25519_sign_SECRETKEYBYTES
#define SIGBYTES lib25519_sign_BYTES

#if PKBYTES > SKBYTES
#error PKBYTES has to be at most SKBYTES
#endif

struct glue_key {
  unsigned char data[SKBYTES];
  unsigned char state;
} ;
#define GLUE_KEY_EMPTY 0
#define GLUE_KEY_PK 1
#define GLUE_KEY_SK 2

static int glue_key_generate(struct glue_key *K)
{
  unsigned char pk[PKBYTES];
  if (!K) return 0;
  lib25519_sign_keypair(pk,K->data);
  if (CRYPTO_memcmp(K->data+SKBYTES-PKBYTES,pk,PKBYTES)) {
    memset(K,0,sizeof(struct glue_key));
    return 0;
  }
  K->state = GLUE_KEY_SK;
  return 1;
}

static int glue_key_sign(unsigned char *sig,const unsigned char *m,size_t mlen,const struct glue_key *K)
{
  int ok = 0;
  void *sm = 0;
  long long smlen = 0;
  long long smlenused = 0;
  if (!sig) return 0;
  memset(sig,0,SIGBYTES);
  if (!K) return 0;
  if (K->state != GLUE_KEY_SK) return 0;
  if (!m) return 0;
  if (mlen > SIZE_MAX-SIGBYTES) return 0;
  if (mlen > LLONG_MAX-SIGBYTES) return 0;
  smlen = mlen+SIGBYTES;
  sm = OPENSSL_secure_zalloc(smlen);
  if (!sm) return 0;
  do {
    lib25519_sign(sm,&smlenused,m,mlen,K->data);
    if (smlenused != smlen) break;
    if (CRYPTO_memcmp(m,sm+SIGBYTES,mlen)) break;
    memcpy(sig,sm,SIGBYTES);
    ok = 1;
  } while(0);
  OPENSSL_secure_clear_free(sm,smlen);
  return ok;
}

static int glue_key_verify(const unsigned char *sig,const unsigned char *m,size_t mlen,const struct glue_key *K)
{
  int ok = 0;
  void *sm = 0;
  long long smlen = 0;
  long long tlen = 0;
  if (!K) return 0;
  if (!K->state) return 0;
  if (!m) return 0;
  if (!sig) return 0;
  if (mlen > SIZE_MAX-SIGBYTES) return 0;
  if (mlen > LLONG_MAX-SIGBYTES) return 0;
  smlen = mlen+SIGBYTES;
  sm = OPENSSL_zalloc(smlen);
  if (!sm) return 0;
  do {
    memcpy(sm,sig,SIGBYTES);
    memcpy(sm+SIGBYTES,m,mlen);
    if (lib25519_sign_open(sm,&tlen,sm,smlen,K->data+SKBYTES-PKBYTES) != 0) break;
    if ((unsigned long long) tlen != mlen) break;
    if (CRYPTO_memcmp(sm,m,mlen)) break;
    ok = 1;
  } while(0);
  OPENSSL_clear_free(sm,smlen);
  return ok;
}


// ----- and now the painful part begins

#include <openssl/core.h>
#include <openssl/core_dispatch.h>
#include <openssl/core_object.h>
#include <openssl/core_names.h>
#include <openssl/params.h>
#include <openssl/param_build.h>
#include <openssl/provider.h>
#include <openssl/bio.h>
#include <openssl/asn1.h>
#include <openssl/pem.h>
#include <openssl/x509.h>
extern int asn1_d2i_read_bio(BIO *,BUF_MEM **); // XXX: sigh

// various objects below have no parameters
static const OSSL_PARAM no_params[] = { OSSL_PARAM_END } ;

#define PROVIDER "provider=" glue_provider_name


// ----- encoder and decoder

// no reason to allocate an extra layer
// so pointer to encoder/decoder context is just provider-context pointer

static void *coder_newctx(void *provctx)
{
  return provctx;
}

static void coder_freectx(void *ctx)
{
  ; // nothing to do
}

static int encode(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg,int flagpem)
{
  // XXX: probably easier to just precompute constant strings for all four cases
  // XXX: is it possible to skip the PEM part of the encoder?
  OSSL_LIB_CTX *provider_context = ctx;
  const struct glue_key *K = obj_raw;
  BIO *bio = 0;
  int ok = 0;
  if (obj_abstract) return 0;
  if (!K) return 0;
  bio = BIO_new_from_core_bio(provider_context,out);
  if (!bio) return 0;
  do {
    if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) {
      ASN1_OCTET_STRING bytes;
      unsigned char *der = 0;
      int derlen;
      PKCS8_PRIV_KEY_INFO *sk_pkcs8;
      if (K->state != GLUE_KEY_SK) break;
      bytes.data = (void *) K->data;
      bytes.length = SKBYTES;
      bytes.flags = 0;
      // XXX: should this be trying to use secure_zalloc instead?
      derlen = i2d_ASN1_OCTET_STRING(&bytes,&der);
      if (derlen < 0) break;
      do {
        sk_pkcs8 = PKCS8_PRIV_KEY_INFO_new();
        if (!sk_pkcs8) break;
        do {
          if (!PKCS8_pkey_set0(sk_pkcs8,OBJ_nid2obj(glue_algorithm_nid),0,V_ASN1_UNDEF,0,der,derlen)) break;
          der = 0; // since sk_pkcs8 has taken responsibility
          if (flagpem) {
            if (!PEM_write_bio_PKCS8_PRIV_KEY_INFO(bio,sk_pkcs8)) break;
          } else {
            if (!i2d_PKCS8_PRIV_KEY_INFO_bio(bio,sk_pkcs8)) break;
          }
          ok = 1;
        } while(0);
        PKCS8_PRIV_KEY_INFO_free(sk_pkcs8);
      } while(0);
      if (der) OPENSSL_clear_free(der,derlen);
      break;
    }
    if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) {
      unsigned char *pk_copy = OPENSSL_zalloc(PKBYTES);
      if (!pk_copy) break;
      do {
        X509_PUBKEY *pk_x509 = X509_PUBKEY_new();
        if (!pk_x509) break;
        do {
          if (!K->state) break;
          memcpy(pk_copy,K->data+SKBYTES-PKBYTES,PKBYTES);
          if (!X509_PUBKEY_set0_param(pk_x509,OBJ_nid2obj(glue_algorithm_nid),V_ASN1_UNDEF,0,pk_copy,PKBYTES)) break;
          pk_copy = 0; // since pk_x509 has taken responsibility
          if (flagpem) {
            if (!PEM_write_bio_X509_PUBKEY(bio,pk_x509)) break;
          } else {
            if (!i2d_X509_PUBKEY_bio(bio,pk_x509)) break;
          }
          ok = 1;
        } while(0);
        X509_PUBKEY_free(pk_x509);
        break;
      } while(0);
      if (pk_copy) OPENSSL_clear_free(pk_copy,PKBYTES);
    }
  } while(0);
  BIO_free(bio);
  return ok;
}

static int encode_DER(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg)
{
  return encode(ctx,out,obj_raw,obj_abstract,selection,cb,cbarg,0);
}

static int encode_PEM(void *ctx,OSSL_CORE_BIO *out,const void *obj_raw,const OSSL_PARAM obj_abstract[],int selection,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg)
{
  return encode(ctx,out,obj_raw,obj_abstract,selection,cb,cbarg,1);
}

static int decode_DER(void *ctx,OSSL_CORE_BIO *in,int selection,OSSL_CALLBACK *data_cb,void *data_cbarg,OSSL_PASSPHRASE_CALLBACK *cb,void *cbarg)
{
  OSSL_LIB_CTX *provider_context = ctx;
  BIO *bio = 0;
  int ok = 1;
  if (!in) return 1;
  bio = BIO_new_from_core_bio(provider_context,in);
  if (!bio) return 1;
  do {
    int flagpub;
    for (flagpub = 0;flagpub < 2;++flagpub) {
      if (!data_cb) break;
      if (flagpub) {
        if (!(selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)) continue;
      } else {
        if (!(selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)) continue;
      }
      BUF_MEM *mem = 0;
      long xlen = 0;
      const unsigned char *x2 = 0;
      PKCS8_PRIV_KEY_INFO *sk_pkcs8 = 0;
      if (asn1_d2i_read_bio(bio,&mem) < 0) continue;
      x2 = (void *) mem->data;
      xlen = mem->length;
      do {
        if (flagpub) {
          // maybe could do something like this: pk_x509 = d2i_X509_PUBKEY(0,&x2,xlen);
          // but clearly easier to just decode by hand
          if (xlen != PKBYTES + sizeof glue_pubkey_asn1_prefix) break;
          if (CRYPTO_memcmp(x2,glue_pubkey_asn1_prefix,sizeof glue_pubkey_asn1_prefix)) break;
        } else {
          // XXX: consider decoding directly in this case too
          sk_pkcs8 = d2i_PKCS8_PRIV_KEY_INFO(0,&x2,xlen);
          if (!sk_pkcs8) break;
        }
        do {
          const unsigned char *y = 0;
          int ylen = 0;
          const X509_ALGOR *algorithmid = 0;
          ASN1_OCTET_STRING *bytes = 0;
          if (!flagpub) {
            if (!PKCS8_pkey_get0(0,&y,&ylen,&algorithmid,sk_pkcs8)) break;
            if (OBJ_obj2nid(algorithmid->algorithm) != glue_algorithm_nid) break;
            bytes = d2i_ASN1_OCTET_STRING(0,&y,ylen);
            if (!bytes) break;
          }
          do {
            struct glue_key *K = 0;
            if (!flagpub) if (ASN1_STRING_length(bytes) != SKBYTES) break;
            K = OPENSSL_secure_zalloc(sizeof(struct glue_key));
            if (!K) break;
            do {
              int pkey = OSSL_OBJECT_PKEY;
              OSSL_PARAM params[4];
              if (flagpub) {
                memcpy(K->data+SKBYTES-PKBYTES,x2+xlen-PKBYTES,PKBYTES);
                K->state = GLUE_KEY_PK;
              } else {
                memcpy(K->data,ASN1_STRING_get0_data(bytes),SKBYTES);
                K->state = GLUE_KEY_SK;
              }
              params[0] = OSSL_PARAM_construct_int(OSSL_OBJECT_PARAM_TYPE,&pkey);
              params[1] = OSSL_PARAM_construct_utf8_string(OSSL_OBJECT_PARAM_DATA_TYPE,glue_algorithm_name,0);
              params[2] = OSSL_PARAM_construct_octet_string(OSSL_OBJECT_PARAM_REFERENCE,&K,sizeof(K));
              params[3] = OSSL_PARAM_construct_end();
              if (data_cb) ok = data_cb(params,data_cbarg);
              data_cb = 0; // make sure we do not call it twice
            } while(0);
            // K should be 0 if and only if callback took control of key
            if (K) OPENSSL_secure_clear_free(K,sizeof(struct glue_key));
          } while(0);
          if (!flagpub)
            ASN1_OCTET_STRING_free(bytes);
        } while(0);
        if (!flagpub)
          PKCS8_PRIV_KEY_INFO_free(sk_pkcs8);
      } while(0);
      BUF_MEM_free(mem);
    }
  } while(0);
  BIO_free(bio);
  return ok;
}

static const OSSL_DISPATCH encode_DER_functions[] = {
  { OSSL_FUNC_ENCODER_NEWCTX, (void(*)(void)) coder_newctx },
  { OSSL_FUNC_ENCODER_FREECTX, (void(*)(void)) coder_freectx },
  { OSSL_FUNC_ENCODER_ENCODE, (void(*)(void)) encode_DER },
  { 0, 0 },
} ;

static const OSSL_DISPATCH encode_PEM_functions[] = {
  { OSSL_FUNC_ENCODER_NEWCTX, (void(*)(void)) coder_newctx },
  { OSSL_FUNC_ENCODER_FREECTX, (void(*)(void)) coder_freectx },
  { OSSL_FUNC_ENCODER_ENCODE, (void(*)(void)) encode_PEM },
  { 0, 0 },
} ;

static const OSSL_DISPATCH decode_DER_functions[] = {
  { OSSL_FUNC_DECODER_NEWCTX, (void(*)(void)) coder_newctx },
  { OSSL_FUNC_DECODER_FREECTX, (void(*)(void)) coder_freectx },
  { OSSL_FUNC_DECODER_DECODE, (void(*)(void)) decode_DER },
  { 0, 0 },
} ;

static const OSSL_ALGORITHM provider_encoder[] = {
  { glue_algorithm_names,PROVIDER ",output=der,structure=PrivateKeyInfo",encode_DER_functions },
  { glue_algorithm_names,PROVIDER ",output=pem,structure=PrivateKeyInfo",encode_PEM_functions },
  { glue_algorithm_names,PROVIDER ",output=der,structure=SubjectPublicKeyInfo",encode_DER_functions },
  { glue_algorithm_names,PROVIDER ",output=pem,structure=SubjectPublicKeyInfo",encode_PEM_functions },
  { 0, 0, 0 },
};

static const OSSL_ALGORITHM provider_decoder[] = {
  { glue_algorithm_names,PROVIDER ",input=der,structure=PrivateKeyInfo",decode_DER_functions },
  { glue_algorithm_names,PROVIDER ",input=der,structure=SubjectPublicKeyInfo",decode_DER_functions },
  { 0, 0, 0 },
};


// ----- keygen

// as in encoder/decoder, no reason to allocate an extra layer
// so pointer to key-generator context is just provider-context pointer

static void *key_gen_init(void *provctx,int selection,const OSSL_PARAM params[])
{
  return provctx;
}

static void key_gen_cleanup(void *genctx)
{
  ; // nothing to do
}

static void *key_new(void *provctx)
{
  return OPENSSL_secure_zalloc(sizeof(struct glue_key));
}

static void key_free(void *keydata)
{
  OPENSSL_secure_clear_free(keydata,sizeof(struct glue_key));
}

static int key_gen_set_params(void *genctx,const OSSL_PARAM params[])
{
  return 1;
}

static const OSSL_PARAM *key_gen_settable_params(void *genctx,void *provctx)
{
  return no_params;
}

static void *key_gen(void *genctx,OSSL_CALLBACK *cb,void *cbarg)
{
  struct glue_key *K = key_new(genctx);
  if (!K) return 0;
  if (!glue_key_generate(K)) {
    key_free(K);
    return 0;
  }
  return K;
}

static void *key_load(const void *reference,size_t reference_sz)
{
  struct glue_key *K = 0;
  if (!reference) return 0;
  if (reference_sz != sizeof(K)) return 0;
  K = *(struct glue_key **) reference;
  *(struct glue_key **) reference = 0;
  return K;
}

static const OSSL_PARAM keygen_params[] = {
  OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_MAX_SIZE,0,0),
  OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_DEFAULT_DIGEST,0,0),
  OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_MANDATORY_DIGEST,0,0),
  OSSL_PARAM_END
} ;

static const OSSL_PARAM *key_gettable_params(void *provctx)
{
  return keygen_params;
}

static int key_get_params(void *keydata,OSSL_PARAM params[])
{
  OSSL_PARAM *p = 0;

  // need MAX_SIZE for openssl pkeyutl -verify
  p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_MAX_SIZE);
  if (p && !OSSL_PARAM_set_int(p,SIGBYTES)) return 0;

  p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_DEFAULT_DIGEST);
  if (p && !OSSL_PARAM_set_utf8_string(p,SN_undef)) return 0;

  p = OSSL_PARAM_locate(params,OSSL_PKEY_PARAM_MANDATORY_DIGEST);
  if (p && !OSSL_PARAM_set_utf8_string(p,SN_undef)) return 0;

  return 1;
}

static const OSSL_PARAM *key_settable_params(void *provctx)
{
  return no_params;
}

static int key_set_params(void *keydata, const OSSL_PARAM params[])
{
  return 1;
}

static int key_has(const void *keydata,int selection)
{
  const struct glue_key *K = keydata;
  int result = 1;
  if (!K) return 0;
  if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) result &= (K->state != 0);
  if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) result &= (K->state == GLUE_KEY_SK);
  return result;
}

static int key_match(const void *keydata1,const void *keydata2,int selection)
{
  const struct glue_key *K1 = keydata1;
  const struct glue_key *K2 = keydata2;
  if (!K1) return 0;
  if (!K2) return 0;
  if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
    if (K1->state && K2->state)
      if (!CRYPTO_memcmp(K1->data+SKBYTES-PKBYTES,K2->data+SKBYTES-PKBYTES,PKBYTES))
        return 1;
  if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)
    if ((K1->state == GLUE_KEY_SK) && (K2->state == GLUE_KEY_SK))
      if (!CRYPTO_memcmp(K1->data,K2->data,SKBYTES))
        return 1;
  return 0;
}

static int key_import(void *keydata,int selection,const OSSL_PARAM params[])
{
  struct glue_key *K = keydata;
  if (!K) return 0;
  if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY) {
    const OSSL_PARAM *p = OSSL_PARAM_locate_const(params,OSSL_PKEY_PARAM_PUB_KEY);
    if (p) {
      size_t len = 0;
      void *ptr = K->data+SKBYTES-PKBYTES;
      memset(K,0,sizeof(struct glue_key));
      if (!OSSL_PARAM_get_octet_string(p,&ptr,PKBYTES,&len)) return 0;
      if (len != PKBYTES) return 0;
      K->state = GLUE_KEY_PK;
    }
  }
  if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY) {
    const OSSL_PARAM *p = OSSL_PARAM_locate_const(params,OSSL_PKEY_PARAM_PRIV_KEY);
    if (p) {
      size_t len = 0;
      void *ptr = K->data;
      memset(K,0,sizeof(struct glue_key));
      if (!OSSL_PARAM_get_octet_string(p,&ptr,SKBYTES,&len)) return 0;
      if (len != SKBYTES) return 0;
      K->state = GLUE_KEY_SK;
    }
  }
  return 1;
}

static int key_export(void *keydata,int selection,OSSL_CALLBACK *param_cb,void *cbarg)
{
  struct glue_key *K = keydata;
#define KEY_EXPORT_PARAMS_MAX 3
  OSSL_PARAM params[KEY_EXPORT_PARAMS_MAX];
  int paramspos = 0;
  if (!K) return 0;
  if (selection & OSSL_KEYMGMT_SELECT_PUBLIC_KEY)
    if (K->state) {
      if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0;
      params[paramspos++] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PUB_KEY,K->data+SKBYTES-PKBYTES,PKBYTES);
    }
  if (selection & OSSL_KEYMGMT_SELECT_PRIVATE_KEY)
    if (K->state == GLUE_KEY_SK) {
      if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0;
      params[paramspos++] = OSSL_PARAM_construct_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,K->data,SKBYTES);
    }
  if (paramspos >= KEY_EXPORT_PARAMS_MAX) return 0;
  params[paramspos++] = OSSL_PARAM_construct_end();
  return param_cb(params,cbarg);
}

static const OSSL_PARAM params_pub_and_priv[] = {
  OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PUB_KEY,0,0),
  OSSL_PARAM_octet_string(OSSL_PKEY_PARAM_PRIV_KEY,0,0),
  OSSL_PARAM_END
} ;

static const OSSL_PARAM *key_import_export_types(int selection)
{
  if (selection & OSSL_KEYMGMT_SELECT_KEYPAIR)
    return params_pub_and_priv;
    // maybe in principle should narrow further by pub-vs-priv selection
    // but examples in openssl do not do that
  return 0;
}

static const OSSL_DISPATCH key_functions[] = {
  { OSSL_FUNC_KEYMGMT_NEW, (void(*)(void)) key_new },
  { OSSL_FUNC_KEYMGMT_FREE, (void(*)(void)) key_free },
  { OSSL_FUNC_KEYMGMT_GEN_INIT, (void(*)(void)) key_gen_init },
  { OSSL_FUNC_KEYMGMT_GEN_SET_PARAMS, (void(*)(void)) key_gen_set_params },
  { OSSL_FUNC_KEYMGMT_GEN_SETTABLE_PARAMS, (void(*)(void)) key_gen_settable_params },
  { OSSL_FUNC_KEYMGMT_GEN, (void(*)(void)) key_gen },
  { OSSL_FUNC_KEYMGMT_GEN_CLEANUP, (void(*)(void)) key_gen_cleanup },
  { OSSL_FUNC_KEYMGMT_LOAD, (void(*)(void)) key_load },
  { OSSL_FUNC_KEYMGMT_GETTABLE_PARAMS, (void(*)(void)) key_gettable_params },
  { OSSL_FUNC_KEYMGMT_GET_PARAMS, (void(*)(void)) key_get_params },
  { OSSL_FUNC_KEYMGMT_SETTABLE_PARAMS, (void(*)(void)) key_settable_params },
  { OSSL_FUNC_KEYMGMT_SET_PARAMS, (void(*)(void)) key_set_params },
  { OSSL_FUNC_KEYMGMT_HAS, (void(*)(void)) key_has },
  { OSSL_FUNC_KEYMGMT_MATCH, (void(*)(void)) key_match },
  { OSSL_FUNC_KEYMGMT_IMPORT, (void(*)(void)) key_import },
  { OSSL_FUNC_KEYMGMT_EXPORT, (void(*)(void)) key_export },
  { OSSL_FUNC_KEYMGMT_IMPORT_TYPES, (void(*)(void)) key_import_export_types },
  { OSSL_FUNC_KEYMGMT_EXPORT_TYPES, (void(*)(void)) key_import_export_types },
  { 0, 0 },
} ;

static const OSSL_ALGORITHM provider_keygen[] = {
  { glue_algorithm_names,PROVIDER,key_functions },
  { 0, 0, 0 },
};


// ----- signing and verification

static void *signver_new(void *provctx,const char *propq)
{
  return OPENSSL_zalloc(sizeof(struct glue_key));
}

static void signver_free(void *ctx)
{
  OPENSSL_clear_free(ctx,sizeof(struct glue_key));
}

static int signver_copykey(void *ctx,const char *mdname,void *provkey,const OSSL_PARAM params[])
{
  if (!ctx) return 0;
  if (!provkey) return 1;
  // XXX: where is it documented that provkey can legitimately be 0?
  // this occurs in "reinit" during, e.g., openssl speed
  memcpy(ctx,provkey,sizeof(struct glue_key));
  return 1;
}

static int sign(void *ctx,unsigned char *sigret,size_t *siglen,size_t sigsize,const unsigned char *tbs,size_t tbslen)
{
  struct glue_key *K = ctx;
  if (!K) return 0;
  if (!sigret) {
    *siglen = SIGBYTES;
    return 1;
  }
  if (sigsize < SIGBYTES) return 0;
  if (!glue_key_sign(sigret,tbs,tbslen,K)) return 0;
  *siglen = SIGBYTES;
  return 1;
}

static int verify(void *ctx,const unsigned char *sig,size_t siglen,const unsigned char *tbs,size_t tbslen)
{
  struct glue_key *K = ctx;
  if (siglen != SIGBYTES) return 0;
  if (!glue_key_verify(sig,tbs,tbslen,K)) return 0;
  return 1;
}

static const OSSL_PARAM signver_params[] = {
  OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_DIGEST,0,0),
  OSSL_PARAM_octet_string(OSSL_SIGNATURE_PARAM_ALGORITHM_ID,0,0),
  OSSL_PARAM_END
} ;

static const OSSL_PARAM *signver_gettable_params(void *provctx)
{
  return signver_params;
}

static int signver_get_params(void *ctx,OSSL_PARAM params[])
{
  OSSL_PARAM *p = 0;

  p = OSSL_PARAM_locate(params,OSSL_SIGNATURE_PARAM_DIGEST);
  if (p && !OSSL_PARAM_set_utf8_string(p,"")) return 0;

  p = OSSL_PARAM_locate(params,OSSL_SIGNATURE_PARAM_ALGORITHM_ID);
  if (p && !OSSL_PARAM_set_octet_string(p,glue_algorithm_asn1,sizeof glue_algorithm_asn1)) return 0;

  return 1;
}

static const OSSL_DISPATCH signver_functions[] = {
  { OSSL_FUNC_SIGNATURE_NEWCTX, (void(*)(void)) signver_new },
  { OSSL_FUNC_SIGNATURE_FREECTX, (void(*)(void)) signver_free },
  { OSSL_FUNC_SIGNATURE_DIGEST_SIGN_INIT, (void(*)(void)) signver_copykey },
  { OSSL_FUNC_SIGNATURE_DIGEST_SIGN, (void(*)(void)) sign },
  { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY_INIT, (void(*)(void)) signver_copykey },
  { OSSL_FUNC_SIGNATURE_DIGEST_VERIFY, (void(*)(void)) verify },
  { OSSL_FUNC_SIGNATURE_GETTABLE_CTX_PARAMS, (void(*)(void)) signver_gettable_params },
  { OSSL_FUNC_SIGNATURE_GET_CTX_PARAMS, (void(*)(void)) signver_get_params },
  { 0, 0 },
};

static const OSSL_ALGORITHM provider_signatures[] = {
  { glue_algorithm_names,PROVIDER,signver_functions },
  { 0, 0, 0 },
};


// ----- top-level provider functions

static const OSSL_ALGORITHM *provider_get_algorithms(void *provctx,int operation_id,int *no_store)
{
  *no_store = 0; // ok for openssl to store results
  if (operation_id == OSSL_OP_ENCODER) return provider_encoder;
  if (operation_id == OSSL_OP_DECODER) return provider_decoder;
  if (operation_id == OSSL_OP_KEYMGMT) return provider_keygen;
  if (operation_id == OSSL_OP_SIGNATURE) return provider_signatures;
  return 0;
}

static const OSSL_PARAM provider_params_list[] = {
  OSSL_PARAM_DEFN(OSSL_PROV_PARAM_NAME,OSSL_PARAM_UTF8_PTR,0,0),
  OSSL_PARAM_DEFN(OSSL_PROV_PARAM_VERSION,OSSL_PARAM_UTF8_PTR,0,0),
  OSSL_PARAM_DEFN(OSSL_PROV_PARAM_STATUS,OSSL_PARAM_INTEGER,0,0),
  OSSL_PARAM_END,
} ;

static const OSSL_PARAM *provider_gettable_params(void)
{
  return provider_params_list;
}

// used by, e.g., openssl list -providers
static int provider_get_params(void *provctx,OSSL_PARAM params[])
{
  OSSL_PARAM *p = 0;

  p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_NAME);
  if (p && !OSSL_PARAM_set_utf8_ptr(p,glue_provider_name)) return 0;

  p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_VERSION);
  if (p && !OSSL_PARAM_set_utf8_ptr(p,glue_provider_version)) return 0;

  p = OSSL_PARAM_locate(params,OSSL_PROV_PARAM_STATUS);
  if (p && !OSSL_PARAM_set_int(p,1)) return 0;
  // 1 means provider is running

  return 1;
}

static void provider_teardown(void *provctx)
{
  OSSL_LIB_CTX_free(provctx);
}

static const OSSL_DISPATCH provider_functions[] = {
  { OSSL_FUNC_PROVIDER_QUERY_OPERATION, (void(*)(void)) provider_get_algorithms },
  { OSSL_FUNC_PROVIDER_GETTABLE_PARAMS, (void(*)(void)) provider_gettable_params },
  { OSSL_FUNC_PROVIDER_GET_PARAMS, (void(*)(void)) provider_get_params },
  { OSSL_FUNC_PROVIDER_TEARDOWN, (void(*)(void)) provider_teardown },
  { 0, 0 },
};

int OSSL_provider_init(const OSSL_CORE_HANDLE *handle,const OSSL_DISPATCH *in,const OSSL_DISPATCH **out,void **provctx)
{
  OSSL_LIB_CTX *provider_context = OSSL_LIB_CTX_new_child(handle,in);
  if (!provider_context) return 0;
  *out = provider_functions;
  *provctx = provider_context;
  return 1;
}

Attachment: test.sh
Description: Bourne shell script

Attachment: signature.asc
Description: PGP signature


[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]

[Index of Archives]     [Linux ARM Kernel]     [Linux ARM]     [Linux Omap]     [Fedora ARM]     [IETF Annouce]     [Security]     [Bugtraq]     [Linux]     [Linux OMAP]     [Linux MIPS]     [ECOS]     [Asterisk Internet PBX]     [Linux API]

  Powered by Linux