xref: /freebsd/contrib/bearssl/src/codec/pemdec.t0 (revision 2aaf9152a852aba9eb2036b95f4948ee77988826)
1*0957b409SSimon J. Gerraty\ Copyright (c) 2016 Thomas Pornin <pornin@bolet.org>
2*0957b409SSimon J. Gerraty\
3*0957b409SSimon J. Gerraty\ Permission is hereby granted, free of charge, to any person obtaining
4*0957b409SSimon J. Gerraty\ a copy of this software and associated documentation files (the
5*0957b409SSimon J. Gerraty\ "Software"), to deal in the Software without restriction, including
6*0957b409SSimon J. Gerraty\ without limitation the rights to use, copy, modify, merge, publish,
7*0957b409SSimon J. Gerraty\ distribute, sublicense, and/or sell copies of the Software, and to
8*0957b409SSimon J. Gerraty\ permit persons to whom the Software is furnished to do so, subject to
9*0957b409SSimon J. Gerraty\ the following conditions:
10*0957b409SSimon J. Gerraty\
11*0957b409SSimon J. Gerraty\ The above copyright notice and this permission notice shall be
12*0957b409SSimon J. Gerraty\ included in all copies or substantial portions of the Software.
13*0957b409SSimon J. Gerraty\
14*0957b409SSimon J. Gerraty\ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15*0957b409SSimon J. Gerraty\ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16*0957b409SSimon J. Gerraty\ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17*0957b409SSimon J. Gerraty\ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS
18*0957b409SSimon J. Gerraty\ BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN
19*0957b409SSimon J. Gerraty\ ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
20*0957b409SSimon J. Gerraty\ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21*0957b409SSimon J. Gerraty\ SOFTWARE.
22*0957b409SSimon J. Gerraty
23*0957b409SSimon J. Gerratypreamble {
24*0957b409SSimon J. Gerraty
25*0957b409SSimon J. Gerraty#include "inner.h"
26*0957b409SSimon J. Gerraty
27*0957b409SSimon J. Gerraty#define CTX   ((br_pem_decoder_context *)(void *)((unsigned char *)t0ctx - offsetof(br_pem_decoder_context, cpu)))
28*0957b409SSimon J. Gerraty
29*0957b409SSimon J. Gerraty/* see bearssl_pem.h */
30*0957b409SSimon J. Gerratyvoid
31*0957b409SSimon J. Gerratybr_pem_decoder_init(br_pem_decoder_context *ctx)
32*0957b409SSimon J. Gerraty{
33*0957b409SSimon J. Gerraty	memset(ctx, 0, sizeof *ctx);
34*0957b409SSimon J. Gerraty	ctx->cpu.dp = &ctx->dp_stack[0];
35*0957b409SSimon J. Gerraty	ctx->cpu.rp = &ctx->rp_stack[0];
36*0957b409SSimon J. Gerraty	br_pem_decoder_init_main(&ctx->cpu);
37*0957b409SSimon J. Gerraty	br_pem_decoder_run(&ctx->cpu);
38*0957b409SSimon J. Gerraty}
39*0957b409SSimon J. Gerraty
40*0957b409SSimon J. Gerraty/* see bearssl_pem.h */
41*0957b409SSimon J. Gerratysize_t
42*0957b409SSimon J. Gerratybr_pem_decoder_push(br_pem_decoder_context *ctx,
43*0957b409SSimon J. Gerraty	const void *data, size_t len)
44*0957b409SSimon J. Gerraty{
45*0957b409SSimon J. Gerraty	if (ctx->event) {
46*0957b409SSimon J. Gerraty		return 0;
47*0957b409SSimon J. Gerraty	}
48*0957b409SSimon J. Gerraty	ctx->hbuf = data;
49*0957b409SSimon J. Gerraty	ctx->hlen = len;
50*0957b409SSimon J. Gerraty	br_pem_decoder_run(&ctx->cpu);
51*0957b409SSimon J. Gerraty	return len - ctx->hlen;
52*0957b409SSimon J. Gerraty}
53*0957b409SSimon J. Gerraty
54*0957b409SSimon J. Gerraty/* see bearssl_pem.h */
55*0957b409SSimon J. Gerratyint
56*0957b409SSimon J. Gerratybr_pem_decoder_event(br_pem_decoder_context *ctx)
57*0957b409SSimon J. Gerraty{
58*0957b409SSimon J. Gerraty	int event;
59*0957b409SSimon J. Gerraty
60*0957b409SSimon J. Gerraty	event = ctx->event;
61*0957b409SSimon J. Gerraty	ctx->event = 0;
62*0957b409SSimon J. Gerraty	return event;
63*0957b409SSimon J. Gerraty}
64*0957b409SSimon J. Gerraty
65*0957b409SSimon J. Gerraty}
66*0957b409SSimon J. Gerraty
67*0957b409SSimon J. Gerraty\ Define a word that evaluates to the address of a field within the
68*0957b409SSimon J. Gerraty\ decoder context.
69*0957b409SSimon J. Gerraty: addr:
70*0957b409SSimon J. Gerraty	next-word { field }
71*0957b409SSimon J. Gerraty	"addr-" field + 0 1 define-word
72*0957b409SSimon J. Gerraty	0 8191 "offsetof(br_pem_decoder_context, " field + ")" + make-CX
73*0957b409SSimon J. Gerraty	postpone literal postpone ; ;
74*0957b409SSimon J. Gerraty
75*0957b409SSimon J. Gerratyaddr: event
76*0957b409SSimon J. Gerratyaddr: name
77*0957b409SSimon J. Gerratyaddr: buf
78*0957b409SSimon J. Gerratyaddr: ptr
79*0957b409SSimon J. Gerraty
80*0957b409SSimon J. Gerraty\ Set a byte at a specific address (offset within the context).
81*0957b409SSimon J. Gerratycc: set8 ( value addr -- ) {
82*0957b409SSimon J. Gerraty	size_t addr = T0_POP();
83*0957b409SSimon J. Gerraty	unsigned x = T0_POP();
84*0957b409SSimon J. Gerraty	*((unsigned char *)CTX + addr) = x;
85*0957b409SSimon J. Gerraty}
86*0957b409SSimon J. Gerraty
87*0957b409SSimon J. Gerraty\ Get a byte at a specific address (offset within the context).
88*0957b409SSimon J. Gerratycc: get8 ( addr -- value ) {
89*0957b409SSimon J. Gerraty	size_t addr = T0_POP();
90*0957b409SSimon J. Gerraty	T0_PUSH(*((unsigned char *)CTX + addr));
91*0957b409SSimon J. Gerraty}
92*0957b409SSimon J. Gerraty
93*0957b409SSimon J. Gerraty\ Send an event.
94*0957b409SSimon J. Gerraty: send-event ( event -- )
95*0957b409SSimon J. Gerraty	addr-event set8 co ;
96*0957b409SSimon J. Gerraty
97*0957b409SSimon J. Gerraty\ Low-level function to read a single byte. Returned value is the byte
98*0957b409SSimon J. Gerraty\ (0 to 255), or -1 if there is no available data.
99*0957b409SSimon J. Gerratycc: read8-native ( -- x ) {
100*0957b409SSimon J. Gerraty	if (CTX->hlen > 0) {
101*0957b409SSimon J. Gerraty		T0_PUSH(*CTX->hbuf ++);
102*0957b409SSimon J. Gerraty		CTX->hlen --;
103*0957b409SSimon J. Gerraty	} else {
104*0957b409SSimon J. Gerraty		T0_PUSHi(-1);
105*0957b409SSimon J. Gerraty	}
106*0957b409SSimon J. Gerraty}
107*0957b409SSimon J. Gerraty
108*0957b409SSimon J. Gerraty\ Read next byte. Block until the next byte is available.
109*0957b409SSimon J. Gerraty: read8 ( -- x )
110*0957b409SSimon J. Gerraty	begin read8-native dup 0< ifnot ret then drop co again ;
111*0957b409SSimon J. Gerraty
112*0957b409SSimon J. Gerraty\ Read bytes until next end-of-line.
113*0957b409SSimon J. Gerraty: skip-newline ( -- )
114*0957b409SSimon J. Gerraty	begin read8 `\n <> while repeat ;
115*0957b409SSimon J. Gerraty
116*0957b409SSimon J. Gerraty\ Read bytes until next end-of-line; verify that they are all whitespace.
117*0957b409SSimon J. Gerraty\ This returns -1 if they were all whitespace, 0 otherwise.
118*0957b409SSimon J. Gerraty: skip-newline-ws ( -- bool )
119*0957b409SSimon J. Gerraty	-1 { r }
120*0957b409SSimon J. Gerraty	begin read8 dup `\n <> while ws? ifnot 0 >r then repeat
121*0957b409SSimon J. Gerraty	drop r ;
122*0957b409SSimon J. Gerraty
123*0957b409SSimon J. Gerraty\ Normalise a byte to uppercase (ASCII only).
124*0957b409SSimon J. Gerraty: norm-upper ( x -- x )
125*0957b409SSimon J. Gerraty	dup dup `a >= swap `z <= and if 32 - then ;
126*0957b409SSimon J. Gerraty
127*0957b409SSimon J. Gerraty\ Read bytes and compare with the provided string. On mismatch, the
128*0957b409SSimon J. Gerraty\ rest of the line is consumed. Matching is not case sensitive.
129*0957b409SSimon J. Gerraty: match-string ( str -- bool )
130*0957b409SSimon J. Gerraty	begin
131*0957b409SSimon J. Gerraty		dup data-get8 norm-upper dup ifnot 2drop -1 ret then
132*0957b409SSimon J. Gerraty		read8 norm-upper dup `\n = if drop 2drop 0 ret then
133*0957b409SSimon J. Gerraty		= ifnot drop skip-newline 0 ret then
134*0957b409SSimon J. Gerraty		1+
135*0957b409SSimon J. Gerraty	again ;
136*0957b409SSimon J. Gerraty
137*0957b409SSimon J. Gerraty\ Read bytes into the provided buffer, but no more than the provided
138*0957b409SSimon J. Gerraty\ count. Reading stops when end-of-line is reached. Returned value
139*0957b409SSimon J. Gerraty\ is the count of bytes written to the buffer, or 0 if the buffer size
140*0957b409SSimon J. Gerraty\ was exceeded. All bytes are normalised to uppercase (ASCII only).
141*0957b409SSimon J. Gerraty: read-bytes ( addr len -- len )
142*0957b409SSimon J. Gerraty	dup { orig-len }
143*0957b409SSimon J. Gerraty	swap
144*0957b409SSimon J. Gerraty	begin
145*0957b409SSimon J. Gerraty		over ifnot 2drop skip-newline 0 ret then
146*0957b409SSimon J. Gerraty		read8 dup `\n = if 2drop orig-len swap - ret then
147*0957b409SSimon J. Gerraty		dup `\r = if drop else norm-upper over set8 then
148*0957b409SSimon J. Gerraty		1+ swap 1- swap
149*0957b409SSimon J. Gerraty	again ;
150*0957b409SSimon J. Gerraty
151*0957b409SSimon J. Gerraty\ Remove trailing dashes from the name buffer.
152*0957b409SSimon J. Gerraty: trim-dashes ( len -- )
153*0957b409SSimon J. Gerraty	begin dup while
154*0957b409SSimon J. Gerraty		1-
155*0957b409SSimon J. Gerraty		dup addr-name + get8 `- <> if
156*0957b409SSimon J. Gerraty			addr-name + 1+ 0 swap set8 ret
157*0957b409SSimon J. Gerraty		then
158*0957b409SSimon J. Gerraty	repeat
159*0957b409SSimon J. Gerraty	addr-name set8 ;
160*0957b409SSimon J. Gerraty
161*0957b409SSimon J. Gerraty\ Scan input for next "begin" banner.
162*0957b409SSimon J. Gerraty: next-banner-begin ( -- )
163*0957b409SSimon J. Gerraty	begin
164*0957b409SSimon J. Gerraty		"-----BEGIN " match-string if
165*0957b409SSimon J. Gerraty			addr-name 127 read-bytes
166*0957b409SSimon J. Gerraty			dup if trim-dashes ret then
167*0957b409SSimon J. Gerraty			drop
168*0957b409SSimon J. Gerraty		then
169*0957b409SSimon J. Gerraty	again ;
170*0957b409SSimon J. Gerraty
171*0957b409SSimon J. Gerraty\ Convert a Base64 character to its numerical value. Returned value is
172*0957b409SSimon J. Gerraty\ 0 to 63 for Base64 characters, -1 for '=', and -2 for all other characters.
173*0957b409SSimon J. Gerratycc: from-base64 ( char -- x ) {
174*0957b409SSimon J. Gerraty	uint32_t c = T0_POP();
175*0957b409SSimon J. Gerraty	uint32_t p, q, r, z;
176*0957b409SSimon J. Gerraty	p = c - 0x41;
177*0957b409SSimon J. Gerraty	q = c - 0x61;
178*0957b409SSimon J. Gerraty	r = c - 0x30;
179*0957b409SSimon J. Gerraty
180*0957b409SSimon J. Gerraty	z = ((p + 2) & -LT(p, 26))
181*0957b409SSimon J. Gerraty		| ((q + 28) & -LT(q, 26))
182*0957b409SSimon J. Gerraty		| ((r + 54) & -LT(r, 10))
183*0957b409SSimon J. Gerraty		| (64 & -EQ(c, 0x2B))
184*0957b409SSimon J. Gerraty		| (65 & -EQ(c, 0x2F))
185*0957b409SSimon J. Gerraty		| EQ(c, 0x3D);
186*0957b409SSimon J. Gerraty	T0_PUSHi((int32_t)z - 2);
187*0957b409SSimon J. Gerraty}
188*0957b409SSimon J. Gerraty
189*0957b409SSimon J. Gerraty\ Test whether a character is whitespace (but not a newline).
190*0957b409SSimon J. Gerraty: ws? ( x -- bool )
191*0957b409SSimon J. Gerraty	dup `\n <> swap 32 <= and ;
192*0957b409SSimon J. Gerraty
193*0957b409SSimon J. Gerraty\ Read next character, skipping whitespace (except newline).
194*0957b409SSimon J. Gerraty: next-nonws ( -- x )
195*0957b409SSimon J. Gerraty	begin
196*0957b409SSimon J. Gerraty		read8 dup ws? ifnot ret then
197*0957b409SSimon J. Gerraty		drop
198*0957b409SSimon J. Gerraty	again ;
199*0957b409SSimon J. Gerraty
200*0957b409SSimon J. Gerraty\ Write one byte in the output buffer.
201*0957b409SSimon J. Gerratycc: write8 ( x -- ) {
202*0957b409SSimon J. Gerraty	unsigned char x = (unsigned char)T0_POP();
203*0957b409SSimon J. Gerraty	CTX->buf[CTX->ptr ++] = x;
204*0957b409SSimon J. Gerraty	if (CTX->ptr == sizeof CTX->buf) {
205*0957b409SSimon J. Gerraty		if (CTX->dest) {
206*0957b409SSimon J. Gerraty			CTX->dest(CTX->dest_ctx, CTX->buf, sizeof CTX->buf);
207*0957b409SSimon J. Gerraty		}
208*0957b409SSimon J. Gerraty		CTX->ptr = 0;
209*0957b409SSimon J. Gerraty	}
210*0957b409SSimon J. Gerraty}
211*0957b409SSimon J. Gerraty
212*0957b409SSimon J. Gerraty\ Flush the output buffer.
213*0957b409SSimon J. Gerratycc: flush-buf ( -- ) {
214*0957b409SSimon J. Gerraty	if (CTX->ptr > 0) {
215*0957b409SSimon J. Gerraty		if (CTX->dest) {
216*0957b409SSimon J. Gerraty			CTX->dest(CTX->dest_ctx, CTX->buf, CTX->ptr);
217*0957b409SSimon J. Gerraty		}
218*0957b409SSimon J. Gerraty		CTX->ptr = 0;
219*0957b409SSimon J. Gerraty	}
220*0957b409SSimon J. Gerraty}
221*0957b409SSimon J. Gerraty
222*0957b409SSimon J. Gerraty\ Decode the four next Base64 characters. Returned value is:
223*0957b409SSimon J. Gerraty\    0   quartet processed, three bytes produced.
224*0957b409SSimon J. Gerraty\   -1   dash encountered as first character (no leading whitespace).
225*0957b409SSimon J. Gerraty\    1   quartet processed, one or two bytes produced, terminator reached.
226*0957b409SSimon J. Gerraty\    2   end-of-line reached.
227*0957b409SSimon J. Gerraty\    3   error.
228*0957b409SSimon J. Gerraty\ For all positive return values, the remaining of the current line has been
229*0957b409SSimon J. Gerraty\ consumed.
230*0957b409SSimon J. Gerraty: decode-next-quartet ( -- r )
231*0957b409SSimon J. Gerraty	\ Process first character. It may be a dash.
232*0957b409SSimon J. Gerraty	read8 dup `- = if drop -1 ret then
233*0957b409SSimon J. Gerraty	dup ws? if drop next-nonws then
234*0957b409SSimon J. Gerraty	dup `\n = if drop 2 ret then
235*0957b409SSimon J. Gerraty	from-base64 dup 0< if drop skip-newline 3 ret then
236*0957b409SSimon J. Gerraty	{ acc }
237*0957b409SSimon J. Gerraty
238*0957b409SSimon J. Gerraty	\ Second character.
239*0957b409SSimon J. Gerraty	next-nonws dup `\n = if drop 3 ret then
240*0957b409SSimon J. Gerraty	from-base64 dup 0< if drop skip-newline 3 ret then
241*0957b409SSimon J. Gerraty	acc 6 << + >acc
242*0957b409SSimon J. Gerraty
243*0957b409SSimon J. Gerraty	\ Third character: may be an equal sign.
244*0957b409SSimon J. Gerraty	next-nonws dup `\n = if drop 3 ret then
245*0957b409SSimon J. Gerraty	dup `= = if
246*0957b409SSimon J. Gerraty		\ Fourth character must be an equal sign.
247*0957b409SSimon J. Gerraty		drop
248*0957b409SSimon J. Gerraty		next-nonws dup `\n = if drop 3 ret then
249*0957b409SSimon J. Gerraty		skip-newline-ws ifnot drop 3 ret then
250*0957b409SSimon J. Gerraty		`= <> if 3 ret then
251*0957b409SSimon J. Gerraty		acc 0x0F and if 3 ret then
252*0957b409SSimon J. Gerraty		acc 4 >> write8
253*0957b409SSimon J. Gerraty		1 ret
254*0957b409SSimon J. Gerraty	then
255*0957b409SSimon J. Gerraty	from-base64 dup 0< if drop skip-newline 3 ret then
256*0957b409SSimon J. Gerraty	acc 6 << + >acc
257*0957b409SSimon J. Gerraty
258*0957b409SSimon J. Gerraty	\ Fourth character: may be an equal sign.
259*0957b409SSimon J. Gerraty	next-nonws dup `\n = if drop 3 ret then
260*0957b409SSimon J. Gerraty	dup `= = if
261*0957b409SSimon J. Gerraty		drop skip-newline-ws ifnot 3 ret then
262*0957b409SSimon J. Gerraty		acc 0x03 and if 3 ret then
263*0957b409SSimon J. Gerraty		acc 10 >> write8
264*0957b409SSimon J. Gerraty		acc 2 >> write8
265*0957b409SSimon J. Gerraty		1 ret
266*0957b409SSimon J. Gerraty	then
267*0957b409SSimon J. Gerraty	from-base64 dup 0< if drop skip-newline 3 ret then
268*0957b409SSimon J. Gerraty	acc 6 << + >acc
269*0957b409SSimon J. Gerraty	acc 16 >> write8
270*0957b409SSimon J. Gerraty	acc 8 >> write8
271*0957b409SSimon J. Gerraty	acc write8
272*0957b409SSimon J. Gerraty	0 ;
273*0957b409SSimon J. Gerraty
274*0957b409SSimon J. Gerraty\ Check trailer line (possibly, the leading dash has been read). This
275*0957b409SSimon J. Gerraty\ sends the appropriate event.
276*0957b409SSimon J. Gerraty: check-trailer ( bool -- )
277*0957b409SSimon J. Gerraty	ifnot
278*0957b409SSimon J. Gerraty		begin read8 dup `\n = while drop repeat
279*0957b409SSimon J. Gerraty		`- <> if skip-newline 3 send-event ret then
280*0957b409SSimon J. Gerraty	then
281*0957b409SSimon J. Gerraty	"----END " match-string ifnot 3 send-event ret then
282*0957b409SSimon J. Gerraty	flush-buf
283*0957b409SSimon J. Gerraty	skip-newline 2 send-event ;
284*0957b409SSimon J. Gerraty
285*0957b409SSimon J. Gerraty\ Decode one line worth of characters. Returned value is 0 if the end of the
286*0957b409SSimon J. Gerraty\ object is reached, -1 otherwise. The end of object or error event is sent.
287*0957b409SSimon J. Gerraty: decode-line ( -- bool )
288*0957b409SSimon J. Gerraty	-1 { first }
289*0957b409SSimon J. Gerraty	begin
290*0957b409SSimon J. Gerraty		decode-next-quartet
291*0957b409SSimon J. Gerraty		case
292*0957b409SSimon J. Gerraty			0 of endof
293*0957b409SSimon J. Gerraty			-1 of
294*0957b409SSimon J. Gerraty				first ifnot
295*0957b409SSimon J. Gerraty					skip-newline 3 send-event
296*0957b409SSimon J. Gerraty				else
297*0957b409SSimon J. Gerraty					-1 check-trailer
298*0957b409SSimon J. Gerraty				then
299*0957b409SSimon J. Gerraty				0 ret
300*0957b409SSimon J. Gerraty			endof
301*0957b409SSimon J. Gerraty			1 of 0 check-trailer 0 ret endof
302*0957b409SSimon J. Gerraty			2 of -1 ret endof
303*0957b409SSimon J. Gerraty
304*0957b409SSimon J. Gerraty			\ On decoding error
305*0957b409SSimon J. Gerraty			drop 3 send-event 0 ret
306*0957b409SSimon J. Gerraty		endcase
307*0957b409SSimon J. Gerraty		0 >first
308*0957b409SSimon J. Gerraty	again ;
309*0957b409SSimon J. Gerraty
310*0957b409SSimon J. Gerraty: main ( -- ! )
311*0957b409SSimon J. Gerraty	begin
312*0957b409SSimon J. Gerraty		next-banner-begin 1 send-event
313*0957b409SSimon J. Gerraty		begin decode-line while repeat
314*0957b409SSimon J. Gerraty	again ;
315