| line |
stmt |
bran |
cond |
sub |
pod |
time |
code |
|
1
|
|
|
|
|
|
|
/* |
|
2
|
|
|
|
|
|
|
* pdfmake_x509.c — X.509 certificate parsing implementation |
|
3
|
|
|
|
|
|
|
* |
|
4
|
|
|
|
|
|
|
* Parse X.509 certificates for PDF digital signatures. |
|
5
|
|
|
|
|
|
|
*/ |
|
6
|
|
|
|
|
|
|
|
|
7
|
|
|
|
|
|
|
#include "pdfmake_x509.h" |
|
8
|
|
|
|
|
|
|
#include "pdfmake_asn1.h" |
|
9
|
|
|
|
|
|
|
#include "pdfmake_arena.h" |
|
10
|
|
|
|
|
|
|
#include |
|
11
|
|
|
|
|
|
|
#include |
|
12
|
|
|
|
|
|
|
#include |
|
13
|
|
|
|
|
|
|
#include |
|
14
|
|
|
|
|
|
|
#include |
|
15
|
|
|
|
|
|
|
|
|
16
|
|
|
|
|
|
|
/*============================================================================ |
|
17
|
|
|
|
|
|
|
* Internal helpers |
|
18
|
|
|
|
|
|
|
*==========================================================================*/ |
|
19
|
|
|
|
|
|
|
|
|
20
|
|
|
|
|
|
|
/* Base64 decode table */ |
|
21
|
|
|
|
|
|
|
static const int8_t b64_table[256] = { |
|
22
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
23
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
24
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,62,-1,-1,-1,63, |
|
25
|
|
|
|
|
|
|
52,53,54,55,56,57,58,59,60,61,-1,-1,-1,-2,-1,-1, |
|
26
|
|
|
|
|
|
|
-1, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9,10,11,12,13,14, |
|
27
|
|
|
|
|
|
|
15,16,17,18,19,20,21,22,23,24,25,-1,-1,-1,-1,-1, |
|
28
|
|
|
|
|
|
|
-1,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40, |
|
29
|
|
|
|
|
|
|
41,42,43,44,45,46,47,48,49,50,51,-1,-1,-1,-1,-1, |
|
30
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
31
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
32
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
33
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
34
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
35
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
36
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1, |
|
37
|
|
|
|
|
|
|
-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1,-1 |
|
38
|
|
|
|
|
|
|
}; |
|
39
|
|
|
|
|
|
|
|
|
40
|
|
|
|
|
|
|
/* Decode base64 to binary */ |
|
41
|
0
|
|
|
|
|
|
static size_t base64_decode( |
|
42
|
|
|
|
|
|
|
const char *src, size_t src_len, |
|
43
|
|
|
|
|
|
|
uint8_t *dst, size_t dst_len) |
|
44
|
|
|
|
|
|
|
{ |
|
45
|
0
|
|
|
|
|
|
size_t out = 0; |
|
46
|
0
|
|
|
|
|
|
uint32_t accum = 0; |
|
47
|
0
|
|
|
|
|
|
int bits = 0; |
|
48
|
|
|
|
|
|
|
size_t i; |
|
49
|
|
|
|
|
|
|
|
|
50
|
0
|
0
|
|
|
|
|
for (i = 0; i < src_len && out < dst_len; i++) { |
|
|
|
0
|
|
|
|
|
|
|
51
|
0
|
|
|
|
|
|
int8_t val = b64_table[(uint8_t)src[i]]; |
|
52
|
0
|
0
|
|
|
|
|
if (val == -1) continue; /* Skip whitespace */ |
|
53
|
0
|
0
|
|
|
|
|
if (val == -2) break; /* Padding '=' */ |
|
54
|
|
|
|
|
|
|
|
|
55
|
0
|
|
|
|
|
|
accum = (accum << 6) | val; |
|
56
|
0
|
|
|
|
|
|
bits += 6; |
|
57
|
|
|
|
|
|
|
|
|
58
|
0
|
0
|
|
|
|
|
if (bits >= 8) { |
|
59
|
0
|
|
|
|
|
|
bits -= 8; |
|
60
|
0
|
|
|
|
|
|
dst[out++] = (accum >> bits) & 0xFF; |
|
61
|
|
|
|
|
|
|
} |
|
62
|
|
|
|
|
|
|
} |
|
63
|
|
|
|
|
|
|
|
|
64
|
0
|
|
|
|
|
|
return out; |
|
65
|
|
|
|
|
|
|
} |
|
66
|
|
|
|
|
|
|
|
|
67
|
|
|
|
|
|
|
/* Duplicate string to arena */ |
|
68
|
|
|
|
|
|
|
/* Convert bytes to hex string */ |
|
69
|
1
|
|
|
|
|
|
static char *bytes_to_hex(pdfmake_arena_t *arena, const uint8_t *data, size_t len) |
|
70
|
|
|
|
|
|
|
{ |
|
71
|
|
|
|
|
|
|
char *hex; |
|
72
|
|
|
|
|
|
|
char *p; |
|
73
|
|
|
|
|
|
|
size_t i; |
|
74
|
|
|
|
|
|
|
|
|
75
|
1
|
|
|
|
|
|
hex = pdfmake_arena_alloc(arena, len * 3 + 1); /* "XX:" format */ |
|
76
|
1
|
50
|
|
|
|
|
if (!hex) return NULL; |
|
77
|
|
|
|
|
|
|
|
|
78
|
1
|
|
|
|
|
|
p = hex; |
|
79
|
21
|
100
|
|
|
|
|
for (i = 0; i < len; i++) { |
|
80
|
20
|
100
|
|
|
|
|
if (i > 0) *p++ = ':'; |
|
81
|
20
|
|
|
|
|
|
sprintf(p, "%02X", data[i]); |
|
82
|
20
|
|
|
|
|
|
p += 2; |
|
83
|
|
|
|
|
|
|
} |
|
84
|
1
|
|
|
|
|
|
*p = '\0'; |
|
85
|
|
|
|
|
|
|
|
|
86
|
1
|
|
|
|
|
|
return hex; |
|
87
|
|
|
|
|
|
|
} |
|
88
|
|
|
|
|
|
|
|
|
89
|
|
|
|
|
|
|
/* Parse signature algorithm OID */ |
|
90
|
1
|
|
|
|
|
|
static pdfmake_sig_algorithm_t parse_sig_algorithm(const char *oid) |
|
91
|
|
|
|
|
|
|
{ |
|
92
|
1
|
50
|
|
|
|
|
if (!oid) return PDFMAKE_SIG_UNKNOWN; |
|
93
|
|
|
|
|
|
|
|
|
94
|
1
|
50
|
|
|
|
|
if (strcmp(oid, OID_RSA_MD5) == 0) return PDFMAKE_SIG_RSA_MD5; |
|
95
|
1
|
50
|
|
|
|
|
if (strcmp(oid, OID_RSA_SHA1) == 0) return PDFMAKE_SIG_RSA_SHA1; |
|
96
|
1
|
50
|
|
|
|
|
if (strcmp(oid, OID_RSA_SHA256) == 0) return PDFMAKE_SIG_RSA_SHA256; |
|
97
|
0
|
0
|
|
|
|
|
if (strcmp(oid, OID_RSA_SHA384) == 0) return PDFMAKE_SIG_RSA_SHA384; |
|
98
|
0
|
0
|
|
|
|
|
if (strcmp(oid, OID_RSA_SHA512) == 0) return PDFMAKE_SIG_RSA_SHA512; |
|
99
|
0
|
0
|
|
|
|
|
if (strcmp(oid, OID_ECDSA_SHA256) == 0) return PDFMAKE_SIG_ECDSA_SHA256; |
|
100
|
0
|
0
|
|
|
|
|
if (strcmp(oid, OID_ECDSA_SHA384) == 0) return PDFMAKE_SIG_ECDSA_SHA384; |
|
101
|
0
|
0
|
|
|
|
|
if (strcmp(oid, OID_ECDSA_SHA512) == 0) return PDFMAKE_SIG_ECDSA_SHA512; |
|
102
|
|
|
|
|
|
|
|
|
103
|
0
|
|
|
|
|
|
return PDFMAKE_SIG_UNKNOWN; |
|
104
|
|
|
|
|
|
|
} |
|
105
|
|
|
|
|
|
|
|
|
106
|
|
|
|
|
|
|
/* Parse X.500 Name (subject/issuer) */ |
|
107
|
2
|
|
|
|
|
|
static void parse_name( |
|
108
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
109
|
|
|
|
|
|
|
const pdfmake_asn1_node_t *name_node, |
|
110
|
|
|
|
|
|
|
pdfmake_x509_name_t *name) |
|
111
|
|
|
|
|
|
|
{ |
|
112
|
|
|
|
|
|
|
char dn_buf[1024]; |
|
113
|
|
|
|
|
|
|
char *dn_p; |
|
114
|
|
|
|
|
|
|
char *dn_end; |
|
115
|
|
|
|
|
|
|
pdfmake_asn1_node_t *rdn; |
|
116
|
|
|
|
|
|
|
|
|
117
|
2
|
50
|
|
|
|
|
if (!pdfmake_asn1_is_sequence(name_node)) { |
|
118
|
0
|
|
|
|
|
|
return; |
|
119
|
|
|
|
|
|
|
} |
|
120
|
|
|
|
|
|
|
|
|
121
|
|
|
|
|
|
|
/* Name is SEQUENCE of RDN (RelativeDistinguishedName) */ |
|
122
|
|
|
|
|
|
|
/* Each RDN is a SET of AttributeTypeAndValue */ |
|
123
|
|
|
|
|
|
|
/* AttributeTypeAndValue is SEQUENCE { type OID, value ANY } */ |
|
124
|
|
|
|
|
|
|
|
|
125
|
|
|
|
|
|
|
/* Build DN string while parsing */ |
|
126
|
2
|
|
|
|
|
|
dn_p = dn_buf; |
|
127
|
2
|
|
|
|
|
|
dn_end = dn_buf + sizeof(dn_buf) - 1; |
|
128
|
|
|
|
|
|
|
|
|
129
|
2
|
|
|
|
|
|
rdn = name_node->children; |
|
130
|
8
|
100
|
|
|
|
|
while (rdn) { |
|
131
|
6
|
50
|
|
|
|
|
if (pdfmake_asn1_is_set(rdn)) { |
|
132
|
6
|
|
|
|
|
|
pdfmake_asn1_node_t *atv = rdn->children; |
|
133
|
12
|
100
|
|
|
|
|
while (atv) { |
|
134
|
6
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(atv)) { |
|
135
|
6
|
|
|
|
|
|
pdfmake_asn1_node_t *oid_node = pdfmake_asn1_child_at(atv, 0); |
|
136
|
6
|
|
|
|
|
|
pdfmake_asn1_node_t *val_node = pdfmake_asn1_child_at(atv, 1); |
|
137
|
|
|
|
|
|
|
|
|
138
|
6
|
50
|
|
|
|
|
if (oid_node && val_node) { |
|
|
|
50
|
|
|
|
|
|
|
139
|
6
|
|
|
|
|
|
char *oid = pdfmake_asn1_get_oid_string(arena, oid_node); |
|
140
|
6
|
|
|
|
|
|
char *val = pdfmake_asn1_get_string(arena, val_node); |
|
141
|
|
|
|
|
|
|
|
|
142
|
6
|
50
|
|
|
|
|
if (oid && val) { |
|
|
|
50
|
|
|
|
|
|
|
143
|
6
|
|
|
|
|
|
const char *attr_name = NULL; |
|
144
|
6
|
|
|
|
|
|
char **target = NULL; |
|
145
|
|
|
|
|
|
|
|
|
146
|
6
|
100
|
|
|
|
|
if (strcmp(oid, OID_COMMON_NAME) == 0) { |
|
147
|
2
|
|
|
|
|
|
attr_name = "CN"; |
|
148
|
2
|
|
|
|
|
|
target = &name->common_name; |
|
149
|
4
|
100
|
|
|
|
|
} else if (strcmp(oid, OID_ORGANIZATION) == 0) { |
|
150
|
2
|
|
|
|
|
|
attr_name = "O"; |
|
151
|
2
|
|
|
|
|
|
target = &name->organization; |
|
152
|
2
|
50
|
|
|
|
|
} else if (strcmp(oid, OID_ORGANIZATIONAL_UNIT) == 0) { |
|
153
|
0
|
|
|
|
|
|
attr_name = "OU"; |
|
154
|
0
|
|
|
|
|
|
target = &name->organizational_unit; |
|
155
|
2
|
50
|
|
|
|
|
} else if (strcmp(oid, OID_COUNTRY) == 0) { |
|
156
|
2
|
|
|
|
|
|
attr_name = "C"; |
|
157
|
2
|
|
|
|
|
|
target = &name->country; |
|
158
|
0
|
0
|
|
|
|
|
} else if (strcmp(oid, OID_STATE) == 0) { |
|
159
|
0
|
|
|
|
|
|
attr_name = "ST"; |
|
160
|
0
|
|
|
|
|
|
target = &name->state; |
|
161
|
0
|
0
|
|
|
|
|
} else if (strcmp(oid, OID_LOCALITY) == 0) { |
|
162
|
0
|
|
|
|
|
|
attr_name = "L"; |
|
163
|
0
|
|
|
|
|
|
target = &name->locality; |
|
164
|
0
|
0
|
|
|
|
|
} else if (strcmp(oid, OID_EMAIL_ADDRESS) == 0) { |
|
165
|
0
|
|
|
|
|
|
attr_name = "emailAddress"; |
|
166
|
0
|
|
|
|
|
|
target = &name->email; |
|
167
|
0
|
0
|
|
|
|
|
} else if (strcmp(oid, OID_SERIAL_NUMBER) == 0) { |
|
168
|
0
|
|
|
|
|
|
attr_name = "serialNumber"; |
|
169
|
0
|
|
|
|
|
|
target = &name->serial_number; |
|
170
|
|
|
|
|
|
|
} |
|
171
|
|
|
|
|
|
|
|
|
172
|
6
|
50
|
|
|
|
|
if (target) { |
|
173
|
6
|
|
|
|
|
|
*target = val; |
|
174
|
|
|
|
|
|
|
} |
|
175
|
|
|
|
|
|
|
|
|
176
|
|
|
|
|
|
|
/* Append to DN string */ |
|
177
|
6
|
50
|
|
|
|
|
if (attr_name && dn_p < dn_end) { |
|
|
|
50
|
|
|
|
|
|
|
178
|
6
|
100
|
|
|
|
|
if (dn_p > dn_buf) { |
|
179
|
4
|
|
|
|
|
|
dn_p += snprintf(dn_p, dn_end - dn_p, ", "); |
|
180
|
|
|
|
|
|
|
} |
|
181
|
6
|
|
|
|
|
|
dn_p += snprintf(dn_p, dn_end - dn_p, "%s=%s", attr_name, val); |
|
182
|
|
|
|
|
|
|
} |
|
183
|
|
|
|
|
|
|
} |
|
184
|
|
|
|
|
|
|
} |
|
185
|
|
|
|
|
|
|
} |
|
186
|
6
|
|
|
|
|
|
atv = atv->next; |
|
187
|
|
|
|
|
|
|
} |
|
188
|
|
|
|
|
|
|
} |
|
189
|
6
|
|
|
|
|
|
rdn = rdn->next; |
|
190
|
|
|
|
|
|
|
} |
|
191
|
|
|
|
|
|
|
|
|
192
|
2
|
|
|
|
|
|
*dn_p = '\0'; |
|
193
|
2
|
|
|
|
|
|
name->dn = pdfmake_arena_strdup(arena, dn_buf); |
|
194
|
|
|
|
|
|
|
} |
|
195
|
|
|
|
|
|
|
|
|
196
|
|
|
|
|
|
|
/* Parse AlgorithmIdentifier */ |
|
197
|
1
|
|
|
|
|
|
static char *parse_algorithm_id( |
|
198
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
199
|
|
|
|
|
|
|
const pdfmake_asn1_node_t *alg_node) |
|
200
|
|
|
|
|
|
|
{ |
|
201
|
|
|
|
|
|
|
pdfmake_asn1_node_t *oid_node; |
|
202
|
|
|
|
|
|
|
|
|
203
|
1
|
50
|
|
|
|
|
if (!pdfmake_asn1_is_sequence(alg_node)) { |
|
204
|
0
|
|
|
|
|
|
return NULL; |
|
205
|
|
|
|
|
|
|
} |
|
206
|
|
|
|
|
|
|
|
|
207
|
1
|
|
|
|
|
|
oid_node = pdfmake_asn1_child_at(alg_node, 0); |
|
208
|
1
|
50
|
|
|
|
|
if (!oid_node) return NULL; |
|
209
|
|
|
|
|
|
|
|
|
210
|
1
|
|
|
|
|
|
return pdfmake_asn1_get_oid_string(arena, oid_node); |
|
211
|
|
|
|
|
|
|
} |
|
212
|
|
|
|
|
|
|
|
|
213
|
|
|
|
|
|
|
/* Parse SubjectPublicKeyInfo */ |
|
214
|
1
|
|
|
|
|
|
static void parse_pubkey_info( |
|
215
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
216
|
|
|
|
|
|
|
const pdfmake_asn1_node_t *spki, |
|
217
|
|
|
|
|
|
|
pdfmake_pubkey_t *pubkey) |
|
218
|
|
|
|
|
|
|
{ |
|
219
|
|
|
|
|
|
|
pdfmake_asn1_node_t *alg_node; |
|
220
|
|
|
|
|
|
|
pdfmake_asn1_node_t *key_node; |
|
221
|
|
|
|
|
|
|
pdfmake_asn1_node_t *oid_node; |
|
222
|
|
|
|
|
|
|
char *oid; |
|
223
|
|
|
|
|
|
|
const uint8_t *bits; |
|
224
|
|
|
|
|
|
|
size_t bit_count; |
|
225
|
|
|
|
|
|
|
size_t pos; |
|
226
|
|
|
|
|
|
|
pdfmake_asn1_node_t *rsa_key; |
|
227
|
|
|
|
|
|
|
pdfmake_asn1_node_t *param; |
|
228
|
|
|
|
|
|
|
|
|
229
|
1
|
50
|
|
|
|
|
if (!pdfmake_asn1_is_sequence(spki)) { |
|
230
|
0
|
|
|
|
|
|
return; |
|
231
|
|
|
|
|
|
|
} |
|
232
|
|
|
|
|
|
|
|
|
233
|
|
|
|
|
|
|
/* SubjectPublicKeyInfo ::= SEQUENCE { |
|
234
|
|
|
|
|
|
|
algorithm AlgorithmIdentifier, |
|
235
|
|
|
|
|
|
|
subjectPublicKey BIT STRING } */ |
|
236
|
|
|
|
|
|
|
|
|
237
|
1
|
|
|
|
|
|
alg_node = pdfmake_asn1_child_at(spki, 0); |
|
238
|
1
|
|
|
|
|
|
key_node = pdfmake_asn1_child_at(spki, 1); |
|
239
|
|
|
|
|
|
|
|
|
240
|
1
|
50
|
|
|
|
|
if (!alg_node || !key_node) return; |
|
|
|
50
|
|
|
|
|
|
|
241
|
|
|
|
|
|
|
|
|
242
|
|
|
|
|
|
|
/* Store raw for signature verification */ |
|
243
|
1
|
|
|
|
|
|
pubkey->raw = spki->data; |
|
244
|
1
|
|
|
|
|
|
pubkey->raw_len = spki->length; |
|
245
|
|
|
|
|
|
|
|
|
246
|
|
|
|
|
|
|
/* Parse algorithm OID */ |
|
247
|
1
|
|
|
|
|
|
oid_node = pdfmake_asn1_child_at(alg_node, 0); |
|
248
|
1
|
50
|
|
|
|
|
if (!oid_node) return; |
|
249
|
|
|
|
|
|
|
|
|
250
|
1
|
|
|
|
|
|
oid = pdfmake_asn1_get_oid_string(arena, oid_node); |
|
251
|
1
|
50
|
|
|
|
|
if (!oid) return; |
|
252
|
|
|
|
|
|
|
|
|
253
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_get_bit_string(key_node, &bits, &bit_count) != 0) { |
|
254
|
0
|
|
|
|
|
|
return; |
|
255
|
|
|
|
|
|
|
} |
|
256
|
|
|
|
|
|
|
|
|
257
|
1
|
50
|
|
|
|
|
if (strcmp(oid, OID_RSA_ENCRYPTION) == 0) { |
|
258
|
1
|
|
|
|
|
|
pubkey->algorithm = PDFMAKE_PK_RSA; |
|
259
|
|
|
|
|
|
|
|
|
260
|
|
|
|
|
|
|
/* RSA public key is DER-encoded in the bit string */ |
|
261
|
|
|
|
|
|
|
/* RSAPublicKey ::= SEQUENCE { modulus INTEGER, publicExponent INTEGER } */ |
|
262
|
1
|
|
|
|
|
|
pos = 0; |
|
263
|
1
|
|
|
|
|
|
rsa_key = pdfmake_asn1_parse_element(arena, bits, bit_count / 8, &pos); |
|
264
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(rsa_key)) { |
|
265
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *mod = pdfmake_asn1_child_at(rsa_key, 0); |
|
266
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *exp = pdfmake_asn1_child_at(rsa_key, 1); |
|
267
|
|
|
|
|
|
|
|
|
268
|
1
|
50
|
|
|
|
|
if (mod && exp) { |
|
|
|
50
|
|
|
|
|
|
|
269
|
1
|
|
|
|
|
|
pubkey->rsa.modulus = pdfmake_arena_memdup(arena, mod->data, mod->length); |
|
270
|
1
|
|
|
|
|
|
pubkey->rsa.modulus_len = mod->length; |
|
271
|
1
|
|
|
|
|
|
pubkey->rsa.exponent = pdfmake_arena_memdup(arena, exp->data, exp->length); |
|
272
|
1
|
|
|
|
|
|
pubkey->rsa.exponent_len = exp->length; |
|
273
|
|
|
|
|
|
|
} |
|
274
|
|
|
|
|
|
|
} |
|
275
|
0
|
0
|
|
|
|
|
} else if (strcmp(oid, OID_EC_PUBLIC_KEY) == 0) { |
|
276
|
0
|
|
|
|
|
|
pubkey->algorithm = PDFMAKE_PK_ECDSA; |
|
277
|
|
|
|
|
|
|
|
|
278
|
|
|
|
|
|
|
/* Get curve OID from algorithm parameters */ |
|
279
|
0
|
|
|
|
|
|
param = pdfmake_asn1_child_at(alg_node, 1); |
|
280
|
0
|
0
|
|
|
|
|
if (param && param->tag == ASN1_TAG_OID) { |
|
|
|
0
|
|
|
|
|
|
|
281
|
0
|
|
|
|
|
|
pubkey->ecdsa.curve_oid = pdfmake_asn1_get_oid_string(arena, param); |
|
282
|
|
|
|
|
|
|
|
|
283
|
|
|
|
|
|
|
/* Determine curve size */ |
|
284
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_oid_equals(param, OID_SECP256R1)) { |
|
285
|
0
|
|
|
|
|
|
pubkey->ecdsa.curve_bits = 256; |
|
286
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(param, OID_SECP384R1)) { |
|
287
|
0
|
|
|
|
|
|
pubkey->ecdsa.curve_bits = 384; |
|
288
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(param, OID_SECP521R1)) { |
|
289
|
0
|
|
|
|
|
|
pubkey->ecdsa.curve_bits = 521; |
|
290
|
|
|
|
|
|
|
} |
|
291
|
|
|
|
|
|
|
} |
|
292
|
|
|
|
|
|
|
|
|
293
|
|
|
|
|
|
|
/* EC point is the bit string content */ |
|
294
|
0
|
|
|
|
|
|
pubkey->ecdsa.point = pdfmake_arena_memdup(arena, bits, bit_count / 8); |
|
295
|
0
|
|
|
|
|
|
pubkey->ecdsa.point_len = bit_count / 8; |
|
296
|
|
|
|
|
|
|
} |
|
297
|
|
|
|
|
|
|
} |
|
298
|
|
|
|
|
|
|
|
|
299
|
|
|
|
|
|
|
/* Parse extension value */ |
|
300
|
3
|
|
|
|
|
|
static void parse_extension( |
|
301
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
302
|
|
|
|
|
|
|
const pdfmake_asn1_node_t *ext, |
|
303
|
|
|
|
|
|
|
pdfmake_x509_cert_t *cert) |
|
304
|
|
|
|
|
|
|
{ |
|
305
|
|
|
|
|
|
|
pdfmake_asn1_node_t *oid_node; |
|
306
|
3
|
|
|
|
|
|
pdfmake_asn1_node_t *value_node = NULL; |
|
307
|
|
|
|
|
|
|
pdfmake_asn1_node_t *child; |
|
308
|
|
|
|
|
|
|
size_t pos; |
|
309
|
|
|
|
|
|
|
pdfmake_asn1_node_t *ext_value; |
|
310
|
|
|
|
|
|
|
|
|
311
|
|
|
|
|
|
|
/* Extension ::= SEQUENCE { extnID OID, critical BOOLEAN DEFAULT FALSE, extnValue OCTET STRING } */ |
|
312
|
|
|
|
|
|
|
|
|
313
|
3
|
|
|
|
|
|
oid_node = pdfmake_asn1_child_at(ext, 0); |
|
314
|
3
|
50
|
|
|
|
|
if (!oid_node) return; |
|
315
|
|
|
|
|
|
|
|
|
316
|
|
|
|
|
|
|
/* Find the extension value (OCTET STRING, may be preceded by critical BOOLEAN) */ |
|
317
|
3
|
|
|
|
|
|
child = oid_node->next; |
|
318
|
4
|
50
|
|
|
|
|
while (child) { |
|
319
|
4
|
100
|
|
|
|
|
if (child->tag == ASN1_TAG_OCTET_STRING) { |
|
320
|
3
|
|
|
|
|
|
value_node = child; |
|
321
|
3
|
|
|
|
|
|
break; |
|
322
|
|
|
|
|
|
|
} |
|
323
|
1
|
|
|
|
|
|
child = child->next; |
|
324
|
|
|
|
|
|
|
} |
|
325
|
3
|
50
|
|
|
|
|
if (!value_node) return; |
|
326
|
|
|
|
|
|
|
|
|
327
|
|
|
|
|
|
|
/* Parse the OCTET STRING content as ASN.1 */ |
|
328
|
3
|
|
|
|
|
|
pos = 0; |
|
329
|
3
|
|
|
|
|
|
ext_value = pdfmake_asn1_parse_element(arena, value_node->data, value_node->length, &pos); |
|
330
|
3
|
50
|
|
|
|
|
if (!ext_value) return; |
|
331
|
|
|
|
|
|
|
|
|
332
|
|
|
|
|
|
|
/* Basic Constraints */ |
|
333
|
3
|
100
|
|
|
|
|
if (pdfmake_asn1_oid_equals(oid_node, OID_BASIC_CONSTRAINTS)) { |
|
334
|
|
|
|
|
|
|
/* BasicConstraints ::= SEQUENCE { cA BOOLEAN DEFAULT FALSE, pathLenConstraint INTEGER OPTIONAL } */ |
|
335
|
1
|
|
|
|
|
|
cert->path_len_constraint = -1; |
|
336
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext_value)) { |
|
337
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *ca_node = pdfmake_asn1_find_tag(ext_value, ASN1_TAG_BOOLEAN); |
|
338
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *path_node = pdfmake_asn1_find_tag(ext_value, ASN1_TAG_INTEGER); |
|
339
|
|
|
|
|
|
|
|
|
340
|
1
|
50
|
|
|
|
|
if (ca_node) { |
|
341
|
|
|
|
|
|
|
int is_ca; |
|
342
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_get_bool(ca_node, &is_ca) == 0) { |
|
343
|
1
|
|
|
|
|
|
cert->is_ca = is_ca; |
|
344
|
|
|
|
|
|
|
} |
|
345
|
|
|
|
|
|
|
} |
|
346
|
1
|
50
|
|
|
|
|
if (path_node) { |
|
347
|
|
|
|
|
|
|
int64_t path_len; |
|
348
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_get_int64(path_node, &path_len) == 0) { |
|
349
|
0
|
|
|
|
|
|
cert->path_len_constraint = (int)path_len; |
|
350
|
|
|
|
|
|
|
} |
|
351
|
|
|
|
|
|
|
} |
|
352
|
|
|
|
|
|
|
} |
|
353
|
|
|
|
|
|
|
} |
|
354
|
|
|
|
|
|
|
/* Key Usage */ |
|
355
|
2
|
50
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_KEY_USAGE)) { |
|
356
|
|
|
|
|
|
|
/* KeyUsage ::= BIT STRING */ |
|
357
|
0
|
0
|
|
|
|
|
if (ext_value->tag == ASN1_TAG_BIT_STRING) { |
|
358
|
|
|
|
|
|
|
const uint8_t *bits; |
|
359
|
|
|
|
|
|
|
size_t bit_count; |
|
360
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_get_bit_string(ext_value, &bits, &bit_count) == 0 && bit_count > 0) { |
|
|
|
0
|
|
|
|
|
|
|
361
|
|
|
|
|
|
|
/* Key usage bits are in order from MSB */ |
|
362
|
0
|
|
|
|
|
|
uint16_t ku = 0; |
|
363
|
0
|
0
|
|
|
|
|
if (bit_count > 0) ku |= (bits[0] & 0x80) ? PDFMAKE_KU_DIGITAL_SIGNATURE : 0; |
|
364
|
0
|
0
|
|
|
|
|
if (bit_count > 1) ku |= (bits[0] & 0x40) ? PDFMAKE_KU_NON_REPUDIATION : 0; |
|
365
|
0
|
0
|
|
|
|
|
if (bit_count > 2) ku |= (bits[0] & 0x20) ? PDFMAKE_KU_KEY_ENCIPHERMENT : 0; |
|
366
|
0
|
0
|
|
|
|
|
if (bit_count > 3) ku |= (bits[0] & 0x10) ? PDFMAKE_KU_DATA_ENCIPHERMENT : 0; |
|
367
|
0
|
0
|
|
|
|
|
if (bit_count > 4) ku |= (bits[0] & 0x08) ? PDFMAKE_KU_KEY_AGREEMENT : 0; |
|
368
|
0
|
0
|
|
|
|
|
if (bit_count > 5) ku |= (bits[0] & 0x04) ? PDFMAKE_KU_KEY_CERT_SIGN : 0; |
|
369
|
0
|
0
|
|
|
|
|
if (bit_count > 6) ku |= (bits[0] & 0x02) ? PDFMAKE_KU_CRL_SIGN : 0; |
|
370
|
0
|
0
|
|
|
|
|
if (bit_count > 7) ku |= (bits[0] & 0x01) ? PDFMAKE_KU_ENCIPHER_ONLY : 0; |
|
371
|
0
|
|
|
|
|
|
cert->key_usage = ku; |
|
372
|
|
|
|
|
|
|
} |
|
373
|
|
|
|
|
|
|
} |
|
374
|
|
|
|
|
|
|
} |
|
375
|
|
|
|
|
|
|
/* Extended Key Usage */ |
|
376
|
2
|
50
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_EXT_KEY_USAGE)) { |
|
377
|
|
|
|
|
|
|
/* ExtKeyUsageSyntax ::= SEQUENCE OF KeyPurposeId */ |
|
378
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext_value)) { |
|
379
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *eku = ext_value->children; |
|
380
|
0
|
0
|
|
|
|
|
while (eku) { |
|
381
|
0
|
0
|
|
|
|
|
if (eku->tag == ASN1_TAG_OID) { |
|
382
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_oid_equals(eku, OID_EKU_SERVER_AUTH)) { |
|
383
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_SERVER_AUTH; |
|
384
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_CLIENT_AUTH)) { |
|
385
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_CLIENT_AUTH; |
|
386
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_CODE_SIGNING)) { |
|
387
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_CODE_SIGNING; |
|
388
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_EMAIL_PROTECTION)) { |
|
389
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_EMAIL_PROTECTION; |
|
390
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_TIME_STAMPING)) { |
|
391
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_TIME_STAMPING; |
|
392
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_OCSP_SIGNING)) { |
|
393
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_OCSP_SIGNING; |
|
394
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_PDF_SIGNING)) { |
|
395
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_PDF_SIGNING; |
|
396
|
0
|
0
|
|
|
|
|
} else if (pdfmake_asn1_oid_equals(eku, OID_EKU_DOCUMENT_SIGNING)) { |
|
397
|
0
|
|
|
|
|
|
cert->ext_key_usage |= PDFMAKE_EKU_DOCUMENT_SIGNING; |
|
398
|
|
|
|
|
|
|
} |
|
399
|
|
|
|
|
|
|
} |
|
400
|
0
|
|
|
|
|
|
eku = eku->next; |
|
401
|
|
|
|
|
|
|
} |
|
402
|
|
|
|
|
|
|
} |
|
403
|
|
|
|
|
|
|
} |
|
404
|
|
|
|
|
|
|
/* Subject Key Identifier */ |
|
405
|
2
|
100
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_SUBJECT_KEY_ID)) { |
|
406
|
|
|
|
|
|
|
/* SubjectKeyIdentifier ::= OCTET STRING */ |
|
407
|
1
|
50
|
|
|
|
|
if (ext_value->tag == ASN1_TAG_OCTET_STRING) { |
|
408
|
1
|
|
|
|
|
|
cert->subject_key_id = pdfmake_arena_memdup(arena, ext_value->data, ext_value->length); |
|
409
|
1
|
|
|
|
|
|
cert->subject_key_id_len = ext_value->length; |
|
410
|
|
|
|
|
|
|
} |
|
411
|
|
|
|
|
|
|
} |
|
412
|
|
|
|
|
|
|
/* Authority Key Identifier */ |
|
413
|
1
|
50
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_AUTHORITY_KEY_ID)) { |
|
414
|
|
|
|
|
|
|
/* AuthorityKeyIdentifier ::= SEQUENCE { keyIdentifier [0] IMPLICIT OCTET STRING OPTIONAL, ... } */ |
|
415
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext_value)) { |
|
416
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *kid = ext_value->children; |
|
417
|
1
|
50
|
|
|
|
|
while (kid) { |
|
418
|
1
|
50
|
|
|
|
|
if ((kid->tag & 0x1F) == 0 && (kid->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT) { |
|
|
|
50
|
|
|
|
|
|
|
419
|
|
|
|
|
|
|
/* [0] keyIdentifier */ |
|
420
|
1
|
|
|
|
|
|
cert->authority_key_id = pdfmake_arena_memdup(arena, kid->data, kid->length); |
|
421
|
1
|
|
|
|
|
|
cert->authority_key_id_len = kid->length; |
|
422
|
1
|
|
|
|
|
|
break; |
|
423
|
|
|
|
|
|
|
} |
|
424
|
0
|
|
|
|
|
|
kid = kid->next; |
|
425
|
|
|
|
|
|
|
} |
|
426
|
|
|
|
|
|
|
} |
|
427
|
|
|
|
|
|
|
} |
|
428
|
|
|
|
|
|
|
/* Authority Info Access */ |
|
429
|
0
|
0
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_AUTHORITY_INFO_ACCESS)) { |
|
430
|
|
|
|
|
|
|
/* AuthorityInfoAccessSyntax ::= SEQUENCE OF AccessDescription |
|
431
|
|
|
|
|
|
|
AccessDescription ::= SEQUENCE { accessMethod OID, accessLocation GeneralName } */ |
|
432
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext_value)) { |
|
433
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *ad = ext_value->children; |
|
434
|
0
|
0
|
|
|
|
|
while (ad) { |
|
435
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ad)) { |
|
436
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *method = pdfmake_asn1_child_at(ad, 0); |
|
437
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *location = pdfmake_asn1_child_at(ad, 1); |
|
438
|
|
|
|
|
|
|
|
|
439
|
0
|
0
|
|
|
|
|
if (method && location && pdfmake_asn1_oid_equals(method, OID_OCSP)) { |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
440
|
|
|
|
|
|
|
/* OCSP responder URL in [6] uniformResourceIdentifier */ |
|
441
|
0
|
0
|
|
|
|
|
if ((location->tag & 0x1F) == 6 && |
|
442
|
0
|
0
|
|
|
|
|
(location->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT) { |
|
443
|
0
|
|
|
|
|
|
char *url = pdfmake_arena_alloc(arena, location->length + 1); |
|
444
|
0
|
0
|
|
|
|
|
if (url) { |
|
445
|
0
|
|
|
|
|
|
memcpy(url, location->data, location->length); |
|
446
|
0
|
|
|
|
|
|
url[location->length] = '\0'; |
|
447
|
0
|
|
|
|
|
|
cert->ocsp_responder = url; |
|
448
|
|
|
|
|
|
|
} |
|
449
|
|
|
|
|
|
|
} |
|
450
|
|
|
|
|
|
|
} |
|
451
|
|
|
|
|
|
|
} |
|
452
|
0
|
|
|
|
|
|
ad = ad->next; |
|
453
|
|
|
|
|
|
|
} |
|
454
|
|
|
|
|
|
|
} |
|
455
|
|
|
|
|
|
|
} |
|
456
|
|
|
|
|
|
|
/* CRL Distribution Points */ |
|
457
|
0
|
0
|
|
|
|
|
else if (pdfmake_asn1_oid_equals(oid_node, OID_CRL_DISTRIBUTION)) { |
|
458
|
|
|
|
|
|
|
/* CRLDistributionPoints ::= SEQUENCE OF DistributionPoint */ |
|
459
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext_value)) { |
|
460
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *dp = ext_value->children; |
|
461
|
0
|
0
|
|
|
|
|
while (dp && !cert->crl_distribution) { |
|
|
|
0
|
|
|
|
|
|
|
462
|
0
|
0
|
|
|
|
|
if (pdfmake_asn1_is_sequence(dp)) { |
|
463
|
|
|
|
|
|
|
/* DistributionPoint has [0] distributionPoint CHOICE */ |
|
464
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *dpname = dp->children; |
|
465
|
0
|
0
|
|
|
|
|
while (dpname && !cert->crl_distribution) { |
|
|
|
0
|
|
|
|
|
|
|
466
|
0
|
0
|
|
|
|
|
if ((dpname->tag & 0x1F) == 0 && |
|
467
|
0
|
0
|
|
|
|
|
(dpname->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT && |
|
468
|
0
|
0
|
|
|
|
|
(dpname->tag & ASN1_CONSTRUCTED)) { |
|
469
|
|
|
|
|
|
|
/* [0] fullName GeneralNames */ |
|
470
|
0
|
|
|
|
|
|
pdfmake_asn1_node_t *gn = dpname->children; |
|
471
|
0
|
0
|
|
|
|
|
while (gn && !cert->crl_distribution) { |
|
|
|
0
|
|
|
|
|
|
|
472
|
|
|
|
|
|
|
/* GeneralName: [6] uniformResourceIdentifier */ |
|
473
|
0
|
0
|
|
|
|
|
if ((gn->tag & 0x1F) == 6 && |
|
474
|
0
|
0
|
|
|
|
|
(gn->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT) { |
|
475
|
0
|
|
|
|
|
|
char *url = pdfmake_arena_alloc(arena, gn->length + 1); |
|
476
|
0
|
0
|
|
|
|
|
if (url) { |
|
477
|
0
|
|
|
|
|
|
memcpy(url, gn->data, gn->length); |
|
478
|
0
|
|
|
|
|
|
url[gn->length] = '\0'; |
|
479
|
0
|
|
|
|
|
|
cert->crl_distribution = url; |
|
480
|
|
|
|
|
|
|
} |
|
481
|
|
|
|
|
|
|
} |
|
482
|
0
|
|
|
|
|
|
gn = gn->next; |
|
483
|
|
|
|
|
|
|
} |
|
484
|
|
|
|
|
|
|
} |
|
485
|
0
|
|
|
|
|
|
dpname = dpname->next; |
|
486
|
|
|
|
|
|
|
} |
|
487
|
|
|
|
|
|
|
} |
|
488
|
0
|
|
|
|
|
|
dp = dp->next; |
|
489
|
|
|
|
|
|
|
} |
|
490
|
|
|
|
|
|
|
} |
|
491
|
|
|
|
|
|
|
} |
|
492
|
|
|
|
|
|
|
} |
|
493
|
|
|
|
|
|
|
|
|
494
|
|
|
|
|
|
|
/* Parse extensions */ |
|
495
|
1
|
|
|
|
|
|
static void parse_extensions( |
|
496
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
497
|
|
|
|
|
|
|
const pdfmake_asn1_node_t *extensions, |
|
498
|
|
|
|
|
|
|
pdfmake_x509_cert_t *cert) |
|
499
|
|
|
|
|
|
|
{ |
|
500
|
|
|
|
|
|
|
pdfmake_asn1_node_t *ext; |
|
501
|
|
|
|
|
|
|
|
|
502
|
1
|
50
|
|
|
|
|
if (!extensions) return; |
|
503
|
|
|
|
|
|
|
|
|
504
|
|
|
|
|
|
|
/* Extensions is SEQUENCE of Extension */ |
|
505
|
1
|
|
|
|
|
|
ext = extensions->children; |
|
506
|
4
|
100
|
|
|
|
|
while (ext) { |
|
507
|
3
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(ext)) { |
|
508
|
3
|
|
|
|
|
|
parse_extension(arena, ext, cert); |
|
509
|
|
|
|
|
|
|
} |
|
510
|
3
|
|
|
|
|
|
ext = ext->next; |
|
511
|
|
|
|
|
|
|
} |
|
512
|
|
|
|
|
|
|
} |
|
513
|
|
|
|
|
|
|
|
|
514
|
|
|
|
|
|
|
/*============================================================================ |
|
515
|
|
|
|
|
|
|
* Public API |
|
516
|
|
|
|
|
|
|
*==========================================================================*/ |
|
517
|
|
|
|
|
|
|
|
|
518
|
1
|
|
|
|
|
|
pdfmake_x509_cert_t *pdfmake_x509_parse_der( |
|
519
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
520
|
|
|
|
|
|
|
const uint8_t *der, |
|
521
|
|
|
|
|
|
|
size_t len) |
|
522
|
|
|
|
|
|
|
{ |
|
523
|
|
|
|
|
|
|
pdfmake_asn1_node_t *root; |
|
524
|
|
|
|
|
|
|
pdfmake_asn1_node_t *tbs; |
|
525
|
|
|
|
|
|
|
pdfmake_asn1_node_t *sig_alg; |
|
526
|
|
|
|
|
|
|
pdfmake_asn1_node_t *sig_val; |
|
527
|
|
|
|
|
|
|
pdfmake_x509_cert_t *cert; |
|
528
|
|
|
|
|
|
|
size_t tbs_offset; |
|
529
|
|
|
|
|
|
|
const uint8_t *sig_bits; |
|
530
|
|
|
|
|
|
|
size_t sig_bit_count; |
|
531
|
|
|
|
|
|
|
pdfmake_asn1_node_t *field; |
|
532
|
|
|
|
|
|
|
|
|
533
|
1
|
50
|
|
|
|
|
if (!arena || !der || len == 0) return NULL; |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
534
|
|
|
|
|
|
|
|
|
535
|
|
|
|
|
|
|
/* Parse the certificate ASN.1 */ |
|
536
|
1
|
|
|
|
|
|
root = pdfmake_asn1_parse(arena, der, len); |
|
537
|
1
|
50
|
|
|
|
|
if (!pdfmake_asn1_is_sequence(root)) { |
|
538
|
0
|
|
|
|
|
|
return NULL; |
|
539
|
|
|
|
|
|
|
} |
|
540
|
|
|
|
|
|
|
|
|
541
|
|
|
|
|
|
|
/* Certificate ::= SEQUENCE { |
|
542
|
|
|
|
|
|
|
tbsCertificate TBSCertificate, |
|
543
|
|
|
|
|
|
|
signatureAlgorithm AlgorithmIdentifier, |
|
544
|
|
|
|
|
|
|
signatureValue BIT STRING } */ |
|
545
|
|
|
|
|
|
|
|
|
546
|
1
|
|
|
|
|
|
tbs = pdfmake_asn1_child_at(root, 0); |
|
547
|
1
|
|
|
|
|
|
sig_alg = pdfmake_asn1_child_at(root, 1); |
|
548
|
1
|
|
|
|
|
|
sig_val = pdfmake_asn1_child_at(root, 2); |
|
549
|
|
|
|
|
|
|
|
|
550
|
1
|
50
|
|
|
|
|
if (!tbs || !sig_alg || !sig_val) { |
|
|
|
50
|
|
|
|
|
|
|
|
|
50
|
|
|
|
|
|
|
551
|
0
|
|
|
|
|
|
return NULL; |
|
552
|
|
|
|
|
|
|
} |
|
553
|
|
|
|
|
|
|
|
|
554
|
|
|
|
|
|
|
/* Allocate certificate */ |
|
555
|
1
|
|
|
|
|
|
cert = pdfmake_arena_alloc(arena, sizeof(pdfmake_x509_cert_t)); |
|
556
|
1
|
50
|
|
|
|
|
if (!cert) return NULL; |
|
557
|
1
|
|
|
|
|
|
memset(cert, 0, sizeof(pdfmake_x509_cert_t)); |
|
558
|
|
|
|
|
|
|
|
|
559
|
1
|
|
|
|
|
|
cert->arena = arena; |
|
560
|
1
|
|
|
|
|
|
cert->der = der; |
|
561
|
1
|
|
|
|
|
|
cert->der_len = len; |
|
562
|
1
|
|
|
|
|
|
cert->path_len_constraint = -1; |
|
563
|
|
|
|
|
|
|
|
|
564
|
|
|
|
|
|
|
/* Store TBS certificate for signature verification */ |
|
565
|
|
|
|
|
|
|
/* We need to find the raw bytes of the TBS certificate */ |
|
566
|
1
|
|
|
|
|
|
cert->tbs_certificate = tbs->data - 1; /* Include tag byte (approximate) */ |
|
567
|
2
|
50
|
|
|
|
|
cert->tbs_certificate_len = tbs->length + 2 + (tbs->length < 0x80 ? 0 : |
|
568
|
1
|
50
|
|
|
|
|
tbs->length < 0x100 ? 1 : |
|
569
|
1
|
50
|
|
|
|
|
tbs->length < 0x10000 ? 2 : 3); |
|
570
|
|
|
|
|
|
|
|
|
571
|
|
|
|
|
|
|
/* Actually, we need to recalculate properly - get offset from start */ |
|
572
|
|
|
|
|
|
|
/* For now, use the first child's data pointer minus some offset */ |
|
573
|
1
|
50
|
|
|
|
|
tbs_offset = (tbs->children ? tbs->children->data : tbs->data) - der; |
|
574
|
|
|
|
|
|
|
/* Back up to find the SEQUENCE tag */ |
|
575
|
6
|
50
|
|
|
|
|
while (tbs_offset > 0 && der[tbs_offset - 1] != (ASN1_TAG_SEQUENCE | ASN1_CONSTRUCTED)) { |
|
|
|
100
|
|
|
|
|
|
|
576
|
5
|
|
|
|
|
|
tbs_offset--; |
|
577
|
|
|
|
|
|
|
} |
|
578
|
1
|
50
|
|
|
|
|
if (tbs_offset > 0) tbs_offset--; |
|
579
|
1
|
|
|
|
|
|
cert->tbs_certificate = der + tbs_offset; |
|
580
|
|
|
|
|
|
|
/* Find length by looking at sig_alg position */ |
|
581
|
1
|
|
|
|
|
|
cert->tbs_certificate_len = (sig_alg->data - cert->tbs_certificate - 1); |
|
582
|
|
|
|
|
|
|
|
|
583
|
|
|
|
|
|
|
/* Parse signature algorithm */ |
|
584
|
1
|
|
|
|
|
|
cert->sig_algorithm_oid = parse_algorithm_id(arena, sig_alg); |
|
585
|
1
|
|
|
|
|
|
cert->sig_algorithm = parse_sig_algorithm(cert->sig_algorithm_oid); |
|
586
|
|
|
|
|
|
|
|
|
587
|
|
|
|
|
|
|
/* Parse signature value */ |
|
588
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_get_bit_string(sig_val, &sig_bits, &sig_bit_count) == 0) { |
|
589
|
1
|
|
|
|
|
|
cert->signature = sig_bits; |
|
590
|
1
|
|
|
|
|
|
cert->signature_len = sig_bit_count / 8; |
|
591
|
|
|
|
|
|
|
} |
|
592
|
|
|
|
|
|
|
|
|
593
|
|
|
|
|
|
|
/* Parse TBSCertificate */ |
|
594
|
|
|
|
|
|
|
/* TBSCertificate ::= SEQUENCE { |
|
595
|
|
|
|
|
|
|
version [0] EXPLICIT Version DEFAULT v1, |
|
596
|
|
|
|
|
|
|
serialNumber CertificateSerialNumber, |
|
597
|
|
|
|
|
|
|
signature AlgorithmIdentifier, |
|
598
|
|
|
|
|
|
|
issuer Name, |
|
599
|
|
|
|
|
|
|
validity Validity, |
|
600
|
|
|
|
|
|
|
subject Name, |
|
601
|
|
|
|
|
|
|
subjectPublicKeyInfo SubjectPublicKeyInfo, |
|
602
|
|
|
|
|
|
|
... extensions [3] EXPLICIT Extensions OPTIONAL } */ |
|
603
|
|
|
|
|
|
|
|
|
604
|
1
|
50
|
|
|
|
|
if (!pdfmake_asn1_is_sequence(tbs)) { |
|
605
|
0
|
|
|
|
|
|
return NULL; |
|
606
|
|
|
|
|
|
|
} |
|
607
|
|
|
|
|
|
|
|
|
608
|
1
|
|
|
|
|
|
field = tbs->children; |
|
609
|
|
|
|
|
|
|
|
|
610
|
|
|
|
|
|
|
/* Version (optional, [0] EXPLICIT) */ |
|
611
|
1
|
50
|
|
|
|
|
if (field && (field->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT && |
|
|
|
50
|
|
|
|
|
|
|
612
|
1
|
50
|
|
|
|
|
(field->tag & 0x1F) == 0) { |
|
613
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *ver = field->children; |
|
614
|
1
|
50
|
|
|
|
|
if (ver) { |
|
615
|
|
|
|
|
|
|
int64_t v; |
|
616
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_get_int64(ver, &v) == 0) { |
|
617
|
1
|
|
|
|
|
|
cert->version = (int)v; |
|
618
|
|
|
|
|
|
|
} |
|
619
|
|
|
|
|
|
|
} |
|
620
|
1
|
|
|
|
|
|
field = field->next; |
|
621
|
|
|
|
|
|
|
} |
|
622
|
|
|
|
|
|
|
|
|
623
|
|
|
|
|
|
|
/* Serial number */ |
|
624
|
1
|
50
|
|
|
|
|
if (field && field->tag == ASN1_TAG_INTEGER) { |
|
|
|
50
|
|
|
|
|
|
|
625
|
1
|
|
|
|
|
|
cert->serial = pdfmake_arena_memdup(arena, field->data, field->length); |
|
626
|
1
|
|
|
|
|
|
cert->serial_len = field->length; |
|
627
|
1
|
|
|
|
|
|
cert->serial_hex = bytes_to_hex(arena, field->data, field->length); |
|
628
|
1
|
|
|
|
|
|
field = field->next; |
|
629
|
|
|
|
|
|
|
} |
|
630
|
|
|
|
|
|
|
|
|
631
|
|
|
|
|
|
|
/* Signature algorithm (skip, same as outer) */ |
|
632
|
1
|
50
|
|
|
|
|
if (field) { |
|
633
|
1
|
|
|
|
|
|
field = field->next; |
|
634
|
|
|
|
|
|
|
} |
|
635
|
|
|
|
|
|
|
|
|
636
|
|
|
|
|
|
|
/* Issuer */ |
|
637
|
1
|
50
|
|
|
|
|
if (field) { |
|
638
|
1
|
|
|
|
|
|
parse_name(arena, field, &cert->issuer); |
|
639
|
1
|
|
|
|
|
|
field = field->next; |
|
640
|
|
|
|
|
|
|
} |
|
641
|
|
|
|
|
|
|
|
|
642
|
|
|
|
|
|
|
/* Validity */ |
|
643
|
1
|
50
|
|
|
|
|
if (pdfmake_asn1_is_sequence(field)) { |
|
644
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *not_before = pdfmake_asn1_child_at(field, 0); |
|
645
|
1
|
|
|
|
|
|
pdfmake_asn1_node_t *not_after = pdfmake_asn1_child_at(field, 1); |
|
646
|
|
|
|
|
|
|
|
|
647
|
1
|
50
|
|
|
|
|
if (not_before) pdfmake_asn1_get_time(not_before, &cert->not_before); |
|
648
|
1
|
50
|
|
|
|
|
if (not_after) pdfmake_asn1_get_time(not_after, &cert->not_after); |
|
649
|
|
|
|
|
|
|
|
|
650
|
1
|
|
|
|
|
|
field = field->next; |
|
651
|
|
|
|
|
|
|
} |
|
652
|
|
|
|
|
|
|
|
|
653
|
|
|
|
|
|
|
/* Subject */ |
|
654
|
1
|
50
|
|
|
|
|
if (field) { |
|
655
|
1
|
|
|
|
|
|
parse_name(arena, field, &cert->subject); |
|
656
|
1
|
|
|
|
|
|
field = field->next; |
|
657
|
|
|
|
|
|
|
} |
|
658
|
|
|
|
|
|
|
|
|
659
|
|
|
|
|
|
|
/* Subject Public Key Info */ |
|
660
|
1
|
50
|
|
|
|
|
if (field) { |
|
661
|
1
|
|
|
|
|
|
parse_pubkey_info(arena, field, &cert->pubkey); |
|
662
|
1
|
|
|
|
|
|
field = field->next; |
|
663
|
|
|
|
|
|
|
} |
|
664
|
|
|
|
|
|
|
|
|
665
|
|
|
|
|
|
|
/* Skip issuerUniqueID [1] and subjectUniqueID [2] if present */ |
|
666
|
2
|
100
|
|
|
|
|
while (field && (field->tag & ASN1_CLASS_MASK) == ASN1_CLASS_CONTEXT) { |
|
|
|
50
|
|
|
|
|
|
|
667
|
1
|
50
|
|
|
|
|
if ((field->tag & 0x1F) == 3) { |
|
668
|
|
|
|
|
|
|
/* Extensions [3] */ |
|
669
|
1
|
50
|
|
|
|
|
if (field->children) { |
|
670
|
1
|
|
|
|
|
|
parse_extensions(arena, field->children, cert); |
|
671
|
|
|
|
|
|
|
} |
|
672
|
|
|
|
|
|
|
} |
|
673
|
1
|
|
|
|
|
|
field = field->next; |
|
674
|
|
|
|
|
|
|
} |
|
675
|
|
|
|
|
|
|
|
|
676
|
|
|
|
|
|
|
/* Check if self-signed */ |
|
677
|
1
|
50
|
|
|
|
|
if (cert->issuer.dn && cert->subject.dn && |
|
|
|
50
|
|
|
|
|
|
|
678
|
1
|
50
|
|
|
|
|
strcmp(cert->issuer.dn, cert->subject.dn) == 0) { |
|
679
|
1
|
|
|
|
|
|
cert->is_self_signed = 1; |
|
680
|
|
|
|
|
|
|
} |
|
681
|
|
|
|
|
|
|
|
|
682
|
1
|
|
|
|
|
|
return cert; |
|
683
|
|
|
|
|
|
|
} |
|
684
|
|
|
|
|
|
|
|
|
685
|
0
|
|
|
|
|
|
pdfmake_x509_cert_t *pdfmake_x509_parse_pem( |
|
686
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
687
|
|
|
|
|
|
|
const char *pem, |
|
688
|
|
|
|
|
|
|
size_t len) |
|
689
|
|
|
|
|
|
|
{ |
|
690
|
|
|
|
|
|
|
const char *begin; |
|
691
|
|
|
|
|
|
|
const char *end; |
|
692
|
|
|
|
|
|
|
size_t b64_len; |
|
693
|
|
|
|
|
|
|
size_t max_der_len; |
|
694
|
|
|
|
|
|
|
uint8_t *der; |
|
695
|
|
|
|
|
|
|
size_t der_len; |
|
696
|
|
|
|
|
|
|
|
|
697
|
0
|
0
|
|
|
|
|
if (!arena || !pem || len == 0) return NULL; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
698
|
|
|
|
|
|
|
|
|
699
|
|
|
|
|
|
|
/* Find BEGIN marker */ |
|
700
|
0
|
|
|
|
|
|
begin = strstr(pem, "-----BEGIN CERTIFICATE-----"); |
|
701
|
0
|
0
|
|
|
|
|
if (!begin) return NULL; |
|
702
|
0
|
|
|
|
|
|
begin += 27; /* Skip marker */ |
|
703
|
|
|
|
|
|
|
|
|
704
|
|
|
|
|
|
|
/* Find END marker */ |
|
705
|
0
|
|
|
|
|
|
end = strstr(begin, "-----END CERTIFICATE-----"); |
|
706
|
0
|
0
|
|
|
|
|
if (!end) return NULL; |
|
707
|
|
|
|
|
|
|
|
|
708
|
|
|
|
|
|
|
/* Allocate buffer for decoded data */ |
|
709
|
0
|
|
|
|
|
|
b64_len = end - begin; |
|
710
|
0
|
|
|
|
|
|
max_der_len = (b64_len * 3) / 4 + 4; |
|
711
|
0
|
|
|
|
|
|
der = pdfmake_arena_alloc(arena, max_der_len); |
|
712
|
0
|
0
|
|
|
|
|
if (!der) return NULL; |
|
713
|
|
|
|
|
|
|
|
|
714
|
|
|
|
|
|
|
/* Decode base64 */ |
|
715
|
0
|
|
|
|
|
|
der_len = base64_decode(begin, b64_len, der, max_der_len); |
|
716
|
0
|
0
|
|
|
|
|
if (der_len == 0) return NULL; |
|
717
|
|
|
|
|
|
|
|
|
718
|
0
|
|
|
|
|
|
return pdfmake_x509_parse_der(arena, der, der_len); |
|
719
|
|
|
|
|
|
|
} |
|
720
|
|
|
|
|
|
|
|
|
721
|
0
|
|
|
|
|
|
pdfmake_cert_chain_t *pdfmake_x509_parse_pem_chain( |
|
722
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
723
|
|
|
|
|
|
|
const char *pem, |
|
724
|
|
|
|
|
|
|
size_t len) |
|
725
|
|
|
|
|
|
|
{ |
|
726
|
|
|
|
|
|
|
pdfmake_cert_chain_t *chain; |
|
727
|
|
|
|
|
|
|
const char *p; |
|
728
|
|
|
|
|
|
|
const char *end; |
|
729
|
|
|
|
|
|
|
pdfmake_x509_cert_t *last; |
|
730
|
|
|
|
|
|
|
const char *begin; |
|
731
|
|
|
|
|
|
|
const char *cert_end; |
|
732
|
|
|
|
|
|
|
pdfmake_x509_cert_t *cert; |
|
733
|
|
|
|
|
|
|
|
|
734
|
0
|
0
|
|
|
|
|
if (!arena || !pem || len == 0) return NULL; |
|
|
|
0
|
|
|
|
|
|
|
|
|
0
|
|
|
|
|
|
|
735
|
|
|
|
|
|
|
|
|
736
|
0
|
|
|
|
|
|
chain = pdfmake_arena_alloc(arena, sizeof(pdfmake_cert_chain_t)); |
|
737
|
0
|
0
|
|
|
|
|
if (!chain) return NULL; |
|
738
|
0
|
|
|
|
|
|
memset(chain, 0, sizeof(pdfmake_cert_chain_t)); |
|
739
|
0
|
|
|
|
|
|
chain->arena = arena; |
|
740
|
|
|
|
|
|
|
|
|
741
|
0
|
|
|
|
|
|
p = pem; |
|
742
|
0
|
|
|
|
|
|
end = pem + len; |
|
743
|
0
|
|
|
|
|
|
last = NULL; |
|
744
|
|
|
|
|
|
|
|
|
745
|
0
|
0
|
|
|
|
|
while (p < end) { |
|
746
|
0
|
|
|
|
|
|
begin = strstr(p, "-----BEGIN CERTIFICATE-----"); |
|
747
|
0
|
0
|
|
|
|
|
if (!begin || begin >= end) break; |
|
|
|
0
|
|
|
|
|
|
|
748
|
|
|
|
|
|
|
|
|
749
|
0
|
|
|
|
|
|
cert_end = strstr(begin, "-----END CERTIFICATE-----"); |
|
750
|
0
|
0
|
|
|
|
|
if (!cert_end || cert_end >= end) break; |
|
|
|
0
|
|
|
|
|
|
|
751
|
0
|
|
|
|
|
|
cert_end += 25; /* Include end marker */ |
|
752
|
|
|
|
|
|
|
|
|
753
|
0
|
|
|
|
|
|
cert = pdfmake_x509_parse_pem(arena, begin, cert_end - begin); |
|
754
|
0
|
0
|
|
|
|
|
if (cert) { |
|
755
|
0
|
0
|
|
|
|
|
if (last) { |
|
756
|
0
|
|
|
|
|
|
last->next = cert; |
|
757
|
|
|
|
|
|
|
} else { |
|
758
|
0
|
|
|
|
|
|
chain->certs = cert; |
|
759
|
|
|
|
|
|
|
} |
|
760
|
0
|
|
|
|
|
|
last = cert; |
|
761
|
0
|
|
|
|
|
|
chain->count++; |
|
762
|
|
|
|
|
|
|
} |
|
763
|
|
|
|
|
|
|
|
|
764
|
0
|
|
|
|
|
|
p = cert_end; |
|
765
|
|
|
|
|
|
|
} |
|
766
|
|
|
|
|
|
|
|
|
767
|
0
|
|
|
|
|
|
return chain; |
|
768
|
|
|
|
|
|
|
} |
|
769
|
|
|
|
|
|
|
|
|
770
|
0
|
|
|
|
|
|
pdfmake_x509_cert_t *pdfmake_x509_load_file( |
|
771
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
772
|
|
|
|
|
|
|
const char *path) |
|
773
|
|
|
|
|
|
|
{ |
|
774
|
|
|
|
|
|
|
FILE *f; |
|
775
|
|
|
|
|
|
|
long size; |
|
776
|
|
|
|
|
|
|
uint8_t *data; |
|
777
|
|
|
|
|
|
|
|
|
778
|
0
|
0
|
|
|
|
|
if (!arena || !path) return NULL; |
|
|
|
0
|
|
|
|
|
|
|
779
|
|
|
|
|
|
|
|
|
780
|
0
|
|
|
|
|
|
f = fopen(path, "rb"); |
|
781
|
0
|
0
|
|
|
|
|
if (!f) return NULL; |
|
782
|
|
|
|
|
|
|
|
|
783
|
|
|
|
|
|
|
/* Get file size */ |
|
784
|
0
|
|
|
|
|
|
fseek(f, 0, SEEK_END); |
|
785
|
0
|
|
|
|
|
|
size = ftell(f); |
|
786
|
0
|
|
|
|
|
|
fseek(f, 0, SEEK_SET); |
|
787
|
|
|
|
|
|
|
|
|
788
|
0
|
0
|
|
|
|
|
if (size <= 0 || size > 1024 * 1024) { /* Max 1MB */ |
|
|
|
0
|
|
|
|
|
|
|
789
|
0
|
|
|
|
|
|
fclose(f); |
|
790
|
0
|
|
|
|
|
|
return NULL; |
|
791
|
|
|
|
|
|
|
} |
|
792
|
|
|
|
|
|
|
|
|
793
|
|
|
|
|
|
|
/* Read file */ |
|
794
|
0
|
|
|
|
|
|
data = pdfmake_arena_alloc(arena, size); |
|
795
|
0
|
0
|
|
|
|
|
if (!data) { |
|
796
|
0
|
|
|
|
|
|
fclose(f); |
|
797
|
0
|
|
|
|
|
|
return NULL; |
|
798
|
|
|
|
|
|
|
} |
|
799
|
|
|
|
|
|
|
|
|
800
|
0
|
0
|
|
|
|
|
if (fread(data, 1, size, f) != (size_t)size) { |
|
801
|
0
|
|
|
|
|
|
fclose(f); |
|
802
|
0
|
|
|
|
|
|
return NULL; |
|
803
|
|
|
|
|
|
|
} |
|
804
|
0
|
|
|
|
|
|
fclose(f); |
|
805
|
|
|
|
|
|
|
|
|
806
|
|
|
|
|
|
|
/* Check for PEM format */ |
|
807
|
0
|
0
|
|
|
|
|
if (size > 27 && memcmp(data, "-----BEGIN", 10) == 0) { |
|
|
|
0
|
|
|
|
|
|
|
808
|
0
|
|
|
|
|
|
return pdfmake_x509_parse_pem(arena, (const char *)data, size); |
|
809
|
|
|
|
|
|
|
} |
|
810
|
|
|
|
|
|
|
|
|
811
|
|
|
|
|
|
|
/* Assume DER */ |
|
812
|
0
|
|
|
|
|
|
return pdfmake_x509_parse_der(arena, data, size); |
|
813
|
|
|
|
|
|
|
} |
|
814
|
|
|
|
|
|
|
|
|
815
|
0
|
|
|
|
|
|
int pdfmake_x509_is_valid( |
|
816
|
|
|
|
|
|
|
const pdfmake_x509_cert_t *cert, |
|
817
|
|
|
|
|
|
|
int64_t check_time) |
|
818
|
|
|
|
|
|
|
{ |
|
819
|
0
|
0
|
|
|
|
|
if (!cert) return 0; |
|
820
|
|
|
|
|
|
|
|
|
821
|
0
|
0
|
|
|
|
|
if (check_time == 0) { |
|
822
|
0
|
|
|
|
|
|
check_time = time(NULL); |
|
823
|
|
|
|
|
|
|
} |
|
824
|
|
|
|
|
|
|
|
|
825
|
0
|
0
|
|
|
|
|
return (check_time >= cert->not_before && check_time <= cert->not_after); |
|
|
|
0
|
|
|
|
|
|
|
826
|
|
|
|
|
|
|
} |
|
827
|
|
|
|
|
|
|
|
|
828
|
1
|
|
|
|
|
|
int pdfmake_x509_can_sign_documents(const pdfmake_x509_cert_t *cert) |
|
829
|
|
|
|
|
|
|
{ |
|
830
|
1
|
50
|
|
|
|
|
if (!cert) return 0; |
|
831
|
|
|
|
|
|
|
|
|
832
|
|
|
|
|
|
|
/* Check key usage if present */ |
|
833
|
1
|
50
|
|
|
|
|
if (cert->key_usage != 0) { |
|
834
|
|
|
|
|
|
|
/* Need digitalSignature or nonRepudiation */ |
|
835
|
0
|
0
|
|
|
|
|
if (!(cert->key_usage & (PDFMAKE_KU_DIGITAL_SIGNATURE | PDFMAKE_KU_NON_REPUDIATION))) { |
|
836
|
0
|
|
|
|
|
|
return 0; |
|
837
|
|
|
|
|
|
|
} |
|
838
|
|
|
|
|
|
|
} |
|
839
|
|
|
|
|
|
|
|
|
840
|
|
|
|
|
|
|
/* Check extended key usage if present */ |
|
841
|
1
|
50
|
|
|
|
|
if (cert->ext_key_usage != 0) { |
|
842
|
|
|
|
|
|
|
/* Need one of: document signing, PDF signing, email protection, or code signing */ |
|
843
|
0
|
|
|
|
|
|
uint32_t signing_ekus = PDFMAKE_EKU_DOCUMENT_SIGNING | PDFMAKE_EKU_PDF_SIGNING | |
|
844
|
|
|
|
|
|
|
PDFMAKE_EKU_EMAIL_PROTECTION | PDFMAKE_EKU_CODE_SIGNING; |
|
845
|
0
|
0
|
|
|
|
|
if (!(cert->ext_key_usage & signing_ekus)) { |
|
846
|
0
|
|
|
|
|
|
return 0; |
|
847
|
|
|
|
|
|
|
} |
|
848
|
|
|
|
|
|
|
} |
|
849
|
|
|
|
|
|
|
|
|
850
|
1
|
|
|
|
|
|
return 1; |
|
851
|
|
|
|
|
|
|
} |
|
852
|
|
|
|
|
|
|
|
|
853
|
0
|
|
|
|
|
|
char *pdfmake_x509_format_name( |
|
854
|
|
|
|
|
|
|
pdfmake_arena_t *arena, |
|
855
|
|
|
|
|
|
|
const pdfmake_x509_name_t *name) |
|
856
|
|
|
|
|
|
|
{ |
|
857
|
|
|
|
|
|
|
char buf[512]; |
|
858
|
|
|
|
|
|
|
char *p; |
|
859
|
|
|
|
|
|
|
char *end; |
|
860
|
|
|
|
|
|
|
|
|
861
|
0
|
0
|
|
|
|
|
if (!arena || !name) return NULL; |
|
|
|
0
|
|
|
|
|
|
|
862
|
|
|
|
|
|
|
|
|
863
|
|
|
|
|
|
|
/* Already have DN string */ |
|
864
|
0
|
0
|
|
|
|
|
if (name->dn) { |
|
865
|
0
|
|
|
|
|
|
return pdfmake_arena_strdup(arena, name->dn); |
|
866
|
|
|
|
|
|
|
} |
|
867
|
|
|
|
|
|
|
|
|
868
|
|
|
|
|
|
|
/* Build from components */ |
|
869
|
0
|
|
|
|
|
|
p = buf; |
|
870
|
0
|
|
|
|
|
|
end = buf + sizeof(buf) - 1; |
|
871
|
|
|
|
|
|
|
|
|
872
|
0
|
0
|
|
|
|
|
if (name->common_name) { |
|
873
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "CN=%s", name->common_name); |
|
874
|
|
|
|
|
|
|
} |
|
875
|
0
|
0
|
|
|
|
|
if (name->organization && p < end) { |
|
|
|
0
|
|
|
|
|
|
|
876
|
0
|
0
|
|
|
|
|
if (p > buf) p += snprintf(p, end - p, ", "); |
|
877
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "O=%s", name->organization); |
|
878
|
|
|
|
|
|
|
} |
|
879
|
0
|
0
|
|
|
|
|
if (name->organizational_unit && p < end) { |
|
|
|
0
|
|
|
|
|
|
|
880
|
0
|
0
|
|
|
|
|
if (p > buf) p += snprintf(p, end - p, ", "); |
|
881
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "OU=%s", name->organizational_unit); |
|
882
|
|
|
|
|
|
|
} |
|
883
|
0
|
0
|
|
|
|
|
if (name->locality && p < end) { |
|
|
|
0
|
|
|
|
|
|
|
884
|
0
|
0
|
|
|
|
|
if (p > buf) p += snprintf(p, end - p, ", "); |
|
885
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "L=%s", name->locality); |
|
886
|
|
|
|
|
|
|
} |
|
887
|
0
|
0
|
|
|
|
|
if (name->state && p < end) { |
|
|
|
0
|
|
|
|
|
|
|
888
|
0
|
0
|
|
|
|
|
if (p > buf) p += snprintf(p, end - p, ", "); |
|
889
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "ST=%s", name->state); |
|
890
|
|
|
|
|
|
|
} |
|
891
|
0
|
0
|
|
|
|
|
if (name->country && p < end) { |
|
|
|
0
|
|
|
|
|
|
|
892
|
0
|
0
|
|
|
|
|
if (p > buf) p += snprintf(p, end - p, ", "); |
|
893
|
0
|
|
|
|
|
|
p += snprintf(p, end - p, "C=%s", name->country); |
|
894
|
|
|
|
|
|
|
} |
|
895
|
|
|
|
|
|
|
|
|
896
|
0
|
|
|
|
|
|
*p = '\0'; |
|
897
|
0
|
|
|
|
|
|
return pdfmake_arena_strdup(arena, buf); |
|
898
|
|
|
|
|
|
|
} |
|
899
|
|
|
|
|
|
|
|
|
900
|
0
|
|
|
|
|
|
const char *pdfmake_x509_sig_algorithm_name(pdfmake_sig_algorithm_t alg) |
|
901
|
|
|
|
|
|
|
{ |
|
902
|
0
|
|
|
|
|
|
switch (alg) { |
|
903
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_RSA_MD5: return "RSA-MD5"; |
|
904
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_RSA_SHA1: return "RSA-SHA1"; |
|
905
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_RSA_SHA256: return "RSA-SHA256"; |
|
906
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_RSA_SHA384: return "RSA-SHA384"; |
|
907
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_RSA_SHA512: return "RSA-SHA512"; |
|
908
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_ECDSA_SHA256: return "ECDSA-SHA256"; |
|
909
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_ECDSA_SHA384: return "ECDSA-SHA384"; |
|
910
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_ECDSA_SHA512: return "ECDSA-SHA512"; |
|
911
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_ED25519: return "Ed25519"; |
|
912
|
0
|
|
|
|
|
|
case PDFMAKE_SIG_ED448: return "Ed448"; |
|
913
|
0
|
|
|
|
|
|
default: return "Unknown"; |
|
914
|
|
|
|
|
|
|
} |
|
915
|
|
|
|
|
|
|
} |
|
916
|
|
|
|
|
|
|
|
|
917
|
0
|
|
|
|
|
|
const char *pdfmake_x509_pk_algorithm_name(pdfmake_pk_algorithm_t alg) |
|
918
|
|
|
|
|
|
|
{ |
|
919
|
0
|
|
|
|
|
|
switch (alg) { |
|
920
|
0
|
|
|
|
|
|
case PDFMAKE_PK_RSA: return "RSA"; |
|
921
|
0
|
|
|
|
|
|
case PDFMAKE_PK_DSA: return "DSA"; |
|
922
|
0
|
|
|
|
|
|
case PDFMAKE_PK_ECDSA: return "ECDSA"; |
|
923
|
0
|
|
|
|
|
|
case PDFMAKE_PK_ED25519: return "Ed25519"; |
|
924
|
0
|
|
|
|
|
|
case PDFMAKE_PK_ED448: return "Ed448"; |
|
925
|
0
|
|
|
|
|
|
default: return "Unknown"; |
|
926
|
|
|
|
|
|
|
} |
|
927
|
|
|
|
|
|
|
} |
|
928
|
|
|
|
|
|
|
|
|
929
|
0
|
|
|
|
|
|
pdfmake_err_t pdfmake_x509_verify_signature( |
|
930
|
|
|
|
|
|
|
const pdfmake_x509_cert_t *cert, |
|
931
|
|
|
|
|
|
|
const pdfmake_x509_cert_t *issuer) |
|
932
|
|
|
|
|
|
|
{ |
|
933
|
|
|
|
|
|
|
/* TODO: Implement actual cryptographic verification */ |
|
934
|
|
|
|
|
|
|
/* This requires RSA/ECDSA signature verification with the issuer's public key */ |
|
935
|
|
|
|
|
|
|
/* For now, just return success for self-signed certs with matching issuer/subject */ |
|
936
|
|
|
|
|
|
|
|
|
937
|
0
|
0
|
|
|
|
|
if (!cert) return PDFMAKE_EINVAL; |
|
938
|
|
|
|
|
|
|
|
|
939
|
0
|
0
|
|
|
|
|
if (!issuer && cert->is_self_signed) { |
|
|
|
0
|
|
|
|
|
|
|
940
|
|
|
|
|
|
|
/* Self-signed certificate - would verify with own public key */ |
|
941
|
0
|
|
|
|
|
|
return PDFMAKE_OK; |
|
942
|
|
|
|
|
|
|
} |
|
943
|
|
|
|
|
|
|
|
|
944
|
0
|
0
|
|
|
|
|
if (issuer) { |
|
945
|
|
|
|
|
|
|
/* Would verify cert->signature against cert->tbs_certificate |
|
946
|
|
|
|
|
|
|
* using issuer->pubkey */ |
|
947
|
0
|
|
|
|
|
|
return PDFMAKE_OK; |
|
948
|
|
|
|
|
|
|
} |
|
949
|
|
|
|
|
|
|
|
|
950
|
0
|
|
|
|
|
|
return PDFMAKE_EINVAL; |
|
951
|
|
|
|
|
|
|
} |
|
952
|
|
|
|
|
|
|
|
|
953
|
0
|
|
|
|
|
|
pdfmake_err_t pdfmake_x509_verify_chain( |
|
954
|
|
|
|
|
|
|
const pdfmake_cert_chain_t *chain, |
|
955
|
|
|
|
|
|
|
const pdfmake_cert_chain_t *trust_anchors) |
|
956
|
|
|
|
|
|
|
{ |
|
957
|
|
|
|
|
|
|
pdfmake_x509_cert_t *cert; |
|
958
|
|
|
|
|
|
|
|
|
959
|
|
|
|
|
|
|
/* TODO: Implement full chain verification */ |
|
960
|
|
|
|
|
|
|
/* 1. Verify each cert is signed by the next in chain */ |
|
961
|
|
|
|
|
|
|
/* 2. Verify the last cert is in trust_anchors or is self-signed */ |
|
962
|
|
|
|
|
|
|
/* 3. Check validity periods */ |
|
963
|
|
|
|
|
|
|
/* 4. Check basic constraints (CA flag) */ |
|
964
|
|
|
|
|
|
|
|
|
965
|
0
|
0
|
|
|
|
|
if (!chain || chain->count == 0) return PDFMAKE_EINVAL; |
|
|
|
0
|
|
|
|
|
|
|
966
|
|
|
|
|
|
|
(void)trust_anchors; |
|
967
|
|
|
|
|
|
|
|
|
968
|
|
|
|
|
|
|
/* For now, basic validity check */ |
|
969
|
0
|
|
|
|
|
|
cert = chain->certs; |
|
970
|
0
|
0
|
|
|
|
|
while (cert) { |
|
971
|
0
|
0
|
|
|
|
|
if (!pdfmake_x509_is_valid(cert, 0)) { |
|
972
|
0
|
|
|
|
|
|
return PDFMAKE_EINVAL; /* Certificate expired */ |
|
973
|
|
|
|
|
|
|
} |
|
974
|
0
|
|
|
|
|
|
cert = cert->next; |
|
975
|
|
|
|
|
|
|
} |
|
976
|
|
|
|
|
|
|
|
|
977
|
0
|
|
|
|
|
|
return PDFMAKE_OK; |
|
978
|
|
|
|
|
|
|
} |
|
979
|
|
|
|
|
|
|
|
|
980
|
0
|
|
|
|
|
|
void pdfmake_x509_cert_free(pdfmake_x509_cert_t *cert) |
|
981
|
|
|
|
|
|
|
{ |
|
982
|
|
|
|
|
|
|
/* If allocated from arena, arena cleanup handles this */ |
|
983
|
|
|
|
|
|
|
(void)cert; |
|
984
|
0
|
|
|
|
|
|
} |
|
985
|
|
|
|
|
|
|
|
|
986
|
0
|
|
|
|
|
|
void pdfmake_cert_chain_free(pdfmake_cert_chain_t *chain) |
|
987
|
|
|
|
|
|
|
{ |
|
988
|
|
|
|
|
|
|
/* If allocated from arena, arena cleanup handles this */ |
|
989
|
|
|
|
|
|
|
(void)chain; |
|
990
|
0
|
|
|
|
|
|
} |