// Copyright 2026 The Go Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.

package rsa

import (
	
	
	
	
	
	
	
	
	
)

// TestingOnlyLargeExponentPublicKey is a variant of [PublicKey] that supports
// large public exponents. It is only meant for supporting the full ACVP test
// suite, which unfortunately forces us to choose between a fixed exponent and
// the full (2¹⁶, 2²⁵⁶) range. This type must not be used in production code,
// nor for e values < 2³¹, which instead must use [PublicKey].
type TestingOnlyLargeExponentPublicKey struct {
	N *bigmod.Modulus
	// E is the public exponent, represented as a big-endian byte slice.
	E []byte
}

func ( *TestingOnlyLargeExponentPublicKey) () int {
	return (.N.BitLen() + 7) / 8
}

func ( *TestingOnlyLargeExponentPublicKey) error {
	if .N == nil {
		return errors.New("crypto/rsa: missing public modulus")
	}
	if .N.Nat().IsOdd() == 0 {
		return errors.New("crypto/rsa: public modulus is even")
	}
	if .N.BitLen() < 2048 {
		return errors.New("crypto/rsa: public modulus too small")
	}
	if .N.BitLen()%2 == 1 {
		return errors.New("crypto/rsa: public modulus bit length not even")
	}
	 := .E
	for len() > 0 && [0] == 0 {
		 = [1:]
	}
	if len() < 32/8 || (len() == 32/8 && [0] < 0x80) {
		// Exponents less than 2^31 must use [PublicKey].
		return errors.New("crypto/rsa: public exponent too small")
	}
	if len() > 256/8 {
		return errors.New("crypto/rsa: public exponent too large")
	}
	if [len()-1]&1 == 0 {
		return errors.New("crypto/rsa: public exponent is even")
	}
	return nil
}

func ( *TestingOnlyLargeExponentPublicKey,  []byte) ([]byte, error) {
	,  := bigmod.NewNat().SetBytes(, .N)
	if  != nil {
		return nil, 
	}
	return bigmod.NewNat().Exp(, .E, .N).Bytes(.N), nil
}

func ( *TestingOnlyLargeExponentPublicKey,  string,  []byte,  []byte) error {
	fipsSelfTest()
	fips140.RecordApproved()
	checkApprovedHashName()
	if  := checkLargeExponentPublicKey();  != nil {
		return 
	}
	if .Size() != len() {
		return ErrVerification
	}
	,  := encryptLargeExponent(, )
	if  != nil {
		return ErrVerification
	}
	,  := pkcs1v15ConstructEM(&PublicKey{N: .N}, , )
	if  != nil {
		return ErrVerification
	}
	if !bytes.Equal(, ) {
		return ErrVerification
	}
	return nil
}

func ( *TestingOnlyLargeExponentPublicKey,  hash.Hash,  []byte,  []byte) error {
	fipsSelfTest()
	fips140.RecordApproved()
	checkApprovedHash()
	if  := checkLargeExponentPublicKey();  != nil {
		return 
	}
	if len() != .Size() {
		return ErrVerification
	}
	 := .N.BitLen() - 1
	 := ( + 7) / 8
	,  := encryptLargeExponent(, )
	if  != nil {
		return ErrVerification
	}
	for len() >  && len() > 0 {
		if [0] != 0 {
			return ErrVerification
		}
		 = [1:]
	}
	return emsaPSSVerify(, , , pssSaltLengthAutodetect, )
}

// 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 ( *TestingOnlyLargeExponentPrivateKey) () int {
	return (.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 ( []byte,  []byte, , , , , ,  []byte) (*TestingOnlyLargeExponentPrivateKey, error) {
	,  := bigmod.NewModulus()
	if  != nil {
		return nil, 
	}
	,  := bigmod.NewModulus()
	if  != nil {
		return nil, 
	}
	,  := bigmod.NewModulus()
	if  != nil {
		return nil, 
	}
	,  := bigmod.NewNat().SetBytes(, )
	if  != nil {
		return nil, 
	}
	,  := bigmod.NewNat().SetBytes(, )
	if  != nil {
		return nil, 
	}

	 := &TestingOnlyLargeExponentPrivateKey{
		n: , e: , d: , p: , q: ,
		dP: , dQ: , qInv: ,
	}
	if  := checkLargeExponentPrivateKey();  != nil {
		return nil, 
	}
	return , nil
}

func ( *TestingOnlyLargeExponentPrivateKey) error {
	// Check public key portion.
	 := &TestingOnlyLargeExponentPublicKey{N: .n, E: .e}
	if  := checkLargeExponentPublicKey();  != nil {
		return 
	}

	 := .n
	 := .p
	 := .q

	// FIPS 186-5, Section 5.1 requires "that p and q be of the same bit length."
	if .BitLen() != .BitLen() {
		// We don't enforce this for testing, just note it.
	}

	// Check that pq ≡ 1 mod N (and that p < N and q < N).
	 := bigmod.NewNat().ExpandFor()
	if ,  := .SetBytes(.Nat().Bytes(), );  != nil {
		return errors.New("crypto/rsa: invalid prime")
	}
	 := bigmod.NewNat().ExpandFor()
	if ,  := .SetBytes(.Nat().Bytes(), );  != nil {
		return errors.New("crypto/rsa: invalid prime")
	}
	if .Mul(, ).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.
	,  := bigmod.NewModulus(.Nat().SubOne().Bytes())
	if  != nil {
		return errors.New("crypto/rsa: invalid prime")
	}
	,  := bigmod.NewNat().SetBytes(.dP, )
	if  != nil {
		return errors.New("crypto/rsa: invalid CRT exponent")
	}
	 := bigmod.NewNat()
	if ,  := .SetBytes(.e, );  != nil {
		// Exponent might be larger than p-1, reduce it.
		,  := bigmod.NewNat().SetBytes(.e, .n)
		.Mod(, )
	}
	.Mul(, )
	if .IsOne() != 1 {
		return errors.New("crypto/rsa: invalid CRT exponent")
	}

	,  := bigmod.NewModulus(.Nat().SubOne().Bytes())
	if  != nil {
		return errors.New("crypto/rsa: invalid prime")
	}
	,  := bigmod.NewNat().SetBytes(.dQ, )
	if  != nil {
		return errors.New("crypto/rsa: invalid CRT exponent")
	}
	if ,  := .SetBytes(.e, );  != nil {
		// Exponent might be larger than q-1, reduce it.
		,  := bigmod.NewNat().SetBytes(.e, .n)
		.Mod(, )
	}
	.Mul(, )
	if .IsOne() != 1 {
		return errors.New("crypto/rsa: invalid CRT exponent")
	}

	// Check that qInv * q ≡ 1 mod p.
	,  := bigmod.NewNat().SetOverflowingBytes(.Nat().Bytes(), )
	if  != nil {
		// q >= 2^⌈log2(p)⌉
		 = bigmod.NewNat().Mod(.Nat(), )
	}
	if .Mul(.qInv, ).IsOne() != 1 {
		return errors.New("crypto/rsa: invalid CRT coefficient")
	}

	return nil
}

func ( *TestingOnlyLargeExponentPrivateKey,  []byte) ([]byte, error) {
	 := .n
	,  := bigmod.NewNat().SetBytes(, )
	if  != nil {
		return nil, ErrDecryption
	}

	// CRT-based decryption (same as regular decrypt, doesn't use E).
	,  := .p, .q
	 := bigmod.NewNat()
	// m = c ^ Dp mod p
	 := bigmod.NewNat().Exp(.Mod(, ), .dP, )
	// m2 = c ^ Dq mod q
	 := bigmod.NewNat().Exp(.Mod(, ), .dQ, )
	// m = m - m2 mod p
	.Sub(.Mod(, ), )
	// m = m * Qinv mod p
	.Mul(.qInv, )
	// m = m * q mod N
	.ExpandFor().Mul(.Mod(.Nat(), ), )
	// m = m + m2 mod N
	.Add(.ExpandFor(), )

	return .Bytes(), 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 (,  hash.Hash,  *TestingOnlyLargeExponentPrivateKey,  []byte,  []byte) ([]byte, error) {
	fipsSelfTest()
	fips140.RecordApproved()
	checkApprovedHash()

	 := .Size()
	if len() >  ||  < .Size()*2+2 {
		return nil, ErrDecryption
	}

	,  := decryptLargeExponent(, )
	if  != nil {
		return nil, 
	}

	.Reset()
	.Write()
	 := .Sum(nil)

	 := constanttime.ByteEq([0], 0)

	 := [1 : .Size()+1]
	 := [.Size()+1:]

	mgf1XOR(, , )
	mgf1XOR(, , )

	 := [0:.Size()]

	 := subtle.ConstantTimeCompare(, )

	var , ,  int
	 = 1
	 := [.Size():]

	for  := 0;  < len(); ++ {
		 := constanttime.ByteEq([], 0)
		 := constanttime.ByteEq([], 1)
		 = constanttime.Select(&, , )
		 = constanttime.Select(, 0, )
		 = constanttime.Select(&^, 1, )
	}

	if &&^&^ != 1 {
		return nil, ErrDecryption
	}

	return [+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 (,  hash.Hash,  io.Reader,  *TestingOnlyLargeExponentPublicKey,  []byte,  []byte) ([]byte, error) {
	fipsSelfTest()
	fips140.RecordApproved()
	checkApprovedHash()
	if  := checkLargeExponentPublicKey();  != nil {
		return nil, 
	}
	 := .Size()
	if len() > -2*.Size()-2 {
		return nil, ErrMessageTooLong
	}

	.Reset()
	.Write()
	 := .Sum(nil)

	 := make([]byte, )
	 := [1 : 1+.Size()]
	 := [1+.Size():]

	copy([0:.Size()], )
	[len()-len()-1] = 1
	copy([len()-len():], )

	if  := drbg.ReadWithReader(, );  != nil {
		return nil, 
	}

	mgf1XOR(, , )
	mgf1XOR(, , )

	return encryptLargeExponent(, )
}