certificate-transparency 0.1.0 → 0.2.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:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 39b2b99f63286dade01640df20a331e7c1281018
|
4
|
+
data.tar.gz: 98f432392e75038c15eaa17090e239501dc8011e
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
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
|
-
|
32
|
-
|
31
|
+
require_relative 'certificate-transparency/extensions/string'
|
32
|
+
require_relative 'certificate-transparency/extensions/time'
|
33
33
|
|
34
|
-
|
34
|
+
require_relative 'certificate-transparency/merkle_tree_leaf'
|
35
|
+
require_relative 'certificate-transparency/signed_tree_head'
|
36
|
+
require_relative 'certificate-transparency/timestamped_entry'
|
data/lib/tls/digitally_signed.rb
CHANGED
@@ -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
|
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
|