From ffb1ac674c8294bf519add26fb37d94b7afeceb4 Mon Sep 17 00:00:00 2001 From: "Dr. Stephen Henson" Date: Thu, 13 Apr 2006 20:16:56 +0000 Subject: [PATCH] Complete key derivation support. --- CHANGES | 4 ++ apps/pkeyutl.c | 125 ++++++++++++++++++++++++++++++----------- crypto/dh/dh.h | 2 + crypto/dh/dh_ameth.c | 21 ++++--- crypto/dh/dh_err.c | 2 + crypto/dh/dh_pmeth.c | 24 +++++++- crypto/evp/evp.h | 8 +++ crypto/evp/evp_err.c | 3 + crypto/evp/pmeth_fn.c | 60 ++++++++++++++++++++ crypto/evp/pmeth_lib.c | 2 + 10 files changed, 211 insertions(+), 40 deletions(-) diff --git a/CHANGES b/CHANGES index 2a07f91dc2..3ba2d71f2f 100644 --- a/CHANGES +++ b/CHANGES @@ -4,6 +4,10 @@ Changes between 0.9.8a and 0.9.9 [xx XXX xxxx] + *) Add support for key derivation (agreement) in the API, DH method and + pkeyutl. + [Steve Henson] + *) Add DSA pkey method and DH pkey methods, extend DH ASN1 method to support public and private key formats. As a side effect these add additional command line functionality not previously available: DSA signatures can be diff --git a/apps/pkeyutl.c b/apps/pkeyutl.c index 522656df9e..a3e55f5a43 100644 --- a/apps/pkeyutl.c +++ b/apps/pkeyutl.c @@ -74,7 +74,10 @@ static void usage(void); static EVP_PKEY_CTX *init_ctx(int *pkeysize, char *keyfile, int keyform, int key_type, - char *passargin, int pkey_op, char *engine); + char *passargin, int pkey_op, ENGINE *e); + +static int setup_peer(BIO *err, EVP_PKEY_CTX *ctx, int peerform, + const char *file); int MAIN(int argc, char **); @@ -82,9 +85,9 @@ int MAIN(int argc, char **argv) { BIO *in = NULL, *out = NULL; char *infile = NULL, *outfile = NULL, *sigfile = NULL; - char *engine = NULL; + ENGINE *e = NULL; int pkey_op = EVP_PKEY_OP_SIGN, key_type = KEY_PRIVKEY; - int keyform = FORMAT_PEM; + int keyform = FORMAT_PEM, peerform = FORMAT_PEM; char badarg = 0, rev = 0; char hexdump = 0, asn1parse = 0; EVP_PKEY_CTX *ctx = NULL; @@ -131,7 +134,7 @@ int MAIN(int argc, char **argv) { ctx = init_ctx(&keysize, *(++argv), keyform, key_type, - passargin, pkey_op, engine); + passargin, pkey_op, e); if (!ctx) { BIO_puts(bio_err, @@ -141,11 +144,23 @@ int MAIN(int argc, char **argv) } } } + else if (!strcmp(*argv,"-peerkey")) + { + if (--argc < 1) + badarg = 1; + else if (!setup_peer(bio_err, ctx, peerform, *(++argv))) + badarg = 1; + } else if (!strcmp(*argv,"-passin")) { if (--argc < 1) badarg = 1; passargin= *(++argv); } + else if (strcmp(*argv,"-peerform") == 0) + { + if (--argc < 1) badarg = 1; + peerform=str2fmt(*(++argv)); + } else if (strcmp(*argv,"-keyform") == 0) { if (--argc < 1) badarg = 1; @@ -157,7 +172,7 @@ int MAIN(int argc, char **argv) if (--argc < 1) badarg = 1; else - engine = *(++argv); + e = setup_engine(bio_err, *(++argv), 0); } #endif else if(!strcmp(*argv, "-pubin")) @@ -180,14 +195,16 @@ int MAIN(int argc, char **argv) pkey_op = EVP_PKEY_OP_ENCRYPT; else if(!strcmp(*argv, "-decrypt")) pkey_op = EVP_PKEY_OP_DECRYPT; + else if(!strcmp(*argv, "-derive")) + pkey_op = EVP_PKEY_OP_DERIVE; else if (strcmp(*argv,"-pkeyopt") == 0) { if (--argc < 1) badarg = 1; - if (!ctx) + else if (!ctx) { BIO_puts(bio_err, - "-param command before -inkey\n"); + "-pkeyopt command before -inkey\n"); badarg = 1; } else if (pkey_ctrl_string(ctx, *(++argv)) <= 0) @@ -228,17 +245,21 @@ int MAIN(int argc, char **argv) /* FIXME: seed PRNG only if needed */ app_RAND_load_file(NULL, bio_err, 0); - if(infile) + if (pkey_op != EVP_PKEY_OP_DERIVE) { - if(!(in = BIO_new_file(infile, "rb"))) + if(infile) { - BIO_printf(bio_err, "Error Reading Input File\n"); - ERR_print_errors(bio_err); - goto end; + if(!(in = BIO_new_file(infile, "rb"))) + { + BIO_puts(bio_err, + "Error Opening Input File\n"); + ERR_print_errors(bio_err); + goto end; + } } + else + in = BIO_new_fp(stdin, BIO_NOCLOSE); } - else - in = BIO_new_fp(stdin, BIO_NOCLOSE); if(outfile) { @@ -280,24 +301,28 @@ int MAIN(int argc, char **argv) buf_out = OPENSSL_malloc(keysize); - /* Read the input data */ - buf_inlen = bio_to_mem(&buf_in, keysize * 10, in); - if(buf_inlen <= 0) + if (in) { - BIO_printf(bio_err, "Error reading input Data\n"); - exit(1); - } - if(rev) - { - int i; - unsigned char ctmp; - for(i = 0; i < buf_inlen/2; i++) + /* Read the input data */ + buf_inlen = bio_to_mem(&buf_in, keysize * 10, in); + if(buf_inlen <= 0) + { + BIO_printf(bio_err, "Error reading input Data\n"); + exit(1); + } + if(rev) { - ctmp = buf_in[i]; - buf_in[i] = buf_in[buf_inlen - 1 - i]; - buf_in[buf_inlen - 1 - i] = ctmp; + int i; + unsigned char ctmp; + for(i = 0; i < buf_inlen/2; i++) + { + ctmp = buf_in[i]; + buf_in[i] = buf_in[buf_inlen - 1 - i]; + buf_in[buf_inlen - 1 - i] = ctmp; + } } } + switch(pkey_op) { case EVP_PKEY_OP_VERIFYRECOVER: @@ -330,6 +355,10 @@ int MAIN(int argc, char **argv) goto end; break; + case EVP_PKEY_OP_DERIVE: + rv = EVP_PKEY_derive(ctx, buf_out, &buf_outlen); + break; + } if(rv <= 0) @@ -387,15 +416,15 @@ static void usage() static EVP_PKEY_CTX *init_ctx(int *pkeysize, char *keyfile, int keyform, int key_type, - char *passargin, int pkey_op, char *engine) + char *passargin, int pkey_op, ENGINE *e) { - ENGINE *e = NULL; EVP_PKEY *pkey = NULL; EVP_PKEY_CTX *ctx = NULL; char *passin = NULL; int rv = -1; X509 *x; - if(((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT)) + if(((pkey_op == EVP_PKEY_OP_SIGN) || (pkey_op == EVP_PKEY_OP_DECRYPT) + || (pkey_op == EVP_PKEY_OP_DERIVE)) && (key_type != KEY_PRIVKEY)) { BIO_printf(bio_err, "A private key is needed for this operation\n"); @@ -435,7 +464,7 @@ static EVP_PKEY_CTX *init_ctx(int *pkeysize, if (!pkey) goto end; - ctx = EVP_PKEY_CTX_new(pkey, NULL); + ctx = EVP_PKEY_CTX_new(pkey, e); EVP_PKEY_free(pkey); @@ -463,6 +492,10 @@ static EVP_PKEY_CTX *init_ctx(int *pkeysize, case EVP_PKEY_OP_DECRYPT: rv = EVP_PKEY_decrypt_init(ctx); break; + + case EVP_PKEY_OP_DERIVE: + rv = EVP_PKEY_derive_init(ctx); + break; } if (rv <= 0) @@ -481,3 +514,31 @@ static EVP_PKEY_CTX *init_ctx(int *pkeysize, } +static int setup_peer(BIO *err, EVP_PKEY_CTX *ctx, int peerform, + const char *file) + { + EVP_PKEY *peer = NULL; + int ret; + if (!ctx) + { + BIO_puts(err, "-peerkey command before -inkey\n"); + return 0; + } + + peer = load_pubkey(bio_err, file, peerform, 0, NULL, NULL, "Peer Key"); + + if (!peer) + { + BIO_printf(bio_err, "Error reading peer key %s\n", file); + ERR_print_errors(err); + return 0; + } + + ret = EVP_PKEY_derive_set_peer(ctx, peer); + + EVP_PKEY_free(peer); + if (ret <= 0) + ERR_print_errors(err); + return ret; + } + diff --git a/crypto/dh/dh.h b/crypto/dh/dh.h index b4b181c4d9..10bf3fe5de 100644 --- a/crypto/dh/dh.h +++ b/crypto/dh/dh.h @@ -234,6 +234,7 @@ void ERR_load_DH_strings(void); #define DH_F_DH_PUB_ENCODE 109 #define DH_F_GENERATE_KEY 103 #define DH_F_GENERATE_PARAMETERS 104 +#define DH_F_PKEY_DH_DERIVE 112 #define DH_F_PKEY_DH_KEYGEN 113 /* Reason codes. */ @@ -242,6 +243,7 @@ void ERR_load_DH_strings(void); #define DH_R_BN_ERROR 106 #define DH_R_DECODE_ERROR 104 #define DH_R_INVALID_PUBKEY 102 +#define DH_R_KEYS_NOT_SET 108 #define DH_R_NO_PARAMETERS_SET 107 #define DH_R_NO_PRIVATE_VALUE 100 #define DH_R_PARAMETER_ENCODING_ERROR 105 diff --git a/crypto/dh/dh_ameth.c b/crypto/dh/dh_ameth.c index 6d3125789d..7a83768e2c 100644 --- a/crypto/dh/dh_ameth.c +++ b/crypto/dh/dh_ameth.c @@ -397,6 +397,15 @@ static int dh_bits(const EVP_PKEY *pkey) return BN_num_bits(pkey->pkey.dh->p); } +static int dh_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) + { + if ( BN_cmp(a->pkey.dh->p,b->pkey.dh->p) || + BN_cmp(a->pkey.dh->g,b->pkey.dh->g)) + return 0; + else + return 1; + } + static int dh_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) { BIGNUM *a; @@ -409,20 +418,18 @@ static int dh_copy_parameters(EVP_PKEY *to, const EVP_PKEY *from) if ((a=BN_dup(from->pkey.dh->g)) == NULL) return 0; - if (to->pkey.dsa->g != NULL) + if (to->pkey.dh->g != NULL) BN_free(to->pkey.dh->g); to->pkey.dh->g=a; return 1; } -static int dh_cmp_parameters(const EVP_PKEY *a, const EVP_PKEY *b) +static int dh_missing_parameters(const EVP_PKEY *a) { - if ( BN_cmp(a->pkey.dh->p,b->pkey.dsa->p) || - BN_cmp(a->pkey.dh->g,b->pkey.dsa->g)) - return 0; - else + if (!a->pkey.dh->p || !a->pkey.dh->g) return 1; + return 0; } static int dh_pub_cmp(const EVP_PKEY *a, const EVP_PKEY *b) @@ -481,7 +488,7 @@ const EVP_PKEY_ASN1_METHOD dh_asn1_meth = dh_param_decode, dh_param_encode, - 0, + dh_missing_parameters, dh_copy_parameters, dh_cmp_parameters, dh_param_print, diff --git a/crypto/dh/dh_err.c b/crypto/dh/dh_err.c index 64acfdea11..2fdfc5516e 100644 --- a/crypto/dh/dh_err.c +++ b/crypto/dh/dh_err.c @@ -82,6 +82,7 @@ static ERR_STRING_DATA DH_str_functs[]= {ERR_FUNC(DH_F_DH_PUB_ENCODE), "DH_PUB_ENCODE"}, {ERR_FUNC(DH_F_GENERATE_KEY), "GENERATE_KEY"}, {ERR_FUNC(DH_F_GENERATE_PARAMETERS), "GENERATE_PARAMETERS"}, +{ERR_FUNC(DH_F_PKEY_DH_DERIVE), "PKEY_DH_DERIVE"}, {ERR_FUNC(DH_F_PKEY_DH_KEYGEN), "PKEY_DH_KEYGEN"}, {0,NULL} }; @@ -93,6 +94,7 @@ static ERR_STRING_DATA DH_str_reasons[]= {ERR_REASON(DH_R_BN_ERROR) ,"bn error"}, {ERR_REASON(DH_R_DECODE_ERROR) ,"decode error"}, {ERR_REASON(DH_R_INVALID_PUBKEY) ,"invalid public key"}, +{ERR_REASON(DH_R_KEYS_NOT_SET) ,"keys not set"}, {ERR_REASON(DH_R_NO_PARAMETERS_SET) ,"no parameters set"}, {ERR_REASON(DH_R_NO_PRIVATE_VALUE) ,"no private value"}, {ERR_REASON(DH_R_PARAMETER_ENCODING_ERROR),"parameter encoding error"}, diff --git a/crypto/dh/dh_pmeth.c b/crypto/dh/dh_pmeth.c index d2e6aaff1e..aaf32804b8 100644 --- a/crypto/dh/dh_pmeth.c +++ b/crypto/dh/dh_pmeth.c @@ -115,11 +115,16 @@ static int pkey_dh_ctrl(EVP_PKEY_CTX *ctx, int type, int p1, void *p2) dctx->generator = p1; return 1; + case EVP_PKEY_CTRL_PEER_KEY: + /* Default behaviour is OK */ + return 1; + default: return -2; } } + static int pkey_dh_ctrl_str(EVP_PKEY_CTX *ctx, const char *type, const char *value) @@ -182,6 +187,22 @@ static int pkey_dh_keygen(EVP_PKEY_CTX *ctx, EVP_PKEY *pkey) return DH_generate_key(pkey->pkey.dh); } +static int pkey_dh_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *keylen) + { + int ret; + if (!ctx->pkey || !ctx->peerkey) + { + DHerr(DH_F_PKEY_DH_DERIVE, DH_R_KEYS_NOT_SET); + return 0; + } + ret = DH_compute_key(key, ctx->peerkey->pkey.dh->pub_key, + ctx->pkey->pkey.dh); + if (ret < 0) + return ret; + *keylen = ret; + return 1; + } + const EVP_PKEY_METHOD dh_pkey_meth = { EVP_PKEY_DH, @@ -209,7 +230,8 @@ const EVP_PKEY_METHOD dh_pkey_meth = 0,0, - 0,0, + 0, + pkey_dh_derive, pkey_dh_ctrl, pkey_dh_ctrl_str diff --git a/crypto/evp/evp.h b/crypto/evp/evp.h index d60781696a..bfffdd562a 100644 --- a/crypto/evp/evp.h +++ b/crypto/evp/evp.h @@ -933,6 +933,7 @@ void EVP_PKEY_asn1_set_ctrl(EVP_PKEY_ASN1_METHOD *ameth, EVP_PKEY_CTRL_MD, 0, (void *)md) #define EVP_PKEY_CTRL_MD 1 +#define EVP_PKEY_CTRL_PEER_KEY 2 #define EVP_PKEY_ALG_CTRL 0x1000 @@ -972,6 +973,10 @@ int EVP_PKEY_decrypt(EVP_PKEY_CTX *ctx, unsigned char *out, int *outlen, const unsigned char *in, int inlen); +int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx); +int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer); +int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *keylen); + typedef int EVP_PKEY_gen_cb(EVP_PKEY_CTX *ctx); int EVP_PKEY_paramgen_init(EVP_PKEY_CTX *ctx); @@ -1018,6 +1023,7 @@ void ERR_load_EVP_strings(void); #define EVP_F_EVP_PKEY_DECRYPT_OLD 151 #define EVP_F_EVP_PKEY_DERIVE 153 #define EVP_F_EVP_PKEY_DERIVE_INIT 154 +#define EVP_F_EVP_PKEY_DERIVE_SET_PEER 155 #define EVP_F_EVP_PKEY_ENCRYPT 105 #define EVP_F_EVP_PKEY_ENCRYPT_INIT 139 #define EVP_F_EVP_PKEY_ENCRYPT_OLD 152 @@ -1061,6 +1067,7 @@ void ERR_load_EVP_strings(void); #define EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH 138 #define EVP_R_DECODE_ERROR 114 #define EVP_R_DIFFERENT_KEY_TYPES 101 +#define EVP_R_DIFFERENT_PARAMETERS 153 #define EVP_R_ENCODE_ERROR 115 #define EVP_R_EVP_PBE_CIPHERINIT_ERROR 119 #define EVP_R_EXPECTING_AN_RSA_KEY 127 @@ -1080,6 +1087,7 @@ void ERR_load_EVP_strings(void); #define EVP_R_NO_CIPHER_SET 131 #define EVP_R_NO_DIGEST_SET 139 #define EVP_R_NO_DSA_PARAMETERS 116 +#define EVP_R_NO_KEY_SET 154 #define EVP_R_NO_OPERATION_SET 149 #define EVP_R_NO_SIGN_FUNCTION_CONFIGURED 104 #define EVP_R_NO_VERIFY_FUNCTION_CONFIGURED 105 diff --git a/crypto/evp/evp_err.c b/crypto/evp/evp_err.c index 50d5a71276..225564d25e 100644 --- a/crypto/evp/evp_err.c +++ b/crypto/evp/evp_err.c @@ -97,6 +97,7 @@ static ERR_STRING_DATA EVP_str_functs[]= {ERR_FUNC(EVP_F_EVP_PKEY_DECRYPT_OLD), "EVP_PKEY_decrypt_old"}, {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE), "EVP_PKEY_DERIVE"}, {ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_INIT), "EVP_PKEY_DERIVE_INIT"}, +{ERR_FUNC(EVP_F_EVP_PKEY_DERIVE_SET_PEER), "EVP_PKEY_DERIVE_SET_PEER"}, {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT), "EVP_PKEY_encrypt"}, {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_INIT), "EVP_PKEY_encrypt_init"}, {ERR_FUNC(EVP_F_EVP_PKEY_ENCRYPT_OLD), "EVP_PKEY_encrypt_old"}, @@ -143,6 +144,7 @@ static ERR_STRING_DATA EVP_str_reasons[]= {ERR_REASON(EVP_R_DATA_NOT_MULTIPLE_OF_BLOCK_LENGTH),"data not multiple of block length"}, {ERR_REASON(EVP_R_DECODE_ERROR) ,"decode error"}, {ERR_REASON(EVP_R_DIFFERENT_KEY_TYPES) ,"different key types"}, +{ERR_REASON(EVP_R_DIFFERENT_PARAMETERS) ,"different parameters"}, {ERR_REASON(EVP_R_ENCODE_ERROR) ,"encode error"}, {ERR_REASON(EVP_R_EVP_PBE_CIPHERINIT_ERROR),"evp pbe cipherinit error"}, {ERR_REASON(EVP_R_EXPECTING_AN_RSA_KEY) ,"expecting an rsa key"}, @@ -162,6 +164,7 @@ static ERR_STRING_DATA EVP_str_reasons[]= {ERR_REASON(EVP_R_NO_CIPHER_SET) ,"no cipher set"}, {ERR_REASON(EVP_R_NO_DIGEST_SET) ,"no digest set"}, {ERR_REASON(EVP_R_NO_DSA_PARAMETERS) ,"no dsa parameters"}, +{ERR_REASON(EVP_R_NO_KEY_SET) ,"no key set"}, {ERR_REASON(EVP_R_NO_OPERATION_SET) ,"no operation set"}, {ERR_REASON(EVP_R_NO_SIGN_FUNCTION_CONFIGURED),"no sign function configured"}, {ERR_REASON(EVP_R_NO_VERIFY_FUNCTION_CONFIGURED),"no verify function configured"}, diff --git a/crypto/evp/pmeth_fn.c b/crypto/evp/pmeth_fn.c index 0ad8098718..3d09ba245a 100644 --- a/crypto/evp/pmeth_fn.c +++ b/crypto/evp/pmeth_fn.c @@ -262,6 +262,66 @@ int EVP_PKEY_derive_init(EVP_PKEY_CTX *ctx) return ret; } +int EVP_PKEY_derive_set_peer(EVP_PKEY_CTX *ctx, EVP_PKEY *peer) + { + int ret; + if (!ctx || !ctx->pmeth || !ctx->pmeth->derive || !ctx->pmeth->ctrl) + { + EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, + EVP_R_OPERATION_NOT_SUPPORTED_FOR_THIS_KEYTYPE); + return -2; + } + if (ctx->operation != EVP_PKEY_OP_DERIVE) + { + EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, + EVP_R_OPERATON_NOT_INITIALIZED); + return -1; + } + + ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 0, peer); + + if (ret <= 0) + return ret; + + if (ret == 2) + return 1; + + if (!ctx->pkey) + { + EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, EVP_R_NO_KEY_SET); + return -1; + } + + if (ctx->pkey->type != peer->type) + { + EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, + EVP_R_DIFFERENT_KEY_TYPES); + return -1; + } + + if (!EVP_PKEY_missing_parameters(peer) && + !EVP_PKEY_cmp_parameters(ctx->pkey, peer)) + { + EVPerr(EVP_F_EVP_PKEY_DERIVE_SET_PEER, + EVP_R_DIFFERENT_PARAMETERS); + return -1; + } + + ctx->peerkey = peer; + + ret = ctx->pmeth->ctrl(ctx, EVP_PKEY_CTRL_PEER_KEY, 1, peer); + + if (ret <= 0) + { + ctx->peerkey = NULL; + return ret; + } + + CRYPTO_add(&peer->references,1,CRYPTO_LOCK_EVP_PKEY); + return 1; + } + + int EVP_PKEY_derive(EVP_PKEY_CTX *ctx, unsigned char *key, int *pkeylen) { if (!ctx || !ctx->pmeth || !ctx->pmeth->derive) diff --git a/crypto/evp/pmeth_lib.c b/crypto/evp/pmeth_lib.c index 95b1e4ed3c..97fcf26012 100644 --- a/crypto/evp/pmeth_lib.c +++ b/crypto/evp/pmeth_lib.c @@ -153,6 +153,8 @@ void EVP_PKEY_CTX_free(EVP_PKEY_CTX *ctx) ctx->pmeth->cleanup(ctx); if (ctx->pkey) EVP_PKEY_free(ctx->pkey); + if (ctx->peerkey) + EVP_PKEY_free(ctx->peerkey); OPENSSL_free(ctx); } -- 2.25.1