krypt 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- 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
data/lib/krypt/hmac.rb
ADDED
@@ -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
|
data/lib/krypt/pkcs5.rb
ADDED
@@ -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
|
data/lib/krypt/x509.rb
ADDED
@@ -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
|