Skip to content

Commit db44f21

Browse files
authored
Add support functions for RSA OAEP, PSS and PKCS1 (#342)
* test RSA signatures with all support hash objects * implement SupportsRSAPKCS1Signature * add support functions * fix test * fix test * fix SupportsRSAOAEP * fix tests * optimize testRSAKey * copilot feedback * add more tests * fix tests
1 parent 52cddb0 commit db44f21

File tree

3 files changed

+661
-157
lines changed

3 files changed

+661
-157
lines changed

evp.go

Lines changed: 97 additions & 77 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,14 @@ func hashToMD(h hash.Hash) ossl.EVP_MD_PTR {
4848
return nil
4949
}
5050

51+
// hashToCryptoHash converts a hash.Hash implementation from this package to a crypto.Hash.
52+
func hashToCryptoHash(h hash.Hash) crypto.Hash {
53+
if h, ok := h.(*Hash); ok {
54+
return h.alg.ch
55+
}
56+
return 0
57+
}
58+
5159
// hashFuncToMD converts a hash.Hash function to a GOossl.EVP_MD_PTR.
5260
// See [hashFuncHash] for details on error handling.
5361
func hashFuncToMD(fn func() hash.Hash) (ossl.EVP_MD_PTR, error) {
@@ -294,92 +302,104 @@ func setupEVP(withKey withKeyFunc, padding int32,
294302
}
295303
// Each padding type has its own requirements in terms of when to apply the padding,
296304
// so it can't be just set at this point.
297-
setPadding := func() error {
298-
_, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PADDING, padding, nil)
299-
return err
300-
}
301305
switch padding {
302306
case ossl.RSA_PKCS1_OAEP_PADDING:
303-
md := hashToMD(h)
304-
if md == nil {
305-
return nil, errors.New("crypto/rsa: unsupported hash function")
306-
}
307-
var mgfMD ossl.EVP_MD_PTR
308-
if mgfHash != nil {
309-
// mgfHash is optional, but if it is set it must match a supported hash function.
310-
mgfMD = hashToMD(mgfHash)
311-
if mgfMD == nil {
312-
return nil, errors.New("crypto/rsa: unsupported hash function")
313-
}
314-
}
315-
// setPadding must happen before setting EVP_PKEY_CTRL_RSA_OAEP_MD.
316-
if err := setPadding(); err != nil {
317-
return nil, err
318-
}
319-
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_OAEP_MD, 0, unsafe.Pointer(md)); err != nil {
320-
return nil, err
321-
}
322-
if mgfHash != nil {
323-
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_MGF1_MD, 0, unsafe.Pointer(mgfMD)); err != nil {
324-
return nil, err
325-
}
326-
}
327-
// ctx takes ownership of label, so malloc a copy for OpenSSL to free.
328-
// OpenSSL does not take ownership of the label if the length is zero,
329-
// so better avoid the allocation.
330-
var clabel *byte
331-
if len(label) > 0 {
332-
clabel = (*byte)(cryptoMalloc(len(label)))
333-
copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
334-
var err error
335-
if major() == 3 {
336-
_, err = ossl.EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Pointer(clabel), int32(len(label)))
337-
} else {
338-
_, err = ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_OAEP_LABEL, int32(len(label)), unsafe.Pointer(clabel))
339-
}
340-
if err != nil {
341-
cryptoFree(unsafe.Pointer(clabel))
342-
return nil, err
343-
}
344-
}
307+
err = setOAEPPadding(ctx, h, mgfHash, label)
345308
case ossl.RSA_PKCS1_PSS_PADDING:
346-
alg := loadHash(ch, false)
347-
if alg == nil {
348-
return nil, errors.New("crypto/rsa: unsupported hash function")
349-
}
350-
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(alg.md)); err != nil {
351-
return nil, err
309+
err = setPSSPadding(ctx, saltLen, ch)
310+
case ossl.RSA_PKCS1_PADDING:
311+
err = setPKCS1Padding(ctx, ch)
312+
default:
313+
_, err = ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PADDING, padding, nil)
314+
}
315+
if err != nil {
316+
return nil, err
317+
}
318+
return ctx, nil
319+
}
320+
321+
func setPSSPadding(ctx ossl.EVP_PKEY_CTX_PTR, saltLen int32, ch crypto.Hash) error {
322+
alg := loadHash(ch, false)
323+
if alg == nil {
324+
return errors.New("crypto/rsa: unsupported hash function")
325+
}
326+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(alg.md)); err != nil {
327+
return err
328+
}
329+
// setPadding must happen after setting EVP_PKEY_CTRL_MD.
330+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PADDING, ossl.RSA_PKCS1_PSS_PADDING, nil); err != nil {
331+
return err
332+
}
333+
if saltLen != 0 {
334+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PSS_SALTLEN, saltLen, nil); err != nil {
335+
return err
352336
}
353-
// setPadding must happen after setting EVP_PKEY_CTRL_MD.
354-
if err := setPadding(); err != nil {
355-
return nil, err
337+
}
338+
return nil
339+
}
340+
341+
func setPKCS1Padding(ctx ossl.EVP_PKEY_CTX_PTR, ch crypto.Hash) error {
342+
if ch == 0 {
343+
// We support unhashed messages.
344+
return nil
345+
}
346+
alg := loadHash(ch, false)
347+
if alg == nil {
348+
return errors.New("crypto/rsa: unsupported hash function")
349+
}
350+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, -1, -1, ossl.EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(alg.md)); err != nil {
351+
return err
352+
}
353+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PADDING, ossl.RSA_PKCS1_PADDING, nil); err != nil {
354+
return err
355+
}
356+
return nil
357+
}
358+
359+
func setOAEPPadding(ctx ossl.EVP_PKEY_CTX_PTR, h, mgfHash hash.Hash, label []byte) error {
360+
md := hashToMD(h)
361+
if md == nil {
362+
return errors.New("crypto/rsa: unsupported hash function")
363+
}
364+
var mgfMD ossl.EVP_MD_PTR
365+
if mgfHash != nil {
366+
// mgfHash is optional, but if it is set it must match a supported hash function.
367+
mgfMD = hashToMD(mgfHash)
368+
if mgfMD == nil {
369+
return errors.New("crypto/rsa: unsupported hash function")
356370
}
357-
if saltLen != 0 {
358-
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PSS_SALTLEN, saltLen, nil); err != nil {
359-
return nil, err
360-
}
371+
}
372+
// setPadding must happen before setting EVP_PKEY_CTRL_RSA_OAEP_MD.
373+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_PADDING, ossl.RSA_PKCS1_OAEP_PADDING, nil); err != nil {
374+
return err
375+
}
376+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_OAEP_MD, 0, unsafe.Pointer(md)); err != nil {
377+
return err
378+
}
379+
if mgfHash != nil {
380+
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_MGF1_MD, 0, unsafe.Pointer(mgfMD)); err != nil {
381+
return err
361382
}
362-
363-
case ossl.RSA_PKCS1_PADDING:
364-
if ch != 0 {
365-
// We support unhashed messages.
366-
alg := loadHash(ch, false)
367-
if alg == nil {
368-
return nil, errors.New("crypto/rsa: unsupported hash function")
369-
}
370-
if _, err := ossl.EVP_PKEY_CTX_ctrl(ctx, -1, -1, ossl.EVP_PKEY_CTRL_MD, 0, unsafe.Pointer(alg.md)); err != nil {
371-
return nil, err
372-
}
373-
if err := setPadding(); err != nil {
374-
return nil, err
375-
}
383+
}
384+
// ctx takes ownership of label, so malloc a copy for OpenSSL to free.
385+
// OpenSSL does not take ownership of the label if the length is zero,
386+
// so better avoid the allocation.
387+
var clabel *byte
388+
if len(label) > 0 {
389+
clabel = (*byte)(cryptoMalloc(len(label)))
390+
copy((*[1 << 30]byte)(unsafe.Pointer(clabel))[:len(label)], label)
391+
var err error
392+
if major() == 3 {
393+
_, err = ossl.EVP_PKEY_CTX_set0_rsa_oaep_label(ctx, unsafe.Pointer(clabel), int32(len(label)))
394+
} else {
395+
_, err = ossl.EVP_PKEY_CTX_ctrl(ctx, ossl.EVP_PKEY_RSA, -1, ossl.EVP_PKEY_CTRL_RSA_OAEP_LABEL, int32(len(label)), unsafe.Pointer(clabel))
376396
}
377-
default:
378-
if err := setPadding(); err != nil {
379-
return nil, err
397+
if err != nil {
398+
cryptoFree(unsafe.Pointer(clabel))
399+
return err
380400
}
381401
}
382-
return ctx, nil
402+
return nil
383403
}
384404

385405
func cryptEVP(withKey withKeyFunc, padding int32,

0 commit comments

Comments
 (0)