mirror of
https://github.com/golang/go.git
synced 2026-06-28 11:50:35 +00:00
crypto/internal/fips140/rsa: add large exponent OAEP for ACVP
Similar to the prior largeexponent.go work this adds a new testing only private key type & associated API surface for performing RSA OAEP encryption and decryption. This is necessary to support ACVP KTS-IFC testing with random exponents, since we can't constrain the ACVP server selected exponent to the range supported by the production APIs. We take the hit of duplicating some code to this test only package instead of increasing the complexity of the production code only to support this narrow usage. Change-Id: I3e34385e808da4cd40dd81e8553e0a92a6c294fe Reviewed-on: https://go-review.googlesource.com/c/go/+/769760 Reviewed-by: Roland Shoemaker <roland@golang.org> LUCI-TryBot-Result: golang-scoped@luci-project-accounts.iam.gserviceaccount.com <golang-scoped@luci-project-accounts.iam.gserviceaccount.com> Reviewed-by: Filippo Valsorda <filippo@golang.org> Reviewed-by: David Chase <drchase@google.com>
This commit is contained in:
parent
7f4f2c1c7b
commit
83b29183af
1 changed files with 250 additions and 0 deletions
|
|
@ -6,10 +6,14 @@ package rsa
|
|||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/internal/constanttime"
|
||||
"crypto/internal/fips140"
|
||||
"crypto/internal/fips140/bigmod"
|
||||
"crypto/internal/fips140/drbg"
|
||||
"crypto/internal/fips140/subtle"
|
||||
"errors"
|
||||
"hash"
|
||||
"io"
|
||||
)
|
||||
|
||||
// TestingOnlyLargeExponentPublicKey is a variant of [PublicKey] that supports
|
||||
|
|
@ -113,3 +117,249 @@ func TestingOnlyLargeExponentVerifyPSS(pub *TestingOnlyLargeExponentPublicKey, h
|
|||
}
|
||||
return emsaPSSVerify(digest, em, emBits, pssSaltLengthAutodetect, hash)
|
||||
}
|
||||
|
||||
// TestingOnlyLargeExponentPrivateKey is a variant of [PrivateKey] that supports
|
||||
// large public exponents. It is only meant for supporting the full ACVP test
|
||||
// suite. This type must not be used in production code.
|
||||
type TestingOnlyLargeExponentPrivateKey struct {
|
||||
n *bigmod.Modulus
|
||||
e []byte // big-endian public exponent
|
||||
d *bigmod.Nat
|
||||
p, q *bigmod.Modulus
|
||||
dP []byte
|
||||
dQ []byte
|
||||
qInv *bigmod.Nat
|
||||
}
|
||||
|
||||
func (priv *TestingOnlyLargeExponentPrivateKey) Size() int {
|
||||
return (priv.n.BitLen() + 7) / 8
|
||||
}
|
||||
|
||||
// TestingOnlyNewLargeExponentPrivateKeyWithPrecomputation creates a new RSA private key
|
||||
// with a large public exponent from the given parameters. It is only meant for ACVP testing.
|
||||
func TestingOnlyNewLargeExponentPrivateKeyWithPrecomputation(N []byte, e []byte, d, P, Q, dP, dQ, qInv []byte) (*TestingOnlyLargeExponentPrivateKey, 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
|
||||
}
|
||||
|
||||
priv := &TestingOnlyLargeExponentPrivateKey{
|
||||
n: n, e: e, d: dN, p: p, q: q,
|
||||
dP: dP, dQ: dQ, qInv: qInvNat,
|
||||
}
|
||||
if err := checkLargeExponentPrivateKey(priv); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
return priv, nil
|
||||
}
|
||||
|
||||
func checkLargeExponentPrivateKey(priv *TestingOnlyLargeExponentPrivateKey) error {
|
||||
// Check public key portion.
|
||||
pub := &TestingOnlyLargeExponentPublicKey{N: priv.n, E: priv.e}
|
||||
if err := checkLargeExponentPublicKey(pub); err != nil {
|
||||
return err
|
||||
}
|
||||
|
||||
N := priv.n
|
||||
p := priv.p
|
||||
q := priv.q
|
||||
|
||||
// FIPS 186-5, Section 5.1 requires "that p and q be of the same bit length."
|
||||
if p.BitLen() != q.BitLen() {
|
||||
// We don't enforce this for testing, just note it.
|
||||
}
|
||||
|
||||
// Check that pq ≡ 1 mod N (and that p < N and q < N).
|
||||
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")
|
||||
}
|
||||
|
||||
// Check that de ≡ 1 mod p-1, and de ≡ 1 mod q-1.
|
||||
// Uses byte-slice exponent for large exponents.
|
||||
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()
|
||||
if _, err := de.SetBytes(priv.e, pMinus1); err != nil {
|
||||
// Exponent might be larger than p-1, reduce it.
|
||||
eNat, _ := bigmod.NewNat().SetBytes(priv.e, priv.n)
|
||||
de.Mod(eNat, 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")
|
||||
}
|
||||
if _, err := de.SetBytes(priv.e, qMinus1); err != nil {
|
||||
// Exponent might be larger than q-1, reduce it.
|
||||
eNat, _ := bigmod.NewNat().SetBytes(priv.e, priv.n)
|
||||
de.Mod(eNat, qMinus1)
|
||||
}
|
||||
de.Mul(dQ, qMinus1)
|
||||
if de.IsOne() != 1 {
|
||||
return errors.New("crypto/rsa: invalid CRT exponent")
|
||||
}
|
||||
|
||||
// Check that qInv * q ≡ 1 mod p.
|
||||
qP, err := bigmod.NewNat().SetOverflowingBytes(q.Nat().Bytes(q), p)
|
||||
if err != nil {
|
||||
// q >= 2^⌈log2(p)⌉
|
||||
qP = bigmod.NewNat().Mod(q.Nat(), p)
|
||||
}
|
||||
if qP.Mul(priv.qInv, p).IsOne() != 1 {
|
||||
return errors.New("crypto/rsa: invalid CRT coefficient")
|
||||
}
|
||||
|
||||
return nil
|
||||
}
|
||||
|
||||
func decryptLargeExponent(priv *TestingOnlyLargeExponentPrivateKey, ciphertext []byte) ([]byte, error) {
|
||||
N := priv.n
|
||||
c, err := bigmod.NewNat().SetBytes(ciphertext, N)
|
||||
if err != nil {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
// CRT-based decryption (same as regular decrypt, doesn't use E).
|
||||
P, Q := priv.p, priv.q
|
||||
t0 := bigmod.NewNat()
|
||||
// m = c ^ Dp mod p
|
||||
m := bigmod.NewNat().Exp(t0.Mod(c, P), priv.dP, P)
|
||||
// m2 = c ^ Dq mod q
|
||||
m2 := bigmod.NewNat().Exp(t0.Mod(c, Q), priv.dQ, Q)
|
||||
// m = m - m2 mod p
|
||||
m.Sub(t0.Mod(m2, P), P)
|
||||
// m = m * Qinv mod p
|
||||
m.Mul(priv.qInv, P)
|
||||
// m = m * q mod N
|
||||
m.ExpandFor(N).Mul(t0.Mod(Q.Nat(), N), N)
|
||||
// m = m + m2 mod N
|
||||
m.Add(m2.ExpandFor(N), N)
|
||||
|
||||
return m.Bytes(N), nil
|
||||
}
|
||||
|
||||
// TestingOnlyLargeExponentDecryptOAEP decrypts ciphertext using RSAES-OAEP with
|
||||
// a private key that has a large public exponent. It is only meant for ACVP testing.
|
||||
func TestingOnlyLargeExponentDecryptOAEP(hash, mgfHash hash.Hash, priv *TestingOnlyLargeExponentPrivateKey, ciphertext []byte, label []byte) ([]byte, error) {
|
||||
fipsSelfTest()
|
||||
fips140.RecordApproved()
|
||||
checkApprovedHash(hash)
|
||||
|
||||
k := priv.Size()
|
||||
if len(ciphertext) > k || k < hash.Size()*2+2 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
em, err := decryptLargeExponent(priv, ciphertext)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
hash.Reset()
|
||||
hash.Write(label)
|
||||
lHash := hash.Sum(nil)
|
||||
|
||||
firstByteIsZero := constanttime.ByteEq(em[0], 0)
|
||||
|
||||
seed := em[1 : hash.Size()+1]
|
||||
db := em[hash.Size()+1:]
|
||||
|
||||
mgf1XOR(seed, mgfHash, db)
|
||||
mgf1XOR(db, mgfHash, seed)
|
||||
|
||||
lHash2 := db[0:hash.Size()]
|
||||
|
||||
lHash2Good := subtle.ConstantTimeCompare(lHash, lHash2)
|
||||
|
||||
var lookingForIndex, index, invalid int
|
||||
lookingForIndex = 1
|
||||
rest := db[hash.Size():]
|
||||
|
||||
for i := 0; i < len(rest); i++ {
|
||||
equals0 := constanttime.ByteEq(rest[i], 0)
|
||||
equals1 := constanttime.ByteEq(rest[i], 1)
|
||||
index = constanttime.Select(lookingForIndex&equals1, i, index)
|
||||
lookingForIndex = constanttime.Select(equals1, 0, lookingForIndex)
|
||||
invalid = constanttime.Select(lookingForIndex&^equals0, 1, invalid)
|
||||
}
|
||||
|
||||
if firstByteIsZero&lHash2Good&^invalid&^lookingForIndex != 1 {
|
||||
return nil, ErrDecryption
|
||||
}
|
||||
|
||||
return rest[index+1:], nil
|
||||
}
|
||||
|
||||
// TestingOnlyLargeExponentEncryptOAEP encrypts the given message with RSAES-OAEP
|
||||
// using a public key with a large exponent. It is only meant for ACVP testing.
|
||||
func TestingOnlyLargeExponentEncryptOAEP(hash, mgfHash hash.Hash, random io.Reader, pub *TestingOnlyLargeExponentPublicKey, msg []byte, label []byte) ([]byte, error) {
|
||||
fipsSelfTest()
|
||||
fips140.RecordApproved()
|
||||
checkApprovedHash(hash)
|
||||
if err := checkLargeExponentPublicKey(pub); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
k := pub.Size()
|
||||
if len(msg) > k-2*hash.Size()-2 {
|
||||
return nil, ErrMessageTooLong
|
||||
}
|
||||
|
||||
hash.Reset()
|
||||
hash.Write(label)
|
||||
lHash := hash.Sum(nil)
|
||||
|
||||
em := make([]byte, k)
|
||||
seed := em[1 : 1+hash.Size()]
|
||||
db := em[1+hash.Size():]
|
||||
|
||||
copy(db[0:hash.Size()], lHash)
|
||||
db[len(db)-len(msg)-1] = 1
|
||||
copy(db[len(db)-len(msg):], msg)
|
||||
|
||||
if err := drbg.ReadWithReader(random, seed); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
|
||||
mgf1XOR(db, mgfHash, seed)
|
||||
mgf1XOR(seed, mgfHash, db)
|
||||
|
||||
return encryptLargeExponent(pub, em)
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue