|
| 1 | +REBOL [ |
| 2 | + title: "REBOL3 codec for DER/BER structures" |
| 3 | + name: 'codec-der |
| 4 | + author: "Oldes" |
| 5 | + version: 0.1.0 |
| 6 | + date: 17-Oct-2018 |
| 7 | + history: [ |
| 8 | + 0.1.0 17-Oct-2018 "Oldes" {Initial version with DECODE and IDENTIFY functions.} |
| 9 | + ] |
| 10 | + notes: { |
| 11 | + Useful command for cross-testing: |
| 12 | + openssl asn1parse -inform DER -in test.pfx |
| 13 | +
|
| 14 | + Current output is as a tree structure, but I'm thinking about using flat structure too |
| 15 | + as it may be easier (and resource friendlier) to deal with in practical use scenarios. |
| 16 | + } |
| 17 | +] |
| 18 | + |
| 19 | +register-codec [ |
| 20 | + name: 'DER |
| 21 | + title: "Distinguished Encoding Rules" |
| 22 | + suffixes: [%.p12 %.pfx %.cer %.der] |
| 23 | + decode: function[data [binary!]][ |
| 24 | + if verbose > 0 [ |
| 25 | + print ["^/^[[1;32mDecode DER data^[[m (^[[1m" length? data "^[[mbytes )"] |
| 26 | + ; count maximal bytes width (used for padding) |
| 27 | + wl: length? form length? data |
| 28 | + wr: negate wl |
| 29 | + ] |
| 30 | + if data/1 <> 48 [ |
| 31 | + if verbose > 0 [ |
| 32 | + print "*** DER data does not start with SEQUENCE tag ***^\*** " |
| 33 | + probe copy/part data 10 |
| 34 | + ] |
| 35 | + return none |
| 36 | + ] |
| 37 | + |
| 38 | + der: binary data |
| 39 | + |
| 40 | + result: out: make block! 32 |
| 41 | + |
| 42 | + tails: make block! 8 ;que for finding closings |
| 43 | + blocks: make block! 8 ;que for constructed data |
| 44 | + |
| 45 | + insert/only blocks out |
| 46 | + |
| 47 | + while [not tail? der/buffer][ |
| 48 | + ;?? tails |
| 49 | + depth: length? blocks |
| 50 | + ;?? depth |
| 51 | + binary/read der [ |
| 52 | + tag-pos: INDEX |
| 53 | + class: UB 2 ;- Class encoding [universal application context-specific private] |
| 54 | + constr: BIT ;- Method: FALSE = primitive, TRUE = constructed |
| 55 | + tag: UB 5 ;- Tag |
| 56 | + length: LENGTH |
| 57 | + data-pos: INDEX |
| 58 | + ] |
| 59 | + |
| 60 | + tag-name: switch class [ |
| 61 | + 0 [ DER-tags/(tag + 1) ] |
| 62 | + 1 [ to word! join "AP" tag ] |
| 63 | + 2 [ to word! join "CS" tag ] |
| 64 | + 3 [ to word! join "PR" tag ] |
| 65 | + ] |
| 66 | + |
| 67 | + if closing-pos: tails/1 [ |
| 68 | + while [tails/1 = tag-pos][ |
| 69 | + ;print ["--- closing constructed" tails/1] |
| 70 | + remove tails |
| 71 | + remove blocks |
| 72 | + out: blocks/1 |
| 73 | + ;?? tails |
| 74 | + ] |
| 75 | + ] |
| 76 | + |
| 77 | + data: none |
| 78 | + |
| 79 | + either constr [ |
| 80 | + ;-constructed |
| 81 | + repend out [ |
| 82 | + tag-name |
| 83 | + out: make block! 32 |
| 84 | + ] |
| 85 | + insert/only blocks out |
| 86 | + insert tails (data-pos + length) |
| 87 | + ][ |
| 88 | + ;- primitive |
| 89 | + binary/read der [data: BYTES :length] |
| 90 | + switch tag-name [ |
| 91 | + OBJECT_IDENTIFIER [ |
| 92 | + ;data: decode-OID data |
| 93 | + ] |
| 94 | + UTC_TIME [ |
| 95 | + data: system/codecs/utc-time/decode data |
| 96 | + ] |
| 97 | + UTF8_STRING |
| 98 | + PRINTABLE_STRING |
| 99 | + IA5_STRING |
| 100 | + T61_STRING |
| 101 | + BMP_STRING [ |
| 102 | + data: to string! data |
| 103 | + ] |
| 104 | + ;OCTET_STRING [ |
| 105 | + ;binary/read der [AT :data-pos] |
| 106 | + ;data: make block! 8 |
| 107 | + ;repend out [tag-name data] |
| 108 | + ;out: data |
| 109 | + ;insert/only blocks out |
| 110 | + ;insert tails (data-pos + length) |
| 111 | + ;data: none |
| 112 | + ;] |
| 113 | + BIT_STRING [ |
| 114 | + if data/1 = 0 [data: next data] |
| 115 | + ;data: enbase/base data 2 |
| 116 | + ] |
| 117 | + INTEGER [ |
| 118 | + if data/1 = 0 [data: next data] |
| 119 | + ] |
| 120 | + ] |
| 121 | + if data [ |
| 122 | + repend out [tag-name data] |
| 123 | + ] |
| 124 | + ] |
| 125 | + if verbose > 0 [ |
| 126 | + if empty? data [data: none] |
| 127 | + if tag-name = 'OBJECT_IDENTIFIER [ |
| 128 | + data: decode-OID/full data |
| 129 | + ] |
| 130 | + if all [binary? data verbose < 3 94 < length? data][ |
| 131 | + data: mold copy/part data 94 |
| 132 | + change skip tail data -2 " ..." |
| 133 | + ] |
| 134 | + printf [ |
| 135 | + #" " $1.35 wr $.32 ":d=" $1.36 2 $.32 |
| 136 | + "hl=" $1.32 2 $.32 |
| 137 | + "l=" $1 wl $.32 #" " -5 |
| 138 | + #" " $1.36 18 $.32 $0] reduce [ |
| 139 | + tag-pos - 1 ; tag start position |
| 140 | + depth - 1 ; current depth |
| 141 | + data-pos - tag-pos ; length of header |
| 142 | + length ; length of data |
| 143 | + pick ["cons:" "prim:"] constr |
| 144 | + tag-name |
| 145 | + either binary? data[ mold data ][ any [data ""] ] |
| 146 | + ] |
| 147 | + ] |
| 148 | + ] |
| 149 | + ;?? tails |
| 150 | + ;?? blocks |
| 151 | + result |
| 152 | + ] |
| 153 | + |
| 154 | + identify: function[data [binary!]][ |
| 155 | + any [ |
| 156 | + data/1 = 48 |
| 157 | + ] |
| 158 | + ] |
| 159 | + |
| 160 | + DER-tags: [ |
| 161 | + END_OF_CONTENTS ;= 00 |
| 162 | + BOOLEAN ;= 01 |
| 163 | + INTEGER ;= 02 |
| 164 | + BIT_STRING ;= 03 |
| 165 | + OCTET_STRING ;= 04 |
| 166 | + NULL ;= 05 |
| 167 | + OBJECT_IDENTIFIER ;= 06 |
| 168 | + OBJECT_DESCRIPTOR ;= 07 |
| 169 | + EXTERNAL ;= 08 |
| 170 | + REAL ;= 09 |
| 171 | + ENUMERATED ;= 0a |
| 172 | + EMBEDDED_PDV ;= 0b |
| 173 | + UTF8_STRING ;= 0c |
| 174 | + RELATIVE_OID ;= 0d |
| 175 | + UNDEFINED |
| 176 | + UNDEFINED |
| 177 | + SEQUENCE ;= 10 |
| 178 | + SET ;= 11 |
| 179 | + NUMERIC_STRING ;= 12 |
| 180 | + PRINTABLE_STRING ;= 13 |
| 181 | + T61_STRING ;= 14 |
| 182 | + VIDEOTEX_STRING ;= 15 |
| 183 | + IA5_STRING ;= 16 |
| 184 | + UTC_TIME ;= 17 |
| 185 | + GENERALIZED_TIME ;= 18 |
| 186 | + GRAPHIC_STRING ;= 19 |
| 187 | + VISIBLE_STRING ;= 1a Visible string (ASCII subset) |
| 188 | + GENERAL_STRING ;= 1b |
| 189 | + UNIVERSAL_STRING ;= 1c |
| 190 | + CHARACTER_STRING ;= 1d |
| 191 | + BMP_STRING ;= 1e Basic Multilingual Plane/Unicode string |
| 192 | + ] |
| 193 | + |
| 194 | + decode-OID: function[ |
| 195 | + oid [binary!] |
| 196 | + /full "Returns name with group name as a string" |
| 197 | + /local main name warn |
| 198 | + ][ |
| 199 | + parse/all oid [ |
| 200 | + #{2B0E0302} (main: "Oddball OIW OID") [ |
| 201 | + #{1A} (name: 'sha1) |
| 202 | + #{1D} (name: 'sha1WithRSAEncryption) |
| 203 | + ] |
| 204 | + | |
| 205 | + #{2B060105050701} (main: "PKIX private extension") [ |
| 206 | + #{01} (name: 'authorityInfoAccess) |
| 207 | + ] |
| 208 | + | |
| 209 | + #{2B060105050730} (main: "PKIX") [ |
| 210 | + ;- access descriptor definitions |
| 211 | + #{01} (name: 'ocsp) ; Online Certificate Status Protocol |
| 212 | + #{02} (name: 'caIssuers) ; Certificate authority issuers |
| 213 | + #{03} (name: 'timeStamping) |
| 214 | + #{05} (name: 'caRepository) |
| 215 | + ] |
| 216 | + | |
| 217 | + #{2A864886F70D01} [ |
| 218 | + #{01} (main: "PKCS #1") [ |
| 219 | + #{01} (name: 'rsaEncryption) |
| 220 | + | #{02} (name: 'md2WithRSAEncryption) |
| 221 | + | #{03} (name: 'md4withRSAEncryption) |
| 222 | + | #{04} (name: 'md5withRSAEncryption) |
| 223 | + | #{05} (name: 'sha1WithRSAEncrption) |
| 224 | + | #{0B} (name: 'sha256WithRSAEncryption) |
| 225 | + |
| 226 | + ] end |
| 227 | + | |
| 228 | + #{07} (main: "PKCS #7") [ |
| 229 | + #{01} (name: 'data) |
| 230 | + | #{02} (name: 'signedData) |
| 231 | + | #{06} (name: 'encryptedData) |
| 232 | + ] end |
| 233 | + | |
| 234 | + #{09} (main: "PKCS #9") [ |
| 235 | + #{01} (name: 'emailAddress warn: "Deprecated, use an altName extension instead") |
| 236 | + | #{14} (name: 'friendlyName) |
| 237 | + | #{15} (name: 'localKeyID) |
| 238 | + ] end |
| 239 | + | |
| 240 | + #{0C} (main: "PKCS #12") [ |
| 241 | + #{0106} (name: 'pbeWithSHAAnd40BitRC2-CBC) |
| 242 | + | #{0103} (name: 'pbeWithSHAAnd3-KeyTripleDES-CBC) |
| 243 | + | #{0A0102} (name: 'pkcs-12-pkcs-8ShroudedKeyBag) |
| 244 | + ] end |
| 245 | + ] end |
| 246 | + | |
| 247 | + #{5504} (main: "X.520 DN component") [ |
| 248 | + #{03} (name: 'commonName) |
| 249 | + | #{06} (name: 'countryName) |
| 250 | + | #{07} (name: 'localityName) |
| 251 | + | #{08} (name: 'stateOrProvinceName) |
| 252 | + | #{0A} (name: 'organizationName) |
| 253 | + | #{0B} (name: 'organizationalUnitName) |
| 254 | + | #{0D} (name: 'description) |
| 255 | + | #{0F} (name: 'businessCategory) |
| 256 | + ] end |
| 257 | + | |
| 258 | + #{551D} (main: "X.509 extension") [ |
| 259 | + #{01} (name: 'authorityKeyIdentifier warn: "Deprecated, use 2 5 29 35 instead") |
| 260 | + | #{04} (name: 'keyUsageRestriction warn: "Obsolete, use keyUsage/extKeyUsage instead") |
| 261 | + | #{0E} (name: 'subjectKeyIdentifier) |
| 262 | + | #{0F} (name: 'keyUsage) |
| 263 | + | #{11} (name: 'subjectAltName) |
| 264 | + | #{13} (name: 'basicConstraints) |
| 265 | + | #{1F} (name: 'cRLDistributionPoints) |
| 266 | + | #{20} (name: 'certificatePolicies) |
| 267 | + | #{23} (name: 'authorityKeyIdentifier) |
| 268 | + | #{25} (name: 'extKeyUsage) |
| 269 | + ] end |
| 270 | + | |
| 271 | + #{2B060105050703} (main: "PKIX key purpose") [ |
| 272 | + #{01} (name: 'serverAuth) |
| 273 | + #{02} (name: 'clientAuth) |
| 274 | + | #{03} (name: 'codeSigning) |
| 275 | + ] end |
| 276 | + | |
| 277 | + #{2B0601040182370201} (main: "Microsoft") [ |
| 278 | + #{15} (name: 'individualCodeSigning) |
| 279 | + ] end |
| 280 | + ] |
| 281 | + ;?? main |
| 282 | + ;?? name |
| 283 | + ;if warn [?? warn] |
| 284 | + |
| 285 | + either all [main name] [ |
| 286 | + either full [ |
| 287 | + rejoin [ any [name "<?name>"] " (" any [main "<?main>"] ")"] |
| 288 | + ][ name ] |
| 289 | + ][ oid ] |
| 290 | + ] |
| 291 | + |
| 292 | + verbose: 2 |
| 293 | +] |
0 commit comments