-
Notifications
You must be signed in to change notification settings - Fork 0
/
crypt_gcrypt.c
170 lines (148 loc) · 4.4 KB
/
crypt_gcrypt.c
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
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
/* crypt_gcrypt.c -- libgcrypt (of GnuPG) crypto interface
*
* Copyright Dean Scarff
*
* Licensed under the Apache License, Version 2.0 (the "License"); you
* may not use this file except in compliance with the License.
*/
#ifdef HAVE_CONFIG_H
#include "config.h"
#endif
#include <assert.h>
#include <errno.h>
#include <limits.h>
#include <stdint.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <gcrypt.h>
#include "hope.h"
#include "parse_pk.h"
/* Footprint in host byte-order. */
static uint16_t footprint;
/* Modulus length in octets. */
static size_t modulus_length;
/* RSA key sexp "(private-key (rsa ...))". */
static gcry_sexp_t rsa_key;
static int ksizes[PKFK_SIZE]; // key field sizes
static char *kparts[PKFK_SIZE]; // key field values
/* Hope an expression `e' of type `gcry_error_t' has non-error
* status. */
#define CHECK(e) \
{ \
gcry_error_t err = e; \
hope(!err, gcry_strerror(err)); \
}
// See crypt.h
void
crypt_init()
{
gcry_check_version(GCRYPT_VERSION);
// Secure memory is somewhat overrated: we're usually reading
// a private key in over stdio's unsecure buffers.
gcry_control(GCRYCTL_SUSPEND_SECMEM_WARN);
gcry_control(GCRYCTL_INIT_SECMEM, 2048, 0);
gcry_control(GCRYCTL_RESUME_SECMEM_WARN);
gcry_control(GCRYCTL_INITIALIZATION_FINISHED, 0);
#ifndef NDEBUG
hope(!gcry_md_test_algo(GCRY_MD_MD5), "libgcrypt's MD5 unavailable");
hope(!gcry_pk_test_algo(GCRY_PK_RSA), "libgcrypt's RSA unavailable");
size_t usage = GCRY_PK_USAGE_SIGN;
hope(!gcry_pk_algo_info(GCRY_PK_RSA, GCRYCTL_TEST_ALGO, NULL, &usage),
"libgcrypt RSA can't sign");
#endif
}
// See crypt.h
void
crypt_finish()
{
gcry_sexp_release(rsa_key);
}
/* Store `data' to the appropriate `ksizes' and 'kparts' members. */
static void
load_line(enum pk_field_key field, const char *data)
{
assert(PKFK_SIZE > field);
char *buf = gcry_malloc_secure(decode64_length(data));
hope(buf, "Out of memory.");
size_t octet_count = decode64(buf, data);
if (PKFK_MODULUS == field) {
footprint = (uint16_t)(buf[octet_count - 3]) << 8;
footprint |= (unsigned char)buf[octet_count - 2];
modulus_length = octet_count;
}
ksizes[field] = octet_count;
kparts[field] = buf;
}
/* Return the 1-based index of the first missing key-part, or 0 if all
* required key parts were present. */
static int
check_kparts()
{
for (int i = 0; i < PKFK_SIZE; ++i)
if (!kparts[i])
return i + 1;
return 0;
}
// See crypt.h
void
crypt_load_key(FILE *privkey)
{
parse_pk_file(privkey, &load_line);
hope(!check_kparts(), "private key missing parts");
// the primes are swapped and thus the CRT coefficient is invalid
gcry_mpi_t u, p, q;
gcry_mpi_scan(&q, GCRYMPI_FMT_USG, kparts[PKFK_PRIME1],
ksizes[PKFK_PRIME1], NULL);
gcry_mpi_scan(&p, GCRYMPI_FMT_USG, kparts[PKFK_PRIME2],
ksizes[PKFK_PRIME2], NULL);
hope(0 < gcry_mpi_cmp(q, p), "key primes out of order");
u = gcry_mpi_new(ksizes[PKFK_COEFFICIENT] * CHAR_BIT);
gcry_mpi_invm(u, p, q);
gcry_sexp_build(&rsa_key, NULL,
"(private-key (rsa "
"(n %b) (e %b) (d %b) (p %m) (q %m) (u %m)))",
ksizes[PKFK_MODULUS], kparts[PKFK_MODULUS],
ksizes[PKFK_PUBLIC_EXPONENT], kparts[PKFK_PUBLIC_EXPONENT],
ksizes[PKFK_PRIVATE_EXPONENT], kparts[PKFK_PRIVATE_EXPONENT], p, q,
u);
CHECK(gcry_pk_testkey(rsa_key));
}
// See crypt.h
uint16_t
crypt_footprint()
{
return footprint;
}
// See crypt.h
size_t
crypt_sign_length()
{
return modulus_length;
}
// See crypt.h
char *
crypt_sign(char *dst, const char *src, size_t length)
{
gcry_sexp_t data, sig, s;
gcry_mpi_t sig_mpi;
size_t written;
size_t digest_length = gcry_md_get_algo_dlen(GCRY_MD_MD5);
unsigned char *digest = malloc(digest_length);
gcry_md_hash_buffer(GCRY_MD_MD5, digest, src, length);
CHECK(gcry_sexp_build(&data, NULL,
"(data (flags pkcs1 no-blinding) (hash md5 %b))", digest_length,
digest));
CHECK(gcry_pk_sign(&sig, data, rsa_key));
s = gcry_sexp_find_token(sig, "s", 0);
assert(s);
sig_mpi = gcry_sexp_nth_mpi(s, 1, GCRYMPI_FMT_USG);
assert(NULL != sig_mpi);
CHECK(gcry_mpi_print(GCRYMPI_FMT_USG, (unsigned char *)dst,
modulus_length, &written, sig_mpi));
gcry_mpi_release(sig_mpi);
gcry_sexp_release(s);
gcry_sexp_release(sig);
gcry_sexp_release(data);
return dst + written;
}