Add log level and log callback support to the RAOP API
[deb_shairplay.git] / src / lib / rsakey.c
CommitLineData
23e7e3ae
JVH
1/**
2 * Copyright (C) 2011-2012 Juho Vähä-Herttua
3 *
4 * This library is free software; you can redistribute it and/or
5 * modify it under the terms of the GNU Lesser General Public
6 * License as published by the Free Software Foundation; either
7 * version 2.1 of the License, or (at your option) any later version.
8 *
9 * This library is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * Lesser General Public License for more details.
13 */
14
2340bcd3
JVH
15#include <stdlib.h>
16#include <string.h>
17#include <stdint.h>
18#include <assert.h>
19
20#include "rsakey.h"
21#include "rsapem.h"
22#include "base64.h"
23#include "crypto/crypto.h"
24
25#define RSA_MIN_PADLEN 8
26#define MAX_KEYLEN 512
27
28struct rsakey_s {
29 int keylen; /* length of modulus in bytes */
30 BI_CTX *bi_ctx; /* bigint context */
31
32 bigint *n; /* modulus */
33 bigint *e; /* public exponent */
34 bigint *d; /* private exponent */
35
36 int use_crt; /* use chinese remainder theorem */
37 bigint *p; /* p as in m = pq */
38 bigint *q; /* q as in m = pq */
39 bigint *dP; /* d mod (p-1) */
40 bigint *dQ; /* d mod (q-1) */
41 bigint *qInv; /* q^-1 mod p */
42
43 base64_t *base64;
44};
45
46rsakey_t *
47rsakey_init(const unsigned char *modulus, int mod_len,
48 const unsigned char *pub_exp, int pub_len,
49 const unsigned char *priv_exp, int priv_len,
50 /* Optional, used for crt optimization */
51 const unsigned char *p, int p_len,
52 const unsigned char *q, int q_len,
53 const unsigned char *dP, int dP_len,
54 const unsigned char *dQ, int dQ_len,
55 const unsigned char *qInv, int qInv_len)
56{
57 rsakey_t *rsakey;
58 int i;
59
60 if (mod_len > MAX_KEYLEN) {
61 return NULL;
62 }
63
64 rsakey = calloc(1, sizeof(rsakey_t));
65 if (!rsakey) {
66 return NULL;
67 }
68 rsakey->base64 = base64_init(NULL, 0, 0);
69 if (!rsakey->base64) {
70 free(rsakey);
71 return NULL;
72 }
73
74 /* Initialize structure */
75 for (i=0; !modulus[i] && i<mod_len; i++);
76 rsakey->keylen = mod_len-i;
77 rsakey->bi_ctx = bi_initialize();
78
79 /* Import public and private keys */
80 rsakey->n = bi_import(rsakey->bi_ctx, modulus, mod_len);
81 rsakey->e = bi_import(rsakey->bi_ctx, pub_exp, pub_len);
82 rsakey->d = bi_import(rsakey->bi_ctx, priv_exp, priv_len);
83
84 if (p && q && dP && dQ && qInv) {
85 /* Import crt optimization keys */
86 rsakey->p = bi_import(rsakey->bi_ctx, p, p_len);
87 rsakey->q = bi_import(rsakey->bi_ctx, q, q_len);
88 rsakey->dP = bi_import(rsakey->bi_ctx, dP, dP_len);
89 rsakey->dQ = bi_import(rsakey->bi_ctx, dQ, dQ_len);
90 rsakey->qInv = bi_import(rsakey->bi_ctx, qInv, qInv_len);
91
92 /* Set imported keys either permanent or modulo */
93 bi_permanent(rsakey->dP);
94 bi_permanent(rsakey->dQ);
95 bi_permanent(rsakey->qInv);
96 bi_set_mod(rsakey->bi_ctx, rsakey->p, BIGINT_P_OFFSET);
97 bi_set_mod(rsakey->bi_ctx, rsakey->q, BIGINT_Q_OFFSET);
98
99 rsakey->use_crt = 1;
100 }
101
102 /* Add keys to the bigint context */
103 bi_set_mod(rsakey->bi_ctx, rsakey->n, BIGINT_M_OFFSET);
104 bi_permanent(rsakey->e);
105 bi_permanent(rsakey->d);
106 return rsakey;
107}
108
109rsakey_t *
110rsakey_init_pem(const char *pemstr)
111{
112 rsapem_t *rsapem;
113 unsigned char *modulus=NULL; unsigned int mod_len=0;
114 unsigned char *pub_exp=NULL; unsigned int pub_len=0;
115 unsigned char *priv_exp=NULL; unsigned int priv_len=0;
116 unsigned char *p=NULL; unsigned int p_len=0;
117 unsigned char *q=NULL; unsigned int q_len=0;
118 unsigned char *dP=NULL; unsigned int dP_len=0;
119 unsigned char *dQ=NULL; unsigned int dQ_len=0;
120 unsigned char *qInv=NULL; unsigned int qInv_len=0;
121 rsakey_t *rsakey=NULL;
122
123 rsapem = rsapem_init(pemstr);
124 if (!rsapem) {
125 return NULL;
126 }
127
128 /* Read public and private keys */
129 mod_len = rsapem_read_vector(rsapem, &modulus);
130 pub_len = rsapem_read_vector(rsapem, &pub_exp);
131 priv_len = rsapem_read_vector(rsapem, &priv_exp);
132 /* Read private keys for crt optimization */
133 p_len = rsapem_read_vector(rsapem, &p);
134 q_len = rsapem_read_vector(rsapem, &q);
135 dP_len = rsapem_read_vector(rsapem, &dP);
136 dQ_len = rsapem_read_vector(rsapem, &dQ);
137 qInv_len = rsapem_read_vector(rsapem, &qInv);
138
139 if (modulus && pub_exp && priv_exp) {
140 /* Initialize rsakey value */
141 rsakey = rsakey_init(modulus, mod_len, pub_exp, pub_len, priv_exp, priv_len,
142 p, p_len, q, q_len, dP, dP_len, dQ, dQ_len, qInv, qInv_len);
143 }
144
145 free(modulus);
146 free(pub_exp);
147 free(priv_exp);
148 free(p);
149 free(q);
150 free(dP);
151 free(dQ);
152 free(qInv);
153 rsapem_destroy(rsapem);
154 return rsakey;
155}
156
157void
158rsakey_destroy(rsakey_t *rsakey)
159{
160 if (rsakey) {
161 bi_free_mod(rsakey->bi_ctx, BIGINT_M_OFFSET);
162 bi_depermanent(rsakey->e);
163 bi_depermanent(rsakey->d);
164 bi_free(rsakey->bi_ctx, rsakey->e);
165 bi_free(rsakey->bi_ctx, rsakey->d);
166
167 if (rsakey->use_crt) {
168 bi_free_mod(rsakey->bi_ctx, BIGINT_P_OFFSET);
169 bi_free_mod(rsakey->bi_ctx, BIGINT_Q_OFFSET);
170 bi_depermanent(rsakey->dP);
171 bi_depermanent(rsakey->dQ);
172 bi_depermanent(rsakey->qInv);
173 bi_free(rsakey->bi_ctx, rsakey->dP);
174 bi_free(rsakey->bi_ctx, rsakey->dQ);
175 bi_free(rsakey->bi_ctx, rsakey->qInv);
176 }
177 bi_terminate(rsakey->bi_ctx);
178
179 base64_destroy(rsakey->base64);
180 free(rsakey);
181 }
182}
183
184static bigint *
185rsakey_modpow(rsakey_t *rsakey, bigint *msg)
186{
187 if (rsakey->use_crt) {
188 return bi_crt(rsakey->bi_ctx, msg,
189 rsakey->dP, rsakey->dQ,
190 rsakey->p, rsakey->q, rsakey->qInv);
191 } else {
192 rsakey->bi_ctx->mod_offset = BIGINT_M_OFFSET;
193 return bi_mod_power(rsakey->bi_ctx, msg, rsakey->d);
194 }
195}
196
197int
198rsakey_sign(rsakey_t *rsakey, char *dst, int dstlen, const char *b64digest,
199 unsigned char *ipaddr, int ipaddrlen,
200 unsigned char *hwaddr, int hwaddrlen)
201{
202 unsigned char buffer[MAX_KEYLEN];
203 unsigned char *digest;
204 int digestlen;
205 int inputlen;
206 bigint *bi_in;
207 bigint *bi_out;
208 int idx;
209
210 assert(rsakey);
211
212 if (dstlen < base64_encoded_length(rsakey->base64, rsakey->keylen)) {
213 return -1;
214 }
215
216 /* Decode the base64 digest */
217 digestlen = base64_decode(rsakey->base64, &digest, b64digest, strlen(b64digest));
218 if (digestlen < 0) {
219 return -2;
220 }
221
222 /* Calculate the input data length */
223 inputlen = digestlen+ipaddrlen+hwaddrlen;
224 if (inputlen > rsakey->keylen-3-RSA_MIN_PADLEN) {
225 free(digest);
226 return -3;
227 }
228 if (inputlen < 32) {
229 /* Minimum size is 32 */
230 inputlen = 32;
231 }
232
233 /* Construct the input buffer with padding */
234 /* See RFC 3447 9.2 for more information */
235 idx = 0;
236 memset(buffer, 0, sizeof(buffer));
237 buffer[idx++] = 0x00;
238 buffer[idx++] = 0x01;
239 memset(buffer+idx, 0xff, rsakey->keylen-inputlen-3);
240 idx += rsakey->keylen-inputlen-3;
241 buffer[idx++] = 0x00;
242 memcpy(buffer+idx, digest, digestlen);
243 idx += digestlen;
244 memcpy(buffer+idx, ipaddr, ipaddrlen);
245 idx += ipaddrlen;
246 memcpy(buffer+idx, hwaddr, hwaddrlen);
247 idx += hwaddrlen;
248
249 /* Calculate the signature s = m^d (mod n) */
250 bi_in = bi_import(rsakey->bi_ctx, buffer, rsakey->keylen);
251 bi_out = rsakey_modpow(rsakey, bi_in);
252
253 /* Encode and save the signature into dst */
254 bi_export(rsakey->bi_ctx, bi_out, buffer, rsakey->keylen);
255 base64_encode(rsakey->base64, dst, buffer, rsakey->keylen);
256
257 free(digest);
258 return 0;
259}
260
261/* Mask generation function with SHA-1 hash */
262/* See RFC 3447 B.2.1 for more information */
263static int
264rsakey_mfg1(unsigned char *dst, int dstlen, const unsigned char *seed, int seedlen, int masklen)
265{
266 SHA1_CTX sha_ctx;
267 int iterations;
268 int dstpos;
269 int i;
270
271 iterations = (masklen+SHA1_SIZE-1)/SHA1_SIZE;
272 if (dstlen < iterations*SHA1_SIZE) {
273 return -1;
274 }
275
276 dstpos = 0;
277 for (i=0; i<iterations; i++) {
278 unsigned char counter[4];
279 counter[0] = (i>>24)&0xff;
280 counter[1] = (i>>16)&0xff;
281 counter[2] = (i>>8)&0xff;
282 counter[3] = i&0xff;
283
284 SHA1_Init(&sha_ctx);
285 SHA1_Update(&sha_ctx, seed, seedlen);
286 SHA1_Update(&sha_ctx, counter, sizeof(counter));
287 SHA1_Final(dst+dstpos, &sha_ctx);
288 dstpos += SHA1_SIZE;
289 }
290 return masklen;
291}
292
293/* OAEP decryption with SHA-1 hash */
294/* See RFC 3447 7.1.2 for more information */
295int
296rsakey_decrypt(rsakey_t *rsakey, unsigned char *dst, int dstlen, const char *b64input)
297{
298 unsigned char buffer[MAX_KEYLEN];
299 unsigned char maskbuf[MAX_KEYLEN];
300 unsigned char *input;
301 int inputlen;
302 bigint *bi_in;
303 bigint *bi_out;
304 int outlen;
305 int i, ret;
306
307 assert(rsakey);
308 if (!dst || !b64input) {
309 return -1;
310 }
311
312 memset(buffer, 0, sizeof(buffer));
313 inputlen = base64_decode(rsakey->base64, &input, b64input, strlen(b64input));
314 if (inputlen < 0 || inputlen > rsakey->keylen) {
315 return -2;
316 }
317 memcpy(buffer+rsakey->keylen-inputlen, input, inputlen);
318 free(input);
319 input = NULL;
320
321 /* Decrypt the input data m = c^d (mod n) */
322 bi_in = bi_import(rsakey->bi_ctx, buffer, rsakey->keylen);
323 bi_out = rsakey_modpow(rsakey, bi_in);
324
325 memset(buffer, 0, sizeof(buffer));
326 bi_export(rsakey->bi_ctx, bi_out, buffer, rsakey->keylen);
327
328 /* First unmask seed in the buffer */
329 ret = rsakey_mfg1(maskbuf, sizeof(maskbuf),
330 buffer+1+SHA1_SIZE,
331 rsakey->keylen-1-SHA1_SIZE,
332 SHA1_SIZE);
333 if (ret < 0) {
334 return -3;
335 }
336 for (i=0; i<ret; i++) {
337 buffer[1+i] ^= maskbuf[i];
338 }
339
340 /* Then unmask the actual message */
341 ret = rsakey_mfg1(maskbuf, sizeof(maskbuf),
342 buffer+1, SHA1_SIZE,
343 rsakey->keylen-1-SHA1_SIZE);
344 if (ret < 0) {
345 return -4;
346 }
347 for (i=0; i<ret; i++) {
348 buffer[1+SHA1_SIZE+i] ^= maskbuf[i];
349 }
350
351 /* Finally find the first data byte */
352 for (i=1+2*SHA1_SIZE; i<rsakey->keylen && !buffer[i++];);
353
354 /* Calculate real output length and return */
355 outlen = rsakey->keylen-i;
356 if (outlen > dstlen) {
357 return -5;
358 }
359 memcpy(dst, buffer+i, outlen);
360 return outlen;
361}
362
363int
364rsakey_parseiv(rsakey_t *rsakey, unsigned char *dst, int dstlen, const char *b64input)
365{
366 unsigned char *tmpptr;
367 int length;
368
369 assert(rsakey);
370 if (!dst || !b64input) {
371 return -1;
372 }
373
374 length = base64_decode(rsakey->base64, &tmpptr, b64input, strlen(b64input));
375 if (length < 0) {
376 return -1;
377 } else if (length > dstlen) {
378 free(tmpptr);
379 return -2;
380 }
381
382 memcpy(dst, tmpptr, length);
383 free(tmpptr);
384 return length;
385}