package rsa
import (
"bytes"
"crypto/internal/fips140"
"crypto/internal/fips140/bigmod"
"errors"
)
type PublicKey struct {
N *bigmod .Modulus
E int
}
func (pub *PublicKey ) Size () int {
return (pub .N .BitLen () + 7 ) / 8
}
type PrivateKey struct {
pub PublicKey
d *bigmod .Nat
p , q *bigmod .Modulus
dP []byte
dQ []byte
qInv *bigmod .Nat
fipsApproved bool
}
func (priv *PrivateKey ) PublicKey () *PublicKey {
return &priv .pub
}
func NewPrivateKey (N []byte , e int , d , P , Q []byte ) (*PrivateKey , error ) {
n , err := bigmod .NewModulus (N )
if err != nil {
return nil , err
}
p , err := bigmod .NewModulus (P )
if err != nil {
return nil , err
}
q , err := bigmod .NewModulus (Q )
if err != nil {
return nil , err
}
dN , err := bigmod .NewNat ().SetBytes (d , n )
if err != nil {
return nil , err
}
return newPrivateKey (n , e , dN , p , q )
}
func newPrivateKey (n *bigmod .Modulus , e int , d *bigmod .Nat , p , q *bigmod .Modulus ) (*PrivateKey , error ) {
pMinusOne := p .Nat ().SubOne (p )
pMinusOneMod , err := bigmod .NewModulus (pMinusOne .Bytes (p ))
if err != nil {
return nil , err
}
dP := bigmod .NewNat ().Mod (d , pMinusOneMod ).Bytes (pMinusOneMod )
qMinusOne := q .Nat ().SubOne (q )
qMinusOneMod , err := bigmod .NewModulus (qMinusOne .Bytes (q ))
if err != nil {
return nil , err
}
dQ := bigmod .NewNat ().Mod (d , qMinusOneMod ).Bytes (qMinusOneMod )
if p .Nat ().IsOdd () == 0 {
return nil , errors .New ("crypto/rsa: p is even" )
}
pMinusTwo := p .Nat ().SubOne (p ).SubOne (p ).Bytes (p )
qInv := bigmod .NewNat ().Mod (q .Nat (), p )
qInv .Exp (qInv , pMinusTwo , p )
pk := &PrivateKey {
pub : PublicKey {
N : n , E : e ,
},
d : d , p : p , q : q ,
dP : dP , dQ : dQ , qInv : qInv ,
}
if err := checkPrivateKey (pk ); err != nil {
return nil , err
}
return pk , nil
}
func NewPrivateKeyWithPrecomputation (N []byte , e int , d , P , Q , dP , dQ , qInv []byte ) (*PrivateKey , error ) {
n , err := bigmod .NewModulus (N )
if err != nil {
return nil , err
}
p , err := bigmod .NewModulus (P )
if err != nil {
return nil , err
}
q , err := bigmod .NewModulus (Q )
if err != nil {
return nil , err
}
dN , err := bigmod .NewNat ().SetBytes (d , n )
if err != nil {
return nil , err
}
qInvNat , err := bigmod .NewNat ().SetBytes (qInv , p )
if err != nil {
return nil , err
}
pk := &PrivateKey {
pub : PublicKey {
N : n , E : e ,
},
d : dN , p : p , q : q ,
dP : dP , dQ : dQ , qInv : qInvNat ,
}
if err := checkPrivateKey (pk ); err != nil {
return nil , err
}
return pk , nil
}
func NewPrivateKeyWithoutCRT (N []byte , e int , d []byte ) (*PrivateKey , error ) {
n , err := bigmod .NewModulus (N )
if err != nil {
return nil , err
}
dN , err := bigmod .NewNat ().SetBytes (d , n )
if err != nil {
return nil , err
}
pk := &PrivateKey {
pub : PublicKey {
N : n , E : e ,
},
d : dN ,
}
if err := checkPrivateKey (pk ); err != nil {
return nil , err
}
return pk , nil
}
func (priv *PrivateKey ) Export () (N []byte , e int , d , P , Q , dP , dQ , qInv []byte ) {
N = priv .pub .N .Nat ().Bytes (priv .pub .N )
e = priv .pub .E
d = priv .d .Bytes (priv .pub .N )
if priv .dP == nil {
return
}
P = priv .p .Nat ().Bytes (priv .p )
Q = priv .q .Nat ().Bytes (priv .q )
dP = bytes .Clone (priv .dP )
dQ = bytes .Clone (priv .dQ )
qInv = priv .qInv .Bytes (priv .p )
return
}
func checkPrivateKey (priv *PrivateKey ) error {
priv .fipsApproved = true
if fipsApproved , err := checkPublicKey (&priv .pub ); err != nil {
return err
} else if !fipsApproved {
priv .fipsApproved = false
}
if priv .dP == nil {
priv .fipsApproved = false
return nil
}
N := priv .pub .N
p := priv .p
q := priv .q
if p .BitLen () != q .BitLen () {
priv .fipsApproved = false
}
pN := bigmod .NewNat ().ExpandFor (N )
if _ , err := pN .SetBytes (p .Nat ().Bytes (p ), N ); err != nil {
return errors .New ("crypto/rsa: invalid prime" )
}
qN := bigmod .NewNat ().ExpandFor (N )
if _ , err := qN .SetBytes (q .Nat ().Bytes (q ), N ); err != nil {
return errors .New ("crypto/rsa: invalid prime" )
}
if pN .Mul (qN , N ).IsZero () != 1 {
return errors .New ("crypto/rsa: p * q != n" )
}
pMinus1 , err := bigmod .NewModulus (p .Nat ().SubOne (p ).Bytes (p ))
if err != nil {
return errors .New ("crypto/rsa: invalid prime" )
}
dP , err := bigmod .NewNat ().SetBytes (priv .dP , pMinus1 )
if err != nil {
return errors .New ("crypto/rsa: invalid CRT exponent" )
}
de := bigmod .NewNat ()
de .SetUint (uint (priv .pub .E )).ExpandFor (pMinus1 )
de .Mul (dP , pMinus1 )
if de .IsOne () != 1 {
return errors .New ("crypto/rsa: invalid CRT exponent" )
}
qMinus1 , err := bigmod .NewModulus (q .Nat ().SubOne (q ).Bytes (q ))
if err != nil {
return errors .New ("crypto/rsa: invalid prime" )
}
dQ , err := bigmod .NewNat ().SetBytes (priv .dQ , qMinus1 )
if err != nil {
return errors .New ("crypto/rsa: invalid CRT exponent" )
}
de .SetUint (uint (priv .pub .E )).ExpandFor (qMinus1 )
de .Mul (dQ , qMinus1 )
if de .IsOne () != 1 {
return errors .New ("crypto/rsa: invalid CRT exponent" )
}
qP , err := bigmod .NewNat ().SetOverflowingBytes (q .Nat ().Bytes (q ), p )
if err != nil {
qP = bigmod .NewNat ().Mod (q .Nat (), p )
}
if qP .Mul (priv .qInv , p ).IsOne () != 1 {
return errors .New ("crypto/rsa: invalid CRT coefficient" )
}
dP1 := bigmod .NewNat ().Mod (priv .d , pMinus1 )
if dP1 .Equal (dP ) != 1 {
return errors .New ("crypto/rsa: d does not match dP" )
}
dQ1 := bigmod .NewNat ().Mod (priv .d , qMinus1 )
if dQ1 .Equal (dQ ) != 1 {
return errors .New ("crypto/rsa: d does not match dQ" )
}
diff := bigmod .NewNat ()
if qP , err := bigmod .NewNat ().SetBytes (q .Nat ().Bytes (q ), p ); err != nil {
pQ , err := bigmod .NewNat ().SetBytes (p .Nat ().Bytes (p ), q )
if err != nil {
return errors .New ("crypto/rsa: p == q" )
}
diff .ExpandFor (q ).Sub (pQ , q )
} else {
diff .ExpandFor (p ).Sub (qP , p )
}
if diff .BitLenVarTime () <= N .BitLen ()/2 -100 {
return errors .New ("crypto/rsa: |p - q| too small" )
}
if priv .d .BitLenVarTime () <= N .BitLen ()/2 {
return errors .New ("crypto/rsa: d too small" )
}
return nil
}
func checkPublicKey (pub *PublicKey ) (fipsApproved bool , err error ) {
fipsApproved = true
if pub .N == nil {
return false , errors .New ("crypto/rsa: missing public modulus" )
}
if pub .N .Nat ().IsOdd () == 0 {
return false , errors .New ("crypto/rsa: public modulus is even" )
}
if pub .N .BitLen () < 2048 {
fipsApproved = false
}
if pub .N .BitLen ()%2 == 1 {
fipsApproved = false
}
if pub .E < 2 {
return false , errors .New ("crypto/rsa: public exponent too small or negative" )
}
if pub .E &1 == 0 {
return false , errors .New ("crypto/rsa: public exponent is even" )
}
if pub .E <= 1 <<16 {
fipsApproved = false
}
if pub .E > 1 <<31 -1 {
return false , errors .New ("crypto/rsa: public exponent too large" )
}
return fipsApproved , nil
}
func Encrypt (pub *PublicKey , plaintext []byte ) ([]byte , error ) {
fips140 .RecordNonApproved ()
if _ , err := checkPublicKey (pub ); err != nil {
return nil , err
}
return encrypt (pub , plaintext )
}
func encrypt (pub *PublicKey , plaintext []byte ) ([]byte , error ) {
m , err := bigmod .NewNat ().SetBytes (plaintext , pub .N )
if err != nil {
return nil , err
}
return bigmod .NewNat ().ExpShortVarTime (m , uint (pub .E ), pub .N ).Bytes (pub .N ), nil
}
var ErrMessageTooLong = errors .New ("crypto/rsa: message too long for RSA key size" )
var ErrDecryption = errors .New ("crypto/rsa: decryption error" )
var ErrVerification = errors .New ("crypto/rsa: verification error" )
const withCheck = true
const noCheck = false
func DecryptWithoutCheck (priv *PrivateKey , ciphertext []byte ) ([]byte , error ) {
fips140 .RecordNonApproved ()
return decrypt (priv , ciphertext , noCheck )
}
func DecryptWithCheck (priv *PrivateKey , ciphertext []byte ) ([]byte , error ) {
fips140 .RecordNonApproved ()
return decrypt (priv , ciphertext , withCheck )
}
func decrypt (priv *PrivateKey , ciphertext []byte , check bool ) ([]byte , error ) {
if !priv .fipsApproved {
fips140 .RecordNonApproved ()
}
var m *bigmod .Nat
N , E := priv .pub .N , priv .pub .E
c , err := bigmod .NewNat ().SetBytes (ciphertext , N )
if err != nil {
return nil , ErrDecryption
}
if priv .dP == nil {
fips140 .RecordNonApproved ()
m = bigmod .NewNat ().Exp (c , priv .d .Bytes (N ), N )
} else {
P , Q := priv .p , priv .q
t0 := bigmod .NewNat ()
m = bigmod .NewNat ().Exp (t0 .Mod (c , P ), priv .dP , P )
m2 := bigmod .NewNat ().Exp (t0 .Mod (c , Q ), priv .dQ , Q )
m .Sub (t0 .Mod (m2 , P ), P )
m .Mul (priv .qInv , P )
m .ExpandFor (N ).Mul (t0 .Mod (Q .Nat (), N ), N )
m .Add (m2 .ExpandFor (N ), N )
}
if check {
c1 := bigmod .NewNat ().ExpShortVarTime (m , uint (E ), N )
if c1 .Equal (c ) != 1 {
return nil , ErrDecryption
}
}
return m .Bytes (N ), nil
}
The pages are generated with Golds v0.8.4 . (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 .