mirror of
https://github.com/OpenVPN/openvpn.git
synced 2026-05-28 04:03:29 -04:00
Add a function to encode digests with PKCS1 DigestInfo wrapper
The EVP_PKEY interface as well as provider passes the raw digest to the sign() function. In case of RSA_PKCS1, our management interface expects an encoded hash, which has the DigestInfo header added as per PKCSv1.5 specs, unless the hash algorithm is legacy MD5_SHA1. Fix this by - add a function to perform the pkcs1 encoding before passing the data to sign to the management interface. The implementation is not pretty, but should work. (Unfortunately OpenSSL does not expose a function for this). Note: 1. cryptoki interface used by pkcs11-helper also requires this to be done before calling the Sign op. This will come handy there too. 2. We have a similar function in ssl_mbedtls.c but its not prettier, and require porting. v2 changes: Use hard-coded headers for known hash algorithms instead of assembling it from the ASN.1 objects. Signed-off-by: Selva Nair <selva.nair@gmail.com> Acked-by: Arne Schwabe <arne@rfc2549.org> Message-Id: <20211214165928.30676-9-selva.nair@gmail.com> URL: https://www.mail-archive.com/openvpn-devel@lists.sourceforge.net/msg23433.html Signed-off-by: Gert Doering <gert@greenie.muc.de>
This commit is contained in:
parent
199df03bf5
commit
cf704eef47
2 changed files with 150 additions and 0 deletions
|
|
@ -96,6 +96,26 @@ typedef void (XKEY_PRIVKEY_FREE_fn)(void *handle);
|
|||
*/
|
||||
EVP_PKEY *xkey_load_management_key(OSSL_LIB_CTX *libctx, EVP_PKEY *pubkey);
|
||||
|
||||
/**
|
||||
* Add PKCS1 DigestInfo to tbs and return the result in *enc.
|
||||
*
|
||||
* @param enc pointer to output buffer
|
||||
* @param enc_len capacity in bytes of output buffer
|
||||
* @param mdname name of the hash algorithm (SHA256, SHA1 etc.)
|
||||
* @param tbs pointer to digest to be encoded
|
||||
* @param tbslen length of data in bytes
|
||||
*
|
||||
* @return false on error, true on success
|
||||
*
|
||||
* On return enc_len is set to actual size of the result.
|
||||
* enc is NULL or enc_len is not enough to store the result, it is set
|
||||
* to the required size and false is returned.
|
||||
*
|
||||
*/
|
||||
bool
|
||||
encode_pkcs1(unsigned char *enc, size_t *enc_len, const char *mdname,
|
||||
const unsigned char *tbs, size_t tbslen);
|
||||
|
||||
#endif /* HAVE_XKEY_PROVIDER */
|
||||
|
||||
#endif /* XKEY_COMMON_H_ */
|
||||
|
|
|
|||
|
|
@ -143,6 +143,9 @@ xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen,
|
|||
unsigned char buf[EVP_MAX_MD_SIZE]; /* for computing digest if required */
|
||||
size_t buflen = sizeof(buf);
|
||||
|
||||
unsigned char enc[EVP_MAX_MD_SIZE + 32]; /* 32 bytes enough for digest inf structure */
|
||||
size_t enc_len = sizeof(enc);
|
||||
|
||||
if (!strcmp(alg.op, "DigestSign"))
|
||||
{
|
||||
dmsg(D_LOW, "xkey_management_sign: computing digest");
|
||||
|
|
@ -165,6 +168,14 @@ xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen,
|
|||
/* else assume RSA key */
|
||||
else if (!strcmp(alg.padmode, "pkcs1"))
|
||||
{
|
||||
/* management interface expects a pkcs1 encoded digest -- add it */
|
||||
if (!encode_pkcs1(enc, &enc_len, alg.mdname, tbs, tbslen))
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
tbs = enc;
|
||||
tbslen = enc_len;
|
||||
|
||||
strncpynt(alg_str, "RSA_PKCS1_PADDING", sizeof(alg_str));
|
||||
}
|
||||
else if (!strcmp(alg.padmode, "none"))
|
||||
|
|
@ -205,4 +216,123 @@ xkey_management_sign(void *unused, unsigned char *sig, size_t *siglen,
|
|||
return (*siglen > 0);
|
||||
}
|
||||
|
||||
/**
|
||||
* Add PKCS1 DigestInfo to tbs and return the result in *enc.
|
||||
*
|
||||
* @param enc pointer to output buffer
|
||||
* @param enc_len capacity in bytes of output buffer
|
||||
* @param mdname name of the hash algorithm (SHA256, SHA1 etc.)
|
||||
* @param tbs pointer to digest to be encoded
|
||||
* @param tbslen length of data in bytes
|
||||
*
|
||||
* @return false on error, true on success
|
||||
*
|
||||
* On return enc_len is set to actual size of the result.
|
||||
* enc is NULL or enc_len is not enough to store the result, it is set
|
||||
* to the required size and false is returned.
|
||||
*/
|
||||
bool
|
||||
encode_pkcs1(unsigned char *enc, size_t *enc_len, const char *mdname,
|
||||
const unsigned char *tbs, size_t tbslen)
|
||||
{
|
||||
ASSERT(enc_len != NULL);
|
||||
ASSERT(tbs != NULL);
|
||||
|
||||
/* Tabulate the digest info header for expected hash algorithms
|
||||
* These were pre-computed using the DigestInfo definition:
|
||||
* DigestInfo ::= SEQUENCE {
|
||||
* digestAlgorithm DigestAlgorithmIdentifier,
|
||||
* digest Digest }
|
||||
* Also see the table in RFC 8017 section 9.2, Note 1.
|
||||
*/
|
||||
|
||||
const unsigned char sha1[] = {0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2b,
|
||||
0x0e, 0x03, 0x02, 0x1a, 0x05, 0x00, 0x04, 0x14};
|
||||
const unsigned char sha256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x01, 0x05, 0x00, 0x04, 0x20};
|
||||
const unsigned char sha384[] = {0x30, 0x41, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x02, 0x05, 0x00, 0x04, 0x30};
|
||||
const unsigned char sha512[] = {0x30, 0x51, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x03, 0x05, 0x00, 0x04, 0x40};
|
||||
const unsigned char sha224[] = {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x04, 0x05, 0x00, 0x04, 0x1c};
|
||||
const unsigned char sha512_224[] = {0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x05, 0x05, 0x00, 0x04, 0x1c};
|
||||
const unsigned char sha512_256[] = {0x30, 0x31, 0x30, 0x0d, 0x06, 0x09, 0x60, 0x86, 0x48,
|
||||
0x01, 0x65, 0x03, 0x04, 0x02, 0x06, 0x05, 0x00, 0x04, 0x20};
|
||||
|
||||
typedef struct {
|
||||
const int nid;
|
||||
const unsigned char *header;
|
||||
size_t sz;
|
||||
} DIG_INFO;
|
||||
|
||||
#define MAKE_DI(x) {NID_##x, x, sizeof(x)}
|
||||
|
||||
DIG_INFO dinfo[] = {MAKE_DI(sha1), MAKE_DI(sha256), MAKE_DI(sha384),
|
||||
MAKE_DI(sha512), MAKE_DI(sha224), MAKE_DI(sha512_224),
|
||||
MAKE_DI(sha512_256), {0,NULL,0}};
|
||||
|
||||
int out_len = 0;
|
||||
int ret = 0;
|
||||
|
||||
int nid = OBJ_sn2nid(mdname);
|
||||
if (nid == NID_undef)
|
||||
{
|
||||
/* try harder -- name variants like SHA2-256 doesn't work */
|
||||
nid = EVP_MD_type(EVP_get_digestbyname(mdname));
|
||||
if (nid == NID_undef)
|
||||
{
|
||||
msg(M_WARN, "Error: encode_pkcs11: invalid digest name <%s>", mdname);
|
||||
goto done;
|
||||
}
|
||||
}
|
||||
|
||||
if (tbslen != EVP_MD_size(EVP_get_digestbyname(mdname)))
|
||||
{
|
||||
msg(M_WARN, "Error: encode_pkcs11: invalid input length <%d>", (int)tbslen);
|
||||
goto done;
|
||||
}
|
||||
|
||||
if (nid == NID_md5_sha1) /* no encoding needed -- just copy */
|
||||
{
|
||||
if (enc && (*enc_len >= tbslen))
|
||||
{
|
||||
memcpy(enc, tbs, tbslen);
|
||||
ret = true;
|
||||
}
|
||||
out_len = tbslen;
|
||||
goto done;
|
||||
}
|
||||
|
||||
/* locate entry for nid in dinfo table */
|
||||
DIG_INFO *di = dinfo;
|
||||
while ((di->nid != nid) && (di->nid != 0))
|
||||
{
|
||||
di++;
|
||||
}
|
||||
if (di->nid != nid) /* not found in our table */
|
||||
{
|
||||
msg(M_WARN, "Error: encode_pkcs11: unsupported hash algorithm <%s>", mdname);
|
||||
goto done;
|
||||
}
|
||||
|
||||
out_len = tbslen + di->sz;
|
||||
|
||||
if (enc && (out_len <= (int) *enc_len))
|
||||
{
|
||||
/* combine header and digest */
|
||||
memcpy(enc, di->header, di->sz);
|
||||
memcpy(enc + di->sz, tbs, tbslen);
|
||||
dmsg(D_LOW, "encode_pkcs1: digest length = %d encoded length = %d",
|
||||
(int) tbslen, (int) out_len);
|
||||
ret = true;
|
||||
}
|
||||
|
||||
done:
|
||||
*enc_len = out_len; /* assignment safe as out_len is > 0 at this point */
|
||||
|
||||
return ret;
|
||||
}
|
||||
|
||||
#endif /* HAVE_XKEY_PROVIDER */
|
||||
|
|
|
|||
Loading…
Reference in a new issue