Am 07.12.18 um 20:17 schrieb selva.n...@gmail.com:
> From: Selva Nair <selva.n...@gmail.com>
> 
> For PSS padding, CNG requires the digest to be signed
> and the digest algorithm in use, which are not accessible
> via the rsa_sign and rsa_priv_enc callbacks of OpenSSL.
> This patch uses the EVP_KEY interface to hook to
> evp_pkey_sign callback if OpenSSL version is > 1.1.0.
> 
> To test this code path, both the server and client should
> be built with OpenSSL 1.1.1 and use TLS version >= 1.2
> 
> Tested on Windows 7 client against a Linux server.

Tested on Windows 10 1809 against a Linux server.

>  
>  #include <openssl/ssl.h>
> +#include <openssl/evp.h>
>  #include <openssl/err.h>
>  #include <windows.h>
>  #include <wincrypt.h>
> @@ -105,6 +106,12 @@ static ERR_STRING_DATA CRYPTOAPI_str_functs[] = {
>  /* index for storing external data in EC_KEY: < 0 means uninitialized */
>  static int ec_data_idx = -1;
>  
> +/* Global EVP_PKEY_METHOD used to override the sign operation */
> +static EVP_PKEY_METHOD *pmethod;
> +static int (*default_pkey_sign_init) (EVP_PKEY_CTX *ctx);
> +static int (*default_pkey_sign) (EVP_PKEY_CTX *ctx, unsigned char *sig,
> +                size_t *siglen, const unsigned char *tbs, size_t tbslen);
> +
>  typedef struct _CAPI_DATA {
>      const CERT_CONTEXT *cert_context;
>      HCRYPTPROV_OR_NCRYPT_KEY_HANDLE crypt_prov;
> @@ -318,28 +325,44 @@ rsa_pub_dec(int flen, const unsigned char *from, 
> unsigned char *to, RSA *rsa, in
>   * Sign the hash in 'from' using NCryptSignHash(). This requires an NCRYPT
>   * key handle in cd->crypt_prov. On return the signature is in 'to'. Returns
>   * the length of the signature or 0 on error.
> + * This is used only for RSA and padding should be BCRYPT_PAD_PKCS1 or
> + * BCRYPT_PAD_PSS.
>   * If the hash_algo is not NULL, PKCS #1 DigestInfo header gets added
> - * to 'from', else it is signed as is.
> - * For now we support only RSA and the padding is assumed to be PKCS1 v1.5
> + * to |from|, else it is signed as is. Use NULL for MD5 + SHA1 hash used
> + * in TLS 1.1 and earlier.
> + * In case of PSS padding, |saltlen| should specify the size of salt to use.
> + * If |to| is NULL returns the required buffer size.
>   */

If you modify the comments anyway, you might consider making them
documentation of the functions (start with /**). This is true for all
multiline comment and I don't comment further on it.


>  
>      /* Unfortunately, there is no "CryptSign()" function in CryptoAPI, that 
> would

Not 100% sure but I think for multiline comments the first line should
be just /* (empty)

> @@ -444,6 +469,7 @@ rsa_priv_enc(int flen, const unsigned char *from, 
> unsigned char *to, RSA *rsa, i
>   * NCryptSignHash() is used to sign and it is instructed to add the
>   * the PKCS #1 DigestInfo header to |m| unless the hash algorithm is
>   * the MD5/SHA1 combination used in TLS 1.1 and earlier versions.
> + * OpenSSL exercises this callback only when padding is PKCS1 v1.5.
>   */

> +
> +    /* We intercept all sign requests, not just the one's for our key.
> +     * Check the key and call the saved OpenSSL method for unknown keys.
> +     */
> +    if (!pkey || !cd)
> +    {
> +        if (default_pkey_sign)
> +        {
> +           return default_pkey_sign(ctx, sig, siglen, tbs, tbslen);
> +        }
> +        else  /* This should not happen */
> +        {
> +            msg(M_FATAL, "cryptopaicert: Unknown key and no default sign 
> operation to fallback on");
> +            return -1;
> +        }
> +    }


> +        msg(M_NONFATAL, "cryptoapicert: could not determine the signature 
> digest algorithm");
> +        RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_UNKNOWN_ALGORITHM_TYPE);
> +        return -1;
> +    }
> +
> +    if (tbslen != (size_t) hashlen)

no space after cast

> +    {
> +        RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_INVALID_DIGEST_LENGTH);
> +        return -1;
> +    }
> +

> +
> +        if (saltlen < 0)
> +        {
> +            RSAerr(RSA_F_PKEY_RSA_SIGN, RSA_R_DATA_TOO_LARGE_FOR_KEY_SIZE);
> +            return -1;
> +        }
> +        msg(D_LOW, "cryptoapicert: PSS padding using saltlen = %d", saltlen);
> +        EVP_MD_size(md);

This call here should not have side effect (unless md is nullptr) and
the result is not used.

> +    }
> +
> +    msg(D_LOW, "cryptoapicert: calling priv_enc_CNG with alg = %ls", alg);
> +    *siglen = priv_enc_CNG(cd, alg, tbs, (int) tbslen, sig, *siglen,
> +                           cng_padding_type(padding), (DWORD) saltlen);
> +

Inconsistent casting, we should always use (cast)variable without space
I am told.


> +            /* Keep a copy of the default sign and sign_init methods */
> +
> +#if (OPENSSL_VERSION_NUMBER < 0x1010009fL)   /* > version 1.1.0i */
> +            /* The function signature is not const-correct in these versions 
> */
> +            EVP_PKEY_meth_get_sign((EVP_PKEY_METHOD*)default_pmethod, 
> &default_pkey_sign_init,

Style dictates a (EVP_PKEY_METHOD *)

Overall the code looks good. The overriding of the global RSA method is
a bit of a hack but I also do not have any better solution for this. It
might break using OpenSSL engines but that is a corner case that I would
not worry about here.

Apart from the minor issues this gets an ACK from me.

Arne

Attachment: signature.asc
Description: OpenPGP digital signature

_______________________________________________
Openvpn-devel mailing list
Openvpn-devel@lists.sourceforge.net
https://lists.sourceforge.net/lists/listinfo/openvpn-devel

Reply via email to