summary refs log tree commit diff
path: root/crypto/src/crypto/engines/RSABlindedEngine.cs
blob: 63b8bbf696fb55ad4a17b2273d5a30d653de7a74 (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
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 with blinding
     */
    public class RsaBlindedEngine
        : IAsymmetricBlockCipher
    {
        private readonly IRsa core;

        private RsaKeyParameters key;
        private SecureRandom random;

        public RsaBlindedEngine()
            : this(new RsaCoreEngine())
        {
        }

        public RsaBlindedEngine(IRsa rsa)
        {
            this.core = rsa;
        }

        public virtual string AlgorithmName
        {
            get { return "RSA"; }
        }

        /**
         * 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 param)
        {
            SecureRandom providedRandom = null;
            if (param is ParametersWithRandom withRandom)
            {
                providedRandom = withRandom.Random;
                param = withRandom.Parameters;
            }

            core.Init(forEncryption, param);

            this.key = (RsaKeyParameters)param;
            this.random = InitSecureRandom(needed: key is RsaPrivateCrtKeyParameters, providedRandom);
        }

        /**
         * 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()
        {
            return core.GetInputBlockSize();
        }

        /**
         * 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()
        {
            return core.GetOutputBlockSize();
        }

        /**
         * Process a single block using the basic RSA algorithm.
         *
         * @param inBuf the input array.
         * @param inOff the offset into the input buffer where the data starts.
         * @param inLen the length of the data to be processed.
         * @return the result of the RSA process.
         * @exception DataLengthException the input block is too large.
         */
        public virtual byte[] ProcessBlock(byte[] inBuf, int inOff, int inLen)
        {
            if (key == null)
                throw new InvalidOperationException("RSA engine not initialised");

            BigInteger input = core.ConvertInput(inBuf, inOff, inLen);
            BigInteger result = ProcessInput(input);
            return core.ConvertOutput(result);
        }

        protected virtual SecureRandom InitSecureRandom(bool needed, SecureRandom provided)
        {
            return needed ? CryptoServicesRegistrar.GetSecureRandom(provided) : null;
        }

        private BigInteger ProcessInput(BigInteger input)
        {
            if (!(key is RsaPrivateCrtKeyParameters crt))
                return core.ProcessBlock(input);

            BigInteger e = crt.PublicExponent;
            BigInteger m = crt.Modulus;

            BigInteger r = BigIntegers.CreateRandomInRange(BigInteger.One, m.Subtract(BigInteger.One), random);
            BigInteger blind = r.ModPow(e, m);
            BigInteger unblind = BigIntegers.ModOddInverse(m, r);

            BigInteger blindedInput = blind.Multiply(input).Mod(m);
            BigInteger blindedResult = core.ProcessBlock(blindedInput);
            return unblind.Multiply(blindedResult).Mod(m);
        }
    }
}