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:
|
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
|