certificate-transparency-client 0.1.0 → 0.1.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6637c2bc059e112bafd047ae7eb04a9eed37d45f
4
- data.tar.gz: 7e5a6922d143c6aa0310d49b4b2d5ffd7f0e1f53
3
+ metadata.gz: d688032d7f32de9399ffac2339ae54897650bfd5
4
+ data.tar.gz: a2127485b2aa9657dc4534ed4c147de6ca25d565
5
5
  SHA512:
6
- metadata.gz: 7e60766f6cc268b8695f8be8dd2dde76403d7391e66ab33f8943d4e79f4c4a7d99bcdab45c6c18ad7795b8ac10cee7f32c54aae2457908383a23b207118b42e1
7
- data.tar.gz: 684eb427b1268cfb82f3858157bf1304477450a617af980be4a9762d3802a9519ad9943435f75f08deeb18973d23eb5d560590db8656644033ef473349873425
6
+ metadata.gz: 353cbf08a26d3d9e9a9adaaf355fa1c33bf7192fafb45fb1e6cc2696fb3453d5094a1fa01e61f58071e4874c513e260ef02cf4bb9e3c63fc1e912922ebb9d678
7
+ data.tar.gz: b12f6515e8aa005bd3199c814544da926f3a4f2b669e11d9784f19ace14d02ed065366ffba81186c5077ecccd8a0bd44ff84c61a7d9caaa80557341b0f7b96dc
@@ -22,6 +22,8 @@ Gem::Specification.new do |s|
22
22
 
23
23
  s.required_ruby_version = ">= 1.9.3"
24
24
 
25
+ s.add_runtime_dependency 'certificate-transparency', '~> 0.0'
26
+
25
27
  s.add_development_dependency 'bundler'
26
28
  s.add_development_dependency 'github-release'
27
29
  s.add_development_dependency 'guard-spork'
@@ -1,2 +1 @@
1
- require 'certificate-transparency'
2
1
  require 'certificate-transparency/client'
@@ -1,3 +1,4 @@
1
+ require 'certificate-transparency'
1
2
  require 'openssl'
2
3
 
3
4
  # Interact with a Certificate Transparency server.
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: certificate-transparency-client
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.1.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-05-29 00:00:00.000000000 Z
11
+ date: 2015-05-30 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: certificate-transparency
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '0.0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '0.0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: bundler
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -156,15 +170,7 @@ files:
156
170
  - certificate-transparency-client.gemspec
157
171
  - lib/.gitkeep
158
172
  - lib/certificate-transparency-client.rb
159
- - lib/certificate-transparency.rb
160
173
  - lib/certificate-transparency/client.rb
161
- - lib/certificate-transparency/constants.rb
162
- - lib/certificate-transparency/extensions/string.rb
163
- - lib/certificate-transparency/extensions/time.rb
164
- - lib/certificate-transparency/signed_tree_head.rb
165
- - lib/tls.rb
166
- - lib/tls/digitally_signed.rb
167
- - lib/tls/opaque.rb
168
174
  homepage: http://theshed.hezmatt.org/certificate-transparency-client
169
175
  licenses: []
170
176
  metadata: {}
@@ -1,14 +0,0 @@
1
- # The base module of everything related to Certificate Transparency.
2
- module CertificateTransparency; end
3
-
4
- unless Kernel.const_defined?(:CT)
5
- #:nodoc:
6
- CT = CertificateTransparency
7
- end
8
-
9
- require 'certificate-transparency/extensions/string'
10
- require 'certificate-transparency/extensions/time'
11
-
12
- require 'certificate-transparency/constants'
13
-
14
- require 'certificate-transparency/signed_tree_head'
@@ -1,23 +0,0 @@
1
- module CertificateTransparency
2
- # RFC6962 s3.1
3
- LogEntryType = {
4
- :x509_entry => 0,
5
- :precert_entry => 1
6
- }
7
-
8
- # RFC6962 s3.4
9
- MerkleLeafType = {
10
- :timestamped_entry => 0
11
- }
12
-
13
- # RFC6962 s3.2
14
- SignatureType = {
15
- :certificate_timestamp => 0,
16
- :tree_hash => 1
17
- }
18
-
19
- # RFC6962 s3.2
20
- Version = {
21
- :v1 => 0
22
- }
23
- end
@@ -1,15 +0,0 @@
1
- # Extensions to the String class.
2
- #
3
- class String
4
- # Return a new string, which is simply the object base64 encoded.
5
- #
6
- def base64
7
- [self.to_s].pack("m0")
8
- end
9
-
10
- # Return a new string, which is simply the object base64 decoded.
11
- #
12
- def unbase64
13
- self.to_s.unpack("m").first
14
- end
15
- end
@@ -1,17 +0,0 @@
1
- # Extensions to the Time class.
2
- #
3
- class Time
4
- # Return the time represented by this object, in milliseconds since the
5
- # epoch.
6
- #
7
- def ms
8
- (self.to_f * 1000).to_i
9
- end
10
-
11
- # Create a new instance of Time, set to the given number of milliseconds
12
- # since the epoch.
13
- #
14
- def self.ms(i)
15
- Time.at(i.to_f / 1000)
16
- end
17
- end
@@ -1,51 +0,0 @@
1
- require 'json'
2
- require 'tls'
3
-
4
- # A CT SignedTreeHead (RFC6962 s3.5, s4.3).
5
- #
6
- class CertificateTransparency::SignedTreeHead
7
- attr_accessor :tree_size
8
- attr_accessor :timestamp
9
- attr_accessor :root_hash
10
- attr_accessor :signature
11
-
12
- # Create a new SignedTreeHead instance from the JSON returned
13
- # by `/ct/v1/get-sth`.
14
- #
15
- def self.from_json(json)
16
- doc = JSON.parse(json)
17
-
18
- self.new.tap do |sth|
19
- sth.tree_size = doc['tree_size']
20
- sth.timestamp = Time.at(doc['timestamp'].to_f / 1000)
21
- sth.root_hash = doc['sha256_root_hash'].unpack("m").first
22
- sth.signature = doc['tree_head_signature'].unpack("m").first
23
- end
24
- end
25
-
26
- # Determine whether or not the signature that was provided in the
27
- # signed tree head is a valid one, based on the provided key.
28
- #
29
- # @param pk [String] the raw binary form of the public key of the
30
- # log.
31
- #
32
- # @return Boolean
33
- #
34
- def valid?(pk)
35
- key = OpenSSL::PKey::EC.new(pk)
36
-
37
- blob = [
38
- CT::Version[:v1],
39
- CT::SignatureType[:tree_hash],
40
- timestamp.ms,
41
- tree_size,
42
- root_hash
43
- ].pack("ccQ>Q>a32")
44
-
45
- ds = TLS::DigitallySigned.from_blob(signature)
46
- ds.content = blob
47
- ds.key = key
48
-
49
- ds.valid?
50
- end
51
- end
data/lib/tls.rb DELETED
@@ -1,24 +0,0 @@
1
- # Constants and types required by CertificateTransparency, which come from
2
- # the core TLS specs.
3
- #
4
- module TLS
5
- # RFC5246 s7.4.1.4.1 (I shit you not, five levels of headings)
6
- HashAlgorithm = { :none => 0,
7
- :md5 => 1,
8
- :sha1 => 2,
9
- :sha224 => 3,
10
- :sha256 => 4,
11
- :sha384 => 5,
12
- :sha512 => 6
13
- }
14
-
15
- # RFC5246 s7.4.1.4.1
16
- SignatureAlgorithm = { :anonymous => 0,
17
- :rsa => 1,
18
- :dsa => 2,
19
- :ecdsa => 3
20
- }
21
- end
22
-
23
- require 'tls/digitally_signed'
24
- require 'tls/opaque'
@@ -1,129 +0,0 @@
1
- require 'openssl'
2
-
3
- unless OpenSSL::PKey::EC.instance_methods.include?(:private?)
4
- OpenSSL::PKey::EC.class_eval("alias_method :private?, :private_key?")
5
- end
6
-
7
- # Create a `DigitallySigned` struct, as defined by RFC5246 s4.7, and adapted
8
- # for the CertificateTransparency system (that is, ECDSA using the NIST
9
- # P-256 curve is the only signature algorithm supported, and SHA-256 is the
10
- # only hash algorithm supported).
11
- #
12
- class TLS::DigitallySigned
13
- # Create a new `DigitallySigned` struct.
14
- #
15
- # Takes a number of named options:
16
- #
17
- # * `:key` -- (required) An instance of `OpenSSL::PKey::EC`. If you pass
18
- # in `:blob` as well, then this can be either a public key or a private
19
- # key (because you only need a public key for validating a signature),
20
- # but if you only pass in `:content`, you must provide a private key
21
- # here.
22
- #
23
- # This key *must* be generated with the NIST P-256 curve (known to
24
- # OpenSSL as `prime256v1`) in order to be compliant with the CT spec.
25
- # However, we can't validate that, so it's up to you to make sure you
26
- # do it right.
27
- #
28
- # * `:content` -- (required) The content to sign, or verify the signature
29
- # of. This can be any string.
30
- #
31
- # * `:blob` -- An existing encoded `DigitallySigned` struct you'd like to
32
- # have decoded and verified against `:content` with `:key`.
33
- #
34
- # Raises an `ArgumentError` if you try to pass in anything that doesn't
35
- # meet the rather stringent requirements.
36
- #
37
- def self.from_blob(blob)
38
- hash_algorithm, signature_algorithm, sig_blob = blob.unpack("CCa*")
39
-
40
- if signature_algorithm != ::TLS::SignatureAlgorithm[:ecdsa]
41
- raise ArgumentError,
42
- "Signature specified in blob is not ECDSA"
43
- end
44
-
45
- if hash_algorithm != ::TLS::HashAlgorithm[:sha256]
46
- raise ArgumentError,
47
- "Hash algorithm specified in blob is not SHA256"
48
- end
49
-
50
- sig, rest = ::TLS::Opaque.from_blob(sig_blob, 2**16-1)
51
- signature = sig.value
52
-
53
- TLS::DigitallySigned.new.tap do |ds|
54
- ds.hash_algorithm = hash_algorithm
55
- ds.signature_algorithm = signature_algorithm
56
- ds.signature = signature
57
- end
58
- end
59
-
60
- attr_accessor :content, :hash_algorithm, :signature_algorithm, :signature
61
- attr_reader :key
62
-
63
- # Set the key for this instance.
64
- #
65
- # @param k [OpenSSL::PKey::EC] a key to verify or generate the signature.
66
- # If you only want to verify an existing signature (ie you created this
67
- # instance via {.from_blob}, then this key can be a public key.
68
- # Otherwise, if you want to generate a new signature, you must pass in
69
- # a private key.
70
- #
71
- # @return void
72
- #
73
- # @raise [ArgumentError] if you pass in a key that isn't of the
74
- # appropriate type.
75
- #
76
- def key=(k)
77
- unless k.is_a? OpenSSL::PKey::EC
78
- raise ArgumentError,
79
- "Key must be an instance of OpenSSL::PKey::EC"
80
- end
81
-
82
- @key = k
83
- end
84
-
85
- # Return a binary string which represents a `DigitallySigned` struct of
86
- # the content passed in.
87
- #
88
- def to_blob
89
- if @key.nil?
90
- raise RuntimeError,
91
- "No key has been supplied"
92
- end
93
- begin
94
- @signature ||= @key.sign(OpenSSL::Digest::SHA256.new, @content)
95
- rescue ArgumentError
96
- raise RuntimeError,
97
- "Must have a private key in order to make a signature"
98
- end
99
-
100
- [
101
- @hash_algorithm,
102
- @signature_algorithm,
103
- @signature.length,
104
- @signature
105
- ].pack("CCna*").force_encoding("BINARY")
106
- end
107
-
108
- # Verify whether or not the `signature` struct given is a valid signature
109
- # for the key/content/blob combination provided to the constructor.
110
- #
111
- def valid?
112
- if @key.nil?
113
- raise RuntimeError,
114
- "No key has been specified"
115
- end
116
-
117
- if @signature.nil?
118
- raise RuntimeError,
119
- "No signature is available yet"
120
- end
121
-
122
- if @content.nil?
123
- raise RuntimeError,
124
- "No content has been specified yet"
125
- end
126
-
127
- @key.verify(OpenSSL::Digest::SHA256.new, @signature, @content)
128
- end
129
- end
data/lib/tls/opaque.rb DELETED
@@ -1,106 +0,0 @@
1
- # An implementation of the TLS 1.2 (RFC5246) "variable length" opaque type.
2
- #
3
- # You can create an instance of this type by passing in a stringish to be
4
- # encoded, and a "maximum length", like this:
5
- #
6
- # TLS::Opaque.new("Hello World", 2**16-1)
7
- #
8
- # If you have a TLS::Opaque-encoded blob, and you'd like to get the
9
- # content out of it, you can use `.from_blob` to create a TLS::Opaque object
10
- # that will contain the data you seek:
11
- #
12
- # TLS::Opaque.from_blob("\x00\x0BHello World", 2**16-1)
13
- #
14
- # In both cases, you need to specify what the maximum length of the `value`
15
- # can be, because that is what determines how many bytes the length field
16
- # takes up at the beginning of the string.
17
- #
18
- # To get the "encoded" form,, call `#to_blob`:
19
- #
20
- # TLS::Opaque.new("Hello World", 255).to_blob
21
- # => "\x0BHello World"
22
- #
23
- # Or, to get the string itself out, call `#value`:
24
- #
25
- # TLS::Opaque.from_blob("\x0BHello World", 255)[0].value
26
- # => "Hello World"
27
- #
28
- # Passing in a value or blob which is longer than the maximum length
29
- # specified will result in `ArgumentError` being thrown.
30
- #
31
- class TLS::Opaque
32
- attr_reader :value
33
-
34
- # Parse out an opaque string from a blob, as well as returning
35
- # any remaining data. The `maxlen` parameter is required to
36
- # know how many octets at the beginning of the string to read to
37
- # determine the length of the opaque string.
38
- #
39
- # Returns a two-element array, `[TLS::Opaque, String]`, being a
40
- # `TLS::Opaque` instance retrieved from the blob provided, and a `String`
41
- # containing any remainder of the blob that wasn't considered part of the
42
- # `TLS::Opaque`. This second element will *always* be a string, but it
43
- # may be an empty string, if the `TLS::Opaque` instance was the entire
44
- # blob.
45
- #
46
- # This method will raise `ArgumentError` if the length encoded at the
47
- # beginning of `blob` is longer than the data in `blob`, or if it is
48
- # larger than `maxlen`.
49
- #
50
- def self.from_blob(blob, maxlen)
51
- len_bytes = lenlen(maxlen)
52
-
53
- len = blob[0..len_bytes-1].split('').inject(0) do |total, c|
54
- total * 256 + c.ord
55
- end
56
-
57
- if len > maxlen
58
- raise ArgumentError,
59
- "Encoded length (#{len}) is greater than maxlen (#{maxlen})"
60
- end
61
-
62
- if len > blob[len_bytes..-1].length
63
- raise ArgumentError,
64
- "Encoded length (#{len}) is greater than the number of bytes available"
65
- end
66
-
67
- [TLS::Opaque.new(blob[len_bytes..(len_bytes+len-1)], maxlen),
68
- blob[(len_bytes+len)..-1]
69
- ]
70
- end
71
-
72
- def initialize(str, maxlen)
73
- unless maxlen.is_a? Integer
74
- raise ArgumentError,
75
- "maxlen must be an Integer"
76
- end
77
-
78
- if str.length > maxlen
79
- raise ArgumentError,
80
- "value given is longer than maxlen (#{maxlen})"
81
- end
82
-
83
- @maxlen = maxlen
84
- @value = str
85
- end
86
-
87
- # Return an encoded Opaque.
88
- #
89
- def to_blob
90
- len = value.length
91
- params = []
92
- self.class.lenlen(@maxlen).times do
93
- params.unshift(len % 256)
94
- len /= 256
95
- end
96
-
97
- params << value
98
-
99
- params.pack("C#{self.class.lenlen(@maxlen)}a*")
100
- end
101
-
102
- private
103
- def self.lenlen(len)
104
- (Math.log2(len).ceil / 8.0).ceil
105
- end
106
- end