Line data Source code
1 : #ifndef HEADER_fd_src_ballet_x509_fd_x509_h
2 : #define HEADER_fd_src_ballet_x509_fd_x509_h
3 :
4 : /* fd_x509.h provides a minimal ASN.1 DER parser for X.509 certificates.
5 :
6 : Supported key / signature algorithms:
7 : - Ed25519 (OID 1.3.101.112)
8 : - ECDSA P-256 with SHA-256
9 : - ECDSA P-384 with SHA-384
10 : - RSA with PKCS#1 v1.5 and SHA-{256,384,512} */
11 :
12 : #include "../rsa/fd_rsa.h"
13 :
14 : /* Key type identifiers */
15 :
16 0 : #define FD_X509_KEY_ED25519 ((uchar)0)
17 0 : #define FD_X509_KEY_ECDSA_P256 ((uchar)1)
18 0 : #define FD_X509_KEY_ECDSA_P384 ((uchar)2)
19 0 : #define FD_X509_KEY_RSA ((uchar)3)
20 0 : #define FD_X509_KEY_UNKNOWN ((uchar)0xFF)
21 :
22 : /* FD_X509_PUBKEY_MAX is the max size of the subjectPublicKey content
23 : of a supported key type. The largest is the DER RSAPublicKey of an
24 : RSA-4096 key with a 64 bit exponent: SEQUENCE header (4), modulus
25 : INTEGER (5+512), exponent INTEGER (2+9). */
26 :
27 : #define FD_X509_PUBKEY_MAX (532UL)
28 :
29 : /* Signature algorithm identifiers */
30 :
31 0 : #define FD_X509_SIG_ED25519 ((uchar)0)
32 0 : #define FD_X509_SIG_ECDSA_SHA256 ((uchar)1)
33 0 : #define FD_X509_SIG_ECDSA_SHA384 ((uchar)2)
34 0 : #define FD_X509_SIG_RSA_SHA256 ((uchar)3) /* RSASSA-PKCS1-v1_5 */
35 0 : #define FD_X509_SIG_RSA_SHA384 ((uchar)4)
36 0 : #define FD_X509_SIG_RSA_SHA512 ((uchar)5)
37 0 : #define FD_X509_SIG_UNKNOWN ((uchar)0xFF)
38 :
39 : /* keyUsage bits (RFC 5280 Section 4.2.1.3), big endian */
40 :
41 : #define FD_X509_KU_DIGITAL_SIGNATURE ((ushort)0x8000) /* bit 0 */
42 : #define FD_X509_KU_NON_REPUDIATION ((ushort)0x4000) /* bit 1 */
43 : #define FD_X509_KU_KEY_ENCIPHERMENT ((ushort)0x2000) /* bit 2 */
44 : #define FD_X509_KU_DATA_ENCIPHERMENT ((ushort)0x1000) /* bit 3 */
45 : #define FD_X509_KU_KEY_AGREEMENT ((ushort)0x0800) /* bit 4 */
46 0 : #define FD_X509_KU_KEY_CERT_SIGN ((ushort)0x0400) /* bit 5 */
47 : #define FD_X509_KU_CRL_SIGN ((ushort)0x0200) /* bit 6 */
48 : #define FD_X509_KU_ENCIPHER_ONLY ((ushort)0x0100) /* bit 7 */
49 : #define FD_X509_KU_DECIPHER_ONLY ((ushort)0x0080) /* bit 8 */
50 :
51 : /* extKeyUsage purposes (RFC 5280 Section 4.2.1.12) */
52 :
53 0 : #define FD_X509_EKU_SERVER_AUTH ((ushort)0x0001) /* 1.3.6.1.5.5.7.3.1 */
54 0 : #define FD_X509_EKU_ANY ((ushort)0x0002) /* 2.5.29.37.0 */
55 :
56 : /* FD_X509_TIME_INVALID marks a validity timestamp that could not be
57 : parsed. */
58 :
59 0 : #define FD_X509_TIME_INVALID (LONG_MIN)
60 :
61 0 : #define FD_X509_EXT_MAX (64UL)
62 :
63 : struct fd_x509_cert_info {
64 : /* TBSCertificate version: 0=v1, 1=v2, 2=v3 */
65 : uchar version;
66 :
67 : /* Subject Public Key Info. pubkey is the subjectPublicKey BIT
68 : STRING content: a raw Ed25519 key, an uncompressed EC point, or a
69 : DER RSAPublicKey (see fd_x509_decode_rsa_pubkey) for the supported
70 : key types, opaque for FD_X509_KEY_UNKNOWN. */
71 : uchar const * pubkey;
72 : ulong pubkey_len;
73 : uchar key_type; /* FD_X509_KEY_{...} */
74 :
75 : /* TBS (to-be-signed) region */
76 : uchar const * tbs;
77 : ulong tbs_len;
78 :
79 : /* Issuer and Subject */
80 : uchar const * issuer;
81 : ulong issuer_len;
82 : uchar const * subject;
83 : ulong subject_len;
84 :
85 : /* Validity period */
86 : uchar const * not_before;
87 : ulong not_before_len;
88 : uchar not_before_tag; /* FD_DER_TAG_{UTC,GENERALIZED}_TIME */
89 : long not_before_unix; /* seconds since the Unix epoch (or FD_X509_TIME_INVALID) */
90 : uchar const * not_after;
91 : ulong not_after_len;
92 : uchar not_after_tag;
93 : long not_after_unix; /* seconds since the Unix epoch (or FD_X509_TIME_INVALID) */
94 :
95 : /* Certificate signature */
96 : uchar const * sig;
97 : ulong sig_len;
98 : uchar sig_alg; /* FD_X509_SIG_{...} */
99 :
100 : /* Basic Constraints (RFC 5280 Section 4.2.1.9) */
101 : ulong path_len_constraint; /* valid if has_path_len_constraint */
102 : int is_ca; /* 1 if basicConstraints cA=TRUE */
103 : uchar has_basic_constraints;
104 : uchar has_path_len_constraint;
105 : uchar basic_constraints_noncritical; /* 1 if cA=TRUE but extension is not critical */
106 :
107 : /* Key Usage (RFC 5280 Section 4.2.1.3) */
108 : ushort key_usage; /* FD_X509_KU_* bits, 0 if absent */
109 : uchar has_key_usage;
110 :
111 : /* Extended Key Usage (RFC 5280 Section 4.2.1.12) */
112 : ushort ext_key_usage; /* FD_X509_EKU_* bits, 0 if absent */
113 : uchar has_ext_key_usage;
114 :
115 : /* Subject Alternative Name GeneralNames content */
116 : uchar const * san_general_names;
117 : ulong san_general_names_len;
118 : uchar has_subject_alt_name;
119 :
120 : /* Name Constraints (RFC 5280 Section 4.2.1.10). Each pointer is the
121 : content of an IMPLICIT GeneralSubtrees field. The verifier
122 : enforces dNSName, iPAddress and directoryName subtrees; a subtree
123 : of any other form rejects certs carrying a SAN of that form. */
124 : uchar const * name_constraints_permitted;
125 : ulong name_constraints_permitted_len;
126 : uchar const * name_constraints_excluded;
127 : ulong name_constraints_excluded_len;
128 : uchar has_name_constraints;
129 : };
130 :
131 : typedef struct fd_x509_cert_info fd_x509_cert_info_t;
132 :
133 : FD_PROTOTYPES_BEGIN
134 :
135 : /* fd_x509_extract_pubkey parses cert and returns its subject public key and
136 : FD_X509_KEY_* type. Supported key types are Ed25519, ECDSA P-256,
137 : ECDSA P-384, and RSA. *out_pubkey aliases cert and remains valid only
138 : while cert remains valid. cert and all output arguments must be
139 : non-NULL.
140 :
141 : Returns 0 on success and -1 if cert is malformed or its key type is
142 : unsupported. */
143 :
144 : int
145 : fd_x509_extract_pubkey( uchar const * cert,
146 : ulong cert_sz,
147 : uchar const ** out_pubkey,
148 : ulong * out_pubkey_len,
149 : uchar * out_key_type );
150 :
151 :
152 : /* fd_x509_decode_ecdsa_sig decodes a DER-encoded ECDSA signature
153 : SEQUENCE { INTEGER r, INTEGER s } into raw_sig
154 : raw_sig must have room for 2*scalar_sz bytes, where scalar_sz is
155 : 32 for P256 and 48 for P384.
156 : Returns 0 on success, -1 on failure. */
157 :
158 : int
159 : fd_x509_decode_ecdsa_sig( uchar const * der,
160 : ulong der_len,
161 : uchar * raw_sig,
162 : ulong scalar_sz );
163 :
164 : /* fd_x509_decode_rsa_pubkey decodes the subjectPublicKey content of
165 : an FD_X509_KEY_RSA key, a DER RSAPublicKey ::= SEQUENCE { modulus
166 : INTEGER, publicExponent INTEGER }, into key. Returns 0 on success
167 : and -1 if pubkey is malformed or the key is outside the range fd_rsa
168 : supports (never the case for pubkey taken from a cert that parsed
169 : with key_type FD_X509_KEY_RSA). */
170 :
171 : int
172 : fd_x509_decode_rsa_pubkey( uchar const * pubkey,
173 : ulong pubkey_len,
174 : fd_rsa_pubkey_t * key );
175 :
176 : /* fd_x509_ec_point_compress compresses a supported
177 : uncompressed EC point (04 || x || y) into compressed form (02/03 || x).
178 : coord_sz must be 32 for P-256 or 48 for P-384. uncompressed must be
179 : at least 1+2*coord_sz bytes. Coordinates must be canonical and the
180 : exact supplied point must satisfy the curve equation. compressed
181 : must be at least 1+coord_sz bytes. Both pointers must be non-NULL.
182 : Returns 0 on success, -1 on failure. */
183 :
184 : int
185 : fd_x509_ec_point_compress( uchar const * uncompressed,
186 : ulong coord_sz,
187 : uchar * compressed );
188 :
189 : /* fd_x509_cert_parse fully parses a DER-encoded X.509 cert.
190 :
191 : Returns 0 on success and -1 if cert is malformed. An unsupported
192 : public key or signature algorithm is not malformed: out->key_type is
193 : then FD_X509_KEY_UNKNOWN or out->sig_alg is FD_X509_SIG_UNKNOWN, and
194 : the cert can be inspected but not used to verify anything.
195 :
196 : All pointers in *out refer into the [cert, cert+cert_sz) buffer. */
197 :
198 : int
199 : fd_x509_cert_parse( uchar const * cert,
200 : ulong cert_sz,
201 : fd_x509_cert_info_t * out );
202 :
203 : /* fd_x509_name_equal compares two DER-encoded X.509 Names according to
204 : the RFC 5280 distinguished-name matching rules supported by this
205 : verifier. Returns 1 if equal and 0 otherwise. */
206 :
207 : int
208 : fd_x509_name_equal( uchar const * a,
209 : ulong a_len,
210 : uchar const * b,
211 : ulong b_len );
212 :
213 : /* fd_x509_name_prefix returns 1 if the RDNs of the DER-encoded Name
214 : prefix, compared with the same rules as fd_x509_name_equal, form a
215 : leading subsequence of the RDNs of name (RFC 5280 Section 4.2.1.10
216 : directoryName subtree matching). An empty prefix matches every
217 : name. Returns 0 otherwise, including on malformed input; Names
218 : taken from fd_x509_cert_parse output are never malformed. */
219 :
220 : int
221 : fd_x509_name_prefix( uchar const * prefix,
222 : ulong prefix_len,
223 : uchar const * name,
224 : ulong name_len );
225 :
226 : /* fd_x509_time_parse converts an ASN.1 time value to seconds since the
227 : Unix epoch. tag is FD_DER_TAG_UTC_TIME (YYMMDDHHMMSSZ, 13 bytes) or
228 : FD_DER_TAG_GENERALIZED_TIME (YYYYMMDDHHMMSSZ, 15 bytes). [s,s+s_len)
229 : is the DER content, without tag and length.
230 :
231 : Returns seconds since the Unix epoch, or FD_X509_TIME_INVALID if the
232 : value is malformed or outside the accepted profile. */
233 :
234 : long
235 : fd_x509_time_parse( uchar tag,
236 : uchar const * s,
237 : ulong s_len );
238 :
239 : /* fd_x509_dns_name_valid returns 1 if [name,name+len) is a
240 : syntactically valid DNS hostname (RFC 1123 preferred syntax, plus
241 : '_'). Rejects empty labels, so a leading dot, a trailing dot, and
242 : ".." are all invalid. fd_x509_dns_eq_ci compares two DNS names of
243 : equal length, folding ASCII case. */
244 :
245 : FD_FN_PURE int
246 : fd_x509_dns_name_valid( char const * name,
247 : ulong len );
248 :
249 : FD_FN_PURE int
250 : fd_x509_dns_eq_ci( char const * a,
251 : char const * b,
252 : ulong len );
253 :
254 : /* fd_x509_san_matches tests hostname against info's subjectAltName
255 : extension. Supports dNSName and IPv4 iPAddress. Returns 1 for a
256 : match and 0 for no match, an absent SAN, malformed input, or a NULL
257 : argument. */
258 :
259 : int
260 : fd_x509_san_matches( fd_x509_cert_info_t const * info,
261 : char const * hostname,
262 : ulong hostname_len );
263 :
264 : FD_PROTOTYPES_END
265 :
266 : #endif /* HEADER_fd_src_ballet_x509_fd_x509_h */
|