krypt 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (70) hide show
  1. data/LICENSE +20 -0
  2. data/README.md +82 -0
  3. data/lib/krypt.rb +49 -0
  4. data/lib/krypt/asn1.rb +3 -0
  5. data/lib/krypt/asn1/common.rb +96 -0
  6. data/lib/krypt/asn1/template.rb +257 -0
  7. data/lib/krypt/codec.rb +57 -0
  8. data/lib/krypt/codec/base64.rb +140 -0
  9. data/lib/krypt/codec/base_codec.rb +36 -0
  10. data/lib/krypt/codec/hex.rb +122 -0
  11. data/lib/krypt/digest.rb +112 -0
  12. data/lib/krypt/hmac.rb +69 -0
  13. data/lib/krypt/pkcs5.rb +1 -0
  14. data/lib/krypt/pkcs5/pbkdf2.rb +41 -0
  15. data/lib/krypt/provider.rb +35 -0
  16. data/lib/krypt/x509.rb +3 -0
  17. data/lib/krypt/x509/certificate.rb +36 -0
  18. data/lib/krypt/x509/common.rb +41 -0
  19. data/lib/krypt/x509/crl.rb +33 -0
  20. data/lib/krypt_missing.rb +32 -0
  21. data/spec/krypt-core/MEMO.txt +85 -0
  22. data/spec/krypt-core/asn1/asn1_bit_string_spec.rb +475 -0
  23. data/spec/krypt-core/asn1/asn1_boolean_spec.rb +392 -0
  24. data/spec/krypt-core/asn1/asn1_constants_spec.rb +71 -0
  25. data/spec/krypt-core/asn1/asn1_data_spec.rb +1153 -0
  26. data/spec/krypt-core/asn1/asn1_end_of_contents_spec.rb +133 -0
  27. data/spec/krypt-core/asn1/asn1_enumerated_spec.rb +458 -0
  28. data/spec/krypt-core/asn1/asn1_generalized_time_spec.rb +492 -0
  29. data/spec/krypt-core/asn1/asn1_integer_spec.rb +557 -0
  30. data/spec/krypt-core/asn1/asn1_null_spec.rb +360 -0
  31. data/spec/krypt-core/asn1/asn1_object_id_spec.rb +495 -0
  32. data/spec/krypt-core/asn1/asn1_octet_string_spec.rb +456 -0
  33. data/spec/krypt-core/asn1/asn1_parser_spec.rb +503 -0
  34. data/spec/krypt-core/asn1/asn1_pem_spec.rb +282 -0
  35. data/spec/krypt-core/asn1/asn1_sequence_spec.rb +637 -0
  36. data/spec/krypt-core/asn1/asn1_set_spec.rb +795 -0
  37. data/spec/krypt-core/asn1/asn1_utc_time_spec.rb +495 -0
  38. data/spec/krypt-core/asn1/asn1_utf8_string_spec.rb +404 -0
  39. data/spec/krypt-core/asn1/resources.rb +53 -0
  40. data/spec/krypt-core/base64/base64_spec.rb +97 -0
  41. data/spec/krypt-core/digest/digest_spec.rb +707 -0
  42. data/spec/krypt-core/hex/hex_spec.rb +102 -0
  43. data/spec/krypt-core/pem/pem_decode_spec.rb +235 -0
  44. data/spec/krypt-core/resources.rb +1 -0
  45. data/spec/krypt-core/template/template_choice_parse_spec.rb +289 -0
  46. data/spec/krypt-core/template/template_dsl_spec.rb +351 -0
  47. data/spec/krypt-core/template/template_seq_of_parse_spec.rb +64 -0
  48. data/spec/krypt-core/template/template_seq_parse_spec.rb +1241 -0
  49. data/spec/krypt/codec/base64_decoder_spec.rb +94 -0
  50. data/spec/krypt/codec/base64_encoder_spec.rb +94 -0
  51. data/spec/krypt/codec/base64_mixed_spec.rb +16 -0
  52. data/spec/krypt/codec/hex_decoder_spec.rb +94 -0
  53. data/spec/krypt/codec/hex_encoder_spec.rb +94 -0
  54. data/spec/krypt/codec/hex_mixed_spec.rb +17 -0
  55. data/spec/krypt/codec/identity_shared.rb +119 -0
  56. data/spec/krypt/hmac/hmac_spec.rb +311 -0
  57. data/spec/krypt/pkcs5/pbkdf2_spec.rb +79 -0
  58. data/spec/krypt/provider/provider_spec.rb +83 -0
  59. data/spec/res/ca-bundle.crt +11758 -0
  60. data/spec/res/certificate.cer +0 -0
  61. data/spec/res/certificate.pem +20 -0
  62. data/spec/res/multiple_certs.pem +60 -0
  63. data/spec/resources.rb +66 -0
  64. data/test/helper.rb +8 -0
  65. data/test/res/certificate.cer +0 -0
  66. data/test/resources.rb +48 -0
  67. data/test/scratch.rb +28 -0
  68. data/test/test_krypt_asn1.rb +119 -0
  69. data/test/test_krypt_parser.rb +331 -0
  70. metadata +134 -0
@@ -0,0 +1,69 @@
1
+ module Krypt
2
+ class HMAC
3
+ include Krypt::Helper::XOR
4
+
5
+ def initialize(digest, key)
6
+ @digest = digest
7
+ @key = process_key(key)
8
+
9
+ # hash ipad
10
+ hash_pad(0x36)
11
+ end
12
+
13
+ def update(data)
14
+ @digest << data
15
+ end
16
+ alias << update
17
+
18
+ def digest
19
+ inner_digest = @digest.digest
20
+ # hash opad
21
+ hash_pad(0x5c)
22
+ @digest << inner_digest
23
+ @digest.digest
24
+ end
25
+
26
+ def hexdigest
27
+ Krypt::Hex.encode(digest)
28
+ end
29
+
30
+ class << self
31
+
32
+ def digest(md, key, data)
33
+ hmac = self.new(md, key)
34
+ hmac << data
35
+ hmac.digest
36
+ end
37
+
38
+ def hexdigest(md, key, data)
39
+ Krypt::Hex.encode(digest(md, key, data))
40
+ end
41
+
42
+ end
43
+
44
+ private
45
+
46
+ def process_key(key)
47
+ block_len = @digest.block_length
48
+
49
+ if key.size > block_len
50
+ key = @digest.digest(key)
51
+ end
52
+
53
+ if key.size < block_len
54
+ new_key = key.dup.tap do |new_key|
55
+ (block_len - key.size).times { new_key << 0 }
56
+ end
57
+ else
58
+ key
59
+ end
60
+ end
61
+
62
+ def hash_pad(pad_char)
63
+ @digest << String.new.tap do |s|
64
+ @key.each_byte { |b| s << (pad_char ^ b) }
65
+ end
66
+ end
67
+
68
+ end
69
+ end
@@ -0,0 +1 @@
1
+ require_relative 'pkcs5/pbkdf2'
@@ -0,0 +1,41 @@
1
+ module Krypt
2
+ class PBKDF2
3
+ include Krypt::Helper::XOR
4
+
5
+ MAX_FACTOR = (2 ** 32) - 1
6
+
7
+ def initialize(digest)
8
+ @digest = digest
9
+ @block_size = digest.digest_length
10
+ end
11
+
12
+ def generate(pwd, salt, iter, outlen)
13
+ raise "outlen too large" if outlen > MAX_FACTOR * @block_size
14
+
15
+ @digest.reset
16
+ num_blocks = (outlen.to_f / @block_size).ceil
17
+ # enforces ASCII-8BIT
18
+ String.new.tap do |result|
19
+ 1.upto(num_blocks) { |i| result << f(pwd, salt, iter, i) }
20
+ end.slice(0, outlen)
21
+ end
22
+
23
+ def generate_hex(pwd, salt, iter, outlen)
24
+ Krypt::Hex.encode(generate(pwd, salt, iter, outlen))
25
+ end
26
+
27
+ private
28
+
29
+ def f(pwd, salt, iter, i)
30
+ u = salt + [i].pack("L>")
31
+ ("\0" * @block_size).force_encoding(Encoding::BINARY).tap do |result|
32
+ 1.upto(iter) do
33
+ u = Krypt::HMAC.digest(@digest, pwd, u)
34
+ xor!(result, u)
35
+ end
36
+ end
37
+ end
38
+
39
+ end
40
+ end
41
+
@@ -0,0 +1,35 @@
1
+ module Krypt::Provider
2
+
3
+ PROVIDERS = {}
4
+ PROVIDER_LIST = []
5
+
6
+ class AlreadyExistsError < Krypt::Error; end
7
+
8
+ class ServiceNotAvailableError < Krypt::Error; end
9
+
10
+ module_function
11
+
12
+ def register(name, provider)
13
+ raise AlreadyExistsError.new("There already is a Provider named #{name}") if PROVIDERS.has_key?(name)
14
+ PROVIDERS[name] = provider
15
+ PROVIDER_LIST << name
16
+ end
17
+
18
+ def by_name(name)
19
+ PROVIDERS[name]
20
+ end
21
+
22
+ def remove(name)
23
+ PROVIDERS.delete(name)
24
+ PROVIDER_LIST.delete(name)
25
+ end
26
+
27
+ def new_service(klass, *args)
28
+ PROVIDER_LIST.reverse.each do |name|
29
+ service = PROVIDERS[name].new_service(klass, *args)
30
+ return service if service
31
+ end
32
+ raise ServiceNotAvailableError.new("The requested service is not available")
33
+ end
34
+
35
+ end
@@ -0,0 +1,3 @@
1
+ require_relative 'x509/common'
2
+ require_relative 'x509/certificate'
3
+ require_relative 'x509/crl'
@@ -0,0 +1,36 @@
1
+ module Krypt
2
+ module X509
3
+
4
+ class Certificate
5
+ include ASN1::Template::Sequence
6
+
7
+ class SubjectPublicKeyInfo
8
+ include ASN1::Template::Sequence
9
+
10
+ asn1_template :algorithm, ASN1::AlgorithmIdentifier
11
+ asn1_bit_string :subject_pkey
12
+ end
13
+
14
+ class TBSCertificate
15
+ include ASN1::Template::Sequence
16
+
17
+ asn1_integer :version, tag: 0, tagging: :EXPLICIT, default: 0
18
+ asn1_integer :serial
19
+ asn1_template :algorithm, ASN1::AlgorithmIdentifier
20
+ asn1_template :issuer, ASN1::DistinguishedName
21
+ asn1_template :validity, X509::Validity
22
+ asn1_template :subject, ASN1::DistinguishedName
23
+ asn1_template :subject_pkey, SubjectPublicKeyInfo
24
+ asn1_bit_string :issuer_id, tag: 1, tagging: :IMPLICIT, optional: true
25
+ asn1_bit_string :subject_id, tag: 2, tagging: :IMPLICIT, optional: true
26
+ asn1_sequence_of :extensions, X509::Extension, tag: 3, tagging: :EXPLICIT, optional: true
27
+ end
28
+
29
+ asn1_template :tbs_cert, TBSCertificate
30
+ asn1_template :algorithm, ASN1::AlgorithmIdentifier
31
+ asn1_bit_string :signature
32
+ end
33
+
34
+ end
35
+ end
36
+
@@ -0,0 +1,41 @@
1
+ module Krypt
2
+ module X509
3
+
4
+ class Extension
5
+ include ASN1::Template::Sequence
6
+
7
+ asn1_object_id :id
8
+ asn1_boolean :critical, default: false
9
+ asn1_octet_string :value
10
+ end
11
+
12
+ class Attribute
13
+ include ASN1::Template::Sequence
14
+
15
+ asn1_object_id :type
16
+ asn1_set_of :value, ASN1::ASN1Data
17
+ end
18
+
19
+ class IssuerSerialNumber
20
+ include Krypt::ASN1::Template::Sequence
21
+
22
+ asn1_template :issuer, ASN1::DistinguishedName
23
+ asn1_integer :serial
24
+ end
25
+
26
+ class Time
27
+ include ASN1::Template::Choice
28
+
29
+ asn1_utc_time
30
+ asn1_generalized_time
31
+ end
32
+
33
+ class Validity
34
+ include ASN1::Template::Sequence
35
+
36
+ asn1_template :not_before, X509::Time
37
+ asn1_template :not_after, X509::Time
38
+ end
39
+
40
+ end
41
+ end
@@ -0,0 +1,33 @@
1
+ module Krypt
2
+ module X509
3
+
4
+ class CRL
5
+ include ASN1::Template::Sequence
6
+
7
+ class RevokedCertificates
8
+ include ASN1::Template::Sequence
9
+
10
+ asn1_integer :serial_number
11
+ asn1_template :revocation_date, X509::Time
12
+ asn1_template :crl_entry_extensions, X509::Extension
13
+ end
14
+
15
+ class TBSCertList
16
+ include ASN1::Template::Sequence
17
+
18
+ asn1_integer :version, default: 1
19
+ asn1_template :signature_algorithm, ASN1::AlgorithmIdentifier
20
+ asn1_template :issuer, ASN1::DistinguishedName
21
+ asn1_template :this_update, X509::Time
22
+ asn1_template :next_update, X509::Time
23
+ asn1_sequence_of :revoked_certificates, RevokedCertificates, optional: true
24
+ asn1_template :extensions, X509::Extension, tag: 0, tagging: :EXPLICIT, optional: true
25
+ end
26
+
27
+ asn1_template :tbs_cert_list, TBSCertList
28
+ asn1_template :signature_algorithm, ASN1::AlgorithmIdentifier
29
+ asn1_bit_string :signature
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,32 @@
1
+ unless Kernel.respond_to? :private_constant
2
+ module Kernel
3
+ def private_constant(*)
4
+ # TODO delete when sufficiently supported
5
+ nil
6
+ end
7
+ end
8
+ end
9
+
10
+ module Krypt
11
+ module Helper
12
+
13
+ module XOR
14
+ def xor(s1, s2)
15
+ String.new.tap do |result|
16
+ s1.bytes.each_with_index do |b, i|
17
+ result << (b ^ s2.getbyte(i))
18
+ end
19
+ end
20
+ end
21
+
22
+ def xor!(recv, other)
23
+ recv.bytes.each_with_index do |b, i|
24
+ recv.setbyte(i, b ^ other.getbyte(i))
25
+ end
26
+ recv
27
+ end
28
+ end
29
+
30
+ end
31
+ end
32
+
@@ -0,0 +1,85 @@
1
+ module Krypt
2
+ class KryptError < StandardError; end
3
+
4
+ module ASN1
5
+ class ASN1Error < KryptError; end
6
+ class ParseError < ASN1Error; end
7
+ class SerializeError < ASN1Error; end
8
+
9
+ END_OF_CONTENTS = 0
10
+ BOOLEAN = 1
11
+ ...
12
+ BMP_STRING = 30
13
+
14
+ UNIVERSAL_TAG_NAME = ["END_OF_CONTENTS", "BOOLEAN", ... ]
15
+
16
+ def self.decode(str_or_file_or_readable)
17
+ end
18
+
19
+ class ASN1Data
20
+ attr_accessor :tag, :tag_class, :value
21
+ attr_reader :infinite_length
22
+
23
+ def initialize(value, tag, tag_class); end
24
+ def to_der; end
25
+ def encode_to(io); end #=> self
26
+ end
27
+
28
+ class Primitive < ASN1Data
29
+ end
30
+
31
+ class Constructive < ASN1Data
32
+ attr_writer :infinite_length # ?
33
+ def each; end
34
+ end
35
+
36
+ class EndOfContents < Primitive; end
37
+ class Boolean < Primitive; end
38
+ class Integer < Primitive; end
39
+ class Enumerated < Primitive; end
40
+ class BitString < Primitive; end
41
+ class OctetString < Primitive; end
42
+ class UTF8String < Primitive; end
43
+ class NumericString < Primitive; end
44
+ class PrintableString < Primitive; end
45
+ class T61String < Primitive; end
46
+ class VideotexString < Primitive; end
47
+ class IA5String < Primitive; end
48
+ class GraphicString < Primitive; end
49
+ class ISO64String < Primitive; end
50
+ class GeneralString < Primitive; end
51
+ class UniversalString < Primitive; end
52
+ class BMPString < Primitive; end
53
+ class Null < Primitive; end
54
+ class ObjectId < Primitive; end
55
+ class UTCTime < Primitive; end
56
+ class GeneralizedTime < Primitive; end
57
+
58
+ class Sequence < Constructive; end
59
+ class Set < Constructive; end
60
+
61
+ class Parser
62
+ def next(io); end #=> Header
63
+ end
64
+
65
+ class Header
66
+ attr_reader :tag, :tag_class, :length, :header_length
67
+ def constructed?; end
68
+ def infinite?; end
69
+
70
+ def bytes; end #=> String
71
+ def encode_to(io); end #=> self
72
+
73
+ def skip_value; end
74
+ def value; end
75
+ def value_io; end # get value (V of TLV) stream
76
+
77
+ def to_s; end
78
+ end
79
+
80
+ class Instream
81
+ def read(len = nil, buf = nil); end
82
+ def seek(offset, whence = SEEK_SET); end
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,475 @@
1
+ # encoding: US-ASCII
2
+
3
+ require 'rspec'
4
+ require 'krypt'
5
+ require 'openssl'
6
+ require_relative './resources'
7
+
8
+ describe Krypt::ASN1::BitString do
9
+ include Krypt::ASN1::Resources
10
+
11
+ let(:mod) { Krypt::ASN1 }
12
+ let(:klass) { mod::BitString }
13
+ let(:decoder) { mod }
14
+ let(:asn1error) { mod::ASN1Error }
15
+
16
+ # For test against OpenSSL
17
+ #
18
+ #let(:mod) { OpenSSL::ASN1 }
19
+ #
20
+ # OpenSSL stub for signature mismatch
21
+ class OpenSSL::ASN1::BitString
22
+ class << self
23
+ alias old_new new
24
+ def new(*args)
25
+ if args.size > 1
26
+ args = [args[0], args[1], :IMPLICIT, args[2]]
27
+ end
28
+ old_new(*args)
29
+ end
30
+ end
31
+ end
32
+
33
+ def _B(bin_encode)
34
+ [bin_encode.reverse].pack('b*').reverse
35
+ end
36
+
37
+ describe '#new' do
38
+ context 'gets value for construct' do
39
+ subject { klass.new(value) }
40
+
41
+ context 'accepts binary packed 01010101 := "\x55"' do
42
+ let(:value) { _B('01010101') }
43
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
44
+ its(:tag_class) { should == :UNIVERSAL }
45
+ its(:value) { should == value }
46
+ its(:infinite_length) { should == false }
47
+ end
48
+
49
+ context 'accepts (empty)' do
50
+ let(:value) { '' }
51
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
52
+ its(:tag_class) { should == :UNIVERSAL }
53
+ its(:value) { should == '' }
54
+ its(:infinite_length) { should == false }
55
+ end
56
+ end
57
+
58
+ context 'gets explicit tag number as the 2nd argument' do
59
+ subject { klass.new(_B('01010101'), tag, :PRIVATE) }
60
+
61
+ context 'accepts default tag' do
62
+ let(:tag) { Krypt::ASN1::BIT_STRING }
63
+ its(:tag) { should == tag }
64
+ end
65
+
66
+ context 'accepts custom tag' do
67
+ let(:tag) { 14 }
68
+ its(:tag) { should == tag }
69
+ end
70
+ end
71
+
72
+ context 'gets tag class symbol as the 3rd argument' do
73
+ subject { klass.new(_B('01010101'), Krypt::ASN1::BIT_STRING, tag_class) }
74
+
75
+ context 'accepts :UNIVERSAL' do
76
+ let(:tag_class) { :UNIVERSAL }
77
+ its(:tag_class) { should == tag_class }
78
+ end
79
+
80
+ context 'accepts :APPLICATION' do
81
+ let(:tag_class) { :APPLICATION }
82
+ its(:tag_class) { should == tag_class }
83
+ end
84
+
85
+ context 'accepts :CONTEXT_SPECIFIC' do
86
+ let(:tag_class) { :CONTEXT_SPECIFIC }
87
+ its(:tag_class) { should == tag_class }
88
+ end
89
+
90
+ context 'accepts :PRIVATE' do
91
+ let(:tag_class) { :PRIVATE }
92
+ its(:tag_class) { should == tag_class }
93
+ end
94
+
95
+ context 'accepts :IMPLICIT' do
96
+ let(:tag_class) { :IMPLICIT }
97
+ its(:tag_class) { should == tag_class }
98
+ end
99
+
100
+ context 'accepts :EXPLICIT' do
101
+ let(:tag_class) { :EXPLICIT }
102
+ its(:tag_class) { should == tag_class }
103
+ end
104
+ end
105
+
106
+ context 'when the 2nd argument is given but 3rd argument is omitted' do
107
+ subject { klass.new(_B('01010101'), Krypt::ASN1::BIT_STRING) }
108
+ its(:tag_class) { should == :CONTEXT_SPECIFIC }
109
+ end
110
+
111
+ context 'sets unused_bits to 0' do
112
+ subject { klass.new(nil) }
113
+ its(:unused_bits) { should == 0 }
114
+ end
115
+ end
116
+
117
+ describe 'accessors' do
118
+ describe '#value' do
119
+ subject { o = klass.new(nil); o.value = value; o }
120
+
121
+ context 'accepts binary packed 01010101 := "\x55"' do
122
+ let(:value) { _B('01010101') }
123
+ its(:value) { should == value }
124
+ end
125
+
126
+ context 'accepts (empty)' do
127
+ let(:value) { '' }
128
+ its(:value) { should == '' }
129
+ end
130
+ end
131
+
132
+ describe '#tag' do
133
+ subject { o = klass.new(nil); o.tag = tag; o }
134
+
135
+ context 'accepts default tag' do
136
+ let(:tag) { Krypt::ASN1::BIT_STRING }
137
+ its(:tag) { should == tag }
138
+ end
139
+
140
+ context 'accepts custom tag' do
141
+ let(:tag) { 14 }
142
+ its(:tag) { should == tag }
143
+ end
144
+ end
145
+
146
+ describe '#tag_class' do
147
+ subject { o = klass.new(nil); o.tag_class = tag_class; o }
148
+
149
+ context 'accepts :UNIVERSAL' do
150
+ let(:tag_class) { :UNIVERSAL }
151
+ its(:tag_class) { should == tag_class }
152
+ end
153
+
154
+ context 'accepts :APPLICATION' do
155
+ let(:tag_class) { :APPLICATION }
156
+ its(:tag_class) { should == tag_class }
157
+ end
158
+
159
+ context 'accepts :CONTEXT_SPECIFIC' do
160
+ let(:tag_class) { :CONTEXT_SPECIFIC }
161
+ its(:tag_class) { should == tag_class }
162
+ end
163
+
164
+ context 'accepts :PRIVATE' do
165
+ let(:tag_class) { :PRIVATE }
166
+ its(:tag_class) { should == tag_class }
167
+ end
168
+
169
+ context 'accepts :IMPLICIT' do
170
+ let(:tag_class) { :IMPLICIT }
171
+ its(:tag_class) { should == tag_class }
172
+ end
173
+
174
+ context 'accepts :EXPLICIT' do
175
+ let(:tag_class) { :EXPLICIT }
176
+ its(:tag_class) { should == tag_class }
177
+ end
178
+ end
179
+ end
180
+
181
+ describe '#to_der' do
182
+ context 'encodes a given value' do
183
+ subject { klass.new(value).to_der }
184
+
185
+ context '01010101' do
186
+ let(:value) { _B('01010101') }
187
+ it { should == "\x03\x02\x00\x55" }
188
+ end
189
+
190
+ context '010101010' do
191
+ let(:value) { _B('010101010') }
192
+ it { should == "\x03\x03\x00\x00\xAA" }
193
+ end
194
+
195
+ context '(empty)' do
196
+ let(:value) { '' }
197
+ it { should == "\x03\x01\x00" }
198
+ end
199
+
200
+ context '999 octets' do
201
+ let(:value) { _B('1' * 8 * 999) }
202
+ it { should == "\x03\x82\x03\xE8\x00" + "\xFF" * 999 }
203
+ end
204
+
205
+ context '1000 octets' do
206
+ let(:value) { _B('0' * 8 * 1000) }
207
+ it { should == "\x03\x82\x03\xE9\x00" + "\x00" * 1000 }
208
+ end
209
+
210
+ context '1001 octets' do
211
+ let(:value) { _B('1' * 8 * 1001) }
212
+ it { should == "\x03\x82\x03\xEA\x00" + "\xFF" * 1001 }
213
+ end
214
+
215
+ context 'nil' do
216
+ let(:value) { nil }
217
+ it { -> { subject }.should raise_error asn1error }
218
+ end
219
+ end
220
+
221
+ context 'encodes tag number' do
222
+ subject { klass.new(_B('01010101'), tag, :PRIVATE).to_der }
223
+
224
+ context 'default tag' do
225
+ let(:tag) { Krypt::ASN1::BIT_STRING }
226
+ it { should == "\xC3\x02\x00\x55" }
227
+ end
228
+
229
+ context 'custom tag' do
230
+ let(:tag) { 14 }
231
+ it { should == "\xCE\x02\x00\x55" }
232
+ end
233
+
234
+ context 'nil' do
235
+ let(:tag) { nil }
236
+ it { -> { subject }.should raise_error asn1error }
237
+ end
238
+ end
239
+
240
+ context 'encodes tag class' do
241
+ subject { klass.new(_B('01010101'), Krypt::ASN1::BIT_STRING, tag_class).to_der }
242
+
243
+ context 'UNIVERSAL' do
244
+ let(:tag_class) { :UNIVERSAL }
245
+ it { should == "\x03\x02\x00\x55" }
246
+ end
247
+
248
+ context 'APPLICATION' do
249
+ let(:tag_class) { :APPLICATION }
250
+ it { should == "\x43\x02\x00\x55" }
251
+ end
252
+
253
+ context 'CONTEXT_SPECIFIC' do
254
+ let(:tag_class) { :CONTEXT_SPECIFIC }
255
+ it { should == "\x83\x02\x00\x55" }
256
+ end
257
+
258
+ context 'PRIVATE' do
259
+ let(:tag_class) { :PRIVATE }
260
+ it { should == "\xC3\x02\x00\x55" }
261
+ end
262
+
263
+ context "IMPLICIT" do
264
+ let(:tag_class) { :IMPLICIT }
265
+ it { should == "\x83\x02\x00\x55" }
266
+ end
267
+
268
+ context "EXPLICIT" do
269
+ let(:tag_class) { :EXPLICIT }
270
+ it { should == "\xA3\x04\x03\x02\x00\x55" }
271
+ end
272
+
273
+ context nil do
274
+ let(:tag_class) { nil }
275
+ it { -> { subject }.should raise_error asn1error } # TODO: ossl does not check nil
276
+ end
277
+
278
+ context :no_such_class do
279
+ let(:tag_class) { :no_such_class }
280
+ it { -> { subject }.should raise_error asn1error }
281
+ end
282
+ end
283
+
284
+ context 'encodes values set via accessors' do
285
+ subject {
286
+ o = klass.new(nil)
287
+ o.value = value if defined? value
288
+ o.tag = tag if defined? tag
289
+ o.tag_class = tag_class if defined? tag_class
290
+ o.unused_bits = unused_bits if defined? unused_bits
291
+ o.to_der
292
+ }
293
+
294
+ context 'value: 01010101' do
295
+ let(:value) { _B('01010101') }
296
+ it { should == "\x03\x02\x00\x55" }
297
+ end
298
+
299
+ context 'custom tag' do
300
+ let(:value) { _B('01010101') }
301
+ let(:tag) { 14 }
302
+ let(:tag_class) { :PRIVATE }
303
+ it { should == "\xCE\x02\x00\x55" }
304
+ end
305
+
306
+ context 'tag_class' do
307
+ let(:value) { _B('01010101') }
308
+ let(:tag_class) { :APPLICATION }
309
+ it { should == "\x43\x02\x00\x55" }
310
+ end
311
+
312
+ context 'unused_bits' do
313
+ let(:value) { _B('01010100') }
314
+ let(:unused_bits) { 2 }
315
+ it { should == "\x03\x02\x02\x54" }
316
+ end
317
+
318
+ context 'rejects unused_bits' do
319
+ let (:value) { _B('01010100') }
320
+
321
+ context '< 0' do
322
+ let(:unused_bits) { -1 }
323
+ it { -> { subject }.should raise_error asn1error }
324
+ end
325
+
326
+ context '> 7' do
327
+ let(:unused_bits) { 8 }
328
+ it { -> { subject }.should raise_error asn1error }
329
+ end
330
+ end
331
+ end
332
+ end
333
+
334
+ describe '#encode_to' do
335
+ context 'encodes to an IO' do
336
+ subject { klass.new(value).encode_to(io); io }
337
+
338
+ context "StringIO" do
339
+ let(:value) { _B('01010101') }
340
+ let(:io) { string_io_object }
341
+ its(:written_bytes) { should == "\x03\x02\x00\x55" }
342
+ end
343
+
344
+ context "Object responds to :write" do
345
+ let(:value) { _B('01010101') }
346
+ let(:io) { writable_object }
347
+ its(:written_bytes) { should == "\x03\x02\x00\x55" }
348
+ end
349
+
350
+ context "raise IO error transparently" do
351
+ let(:value) { _B('01010101') }
352
+ let(:io) { io_error_object }
353
+ it { -> { subject }.should raise_error asn1error }
354
+ end
355
+ end
356
+
357
+ it 'returns self' do
358
+ obj = klass.new(_B('01010101'))
359
+ obj.encode_to(string_io_object).should == obj
360
+ end
361
+ end
362
+
363
+ describe 'extracted from ASN1.decode' do
364
+ subject { decoder.decode(der) }
365
+
366
+ context 'extracted value' do
367
+ context '01010101' do
368
+ let(:der) { "\x03\x02\x00\x55" }
369
+ its(:class) { should == klass }
370
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
371
+ its(:value) { should == _B('01010101') }
372
+ end
373
+
374
+ context '010101010' do
375
+ let(:der) { "\x03\x03\x00\x00\xAA" }
376
+ its(:class) { should == klass }
377
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
378
+ its(:value) { should == "\x00\xAA" }
379
+ end
380
+
381
+ context '(empty)' do
382
+ let(:der) { "\x03\x01\x00" }
383
+ its(:class) { should == klass }
384
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
385
+ its(:value) { should == '' }
386
+ end
387
+
388
+ context '999 octets' do
389
+ let(:der) { "\x03\x82\x03\xE8\x00" + "\xFF" * 999 }
390
+ its(:class) { should == klass }
391
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
392
+ its(:value) { should == "\xFF" * 999 }
393
+ end
394
+
395
+ context '1000 octets' do
396
+ let(:der) { "\x03\x82\x03\xE9\x00" + "\x00" * 1000 }
397
+ its(:class) { should == klass }
398
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
399
+ its(:value) { should == "\x00" * 1000 }
400
+ end
401
+
402
+ context '1001 octets' do
403
+ let(:der) { "\x03\x82\x03\xEA\x00" + "\xFF" * 1001 }
404
+ its(:class) { should == klass }
405
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
406
+ its(:value) { should == "\xFF" * 1001 }
407
+ end
408
+
409
+ it 'rejects incomplete value' do
410
+ asn1 = decoder.decode("\x03\x00")
411
+ -> { asn1.value }.should raise_error asn1error
412
+ end
413
+
414
+ context 'unused_bits is 0 for empty value' do
415
+ let(:der) { "\x03\x01\x00" }
416
+ its(:class) { should == klass }
417
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
418
+ its(:value) { should == '' }
419
+ its(:unused_bits) { should == 0 }
420
+ end
421
+
422
+ context 'unused_bits non-zero' do
423
+ let(:der) { "\x03\x02\x02\x01" }
424
+ its(:class) { should == klass }
425
+ its(:tag) { should == Krypt::ASN1::BIT_STRING }
426
+ its(:value) { should == "\x01" }
427
+ its(:unused_bits) { should == 2 }
428
+ end
429
+
430
+ context 'rejects unused_bits larger than 7' do
431
+ let(:der) { "\x03\x02\x08\x01" }
432
+ it { -> { subject.value }.should raise_error asn1error }
433
+ end
434
+ end
435
+
436
+ context 'extracted tag class' do
437
+ context 'UNIVERSAL' do
438
+ let(:der) { "\x03\x03\x00\x00\xAA" }
439
+ its(:tag_class) { should == :UNIVERSAL }
440
+ end
441
+
442
+ context 'APPLICATION' do
443
+ let(:der) { "\x43\x03\x00\x00\xAA" }
444
+ its(:tag_class) { should == :APPLICATION }
445
+ end
446
+
447
+ context 'CONTEXT_SPECIFIC' do
448
+ let(:der) { "\x83\x03\x00\x00\xAA" }
449
+ its(:tag_class) { should == :CONTEXT_SPECIFIC }
450
+ end
451
+
452
+ context 'PRIVATE' do
453
+ let(:der) { "\xC3\x03\x00\x00\xAA" }
454
+ its(:tag_class) { should == :PRIVATE }
455
+ end
456
+
457
+ context "setting IMPLICIT will result in CONTEXT_SPECIFIC" do
458
+ let(:der) { "\x03\x03\x00\x00\xAA" }
459
+ it do
460
+ subject.tag_class = :IMPLICIT
461
+ subject.to_der.should == "\x83\x03\x00\x00\xAA"
462
+ end
463
+ end
464
+
465
+ context "setting EXPLICIT will reencode as CONTEXT_SPECIFIC" do
466
+ let(:der) { "\x03\x03\x00\x00\xAA" }
467
+ it do
468
+ subject.tag_class = :EXPLICIT
469
+ subject.tag = 0
470
+ subject.to_der.should == "\xA0\x05\x03\x03\x00\x00\xAA"
471
+ end
472
+ end
473
+ end
474
+ end
475
+ end