tinydtls 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/lib/tinydtls.rb +16 -0
- data/lib/tinydtls/context.rb +35 -0
- data/lib/tinydtls/security_conf.rb +83 -0
- data/lib/tinydtls/session.rb +47 -0
- data/lib/tinydtls/session_manager.rb +88 -0
- data/lib/tinydtls/udpsocket.rb +206 -0
- data/lib/tinydtls/wrapper.rb +125 -0
- metadata +79 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA256:
|
3
|
+
metadata.gz: 2e326a7a3bf6820d5596c83553eb9d0cd9c2309ffbb00e24d3045e5a2b69e8c5
|
4
|
+
data.tar.gz: b446f4a18ca71e313bf5ad06d651e89442ac847526ac181b826fa9fac1d1d99c
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 3e1a1a8fe929488102db4cdf3d2d0ef3ec9dec8518fb74d00ed29afb868ea60010d7ee0b5fe64300a61f6b0cd7633293ccca4cdef4bb3122f6e2e70a27118319
|
7
|
+
data.tar.gz: 5b873f6d31d0ce0ba9cc3f0795bc8a95b078761ceab382c719acb3a19a1de2a3231873b4cdd86bc419db79682ea154532928261b3503d6906bd58e7c810906c1
|
data/lib/tinydtls.rb
ADDED
@@ -0,0 +1,16 @@
|
|
1
|
+
require "socket"
|
2
|
+
require "ffi"
|
3
|
+
|
4
|
+
require "tinydtls/wrapper"
|
5
|
+
require "tinydtls/context"
|
6
|
+
require "tinydtls/session"
|
7
|
+
require "tinydtls/security_conf"
|
8
|
+
require "tinydtls/session_manager"
|
9
|
+
require "tinydtls/udpsocket"
|
10
|
+
|
11
|
+
module TinyDTLS
|
12
|
+
# Map used to map `object_ids` passed as void pointers to the tinydtls
|
13
|
+
# callback functions to actually ruby UDPSockets. This is neccessary
|
14
|
+
# since we can't pass pointers to ruby objects to C functions.
|
15
|
+
CONTEXT_MAP = Hash.new
|
16
|
+
end
|
@@ -0,0 +1,35 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# The class Context stores all per-connection information,
|
3
|
+
# it is exclusively used in the `TinyDTLS::CONTEXT_MAP`.
|
4
|
+
class Context
|
5
|
+
# The method used for sending data on the socket.
|
6
|
+
attr_reader :sendfn
|
7
|
+
|
8
|
+
# The queue used for communication with the receive thread.
|
9
|
+
attr_reader :queue
|
10
|
+
|
11
|
+
# An instance of the security configuration class.
|
12
|
+
attr_reader :secconf
|
13
|
+
|
14
|
+
# Create a new instance of this class with a given function to send
|
15
|
+
# message on the transport layer, a queue for storing received
|
16
|
+
# messages and a security configuration containing a key to identity
|
17
|
+
# mapping.
|
18
|
+
def initialize(sendfn, queue, secconf)
|
19
|
+
@sendfn = sendfn
|
20
|
+
@queue = queue
|
21
|
+
@secconf = secconf
|
22
|
+
end
|
23
|
+
|
24
|
+
# Create a new instance of this class from a pointer to a `struct
|
25
|
+
# dtls_context_t`. Such a pointer is, for instance, passed to the
|
26
|
+
# various tinydtls callback functions.
|
27
|
+
#
|
28
|
+
# The `struct dtls_context_t` which the given pointer points to must
|
29
|
+
# have been created by TinyDTLS::UDPSocket#initialize.
|
30
|
+
def self.from_ptr(ptr)
|
31
|
+
obj = Wrapper::DTLSContextStruct.new(ptr)
|
32
|
+
return CONTEXT_MAP[Wrapper::dtls_get_app_data(obj).to_i]
|
33
|
+
end
|
34
|
+
end
|
35
|
+
end
|
@@ -0,0 +1,83 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# This class is used to map user identity for pre-shared keys to their
|
3
|
+
# actual keys. It provides an implementation of the `get_psk_info`
|
4
|
+
# function pointer used in the `dtls_handler_t` struct which is used
|
5
|
+
# by tinydtls to retrieve keys and identities.
|
6
|
+
#
|
7
|
+
# XXX: Currently this function doesn't map IP address to keys/identities.
|
8
|
+
class SecurityConfig
|
9
|
+
# Implementation of the `get_psk_info` function pointer as used by
|
10
|
+
# the `dtls_handler_t` struct.
|
11
|
+
#
|
12
|
+
# If tinydtls requests a key for a given identity the key is
|
13
|
+
# returned if the identity exists. If no identity was specified the
|
14
|
+
# #default_key is returned.
|
15
|
+
#
|
16
|
+
# If tinydtls requests an id the #default_id is always returned.
|
17
|
+
#
|
18
|
+
# TODO: It would be nice to return an id depending on the
|
19
|
+
# `session_t` passad to this callback.
|
20
|
+
GetPSKInfo = Proc.new do |ctx, sess, type, desc, dlen, result, rlen|
|
21
|
+
secconf = TinyDTLS::Context.from_ptr(ctx).secconf
|
22
|
+
if desc.null?
|
23
|
+
key = secconf.default_key
|
24
|
+
end
|
25
|
+
|
26
|
+
if type == :DTLS_PSK_KEY
|
27
|
+
key ||= secconf.get_key(desc.read_string(dlen))
|
28
|
+
if key.nil?
|
29
|
+
Wrapper::dtls_alert_fatal_create(
|
30
|
+
Wrapper::Alert[:DTLS_ALERT_DECRYPT_ERROR])
|
31
|
+
elsif key.bytesize > rlen
|
32
|
+
Wrapper::dtls_alert_fatal_create(
|
33
|
+
Wrapper::Alert[:DTLS_ALERT_INTERNAL_ERROR])
|
34
|
+
else
|
35
|
+
result.put_bytes(0, key)
|
36
|
+
key.bytesize
|
37
|
+
end
|
38
|
+
elsif type == :DTLS_PSK_IDENTITY
|
39
|
+
identity = secconf.default_id
|
40
|
+
result.put_bytes(0, identity)
|
41
|
+
identity.bytesize
|
42
|
+
else
|
43
|
+
0
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
# Create a new instance of this class. A #default_key and a
|
48
|
+
# #default_id can be optionally specified. If they are not specified
|
49
|
+
# the first key/identity added is used as the default value.
|
50
|
+
def initialize(default_id = nil, default_key = nil)
|
51
|
+
@default_id = default_id
|
52
|
+
@default_key = default_key
|
53
|
+
|
54
|
+
@identity_map = Hash.new
|
55
|
+
end
|
56
|
+
|
57
|
+
# Adds a security configuration for the given identity.
|
58
|
+
def add_client(id, key)
|
59
|
+
@identity_map[id] = key
|
60
|
+
end
|
61
|
+
|
62
|
+
# Retrieves the key associated with the given identity.
|
63
|
+
def get_key(id)
|
64
|
+
@identity_map[id]
|
65
|
+
end
|
66
|
+
|
67
|
+
def default_id
|
68
|
+
if @default_id.nil?
|
69
|
+
@identity_map.to_a.first.first
|
70
|
+
else
|
71
|
+
@default_id
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
def default_key
|
76
|
+
if @default_key.nil?
|
77
|
+
@identity_map.to_a.first.last
|
78
|
+
else
|
79
|
+
@default_key
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
end
|
@@ -0,0 +1,47 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# This class offers a higher-level abstraction for the `session_t` type.
|
3
|
+
class Session
|
4
|
+
attr_reader :addrinfo
|
5
|
+
|
6
|
+
# Creates a new instance of this class from the given Addrinfo.
|
7
|
+
def initialize(addrinfo)
|
8
|
+
@addrinfo = addrinfo
|
9
|
+
unless @addrinfo.is_a? Addrinfo
|
10
|
+
raise TypeError
|
11
|
+
end
|
12
|
+
|
13
|
+
sockaddr = @addrinfo.to_sockaddr
|
14
|
+
@session = Wrapper::dtls_new_session(sockaddr, sockaddr.bytesize)
|
15
|
+
if @session.null?
|
16
|
+
raise Errno::ENOMEM
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# Creates a new instance of this class from a pointer to a
|
21
|
+
# `session_t` tinydtls type. Such a pointer is, for instance, passed
|
22
|
+
# to the various tinydtls callback functions.
|
23
|
+
def self.from_ptr(ptr)
|
24
|
+
lenptr = Wrapper::SocklenPtr.new
|
25
|
+
sockaddr = Wrapper::dtls_session_addr(ptr, lenptr)
|
26
|
+
|
27
|
+
addrinfo = Addrinfo.new(sockaddr.read_string(lenptr[:value]))
|
28
|
+
return Session.new(addrinfo)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Converts the object into a C pointer to a `session_t` tinydtls
|
32
|
+
# type. This pointer can be passed to various functions provided by
|
33
|
+
# TinyDTLS::Wrapper.
|
34
|
+
def to_ptr
|
35
|
+
@session
|
36
|
+
end
|
37
|
+
|
38
|
+
# Frees all resources associated with the underlying `session_t`
|
39
|
+
# tinydtls type and reset any existing connections.
|
40
|
+
def destroy!(ctx)
|
41
|
+
peer = Wrapper::dtls_get_peer(ctx, @session)
|
42
|
+
unless peer.null?
|
43
|
+
Wrapper::dtls_reset_peer(ctx, peer)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
@@ -0,0 +1,88 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# This class is used to manage established tinydtls sessions. It
|
3
|
+
# stores instances of the TinyDTLS::Session class.
|
4
|
+
#
|
5
|
+
# While memory allocated for sessions is automatically freed by
|
6
|
+
# tinydtls, if it receive an alert from the peer associated with that
|
7
|
+
# session, memory isn't freed if the peer doesn't send an alert.
|
8
|
+
# Therefore this class starts a background thread automatically
|
9
|
+
# freeing memory associated with sessions which haven't been used
|
10
|
+
# since a specified duration.
|
11
|
+
class SessionManager
|
12
|
+
# Default timeout for the cleanup thread in seconds.
|
13
|
+
DEFAULT_TIMEOUT = (5 * 60).freeze
|
14
|
+
|
15
|
+
attr_accessor :timeout
|
16
|
+
|
17
|
+
# Creates a new instance of this class. A tinydtls `context_t`
|
18
|
+
# pointer is required to free sessions in the background thread.
|
19
|
+
def initialize(ctx, timeout = DEFAULT_TIMEOUT)
|
20
|
+
@store = {}
|
21
|
+
@mutex = Mutex.new
|
22
|
+
@timeout = timeout
|
23
|
+
|
24
|
+
start_thread(ctx)
|
25
|
+
end
|
26
|
+
|
27
|
+
# Retrieve a session from the session manager.
|
28
|
+
def [](addrinfo, &f)
|
29
|
+
unless addrinfo.is_a? Addrinfo
|
30
|
+
raise TypeError
|
31
|
+
end
|
32
|
+
|
33
|
+
key = addrinfo.getnameinfo
|
34
|
+
if @store.has_key? key
|
35
|
+
sess, _ = @store[key]
|
36
|
+
else
|
37
|
+
sess = Session.new(addrinfo)
|
38
|
+
@store[key] = [sess, true]
|
39
|
+
end
|
40
|
+
|
41
|
+
@mutex.synchronize { f.call(sess) }
|
42
|
+
end
|
43
|
+
|
44
|
+
# Frees all ressources associated with this class.
|
45
|
+
def destroy!
|
46
|
+
@mutex.lock
|
47
|
+
@store.clear
|
48
|
+
@thread.kill
|
49
|
+
end
|
50
|
+
|
51
|
+
private
|
52
|
+
|
53
|
+
# Creates a thread responsible for freeing ressources assigned to
|
54
|
+
# stale connection. This thread implements the clock hand algorithm
|
55
|
+
# as described in Modern Operating Systems, p. 212.
|
56
|
+
#
|
57
|
+
# The thread is only created once.
|
58
|
+
def start_thread(ctx)
|
59
|
+
@thread ||= Thread.new do
|
60
|
+
while true
|
61
|
+
# XXX: How does concurrent access to variables work in ruby?
|
62
|
+
# as known as: Is this a concurrency problems since the value
|
63
|
+
# of @timeout might be changed by a different thread since an
|
64
|
+
# attr_accessor for it is declared.
|
65
|
+
sleep @timeout
|
66
|
+
|
67
|
+
@mutex.lock
|
68
|
+
@store.transform_values! do |value|
|
69
|
+
sess, used = value
|
70
|
+
if used
|
71
|
+
[sess, !used]
|
72
|
+
else # Not used since we've been here last time → free resources
|
73
|
+
sess.destroy!(ctx)
|
74
|
+
nil
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# We can't delete elements from the map in the #transform_values!
|
79
|
+
# block, we just assign nil to them. Thus we need to filter
|
80
|
+
# the map again here.
|
81
|
+
@store.reject! { |_, v| v.nil? }
|
82
|
+
|
83
|
+
@mutex.unlock
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
end
|
@@ -0,0 +1,206 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# This class implements a DTLS socket on top of a ruby UDPSocket. It
|
3
|
+
# isn't currently nowhere near being API compatible with the ruby
|
4
|
+
# UDPSocket. Being 100% backwards compatible with the ruby UDPSocket
|
5
|
+
# is not possible to to tinydtls internals. For instance we can't
|
6
|
+
# properly implement IO#select. It should thus be considered if it is
|
7
|
+
# really a good idea to extend the ruby UDPSocket in the long run.
|
8
|
+
#
|
9
|
+
# Basic send and receive methods are implemented and should work.
|
10
|
+
class UDPSocket < ::UDPSocket
|
11
|
+
Write = Proc.new do |ctx, sess, buf, len|
|
12
|
+
addrinfo = Session.from_ptr(sess).addrinfo
|
13
|
+
|
14
|
+
ctxobj = TinyDTLS::Context.from_ptr(ctx)
|
15
|
+
ctxobj.sendfn.call(buf.read_string(len),
|
16
|
+
Socket::MSG_DONTWAIT,
|
17
|
+
addrinfo.ip_address, addrinfo.ip_port)
|
18
|
+
end
|
19
|
+
|
20
|
+
Read = Proc.new do |ctx, sess, buf, len|
|
21
|
+
addrinfo = Session.from_ptr(sess).addrinfo
|
22
|
+
|
23
|
+
# We need to perform a reverse lookup here because
|
24
|
+
# the #recvfrom function needs to return the DNS
|
25
|
+
# hostname.
|
26
|
+
sender = Socket.getaddrinfo(addrinfo.ip_address,
|
27
|
+
addrinfo.ip_port, nil, :DGRAM,
|
28
|
+
0, 0, true).first
|
29
|
+
|
30
|
+
ctxobj = TinyDTLS::Context.from_ptr(ctx)
|
31
|
+
ctxobj.queue.push([buf.read_string(len), sender])
|
32
|
+
|
33
|
+
# It is unclear to me why this callback even needs a return value,
|
34
|
+
# the `tests/dtls-client.c` program in the tinydtls repository
|
35
|
+
# simply uses 0 as a return value, so let's do that as well.
|
36
|
+
0
|
37
|
+
end
|
38
|
+
|
39
|
+
def initialize(address_family = Socket::AF_INET, timeout = nil)
|
40
|
+
super(address_family)
|
41
|
+
Wrapper::dtls_init
|
42
|
+
|
43
|
+
@timeout = timeout.freeze
|
44
|
+
@queue = Queue.new
|
45
|
+
@family = address_family
|
46
|
+
@sendfn = method(:send).super_method
|
47
|
+
@secconf = SecurityConfig.new
|
48
|
+
|
49
|
+
id = object_id
|
50
|
+
CONTEXT_MAP[id] = TinyDTLS::Context.new(@sendfn, @queue, @secconf)
|
51
|
+
|
52
|
+
cptr = Wrapper::dtls_new_context(FFI::Pointer.new(id))
|
53
|
+
@ctx = Wrapper::DTLSContextStruct.new(cptr)
|
54
|
+
|
55
|
+
if timeout.nil?
|
56
|
+
@sessions = SessionManager.new(@ctx)
|
57
|
+
else
|
58
|
+
@sessions = SessionManager.new(@ctx, timeout)
|
59
|
+
end
|
60
|
+
|
61
|
+
@handler = Wrapper::DTLSHandlerStruct.new
|
62
|
+
@handler[:write] = UDPSocket::Write
|
63
|
+
@handler[:read] = UDPSocket::Read
|
64
|
+
@handler[:get_psk_info] = SecurityConfig::GetPSKInfo
|
65
|
+
Wrapper::dtls_set_handler(@ctx, @handler)
|
66
|
+
end
|
67
|
+
|
68
|
+
def add_client(id, key)
|
69
|
+
@secconf.add_client(id, key)
|
70
|
+
end
|
71
|
+
|
72
|
+
def bind(host, port)
|
73
|
+
super(host, port)
|
74
|
+
start_thread
|
75
|
+
end
|
76
|
+
|
77
|
+
# TODO: close_{read,write}
|
78
|
+
|
79
|
+
def close
|
80
|
+
@sessions.destroy!
|
81
|
+
@thread.kill unless @thread.nil?
|
82
|
+
|
83
|
+
# DTLS free context sends messages to peers so we need to
|
84
|
+
# call it before we actually close the underlying socket.
|
85
|
+
Wrapper::dtls_free_context(@ctx)
|
86
|
+
super
|
87
|
+
|
88
|
+
# Assuming the @thread is already stopped at this point
|
89
|
+
# we can safely access the CONTEXT_MAP without running
|
90
|
+
# into any kind of concurrency problems.
|
91
|
+
CONTEXT_MAP.delete(object_id)
|
92
|
+
end
|
93
|
+
|
94
|
+
def connect(host, port)
|
95
|
+
@defhost = host
|
96
|
+
@defport = port
|
97
|
+
end
|
98
|
+
|
99
|
+
def recvfrom(len = -1, flags = 0)
|
100
|
+
ary = @queue.pop
|
101
|
+
return [byteslice(ary.first, len), ary.last]
|
102
|
+
end
|
103
|
+
|
104
|
+
def recvfrom_nonblock(len = -1, flag = 0, outbuf = nil, exception: true)
|
105
|
+
ary = nil
|
106
|
+
begin
|
107
|
+
ary = @queue.pop(true)
|
108
|
+
rescue ThreadError
|
109
|
+
if exception
|
110
|
+
raise IO::EAGAINWaitReadable
|
111
|
+
else
|
112
|
+
return :wait_readable
|
113
|
+
end
|
114
|
+
end
|
115
|
+
|
116
|
+
pay = byteslice(ary.first, len)
|
117
|
+
unless outbuf.nil?
|
118
|
+
outbuf << pay
|
119
|
+
end
|
120
|
+
|
121
|
+
return [pay, ary.last]
|
122
|
+
end
|
123
|
+
|
124
|
+
# TODO: The recvmsg functions only implement a subset of the
|
125
|
+
# functionallity of the UDP socket class, e.g. they don't return
|
126
|
+
# ancillary data.
|
127
|
+
|
128
|
+
def recvmsg(maxmesglen = nil, flags = 0, maxcontrollen = nil, opts = {})
|
129
|
+
mesg, sender = recvfrom(maxmesglen.nil? ? -1 : maxmesglen, flags)
|
130
|
+
return [mesg, to_addrinfo(*sender), 0, nil]
|
131
|
+
end
|
132
|
+
|
133
|
+
def recvmsg_nonblock(maxdatalen = nil, flags = 0, maxcontrollen = nil, opts = {})
|
134
|
+
mesg, sender = recvfrom_nonblock(maxdatalen.nil? ? -1 : maxdatalen, flags)
|
135
|
+
return [mesg, to_addrinfo(*sender), 0, nil]
|
136
|
+
end
|
137
|
+
|
138
|
+
def send(mesg, flags, host = nil, port = nil)
|
139
|
+
start_thread
|
140
|
+
|
141
|
+
if host.nil? and port.nil?
|
142
|
+
if @defport.nil? or @defhost.nil?
|
143
|
+
raise Errno::EDESTADDRREQ
|
144
|
+
end
|
145
|
+
|
146
|
+
host = @defhost
|
147
|
+
port = @defport
|
148
|
+
elsif port.nil? # host is not nil and must be a sockaddr_to
|
149
|
+
port, host = Socket.unpack_sockaddr_in(host)
|
150
|
+
end
|
151
|
+
|
152
|
+
addr = Addrinfo.getaddrinfo(host, port, nil, :DGRAM).first
|
153
|
+
|
154
|
+
# If a new thread has been started above a new handshake needs to
|
155
|
+
# be performed by it. We need to block here until the handshake
|
156
|
+
# was completed.
|
157
|
+
#
|
158
|
+
# The current approach is calling `Wrapper::dtls_write` until it
|
159
|
+
# succeeds which is suboptimal because it doesn't take into
|
160
|
+
# account that the handshake may fail.
|
161
|
+
until (res = dtls_send(addr, mesg)) > 0
|
162
|
+
sleep 1
|
163
|
+
end
|
164
|
+
|
165
|
+
return res
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def to_addrinfo(*args)
|
171
|
+
af, port, _, addr = args
|
172
|
+
Addrinfo.getaddrinfo(addr, port, af, :DGRAM).first
|
173
|
+
end
|
174
|
+
|
175
|
+
def byteslice(str, len)
|
176
|
+
return len >= 0 ? str.byteslice(0, len) : str
|
177
|
+
end
|
178
|
+
|
179
|
+
# Sends a dtls message to a specified address. It also takes care
|
180
|
+
# of locking the session manager and is thus thread-safe.
|
181
|
+
def dtls_send(addr, mesg)
|
182
|
+
@sessions[addr] do |sess|
|
183
|
+
res = Wrapper::dtls_write(@ctx, sess.to_ptr, mesg, mesg.bytesize)
|
184
|
+
res == -1 ? raise(Errno::EIO) : res
|
185
|
+
end
|
186
|
+
end
|
187
|
+
|
188
|
+
# Creates a thread responsible for reading from reciving messages
|
189
|
+
# from the underlying socket and passing them to tinydtls.
|
190
|
+
#
|
191
|
+
# The thread is only created once.
|
192
|
+
def start_thread
|
193
|
+
@thread ||= Thread.new do
|
194
|
+
while true
|
195
|
+
data, addr = method(:recvfrom).super_method
|
196
|
+
.call(Wrapper::DTLS_MAX_BUF)
|
197
|
+
addrinfo = to_addrinfo(*addr)
|
198
|
+
|
199
|
+
@sessions[addrinfo] do |sess|
|
200
|
+
Wrapper::dtls_handle_message(@ctx, sess.to_ptr, data, data.bytesize)
|
201
|
+
end
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
end
|
@@ -0,0 +1,125 @@
|
|
1
|
+
module TinyDTLS
|
2
|
+
# This module provides a low level FFI wrapper for the relevant
|
3
|
+
# tinydtls functions. It might be subject to change thus it is highly
|
4
|
+
# recommended to use the high level abstraction layer instead.
|
5
|
+
module Wrapper
|
6
|
+
extend FFI::Library
|
7
|
+
ffi_lib "libtinydtls.so"
|
8
|
+
|
9
|
+
# Constants defined as macros in the tinydtls header files
|
10
|
+
DTLS_COOKIE_SECRET_LENGTH = 12
|
11
|
+
DTLS_MAX_BUF = 1400
|
12
|
+
|
13
|
+
Alert = enum(
|
14
|
+
:DTLS_ALERT_CLOSE_NOTIFY, 0,
|
15
|
+
:DTLS_ALERT_UNEXPECTED_MESSAGE, 10,
|
16
|
+
:DTLS_ALERT_BAD_RECORD_MAC, 20,
|
17
|
+
:DTLS_ALERT_RECORD_OVERFLOW, 22,
|
18
|
+
:DTLS_ALERT_DECOMPRESSION_FAILURE, 30,
|
19
|
+
:DTLS_ALERT_HANDSHAKE_FAILURE, 40,
|
20
|
+
:DTLS_ALERT_BAD_CERTIFICATE, 42,
|
21
|
+
:DTLS_ALERT_UNSUPPORTED_CERTIFICATE, 43,
|
22
|
+
:DTLS_ALERT_CERTIFICATE_REVOKED, 44,
|
23
|
+
:DTLS_ALERT_CERTIFICATE_EXPIRED, 45,
|
24
|
+
:DTLS_ALERT_CERTIFICATE_UNKNOWN, 46,
|
25
|
+
:DTLS_ALERT_ILLEGAL_PARAMETER, 47,
|
26
|
+
:DTLS_ALERT_UNKNOWN_CA, 48,
|
27
|
+
:DTLS_ALERT_ACCESS_DENIED, 49,
|
28
|
+
:DTLS_ALERT_DECODE_ERROR, 50,
|
29
|
+
:DTLS_ALERT_DECRYPT_ERROR, 51,
|
30
|
+
:DTLS_ALERT_PROTOCOL_VERSION, 70,
|
31
|
+
:DTLS_ALERT_INSUFFICIENT_SECURITY, 71,
|
32
|
+
:DTLS_ALERT_INTERNAL_ERROR, 80,
|
33
|
+
:DTLS_ALERT_USER_CANCELED, 90,
|
34
|
+
:DTLS_ALERT_NO_RENEGOTIATION, 100,
|
35
|
+
:DTLS_ALERT_UNSUPPORTED_EXTENSION, 110
|
36
|
+
)
|
37
|
+
|
38
|
+
LogLevel = enum(
|
39
|
+
:DTLS_LOG_EMERG, 0,
|
40
|
+
:DTLS_LOG_ALERT,
|
41
|
+
:DTLS_LOG_CRIT,
|
42
|
+
:DTLS_LOG_WARN,
|
43
|
+
:DTLS_LOG_NOTICE,
|
44
|
+
:DTLS_LOG_INFO,
|
45
|
+
:DTLS_LOG_DEBUG
|
46
|
+
)
|
47
|
+
|
48
|
+
enum :alert_level, [
|
49
|
+
:DTLS_ALERT_LEVEL_WARNING, 1,
|
50
|
+
:DTLS_ALERT_LEVEL_FATAL, 2,
|
51
|
+
]
|
52
|
+
|
53
|
+
enum :credential_type, [
|
54
|
+
:DTLS_PSK_HINT,
|
55
|
+
:DTLS_PSK_IDENTITY,
|
56
|
+
:DTLS_PSK_KEY,
|
57
|
+
]
|
58
|
+
|
59
|
+
class DTLSContextStruct < FFI::Struct
|
60
|
+
layout :cookie_secret, [:uchar, DTLS_COOKIE_SECRET_LENGTH],
|
61
|
+
:cookie_secret_age, :uint32,
|
62
|
+
:peers, :pointer,
|
63
|
+
:sendqueue, :pointer,
|
64
|
+
:app, :pointer,
|
65
|
+
:h, :pointer,
|
66
|
+
:readbuf, [:uchar, DTLS_MAX_BUF]
|
67
|
+
end
|
68
|
+
|
69
|
+
class DTLSHandlerStruct < FFI::Struct
|
70
|
+
layout :write,
|
71
|
+
callback([:pointer, :pointer, :pointer, :size_t], :int),
|
72
|
+
:read,
|
73
|
+
callback([:pointer, :pointer, :pointer, :size_t], :int),
|
74
|
+
:event_function,
|
75
|
+
callback([:pointer, :pointer, :alert_level, :ushort], :int),
|
76
|
+
:get_psk_info,
|
77
|
+
callback([:pointer, :pointer, :credential_type,
|
78
|
+
:pointer, :size_t, :pointer, :size_t], :int),
|
79
|
+
:get_ecdsa_key,
|
80
|
+
callback([:pointer, :pointer, :pointer], :int),
|
81
|
+
:verify_ecdsa_key,
|
82
|
+
callback([:pointer, :pointer, :uchar, :uchar, :size_t], :int)
|
83
|
+
end
|
84
|
+
|
85
|
+
attach_function :dtls_init, [], :void
|
86
|
+
attach_function :dtls_new_context, [:pointer], :pointer
|
87
|
+
attach_function :dtls_free_context, [:pointer], :void
|
88
|
+
attach_function :dtls_handle_message,
|
89
|
+
[:pointer, :pointer, :pointer, :int], :int
|
90
|
+
attach_function :dtls_write,
|
91
|
+
[:pointer, :pointer, :pointer, :size_t], :int
|
92
|
+
attach_function :dtls_connect,
|
93
|
+
[:pointer, :pointer], :int
|
94
|
+
attach_function :dtls_set_log_level, [LogLevel], :void
|
95
|
+
|
96
|
+
attach_function :dtls_get_peer, [:pointer, :pointer], :pointer
|
97
|
+
attach_function :dtls_reset_peer, [:pointer, :pointer], :void
|
98
|
+
|
99
|
+
def self.dtls_alert_fatal_create(desc)
|
100
|
+
return -((2 << 8) | desc)
|
101
|
+
end
|
102
|
+
|
103
|
+
# This type is needed for the `dtls_session_addr` wrapper.
|
104
|
+
# See https://github.com/ffi/ffi/wiki/Pointers#passing-by-reference
|
105
|
+
class SocklenPtr < FFI::Struct
|
106
|
+
layout :value, :socklen_t
|
107
|
+
end
|
108
|
+
|
109
|
+
# These functions are not available in vanilla tinydtls.
|
110
|
+
# They are required to make interaction with the tinydtls `session_t`
|
111
|
+
# type possible without creating a ruby wrapper for `struct
|
112
|
+
# sockaddr_in{,6}`.
|
113
|
+
attach_function :dtls_new_session,
|
114
|
+
[:pointer, :socklen_t], :pointer
|
115
|
+
attach_function :dtls_session_addr, [:pointer, SocklenPtr], :pointer
|
116
|
+
|
117
|
+
def self.dtls_get_app_data(ctx)
|
118
|
+
return ctx[:app]
|
119
|
+
end
|
120
|
+
|
121
|
+
def self.dtls_set_handler(ctx, handler)
|
122
|
+
ctx[:h] = handler
|
123
|
+
end
|
124
|
+
end
|
125
|
+
end
|
metadata
ADDED
@@ -0,0 +1,79 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: tinydtls
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.1.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Sören Tempel
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2018-07-14 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: ffi
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.9'
|
20
|
+
type: :runtime
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.9'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: minitest
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '5.11'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '5.11'
|
41
|
+
description: tinydtls provides a DTLS implementation
|
42
|
+
email:
|
43
|
+
- tempel@uni-bremen.de
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- lib/tinydtls.rb
|
49
|
+
- lib/tinydtls/context.rb
|
50
|
+
- lib/tinydtls/security_conf.rb
|
51
|
+
- lib/tinydtls/session.rb
|
52
|
+
- lib/tinydtls/session_manager.rb
|
53
|
+
- lib/tinydtls/udpsocket.rb
|
54
|
+
- lib/tinydtls/wrapper.rb
|
55
|
+
homepage: https://github.com/ruby-dtls/rb-tinydtls
|
56
|
+
licenses:
|
57
|
+
- MIT
|
58
|
+
metadata: {}
|
59
|
+
post_install_message:
|
60
|
+
rdoc_options: []
|
61
|
+
require_paths:
|
62
|
+
- lib
|
63
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
64
|
+
requirements:
|
65
|
+
- - ">="
|
66
|
+
- !ruby/object:Gem::Version
|
67
|
+
version: 2.2.0
|
68
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - ">="
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
requirements: []
|
74
|
+
rubyforge_project:
|
75
|
+
rubygems_version: 2.7.3
|
76
|
+
signing_key:
|
77
|
+
specification_version: 4
|
78
|
+
summary: It wraps the tinydtls library
|
79
|
+
test_files: []
|