codtls 0.0.1.alpha
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/.rubocop.yml +12 -0
- data/.yardopts +4 -0
- data/Gemfile +12 -0
- data/LICENSE +21 -0
- data/README.md +78 -0
- data/Rakefile +29 -0
- data/lib/codtls.rb +186 -0
- data/lib/codtls/abstract_session.rb +179 -0
- data/lib/codtls/alert.rb +64 -0
- data/lib/codtls/decrypt.rb +72 -0
- data/lib/codtls/ecc.rb +26 -0
- data/lib/codtls/encrypt.rb +29 -0
- data/lib/codtls/h_changecipherspec.rb +25 -0
- data/lib/codtls/h_chello.rb +79 -0
- data/lib/codtls/h_content.rb +57 -0
- data/lib/codtls/h_finished.rb +30 -0
- data/lib/codtls/h_keyexchange.rb +131 -0
- data/lib/codtls/h_shello.rb +51 -0
- data/lib/codtls/h_shellodone.rb +22 -0
- data/lib/codtls/h_type.rb +22 -0
- data/lib/codtls/h_verify.rb +30 -0
- data/lib/codtls/handshake.rb +173 -0
- data/lib/codtls/models/codtls_connection.rb +3 -0
- data/lib/codtls/models/codtls_device.rb +3 -0
- data/lib/codtls/prf.rb +40 -0
- data/lib/codtls/pskdb.rb +104 -0
- data/lib/codtls/ram_session.rb +214 -0
- data/lib/codtls/rampskdb.rb +87 -0
- data/lib/codtls/record.rb +202 -0
- data/lib/codtls/session.rb +284 -0
- data/lib/codtls/version.rb +3 -0
- data/lib/generators/codtls/codtls_generator.rb +56 -0
- data/lib/generators/codtls/templates/create_codtls_connections.rb +15 -0
- data/lib/generators/codtls/templates/create_codtls_devices.rb +11 -0
- data/test/test_codtls.rb +75 -0
- data/test/test_ecc.rb +44 -0
- data/test/test_h_chello.rb +40 -0
- data/test/test_h_content.rb +59 -0
- data/test/test_h_keyexchange.rb +36 -0
- data/test/test_helper.rb +3 -0
- data/test/test_pskdb.rb +37 -0
- data/test/test_ram_session.rb +131 -0
- data/test/test_rampskdb.rb +26 -0
- data/test/test_record.rb +128 -0
- data/test/test_send_recv.rb +178 -0
- data/test/test_session.rb +164 -0
- metadata +303 -0
data/lib/codtls/alert.rb
ADDED
@@ -0,0 +1,64 @@
|
|
1
|
+
require 'codtls/h_type'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
# Tolle Klasse
|
5
|
+
class Alert < Handshake::Type
|
6
|
+
LEVEL = { warning: 1, fatal: 2 }
|
7
|
+
DESCRIPTION = { close_notify: 0,
|
8
|
+
unexpected_message: 10,
|
9
|
+
bad_record_mac: 20,
|
10
|
+
decryption_failed_reserved: 21,
|
11
|
+
record_overflow: 22,
|
12
|
+
decompression_failure: 30,
|
13
|
+
handshake_failure: 40,
|
14
|
+
no_certificate_reserved: 41,
|
15
|
+
bad_certificate: 42,
|
16
|
+
unsupported_certificate: 43,
|
17
|
+
certificate_revoked: 44,
|
18
|
+
certificate_expired: 45,
|
19
|
+
certificate_unknown: 46,
|
20
|
+
illegal_parameter: 47,
|
21
|
+
unknown_ca: 48,
|
22
|
+
access_denied: 49,
|
23
|
+
decode_error: 50,
|
24
|
+
decrypt_error: 51,
|
25
|
+
export_restriction_reserved: 60,
|
26
|
+
protocol_version: 70,
|
27
|
+
insufficient_security: 71,
|
28
|
+
internal_error: 80,
|
29
|
+
user_canceled: 90,
|
30
|
+
no_renegotiation: 100,
|
31
|
+
unsupported_extension: 110 }
|
32
|
+
|
33
|
+
attr_reader :level, :description
|
34
|
+
|
35
|
+
def self.parse(data)
|
36
|
+
data.force_encoding('ASCII-8BIT')
|
37
|
+
Alert.new(LEVEL.key(data[0].ord), DESCRIPTION.key(data[1].ord))
|
38
|
+
end
|
39
|
+
|
40
|
+
public
|
41
|
+
|
42
|
+
def initialize(level, description)
|
43
|
+
super(33)
|
44
|
+
self.level = level
|
45
|
+
self.description = description
|
46
|
+
end
|
47
|
+
|
48
|
+
def level=(level)
|
49
|
+
fail HandshakeError, 'Unknown Level' if LEVEL[level].nil?
|
50
|
+
@level = level
|
51
|
+
end
|
52
|
+
|
53
|
+
def description=(description)
|
54
|
+
fail HandshakeError, 'Unknown Level' if DESCRIPTION[description].nil?
|
55
|
+
@description = description
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_wire
|
59
|
+
w = ''.force_encoding('ASCII-8BIT')
|
60
|
+
w.concat(LEVEL[@level].chr)
|
61
|
+
w.concat(DESCRIPTION[@description].chr)
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,72 @@
|
|
1
|
+
require 'codtls/record'
|
2
|
+
require 'codtls/session'
|
3
|
+
require 'openssl/ccm'
|
4
|
+
require 'codtls/alert'
|
5
|
+
|
6
|
+
module CoDTLS
|
7
|
+
# TODO
|
8
|
+
module RecordLayer
|
9
|
+
# first dtls message will be removed from mesg, so u can call parse
|
10
|
+
# multiple times on a concatenation of many dtls records
|
11
|
+
def self.decrypt(packet, maxlen)
|
12
|
+
# packet = mesg, (address_family, port, hostname, numeric_address)
|
13
|
+
|
14
|
+
mesg, sender_inet_addr = packet
|
15
|
+
|
16
|
+
begin
|
17
|
+
record, data = Record.parse(mesg)
|
18
|
+
rescue RecordError
|
19
|
+
send_alert(sender_inet_addr, :fatal, :decode_error)
|
20
|
+
return ['', sender_inet_addr]
|
21
|
+
end
|
22
|
+
|
23
|
+
session = Session.new(sender_inet_addr[3])
|
24
|
+
unless session.check_seq(record.seq_num)
|
25
|
+
send_alert(sender_inet_addr, :fatal, :decode_error)
|
26
|
+
return ['', sender_inet_addr]
|
27
|
+
end
|
28
|
+
|
29
|
+
if record.epoch > 0
|
30
|
+
keyblock = session.key_block
|
31
|
+
if keyblock.empty?
|
32
|
+
send_alert(sender_inet_addr, :fatal, :decode_error)
|
33
|
+
return ['', sender_inet_addr]
|
34
|
+
end
|
35
|
+
|
36
|
+
ccm = OpenSSL::CCM.new('AES', keyblock[16...32], 8)
|
37
|
+
data = ccm.decrypt(data, record.nonce(keyblock[36...40]))
|
38
|
+
if data.empty?
|
39
|
+
send_alert(sender_inet_addr, :fatal, :bad_record_mac)
|
40
|
+
return ['', sender_inet_addr]
|
41
|
+
end
|
42
|
+
else
|
43
|
+
if session.epoch > 0
|
44
|
+
# When Epoch > 0 is known, message in epoch 0 isnt acceptable
|
45
|
+
send_alert(sender_inet_addr, :fatal, :unexpected_message)
|
46
|
+
return ['', sender_inet_addr]
|
47
|
+
end
|
48
|
+
|
49
|
+
# WARNING: !!! -> disabled for testing purpose
|
50
|
+
# if record.type == :appdata
|
51
|
+
# send_alert(sender_inet_addr, :fatal, :unexpected_message)
|
52
|
+
# return ['', sender_inet_addr]
|
53
|
+
# end
|
54
|
+
end
|
55
|
+
|
56
|
+
if record.type == :alert
|
57
|
+
session.clear
|
58
|
+
return ['', sender_inet_addr]
|
59
|
+
end
|
60
|
+
|
61
|
+
session.seq = record.seq_num
|
62
|
+
[data[0...maxlen], sender_inet_addr]
|
63
|
+
end
|
64
|
+
|
65
|
+
def self.send_alert(sender_inet_addr, lvl, desc)
|
66
|
+
e = encrypt(Alert.new(lvl, desc).to_wire, sender_inet_addr[3], :alert)
|
67
|
+
|
68
|
+
s = UDPSocket.new(sender_inet_addr[0])
|
69
|
+
s.send(e, 0, sender_inet_addr[3], sender_inet_addr[1])
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
data/lib/codtls/ecc.rb
ADDED
@@ -0,0 +1,26 @@
|
|
1
|
+
require 'openssl'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
# Class for multiplicaion on elliptic curve secp256r1 (prime256v1)
|
5
|
+
class ECC
|
6
|
+
# Does a scalar multiplication with a point on elliptic curve secp256r1.
|
7
|
+
#
|
8
|
+
# @param private_key [String] scalar for the multiplication (msb)
|
9
|
+
# @param public_key [String] point for the multiplication (0x04, x, y)
|
10
|
+
#
|
11
|
+
# @return [String] the resulting point (0x04, x, y)
|
12
|
+
def self.mult(private_key, public_key = nil)
|
13
|
+
key = OpenSSL::BN.new(private_key, 2)
|
14
|
+
|
15
|
+
group = OpenSSL::PKey::EC::Group.new('prime256v1')
|
16
|
+
if public_key.nil?
|
17
|
+
point = group.generator
|
18
|
+
else
|
19
|
+
bignum = OpenSSL::BN.new(public_key, 2)
|
20
|
+
point = OpenSSL::PKey::EC::Point.new(group, bignum)
|
21
|
+
end
|
22
|
+
|
23
|
+
point.mul(key).to_bn.to_s(2)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'codtls/record'
|
2
|
+
require 'codtls/session'
|
3
|
+
require 'openssl/ccm'
|
4
|
+
|
5
|
+
module CoDTLS
|
6
|
+
# TODO
|
7
|
+
module RecordLayer
|
8
|
+
# TODO
|
9
|
+
def self.encrypt(mesg, ip, type = :default)
|
10
|
+
session = Session.new(ip)
|
11
|
+
type = session.handshake? ? :handshake : :appdata if type == :default
|
12
|
+
|
13
|
+
# WARNING: !!! -> disabled for testing purpose
|
14
|
+
# if session.epoch == 0 && type == :appdata
|
15
|
+
# fail SecureSocketError, 'app-data not allowed in epoch 0'
|
16
|
+
# end
|
17
|
+
|
18
|
+
record = Record.new(type, session.epoch, session.seq)
|
19
|
+
|
20
|
+
if record.epoch > 0
|
21
|
+
keyblock = session.key_block
|
22
|
+
ccm = OpenSSL::CCM.new('AES', keyblock[0...16], 8)
|
23
|
+
record.to_wire + ccm.encrypt(mesg, record.nonce(keyblock[32...36]))
|
24
|
+
else
|
25
|
+
record.to_wire + mesg
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'codtls/h_type'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
module Handshake
|
5
|
+
# Tolle Klasse
|
6
|
+
class ChangeCipherSpec < Type
|
7
|
+
def self.parse(data)
|
8
|
+
data.force_encoding('ASCII-8BIT')
|
9
|
+
fail HandshakeError, 'Missing data' if data.length < 1
|
10
|
+
fail HandshakeError, 'Wrong value' unless data[0] == "\x01"
|
11
|
+
ChangeCipherSpec.new
|
12
|
+
end
|
13
|
+
|
14
|
+
public
|
15
|
+
|
16
|
+
def initialize
|
17
|
+
super(32)
|
18
|
+
end
|
19
|
+
|
20
|
+
def to_wire
|
21
|
+
"\x01".force_encoding('ASCII-8BIT')
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
@@ -0,0 +1,79 @@
|
|
1
|
+
require 'codtls/h_type'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
module Handshake
|
5
|
+
# Tolle Klasse
|
6
|
+
class ClientHello < Type
|
7
|
+
attr_reader :time, :random, :cookie
|
8
|
+
|
9
|
+
public
|
10
|
+
|
11
|
+
def initialize(time, random, cookie = nil)
|
12
|
+
super(1)
|
13
|
+
self.time = time
|
14
|
+
self.random = random
|
15
|
+
self.cookie = cookie
|
16
|
+
end
|
17
|
+
|
18
|
+
def time=(time)
|
19
|
+
if time.nil? || time > 0xFFFFFFFF
|
20
|
+
fail HandshakeError, 'Invalid time value'
|
21
|
+
end
|
22
|
+
@time = time
|
23
|
+
end
|
24
|
+
|
25
|
+
def random=(random)
|
26
|
+
if random.nil? || random.b.length != 28
|
27
|
+
fail HandshakeError, 'Random needs to have a length of 28 byte'
|
28
|
+
else
|
29
|
+
@random = random.force_encoding('ASCII-8BIT')
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
def cookie=(cookie)
|
34
|
+
@cookie = cookie
|
35
|
+
unless @cookie.nil?
|
36
|
+
@cookie.force_encoding('ASCII-8BIT')
|
37
|
+
if @cookie.length > 255
|
38
|
+
fail HandshakeError, 'Maximum cookie length is 255'
|
39
|
+
end
|
40
|
+
end
|
41
|
+
end
|
42
|
+
|
43
|
+
def to_wire
|
44
|
+
s = String.new("\xFE\xFD".force_encoding('ASCII-8BIT')) # Version 1.2
|
45
|
+
s.concat([@time].pack('N'))
|
46
|
+
s.concat(@random)
|
47
|
+
if @cookie.nil?
|
48
|
+
s.concat('00'.hex.chr)
|
49
|
+
else
|
50
|
+
s.concat([@cookie.length].pack('C'))
|
51
|
+
s.concat(@cookie)
|
52
|
+
end
|
53
|
+
s.concat('00'.hex.chr) # Ciphersuite Length
|
54
|
+
s.concat('02'.hex.chr) # Ciphersuite Length
|
55
|
+
s.concat('FF'.hex.chr) # Ciphersuite: TLS_PSK_ECDH_WITH_AES_128_CCM_8
|
56
|
+
s.concat('01'.hex.chr) # Ciphersuite: TLS_PSK_ECDH_WITH_AES_128_CCM_8
|
57
|
+
s.concat('01'.hex.chr) # Compression Methods Length
|
58
|
+
s.concat('00'.hex.chr) # No Compression
|
59
|
+
s.concat('00'.hex.chr) # Extensions Length
|
60
|
+
s.concat('0E'.hex.chr) # Extensions Length
|
61
|
+
s.concat('00'.hex.chr) # Supported Elliptic Curves Extension
|
62
|
+
s.concat('0a'.hex.chr) # Supported Elliptic Curves Extension
|
63
|
+
s.concat('00'.hex.chr) # Supported Elliptic Curves Extension Length
|
64
|
+
s.concat('04'.hex.chr) # Supported Elliptic Curves Extension Length
|
65
|
+
s.concat('00'.hex.chr) # Elliptic Curves Arrays Length
|
66
|
+
s.concat('02'.hex.chr) # Elliptic Curves Arrays Length
|
67
|
+
s.concat('00'.hex.chr) # Elliptic Curve secp256r1
|
68
|
+
s.concat('23'.hex.chr) # Elliptic Curve secp256r1
|
69
|
+
s.concat('00'.hex.chr) # Supported Point Formats Extension
|
70
|
+
s.concat('0B'.hex.chr) # Supported Point Formats Extension
|
71
|
+
s.concat('00'.hex.chr) # Supported Point Formats Extension Length
|
72
|
+
s.concat('02'.hex.chr) # Supported Point Formats Extension Length
|
73
|
+
s.concat('01'.hex.chr) # Point Formats Arrays Length
|
74
|
+
s.concat('00'.hex.chr) # Uncompressed Point
|
75
|
+
s
|
76
|
+
end
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
@@ -0,0 +1,57 @@
|
|
1
|
+
require 'codtls/h_chello'
|
2
|
+
require 'codtls/h_verify'
|
3
|
+
require 'codtls/h_shello'
|
4
|
+
require 'codtls/h_keyexchange'
|
5
|
+
require 'codtls/h_shellodone'
|
6
|
+
require 'codtls/h_finished'
|
7
|
+
require 'codtls/h_changecipherspec'
|
8
|
+
require 'codtls/alert'
|
9
|
+
|
10
|
+
module CoDTLS
|
11
|
+
module Handshake
|
12
|
+
# Tolle Klasse
|
13
|
+
class Content
|
14
|
+
TYPE = Array.new(64, nil)
|
15
|
+
# TYPE[0] = hello_request
|
16
|
+
TYPE[1] = Handshake::ClientHello
|
17
|
+
TYPE[2] = Handshake::ServerHello
|
18
|
+
TYPE[3] = Handshake::HelloVerifyRequest
|
19
|
+
# TYPE[11] = certificate
|
20
|
+
TYPE[12] = Handshake::ServerKeyExchange
|
21
|
+
# TYPE[13] = certificate_request
|
22
|
+
TYPE[14] = Handshake::ServerHelloDone
|
23
|
+
# TYPE[15] = certificate_verify
|
24
|
+
TYPE[16] = Handshake::ClientKeyExchange
|
25
|
+
TYPE[20] = Handshake::Finished
|
26
|
+
TYPE[32] = Handshake::ChangeCipherSpec
|
27
|
+
TYPE[33] = Alert
|
28
|
+
|
29
|
+
# data == string -> content vorne abnehmen.
|
30
|
+
# rueckgabe ist spezifisches content object
|
31
|
+
def self.get_content(data)
|
32
|
+
data.force_encoding('ASCII-8BIT')
|
33
|
+
header = data.slice!(0).ord
|
34
|
+
(4 - header & 0x03).times { data.insert(0, "\x00") }
|
35
|
+
length = data.slice!(0...4).unpack('N')[0]
|
36
|
+
if TYPE[(header & 0xFC) >> 2].nil?
|
37
|
+
fail HandshakeError, 'unknown content type'
|
38
|
+
end
|
39
|
+
fail HandshakeError, 'missing handshake data' if data.length < length
|
40
|
+
TYPE[(header & 0xFC) >> 2].parse(data.slice!(0...length))
|
41
|
+
end
|
42
|
+
|
43
|
+
# content braucht to_wire methode.
|
44
|
+
# wird in content verpackt und an data-string angehangen
|
45
|
+
def self.add_content(data, content)
|
46
|
+
header = content.id << 2
|
47
|
+
content = content.to_wire
|
48
|
+
length = [content.length].pack('N')
|
49
|
+
length.slice!(0) while length[0] == "\x00"
|
50
|
+
header |= length.length
|
51
|
+
data.concat(header.chr)
|
52
|
+
data.concat(length)
|
53
|
+
data.concat(content)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
require 'codtls/h_type'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
module Handshake
|
5
|
+
# Tolle Klasse
|
6
|
+
class Finished < Type
|
7
|
+
attr_reader :value
|
8
|
+
|
9
|
+
def self.parse(data)
|
10
|
+
if data.force_encoding('ASCII-8BIT').length < 2
|
11
|
+
fail HandshakeError, 'Missing data'
|
12
|
+
end
|
13
|
+
Finished.new(data)
|
14
|
+
end
|
15
|
+
|
16
|
+
public
|
17
|
+
|
18
|
+
def initialize(value)
|
19
|
+
super(20)
|
20
|
+
@value = value
|
21
|
+
@value = '' if value.nil?
|
22
|
+
@value.force_encoding('ASCII-8BIT')
|
23
|
+
end
|
24
|
+
|
25
|
+
def to_wire
|
26
|
+
@value.force_encoding('ASCII-8BIT')
|
27
|
+
end
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
@@ -0,0 +1,131 @@
|
|
1
|
+
require 'codtls/h_type'
|
2
|
+
|
3
|
+
module CoDTLS
|
4
|
+
module Handshake
|
5
|
+
# Tolle Klasse
|
6
|
+
class KeyExchange < Type
|
7
|
+
ECCURVETYPE = { explicit_prime: "\x01",
|
8
|
+
explicit_char2: "\x02",
|
9
|
+
named_curve: "\x03"
|
10
|
+
# reserved(248..255)
|
11
|
+
# max = 255
|
12
|
+
}
|
13
|
+
NAMEDCURVE = { sect163k1: "\x00\x01",
|
14
|
+
sect163r1: "\x00\x02",
|
15
|
+
sect163r2: "\x00\x03",
|
16
|
+
sect193r1: "\x00\x04",
|
17
|
+
sect193r2: "\x00\x05",
|
18
|
+
sect233k1: "\x00\x06",
|
19
|
+
sect233r1: "\x00\x07",
|
20
|
+
sect239k1: "\x00\x08",
|
21
|
+
sect283k1: "\x00\x09",
|
22
|
+
sect283r1: "\x00\x10",
|
23
|
+
sect409k1: "\x00\x11",
|
24
|
+
sect409r1: "\x00\x12",
|
25
|
+
sect571k1: "\x00\x13",
|
26
|
+
sect571r1: "\x00\x14",
|
27
|
+
secp160k1: "\x00\x15",
|
28
|
+
secp160r1: "\x00\x16",
|
29
|
+
secp160r2: "\x00\x17",
|
30
|
+
secp192k1: "\x00\x18",
|
31
|
+
secp192r1: "\x00\x19",
|
32
|
+
secp224k1: "\x00\x20",
|
33
|
+
secp224r1: "\x00\x21",
|
34
|
+
secp256k1: "\x00\x22",
|
35
|
+
secp256r1: "\x00\x23",
|
36
|
+
secp384r1: "\x00\x24",
|
37
|
+
secp521r1: "\x00\x25",
|
38
|
+
# reserved: \xfe\x00..\xfe\xff
|
39
|
+
arbitrary_explicit_prime_curves: "\xff\x01",
|
40
|
+
arbitrary_explicit_char2_curves: "\xff\x02"
|
41
|
+
# max: \xffff
|
42
|
+
}
|
43
|
+
POINTTYPE = { compressed: "\x02",
|
44
|
+
uncompressed: "\x04",
|
45
|
+
hybrid: "\x06"
|
46
|
+
}
|
47
|
+
|
48
|
+
attr_reader :psk_hint, :curve, :point
|
49
|
+
|
50
|
+
def self.parse_ex(type, data)
|
51
|
+
# pskHint_len (2) + pskHint (16) + ECTyp (1) + NamedCurve (2) +
|
52
|
+
# ECPoint_len (1) + Point_type (1) + p_X (32) + p_Y (32) = (87)
|
53
|
+
data.force_encoding('ASCII-8BIT')
|
54
|
+
psk_len = data.slice!(0...2).unpack('n')[0]
|
55
|
+
psk_hint = data.slice!(0...psk_len)
|
56
|
+
curve = data.slice!(0...3)
|
57
|
+
point_len = data.slice!(0...1).ord
|
58
|
+
point = data.slice!(0...point_len)
|
59
|
+
type.new(psk_hint, curve, point)
|
60
|
+
end
|
61
|
+
|
62
|
+
public
|
63
|
+
|
64
|
+
def initialize(type, psk_hint, curve, point)
|
65
|
+
super(type)
|
66
|
+
self.psk_hint = psk_hint
|
67
|
+
self.curve = curve
|
68
|
+
self.point = point
|
69
|
+
end
|
70
|
+
|
71
|
+
def psk_hint=(psk_hint)
|
72
|
+
if psk_hint.nil? || psk_hint.b.length == 0
|
73
|
+
fail HandshakeError, 'PSK-Hint needed'
|
74
|
+
else
|
75
|
+
@psk_hint = psk_hint.force_encoding('ASCII-8BIT')
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
def curve=(curve)
|
80
|
+
if curve.nil? || curve.b.length == 0
|
81
|
+
fail HandshakeError, 'Curve needed'
|
82
|
+
else
|
83
|
+
@curve = curve.force_encoding('ASCII-8BIT')
|
84
|
+
end
|
85
|
+
end
|
86
|
+
|
87
|
+
def point=(point)
|
88
|
+
if point.nil? || point.b.length == 0
|
89
|
+
fail HandshakeError, 'Point needed'
|
90
|
+
else
|
91
|
+
@point = point.force_encoding('ASCII-8BIT')
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
95
|
+
def to_wire
|
96
|
+
# pskHint_len (2) + pskHint (16) + ECTyp (1) + NamedCurve (2) +
|
97
|
+
# ECPoint_len (1) + Point_type (1) + p_X (32) + p_Y (32) = (87)
|
98
|
+
s = ''
|
99
|
+
s.force_encoding('ASCII-8BIT')
|
100
|
+
s.concat([@psk_hint.length].pack('n'))
|
101
|
+
s.concat(@psk_hint)
|
102
|
+
s.concat(@curve)
|
103
|
+
s.concat([@point.length].pack('c'))
|
104
|
+
s.concat(@point)
|
105
|
+
s
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
# laber
|
110
|
+
class ServerKeyExchange < KeyExchange
|
111
|
+
def self.parse(data)
|
112
|
+
parse_ex(ServerKeyExchange, data)
|
113
|
+
end
|
114
|
+
|
115
|
+
def initialize(psk_hint, curve, point)
|
116
|
+
super(12, psk_hint, curve, point)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
# laber
|
121
|
+
class ClientKeyExchange < KeyExchange
|
122
|
+
def self.parse(data)
|
123
|
+
parse_ex(ClientKeyExchange, data)
|
124
|
+
end
|
125
|
+
|
126
|
+
def initialize(psk_hint, curve, point)
|
127
|
+
super(16, psk_hint, curve, point)
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
end
|