Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
R
re6stnet
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
2
Issues
2
List
Boards
Labels
Milestones
Merge Requests
4
Merge Requests
4
Analytics
Analytics
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Commits
Issue Boards
Open sidebar
nexedi
re6stnet
Commits
a7a86341
Commit
a7a86341
authored
Feb 24, 2015
by
Julien Muchembled
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
New protocol between nodes with authentication
parent
32ebb80b
Changes
6
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
6 changed files
with
325 additions
and
132 deletions
+325
-132
demo/demo
demo/demo
+13
-7
re6st/db.py
re6st/db.py
+1
-0
re6st/registry.py
re6st/registry.py
+52
-56
re6st/tunnel.py
re6st/tunnel.py
+132
-65
re6st/utils.py
re6st/utils.py
+11
-1
re6st/x509.py
re6st/x509.py
+116
-3
No files found.
demo/demo
View file @
a7a86341
...
...
@@ -297,21 +297,27 @@ if len(sys.argv) > 1:
elif self.path == '/tunnel.html':
other = 'route'
gv = registry.Popen(('python', '-c', r"""if 1:
import math
import math
, json
from re6st.registry import RegistryClient
g = eval(RegistryClient('http://localhost/').topology())
g = json.loads(RegistryClient(
'http://localhost/').topology())
r = set(g.pop('', ()))
a = set()
for v in g.itervalues():
a.update(v)
g.update(dict.fromkeys(a.difference(g), ()))
print 'digraph {'
a = 2 * math.pi / len(g)
z = 4
m2 = '%u/80' % (2
<
<
64
)
title =
lambda
n:
'
2
|
80
'
if
n =
=
m2
else
n
g =
sorted((title(k),
v
)
for
k
,
v
in
g
.
iteritems
())
for
i
,
(
n
,
p
)
in
enumerate
(
g
)
:
g =
sorted((title(k),
k
in
r
,
v
)
for
k
,
v
in
g
.
iteritems
())
for
i
,
(
n
,
r
,
v
)
in
enumerate
(
g
)
:
print
'"%
s
"
[pos=
"%s,%s!"
%
s
];'
%
(
title
(
n
),
z
*
math
.
cos
(
a
*
i
),
z
*
math
.
sin
(
a
*
i
),
'
,
style=
dashed'
if
p
is
None
else
'
')
for
p
in
p
or
()
:
print
'"%
s
"
-
>
"%s";' % (n, title(
p
))
'
'
if
r
else
',
style=
dashed
')
for
v
in
v
:
print
'"%
s
"
-
>
"%s";' % (n, title(
v
))
print '}'
"""), stdout=subprocess.PIPE, cwd="..").communicate()[0]
if gv:
...
...
re6st/db.py
View file @
a7a86341
...
...
@@ -12,6 +12,7 @@ class PeerDB(object):
logging
.
info
(
'Initialize cache ...'
)
self
.
_db
=
sqlite3
.
connect
(
db_path
,
isolation_level
=
None
)
self
.
_db
.
text_factory
=
str
q
=
self
.
_db
.
execute
q
(
"PRAGMA synchronous = OFF"
)
q
(
"PRAGMA journal_mode = MEMORY"
)
...
...
re6st/registry.py
View file @
a7a86341
...
...
@@ -18,10 +18,10 @@ Authenticated communication:
- the last one that was really used by the client (!hello)
- the one of the last handshake (hello)
"""
import
base64
,
hmac
,
hashlib
,
httplib
,
inspect
,
logging
import
base64
,
hmac
,
hashlib
,
httplib
,
inspect
,
json
,
logging
import
mailbox
,
os
,
random
,
select
,
smtplib
,
socket
,
sqlite3
import
string
,
s
truct
,
s
ys
,
threading
,
time
,
weakref
from
collections
import
deque
import
string
,
sys
,
threading
,
time
,
weakref
from
collections
import
de
faultdict
,
de
que
from
datetime
import
datetime
from
BaseHTTPServer
import
HTTPServer
,
BaseHTTPRequestHandler
from
email.mime.text
import
MIMEText
...
...
@@ -92,6 +92,20 @@ class RegistryServer(object):
self
.
onTimeout
()
def
sendto
(
self
,
prefix
,
code
):
self
.
sock
.
sendto
(
"%s
\
0
%c"
%
(
prefix
,
code
),
(
'::1'
,
tunnel
.
PORT
))
def
recv
(
self
,
code
):
try
:
prefix
,
msg
=
self
.
sock
.
recv
(
1
<<
16
).
split
(
'
\
0
'
,
1
)
int
(
prefix
,
2
)
except
ValueError
:
pass
else
:
if
msg
and
ord
(
msg
[
0
])
==
code
:
return
prefix
,
msg
[
1
:]
return
None
,
None
def
select
(
self
,
r
,
w
,
t
):
if
self
.
timeout
:
t
.
append
((
self
.
timeout
,
self
.
onTimeout
))
...
...
@@ -198,8 +212,7 @@ class RegistryServer(object):
def
hello
(
self
,
client_prefix
):
with
self
.
lock
:
cert
=
self
.
getCert
(
client_prefix
)
key
=
hashlib
.
sha1
(
struct
.
pack
(
'Q'
,
random
.
getrandbits
(
64
))).
digest
()
key
=
utils
.
newHmacSecret
()
self
.
sessions
.
setdefault
(
client_prefix
,
[])[
1
:]
=
key
,
key
=
x509
.
encrypt
(
cert
,
key
)
sign
=
self
.
cert
.
sign
(
key
)
...
...
@@ -349,26 +362,22 @@ class RegistryServer(object):
# (in case 'peers' is empty).
peer
=
self
.
prefix
with
self
.
lock
:
address
=
utils
.
ipFromBin
(
self
.
network
+
peer
),
tunnel
.
PORT
self
.
sock
.
sendto
(
'
\
2
'
,
address
)
self
.
sendto
(
peer
,
1
)
s
=
self
.
sock
,
timeout
=
3
end
=
timeout
+
time
.
time
()
# Loop because there may be answers from previous requests.
while
select
.
select
(
s
,
(),
(),
timeout
)[
0
]:
msg
=
self
.
sock
.
recv
(
1
<<
16
)
if
msg
[
0
]
==
'
\
1
'
:
try
:
msg
=
msg
[
1
:
msg
.
index
(
'
\
n
'
)]
except
ValueError
:
continue
if
msg
.
split
()[
0
]
==
peer
:
break
prefix
,
msg
=
self
.
recv
(
1
)
if
prefix
==
peer
:
break
timeout
=
max
(
0
,
end
-
time
.
time
())
else
:
logging
.
info
(
"Timeout while querying [%s]:%u"
,
*
address
)
logging
.
info
(
"Timeout while querying address for %s/%s"
,
int
(
peer
,
2
),
len
(
peer
))
return
cert
=
self
.
getCert
(
cn
)
msg
=
"%s %s"
%
(
peer
,
msg
)
logging
.
info
(
"Sending bootstrap peer: %s"
,
msg
)
return
x509
.
encrypt
(
cert
,
msg
)
...
...
@@ -387,59 +396,46 @@ class RegistryServer(object):
while
True
:
r
,
w
,
_
=
select
.
select
(
s
,
s
if
peers
else
(),
(),
3
)
if
r
:
ver
,
address
=
self
.
sock
.
recvfrom
(
1
<<
16
)
address
=
utils
.
binFromIp
(
address
[
0
])
if
(
address
.
startswith
(
self
.
network
)
and
len
(
ver
)
>
1
and
ver
[
0
]
in
'
\
3
\
4
'
# BBB
):
try
:
peer_dict
[
max
(
filter
(
address
[
len
(
self
.
network
):]
.
startswith
,
peer_dict
),
key
=
len
)]
=
ver
[
1
:]
except
ValueError
:
pass
prefix
,
ver
=
self
.
recv
(
4
)
if
prefix
:
peer_dict
[
prefix
]
=
ver
if
w
:
x
=
peers
.
pop
()
peer_dict
[
x
]
=
None
x
=
utils
.
ipFromBin
(
self
.
network
+
x
)
try
:
self
.
sock
.
sendto
(
'
\
3
'
,
(
x
,
tunnel
.
PORT
))
except
socket
.
error
:
pass
prefix
=
peers
.
pop
()
peer_dict
[
prefix
]
=
None
self
.
sendto
(
prefix
,
4
)
elif
not
r
:
break
return
repr
(
peer_dict
)
return
json
.
dumps
(
peer_dict
)
@
rpc
def
topology
(
self
):
p
=
lambda
p
:
'%s/%s'
%
(
int
(
p
,
2
),
len
(
p
))
peers
=
deque
((
p
(
self
.
prefix
),))
graph
=
defaultdict
(
set
)
s
=
self
.
sock
,
with
self
.
lock
:
peers
=
deque
((
'%u/%u'
%
(
int
(
self
.
prefix
,
2
),
len
(
self
.
prefix
)),))
cookie
=
hex
(
random
.
randint
(
0
,
1
<<
32
))[
2
:]
graph
=
dict
.
fromkeys
(
peers
)
s
=
self
.
sock
,
while
True
:
r
,
w
,
_
=
select
.
select
(
s
,
s
if
peers
else
(),
(),
3
)
if
r
:
answer
=
self
.
sock
.
recv
(
1
<<
16
)
if
answer
[
0
]
==
'
\
xfe
'
:
answer
=
answer
[
1
:].
split
(
'
\
n
'
)[:
-
1
]
if
len
(
answer
)
>=
3
and
answer
[
0
]
==
cookie
:
x
=
answer
[
3
:]
assert
answer
[
1
]
not
in
x
,
(
answer
,
graph
)
graph
[
answer
[
1
]]
=
x
[:
int
(
answer
[
2
])]
x
=
set
(
x
).
difference
(
graph
)
peers
+=
x
graph
.
update
(
dict
.
fromkeys
(
x
))
prefix
,
x
=
self
.
recv
(
5
)
if
prefix
and
x
:
prefix
=
p
(
prefix
)
x
=
x
.
split
()
try
:
n
=
int
(
x
.
pop
(
0
))
except
ValueError
:
continue
if
n
<=
len
(
x
)
and
prefix
not
in
x
:
graph
[
prefix
].
update
(
x
[:
n
])
peers
+=
set
(
x
).
difference
(
graph
)
for
x
in
x
[
n
:]:
graph
[
x
].
add
(
prefix
)
graph
[
''
].
add
(
prefix
)
if
w
:
x
=
utils
.
binFromSubnet
(
peers
.
popleft
())
x
=
utils
.
ipFromBin
(
self
.
network
+
x
)
try
:
self
.
sock
.
sendto
(
'
\
xff
%s
\
n
'
%
cookie
,
(
x
,
tunnel
.
PORT
))
except
socket
.
error
:
pass
self
.
sendto
(
utils
.
binFromSubnet
(
peers
.
popleft
()),
5
)
elif
not
r
:
break
return
repr
(
graph
)
return
json
.
dumps
(
dict
((
k
,
list
(
v
))
for
k
,
v
in
graph
.
iteritems
())
)
class
RegistryClient
(
object
):
...
...
re6st/tunnel.py
View file @
a7a86341
This diff is collapsed.
Click to expand it.
re6st/utils.py
View file @
a7a86341
import
argparse
,
errno
,
logging
,
os
,
select
as
_select
,
shlex
,
signal
import
argparse
,
errno
,
hashlib
,
logging
,
os
,
select
as
_select
,
shlex
,
signal
import
socket
,
struct
,
subprocess
,
sys
,
textwrap
,
threading
,
time
,
traceback
HMAC_LEN
=
len
(
hashlib
.
sha1
(
''
).
digest
())
try
:
subprocess
.
CalledProcessError
(
0
,
''
,
''
)
except
TypeError
:
# BBB: Python < 2.7
...
...
@@ -223,3 +226,10 @@ def parse_address(address_list):
def
binFromSubnet
(
subnet
):
p
,
l
=
subnet
.
split
(
'/'
)
return
bin
(
int
(
p
))[
2
:].
rjust
(
int
(
l
),
'0'
)
def
newHmacSecret
():
from
random
import
getrandbits
as
g
pack
=
struct
.
Struct
(
">QQI"
).
pack
assert
len
(
pack
(
0
,
0
,
0
))
==
HMAC_LEN
return
lambda
x
=
None
:
pack
(
g
(
64
)
if
x
is
None
else
x
,
g
(
64
),
g
(
32
))
newHmacSecret
=
newHmacSecret
()
re6st/x509.py
View file @
a7a86341
import
calendar
,
logging
,
os
,
subprocess
,
threading
,
time
# -*- coding: utf-8 -*-
import
calendar
,
hashlib
,
hmac
,
logging
,
os
,
struct
,
subprocess
,
threading
,
time
from
collections
import
deque
from
datetime
import
datetime
from
OpenSSL
import
crypto
from
.
import
utils
def
newHmacSecret
():
x
=
datetime
.
utcnow
()
return
utils
.
newHmacSecret
(
int
(
time
.
mktime
(
x
.
timetuple
()))
*
1000000
+
x
.
microsecond
)
def
networkFromCa
(
ca
):
return
bin
(
ca
.
get_serial_number
())[
3
:]
...
...
@@ -65,9 +73,14 @@ def maybe_renew(path, cert, info, renew):
info
,
exc_info
=
exc_info
)
return
cert
,
time
.
time
()
+
86400
class
VerifyError
(
Exception
):
pass
class
NewSessionError
(
Exception
):
pass
class
Cert
(
object
):
def
__init__
(
self
,
ca
,
key
,
cert
=
None
):
...
...
@@ -105,11 +118,13 @@ class Cert(object):
"CA Certificate"
,
registry
.
getCa
)
return
min
(
next_renew
,
ca_renew
)
def
loadVerify
(
self
,
cert
,
strict
=
False
):
def
loadVerify
(
self
,
cert
,
strict
=
False
,
type
=
crypto
.
FILETYPE_PEM
):
try
:
r
=
crypto
.
load_certificate
(
crypto
.
FILETYPE_PEM
,
cert
)
r
=
crypto
.
load_certificate
(
type
,
cert
)
except
crypto
.
Error
:
raise
VerifyError
(
None
,
None
,
'unable to load certificate'
)
if
type
!=
crypto
.
FILETYPE_PEM
:
cert
=
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
r
)
p
=
openssl
(
'verify'
,
'-CAfile'
,
self
.
ca_path
)
out
,
err
=
p
.
communicate
(
cert
)
if
p
.
returncode
or
strict
:
...
...
@@ -132,3 +147,101 @@ class Cert(object):
if
p
.
returncode
:
raise
subprocess
.
CalledProcessError
(
p
.
returncode
,
'openssl'
,
err
)
return
out
class
Peer
(
object
):
"""
UDP: A ─────────────────────────────────────────────> B
hello0: 0, A
1, fingerprint(B), A
hello: 2, X = E(B)(secret), S(A)(X)
!hello: #, ver, type, value, HMAC(secret)(payload)
└──── payload ────┘
new secret > old secret
(concat timestamp with random bits)
Reject messages with # smaller or equal than previously processed.
Yes, we do UDP on purpose. The only drawbacks are:
- The limited size of packets, but they are big enough for a network
using 4096-bits RSA keys.
- hello0 packets (0 & 1) are subject to DoS, because verifying a
certificate uses much CPU. A solution would be to use TCP until the
secret is exchanged and continue with UDP.
"""
_hello
=
_last
=
0
_key
=
newHmacSecret
()
def
__init__
(
self
,
prefix
):
assert
len
(
prefix
)
==
16
or
prefix
==
(
'0'
*
14
+
'1'
+
'0'
*
65
),
prefix
self
.
prefix
=
prefix
@
property
def
connected
(
self
):
return
self
.
_last
is
None
or
time
.
time
()
<
self
.
_last
+
60
def
__ne__
(
self
,
other
):
raise
AssertionError
__eq__
=
__ge__
=
__le__
=
__ne__
def
__gt__
(
self
,
other
):
return
self
.
prefix
>
(
other
if
type
(
other
)
is
str
else
other
.
prefix
)
def
__lt__
(
self
,
other
):
return
self
.
prefix
<
(
other
if
type
(
other
)
is
str
else
other
.
prefix
)
def
hello0
(
self
,
cert
):
if
self
.
_hello
<
time
.
time
():
try
:
msg
=
'
\
0
\
0
\
0
\
1
'
+
fingerprint
(
self
.
cert
).
digest
()
except
AttributeError
:
msg
=
'
\
0
\
0
\
0
\
0
'
return
msg
+
crypto
.
dump_certificate
(
crypto
.
FILETYPE_ASN1
,
cert
)
def
hello0Sent
(
self
):
self
.
_hello
=
time
.
time
()
+
60
def
hello
(
self
,
cert
):
key
=
self
.
_key
=
newHmacSecret
()
h
=
encrypt
(
crypto
.
dump_certificate
(
crypto
.
FILETYPE_PEM
,
self
.
cert
),
key
)
self
.
_i
=
self
.
_j
=
2
self
.
_last
=
0
return
'
\
0
\
0
\
0
\
2
'
+
h
+
cert
.
sign
(
h
)
def
_hmac
(
self
,
msg
):
return
hmac
.
HMAC
(
self
.
_key
,
msg
,
hashlib
.
sha1
).
digest
()
def
newSession
(
self
,
key
):
if
key
<=
self
.
_key
:
raise
NewSessionError
(
self
.
_key
,
key
)
self
.
_key
=
key
self
.
_i
=
self
.
_j
=
2
self
.
_last
=
None
def
verify
(
self
,
sign
,
data
):
crypto
.
verify
(
self
.
cert
,
sign
,
data
,
'sha1'
)
seqno_struct
=
struct
.
Struct
(
"!L"
)
def
decode
(
self
,
msg
,
_unpack
=
seqno_struct
.
unpack
):
seqno
,
=
_unpack
(
msg
[:
4
])
if
seqno
<=
2
:
return
seqno
,
msg
[
4
:]
i
=
-
utils
.
HMAC_LEN
if
self
.
_hmac
(
msg
[:
i
])
==
msg
[
i
:]
and
self
.
_i
<
seqno
:
self
.
_last
=
None
self
.
_i
=
seqno
return
msg
[
4
:
i
]
def
encode
(
self
,
msg
,
_pack
=
seqno_struct
.
pack
):
self
.
_j
+=
1
msg
=
_pack
(
self
.
_j
)
+
msg
return
msg
+
self
.
_hmac
(
msg
)
del
seqno_struct
def
sent
(
self
):
if
not
self
.
_last
:
self
.
_last
=
time
.
time
()
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment