-
Notifications
You must be signed in to change notification settings - Fork 20
Added basic support for TLS 1.3 using openssl min/max proto version functions #13
base: master
Are you sure you want to change the base?
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -40,13 +40,16 @@ var ( | |
type Conn struct { | ||
*SSL | ||
|
||
conn net.Conn | ||
ctx *Ctx // for gc | ||
into_ssl *readBio | ||
from_ssl *writeBio | ||
is_shutdown bool | ||
mtx sync.Mutex | ||
want_read_future *utils.Future | ||
conn net.Conn | ||
ctx *Ctx // for gc | ||
into_ssl *readBio | ||
from_ssl *writeBio | ||
is_shutdown bool | ||
mtx sync.Mutex | ||
want_read_future *utils.Future | ||
handshake_started bool | ||
handshake_finished bool | ||
handshake_successful bool | ||
} | ||
|
||
type VerifyResult int | ||
|
@@ -142,10 +145,14 @@ func newConn(conn net.Conn, ctx *Ctx) (*Conn, error) { | |
c := &Conn{ | ||
SSL: s, | ||
|
||
conn: conn, | ||
ctx: ctx, | ||
into_ssl: into_ssl, | ||
from_ssl: from_ssl} | ||
conn: conn, | ||
ctx: ctx, | ||
into_ssl: into_ssl, | ||
from_ssl: from_ssl, | ||
handshake_started: false, | ||
handshake_finished: false, | ||
handshake_successful: false, | ||
} | ||
runtime.SetFinalizer(c, func(c *Conn) { | ||
c.into_ssl.Disconnect(into_ssl_cbio) | ||
c.from_ssl.Disconnect(from_ssl_cbio) | ||
|
@@ -303,11 +310,26 @@ func (c *Conn) handshake() func() error { | |
// Handshake performs an SSL handshake. If a handshake is not manually | ||
// triggered, it will run before the first I/O on the encrypted stream. | ||
func (c *Conn) Handshake() error { | ||
c.mtx.Lock() | ||
c.handshake_started = true | ||
c.handshake_finished = false | ||
c.handshake_successful = false | ||
c.mtx.Unlock() | ||
defer func() { | ||
c.mtx.Lock() | ||
c.handshake_finished = true | ||
c.mtx.Unlock() | ||
}() | ||
err := tryAgain | ||
for err == tryAgain { | ||
err = c.handleError(c.handshake()) | ||
} | ||
go c.flushOutputBuffer() | ||
if err == nil { | ||
c.mtx.Lock() | ||
c.handshake_successful = true | ||
c.mtx.Unlock() | ||
} | ||
return err | ||
} | ||
|
||
|
@@ -383,6 +405,16 @@ func (c *Conn) shutdown() func() error { | |
defer c.mtx.Unlock() | ||
runtime.LockOSThread() | ||
defer runtime.UnlockOSThread() | ||
timed_out := false | ||
time.AfterFunc(300*time.Millisecond, func() { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Where does this value come from? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marten-seemann It's a local variable meant to prevent an infinite loop. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. First, I don't understand where an infinite loop would come from (I don't understand the whole Second, starting a timer here is definitely not the solution. 300 ms seem to be an arbitrary value, and it will just cause problems later on. Furthermore, this code is racy anyway (you're writing to and reading from There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. @marten-seemann The point of the timeout loop is that it prevents a connection being closed while a handshake is in progress, which occurs in the C portion of the OpenSSL library. I consistently run into that error in my libp2p-tls PR. I can remove this from this PR for now, but it will still be necessary I think. Also, I can't test how well this PR helps with libp2p integration until this is merged. Assuming that Go booleans are equivalent to C booleans once compiled, they are atomic (modified and read in 1 instruction) and thus don't actually have any race conditions. |
||
timed_out = true | ||
}) | ||
for !timed_out && c.handshake_started && !c.handshake_finished { | ||
c.mtx.Unlock() | ||
runtime.UnlockOSThread() | ||
c.mtx.Lock() | ||
runtime.LockOSThread() | ||
} | ||
rv, errno := C.SSL_shutdown(c.ssl) | ||
if rv > 0 { | ||
return nil | ||
|
Original file line number | Diff line number | Diff line change |
---|---|---|
|
@@ -95,7 +95,7 @@ func NewCtxWithVersion(version SSLVersion) (*Ctx, error) { | |
case TLSv1_2: | ||
method = C.X_TLSv1_2_method() | ||
case AnyVersion: | ||
method = C.X_SSLv23_method() | ||
method = C.X_TLS_method() | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Why is here no case for There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Using the method parameter is deprecated in openssl now, so there is no TLSv1_3. That's why I added the min and max proto functions. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think we'll need to build an abstraction around that then, i.e. we shouldn't have a
The first option probably allows for more flexibility (as you can set min and max separately), whereas What do you think? There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I think the real question is what is the policy for deprecated functions in OpenSSL? I've seen in some other Go wrapper libraries that by default they don't include deprecated functions but have a tag to enable them at compile time. I can see implementing something before the next version, but that's a PR in itself. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. As a general rule, I'd try to avoid deprecated functions, if possible. After all, they're deprecated for a reason. My point in my previous comment was that we should abstract away that complexity from users of |
||
} | ||
if method == nil { | ||
return nil, errors.New("unknown ssl/tls version") | ||
|
@@ -361,6 +361,36 @@ func (c *Ctx) LoadVerifyLocations(ca_file string, ca_path string) error { | |
return nil | ||
} | ||
|
||
type Version int | ||
|
||
const ( | ||
SSL3_VERSION Version = C.SSL3_VERSION | ||
TLS1_VERSION Version = C.TLS1_VERSION | ||
TLS1_1_VERSION Version = C.TLS1_1_VERSION | ||
TLS1_2_VERSION Version = C.TLS1_2_VERSION | ||
TLS1_3_VERSION Version = C.TLS1_3_VERSION | ||
DTLS1_VERSION Version = C.DTLS1_VERSION | ||
DTLS1_2_VERSION Version = C.DTLS1_2_VERSION | ||
) | ||
|
||
func (c *Ctx) SetMinProtoVersion(version Version) bool { | ||
return C.X_SSL_CTX_set_min_proto_version( | ||
c.ctx, C.int(version)) == 1 | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Under which circumstances can this fail? I wouldn't expect a return value on a setter function. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. Not sure how it could fail. I'm just passing along the value from the openssl C library. |
||
} | ||
|
||
func (c *Ctx) SetMaxProtoVersion(version Version) bool { | ||
return C.X_SSL_CTX_set_max_proto_version( | ||
c.ctx, C.int(version)) == 1 | ||
} | ||
|
||
func (c *Ctx) GetMinProtoVersion() Version { | ||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. What do we need these getter functions for? This value was set by the user before, so it seems like there's no need for it? |
||
return Version(C.X_SSL_CTX_get_min_proto_version(c.ctx)) | ||
} | ||
|
||
func (c *Ctx) GetMaxProtoVersion() Version { | ||
return Version(C.X_SSL_CTX_get_max_proto_version(c.ctx)) | ||
} | ||
|
||
type Options int | ||
|
||
const ( | ||
|
Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||
---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
|
@@ -191,10 +191,11 @@ func TestGenerateEd25519(t *testing.T) { | |||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||
t.Fatal(err) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
_, err = key.MarshalPKCS1PrivateKeyPEM() | ||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||
t.Fatal(err) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
// FIXME | ||||||||||||||||||||||||||||||
//_, err = key.MarshalPKCS1PrivateKeyPEM() | ||||||||||||||||||||||||||||||
//if err != nil { | ||||||||||||||||||||||||||||||
// t.Fatal(err) | ||||||||||||||||||||||||||||||
//} | ||||||||||||||||||||||||||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This should probably be fixed. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. This is unrelated to TLS 1.3 so I'm just suppressing the error for now. I think the function is also deprecated. I'm sure if you the tests without this commit you'll see the function fails there. It doesn't seem to affect overall functionality of the go-openssl library. There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I don't see any deprecation in the interface: Lines 97 to 110 in 6f65c2c
I can confirm that the test is failing on my local machine though, and I think this should be fixed in a separate PR (with a proper fix, not a FIXME). There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. I'll remove the FIXMEs for now. |
||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
func TestSign(t *testing.T) { | ||||||||||||||||||||||||||||||
|
@@ -435,10 +436,11 @@ func TestMarshalEd25519(t *testing.T) { | |||||||||||||||||||||||||||||
t.Fatal("invalid cert pem bytes") | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
pem, err = key.MarshalPKCS1PrivateKeyPEM() | ||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||
t.Fatal(err) | ||||||||||||||||||||||||||||||
} | ||||||||||||||||||||||||||||||
// FIXME | ||||||||||||||||||||||||||||||
//pem, err = key.MarshalPKCS1PrivateKeyPEM() | ||||||||||||||||||||||||||||||
//if err != nil { | ||||||||||||||||||||||||||||||
// t.Fatal(err) | ||||||||||||||||||||||||||||||
//} | ||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||
der, err := key.MarshalPKCS1PrivateKeyDER() | ||||||||||||||||||||||||||||||
if err != nil { | ||||||||||||||||||||||||||||||
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Not sure I understand these changes. Why do you need those state bools?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In the go-libp2p-tls test, the handshake tests seem to fail because the tls connection is still being initialize when it is requested to be closed. This should hopefully fix it. But the only way to test if it works is adding it to the go-openssl repo. If the test results aren't promising then we can remove these.