Skip to content

Commit be6b65b

Browse files
committed
Add bindings for DANE and handshake tracing.
1 parent 576e43d commit be6b65b

11 files changed

+369
-2
lines changed

.gitignore

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1 +1,4 @@
11
openssl.test
2+
/.ccls-cache/
3+
/.ccls
4+
/.idea/

CHANGELOG.md

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,12 @@ Versioning](http://semver.org/spec/v2.0.0.html) except to the first release.
1111

1212
### Added
1313

14+
- Bindings for [DANE](https://docs.openssl.org/1.1.1/man3/SSL_CTX_dane_enable/).
15+
- Bindings for [TLS handshake tracing](https://docs.openssl.org/master/man3/SSL_CTX_set_msg_callback/).
16+
- Bindings for `X509_digest()`.
17+
- Bindings for `X509_verify_cert_error_string()`.
18+
- Bindings for `SSL_get_version()`.
19+
1420
### Changed
1521

1622
### Fixed

README.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@ Forked from https://github.com/libp2p/openssl (unmaintained) to add:
66
2. Fix build on Apple M1.
77
3. Fix static build.
88
4. Fix error extraction on key reading.
9+
5. Bindings for DANE.
910

1011
### License
1112

cert.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -430,3 +430,27 @@ func (c *Certificate) GetExtensionValue(nid NID) []byte {
430430
val := C.get_extention(c.x, C.int(nid), &dataLength)
431431
return C.GoBytes(unsafe.Pointer(val), dataLength)
432432
}
433+
434+
// Hash uses the given digest to generate a hash of the certificate. Use GetDigestByName
435+
// to get a digest.
436+
func (c *Certificate) Hash(digest *Digest) []byte {
437+
var hashLength C.uint
438+
hash := make([]byte, C.EVP_MAX_MD_SIZE)
439+
440+
C.X509_digest(c.x, digest.ptr, (*C.uchar)(unsafe.Pointer(&hash[0])), &hashLength)
441+
442+
return hash[:hashLength]
443+
}
444+
445+
// VerifyCertErrorString returns a human-readable error string for the given verification error.
446+
// https://www.openssl.org/docs/man3.1/man3/X509_verify_cert_error_string.html
447+
func VerifyCertErrorString(result VerifyResult) string {
448+
// Locking the thread because the docs say:
449+
// If an unrecognised error code is passed to X509_verify_cert_error_string() the
450+
// numerical value of the unknown code is returned in a static buffer. This is not
451+
// thread safe but will never happen unless an invalid code is passed.
452+
runtime.LockOSThread()
453+
defer runtime.UnlockOSThread()
454+
455+
return C.GoString(C.X509_verify_cert_error_string(C.long(result)))
456+
}

cert_test.go

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@
1515
package openssl
1616

1717
import (
18+
"encoding/hex"
1819
"math/big"
1920
"testing"
2021
"time"
@@ -162,3 +163,26 @@ func TestCertVersion(t *testing.T) {
162163
t.Fatalf("bad version: %d", vers)
163164
}
164165
}
166+
167+
func TestCertHash(t *testing.T) {
168+
cert, err := LoadCertificateFromPEM(certBytes)
169+
if err != nil {
170+
t.Fatal(err)
171+
}
172+
173+
digest, err := GetDigestByName("SHA256")
174+
if err != nil {
175+
t.Fatal(err)
176+
}
177+
178+
if hash := hex.EncodeToString(cert.Hash(digest)); hash != certHashHex {
179+
t.Fatalf("Wrong hash returned, expected %q, got %q", certHashHex, hash)
180+
}
181+
}
182+
183+
func TestVerifyCertErrorString(t *testing.T) {
184+
expected := "unable to get issuer certificate"
185+
if result := VerifyCertErrorString(UnableToGetIssuerCert); result != expected {
186+
t.Fatalf("Wrong cert error string returned, expected %q, got %q", expected, result)
187+
}
188+
}

ctx.go

Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -616,3 +616,35 @@ func (c *Ctx) SessSetCacheSize(t int) int {
616616
func (c *Ctx) SessGetCacheSize() int {
617617
return int(C.X_SSL_CTX_sess_get_cache_size(c.ctx))
618618
}
619+
620+
// DaneEnable initializes shared state required for DANE support. Must be
621+
// called before any other Dane function.
622+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
623+
func (c *Ctx) DaneEnable() error {
624+
runtime.LockOSThread()
625+
defer runtime.UnlockOSThread()
626+
627+
if C.SSL_CTX_dane_enable(c.ctx) <= 0 {
628+
return errorFromErrorQueue()
629+
}
630+
631+
return nil
632+
}
633+
634+
type DaneFlags int
635+
636+
const (
637+
DaneFlagNoDaneEeNamechecks DaneFlags = C.DANE_FLAG_NO_DANE_EE_NAMECHECKS
638+
)
639+
640+
// DaneSetFlags enables the default flags of every connection associated
641+
// with this context. See DaneFlag for available flags.
642+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
643+
func (c *Ctx) DaneSetFlags(flags DaneFlags) DaneFlags {
644+
return DaneFlags(C.SSL_CTX_dane_set_flags(c.ctx, C.ulong(flags)))
645+
}
646+
647+
// DaneClearFlags disables flags set by DaneSetFlags.
648+
func (c *Ctx) DaneClearFlags(flags DaneFlags) DaneFlags {
649+
return DaneFlags(C.SSL_CTX_dane_clear_flags(c.ctx, C.ulong(flags)))
650+
}

digest.go

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -28,7 +28,8 @@ type Digest struct {
2828
}
2929

3030
// GetDigestByName returns the Digest with the name or nil and an error if the
31-
// digest was not found.
31+
// digest was not found. Use `openssl list -digest-algorithms` to list available
32+
// digest names.
3233
func GetDigestByName(name string) (*Digest, error) {
3334
cname := C.CString(name)
3435
defer C.free(unsafe.Pointer(cname))

shim.c

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -439,6 +439,16 @@ int X_SSL_verify_cb(int ok, X509_STORE_CTX* store) {
439439
return go_ssl_verify_cb_thunk(p, ok, store);
440440
}
441441

442+
void X_SSL_toggle_tracing(SSL* ssl, FILE* output, short enable) {
443+
if (enable) {
444+
SSL_set_msg_callback(ssl, SSL_trace);
445+
SSL_set_msg_callback_arg(ssl, BIO_new_fp(output, BIO_NOCLOSE));
446+
} else {
447+
SSL_set_msg_callback(ssl, NULL);
448+
SSL_set_msg_callback_arg(ssl, NULL);
449+
}
450+
}
451+
442452
const SSL_METHOD *X_SSLv23_method() {
443453
return SSLv23_method();
444454
}

shim.h

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -17,6 +17,7 @@
1717

1818
#include <stdlib.h>
1919
#include <string.h>
20+
#include <stdio.h>
2021

2122
#include <openssl/bio.h>
2223
#include <openssl/crypto.h>
@@ -53,6 +54,7 @@ extern long X_SSL_set_tlsext_host_name(SSL *ssl, const char *name);
5354
extern const char * X_SSL_get_cipher_name(const SSL *ssl);
5455
extern int X_SSL_session_reused(SSL *ssl);
5556
extern int X_SSL_new_index();
57+
extern void X_SSL_toggle_tracing(SSL* ssl, FILE* output, short enable);
5658

5759
extern const SSL_METHOD *X_SSLv23_method();
5860
extern const SSL_METHOD *X_SSLv3_method();

ssl.go

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,6 +19,7 @@ import "C"
1919

2020
import (
2121
"os"
22+
"runtime"
2223
"unsafe"
2324

2425
"github.com/mattn/go-pointer"
@@ -92,6 +93,23 @@ func (s *SSL) ClearOptions(options Options) Options {
9293
return Options(C.X_SSL_clear_options(s.ssl, C.long(options)))
9394
}
9495

96+
// EnableTracing enables TLS handshake tracing using openssls
97+
// SSL_trace function. If useStderr is false, stdout is used.
98+
// https://www.openssl.org/docs/manmaster/man3/SSL_trace.html
99+
func (s *SSL) EnableTracing(useStderr bool) {
100+
output := C.stdout
101+
if useStderr {
102+
output = C.stderr
103+
}
104+
105+
C.X_SSL_toggle_tracing(s.ssl, output, 1)
106+
}
107+
108+
// DisableTracing unsets the msg callback from EnableTracing.
109+
func (s *SSL) DisableTracing() {
110+
C.X_SSL_toggle_tracing(s.ssl, nil, 0)
111+
}
112+
95113
// SetVerify controls peer verification settings. See
96114
// http://www.openssl.org/docs/ssl/SSL_CTX_set_verify.html
97115
func (s *SSL) SetVerify(options VerifyOptions, verify_cb VerifyCallback) {
@@ -152,6 +170,79 @@ func (s *SSL) SetSSLCtx(ctx *Ctx) {
152170
C.SSL_set_SSL_CTX(s.ssl, ctx.ctx)
153171
}
154172

173+
// GetVersion() returns the name of the protocol used for the connection. It
174+
// should only be called after the initial handshake has been completed otherwise
175+
// the result may be unreliable.
176+
// https://www.openssl.org/docs/man1.0.2/man3/SSL_get_version.html
177+
func (s *SSL) GetVersion() string {
178+
return C.GoString(C.SSL_get_version(s.ssl))
179+
}
180+
181+
// DaneEnable enables DANE validation for this connection. It must be called
182+
// before the TLS handshake.
183+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
184+
func (s *SSL) DaneEnable(tlsaBaseDomain string) error {
185+
tlsaBaseDomainCString := C.CString(tlsaBaseDomain)
186+
defer C.free(unsafe.Pointer(tlsaBaseDomainCString))
187+
188+
runtime.LockOSThread()
189+
defer runtime.UnlockOSThread()
190+
191+
if C.SSL_dane_enable(s.ssl, tlsaBaseDomainCString) <= 0 {
192+
return errorFromErrorQueue()
193+
}
194+
195+
return nil
196+
}
197+
198+
// DaneTlsaAdd loads a TLSA record that will be validated against the presented certificate.
199+
// Data must be in wire form, not hex ASCII. If all TLSA records you try to add are unusable
200+
// (bool return value) an opportunistic application must disable peer authentication by
201+
// using a verify mode equal to VerifyNone.
202+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
203+
func (s *SSL) DaneTlsaAdd(usage, selector, matchingType byte, data []byte) (bool, error) {
204+
cData := C.CBytes(data)
205+
defer C.free(cData)
206+
207+
runtime.LockOSThread()
208+
defer runtime.UnlockOSThread()
209+
210+
if status := C.SSL_dane_tlsa_add(
211+
s.ssl,
212+
C.uchar(usage),
213+
C.uchar(selector),
214+
C.uchar(matchingType),
215+
(*C.uchar)(cData),
216+
C.size_t(len(data)),
217+
); status < 0 {
218+
return false, errorFromErrorQueue()
219+
} else if status == 0 {
220+
return false, nil
221+
}
222+
return true, nil
223+
}
224+
225+
// DaneGet0DaneAuthority returns a value that is negative if DANE verification failed (or
226+
// was not enabled), 0 if an EE TLSA record directly matched the leaf certificate, or a
227+
// positive number indicating the depth at which a TA record matched an issuer certificate.
228+
// However, the depth doesn't refer to the list of certificates as sent by the peer but rather
229+
// how it's returned from SSL_get0_verified_chain.
230+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
231+
func (s *SSL) DaneGet0DaneAuthority() int {
232+
return int(C.SSL_get0_dane_authority(s.ssl, nil, nil))
233+
}
234+
235+
// DaneSetFlags enables given flags for this connection. Returns previous flags.
236+
// https://www.openssl.org/docs/man1.1.1/man3/SSL_dane_clear_flags.html
237+
func (s *SSL) DaneSetFlags(flags DaneFlags) DaneFlags {
238+
return DaneFlags(C.SSL_dane_set_flags(s.ssl, C.ulong(flags)))
239+
}
240+
241+
// DaneClearFlags disables flags set by DaneSetFlags. Returns previous flags.
242+
func (s *SSL) DaneClearFlags(flags DaneFlags) DaneFlags {
243+
return DaneFlags(C.SSL_dane_clear_flags(s.ssl, C.ulong(flags)))
244+
}
245+
155246
//export sni_cb_thunk
156247
func sni_cb_thunk(p unsafe.Pointer, con *C.SSL, ad unsafe.Pointer, arg unsafe.Pointer) C.int {
157248
defer func() {

0 commit comments

Comments
 (0)