certificate-transparency-client 0.1.0
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 +7 -0
- data/.gitignore +4 -0
- data/.yardopts +1 -0
- data/LICENCE +674 -0
- data/README.md +79 -0
- data/certificate-transparency-client.gemspec +35 -0
- data/lib/.gitkeep +0 -0
- data/lib/certificate-transparency/client.rb +27 -0
- data/lib/certificate-transparency/constants.rb +23 -0
- data/lib/certificate-transparency/extensions/string.rb +15 -0
- data/lib/certificate-transparency/extensions/time.rb +17 -0
- data/lib/certificate-transparency/signed_tree_head.rb +51 -0
- data/lib/certificate-transparency-client.rb +2 -0
- data/lib/certificate-transparency.rb +14 -0
- data/lib/tls/digitally_signed.rb +129 -0
- data/lib/tls/opaque.rb +106 -0
- data/lib/tls.rb +24 -0
- metadata +192 -0
data/README.md
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
This is a Ruby client library for interacting with
|
2
|
+
[RFC6962](http://tools.ietf.org/html/rfc6962) [Certificate
|
3
|
+
Transparency](http://www.certificate-transparency.org/) servers. It
|
4
|
+
aims to provide a complete interface for retrieving and validating tree
|
5
|
+
heads, entries, SCTs, as well as submitting certificates and precerts to a
|
6
|
+
log.
|
7
|
+
|
8
|
+
At present, it is not feature complete, however what is released is well
|
9
|
+
tested, heavily documented, and should be ready for production use.
|
10
|
+
|
11
|
+
|
12
|
+
# Installation
|
13
|
+
|
14
|
+
It's a gem:
|
15
|
+
|
16
|
+
gem install certificate-transparency-client
|
17
|
+
|
18
|
+
There's also the wonders of [the Gemfile](http://bundler.io):
|
19
|
+
|
20
|
+
gem 'certificate-transparency-client'
|
21
|
+
|
22
|
+
If you're the sturdy type that likes to run from git:
|
23
|
+
|
24
|
+
rake build; gem install pkg/certificate-transparency-client-<whatever>.gem
|
25
|
+
|
26
|
+
Or, if you've eschewed the convenience of Rubygems entirely, then you
|
27
|
+
presumably know what to do already.
|
28
|
+
|
29
|
+
|
30
|
+
# Usage
|
31
|
+
|
32
|
+
To get started, instantiate a new instance of {CT::Client}:
|
33
|
+
|
34
|
+
require 'certificate-transparency-client'
|
35
|
+
|
36
|
+
ct = CT::Client.new "https://ct.example.org"
|
37
|
+
|
38
|
+
The URL provided should be the "base" URL for the log; that is, everything
|
39
|
+
immediately preceding the `/ct/v1/<blah>` parts of the URL when making a
|
40
|
+
complete request.
|
41
|
+
|
42
|
+
If you only provide a URL, you can retrieve things and submit entries, but
|
43
|
+
if you provide a public key, {CT::Client} will also validate signatures for
|
44
|
+
you:
|
45
|
+
|
46
|
+
ct = CT::Client.new "https://ct.example.org",
|
47
|
+
:public_key => "<native or base64 key>"
|
48
|
+
|
49
|
+
To discover what you can do with an instance of {CT::Client}, see the API
|
50
|
+
docs for the {CT::Client} class.
|
51
|
+
|
52
|
+
|
53
|
+
# Contributing
|
54
|
+
|
55
|
+
Bug reports should be sent to the [Github issue
|
56
|
+
tracker](https://github.com/mpalmer/certificate-transparency-client/issues),
|
57
|
+
or [e-mailed](mailto:theshed+certificate-transparency-client@hezmatt.org).
|
58
|
+
Patches can be sent as a Github pull request, or
|
59
|
+
[e-mailed](mailto:theshed+certificate-transparency-client@hezmatt.org).
|
60
|
+
|
61
|
+
|
62
|
+
# Licence
|
63
|
+
|
64
|
+
Unless otherwise stated, everything in this repo is covered by the following
|
65
|
+
copyright notice:
|
66
|
+
|
67
|
+
Copyright (C) 2014,2015 Matt Palmer <matt@hezmatt.org>
|
68
|
+
|
69
|
+
This program is free software: you can redistribute it and/or modify it
|
70
|
+
under the terms of the GNU General Public License version 3, as
|
71
|
+
published by the Free Software Foundation.
|
72
|
+
|
73
|
+
This program is distributed in the hope that it will be useful,
|
74
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
75
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
76
|
+
GNU General Public License for more details.
|
77
|
+
|
78
|
+
You should have received a copy of the GNU General Public License
|
79
|
+
along with this program. If not, see <http://www.gnu.org/licenses/>.
|
@@ -0,0 +1,35 @@
|
|
1
|
+
begin
|
2
|
+
require 'git-version-bump'
|
3
|
+
rescue LoadError
|
4
|
+
nil
|
5
|
+
end
|
6
|
+
|
7
|
+
Gem::Specification.new do |s|
|
8
|
+
s.name = "certificate-transparency-client"
|
9
|
+
|
10
|
+
s.version = GVB.version rescue "0.0.0.1.NOGVB"
|
11
|
+
s.date = GVB.date rescue Time.now.strftime("%Y-%m-%d")
|
12
|
+
|
13
|
+
s.platform = Gem::Platform::RUBY
|
14
|
+
|
15
|
+
s.summary = "A client for RFC6962 Certificate Transparency log servers"
|
16
|
+
|
17
|
+
s.authors = ["Matt Palmer"]
|
18
|
+
s.email = ["theshed+certificate-transparency-client@hezmatt.org"]
|
19
|
+
s.homepage = "http://theshed.hezmatt.org/certificate-transparency-client"
|
20
|
+
|
21
|
+
s.files = `git ls-files -z`.split("\0").reject { |f| f =~ /^(G|spec|Rakefile)/ }
|
22
|
+
|
23
|
+
s.required_ruby_version = ">= 1.9.3"
|
24
|
+
|
25
|
+
s.add_development_dependency 'bundler'
|
26
|
+
s.add_development_dependency 'github-release'
|
27
|
+
s.add_development_dependency 'guard-spork'
|
28
|
+
s.add_development_dependency 'guard-rspec'
|
29
|
+
s.add_development_dependency 'rake', '~> 10.4', '>= 10.4.2'
|
30
|
+
# Needed for guard
|
31
|
+
s.add_development_dependency 'rb-inotify', '~> 0.9'
|
32
|
+
s.add_development_dependency 'redcarpet'
|
33
|
+
s.add_development_dependency 'rspec'
|
34
|
+
s.add_development_dependency 'yard'
|
35
|
+
end
|
data/lib/.gitkeep
ADDED
File without changes
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
# Interact with a Certificate Transparency server.
|
4
|
+
#
|
5
|
+
class CertificateTransparency::Client
|
6
|
+
def initialize(url, opts = {})
|
7
|
+
unless opts.is_a? Hash
|
8
|
+
raise ArgumentError,
|
9
|
+
"Must pass a hash of options as second argument"
|
10
|
+
end
|
11
|
+
|
12
|
+
if opts[:public_key]
|
13
|
+
begin
|
14
|
+
@pubkey = if opts[:public_key].valid_encoding? && opts[:public_key] =~ /^[A-Za-z0-9+\/]+=*$/
|
15
|
+
OpenSSL::PKey::EC.new(opts[:public_key].unpack("m").first)
|
16
|
+
else
|
17
|
+
OpenSSL::PKey::EC.new(opts[:public_key])
|
18
|
+
end
|
19
|
+
rescue OpenSSL::PKey::ECError
|
20
|
+
raise ArgumentError,
|
21
|
+
"Invalid public key"
|
22
|
+
end
|
23
|
+
end
|
24
|
+
|
25
|
+
@url = URI(url)
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,23 @@
|
|
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
|
@@ -0,0 +1,15 @@
|
|
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
|
@@ -0,0 +1,17 @@
|
|
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
|
@@ -0,0 +1,51 @@
|
|
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
|
@@ -0,0 +1,14 @@
|
|
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'
|
@@ -0,0 +1,129 @@
|
|
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
ADDED
@@ -0,0 +1,106 @@
|
|
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
|
data/lib/tls.rb
ADDED
@@ -0,0 +1,24 @@
|
|
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'
|