- This automatically supports EC certificates through
--management-external-cert
- EC signature request from management is prompted by
>PK_SIGN if the client supports it (or >RSA_SIGN)
Response should be of the form 'pk-sig' (or rsa-sig
by older clients) followed by DER encoded signature
as base64 terminated by 'END' on a new line.
v3: This is v2 adapted to the client_version capability
Requires pacthes 1 and 2 of the series 147:
https://patchwork.openvpn.net/project/openvpn2/list/?series=147
Signed-off-by: Selva Nair <selva.nair@gmail.com>
Acked-by: Arne Schwabe <arne@rfc2549.org>
Message-Id: <1516909513-31683-1-git-send-email-selva.nair@gmail.com>
URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg16365.html
Signed-off-by: Gert Doering <gert@greenie.muc.de>
... | ... |
@@ -779,14 +779,14 @@ COMMAND -- rsa-sig (OpenVPN 2.3 or higher, management version <= 1) |
779 | 779 |
Provides support for external storage of the private key. Requires the |
780 | 780 |
--management-external-key option. This option can be used instead of "key" |
781 | 781 |
in client mode, and allows the client to run without the need to load the |
782 |
-actual private key. When the SSL protocol needs to perform an RSA sign |
|
782 |
+actual private key. When the SSL protocol needs to perform a sign |
|
783 | 783 |
operation, the data to be signed will be sent to the management interface |
784 | 784 |
via a notification as follows: |
785 | 785 |
|
786 | 786 |
>PK_SIGN:[BASE64_DATA] (if client announces support for management version > 1) |
787 | 787 |
>RSA_SIGN:[BASE64_DATA] (only older clients will be prompted like this) |
788 | 788 |
|
789 |
-The management interface client should then create a PKCS#1 v1.5 signature of |
|
789 |
+The management interface client should then create an appropriate signature of |
|
790 | 790 |
the (decoded) BASE64_DATA using the private key and return the SSL signature as |
791 | 791 |
follows: |
792 | 792 |
|
... | ... |
@@ -797,8 +797,9 @@ pk-sig (or rsa-sig) |
797 | 797 |
. |
798 | 798 |
END |
799 | 799 |
|
800 |
-Base64 encoded output of RSA_private_encrypt() (OpenSSL) or mbedtls_pk_sign() |
|
801 |
-(mbed TLS) will provide a correct signature. |
|
800 |
+Base64 encoded output of RSA_private_encrypt for RSA or ECDSA_sign() for EC |
|
801 |
+using OpenSSL or mbedtls_pk_sign() using mbed TLS will provide a correct |
|
802 |
+signature. |
|
802 | 803 |
|
803 | 804 |
This capability is intended to allow the use of arbitrary cryptographic |
804 | 805 |
service providers with OpenVPN via the management interface. |
... | ... |
@@ -1043,58 +1043,51 @@ openvpn_extkey_rsa_finish(RSA *rsa) |
1043 | 1043 |
return 1; |
1044 | 1044 |
} |
1045 | 1045 |
|
1046 |
-/* sign arbitrary data */ |
|
1046 |
+/* Pass the input hash in 'dgst' to management and get the signature back. |
|
1047 |
+ * On input siglen contains the capacity of the buffer 'sig'. |
|
1048 |
+ * On return signature is in sig. |
|
1049 |
+ * Return value is signature length or -1 on error. |
|
1050 |
+ */ |
|
1047 | 1051 |
static int |
1048 |
-rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) |
|
1052 |
+get_sig_from_man(const unsigned char *dgst, unsigned int dgstlen, |
|
1053 |
+ unsigned char *sig, unsigned int siglen) |
|
1049 | 1054 |
{ |
1050 |
- /* optional app data in rsa->meth->app_data; */ |
|
1051 | 1055 |
char *in_b64 = NULL; |
1052 | 1056 |
char *out_b64 = NULL; |
1053 |
- int ret = -1; |
|
1054 |
- int len; |
|
1057 |
+ int len = -1; |
|
1055 | 1058 |
|
1056 |
- if (padding != RSA_PKCS1_PADDING) |
|
1057 |
- { |
|
1058 |
- RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
|
1059 |
- goto done; |
|
1060 |
- } |
|
1061 |
- |
|
1062 |
- /* convert 'from' to base64 */ |
|
1063 |
- if (openvpn_base64_encode(from, flen, &in_b64) <= 0) |
|
1064 |
- { |
|
1065 |
- goto done; |
|
1066 |
- } |
|
1067 |
- |
|
1068 |
- /* call MI for signature */ |
|
1069 |
- if (management) |
|
1059 |
+ /* convert 'dgst' to base64 */ |
|
1060 |
+ if (management |
|
1061 |
+ && openvpn_base64_encode(dgst, dgstlen, &in_b64) > 0) |
|
1070 | 1062 |
{ |
1071 | 1063 |
out_b64 = management_query_pk_sig(management, in_b64); |
1072 | 1064 |
} |
1073 |
- if (!out_b64) |
|
1065 |
+ if (out_b64) |
|
1074 | 1066 |
{ |
1075 |
- goto done; |
|
1067 |
+ len = openvpn_base64_decode(out_b64, sig, siglen); |
|
1076 | 1068 |
} |
1077 | 1069 |
|
1078 |
- /* decode base64 signature to binary */ |
|
1079 |
- len = RSA_size(rsa); |
|
1080 |
- ret = openvpn_base64_decode(out_b64, to, len); |
|
1070 |
+ free(in_b64); |
|
1071 |
+ free(out_b64); |
|
1072 |
+ return len; |
|
1073 |
+} |
|
1081 | 1074 |
|
1082 |
- /* verify length */ |
|
1083 |
- if (ret != len) |
|
1084 |
- { |
|
1085 |
- ret = -1; |
|
1086 |
- } |
|
1075 |
+/* sign arbitrary data */ |
|
1076 |
+static int |
|
1077 |
+rsa_priv_enc(int flen, const unsigned char *from, unsigned char *to, RSA *rsa, int padding) |
|
1078 |
+{ |
|
1079 |
+ unsigned int len = RSA_size(rsa); |
|
1080 |
+ int ret = -1; |
|
1087 | 1081 |
|
1088 |
-done: |
|
1089 |
- if (in_b64) |
|
1090 |
- { |
|
1091 |
- free(in_b64); |
|
1092 |
- } |
|
1093 |
- if (out_b64) |
|
1082 |
+ if (padding != RSA_PKCS1_PADDING) |
|
1094 | 1083 |
{ |
1095 |
- free(out_b64); |
|
1084 |
+ RSAerr(RSA_F_RSA_OSSL_PRIVATE_ENCRYPT, RSA_R_UNKNOWN_PADDING_TYPE); |
|
1085 |
+ return -1; |
|
1096 | 1086 |
} |
1097 |
- return ret; |
|
1087 |
+ |
|
1088 |
+ ret = get_sig_from_man(from, flen, to, len); |
|
1089 |
+ |
|
1090 |
+ return (ret == len)? ret : -1; |
|
1098 | 1091 |
} |
1099 | 1092 |
|
1100 | 1093 |
static int |
... | ... |
@@ -1166,6 +1159,130 @@ err: |
1166 | 1166 |
return 0; |
1167 | 1167 |
} |
1168 | 1168 |
|
1169 |
+#if OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_EC) |
|
1170 |
+ |
|
1171 |
+/* called when EC_KEY is destroyed */ |
|
1172 |
+static void |
|
1173 |
+openvpn_extkey_ec_finish(EC_KEY *ec) |
|
1174 |
+{ |
|
1175 |
+ /* release the method structure */ |
|
1176 |
+ const EC_KEY_METHOD *ec_meth = EC_KEY_get_method(ec); |
|
1177 |
+ EC_KEY_METHOD_free((EC_KEY_METHOD *) ec_meth); |
|
1178 |
+} |
|
1179 |
+ |
|
1180 |
+/* EC_KEY_METHOD callback: sign(). |
|
1181 |
+ * Sign the hash using EC key and return DER encoded signature in sig, |
|
1182 |
+ * its length in siglen. Return value is 1 on success, 0 on error. |
|
1183 |
+ */ |
|
1184 |
+static int |
|
1185 |
+ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, |
|
1186 |
+ unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) |
|
1187 |
+{ |
|
1188 |
+ int capacity = ECDSA_size(ec); |
|
1189 |
+ int len = get_sig_from_man(dgst, dgstlen, sig, capacity); |
|
1190 |
+ |
|
1191 |
+ if (len > 0) |
|
1192 |
+ { |
|
1193 |
+ *siglen = len; |
|
1194 |
+ return 1; |
|
1195 |
+ } |
|
1196 |
+ return 0; |
|
1197 |
+} |
|
1198 |
+ |
|
1199 |
+/* EC_KEY_METHOD callback: sign_setup(). We do no precomputations */ |
|
1200 |
+static int |
|
1201 |
+ecdsa_sign_setup(EC_KEY *ec, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) |
|
1202 |
+{ |
|
1203 |
+ return 1; |
|
1204 |
+} |
|
1205 |
+ |
|
1206 |
+/* EC_KEY_METHOD callback: sign_sig(). |
|
1207 |
+ * Sign the hash and return the result as a newly allocated ECDS_SIG |
|
1208 |
+ * struct or NULL on error. |
|
1209 |
+ */ |
|
1210 |
+static ECDSA_SIG * |
|
1211 |
+ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, const BIGNUM *in_kinv, |
|
1212 |
+ const BIGNUM *in_r, EC_KEY *ec) |
|
1213 |
+{ |
|
1214 |
+ ECDSA_SIG *ecsig = NULL; |
|
1215 |
+ unsigned int len = ECDSA_size(ec); |
|
1216 |
+ struct gc_arena gc = gc_new(); |
|
1217 |
+ |
|
1218 |
+ unsigned char *buf = gc_malloc(len, false, &gc); |
|
1219 |
+ if (ecdsa_sign(0, dgst, dgstlen, buf, &len, NULL, NULL, ec) != 1) |
|
1220 |
+ { |
|
1221 |
+ goto out; |
|
1222 |
+ } |
|
1223 |
+ /* const char ** should be avoided: not up to us, so we cast our way through */ |
|
1224 |
+ ecsig = d2i_ECDSA_SIG(NULL, (const unsigned char **)&buf, len); |
|
1225 |
+ |
|
1226 |
+out: |
|
1227 |
+ gc_free(&gc); |
|
1228 |
+ return ecsig; |
|
1229 |
+} |
|
1230 |
+ |
|
1231 |
+static int |
|
1232 |
+tls_ctx_use_external_ec_key(struct tls_root_ctx *ctx, EVP_PKEY *pkey) |
|
1233 |
+{ |
|
1234 |
+ EC_KEY *ec = NULL; |
|
1235 |
+ EVP_PKEY *privkey = NULL; |
|
1236 |
+ EC_KEY_METHOD *ec_method; |
|
1237 |
+ |
|
1238 |
+ ASSERT(ctx); |
|
1239 |
+ |
|
1240 |
+ ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); |
|
1241 |
+ if (!ec_method) |
|
1242 |
+ { |
|
1243 |
+ goto err; |
|
1244 |
+ } |
|
1245 |
+ |
|
1246 |
+ /* Among init methods, we only need the finish method */ |
|
1247 |
+ EC_KEY_METHOD_set_init(ec_method, NULL, openvpn_extkey_ec_finish, NULL, NULL, NULL, NULL); |
|
1248 |
+ EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); |
|
1249 |
+ |
|
1250 |
+ ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); |
|
1251 |
+ if (!ec) |
|
1252 |
+ { |
|
1253 |
+ EC_KEY_METHOD_free(ec_method); |
|
1254 |
+ goto err; |
|
1255 |
+ } |
|
1256 |
+ if (!EC_KEY_set_method(ec, ec_method)) |
|
1257 |
+ { |
|
1258 |
+ EC_KEY_METHOD_free(ec_method); |
|
1259 |
+ goto err; |
|
1260 |
+ } |
|
1261 |
+ /* from this point ec_method will get freed when ec is freed */ |
|
1262 |
+ |
|
1263 |
+ privkey = EVP_PKEY_new(); |
|
1264 |
+ if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) |
|
1265 |
+ { |
|
1266 |
+ goto err; |
|
1267 |
+ } |
|
1268 |
+ /* from this point ec will get freed when privkey is freed */ |
|
1269 |
+ |
|
1270 |
+ if (!SSL_CTX_use_PrivateKey(ctx->ctx, privkey)) |
|
1271 |
+ { |
|
1272 |
+ ec = NULL; /* avoid double freeing it below */ |
|
1273 |
+ goto err; |
|
1274 |
+ } |
|
1275 |
+ |
|
1276 |
+ EVP_PKEY_free(privkey); /* this will down ref privkey and ec */ |
|
1277 |
+ return 1; |
|
1278 |
+ |
|
1279 |
+err: |
|
1280 |
+ /* Reach here only when ec and privkey can be independenly freed */ |
|
1281 |
+ if (privkey) |
|
1282 |
+ { |
|
1283 |
+ EVP_PKEY_free(privkey); |
|
1284 |
+ } |
|
1285 |
+ if(ec) |
|
1286 |
+ { |
|
1287 |
+ EC_KEY_free(ec); |
|
1288 |
+ } |
|
1289 |
+ return 0; |
|
1290 |
+} |
|
1291 |
+#endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev */ |
|
1292 |
+ |
|
1169 | 1293 |
int |
1170 | 1294 |
tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, |
1171 | 1295 |
const char *cert_file, const char *cert_file_inline) |
... | ... |
@@ -1183,18 +1300,33 @@ tls_ctx_use_external_private_key(struct tls_root_ctx *ctx, |
1183 | 1183 |
ASSERT(pkey); /* NULL before SSL_CTX_use_certificate() is called */ |
1184 | 1184 |
X509_free(cert); |
1185 | 1185 |
|
1186 |
- if (EVP_PKEY_get0_RSA(pkey)) |
|
1186 |
+ if (EVP_PKEY_id(pkey) == EVP_PKEY_RSA) |
|
1187 | 1187 |
{ |
1188 | 1188 |
if (!tls_ctx_use_external_rsa_key(ctx, pkey)) |
1189 | 1189 |
{ |
1190 | 1190 |
goto err; |
1191 | 1191 |
} |
1192 | 1192 |
} |
1193 |
+#if OPENSSL_VERSION_NUMBER > 0x10100000L && !defined(OPENSSL_NO_EC) |
|
1194 |
+ else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) |
|
1195 |
+ { |
|
1196 |
+ if (!tls_ctx_use_external_ec_key(ctx, pkey)) |
|
1197 |
+ { |
|
1198 |
+ goto err; |
|
1199 |
+ } |
|
1200 |
+ } |
|
1201 |
+ else |
|
1202 |
+ { |
|
1203 |
+ crypto_msg(M_WARN, "management-external-key requires an RSA or EC certificate"); |
|
1204 |
+ goto err; |
|
1205 |
+ } |
|
1206 |
+#else |
|
1193 | 1207 |
else |
1194 | 1208 |
{ |
1195 |
- crypto_msg(M_WARN, "management-external-key requires a RSA certificate"); |
|
1209 |
+ crypto_msg(M_WARN, "management-external-key requires an RSA certificate"); |
|
1196 | 1210 |
goto err; |
1197 | 1211 |
} |
1212 |
+#endif /* OPENSSL_VERSION_NUMBER > 1.1.0 dev */ |
|
1198 | 1213 |
return 1; |
1199 | 1214 |
|
1200 | 1215 |
err: |