Coverage Report

Created: 2023-09-25 06:56

/src/FreeRDP/libfreerdp/crypto/crypto.c
Line
Count
Source (jump to first uncovered line)
1
/**
2
 * FreeRDP: A Remote Desktop Protocol Implementation
3
 * Cryptographic Abstraction Layer
4
 *
5
 * Copyright 2011-2012 Marc-Andre Moreau <marcandre.moreau@gmail.com>
6
 * Copyright 2023 Armin Novak <anovak@thincast.com>
7
 * Copyright 2023 Thincast Technologies GmbH
8
 *
9
 * Licensed under the Apache License, Version 2.0 (the "License");
10
 * you may not use this file except in compliance with the License.
11
 * You may obtain a copy of the License at
12
 *
13
 *   http://www.apache.org/licenses/LICENSE-2.0
14
 *
15
 * Unless required by applicable law or agreed to in writing, software
16
 * distributed under the License is distributed on an "AS IS" BASIS,
17
 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
18
 * See the License for the specific language governing permissions and
19
 * limitations under the License.
20
 */
21
22
#include <errno.h>
23
24
#include <openssl/objects.h>
25
26
#include <freerdp/config.h>
27
28
#include <winpr/crt.h>
29
#include <winpr/assert.h>
30
31
#include <freerdp/log.h>
32
#include <freerdp/crypto/crypto.h>
33
34
#include "crypto.h"
35
#include "privatekey.h"
36
37
#define TAG FREERDP_TAG("crypto")
38
39
static SSIZE_T crypto_rsa_common(const BYTE* input, size_t length, UINT32 key_length,
40
                                 const BYTE* modulus, const BYTE* exponent, size_t exponent_size,
41
                                 BYTE* output, size_t out_length)
42
0
{
43
0
  BN_CTX* ctx = NULL;
44
0
  int output_length = -1;
45
0
  BYTE* input_reverse = NULL;
46
0
  BYTE* modulus_reverse = NULL;
47
0
  BYTE* exponent_reverse = NULL;
48
0
  BIGNUM* mod = NULL;
49
0
  BIGNUM* exp = NULL;
50
0
  BIGNUM* x = NULL;
51
0
  BIGNUM* y = NULL;
52
0
  size_t bufferSize = 0;
53
54
0
  if (!input || !modulus || !exponent || !output)
55
0
    return -1;
56
57
0
  if ((size_t)exponent_size > INT_MAX / 2)
58
0
    return -1;
59
60
0
  if (key_length >= INT_MAX / 2 - exponent_size)
61
0
    return -1;
62
63
0
  bufferSize = 2ULL * key_length + exponent_size;
64
0
  if ((size_t)length > bufferSize)
65
0
    bufferSize = (size_t)length;
66
67
0
  input_reverse = (BYTE*)calloc(bufferSize, 1);
68
69
0
  if (!input_reverse)
70
0
    return -1;
71
72
0
  modulus_reverse = input_reverse + key_length;
73
0
  exponent_reverse = modulus_reverse + key_length;
74
0
  memcpy(modulus_reverse, modulus, key_length);
75
0
  crypto_reverse(modulus_reverse, key_length);
76
0
  memcpy(exponent_reverse, exponent, exponent_size);
77
0
  crypto_reverse(exponent_reverse, exponent_size);
78
0
  memcpy(input_reverse, input, length);
79
0
  crypto_reverse(input_reverse, length);
80
81
0
  if (!(ctx = BN_CTX_new()))
82
0
    goto fail;
83
84
0
  if (!(mod = BN_new()))
85
0
    goto fail;
86
87
0
  if (!(exp = BN_new()))
88
0
    goto fail;
89
90
0
  if (!(x = BN_new()))
91
0
    goto fail;
92
93
0
  if (!(y = BN_new()))
94
0
    goto fail;
95
96
0
  if (!BN_bin2bn(modulus_reverse, key_length, mod))
97
0
    goto fail;
98
99
0
  if (!BN_bin2bn(exponent_reverse, exponent_size, exp))
100
0
    goto fail;
101
0
  if (!BN_bin2bn(input_reverse, length, x))
102
0
    goto fail;
103
0
  if (BN_mod_exp(y, x, exp, mod, ctx) != 1)
104
0
    goto fail;
105
0
  output_length = BN_bn2bin(y, output);
106
0
  if (output_length < 0)
107
0
    goto fail;
108
0
  if ((size_t)output_length > out_length)
109
0
    goto fail;
110
0
  crypto_reverse(output, output_length);
111
112
0
  if ((size_t)output_length < key_length)
113
0
  {
114
0
    size_t diff = key_length - output_length;
115
0
    if ((size_t)output_length + diff > out_length)
116
0
      diff = out_length - (size_t)output_length;
117
0
    memset(output + output_length, 0, diff);
118
0
  }
119
120
0
fail:
121
0
  BN_free(y);
122
0
  BN_clear_free(x);
123
0
  BN_free(exp);
124
0
  BN_free(mod);
125
0
  BN_CTX_free(ctx);
126
0
  free(input_reverse);
127
0
  return output_length;
128
0
}
129
130
static SSIZE_T crypto_rsa_public(const BYTE* input, size_t length, const rdpCertInfo* cert,
131
                                 BYTE* output, size_t output_length)
132
0
{
133
0
  WINPR_ASSERT(cert);
134
0
  return crypto_rsa_common(input, length, cert->ModulusLength, cert->Modulus, cert->exponent,
135
0
                           sizeof(cert->exponent), output, output_length);
136
0
}
137
138
static SSIZE_T crypto_rsa_private(const BYTE* input, size_t length, const rdpPrivateKey* key,
139
                                  BYTE* output, size_t output_length)
140
0
{
141
0
  WINPR_ASSERT(key);
142
0
  const rdpCertInfo* info = freerdp_key_get_info(key);
143
0
  WINPR_ASSERT(info);
144
145
0
  size_t PrivateExponentLength = 0;
146
0
  const BYTE* PrivateExponent = freerdp_key_get_exponent(key, &PrivateExponentLength);
147
0
  return crypto_rsa_common(input, length, info->ModulusLength, info->Modulus, PrivateExponent,
148
0
                           PrivateExponentLength, output, output_length);
149
0
}
150
151
SSIZE_T crypto_rsa_public_encrypt(const BYTE* input, size_t length, const rdpCertInfo* cert,
152
                                  BYTE* output, size_t output_length)
153
0
{
154
0
  return crypto_rsa_public(input, length, cert, output, output_length);
155
0
}
156
157
SSIZE_T crypto_rsa_public_decrypt(const BYTE* input, size_t length, const rdpCertInfo* cert,
158
                                  BYTE* output, size_t output_length)
159
0
{
160
0
  return crypto_rsa_public(input, length, cert, output, output_length);
161
0
}
162
163
SSIZE_T crypto_rsa_private_encrypt(const BYTE* input, size_t length, const rdpPrivateKey* key,
164
                                   BYTE* output, size_t output_length)
165
0
{
166
0
  return crypto_rsa_private(input, length, key, output, output_length);
167
0
}
168
169
SSIZE_T crypto_rsa_private_decrypt(const BYTE* input, size_t length, const rdpPrivateKey* key,
170
                                   BYTE* output, size_t output_length)
171
0
{
172
0
  return crypto_rsa_private(input, length, key, output, output_length);
173
0
}
174
175
void crypto_reverse(BYTE* data, size_t length)
176
0
{
177
0
  size_t i, j;
178
179
0
  if (length < 1)
180
0
    return;
181
182
0
  for (i = 0, j = length - 1; i < j; i++, j--)
183
0
  {
184
0
    const BYTE temp = data[i];
185
0
    data[i] = data[j];
186
0
    data[j] = temp;
187
0
  }
188
0
}
189
190
char* crypto_read_pem(const char* filename, size_t* plength)
191
0
{
192
0
  char* pem = NULL;
193
0
  FILE* fp = NULL;
194
195
0
  WINPR_ASSERT(filename);
196
197
0
  if (plength)
198
0
    *plength = 0;
199
200
0
  fp = winpr_fopen(filename, "r");
201
0
  if (!fp)
202
0
    goto fail;
203
0
  const int rs = _fseeki64(fp, 0, SEEK_END);
204
0
  if (rs < 0)
205
0
    goto fail;
206
0
  const SSIZE_T size = _ftelli64(fp);
207
0
  if (size < 0)
208
0
    goto fail;
209
0
  const int rc = _fseeki64(fp, 0, SEEK_SET);
210
0
  if (rc < 0)
211
0
    goto fail;
212
213
0
  pem = calloc(size + 1, sizeof(char));
214
0
  if (!pem)
215
0
    goto fail;
216
217
0
  const size_t fr = fread(pem, (size_t)size, 1, fp);
218
0
  if (fr != 1)
219
0
    goto fail;
220
221
0
  if (plength)
222
0
    *plength = (size_t)strnlen(pem, size);
223
0
  fclose(fp);
224
0
  return pem;
225
226
0
fail:
227
0
{
228
0
  char buffer[8192] = { 0 };
229
0
  WLog_WARN(TAG, "Failed to read PEM from file '%s' [%s]", filename,
230
0
            winpr_strerror(errno, buffer, sizeof(buffer)));
231
0
}
232
0
  if (fp)
233
0
    fclose(fp);
234
0
  free(pem);
235
0
  return NULL;
236
0
}
237
238
BOOL crypto_write_pem(const char* filename, const char* pem, size_t length)
239
0
{
240
0
  WINPR_ASSERT(filename);
241
0
  WINPR_ASSERT(pem || (length == 0));
242
243
0
  WINPR_ASSERT(filename);
244
0
  WINPR_ASSERT(pem);
245
246
0
  const size_t size = strnlen(pem, length) + 1;
247
0
  size_t rc = 0;
248
0
  FILE* fp = winpr_fopen(filename, "w");
249
0
  if (!fp)
250
0
    goto fail;
251
0
  rc = fwrite(pem, 1, size, fp);
252
0
  fclose(fp);
253
0
fail:
254
0
  if (rc == 0)
255
0
  {
256
0
    char buffer[8192] = { 0 };
257
0
    WLog_WARN(TAG, "Failed to write PEM [%" PRIuz "] to file '%s' [%s]", length, filename,
258
0
              winpr_strerror(errno, buffer, sizeof(buffer)));
259
0
  }
260
0
  return rc == size;
261
0
}