Skip to content

Commit 3d347f1

Browse files
committed
FEAT: initial version of DER (Distinguished Encoding Rules) decoder (codec)
1 parent dac96b2 commit 3d347f1

File tree

3 files changed

+308
-0
lines changed

3 files changed

+308
-0
lines changed

src/mezz/codec-der.r

+293
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,293 @@
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+
]

src/tests/units/codecs-test.r3

+15
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,19 @@ if find system/codecs 'wav [
3131
system/codecs/wav/verbose: 0
3232
]
3333

34+
if find system/codecs 'der [
35+
system/codecs/der/verbose: 2
36+
===start-group=== "DER codec"
37+
38+
--test-- "Load DER file"
39+
--assert block? pfx: load %units/files/test.pfx
40+
--assert binary? try [a: pfx/sequence/sequence/4/2]
41+
--assert block? b: decode 'DER a
42+
--assert binary? try [c: b/sequence/sequence/4/2]
43+
--assert block? d: decode 'DER c
44+
45+
===end-group===
46+
system/codecs/der/verbose: 0
47+
]
48+
3449
~~~end-file~~~

src/tests/units/files/test.pfx

2.35 KB
Binary file not shown.

0 commit comments

Comments
 (0)