-
Notifications
You must be signed in to change notification settings - Fork 145
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
soter: Fix misuse of OpenSSL API for EC key generation (and more) #875
Changes from all commits
f5df81c
d584886
dddb5de
5b2fd63
b7a033d
1db92dd
a397d20
5e117ae
8d78f4a
afec504
7d26fa2
d6fe9ef
cfac368
75eed90
1aac793
3e75937
74dc62d
7e10f13
430f4d1
38528e7
20045ff
641b2b4
07857ef
bd761cb
71d8f54
958a2da
8fcebc7
249df47
4309d10
5057246
e776d38
a17b904
8dcf4cf
0493e01
5ffd622
c21361f
53d8dd6
2815d50
4301971
f5191ab
4e13e9e
e2f296f
69e0640
8e8ed18
818f84a
f1f3c64
9c7323d
464568b
1f2fe5f
773a317
a88c040
18c01ff
5fa6b5d
32e5cf6
da25170
c4f514b
d55e305
86fb806
7dcc4ce
5de6be2
e6fc385
108e666
fa65591
ddcb7e6
7b1c40f
f077bab
c350c4e
a8dbfe9
78e60b7
bce1c76
1a10739
14fca9b
c6cbfb5
a611770
6255590
da03406
0e5793a
bd6a952
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -22,28 +22,59 @@ | |
#include "soter/openssl/soter_engine.h" | ||
#include "soter/soter_ec_key.h" | ||
|
||
soter_status_t soter_ec_gen_key(EVP_PKEY_CTX* pkey_ctx) | ||
soter_status_t soter_ec_gen_key(EVP_PKEY** ppkey) | ||
{ | ||
EVP_PKEY* pkey; | ||
EC_KEY* ec; | ||
if (!pkey_ctx) { | ||
soter_status_t res = SOTER_FAIL; | ||
EVP_PKEY* param = NULL; | ||
EVP_PKEY_CTX* param_ctx = NULL; | ||
EVP_PKEY_CTX* pkey_ctx = NULL; | ||
|
||
if (!ppkey) { | ||
return SOTER_INVALID_PARAMETER; | ||
} | ||
pkey = EVP_PKEY_CTX_get0_pkey(pkey_ctx); | ||
if (!pkey) { | ||
return SOTER_INVALID_PARAMETER; | ||
|
||
param_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_EC, NULL); | ||
if (!param_ctx) { | ||
res = SOTER_NO_MEMORY; | ||
goto err; | ||
} | ||
if (EVP_PKEY_EC != EVP_PKEY_id(pkey)) { | ||
return SOTER_INVALID_PARAMETER; | ||
|
||
if (EVP_PKEY_paramgen_init(param_ctx) != 1) { | ||
res = SOTER_FAIL; | ||
goto err; | ||
} | ||
ec = EVP_PKEY_get0(pkey); | ||
if (NULL == ec) { | ||
return SOTER_INVALID_PARAMETER; | ||
if (EVP_PKEY_CTX_set_ec_paramgen_curve_nid(param_ctx, NID_X9_62_prime256v1) != 1) { | ||
res = SOTER_FAIL; | ||
goto err; | ||
} | ||
if (1 == EC_KEY_generate_key(ec)) { | ||
return SOTER_SUCCESS; | ||
if (EVP_PKEY_paramgen(param_ctx, ¶m) != 1) { | ||
res = SOTER_FAIL; | ||
goto err; | ||
} | ||
return SOTER_FAIL; | ||
|
||
pkey_ctx = EVP_PKEY_CTX_new(param, NULL); | ||
if (!pkey_ctx) { | ||
res = SOTER_NO_MEMORY; | ||
goto err; | ||
} | ||
|
||
if (EVP_PKEY_keygen_init(pkey_ctx) != 1) { | ||
res = SOTER_FAIL; | ||
goto err; | ||
} | ||
if (EVP_PKEY_keygen(pkey_ctx, ppkey) != 1) { | ||
res = SOTER_FAIL; | ||
goto err; | ||
} | ||
|
||
res = SOTER_SUCCESS; | ||
|
||
err: | ||
EVP_PKEY_CTX_free(param_ctx); | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I assume these functions check for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
That is so. They follow the free() convention: NULL argument is a no-op. |
||
EVP_PKEY_CTX_free(pkey_ctx); | ||
EVP_PKEY_free(param); | ||
|
||
return res; | ||
} | ||
|
||
soter_status_t soter_ec_import_key(EVP_PKEY* pkey, const void* key, const size_t key_length) | ||
|
@@ -71,9 +102,8 @@ soter_status_t soter_ec_import_key(EVP_PKEY* pkey, const void* key, const size_t | |
return SOTER_INVALID_PARAMETER; | ||
} | ||
|
||
soter_status_t soter_ec_export_key(soter_sign_ctx_t* ctx, void* key, size_t* key_length, bool isprivate) | ||
soter_status_t soter_ec_export_key(EVP_PKEY* pkey, void* key, size_t* key_length, bool isprivate) | ||
{ | ||
EVP_PKEY* pkey = EVP_PKEY_CTX_get0_pkey(ctx->pkey_ctx); | ||
if (!pkey) { | ||
return SOTER_INVALID_PARAMETER; | ||
} | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -54,7 +54,7 @@ struct soter_asym_ka_type { | |
}; | ||
|
||
struct soter_sign_ctx_type { | ||
EVP_PKEY_CTX* pkey_ctx; | ||
EVP_PKEY* pkey; | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. correct me if I am wrong. as I understand, we stop storing EVP_KEY_CTX because if used only once for key generation and initialization and after that we don't use it directly, only to extract private/public key from this private structure. And now we store EVP_PKEY, which can store private and public keys, that are only used in all further function calls, yes? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Yes.
Correct.
Yes and no. We don't need to free EVP_PKEY_CTX right away but we don't have to retain it either. That said, it is a bit more heavy data structure than EVP_PKEY, but not very much. As you said, all following methods on The main motivation for removing EVP_PKEY_CTX is to prevent the flawed notion from spreading further. EVP_PKEY_CTX is not a general-purpose container for EVP_PKEY, but rather a transient helper. It matters how EVP_PKEY_CTX is created, what for it has been initialized, etc. It's much easier to check correctness of EVP_PKEY_CTX handling when it's constrained to one function, not retained in an object with multiple methods that can affect and reconfigure the context. |
||
EVP_MD_CTX* md_ctx; | ||
soter_sign_alg_t alg; | ||
}; | ||
|
Original file line number | Diff line number | Diff line change | ||||||
---|---|---|---|---|---|---|---|---|
|
@@ -24,51 +24,66 @@ | |||||||
#define SOTER_RSA_KEY_LENGTH 2048 | ||||||||
#endif | ||||||||
|
||||||||
soter_status_t soter_rsa_gen_key(EVP_PKEY_CTX* pkey_ctx) | ||||||||
soter_status_t soter_rsa_gen_key(EVP_PKEY** ppkey) | ||||||||
{ | ||||||||
/* it is copy-paste from /src/soter/openssl/soter_asym_cipher.c */ | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. it's not anymore? :) There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
You know the story about the ship of Theseus? Right now |
||||||||
BIGNUM* pub_exp; | ||||||||
EVP_PKEY* pkey = EVP_PKEY_CTX_get0_pkey(pkey_ctx); | ||||||||
if (!pkey) { | ||||||||
soter_status_t res = SOTER_FAIL; | ||||||||
BIGNUM* pub_exp = NULL; | ||||||||
EVP_PKEY_CTX* pkey_ctx = NULL; | ||||||||
|
||||||||
if (!ppkey) { | ||||||||
return SOTER_INVALID_PARAMETER; | ||||||||
} | ||||||||
|
||||||||
if (EVP_PKEY_RSA != EVP_PKEY_id(pkey)) { | ||||||||
return SOTER_INVALID_PARAMETER; | ||||||||
pkey_ctx = EVP_PKEY_CTX_new_id(EVP_PKEY_RSA, NULL); | ||||||||
if (!pkey_ctx) { | ||||||||
res = SOTER_NO_MEMORY; | ||||||||
goto err; | ||||||||
} | ||||||||
|
||||||||
if (!EVP_PKEY_keygen_init(pkey_ctx)) { | ||||||||
return SOTER_INVALID_PARAMETER; | ||||||||
if (EVP_PKEY_keygen_init(pkey_ctx) != 1) { | ||||||||
res = SOTER_INVALID_PARAMETER; | ||||||||
goto err; | ||||||||
} | ||||||||
|
||||||||
/* Although it seems that OpenSSL/LibreSSL use 0x10001 as default public exponent, we will set | ||||||||
* it explicitly just in case */ | ||||||||
pub_exp = BN_new(); | ||||||||
if (!pub_exp) { | ||||||||
return SOTER_NO_MEMORY; | ||||||||
res = SOTER_NO_MEMORY; | ||||||||
goto err; | ||||||||
} | ||||||||
|
||||||||
if (!BN_set_word(pub_exp, RSA_F4)) { | ||||||||
BN_free(pub_exp); | ||||||||
return SOTER_FAIL; | ||||||||
res = SOTER_FAIL; | ||||||||
goto err; | ||||||||
} | ||||||||
|
||||||||
/* Passing ownership over pub_exp to EVP_PKEY_CTX */ | ||||||||
if (1 > EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1, EVP_PKEY_CTRL_RSA_KEYGEN_PUBEXP, 0, pub_exp)) { | ||||||||
BN_free(pub_exp); | ||||||||
return SOTER_FAIL; | ||||||||
res = SOTER_FAIL; | ||||||||
goto err; | ||||||||
} | ||||||||
pub_exp = NULL; | ||||||||
|
||||||||
/* Override default key size for RSA key. Currently OpenSSL has default key size of 1024. | ||||||||
* LibreSSL has 2048. We will put 2048 explicitly */ | ||||||||
if (1 > EVP_PKEY_CTX_ctrl(pkey_ctx, -1, -1, EVP_PKEY_CTRL_RSA_KEYGEN_BITS, SOTER_RSA_KEY_LENGTH, NULL)) { | ||||||||
return SOTER_FAIL; | ||||||||
res = SOTER_FAIL; | ||||||||
goto err; | ||||||||
} | ||||||||
|
||||||||
if (!EVP_PKEY_keygen(pkey_ctx, &pkey)) { | ||||||||
return SOTER_FAIL; | ||||||||
if (EVP_PKEY_keygen(pkey_ctx, ppkey) != 1) { | ||||||||
res = SOTER_FAIL; | ||||||||
goto err; | ||||||||
} | ||||||||
return SOTER_SUCCESS; | ||||||||
/* end of copy-paste from /src/soter/openssl/soter_asym_cipher.c*/ | ||||||||
|
||||||||
res = SOTER_SUCCESS; | ||||||||
|
||||||||
err: | ||||||||
BN_free(pub_exp); | ||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
shouldn't we also null the There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
I don't think there is much point in doing that to a local variable on the cleanup code path. For a field – sure, that keeps invalid pointers out. But here the variable will cease to exist with |
||||||||
EVP_PKEY_CTX_free(pkey_ctx); | ||||||||
|
||||||||
return res; | ||||||||
} | ||||||||
|
||||||||
soter_status_t soter_rsa_import_key(EVP_PKEY* pkey, const void* key, const size_t key_length) | ||||||||
|
@@ -94,10 +109,8 @@ soter_status_t soter_rsa_import_key(EVP_PKEY* pkey, const void* key, const size_ | |||||||
return SOTER_INVALID_PARAMETER; | ||||||||
} | ||||||||
|
||||||||
soter_status_t soter_rsa_export_key(soter_sign_ctx_t* ctx, void* key, size_t* key_length, bool isprivate) | ||||||||
soter_status_t soter_rsa_export_key(EVP_PKEY* pkey, void* key, size_t* key_length, bool isprivate) | ||||||||
{ | ||||||||
EVP_PKEY* pkey = EVP_PKEY_CTX_get0_pkey(ctx->pkey_ctx); | ||||||||
|
||||||||
if (!pkey) { | ||||||||
return SOTER_INVALID_PARAMETER; | ||||||||
} | ||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
my original preference was to have constants in comparison operators first, but if the current code style changed - I won't fight for it