package rsa
import (
"crypto"
"crypto/internal/boring"
"crypto/internal/fips140/rsa"
"crypto/internal/fips140only"
"errors"
"hash"
"io"
)
const (
PSSSaltLengthAuto = 0
PSSSaltLengthEqualsHash = -1
)
type PSSOptions struct {
SaltLength int
Hash crypto .Hash
}
func (opts *PSSOptions ) HashFunc () crypto .Hash {
return opts .Hash
}
func (opts *PSSOptions ) saltLength () int {
if opts == nil {
return PSSSaltLengthAuto
}
return opts .SaltLength
}
func SignPSS (rand io .Reader , priv *PrivateKey , hash crypto .Hash , digest []byte , opts *PSSOptions ) ([]byte , error ) {
if err := checkPublicKeySize (&priv .PublicKey ); err != nil {
return nil , err
}
if err := checkFIPS140OnlyPrivateKey (priv ); err != nil {
return nil , err
}
if fips140only .Enabled && !fips140only .ApprovedHash (hash .New ()) {
return nil , errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
if fips140only .Enabled && !fips140only .ApprovedRandomReader (rand ) {
return nil , errors .New ("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode" )
}
if opts != nil && opts .Hash != 0 {
hash = opts .Hash
}
if boring .Enabled && rand == boring .RandReader {
bkey , err := boringPrivateKey (priv )
if err != nil {
return nil , err
}
return boring .SignRSAPSS (bkey , hash , digest , opts .saltLength ())
}
boring .UnreachableExceptTests ()
k , err := fipsPrivateKey (priv )
if err != nil {
return nil , err
}
h := hash .New ()
saltLength := opts .saltLength ()
if fips140only .Enabled && saltLength > hash .Size () {
return nil , errors .New ("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode" )
}
switch saltLength {
case PSSSaltLengthAuto :
saltLength , err = rsa .PSSMaxSaltLength (k .PublicKey (), h )
if err != nil {
return nil , fipsError (err )
}
case PSSSaltLengthEqualsHash :
saltLength = hash .Size ()
default :
if saltLength <= 0 {
return nil , errors .New ("crypto/rsa: invalid PSS salt length" )
}
}
return fipsError2 (rsa .SignPSS (rand , k , h , digest , saltLength ))
}
func VerifyPSS (pub *PublicKey , hash crypto .Hash , digest []byte , sig []byte , opts *PSSOptions ) error {
if err := checkPublicKeySize (pub ); err != nil {
return err
}
if err := checkFIPS140OnlyPublicKey (pub ); err != nil {
return err
}
if fips140only .Enabled && !fips140only .ApprovedHash (hash .New ()) {
return errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
if boring .Enabled {
bkey , err := boringPublicKey (pub )
if err != nil {
return err
}
if err := boring .VerifyRSAPSS (bkey , hash , digest , sig , opts .saltLength ()); err != nil {
return ErrVerification
}
return nil
}
k , err := fipsPublicKey (pub )
if err != nil {
return err
}
saltLength := opts .saltLength ()
if fips140only .Enabled && saltLength > hash .Size () {
return errors .New ("crypto/rsa: use of PSS salt longer than the hash is not allowed in FIPS 140-only mode" )
}
switch saltLength {
case PSSSaltLengthAuto :
return fipsError (rsa .VerifyPSS (k , hash .New (), digest , sig ))
case PSSSaltLengthEqualsHash :
return fipsError (rsa .VerifyPSSWithSaltLength (k , hash .New (), digest , sig , hash .Size ()))
default :
return fipsError (rsa .VerifyPSSWithSaltLength (k , hash .New (), digest , sig , saltLength ))
}
}
func EncryptOAEP (hash hash .Hash , random io .Reader , pub *PublicKey , msg []byte , label []byte ) ([]byte , error ) {
if err := checkPublicKeySize (pub ); err != nil {
return nil , err
}
if err := checkFIPS140OnlyPublicKey (pub ); err != nil {
return nil , err
}
if fips140only .Enabled && !fips140only .ApprovedHash (hash ) {
return nil , errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
if fips140only .Enabled && !fips140only .ApprovedRandomReader (random ) {
return nil , errors .New ("crypto/rsa: only crypto/rand.Reader is allowed in FIPS 140-only mode" )
}
defer hash .Reset ()
if boring .Enabled && random == boring .RandReader {
hash .Reset ()
k := pub .Size ()
if len (msg ) > k -2 *hash .Size ()-2 {
return nil , ErrMessageTooLong
}
bkey , err := boringPublicKey (pub )
if err != nil {
return nil , err
}
return boring .EncryptRSAOAEP (hash , hash , bkey , msg , label )
}
boring .UnreachableExceptTests ()
k , err := fipsPublicKey (pub )
if err != nil {
return nil , err
}
return fipsError2 (rsa .EncryptOAEP (hash , hash , random , k , msg , label ))
}
func DecryptOAEP (hash hash .Hash , random io .Reader , priv *PrivateKey , ciphertext []byte , label []byte ) ([]byte , error ) {
defer hash .Reset ()
return decryptOAEP (hash , hash , priv , ciphertext , label )
}
func decryptOAEP(hash , mgfHash hash .Hash , priv *PrivateKey , ciphertext []byte , label []byte ) ([]byte , error ) {
if err := checkPublicKeySize (&priv .PublicKey ); err != nil {
return nil , err
}
if err := checkFIPS140OnlyPrivateKey (priv ); err != nil {
return nil , err
}
if fips140only .Enabled {
if !fips140only .ApprovedHash (hash ) || !fips140only .ApprovedHash (mgfHash ) {
return nil , errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
}
if boring .Enabled {
k := priv .Size ()
if len (ciphertext ) > k ||
k < hash .Size ()*2 +2 {
return nil , ErrDecryption
}
bkey , err := boringPrivateKey (priv )
if err != nil {
return nil , err
}
out , err := boring .DecryptRSAOAEP (hash , mgfHash , bkey , ciphertext , label )
if err != nil {
return nil , ErrDecryption
}
return out , nil
}
k , err := fipsPrivateKey (priv )
if err != nil {
return nil , err
}
return fipsError2 (rsa .DecryptOAEP (hash , mgfHash , k , ciphertext , label ))
}
func SignPKCS1v15 (random io .Reader , priv *PrivateKey , hash crypto .Hash , hashed []byte ) ([]byte , error ) {
var hashName string
if hash != crypto .Hash (0 ) {
if len (hashed ) != hash .Size () {
return nil , errors .New ("crypto/rsa: input must be hashed message" )
}
hashName = hash .String ()
}
if err := checkPublicKeySize (&priv .PublicKey ); err != nil {
return nil , err
}
if err := checkFIPS140OnlyPrivateKey (priv ); err != nil {
return nil , err
}
if fips140only .Enabled && !fips140only .ApprovedHash (hash .New ()) {
return nil , errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
if boring .Enabled {
bkey , err := boringPrivateKey (priv )
if err != nil {
return nil , err
}
return boring .SignRSAPKCS1v15 (bkey , hash , hashed )
}
k , err := fipsPrivateKey (priv )
if err != nil {
return nil , err
}
return fipsError2 (rsa .SignPKCS1v15 (k , hashName , hashed ))
}
func VerifyPKCS1v15 (pub *PublicKey , hash crypto .Hash , hashed []byte , sig []byte ) error {
if err := checkPublicKeySize (pub ); err != nil {
return err
}
if err := checkFIPS140OnlyPublicKey (pub ); err != nil {
return err
}
if fips140only .Enabled && !fips140only .ApprovedHash (hash .New ()) {
return errors .New ("crypto/rsa: use of hash functions other than SHA-2 or SHA-3 is not allowed in FIPS 140-only mode" )
}
if boring .Enabled {
bkey , err := boringPublicKey (pub )
if err != nil {
return err
}
if err := boring .VerifyRSAPKCS1v15 (bkey , hash , hashed , sig ); err != nil {
return ErrVerification
}
return nil
}
k , err := fipsPublicKey (pub )
if err != nil {
return err
}
var hashName string
if hash != crypto .Hash (0 ) {
if len (hashed ) != hash .Size () {
return errors .New ("crypto/rsa: input must be hashed message" )
}
hashName = hash .String ()
}
return fipsError (rsa .VerifyPKCS1v15 (k , hashName , hashed , sig ))
}
func fipsError(err error ) error {
switch err {
case rsa .ErrDecryption :
return ErrDecryption
case rsa .ErrVerification :
return ErrVerification
case rsa .ErrMessageTooLong :
return ErrMessageTooLong
}
return err
}
func fipsError2[T any ](x T , err error ) (T , error ) {
return x , fipsError (err )
}
func checkFIPS140OnlyPublicKey(pub *PublicKey ) error {
if !fips140only .Enabled {
return nil
}
if pub .N == nil {
return errors .New ("crypto/rsa: public key missing N" )
}
if pub .N .BitLen () < 2048 {
return errors .New ("crypto/rsa: use of keys smaller than 2048 bits is not allowed in FIPS 140-only mode" )
}
if pub .N .BitLen ()%2 == 1 {
return errors .New ("crypto/rsa: use of keys with odd size is not allowed in FIPS 140-only mode" )
}
if pub .E <= 1 <<16 {
return errors .New ("crypto/rsa: use of public exponent <= 2¹⁶ is not allowed in FIPS 140-only mode" )
}
if pub .E &1 == 0 {
return errors .New ("crypto/rsa: use of even public exponent is not allowed in FIPS 140-only mode" )
}
return nil
}
func checkFIPS140OnlyPrivateKey(priv *PrivateKey ) error {
if !fips140only .Enabled {
return nil
}
if err := checkFIPS140OnlyPublicKey (&priv .PublicKey ); err != nil {
return err
}
if len (priv .Primes ) != 2 {
return errors .New ("crypto/rsa: use of multi-prime keys is not allowed in FIPS 140-only mode" )
}
if priv .Primes [0 ] == nil || priv .Primes [1 ] == nil || priv .Primes [0 ].BitLen () != priv .Primes [1 ].BitLen () {
return errors .New ("crypto/rsa: use of primes of different sizes is not allowed in FIPS 140-only mode" )
}
return nil
}
The pages are generated with Golds v0.7.3 . (GOOS=linux GOARCH=amd64)
Golds is a Go 101 project developed by Tapir Liu .
PR and bug reports are welcome and can be submitted to the issue list .
Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds .