Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Support PKCS1 with MD5SHA1 #75

Merged
merged 2 commits into from
Nov 25, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
46 changes: 36 additions & 10 deletions cng/rsa.go
Original file line number Diff line number Diff line change
Expand Up @@ -232,18 +232,36 @@ func VerifyRSAPSS(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte, saltLen
return keyVerify(pub.hkey, unsafe.Pointer(&info), hashed, sig, bcrypt.PAD_PSS)
}

func SignRSAPKCS1v15(priv *PrivateKeyRSA, h crypto.Hash, hashed []byte) ([]byte, error) {
// SignRSAPKCS1v15 calculates the signature of hashed using
// RSASSA-PKCS1-V1_5-SIGN from RSA PKCS #1 v1.5. Note that hashed must
// be the result of hashing the input message using the given hash
// function. If hash is zero, hashed is signed directly.
func SignRSAPKCS1v15(priv *PrivateKeyRSA, hash crypto.Hash, hashed []byte) ([]byte, error) {
defer runtime.KeepAlive(priv)
info, err := newPKCS1_PADDING_INFO(h)
if hash != crypto.Hash(0) {
if len(hashed) != hash.Size() {
return nil, errors.New("crypto/rsa: input must be hashed message")
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
}
}
info, err := newPKCS1_PADDING_INFO(hash)
if err != nil {
return nil, err
}
return keySign(priv.hkey, unsafe.Pointer(&info), hashed, bcrypt.PAD_PKCS1)
}

func VerifyRSAPKCS1v15(pub *PublicKeyRSA, h crypto.Hash, hashed, sig []byte) error {
// VerifyPKCS1v15 verifies an RSA PKCS #1 v1.5 signature.
// hashed is the result of hashing the input message using the given hash
// function and sig is the signature. A valid signature is indicated by
// returning a nil error. If hash is zero then hashed is used directly.
func VerifyRSAPKCS1v15(pub *PublicKeyRSA, hash crypto.Hash, hashed, sig []byte) error {
defer runtime.KeepAlive(pub)
info, err := newPKCS1_PADDING_INFO(h)
if hash != crypto.Hash(0) {
if len(hashed) != hash.Size() {
return errors.New("crypto/rsa: input must be hashed message")
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
}
}
info, err := newPKCS1_PADDING_INFO(hash)
if err != nil {
return err
}
Expand Down Expand Up @@ -341,16 +359,24 @@ func newPSS_PADDING_INFO(h crypto.Hash, sizeBits uint32, saltLen int, sign bool)
return
}

func newPKCS1_PADDING_INFO(h crypto.Hash) (info bcrypt.PKCS1_PADDING_INFO, err error) {
if h != 0 {
func newPKCS1_PADDING_INFO(h crypto.Hash) (bcrypt.PKCS1_PADDING_INFO, error) {
var alg *uint16
switch h {
case 0:
// Unpadded RSA signatures, no need to set the hash algorithm.
case crypto.MD5SHA1:
// The MD5SHA1 hash is not supported by CNG, but the AlgId field
// is only used to pad the signature with the hash OID, and
// PKCS1 has historically used a null OID for MD5SHA1.
// This is a special case for compatibility with TLS 1.0/1.1.
default:
hashID := cryptoHashToID(h)
if hashID == "" {
err = errors.New("crypto/rsa: unsupported hash function")
} else {
info.AlgId = utf16PtrFromString(hashID)
return bcrypt.PKCS1_PADDING_INFO{}, errors.New("crypto/rsa: unsupported hash function")
}
alg = utf16PtrFromString(hashID)
}
return
return bcrypt.PKCS1_PADDING_INFO{AlgId: alg}, nil
}

func cryptoHashToID(ch crypto.Hash) string {
Expand Down
67 changes: 66 additions & 1 deletion cng/rsa_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -163,6 +163,70 @@ func TestSignVerifyPKCS1v15_Unhashed(t *testing.T) {
}
}

func TestSignVerifyPKCS1v15_MD5SHA1(t *testing.T) {
msg := []byte("hi!")

// MD5+SHA1 hash
md5, sha1 := cng.NewMD5(), cng.NewSHA1()
hashed := make([]byte, md5.Size()+sha1.Size())
md5.Write(msg)
sha1.Write(msg)
copy(hashed, md5.Sum(nil))
copy(hashed[md5.Size():], sha1.Sum(nil))

priv, pub := newRSAKey(t, 2048)
signed, err := cng.SignRSAPKCS1v15(priv, crypto.MD5SHA1, hashed)
if err != nil {
t.Fatal(err)
}
err = cng.VerifyRSAPKCS1v15(pub, crypto.MD5SHA1, hashed, signed)
if err != nil {
t.Fatal(err)
}
}

func TestSignPKCS1v15_NotHashed(t *testing.T) {
sha256 := cng.NewSHA256()
msg := []byte("hi!")
priv, _ := newRSAKey(t, 2048)
sha256.Write(msg)
hashed := sha256.Sum(nil)
_, err := cng.SignRSAPKCS1v15(priv, crypto.SHA1, hashed)
if err == nil {
t.Fatal("error expected")
} else if err.Error() != "crypto/rsa: input must be hashed message" {
t.Fatalf("unexpected error: %v", err)
}
}

func TestVerifyPKCS1v15_NotHashed(t *testing.T) {
sha256 := cng.NewSHA256()
msg := []byte("hi!")
qmuntal marked this conversation as resolved.
Show resolved Hide resolved
priv, pub := newRSAKey(t, 2048)
sha256.Write(msg)
hashed := sha256.Sum(nil)
signed, err := cng.SignRSAPKCS1v15(priv, crypto.SHA256, hashed)
if err != nil {
t.Fatal(err)
}
err = cng.VerifyRSAPKCS1v15(pub, crypto.SHA1, hashed, signed)
if err == nil {
t.Fatal("error expected")
} else if err.Error() != "crypto/rsa: input must be hashed message" {
t.Fatalf("unexpected error: %v", err)
}
}

func TestSignPKCS1v15_Empty(t *testing.T) {
priv, _ := newRSAKey(t, 2048)
_, err := cng.SignRSAPKCS1v15(priv, crypto.SHA256, nil)
if err == nil {
t.Fatal("error expected")
} else if err.Error() != "crypto/rsa: input must be hashed message" {
t.Fatalf("unexpected error: %v", err)
}
}

func TestSignVerifyPKCS1v15_Invalid(t *testing.T) {
sha256 := cng.NewSHA256()
msg := []byte("hi!")
Expand All @@ -173,7 +237,8 @@ func TestSignVerifyPKCS1v15_Invalid(t *testing.T) {
if err != nil {
t.Fatal(err)
}
err = cng.VerifyRSAPKCS1v15(pub, crypto.SHA256, msg, signed)
signed[len(signed)-1] ^= 0xff
err = cng.VerifyRSAPKCS1v15(pub, crypto.SHA256, hashed, signed)
if err == nil {
t.Fatal("error expected")
}
Expand Down
Loading