From patchwork Sat Feb 24 09:57:33 2018 Content-Type: text/plain; charset="utf-8" MIME-Version: 1.0 Content-Transfer-Encoding: 7bit X-Patchwork-Submitter: Selva Nair X-Patchwork-Id: 248 Return-Path: Delivered-To: patchwork@openvpn.net Delivered-To: patchwork@openvpn.net Received: from director9.mail.ord1d.rsapps.net ([172.30.191.6]) by backend30.mail.ord1d.rsapps.net (Dovecot) with LMTP id A2ziARDSkVocQgAAIUCqbw for ; Sat, 24 Feb 2018 15:58:56 -0500 Received: from proxy12.mail.ord1d.rsapps.net ([172.30.191.6]) by director9.mail.ord1d.rsapps.net (Dovecot) with LMTP id 0wPLARDSkVqkdgAAalYnBA ; Sat, 24 Feb 2018 15:58:56 -0500 Received: from smtp38.gate.ord1d ([172.30.191.6]) (using TLSv1.2 with cipher ECDHE-RSA-AES256-GCM-SHA384 (256/256 bits)) by proxy12.mail.ord1d.rsapps.net (Dovecot) with LMTP id mk5QABDSkVrzMwAA7PHxkg ; Sat, 24 Feb 2018 15:58:56 -0500 X-Spam-Threshold: 95 X-Spam-Score: 0 X-Spam-Flag: NO X-Virus-Scanned: OK X-Orig-To: openvpnslackdevel@openvpn.net X-Originating-Ip: [216.105.38.7] Authentication-Results: smtp38.gate.ord1d.rsapps.net; iprev=pass policy.iprev="216.105.38.7"; spf=pass smtp.mailfrom="openvpn-devel-bounces@lists.sourceforge.net" smtp.helo="lists.sourceforge.net"; dkim=fail (signature verification failed) header.d=sourceforge.net; dkim=fail (signature verification failed) header.d=sf.net; dkim=fail (signature verification failed) header.d=gmail.com; dmarc=fail (p=none; dis=none) header.from=gmail.com X-Classification-ID: 86764e52-19a5-11e8-ae7f-525400f6a58b-1-1 Received: from [216.105.38.7] ([216.105.38.7:32990] helo=lists.sourceforge.net) by smtp38.gate.ord1d.rsapps.net (envelope-from ) (ecelerity 4.2.1.56364 r(Core:4.2.1.14)) with ESMTPS (cipher=DHE-RSA-AES256-GCM-SHA384) id D4/84-05434-F02D19A5; Sat, 24 Feb 2018 15:58:55 -0500 Received: from [127.0.0.1] (helo=sfs-ml-2.v29.lw.sourceforge.com) by sfs-ml-2.v29.lw.sourceforge.com with esmtp (Exim 4.89) (envelope-from ) id 1epgtK-000jIl-Eu; Sat, 24 Feb 2018 20:57:58 +0000 Received: from [172.30.20.202] (helo=mx.sourceforge.net) by sfs-ml-2.v29.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES256-GCM-SHA384:256) (Exim 4.89) (envelope-from ) id 1epgtK-000jIc-0u for openvpn-devel@lists.sourceforge.net; Sat, 24 Feb 2018 20:57:58 +0000 DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sourceforge.net; s=x; h=References:In-Reply-To:Message-Id:Date:Subject:Cc: To:From:Sender:Reply-To:MIME-Version:Content-Type:Content-Transfer-Encoding: Content-ID:Content-Description:Resent-Date:Resent-From:Resent-Sender: Resent-To:Resent-Cc:Resent-Message-ID:List-Id:List-Help:List-Unsubscribe: List-Subscribe:List-Post:List-Owner:List-Archive; bh=qLWd8lVj2P2+38RnXfQSKUKm0Qk5xPPORy94mBUhrLg=; b=NL4GBtb5aUMUOT2UT4tLJZQPYE +SaHHOWYUHRFsitVihf8JZeD4xMZk/D7KTxiKTYJRXSB0NXSbB/SW5dfn4kdm+q8BW3Dhu00EBrAx hV7tjdhVKHgMe0NQbR06rJ+yO/AhdPsaoey69ei1X6LJHSK/vDkLHd48UXXztqdrFr7s=; DKIM-Signature: v=1; a=rsa-sha256; q=dns/txt; c=relaxed/relaxed; d=sf.net; s=x ; h=References:In-Reply-To:Message-Id:Date:Subject:Cc:To:From:Sender:Reply-To :MIME-Version:Content-Type:Content-Transfer-Encoding:Content-ID: Content-Description:Resent-Date:Resent-From:Resent-Sender:Resent-To:Resent-Cc :Resent-Message-ID:List-Id:List-Help:List-Unsubscribe:List-Subscribe: List-Post:List-Owner:List-Archive; bh=qLWd8lVj2P2+38RnXfQSKUKm0Qk5xPPORy94mBUhrLg=; b=ITjXR6WkGJr0SXO3DZnmvsXDN3 8CUfe3fH4wlCw+ts3bvxplSnUgMqPUymnCA69oxW9pwUEDqGWcSlLokeBrG5BT9F4mgCbUQOWtDwH 98IX0qw4DpEw259ELH3pBW/Lm2rMHljE0JsVyrJJPRh3LmeBvCtSdeZVzxtZZwA8VtI8=; Received: from sfi-lb-mx.v20.lw.sourceforge.com ([172.30.20.201] helo=mail-it0-f51.google.com) by sfi-mx-2.v28.lw.sourceforge.com with esmtps (TLSv1.2:ECDHE-RSA-AES128-GCM-SHA256:128) (Exim 4.89) id 1epgtI-00136l-5K for openvpn-devel@lists.sourceforge.net; Sat, 24 Feb 2018 20:57:57 +0000 Received: by mail-it0-f51.google.com with SMTP id l187so6954630ith.4 for ; Sat, 24 Feb 2018 12:57:56 -0800 (PST) DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=gmail.com; s=20161025; h=from:to:cc:subject:date:message-id:in-reply-to:references; bh=qLWd8lVj2P2+38RnXfQSKUKm0Qk5xPPORy94mBUhrLg=; b=SST0eJ0X1ZIWUzGLMOpoaGPDcOeG2TJ5SWA7AWnX7twkxRRpx5aZIE7BXE6rrJg8E/ r4QKlrJAfmjzHYr8f1t3CC4ok8wQUltQPP43UkssUz1PeF9ryHUYfnfcuo0W3Gj3cLtg VI05cJ+RbXIbIoZrUeNDd96xfJ2OP3k4ta6wFy7DU8OlOp1ZRa9UNM4Eta9nu3JtESe0 H+Z5Dqt4K1eRUWhIcAST1fMSJqAa0Fp97lUC5469E7SRsb2Lma2VnGusgZdc9m9Z6thQ SycwioChVBvyyZEfGwjpDR+n8gkmzc+T3+nPMlaq6WQbSsseikUafA4OD3GCQnG7lnon Xe9g== X-Google-DKIM-Signature: v=1; a=rsa-sha256; c=relaxed/relaxed; d=1e100.net; s=20161025; h=x-gm-message-state:from:to:cc:subject:date:message-id:in-reply-to :references; bh=qLWd8lVj2P2+38RnXfQSKUKm0Qk5xPPORy94mBUhrLg=; b=H/O0uexp9psQ0nPn3XXySkjkL6oZSPmcfBfRK9sorZhKgTjorZrcfphfFtLd0N8UTS AMNqcMaYhQHBoL1UquoYeKQdn+JM34RTrylIFqbzJ625Pqs/+GJHsv2W4wTEqj0OoREp jDyoVV/VzygdKZWZRn5z/ZK5GfO1rOVrcMGDFTu4V59lwQkdh8Rysw973dBbnRkeINDP ouaYNzJriNdttWn7NvBfj5foFbZaNRtVknOZTSYeQhIbDw+z3rPxqw2zuRZqKuYrNnZa XWvGFTk0D8dzWTbpup42ofQXV3H02BDIqeBVgOBr9BZeOTgmMpiR2HQMRPu5J2+sq0j/ yAdQ== X-Gm-Message-State: APf1xPDj7hDKrMIDodemiv3BaeFaBhvgawhkLrSYlmPUkdZN9X2dsd8k QD1BnOA1g/j4/dlEZBtcqdNMNeVg X-Google-Smtp-Source: AH8x2272tNu2P+MB2ckW+FXQorqIcGeVkqXBeq1bCwLv8Unitkm/md3UXTFoPGGt0ELvE1rbtfOrzw== X-Received: by 10.36.249.203 with SMTP id l194mr7307641ith.81.1519505870200; Sat, 24 Feb 2018 12:57:50 -0800 (PST) Received: from saturn.home.sansel.ca (CPE40167ea0e1c2-CM788df74daaa0.cpe.net.cable.rogers.com. [99.228.215.92]) by smtp.gmail.com with ESMTPSA id 7sm3424311ioo.12.2018.02.24.12.57.49 (version=TLS1_2 cipher=ECDHE-RSA-AES128-SHA bits=128/128); Sat, 24 Feb 2018 12:57:49 -0800 (PST) From: selva.nair@gmail.com To: openvpn-devel@lists.sourceforge.net Date: Sat, 24 Feb 2018 15:57:33 -0500 Message-Id: <1519505853-11340-1-git-send-email-selva.nair@gmail.com> X-Mailer: git-send-email 2.1.4 In-Reply-To: <9d49e636-9208-772e-7669-1bbe98a440c4@karger.me> References: <9d49e636-9208-772e-7669-1bbe98a440c4@karger.me> X-Spam-Report: Spam Filtering performed by mx.sourceforge.net. See http://spamassassin.org/tag/ for more details. 0.0 FREEMAIL_FROM Sender email is commonly abused enduser mail provider (selva.nair[at]gmail.com) 1.3 RCVD_IN_RP_RNBL RBL: Relay in RNBL, https://senderscore.org/blacklistlookup/ [99.228.215.92 listed in bl.score.senderscore.com] 3.6 RCVD_IN_PBL RBL: Received via a relay in Spamhaus PBL [99.228.215.92 listed in zen.spamhaus.org] 1.0 SPF_SOFTFAIL SPF: sender does not match SPF record (softfail) -0.1 DKIM_VALID_AU Message has a valid DKIM or DK signature from author's domain 0.1 DKIM_SIGNED Message has a DKIM or DK signature, not necessarily valid -0.1 DKIM_VALID Message has at least one valid DKIM or DK signature -1.8 AWL AWL: Adjusted score from AWL reputation of From: address X-Headers-End: 1epgtI-00136l-5K Subject: [Openvpn-devel] [PATCH v3 3/3] Support EC certificates with cryptoapicert X-BeenThere: openvpn-devel@lists.sourceforge.net X-Mailman-Version: 2.1.21 Precedence: list List-Id: List-Unsubscribe: , List-Archive: List-Post: List-Help: List-Subscribe: , MIME-Version: 1.0 Errors-To: openvpn-devel-bounces@lists.sourceforge.net X-getmail-retrieved-from-mailbox: Inbox From: Selva Nair Requires openssl 1.1.0 or higher Signed-off-by: Selva Nair --- v3 changes: - check return value of ECDSA_SIG_set0 - ensure buffer size needed by i2d_ECDSA_SIG does not exceed the expected capacity of the sig buffer - Fix a typo and add contextual info to a debug message - Remove an superflous cast leftover from an older version src/openvpn/cryptoapi.c | 208 +++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 207 insertions(+), 1 deletion(-) diff --git a/src/openvpn/cryptoapi.c b/src/openvpn/cryptoapi.c index 1097286..d086631 100644 --- a/src/openvpn/cryptoapi.c +++ b/src/openvpn/cryptoapi.c @@ -1,5 +1,6 @@ /* * Copyright (c) 2004 Peter 'Luna' Runestig + * Copyright (c) 2018 Selva Nair * All rights reserved. * * Redistribution and use in source and binary forms, with or without modifi- @@ -101,6 +102,9 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = { { 0, NULL } }; +/* index for storing external data in EC_KEY: < 0 means uninitialized */ +static int ec_data_idx = -1; + typedef struct _CAPI_DATA { const CERT_CONTEXT *cert_context; HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov; @@ -394,6 +398,199 @@ finish(RSA *rsa) return 1; } +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC) + +static EC_KEY_METHOD *ec_method = NULL; + +/** EC_KEY_METHOD callback: called when the key is freed */ +static void +ec_finish(EC_KEY *ec) +{ + EC_KEY_METHOD_free(ec_method); + ec_method = NULL; + CAPI_DATA *cd = EC_KEY_get_ex_data(ec, ec_data_idx); + CAPI_DATA_free(cd); + EC_KEY_set_ex_data(ec, ec_data_idx, NULL); +} + +/** EC_KEY_METHOD callback sign_setup(): we do nothing here */ +static int +ecdsa_sign_setup(EC_KEY *eckey, BN_CTX *ctx_in, BIGNUM **kinvp, BIGNUM **rp) +{ + return 1; +} + +/** + * Helper to convert ECDSA signature returned by NCryptSignHash + * to an ECDSA_SIG structure. + * On entry 'buf[]' of length len contains r and s concatenated. + * Returns a newly allocated ECDSA_SIG or NULL (on error). + */ +static ECDSA_SIG * +ecdsa_bin2sig(unsigned char *buf, int len) +{ + ECDSA_SIG *ecsig = NULL; + DWORD rlen = len/2; + BIGNUM *r = BN_bin2bn(buf, rlen, NULL); + BIGNUM *s = BN_bin2bn(buf+rlen, rlen, NULL); + if (!r || !s) + { + goto err; + } + ecsig = ECDSA_SIG_new(); /* in openssl 1.1 this does not allocate r, s */ + if (!ecsig) + { + goto err; + } + if (!ECDSA_SIG_set0(ecsig, r, s)) /* ecsig takes ownership of r and s */ + { + goto err; + } + return ecsig; +err: + BN_free(r); /* it is ok to free NULL BN */ + BN_free(s); + return NULL; +} + +/** EC_KEY_METHOD callback sign_sig(): sign and return an ECDSA_SIG pointer. */ +static ECDSA_SIG * +ecdsa_sign_sig(const unsigned char *dgst, int dgstlen, + const BIGNUM *in_kinv, const BIGNUM *in_r, EC_KEY *ec) +{ + ECDSA_SIG *ecsig = NULL; + CAPI_DATA *cd = (CAPI_DATA *)EC_KEY_get_ex_data(ec, ec_data_idx); + + ASSERT(cd->key_spec == CERT_NCRYPT_KEY_SPEC); + + NCRYPT_KEY_HANDLE hkey = cd->crypt_prov; + BYTE buf[512]; /* large enough buffer for signature to avoid malloc */ + DWORD len = _countof(buf); + + msg(D_LOW, "Cryptoapi: signing hash using EC key: data size = %d", dgstlen); + + DWORD status = NCryptSignHash(hkey, NULL, (BYTE *)dgst, dgstlen, (BYTE *)buf, len, &len, 0); + if (status != ERROR_SUCCESS) + { + SetLastError(status); + CRYPTOAPIerr(CRYPTOAPI_F_NCRYPT_SIGN_HASH); + } + else + { + /* NCryptSignHash returns r, s concatenated in buf[] */ + ecsig = ecdsa_bin2sig(buf, len); + } + return ecsig; +} + +/** EC_KEY_METHOD callback sign(): sign and return a DER encoded signature */ +static int +ecdsa_sign(int type, const unsigned char *dgst, int dgstlen, unsigned char *sig, + unsigned int *siglen, const BIGNUM *kinv, const BIGNUM *r, EC_KEY *ec) +{ + ECDSA_SIG *s; + + *siglen = 0; + s = ecdsa_sign_sig(dgst, dgstlen, NULL, NULL, ec); + if (s == NULL) + { + return 0; + } + + /* convert internal signature structure 's' to DER encoded byte array in sig */ + int len = i2d_ECDSA_SIG(s, NULL); + if (len > ECDSA_size(ec)) + { + msg(M_NONFATAL,"Error: DER encoded ECDSA signature is too long (%d bytes)", len); + return 0; + } + *siglen = i2d_ECDSA_SIG(s, &sig); + ECDSA_SIG_free(s); + + return 1; +} + +static int +ssl_ctx_set_eckey(SSL_CTX *ssl_ctx, CAPI_DATA *cd, EVP_PKEY *pkey) +{ + EC_KEY *ec = NULL; + EVP_PKEY *privkey = NULL; + + if (cd->key_spec != CERT_NCRYPT_KEY_SPEC) + { + msg(M_NONFATAL, "ERROR: cryptoapicert with only legacy private key handle available." + " EC certificate not supported."); + goto err; + } + /* create a method struct with default callbacks filled in */ + ec_method = EC_KEY_METHOD_new(EC_KEY_OpenSSL()); + if (!ec_method) + { + goto err; + } + + /* We only need to set finish among init methods, and sign methods */ + EC_KEY_METHOD_set_init(ec_method, NULL, ec_finish, NULL, NULL, NULL, NULL); + EC_KEY_METHOD_set_sign(ec_method, ecdsa_sign, ecdsa_sign_setup, ecdsa_sign_sig); + + ec = EC_KEY_dup(EVP_PKEY_get0_EC_KEY(pkey)); + if (!ec) + { + goto err; + } + if (!EC_KEY_set_method(ec, ec_method)) + { + goto err; + } + + /* get an index to store cd as external data */ + if (ec_data_idx < 0) + { + ec_data_idx = EC_KEY_get_ex_new_index(0, "cryptapicert ec key", NULL, NULL, NULL); + if (ec_data_idx < 0) + { + goto err; + } + } + EC_KEY_set_ex_data(ec, ec_data_idx, cd); + + /* cd assigned to ec as ex_data, increase its refcount */ + cd->ref_count++; + + privkey = EVP_PKEY_new(); + if (!EVP_PKEY_assign_EC_KEY(privkey, ec)) + { + EC_KEY_free(ec); + goto err; + } + /* from here on ec will get freed with privkey */ + + if (!SSL_CTX_use_PrivateKey(ssl_ctx, privkey)) + { + goto err; + } + EVP_PKEY_free(privkey); /* this will dn_ref or free ec as well */ + return 1; + +err: + if (privkey) + { + EVP_PKEY_free(privkey); + } + else if (ec) + { + EC_KEY_free(ec); + } + if (ec_method) /* do always set ec_method = NULL after freeing it */ + { + EC_KEY_METHOD_free(ec_method); + ec_method = NULL; + } + return 0; +} + +#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */ + static const CERT_CONTEXT * find_certificate_in_store(const char *cert_prop, HCERTSTORE cert_store) { @@ -642,9 +839,18 @@ SSL_CTX_use_CryptoAPI_certificate(SSL_CTX *ssl_ctx, const char *cert_prop) goto err; } } +#if (OPENSSL_VERSION_NUMBER >= 0x10100000L) && !defined(OPENSSL_NO_EC) + else if (EVP_PKEY_id(pkey) == EVP_PKEY_EC) + { + if (!ssl_ctx_set_eckey(ssl_ctx, cd, pkey)) + { + goto err; + } + } +#endif /* OPENSSL_VERSION_NUMBER >= 1.1.0 */ else { - msg(M_WARN, "cryptoapicert requires an RSA certificate"); + msg(M_WARN, "WARNING: cryptoapicert: certificate type not supported"); goto err; } CAPI_DATA_free(cd); /* this will do a ref_count-- */