diff --git a/certstore/certstore_darwin.go b/certstore/certstore_darwin.go index a6ef2ea..08df732 100644 --- a/certstore/certstore_darwin.go +++ b/certstore/certstore_darwin.go @@ -279,7 +279,7 @@ func (i *macIdentity) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts } defer C.CFRelease(C.CFTypeRef(cdigest)) - algo, err := i.getAlgo(hash) + algo, err := i.getAlgo(opts) if err != nil { return nil, err } @@ -305,8 +305,10 @@ func (i *macIdentity) Sign(rand io.Reader, digest []byte, opts crypto.SignerOpts return sig, nil } -// getAlgo decides which algorithm to use with this key type for the given hash. -func (i *macIdentity) getAlgo(hash crypto.Hash) (algo C.SecKeyAlgorithm, err error) { +// getAlgo decides which algorithm to use with this key type for the given opts. +func (i *macIdentity) getAlgo(opts crypto.SignerOpts) (algo C.SecKeyAlgorithm, err error) { + hash := opts.HashFunc() + var crt *x509.Certificate if crt, err = i.Certificate(); err != nil { return @@ -327,17 +329,31 @@ func (i *macIdentity) getAlgo(hash crypto.Hash) (algo C.SecKeyAlgorithm, err err err = ErrUnsupportedHash } case *rsa.PublicKey: - switch hash { - case crypto.SHA1: - algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1 - case crypto.SHA256: - algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256 - case crypto.SHA384: - algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384 - case crypto.SHA512: - algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512 - default: - err = ErrUnsupportedHash + _, isPSS := opts.(*rsa.PSSOptions) + if isPSS { + switch hash { + case crypto.SHA256: + algo = C.kSecKeyAlgorithmRSASignatureDigestPSSSHA256 + case crypto.SHA384: + algo = C.kSecKeyAlgorithmRSASignatureDigestPSSSHA384 + case crypto.SHA512: + algo = C.kSecKeyAlgorithmRSASignatureDigestPSSSHA512 + default: + err = ErrUnsupportedHash + } + } else { + switch hash { + case crypto.SHA1: + algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA1 + case crypto.SHA256: + algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA256 + case crypto.SHA384: + algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA384 + case crypto.SHA512: + algo = C.kSecKeyAlgorithmRSASignatureDigestPKCS1v15SHA512 + default: + err = ErrUnsupportedHash + } } default: err = errors.New("unsupported key type") diff --git a/certstore/certstore_test.go b/certstore/certstore_test.go index 84b22b6..727c988 100644 --- a/certstore/certstore_test.go +++ b/certstore/certstore_test.go @@ -89,6 +89,61 @@ func ImportDeleteHelper(t *testing.T, i *fakeca.Identity) { }) } +func TestSignerRSAPSS(t *testing.T) { + withIdentity(t, leafRSA, func(ident Identity) { + signer, err := ident.Signer() + if err != nil { + t.Fatal(err) + } + + // RSA-PSS with SHA-256 (used by TLS 1.3 CertificateVerify) + sha256Digest := sha256.Sum256([]byte("hello")) + pssOpts := &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA256, + } + sig, err := signer.Sign(rand.Reader, sha256Digest[:], pssOpts) + if err != nil { + t.Fatal(err) + } + + pub := signer.Public().(*rsa.PublicKey) + if err := rsa.VerifyPSS(pub, crypto.SHA256, sha256Digest[:], sig, pssOpts); err != nil { + t.Fatalf("PSS SHA-256 signature verification failed: %v", err) + } + + // RSA-PSS with SHA-384 + sha384Digest := sha512.Sum384([]byte("hello")) + pssOpts384 := &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA384, + } + sig, err = signer.Sign(rand.Reader, sha384Digest[:], pssOpts384) + if err != nil { + t.Fatal(err) + } + + if err := rsa.VerifyPSS(pub, crypto.SHA384, sha384Digest[:], sig, pssOpts384); err != nil { + t.Fatalf("PSS SHA-384 signature verification failed: %v", err) + } + + // RSA-PSS with SHA-512 + sha512Digest := sha512.Sum512([]byte("hello")) + pssOpts512 := &rsa.PSSOptions{ + SaltLength: rsa.PSSSaltLengthEqualsHash, + Hash: crypto.SHA512, + } + sig, err = signer.Sign(rand.Reader, sha512Digest[:], pssOpts512) + if err != nil { + t.Fatal(err) + } + + if err := rsa.VerifyPSS(pub, crypto.SHA512, sha512Digest[:], sig, pssOpts512); err != nil { + t.Fatalf("PSS SHA-512 signature verification failed: %v", err) + } + }) +} + func TestSignerRSA(t *testing.T) { rsaPriv, ok := leafRSA.PrivateKey.(*rsa.PrivateKey) if !ok { diff --git a/certstore/certstore_windows.go b/certstore/certstore_windows.go index 2b38173..451e300 100644 --- a/certstore/certstore_windows.go +++ b/certstore/certstore_windows.go @@ -361,14 +361,16 @@ func (wpk *winPrivateKey) Sign(rand io.Reader, digest []byte, opts crypto.Signer if wpk.capiProv != 0 { return wpk.capiSignHash(opts.HashFunc(), digest) } else if wpk.cngHandle != 0 { - return wpk.cngSignHash(opts.HashFunc(), digest) + return wpk.cngSignHash(opts, digest) } else { return nil, errors.New("bad private key") } } // cngSignHash signs a digest using the CNG APIs. -func (wpk *winPrivateKey) cngSignHash(hash crypto.Hash, digest []byte) ([]byte, error) { +func (wpk *winPrivateKey) cngSignHash(opts crypto.SignerOpts, digest []byte) ([]byte, error) { + hash := opts.HashFunc() + if len(digest) != hash.Size() { return nil, errors.New("bad digest for hash") } @@ -384,23 +386,43 @@ func (wpk *winPrivateKey) cngSignHash(hash crypto.Hash, digest []byte) ([]byte, sigLen = C.DWORD(0) ) - // setup pkcs1v1.5 padding for RSA + // setup padding for RSA if _, isRSA := wpk.publicKey.(*rsa.PublicKey); isRSA { - flags |= C.BCRYPT_PAD_PKCS1 - padInfo := C.BCRYPT_PKCS1_PADDING_INFO{} - padPtr = unsafe.Pointer(&padInfo) - - switch hash { - case crypto.SHA1: - padInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM - case crypto.SHA256: - padInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM - case crypto.SHA384: - padInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM - case crypto.SHA512: - padInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM - default: - return nil, ErrUnsupportedHash + _, isPSS := opts.(*rsa.PSSOptions) + if isPSS { + flags |= C.BCRYPT_PAD_PSS + padInfo := C.BCRYPT_PSS_PADDING_INFO{ + cbSalt: C.ULONG(hash.Size()), + } + padPtr = unsafe.Pointer(&padInfo) + + switch hash { + case crypto.SHA256: + padInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM + case crypto.SHA384: + padInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM + case crypto.SHA512: + padInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM + default: + return nil, ErrUnsupportedHash + } + } else { + flags |= C.BCRYPT_PAD_PKCS1 + padInfo := C.BCRYPT_PKCS1_PADDING_INFO{} + padPtr = unsafe.Pointer(&padInfo) + + switch hash { + case crypto.SHA1: + padInfo.pszAlgId = BCRYPT_SHA1_ALGORITHM + case crypto.SHA256: + padInfo.pszAlgId = BCRYPT_SHA256_ALGORITHM + case crypto.SHA384: + padInfo.pszAlgId = BCRYPT_SHA384_ALGORITHM + case crypto.SHA512: + padInfo.pszAlgId = BCRYPT_SHA512_ALGORITHM + default: + return nil, ErrUnsupportedHash + } } }