krypt 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +20 -0
- data/README.md +82 -0
- data/lib/krypt.rb +49 -0
- data/lib/krypt/asn1.rb +3 -0
- data/lib/krypt/asn1/common.rb +96 -0
- data/lib/krypt/asn1/template.rb +257 -0
- data/lib/krypt/codec.rb +57 -0
- data/lib/krypt/codec/base64.rb +140 -0
- data/lib/krypt/codec/base_codec.rb +36 -0
- data/lib/krypt/codec/hex.rb +122 -0
- data/lib/krypt/digest.rb +112 -0
- data/lib/krypt/hmac.rb +69 -0
- data/lib/krypt/pkcs5.rb +1 -0
- data/lib/krypt/pkcs5/pbkdf2.rb +41 -0
- data/lib/krypt/provider.rb +35 -0
- data/lib/krypt/x509.rb +3 -0
- data/lib/krypt/x509/certificate.rb +36 -0
- data/lib/krypt/x509/common.rb +41 -0
- data/lib/krypt/x509/crl.rb +33 -0
- data/lib/krypt_missing.rb +32 -0
- data/spec/krypt-core/MEMO.txt +85 -0
- data/spec/krypt-core/asn1/asn1_bit_string_spec.rb +475 -0
- data/spec/krypt-core/asn1/asn1_boolean_spec.rb +392 -0
- data/spec/krypt-core/asn1/asn1_constants_spec.rb +71 -0
- data/spec/krypt-core/asn1/asn1_data_spec.rb +1153 -0
- data/spec/krypt-core/asn1/asn1_end_of_contents_spec.rb +133 -0
- data/spec/krypt-core/asn1/asn1_enumerated_spec.rb +458 -0
- data/spec/krypt-core/asn1/asn1_generalized_time_spec.rb +492 -0
- data/spec/krypt-core/asn1/asn1_integer_spec.rb +557 -0
- data/spec/krypt-core/asn1/asn1_null_spec.rb +360 -0
- data/spec/krypt-core/asn1/asn1_object_id_spec.rb +495 -0
- data/spec/krypt-core/asn1/asn1_octet_string_spec.rb +456 -0
- data/spec/krypt-core/asn1/asn1_parser_spec.rb +503 -0
- data/spec/krypt-core/asn1/asn1_pem_spec.rb +282 -0
- data/spec/krypt-core/asn1/asn1_sequence_spec.rb +637 -0
- data/spec/krypt-core/asn1/asn1_set_spec.rb +795 -0
- data/spec/krypt-core/asn1/asn1_utc_time_spec.rb +495 -0
- data/spec/krypt-core/asn1/asn1_utf8_string_spec.rb +404 -0
- data/spec/krypt-core/asn1/resources.rb +53 -0
- data/spec/krypt-core/base64/base64_spec.rb +97 -0
- data/spec/krypt-core/digest/digest_spec.rb +707 -0
- data/spec/krypt-core/hex/hex_spec.rb +102 -0
- data/spec/krypt-core/pem/pem_decode_spec.rb +235 -0
- data/spec/krypt-core/resources.rb +1 -0
- data/spec/krypt-core/template/template_choice_parse_spec.rb +289 -0
- data/spec/krypt-core/template/template_dsl_spec.rb +351 -0
- data/spec/krypt-core/template/template_seq_of_parse_spec.rb +64 -0
- data/spec/krypt-core/template/template_seq_parse_spec.rb +1241 -0
- data/spec/krypt/codec/base64_decoder_spec.rb +94 -0
- data/spec/krypt/codec/base64_encoder_spec.rb +94 -0
- data/spec/krypt/codec/base64_mixed_spec.rb +16 -0
- data/spec/krypt/codec/hex_decoder_spec.rb +94 -0
- data/spec/krypt/codec/hex_encoder_spec.rb +94 -0
- data/spec/krypt/codec/hex_mixed_spec.rb +17 -0
- data/spec/krypt/codec/identity_shared.rb +119 -0
- data/spec/krypt/hmac/hmac_spec.rb +311 -0
- data/spec/krypt/pkcs5/pbkdf2_spec.rb +79 -0
- data/spec/krypt/provider/provider_spec.rb +83 -0
- data/spec/res/ca-bundle.crt +11758 -0
- data/spec/res/certificate.cer +0 -0
- data/spec/res/certificate.pem +20 -0
- data/spec/res/multiple_certs.pem +60 -0
- data/spec/resources.rb +66 -0
- data/test/helper.rb +8 -0
- data/test/res/certificate.cer +0 -0
- data/test/resources.rb +48 -0
- data/test/scratch.rb +28 -0
- data/test/test_krypt_asn1.rb +119 -0
- data/test/test_krypt_parser.rb +331 -0
- metadata +134 -0
Binary file
|
@@ -0,0 +1,20 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
3
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
4
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
5
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
6
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
7
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
8
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
9
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
10
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
11
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
12
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
13
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
14
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
15
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
16
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
17
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
18
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
19
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
20
|
+
-----END CERTIFICATE-----
|
@@ -0,0 +1,60 @@
|
|
1
|
+
-----BEGIN CERTIFICATE-----
|
2
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
3
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
4
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
5
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
6
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
7
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
8
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
9
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
10
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
11
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
12
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
13
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
14
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
15
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
16
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
17
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
18
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
19
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
20
|
+
-----END CERTIFICATE-----
|
21
|
+
-----BEGIN CERTIFICATE-----
|
22
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
23
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
24
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
25
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
26
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
27
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
28
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
29
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
30
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
31
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
32
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
33
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
34
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
35
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
36
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
37
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
38
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
39
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
40
|
+
-----END CERTIFICATE-----
|
41
|
+
-----BEGIN CERTIFICATE-----
|
42
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
43
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
44
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
45
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
46
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
47
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
48
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
49
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
50
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
51
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
52
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
53
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
54
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
55
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
56
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
57
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
58
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
59
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
60
|
+
-----END CERTIFICATE-----
|
data/spec/resources.rb
ADDED
@@ -0,0 +1,66 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Resources
|
4
|
+
|
5
|
+
CERTIFICATE_PEM = <<-_EOF_
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
8
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
9
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
10
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
11
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
12
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
13
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
14
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
15
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
16
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
17
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
18
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
19
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
20
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
21
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
22
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
23
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
24
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
25
|
+
-----END CERTIFICATE-----
|
26
|
+
_EOF_
|
27
|
+
|
28
|
+
CERTIFICATE = OpenSSL::X509::Certificate.new CERTIFICATE_PEM
|
29
|
+
|
30
|
+
class << self
|
31
|
+
def certificate
|
32
|
+
CERTIFICATE.to_der
|
33
|
+
end
|
34
|
+
|
35
|
+
def certificate_pem
|
36
|
+
CERTIFICATE_PEM
|
37
|
+
end
|
38
|
+
|
39
|
+
def certificate_io
|
40
|
+
File.open(File.expand_path('res/certificate.cer', File.dirname(__FILE__)), "rb")
|
41
|
+
end
|
42
|
+
|
43
|
+
def certificate_pem_io
|
44
|
+
File.open(File.expand_path('res/certificate.pem', File.dirname(__FILE__)), "rb")
|
45
|
+
end
|
46
|
+
|
47
|
+
def multi_certificate_pem_io
|
48
|
+
File.open(File.expand_path('res/multiple_certs.pem', File.dirname(__FILE__)), "rb")
|
49
|
+
end
|
50
|
+
|
51
|
+
def ca_certificate_pem_io
|
52
|
+
File.open(File.expand_path('res/ca-bundle.crt', File.dirname(__FILE__)), "rb")
|
53
|
+
end
|
54
|
+
|
55
|
+
def bytes_to_io(str)
|
56
|
+
raw = [str.join('')].pack('H*')
|
57
|
+
StringIO.new raw
|
58
|
+
end
|
59
|
+
|
60
|
+
def bytes(str)
|
61
|
+
[str.join('')].pack('H*')
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
end
|
66
|
+
|
data/test/helper.rb
ADDED
Binary file
|
data/test/resources.rb
ADDED
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module Resources
|
4
|
+
|
5
|
+
CERTIFICATE = OpenSSL::X509::Certificate.new <<-_EOF_
|
6
|
+
-----BEGIN CERTIFICATE-----
|
7
|
+
MIIDPTCCAiWgAwIBAgIBAzANBgkqhkiG9w0BAQQFADA9MRMwEQYKCZImiZPyLGQB
|
8
|
+
GRYDb3JnMRkwFwYKCZImiZPyLGQBGRYJcnVieS1sYW5nMQswCQYDVQQDDAJDQTAe
|
9
|
+
Fw0xMTEwMjkxNTM2MzJaFw0xMTEwMjkxNjA2MzJaMD4xEzARBgoJkiaJk/IsZAEZ
|
10
|
+
FgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxDDAKBgNVBAMMA0VFMjCB
|
11
|
+
nzANBgkqhkiG9w0BAQEFAAOBjQAwgYkCgYEAy8LEsNRApz7U/j5DoB4XBgO9Z8At
|
12
|
+
v5y/OVQRp0ag8Tqo1YewsWijxEWB7JOATwpBN267U4T1nPZIxxEEO7n/WNa2ws9J
|
13
|
+
Wsjah8ssEBFSxZqdXKSLf0N4Hi7/GQ/aYoaMCiQ8jA4jegK2FJmXM71uPe+jFN/p
|
14
|
+
eeBOpRfyXxRFOYcCAwEAAaOByjCBxzAOBgNVHQ8BAf8EBAMCBeAwHQYDVR0OBBYE
|
15
|
+
FNH++fv4rhvBYMv6A+JZbdhzCJITME8GA1UdIwRIMEahQaQ/MD0xEzARBgoJkiaJ
|
16
|
+
k/IsZAEZFgNvcmcxGTAXBgoJkiaJk/IsZAEZFglydWJ5LWxhbmcxCzAJBgNVBAMM
|
17
|
+
AkNBggEBMCcGA1UdJQQgMB4GCCsGAQUFBwMCBggrBgEFBQcDBAYIKwYBBQUHAwMw
|
18
|
+
HAYDVR0RBBUwE4ERZWUyQHJ1YnktbGFuZy5vcmcwDQYJKoZIhvcNAQEEBQADggEB
|
19
|
+
ABLJKAS9nXFI3idP3dXXs8n4gLBrWHyaacklwnj8gvVoNtYQIICXODY/zQLtzfjx
|
20
|
+
SQxlrOUtn0uYTzJUNc+NLfc8EzcVDhmSWhq+msCFEyhL5+kBceFj2ZI8f9OuQ87m
|
21
|
+
zy1yqE1jxP1fsGDZ3Cqny86jJvwRzPW6NkPtkFzS6ZYe09DXDZnUuZP8kVpn4Pit
|
22
|
+
vHmj/XVKl4LGxlrebReqIn+m2K46uxUkQEDhk7R16SAl6N92W7vI/lXr98aVdfF4
|
23
|
+
Ozmrzopvt+XbjtvJztDNhpG0G/celhgAOzNx/eq3wl89qHIa+9vvVbFZ0BdYUHai
|
24
|
+
z+64/9KqwBAeA66J6k7l7C0=
|
25
|
+
-----END CERTIFICATE-----
|
26
|
+
_EOF_
|
27
|
+
|
28
|
+
class << self
|
29
|
+
def certificate
|
30
|
+
CERTIFICATE.to_der
|
31
|
+
end
|
32
|
+
|
33
|
+
def certificate_io
|
34
|
+
File.new(File.expand_path('res/certificate.cer', File.dirname(__FILE__)))
|
35
|
+
end
|
36
|
+
|
37
|
+
def bytes_to_io(str)
|
38
|
+
raw = [str.join('')].pack('H*')
|
39
|
+
StringIO.new raw
|
40
|
+
end
|
41
|
+
|
42
|
+
def bytes(str)
|
43
|
+
[str.join('')].pack('H*')
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
end
|
48
|
+
|
data/test/scratch.rb
ADDED
@@ -0,0 +1,28 @@
|
|
1
|
+
# encoding: US-ASCII
|
2
|
+
require 'krypt'
|
3
|
+
require_relative 'resources'
|
4
|
+
require 'openssl'
|
5
|
+
require 'stringio'
|
6
|
+
require 'pp'
|
7
|
+
|
8
|
+
decoder = Krypt::ASN1
|
9
|
+
|
10
|
+
stringio = StringIO.new(
|
11
|
+
[
|
12
|
+
Krypt::ASN1::Null.new,
|
13
|
+
Krypt::ASN1::Integer.new(0)
|
14
|
+
].map { |e| e.to_der }.join
|
15
|
+
)
|
16
|
+
c = Class.new do
|
17
|
+
def initialize(io)
|
18
|
+
@io = io
|
19
|
+
end
|
20
|
+
|
21
|
+
def read(*args)
|
22
|
+
@io.read(*args)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
generic = c.new(stringio)
|
27
|
+
p decoder.decode_der(generic)
|
28
|
+
p decoder.decode_der(generic)
|
@@ -0,0 +1,119 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class Krypt::ASN1Test < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_parse_encode_equality_file
|
7
|
+
io = Resources.certificate_io
|
8
|
+
begin
|
9
|
+
asn1 = Krypt::ASN1.decode(io)
|
10
|
+
cert = Resources.certificate
|
11
|
+
assert_equal(cert, asn1.to_der)
|
12
|
+
assert_equal_streaming(cert, asn1)
|
13
|
+
ensure
|
14
|
+
io.close
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parse_encode_equality_string_io
|
19
|
+
io = StringIO.new(Resources.certificate)
|
20
|
+
asn1 = Krypt::ASN1.decode(io)
|
21
|
+
cert = Resources.certificate
|
22
|
+
assert_equal(cert, asn1.to_der)
|
23
|
+
assert_equal_streaming(cert, asn1)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_parse_encode_equality_string
|
27
|
+
asn1 = Krypt::ASN1.decode(Resources.certificate)
|
28
|
+
cert = Resources.certificate
|
29
|
+
assert_equal(cert, asn1.to_der)
|
30
|
+
assert_equal_streaming(cert, asn1)
|
31
|
+
end
|
32
|
+
|
33
|
+
def test_attributes
|
34
|
+
raw = [%w{30 06 04 01 01 02 01 01}.join("")].pack("H*")
|
35
|
+
asn1 = Krypt::ASN1.decode(raw)
|
36
|
+
assert_universal(Krypt::ASN1::SEQUENCE, asn1)
|
37
|
+
seq = asn1.value
|
38
|
+
assert_equal(2, seq.size)
|
39
|
+
octet = seq[0]
|
40
|
+
assert_universal(Krypt::ASN1::OCTET_STRING, octet)
|
41
|
+
assert_equal("\1", octet.value)
|
42
|
+
integer = seq[1]
|
43
|
+
assert_universal(Krypt::ASN1::INTEGER, integer)
|
44
|
+
assert_equal(1, integer.value)
|
45
|
+
|
46
|
+
assert_equal(raw, asn1.to_der)
|
47
|
+
end
|
48
|
+
|
49
|
+
def test_parse_infinite_length_sequence
|
50
|
+
raw = [%w{30 80 04 01 01 02 01 01 00 00}.join("")].pack("H*")
|
51
|
+
asn1 = Krypt::ASN1.decode(raw)
|
52
|
+
assert_universal(Krypt::ASN1::SEQUENCE, asn1, true)
|
53
|
+
seq = asn1.value
|
54
|
+
assert_equal(3, seq.size)
|
55
|
+
octet = seq[0]
|
56
|
+
assert_universal(Krypt::ASN1::OCTET_STRING, octet)
|
57
|
+
assert_equal("\1", octet.value)
|
58
|
+
integer = seq[1]
|
59
|
+
assert_universal(Krypt::ASN1::INTEGER, integer)
|
60
|
+
assert_equal(1, integer.value)
|
61
|
+
eoc = seq[2]
|
62
|
+
assert_universal(Krypt::ASN1::END_OF_CONTENTS, eoc)
|
63
|
+
assert_nil(eoc.value)
|
64
|
+
|
65
|
+
assert_equal(raw, asn1.to_der)
|
66
|
+
end
|
67
|
+
|
68
|
+
def test_parse_infinite_length_octet_string
|
69
|
+
raw = [%w{24 80 04 01 01 04 01 02 00 00}.join("")].pack("H*")
|
70
|
+
asn1 = Krypt::ASN1.decode(raw)
|
71
|
+
assert_universal(Krypt::ASN1::OCTET_STRING, asn1, true)
|
72
|
+
assert_equal(true, asn1.is_a?(Krypt::ASN1::Constructive))
|
73
|
+
seq = asn1.value
|
74
|
+
assert_equal(3, seq.size)
|
75
|
+
octet1 = seq[0]
|
76
|
+
assert_universal(Krypt::ASN1::OCTET_STRING, octet1)
|
77
|
+
assert_equal("\1", octet1.value)
|
78
|
+
octet2 = seq[1]
|
79
|
+
assert_universal(Krypt::ASN1::OCTET_STRING, octet2)
|
80
|
+
assert_equal("\2", octet2.value)
|
81
|
+
eoc = seq[2]
|
82
|
+
assert_universal(Krypt::ASN1::END_OF_CONTENTS, eoc)
|
83
|
+
assert_nil(eoc.value)
|
84
|
+
|
85
|
+
assert_equal(raw, asn1.to_der)
|
86
|
+
end
|
87
|
+
|
88
|
+
def test_each
|
89
|
+
[%w{30 06 04 01 01 02 01 01},
|
90
|
+
%w{31 06 04 01 01 02 01 01},
|
91
|
+
%w{24 80 04 01 01 04 01 02 00 00}].each do |raw|
|
92
|
+
val = [raw.join("")].pack("H*")
|
93
|
+
cons_header = Krypt::ASN1::Parser.new.next(StringIO.new(val))
|
94
|
+
io = StringIO.new
|
95
|
+
cons_header.encode_to(io)
|
96
|
+
asn1 = Krypt::ASN1.decode(val)
|
97
|
+
asn1.each do |val|
|
98
|
+
val.encode_to(io)
|
99
|
+
end
|
100
|
+
assert_equal(val, io.string.force_encoding("ASCII-8BIT"))
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
private
|
105
|
+
|
106
|
+
def assert_universal(tag, asn1, inf_len=false)
|
107
|
+
assert_equal(tag, asn1.tag)
|
108
|
+
assert_equal(:UNIVERSAL, asn1.tag_class)
|
109
|
+
assert_equal(inf_len, asn1.infinite_length)
|
110
|
+
end
|
111
|
+
|
112
|
+
def assert_equal_streaming(raw, asn1)
|
113
|
+
io = StringIO.new
|
114
|
+
asn1.encode_to(io)
|
115
|
+
assert_equal(raw, io.string.force_encoding("ASCII-8BIT"))
|
116
|
+
end
|
117
|
+
|
118
|
+
end
|
119
|
+
|
@@ -0,0 +1,331 @@
|
|
1
|
+
require_relative 'helper'
|
2
|
+
require 'stringio'
|
3
|
+
|
4
|
+
class Krypt::ParserTest < Test::Unit::TestCase
|
5
|
+
|
6
|
+
def test_parse_and_skip_top_level_file
|
7
|
+
parse_and_skip_top_level(Resources.certificate_io)
|
8
|
+
end
|
9
|
+
|
10
|
+
def test_parse_and_skip_top_level_string_io
|
11
|
+
parse_and_skip_top_level(StringIO.new(Resources.certificate))
|
12
|
+
end
|
13
|
+
|
14
|
+
def test_parse_and_skip_file
|
15
|
+
parse_and_skip(Resources.certificate_io)
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_parse_and_skip_string_io
|
19
|
+
parse_and_skip(StringIO.new(Resources.certificate))
|
20
|
+
end
|
21
|
+
|
22
|
+
def test_consume_top_level_streaming_file
|
23
|
+
consume_top_level_streaming(Resources.certificate_io)
|
24
|
+
end
|
25
|
+
|
26
|
+
def test_consume_top_level_streaming_string_io
|
27
|
+
consume_top_level_streaming(StringIO.new(Resources.certificate))
|
28
|
+
end
|
29
|
+
|
30
|
+
def test_consume_top_level_at_once_file
|
31
|
+
consume_top_level_at_once(Resources.certificate_io)
|
32
|
+
end
|
33
|
+
|
34
|
+
def test_consume_top_level_at_once_string_io
|
35
|
+
consume_top_level_at_once(StringIO.new(Resources.certificate))
|
36
|
+
end
|
37
|
+
|
38
|
+
def test_consume_all_streaming_file
|
39
|
+
consume_all_streaming(Resources.certificate_io)
|
40
|
+
end
|
41
|
+
|
42
|
+
def test_consume_all_streaming_string_io
|
43
|
+
consume_all_streaming(StringIO.new(Resources.certificate))
|
44
|
+
end
|
45
|
+
|
46
|
+
def test_consume_all_at_once_file
|
47
|
+
consume_all_at_once(Resources.certificate_io)
|
48
|
+
end
|
49
|
+
|
50
|
+
def test_consume_all_at_once_string_io
|
51
|
+
consume_all_at_once(StringIO.new(Resources.certificate))
|
52
|
+
end
|
53
|
+
|
54
|
+
def test_parse_tokens_methods_file
|
55
|
+
parse_tokens_test_methods(Resources.certificate_io)
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_parse_tokens_methods_string_io
|
59
|
+
parse_tokens_test_methods(StringIO.new(Resources.certificate))
|
60
|
+
end
|
61
|
+
|
62
|
+
def test_parse_primitive
|
63
|
+
raw = [%w{02 02 01 00}.join("")].pack("H*")
|
64
|
+
parser = Krypt::ASN1::Parser.new
|
65
|
+
io = StringIO.new(raw)
|
66
|
+
header = parser.next(io)
|
67
|
+
assert_equal(Krypt::ASN1::INTEGER, header.tag)
|
68
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
69
|
+
assert_equal(false, header.constructed?)
|
70
|
+
assert_equal(false, header.infinite?)
|
71
|
+
assert_equal(2, header.length)
|
72
|
+
assert_equal(2, header.header_length)
|
73
|
+
assert_header_value_equal(raw, header)
|
74
|
+
end
|
75
|
+
|
76
|
+
def test_parse_constructed
|
77
|
+
raw = [%w{30 06 04 01 01 04 01 02}.join("")].pack("H*")
|
78
|
+
parser = Krypt::ASN1::Parser.new
|
79
|
+
io = StringIO.new(raw)
|
80
|
+
header = parser.next(io)
|
81
|
+
assert_equal(Krypt::ASN1::SEQUENCE, header.tag)
|
82
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
83
|
+
assert_equal(true, header.constructed?)
|
84
|
+
assert_equal(false, header.infinite?)
|
85
|
+
assert_equal(6, header.length)
|
86
|
+
assert_equal(2, header.header_length)
|
87
|
+
assert_header_value_equal(raw, header)
|
88
|
+
end
|
89
|
+
|
90
|
+
def test_parse_constructed
|
91
|
+
raw = [%w{30 02 80 01 00}.join("")].pack("H*")
|
92
|
+
parser = Krypt::ASN1::Parser.new
|
93
|
+
io = StringIO.new(raw)
|
94
|
+
header = parser.next(io)
|
95
|
+
assert_equal(Krypt::ASN1::SEQUENCE, header.tag)
|
96
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
97
|
+
assert_equal(true, header.constructed?)
|
98
|
+
assert_equal(false, header.infinite?)
|
99
|
+
assert_equal(2, header.length)
|
100
|
+
assert_equal(2, header.header_length)
|
101
|
+
header = parser.next(io)
|
102
|
+
assert_equal(0, header.tag)
|
103
|
+
assert_equal(:CONTEXT_SPECIFIC, header.tag_class)
|
104
|
+
assert_equal(false, header.constructed?)
|
105
|
+
assert_equal(false, header.infinite?)
|
106
|
+
assert_equal(1, header.length)
|
107
|
+
assert_equal(2, header.header_length)
|
108
|
+
assert_equal("\0", header.value)
|
109
|
+
assert_nil(parser.next(io))
|
110
|
+
end
|
111
|
+
|
112
|
+
def test_complex_length
|
113
|
+
raw = [%w{04 82 03 e8}.join("")].pack("H*")
|
114
|
+
raw << "\0" * 1000
|
115
|
+
io = StringIO.new(raw)
|
116
|
+
parser = Krypt::ASN1::Parser.new
|
117
|
+
header = parser.next(io)
|
118
|
+
assert_equal(Krypt::ASN1::OCTET_STRING, header.tag)
|
119
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
120
|
+
assert_equal(false, header.constructed?)
|
121
|
+
assert_equal(false, header.infinite?)
|
122
|
+
assert_equal(1000, header.length)
|
123
|
+
assert_equal(4, header.header_length)
|
124
|
+
assert_header_value_equal(raw, header)
|
125
|
+
end
|
126
|
+
|
127
|
+
def test_complex_length_single_octet
|
128
|
+
raw = [%w{df 2a 01 00}.join("")].pack("H*")
|
129
|
+
parser = Krypt::ASN1::Parser.new
|
130
|
+
io = StringIO.new(raw)
|
131
|
+
header = parser.next(io)
|
132
|
+
assert_equal(42, header.tag)
|
133
|
+
assert_equal(:PRIVATE, header.tag_class)
|
134
|
+
assert_equal(false, header.constructed?)
|
135
|
+
assert_equal(false, header.infinite?)
|
136
|
+
assert_equal(1, header.length)
|
137
|
+
assert_equal(3, header.header_length)
|
138
|
+
assert_header_value_equal(raw, header)
|
139
|
+
end
|
140
|
+
|
141
|
+
def test_complex_tag_two_octets
|
142
|
+
raw = [%w{5f 82 2c 01 00}.join("")].pack("H*")
|
143
|
+
parser = Krypt::ASN1::Parser.new
|
144
|
+
io = StringIO.new(raw)
|
145
|
+
header = parser.next(io)
|
146
|
+
assert_equal(300, header.tag)
|
147
|
+
assert_equal(:APPLICATION, header.tag_class)
|
148
|
+
assert_equal(false, header.constructed?)
|
149
|
+
assert_equal(false, header.infinite?)
|
150
|
+
assert_equal(1, header.length)
|
151
|
+
assert_equal(4, header.header_length)
|
152
|
+
assert_header_value_equal(raw, header)
|
153
|
+
end
|
154
|
+
|
155
|
+
def test_inf_length_parsing_at_once_string_io
|
156
|
+
inf_length_parsing_streaming_string_io(:AT_ONCE)
|
157
|
+
end
|
158
|
+
|
159
|
+
def test_inf_length_parsing_streaming_string_io
|
160
|
+
inf_length_parsing_streaming_string_io(:STREAMING)
|
161
|
+
end
|
162
|
+
|
163
|
+
def test_inf_length_parsing_streaming_fixed_buffer_string_io
|
164
|
+
inf_length_parsing_streaming_string_io(:STREAMING_FIXED)
|
165
|
+
end
|
166
|
+
|
167
|
+
def test_inf_length_parsing_at_once_values_only_string_io
|
168
|
+
inf_length_parsing_streaming_string_io(:AT_ONCE, true)
|
169
|
+
end
|
170
|
+
|
171
|
+
def test_inf_length_parsing_streaming_values_only_string_io
|
172
|
+
inf_length_parsing_streaming_string_io(:STREAMING, true)
|
173
|
+
end
|
174
|
+
|
175
|
+
def test_inf_length_parsing_streaming_fixed_buffer_values_only_string_io
|
176
|
+
inf_length_parsing_streaming_string_io(:STREAMING_FIXED, true)
|
177
|
+
end
|
178
|
+
|
179
|
+
private
|
180
|
+
|
181
|
+
def parse_and_skip_top_level(io)
|
182
|
+
parser = Krypt::ASN1::Parser.new
|
183
|
+
header = parser.next(io)
|
184
|
+
assert_equal(Krypt::ASN1::SEQUENCE, header.tag)
|
185
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
186
|
+
assert(header.constructed?)
|
187
|
+
assert(!header.infinite?)
|
188
|
+
assert_equal(Resources.certificate.size, header.header_size + header.size)
|
189
|
+
header.skip_value
|
190
|
+
assert_nil(parser.next(io))
|
191
|
+
ensure
|
192
|
+
io.close
|
193
|
+
end
|
194
|
+
|
195
|
+
def parse_and_skip(io)
|
196
|
+
parser = Krypt::ASN1::Parser.new
|
197
|
+
num_tokens = 0
|
198
|
+
while header = parser.next(io)
|
199
|
+
num_tokens += 1
|
200
|
+
unless header.constructed?
|
201
|
+
header.skip_value
|
202
|
+
end
|
203
|
+
end
|
204
|
+
assert(num_tokens > 1)
|
205
|
+
ensure
|
206
|
+
io.close
|
207
|
+
end
|
208
|
+
|
209
|
+
def consume_top_level_streaming(io)
|
210
|
+
parser = Krypt::ASN1::Parser.new
|
211
|
+
header = parser.next(io)
|
212
|
+
stream = header.value_io
|
213
|
+
consume_streaming(stream)
|
214
|
+
assert_nil(parser.next(io))
|
215
|
+
ensure
|
216
|
+
io.close
|
217
|
+
end
|
218
|
+
|
219
|
+
def consume_top_level_at_once(io)
|
220
|
+
parser = Krypt::ASN1::Parser.new
|
221
|
+
header = parser.next(io)
|
222
|
+
stream = header.value_io
|
223
|
+
stream.read
|
224
|
+
assert_nil(parser.next(io))
|
225
|
+
ensure
|
226
|
+
io.close
|
227
|
+
end
|
228
|
+
|
229
|
+
def consume_all_streaming(io)
|
230
|
+
parser = Krypt::ASN1::Parser.new
|
231
|
+
while header = parser.next(io)
|
232
|
+
unless header.constructed?
|
233
|
+
stream = header.value_io
|
234
|
+
consume_streaming(stream)
|
235
|
+
end
|
236
|
+
end
|
237
|
+
assert_nil(parser.next(io))
|
238
|
+
ensure
|
239
|
+
io.close
|
240
|
+
end
|
241
|
+
|
242
|
+
def consume_all_at_once(io)
|
243
|
+
parser = Krypt::ASN1::Parser.new
|
244
|
+
while header = parser.next(io)
|
245
|
+
unless header.constructed?
|
246
|
+
stream = header.value_io
|
247
|
+
stream.read
|
248
|
+
end
|
249
|
+
end
|
250
|
+
assert_nil(parser.next(io))
|
251
|
+
ensure
|
252
|
+
io.close
|
253
|
+
end
|
254
|
+
|
255
|
+
def parse_tokens_test_methods(io)
|
256
|
+
parser = Krypt::ASN1::Parser.new
|
257
|
+
while header = parser.next(io)
|
258
|
+
assert_not_nil(header.tag)
|
259
|
+
assert_not_nil(header.tag_class)
|
260
|
+
assert_not_nil(header.size)
|
261
|
+
assert_not_nil(header.length)
|
262
|
+
assert_not_nil(header.header_size)
|
263
|
+
assert_not_nil(header.header_length)
|
264
|
+
assert_not_nil(header.constructed?)
|
265
|
+
assert_not_nil(header.infinite?)
|
266
|
+
unless header.constructed?
|
267
|
+
if (header.tag == Krypt::ASN1::NULL || header.tag == Krypt::ASN1::END_OF_CONTENTS)
|
268
|
+
assert_nil(header.value)
|
269
|
+
else
|
270
|
+
assert_not_nil(header.value)
|
271
|
+
end
|
272
|
+
end
|
273
|
+
end
|
274
|
+
ensure
|
275
|
+
io.close
|
276
|
+
end
|
277
|
+
|
278
|
+
def inf_length_parsing_streaming_string_io(mode, values_only=false)
|
279
|
+
raw = [%w{24 80 04 01 01 04 01 02 00 00}.join("")].pack("H*")
|
280
|
+
io = StringIO.new(raw)
|
281
|
+
parser = Krypt::ASN1::Parser.new
|
282
|
+
header = parser.next(io)
|
283
|
+
assert_equal(Krypt::ASN1::OCTET_STRING, header.tag)
|
284
|
+
assert_equal(:UNIVERSAL, header.tag_class)
|
285
|
+
assert_equal(true, header.constructed?)
|
286
|
+
assert_equal(true, header.infinite?)
|
287
|
+
assert_equal(0, header.length)
|
288
|
+
assert_equal(2, header.header_length)
|
289
|
+
value_io = header.value_io(values_only)
|
290
|
+
|
291
|
+
result = StringIO.new("", "wb")
|
292
|
+
unless values_only
|
293
|
+
header.encode_to(result)
|
294
|
+
end
|
295
|
+
|
296
|
+
case mode
|
297
|
+
when :AT_ONCE
|
298
|
+
result << value_io.read
|
299
|
+
when :STREAMING
|
300
|
+
while buf = value_io.read(3, buf)
|
301
|
+
result << buf
|
302
|
+
end
|
303
|
+
when :STREAMING_FIXED
|
304
|
+
buf = ""
|
305
|
+
while value_io.read(3, buf)
|
306
|
+
result << buf
|
307
|
+
end
|
308
|
+
end
|
309
|
+
|
310
|
+
if values_only
|
311
|
+
assert_equal( [%w{01 02}.join("")].pack("H*"), result.string)
|
312
|
+
else
|
313
|
+
assert_equal(raw, result.string.force_encoding("ASCII-8BIT"))
|
314
|
+
end
|
315
|
+
end
|
316
|
+
|
317
|
+
def consume_streaming(io)
|
318
|
+
buffer = ""
|
319
|
+
while io.read(3, buffer)
|
320
|
+
end
|
321
|
+
end
|
322
|
+
|
323
|
+
def assert_header_value_equal(expected, header)
|
324
|
+
io = StringIO.new()
|
325
|
+
header.encode_to(io)
|
326
|
+
io << header.value
|
327
|
+
assert_equal(expected, io.string.force_encoding("ASCII-8BIT"))
|
328
|
+
end
|
329
|
+
|
330
|
+
end
|
331
|
+
|