summary refs log tree commit diff
path: root/crypto/src/crypto/engines/RSACoreEngine.cs
blob: ffa448b3d82ff1c7d450ac483a916d6ed6d6f3d4 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
using System;

using Org.BouncyCastle.Crypto.Parameters;
using Org.BouncyCastle.Math;
using Org.BouncyCastle.Security;
using Org.BouncyCastle.Utilities;

namespace Org.BouncyCastle.Crypto.Engines
{
	/**
	* this does your basic RSA algorithm.
	*/
	public class RsaCoreEngine
        : IRsa
	{
		private RsaKeyParameters	key;
		private bool				forEncryption;
		private int					bitSize;

        private void CheckInitialised()
        {
            if (key == null)
                throw new InvalidOperationException("RSA engine not initialised");
        }

        /**
		* initialise the RSA engine.
		*
		* @param forEncryption true if we are encrypting, false otherwise.
		* @param param the necessary RSA key parameters.
		*/
        public virtual void Init(
			bool				forEncryption,
			ICipherParameters	parameters)
		{
			if (parameters is ParametersWithRandom withRandom)
			{
				parameters = withRandom.Parameters;
			}

			if (!(parameters is RsaKeyParameters rsaKeyParameters))
				throw new InvalidKeyException("Not an RSA key");

			this.key = rsaKeyParameters;
			this.forEncryption = forEncryption;
			this.bitSize = key.Modulus.BitLength;
		}

		/**
		* Return the maximum size for an input block to this engine.
		* For RSA this is always one byte less than the key size on
		* encryption, and the same length as the key size on decryption.
		*
		* @return maximum size for an input block.
		*/
        public virtual int GetInputBlockSize()
		{
            CheckInitialised();

			if (forEncryption)
			{
				return (bitSize - 1) / 8;
			}

			return (bitSize + 7) / 8;
		}

		/**
		* Return the maximum size for an output block to this engine.
		* For RSA this is always one byte less than the key size on
		* decryption, and the same length as the key size on encryption.
		*
		* @return maximum size for an output block.
		*/
        public virtual int GetOutputBlockSize()
		{
            CheckInitialised();

            if (forEncryption)
			{
				return (bitSize + 7) / 8;
			}

			return (bitSize - 1) / 8;
		}

        public virtual BigInteger ConvertInput(
			byte[]	inBuf,
			int		inOff,
			int		inLen)
		{
            CheckInitialised();

            int maxLength = (bitSize + 7) / 8;

			if (inLen > maxLength)
				throw new DataLengthException("input too large for RSA cipher.");

			BigInteger input = new BigInteger(1, inBuf, inOff, inLen);

			if (input.CompareTo(key.Modulus) >= 0)
				throw new DataLengthException("input too large for RSA cipher.");

			return input;
		}

        public virtual byte[] ConvertOutput(BigInteger result)
		{
            CheckInitialised();

			return forEncryption
				? BigIntegers.AsUnsignedByteArray(GetOutputBlockSize(), result)
				: BigIntegers.AsUnsignedByteArray(result);
		}

        public virtual BigInteger ProcessBlock(
			BigInteger input)
		{
            CheckInitialised();

            if (key is RsaPrivateCrtKeyParameters crt)
			{
				//
				// we have the extra factors, use the Chinese Remainder Theorem - the author
				// wishes to express his thanks to Dirk Bonekaemper at rtsffm.com for
				// advice regarding the expression of this.
				//
				BigInteger p = crt.P;
				BigInteger q = crt.Q;
				BigInteger dP = crt.DP;
				BigInteger dQ = crt.DQ;
				BigInteger qInv = crt.QInv;

				// mP = ((input Mod p) ^ dP)) Mod p
				BigInteger mP = (input.Remainder(p)).ModPow(dP, p);

                // mQ = ((input Mod q) ^ dQ)) Mod q
                BigInteger mQ = (input.Remainder(q)).ModPow(dQ, q);

				// h = qInv * (mP - mQ) Mod p
				BigInteger h = mP.Subtract(mQ).Multiply(qInv).Mod(p);

                // m = h * q + mQ
                return h.Multiply(q).Add(mQ);
			}

			return input.ModPow(key.Exponent, key.Modulus);
		}
	}
}