codtls 0.0.1.alpha
Sign up to get free protection for your applications and to get access to all the features.
- 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
|