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