certificate-transparency-client 0.1.0 → 0.1.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.
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