certificate-transparency 0.1.0 → 0.2.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: e4ea6b3e2c15000d7eaca39ef6b3661c0bab1865
4
- data.tar.gz: 093bb71663c96c018b54f6c8604ae69a3f439e38
3
+ metadata.gz: 39b2b99f63286dade01640df20a331e7c1281018
4
+ data.tar.gz: 98f432392e75038c15eaa17090e239501dc8011e
5
5
  SHA512:
6
- metadata.gz: 0d4c7c9b2788e6103c6a2382f2810ff3e2e4ae0b62251f1f53d9c739aa115897de996b53f269f92510e03b1a431df8f0c27c4c096f7fdba0a46c66e7a7b42805
7
- data.tar.gz: 105393ac84c748cf4cef092c2550653c00fc3d1ae99de0543c0ef77b7e4796bb230ea603f4429781d4f58a24226542364db4f345edba54e1cc232e6f289fab60
6
+ metadata.gz: 4b1d533fafad47199195eaf133fe8cdbcc7889897fd413336ed8d48bbbaf466bcc566f2238cd16c459df2a0f34020d356556b6b623ccbf9a05dded51cd67ab2f
7
+ data.tar.gz: d49e9c47f74b63f6c8b5bf1c063d34f730abd84dd7282beb8bb06ac28162f988bd01b46acb026f5d0aad2977964cd30954f79a99bb4b1b8b14ff75b58772b91e
@@ -0,0 +1,126 @@
1
+ # An RFC6962 MerkleTreeLeaf structure
2
+ #
3
+ # Use {.from_blob} if you have an encoded MTL you wish to decode, or
4
+ # else create a new instance, pass in a `TimestampedEntry` object via
5
+ # `#timestamped_entry=`, and then call `#to_blob` to get the encoded MTL.
6
+ #
7
+ class CertificateTransparency::MerkleTreeLeaf
8
+ attr_reader :timestamped_entry
9
+
10
+ # Return a new MerkleTreeLeaf instance, from a binary blob of data.
11
+ # Raises an ArgumentError if the blob is invalid in some way.
12
+ #
13
+ # @param blob [String]
14
+ #
15
+ # @return [CertificateTransparency::MerkleTreeLeaf]
16
+ #
17
+ def self.from_blob(blob)
18
+ new.tap do |mtl|
19
+ mtl.version, leaf_type, te = blob.unpack("CCa*")
20
+ unless leaf_type == ::CertificateTransparency::MerkleLeafType[:timestamped_entry]
21
+ raise ArgumentError,
22
+ "Unknown leaf type in blob"
23
+ end
24
+
25
+ mtl.timestamped_entry =
26
+ ::CertificateTransparency::TimestampedEntry.from_blob(te)
27
+ end
28
+ end
29
+
30
+ # Instantiate a new MerkleTreeLeaf.
31
+ #
32
+ def initialize
33
+ @version = ::CertificateTransparency::Version[:v1]
34
+ @leaf_type = ::CertificateTransparency::MerkleLeafType[:timestamped_entry]
35
+ end
36
+
37
+ # Set the version of the MerkleTreeLeaf structure to create. At present,
38
+ # only `:v1` is supported, so there isn't much point in ever calling this
39
+ # method.
40
+ #
41
+ # @param v [Symbol]
42
+ #
43
+ # @return void
44
+ #
45
+ def version=(v)
46
+ @version = case v
47
+ when Symbol
48
+ ::CertificateTransparency::Version[v]
49
+ when Integer
50
+ v
51
+ else
52
+ nil
53
+ end
54
+
55
+ if @version.nil? or !::CertificateTransparency::Version.values.include?(@version)
56
+ raise ArgumentError,
57
+ "Invalid version #{v.inspect}"
58
+ end
59
+ end
60
+
61
+ # Return a symbol indicating the version of the MerkleTreeLeaf structure
62
+ # represented by this object. At present, only `:v1` is supported.
63
+ #
64
+ # @return Symbol
65
+ #
66
+ def version
67
+ ::CertificateTransparency::Version.invert[@version]
68
+ end
69
+
70
+ # Set the leaf type of the MerkleTreeLeaf structure. At present, only
71
+ # `:timestamped_entry` is supported, so there isn't much point in ever
72
+ # calling this method.
73
+ #
74
+ # @param lt [Symbol]
75
+ #
76
+ # @return void
77
+ #
78
+ def leaf_type=(lt)
79
+ @leaf_type = ::CertificateTransparency::MerkleLeafType[lt]
80
+
81
+ if @leaf_type.nil?
82
+ raise ArgumentError,
83
+ "Invalid leaf_type #{lt.inspect}"
84
+ end
85
+ end
86
+
87
+ # Return a symbol indicating the leaf type of the MerkleTreeLeaf
88
+ # structure represented by this object. At present, only
89
+ # `:timestamped_entry` is supported.
90
+ #
91
+ # @return Symbol
92
+ #
93
+ def leaf_type
94
+ ::CertificateTransparency::MerkleLeafType.invert[@leaf_type]
95
+ end
96
+
97
+ # Set the TimestampedEntry element for this MerkleTreeLeaf. It must be
98
+ # an instance of CertificateTransparency::TimestampedEntry, or an
99
+ # ArgumentError will be raised.
100
+ #
101
+ # @param te [CertificateTransparency::TimestampedEntry]
102
+ #
103
+ # @return void
104
+ #
105
+ def timestamped_entry=(te)
106
+ unless te.is_a? ::CertificateTransparency::TimestampedEntry
107
+ raise ArgumentError,
108
+ "Wasn't passed a TimestampedEntry (got a #{te.class})"
109
+ end
110
+
111
+ @timestamped_entry = te
112
+ end
113
+
114
+ # Generate a binary blob representing this MerkleTreeLeaf structure.
115
+ #
116
+ # @return [String]
117
+ #
118
+ def to_blob
119
+ if @timestamped_entry.nil?
120
+ raise RuntimeError,
121
+ "timestamped_entry has not been set"
122
+ end
123
+
124
+ [@version, @leaf_type, @timestamped_entry.to_blob].pack("CCa*")
125
+ end
126
+ end
@@ -0,0 +1,166 @@
1
+ # An RFC6962 TimestampedEntry structure
2
+ #
3
+ # Use {.from_blob} if you have an encoded TE you wish to decode, or create a
4
+ # new instance, set the various parameters, and use `#to_blob` to give you
5
+ # an encoded structure you can put over the wire. The various elements of
6
+ # the TE struct are available via accessors.
7
+ #
8
+ class CertificateTransparency::TimestampedEntry
9
+ # An instance of Time representing the timestamp of this entry
10
+ #
11
+ # @return [Time]
12
+ #
13
+ attr_reader :timestamp
14
+
15
+ # The type of entry we've got here. Is a symbol, either
16
+ # :x509_entry or :precert_entry.
17
+ #
18
+ # @return [Symbol]
19
+ #
20
+ attr_reader :entry_type
21
+
22
+ # An OpenSSL::X509::Certificate instance, if `entry_type == :x509_entry`,
23
+ # or nil otherwise.
24
+ #
25
+ # @return [OpenSSL::X509::Certificate]
26
+ #
27
+ attr_reader :x509_entry
28
+
29
+ # An instance of ::CertificateTransparency::PreCert if `entry_type ==
30
+ # :precert_entry`, or nil otherwise.
31
+ #
32
+ # @return [CertificateTransparency::PreCert]
33
+ #
34
+ attr_reader :precert_entry
35
+
36
+ # Create a new {CT::TimestampedEntry} by decoding a binary blob.
37
+ #
38
+ # @param blob [String]
39
+ #
40
+ # @return [CertificateTransparency::TimestampedEntry]
41
+ #
42
+ # @raise [ArgumentError] if we can't understand how to decode the
43
+ # provided blob.
44
+ #
45
+ def self.from_blob(blob)
46
+ ts, entry_type, rest = blob.unpack("Q>na*")
47
+
48
+ new.tap do |te|
49
+ te.timestamp = Time.ms(ts)
50
+
51
+ case CertificateTransparency::LogEntryType.invert[entry_type]
52
+ when :x509_entry
53
+ cert_data, rest = TLS::Opaque.from_blob(rest, 2**24-1)
54
+ te.x509_entry = OpenSSL::X509::Certificate.new(cert_data.value)
55
+ when :precert_entry
56
+ # Holy fuck, can I have ASN1 back, please? I can't just pass
57
+ # the PreCert part of the blob into CT::PreCert.new, because I
58
+ # can't parse the PreCert part out of the blob without digging
59
+ # *into* the PreCert part, because the only information on how
60
+ # long TBSCertificate is is contained *IN THE PRECERT!*
61
+ #
62
+ # I'm surprised there aren't a lot more bugs in TLS
63
+ # implementations, if this is how they lay out their data
64
+ # structures.
65
+ ikh, tbsc_len_hi, tbsc_len_lo, rest = rest.unpack("a32nCa*")
66
+ tbsc_len = tbsc_len_hi * 256 + tbsc_len_lo
67
+ tbsc, rest = rest.unpack("a#{tbsc_len}a*")
68
+ te.precert_entry = ::CertificateTransparency::PreCert.new do |ctpc|
69
+ ctpc.issuer_key_hash = ikh
70
+ ctpc.tbs_certificate = tbsc
71
+ end
72
+ else
73
+ raise ArgumentError,
74
+ "Unknown LogEntryType: #{entry_type} (corrupt TimestampedEntry?)"
75
+ end
76
+
77
+ exts, rest = TLS::Opaque.from_blob(rest, 2**16-1)
78
+ unless exts.value == ""
79
+ raise ArgumentError,
80
+ "Non-empty extensions found (#{exts.value.inspect})"
81
+ end
82
+
83
+ unless rest == ""
84
+ raise ArgumentError,
85
+ "Corrupted blob (garbage data after extensions)"
86
+ end
87
+ end
88
+ end
89
+
90
+ # Gives you whichever of `#x509_entry` or `#precert_entry` is
91
+ # not nil, or `nil` if both of them are `nil`.
92
+ #
93
+ # @return [OpenSSL::X509::Certificate, CertificateTransparency::PreCert]
94
+ #
95
+ def signed_entry
96
+ @x509_entry or @precert_entry
97
+ end
98
+
99
+ # Set the timestamp for this entry
100
+ #
101
+ # Must be a Time object, or something that can be bludgeoned
102
+ # into a Time object.
103
+ #
104
+ # @param ts [Time]
105
+ #
106
+ # @return void
107
+ #
108
+ def timestamp=(ts)
109
+ unless ts.is_a? Time or ts.respond_to? :to_time
110
+ raise ArgumentError,
111
+ "Must pass me a Time or something that responds to :to_time"
112
+ end
113
+
114
+ @timestamp = ts.is_a?(Time) ? ts : ts.to_time
115
+ end
116
+
117
+ # Set the entry to be an x509_entry with the given certificate.
118
+ #
119
+ # @param xe [OpenSSL::X509::Certificate]
120
+ #
121
+ # @return void
122
+ #
123
+ def x509_entry=(xe)
124
+ @x509_entry = OpenSSL::X509::Certificate.new(xe.to_s)
125
+ @entry_type = :x509_entry
126
+ @precert_entry = nil
127
+ end
128
+
129
+ # Set the entry to be a precert_entry with the given precert data. You
130
+ # must pass in a CertificateTransparency::PreCert instance.
131
+ #
132
+ # @param pe [CertificateTransparency::PreCert]
133
+ #
134
+ # @return void
135
+ #
136
+ def precert_entry=(pe)
137
+ unless pe.is_a? ::CertificateTransparency::PreCert
138
+ raise ArgumentError,
139
+ "I only accept PreCert instances (you gave me a #{pe.class})"
140
+ end
141
+
142
+ @precert_entry = pe
143
+ @entry_type = :precert_entry
144
+ @x509_entry = nil
145
+ end
146
+
147
+ # Encode this {TimestampedEntry} into a binary blob.
148
+ #
149
+ # @return [String]
150
+ #
151
+ def to_blob
152
+ signed_entry = if @x509_entry
153
+ TLS::Opaque.new(@x509_entry.to_der, 2**24-1).to_blob
154
+ elsif @precert_entry
155
+ @precert_entry.to_blob
156
+ else
157
+ raise RuntimeError,
158
+ "You must call #precert_entry= or #x509_entry= before calling #to_blob"
159
+ end
160
+
161
+ [@timestamp.ms,
162
+ CertificateTransparency::LogEntryType[entry_type],
163
+ signed_entry, 0
164
+ ].pack("Q>na*n")
165
+ end
166
+ end
@@ -28,7 +28,9 @@ unless Kernel.const_defined?(:CT)
28
28
  CT = CertificateTransparency
29
29
  end
30
30
 
31
- require 'certificate-transparency/extensions/string'
32
- require 'certificate-transparency/extensions/time'
31
+ require_relative 'certificate-transparency/extensions/string'
32
+ require_relative 'certificate-transparency/extensions/time'
33
33
 
34
- require 'certificate-transparency/signed_tree_head'
34
+ require_relative 'certificate-transparency/merkle_tree_leaf'
35
+ require_relative 'certificate-transparency/signed_tree_head'
36
+ require_relative 'certificate-transparency/timestamped_entry'
@@ -62,9 +62,9 @@ class TLS::DigitallySigned
62
62
 
63
63
  # Set the key for this instance.
64
64
  #
65
- # @param k [OpenSSL::PKey::EC] a key to verify or generate the signature.
65
+ # @param k [OpenSSL::PKey::EC] a key to verify or generate the signature.
66
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.
67
+ # instance via {.from_blob}, then this key can be a public key.
68
68
  # Otherwise, if you want to generate a new signature, you must pass in
69
69
  # a private key.
70
70
  #
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: certificate-transparency
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0
4
+ version: 0.2.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Matt Palmer
@@ -159,7 +159,9 @@ files:
159
159
  - lib/certificate-transparency.rb
160
160
  - lib/certificate-transparency/extensions/string.rb
161
161
  - lib/certificate-transparency/extensions/time.rb
162
+ - lib/certificate-transparency/merkle_tree_leaf.rb
162
163
  - lib/certificate-transparency/signed_tree_head.rb
164
+ - lib/certificate-transparency/timestamped_entry.rb
163
165
  - lib/tls.rb
164
166
  - lib/tls/digitally_signed.rb
165
167
  - lib/tls/opaque.rb