Richard Boegli's CnC_Generals_Zero_Hour Fork WIP
This is documentation of Richard Boegil's Zero Hour Fork
 
Loading...
Searching...
No Matches
rsacrypt.h
Go to the documentation of this file.
1/*
2** Command & Conquer Generals Zero Hour(tm)
3** Copyright 2025 Electronic Arts Inc.
4**
5** This program is free software: you can redistribute it and/or modify
6** it under the terms of the GNU General Public License as published by
7** the Free Software Foundation, either version 3 of the License, or
8** (at your option) any later version.
9**
10** This program is distributed in the hope that it will be useful,
11** but WITHOUT ANY WARRANTY; without even the implied warranty of
12** MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13** GNU General Public License for more details.
14**
15** You should have received a copy of the GNU General Public License
16** along with this program. If not, see <http://www.gnu.org/licenses/>.
17*/
18
19#ifndef RSACRYPT_H
20#define RSACRYPT_H
21
22#include <wwlib/int.h>
23#include <wwlib/wwfile.h>
24
25//#define SIMPLE_AND_SLOW_RSA
26
27// Version identification string for OpenSSH identity files.
28#define AUTHFILE_ID_STRING "SSH PRIVATE KEY FILE FORMAT 1.1\n"
29
30//
31// RSACrypt - Implementation of RSA using the Int class from wwlib
32//
33// For key generation I suggest you use OpenSSH (http://www.openssh.com/)
34// There is a utility called "ssh-keygen", call:
35//
36// ssh-keygen [-b bits]
37//
38// If you generate the file on a UNIX system, be sure and FTP it in BINARY MODE!!!!
39//
40// You can then use 'Load_SSH_Keyset' on the identity file it produces.
41//
42// Note: Make sure you leave the keyphrase blank for the SSH keyset. I don't support
43// decrypting that file!
44//
45// Notation:
46// Public keys:
47// n = product of two primes, p & q (p & q must remain secret)
48// e = relatively prime to (p-1)(q-1)
49//
50// Private keys:
51// d = e^-1 mod ((p-1)(q-1)) // e^-1 = inverse of e
52//
53// Encrypting:
54// cyphertext = m^e mod n
55//
56// Decrypting:
57// message = cyphertext^d mod n
58//
59// Note: I use a trick involving the chinese remainder theorem to get a 3x speedup in decryption.
60// It's well documented on the web so I won't get into detail here.
61//
62template <int PRECISION>
64{
65 public:
67
70
71 void Set_Public_Keys(const Integer &pub_n, const Integer &pub_e);
72 void Set_Keys(const Integer &pub_n, const Integer &pub_e,
73 const Integer &priv_d, const Integer &keygen_p, const Integer &keygen_q);
74
75 void Get_Public_Keys(Integer &pub_n, Integer &pub_e) const;
76 void Get_Private_Key(Integer &priv_d) const;
77 void Get_Keygen_Keys(Integer &keygen_p, Integer &keygen_q) const;
78
79 bool Load_SSH_Keyset(FileClass *file);
80
81 void Encrypt(const Integer &plaintext, Integer &cyphertext) const;
82 void Decrypt(const Integer &cyphertext, Integer &plaintext) const;
83
84 private:
85 bool Load_Bignum(FileClass *file, Integer &num);
86
87 void Decryption_Setup(); // Do precomputation to speedup decryption
88
89 Integer PublicN;
90 Integer PublicE;
91
92 Integer PrivateD;
93
94 Integer KeygenP; // Primes P & Q generated as part of the keyset
95 Integer KeygenQ;
96
97 // Precomputed values that speed up encryption
98 Integer DmodPm1; // d mod p-1
99 Integer DmodQm1; // d mod q-1
100
101 Integer RP; // RP = q^(p-1) mod n
102 Integer RQ; // RQ = p^(q-1) mod n;
103};
104
105//
106// Set the two public keys: n & e
107//
108template <int PRECISION>
110{
111 PublicN=pub_n;
112 PublicE=pub_e;
113}
114
115//
116// Set the public & private keys: n, e & d
117//
118template <int PRECISION>
119void RSACrypt<PRECISION>::Set_Keys(const Integer &pub_n, const Integer &pub_e,
120 const Integer &priv_d, const Integer &keygen_p, const Integer &keygen_q)
121{
122 PublicN=pub_n;
123 PublicE=pub_e;
124 PrivateD=priv_d;
125
126 KeygenP=keygen_p;
127 KeygenQ=keygen_q;
128
129 Decrtyption_Setup();
130}
131
132//
133// Get the public keys
134//
135template <int PRECISION>
137{
138 pub_n=PublicN;
139 pub_e=PublicE;
140}
141
142//
143// Get the private key
144//
145template <int PRECISION>
147{
148 priv_d=PrivateD;
149}
150
151
152//
153// Get the private numbers created during the keyset generation
154// Private as in revealing these will reveal the private key!
155//
156template <int PRECISION>
158{
159 keygen_p=KeygenP;
160 keygen_q=KeygenQ;
161}
162
163
164//
165// Load an RSA private keyset from an OpenSSH "identity" file.
166//
167template <int PRECISION>
169{
170 assert(file);
171 if ( ! file)
172 return(false);
173
174 bool retval=true;
175 unsigned char buffer[1024];
176
177 if ( ! file->Open())
178 return(false);
179
180 file->Read(buffer, strlen(AUTHFILE_ID_STRING)+1);
181 buffer[strlen(AUTHFILE_ID_STRING)]=0; // null term
182
183 if (strcmp((char *)buffer, AUTHFILE_ID_STRING))
184 return(false);
185
186 unsigned char cypher_type; // keyfile encryption method
187 file->Read(&cypher_type, 1);
188 if (cypher_type != 0)
189 return(false);
190
191 file->Read(buffer, 4); // reserved data
192
193 file->Read(buffer, 4); // ignored
194
195 retval=retval && Load_Bignum(file, PublicN);
196 retval=retval && Load_Bignum(file, PublicE);
197 if (!retval)
198 return(false);
199
200 // comment string
201 int comment_length;
202 file->Read(&comment_length, 4);
203 comment_length=ntohl(comment_length);
204
205 file->Read(buffer, comment_length);
206
207 // post-decrypt check chars
208 file->Read(buffer, 4);
209 if ((buffer[0] != buffer[2]) || (buffer[1] != buffer[3]))
210 return(false);
211
212 Integer q_inv_mod_p; // invserse of q mod p (we don't need this)
213
214 retval=retval && Load_Bignum(file, PrivateD);
215 retval=retval && Load_Bignum(file, q_inv_mod_p);
216 retval=retval && Load_Bignum(file, KeygenP);
217 retval=retval && Load_Bignum(file, KeygenQ);
218 if (!retval)
219 return(false);
220
221 // Any remaining bytes are padding
222
223 Decryption_Setup();
224
225 return(true);
226}
227
228//
229// Precomputation for fast decryption
230//
231template <int PRECISION>
232void RSACrypt<PRECISION>::Decryption_Setup(void)
233{
234 Integer temp;
235
236 // If p < q, swap p & q
237 if (KeygenP < KeygenQ)
238 {
239 temp=KeygenP;
240 KeygenP=KeygenQ;
241 KeygenQ=temp;
242 }
243
244 assert(KeygenP > KeygenQ);
245
246 // pm1 = p - 1
247 Integer pm1(KeygenP);
248 --pm1;
249
250 // pm1 = p - 1
251 Integer qm1(KeygenQ);
252 --qm1;
253
254 // DmodPm1 = d mod (p-1)
255 Integer::Unsigned_Divide(DmodPm1, temp, PrivateD, pm1);
256 assert(DmodPm1 < pm1);
257
258 // DmodQm1 = d mod (q-1)
259 Integer::Unsigned_Divide(DmodQm1, temp, PrivateD, qm1);
260 assert(DmodQm1 < qm1);
261
262 RP = KeygenQ.exp_b_mod_c(pm1, PublicN);
263 RQ = KeygenP.exp_b_mod_c(qm1, PublicN);
264}
265
266
267//
268// RSA Encryption c = m^e mod n
269//
270template <int PRECISION>
271void RSACrypt<PRECISION>::Encrypt(const Integer &plaintext, Integer &cyphertext) const
272{
273 Integer m(plaintext);
274 cyphertext=m.exp_b_mod_c(PublicE, PublicN);
275}
276
277
278//
279// RSA Decryption m = c^d mod n
280//
281template <int PRECISION>
282void RSACrypt<PRECISION>::Decrypt(const Integer &cyphertext, Integer &plaintext) const
283{
284#ifdef SIMPLE_AND_SLOW_RSA
285 Integer c(cyphertext);
286 plaintext=c.exp_b_mod_c(PrivateD, PublicN);
287#else
288 Integer temp;
289
290 // Get a version of the cyphertext mod q & p
291 Integer cmp, cmq;
292 Integer::Unsigned_Divide(cmp, temp, cyphertext, KeygenP);
293 Integer::Unsigned_Divide(cmq, temp, cyphertext, KeygenQ);
294
295 // mp = cmp ^ dmp mod p
296 Integer mp;
297 mp=cmp.exp_b_mod_c(DmodPm1, KeygenP);
298
299 // mq = cmq ^ dmq mod q
300 Integer mq;
301 mq=cmq.exp_b_mod_c(DmodQm1, KeygenQ);
302
303
304 Integer sp, sq;
305
306 //sp=mp * RP mod n;
307 //sq=mq * RQ mod n;
308 XMP_Prepare_Modulus(&PublicN.reg[0], PRECISION);
309 XMP_Mod_Mult(&sp.reg[0], &mp.reg[0], &RP.reg[0], PRECISION);
310 XMP_Mod_Mult(&sq.reg[0], &mq.reg[0], &RQ.reg[0], PRECISION);
311 XMP_Mod_Mult_Clear(PRECISION);
312
313 plaintext = sp+sq;
314 if (plaintext >= PublicN)
315 plaintext-=PublicN;
316
317#endif
318}
319
320
322
323//
324// Load a large number from the SSH keyset file
325//
326template <int PRECISION>
327bool RSACrypt<PRECISION>::Load_Bignum(FileClass *file, Integer &num)
328{
329 int readlen;
330 unsigned char buffer[1024];
331 unsigned short int n_bits, n_bytes;
332
333 readlen=file->Read(&n_bits, 2); // bits in network byte order
334 if (readlen != 2)
335 return(false);
336 n_bits=ntohs(n_bits);
337 n_bytes=(n_bits+7)/8;
338
339 readlen=file->Read(buffer, n_bytes);
340 if (readlen != n_bytes)
341 return(false);
342
343 num.Unsigned_Decode(buffer, n_bytes);
344
345 return(true);
346}
347
348
349#endif
virtual int Read(void *buffer, int size)=0
virtual int Open(char const *filename, int rights=READ)=0
void Get_Public_Keys(Integer &pub_n, Integer &pub_e) const
Definition rsacrypt.h:136
Int< PRECISION > Integer
Definition rsacrypt.h:66
void Set_Public_Keys(const Integer &pub_n, const Integer &pub_e)
Definition rsacrypt.h:109
RSACrypt()
Definition rsacrypt.h:68
void Decrypt(const Integer &cyphertext, Integer &plaintext) const
Definition rsacrypt.h:282
void Get_Keygen_Keys(Integer &keygen_p, Integer &keygen_q) const
Definition rsacrypt.h:157
void Set_Keys(const Integer &pub_n, const Integer &pub_e, const Integer &priv_d, const Integer &keygen_p, const Integer &keygen_q)
Definition rsacrypt.h:119
bool Load_SSH_Keyset(FileClass *file)
Definition rsacrypt.h:168
~RSACrypt()
Definition rsacrypt.h:69
void Encrypt(const Integer &plaintext, Integer &cyphertext) const
Definition rsacrypt.h:271
void Get_Private_Key(Integer &priv_d) const
Definition rsacrypt.h:146
int MPEXPORT XMP_Mod_Mult(digit *prod, const digit *multiplicand, const digit *multiplier, int precision)
Definition mpmath.cpp:1966
int XMP_Prepare_Modulus(const digit *n_modulus, int precision)
Definition mpmath.cpp:1908
void MPEXPORT XMP_Mod_Mult_Clear(int precision)
Definition mpmath.cpp:2057
#define AUTHFILE_ID_STRING
Definition rsacrypt.h:28
Int exp_b_mod_c(const Int &e, const Int &m) const
Definition INT.H:159
static void Unsigned_Divide(Int &remainder, Int &quotient, const Int &dividend, const Int &divisor)
Definition INT.H:167
digit reg[PRECISION]
Definition INT.H:198