make client authentication mandatory; verify cert chain using highest cert as CA cert
This commit is contained in:
parent
f7feb4283f
commit
de780656f0
92
quicp2p.nim
92
quicp2p.nim
|
@ -16,25 +16,43 @@ import picotls/openssl as ptls_openssl
|
||||||
import strformat
|
import strformat
|
||||||
|
|
||||||
from nativesockets import SockAddr, Sockaddr_storage, SockLen, getHostByName
|
from nativesockets import SockAddr, Sockaddr_storage, SockLen, getHostByName
|
||||||
from openssl import DLLSSLName, EVP_PKEY
|
|
||||||
from posix import IOVec
|
from posix import IOVec
|
||||||
from strutils import parseUInt
|
from strutils import parseUInt
|
||||||
|
|
||||||
|
from openssl import
|
||||||
|
DLLSSLName,
|
||||||
|
EVP_PKEY,
|
||||||
|
SslPtr,
|
||||||
|
PX509,
|
||||||
|
PX509_STORE,
|
||||||
|
X509_STORE_new,
|
||||||
|
X509_STORE_free,
|
||||||
|
X509_STORE_add_cert,
|
||||||
|
PSTACK,
|
||||||
|
d2i_X509
|
||||||
|
|
||||||
const serverCertChainPath = "./certs/server-certchain.pem"
|
const serverCertChainPath = "./certs/server-certchain.pem"
|
||||||
const serverKeyPath = "./certs/server-cert.key"
|
const serverKeyPath = "./certs/server-cert.key"
|
||||||
const clientCertChainPath = "./certs/server-certchain.pem"
|
const clientCertChainPath = "./certs/server-certchain.pem"
|
||||||
const clientKeyPath = "./certs/server-cert.key"
|
const clientKeyPath = "./certs/server-cert.key"
|
||||||
|
|
||||||
|
const X509_V_FLAG_CHECK_SS_SIGNATURE = 0x00004000
|
||||||
|
|
||||||
type
|
type
|
||||||
QuicP2PContext = ref object
|
QuicP2PContext = ref object
|
||||||
sock: AsyncSocket
|
sock: AsyncSocket
|
||||||
streamOpen: quicly_stream_open_t
|
streamOpen: quicly_stream_open_t
|
||||||
nextCid: quicly_cid_plaintext_t
|
nextCid: quicly_cid_plaintext_t
|
||||||
signCertificate: ptls_openssl_sign_certificate_t
|
signCertCb: ptls_openssl_sign_certificate_t
|
||||||
|
verifyCertsCb: ptls_verify_certificate_t
|
||||||
tlsCtx: ptls_context_t
|
tlsCtx: ptls_context_t
|
||||||
quiclyCtx: quicly_context_t
|
quiclyCtx: quicly_context_t
|
||||||
connections: seq[ptr quicly_conn_t]
|
connections: seq[ptr quicly_conn_t]
|
||||||
|
|
||||||
|
PX509_STORE_CTX = SslPtr
|
||||||
|
|
||||||
|
PX509_VERIFY_PARAM = SslPtr
|
||||||
|
|
||||||
proc PEM_read_PrivateKey(fp: File, x: ptr EVP_PKEY,
|
proc PEM_read_PrivateKey(fp: File, x: ptr EVP_PKEY,
|
||||||
cb: proc(buf: cstring, size: cint, rwflag: cint, u: pointer): cint {.cdecl.},
|
cb: proc(buf: cstring, size: cint, rwflag: cint, u: pointer): cint {.cdecl.},
|
||||||
u: pointer): EVP_PKEY
|
u: pointer): EVP_PKEY
|
||||||
|
@ -42,6 +60,31 @@ proc PEM_read_PrivateKey(fp: File, x: ptr EVP_PKEY,
|
||||||
|
|
||||||
proc EVP_PKEY_free(key: EVP_PKEY) {.importc, dynlib: DLLSSLName, cdecl.}
|
proc EVP_PKEY_free(key: EVP_PKEY) {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_free(a: PX509) {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_new(): PX509_STORE_CTX
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_free(ctx: PX509_STORE_CTX)
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_init(ctx: PX509_STORE_CTX, store: PX509_STORE, x509: PX509,
|
||||||
|
chain: PSTACK): cint
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_get0_param(ctx: PX509_STORE_CTX): PX509_VERIFY_PARAM
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_VERIFY_PARAM_get_flags(param: PX509_VERIFY_PARAM): culong
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_VERIFY_PARAM_set_flags(param: PX509_VERIFY_PARAM,
|
||||||
|
flags: culong): int
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_verify_cert(ctx: PX509_STORE_CTX): int
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
proc getRelativeTimeout(ctx: QuicP2PContext): int32 =
|
proc getRelativeTimeout(ctx: QuicP2PContext): int32 =
|
||||||
## Obtain the absolute int64 timeout from quicly and convert it to the
|
## Obtain the absolute int64 timeout from quicly and convert it to the
|
||||||
## relative int32 timeout expected by poll.
|
## relative int32 timeout expected by poll.
|
||||||
|
@ -122,10 +165,46 @@ proc onClientStreamOpen(self: ptr quicly_stream_open_t,
|
||||||
let msg = "hello server"
|
let msg = "hello server"
|
||||||
discard quicly_streambuf_egress_write(stream, msg.cstring, msg.len().csize_t)
|
discard quicly_streambuf_egress_write(stream, msg.cstring, msg.len().csize_t)
|
||||||
|
|
||||||
|
proc verifyCACertSignature(cert: PX509): bool =
|
||||||
|
let ctx = X509_STORE_CTX_new()
|
||||||
|
let store = X509_STORE_new()
|
||||||
|
discard X509_STORE_add_cert(store, cert)
|
||||||
|
discard X509_STORE_CTX_init(ctx, store, cert, nil)
|
||||||
|
let verifyParams = X509_STORE_CTX_get0_param(ctx)
|
||||||
|
let flags = X509_VERIFY_PARAM_get_flags(verifyParams)
|
||||||
|
discard X509_VERIFY_PARAM_set_flags(verifyParams, flags or X509_V_FLAG_CHECK_SS_SIGNATURE)
|
||||||
|
let verifyResult = X509_verify_cert(ctx)
|
||||||
|
result = verifyResult == 1
|
||||||
|
X509_STORE_CTX_free(ctx)
|
||||||
|
X509_STORE_free(store)
|
||||||
|
|
||||||
|
proc verifyCerts(self: ptr ptls_verify_certificate_t, tls: ptr ptls_t,
|
||||||
|
verify_sign: ptr VerifySignCb, verify_data: ptr pointer,
|
||||||
|
certs: ptr ptls_iovec_t, num_certs: csize_t): cint {.cdecl.} =
|
||||||
|
if num_certs != 2:
|
||||||
|
return PTLS_ALERT_UNKNOWN_CA
|
||||||
|
# parse the highest certificate and use it as CA certificate
|
||||||
|
var iovec = cast[ptr ptls_iovec_t](cast[ByteAddress](certs) + sizeof(ptls_iovec_t))
|
||||||
|
var iovecBase = iovec.base
|
||||||
|
let caCert = d2i_X509(nil, cast[ptr ptr cuchar](addr iovecBase),
|
||||||
|
iovec.len.cint)
|
||||||
|
if caCert.isNil:
|
||||||
|
return PTLS_ALERT_BAD_CERTIFICATE
|
||||||
|
if not verifyCACertSignature(caCert):
|
||||||
|
return PTLS_ALERT_BAD_CERTIFICATE
|
||||||
|
let store = X509_STORE_new()
|
||||||
|
discard X509_STORE_add_cert(store, caCert)
|
||||||
|
var opensslVerifier: ptls_openssl_verify_certificate_t
|
||||||
|
discard ptls_openssl_init_verify_certificate(addr opensslVerifier, store)
|
||||||
|
result = opensslVerifier.super.cb(addr opensslVerifier.super, tls,
|
||||||
|
verify_sign, verify_data, certs, num_certs)
|
||||||
|
ptls_openssl_dispose_verify_certificate(addr opensslVerifier)
|
||||||
|
X509_STORE_free(store)
|
||||||
|
X509_free(caCert)
|
||||||
|
|
||||||
proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
||||||
streamOpenCb: typeof(quicly_stream_open_t.cb)):
|
streamOpenCb: typeof(quicly_stream_open_t.cb)):
|
||||||
QuicP2PContext =
|
QuicP2PContext =
|
||||||
|
|
||||||
var tlsCtx = ptls_context_t(randomBytes: ptls_openssl_random_bytes,
|
var tlsCtx = ptls_context_t(randomBytes: ptls_openssl_random_bytes,
|
||||||
getTime: addr ptls_get_time,
|
getTime: addr ptls_get_time,
|
||||||
keyExchanges: ptls_openssl_key_exchanges,
|
keyExchanges: ptls_openssl_key_exchanges,
|
||||||
|
@ -133,9 +212,11 @@ proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
||||||
quicly_amend_ptls_context(addr tlsCtx)
|
quicly_amend_ptls_context(addr tlsCtx)
|
||||||
result = QuicP2PContext(sock: sock,
|
result = QuicP2PContext(sock: sock,
|
||||||
streamOpen: quicly_stream_open_t(cb: streamOpenCb),
|
streamOpen: quicly_stream_open_t(cb: streamOpenCb),
|
||||||
|
verifyCertsCb: ptls_verify_certificate_t(cb: verifyCerts),
|
||||||
tlsCtx: tlsCtx, quiclyCtx: quicly_spec_context)
|
tlsCtx: tlsCtx, quiclyCtx: quicly_spec_context)
|
||||||
result.quiclyCtx.tls = addr result.tlsCtx
|
result.quiclyCtx.tls = addr result.tlsCtx
|
||||||
result.quiclyCtx.stream_open = addr result.streamOpen
|
result.quiclyCtx.stream_open = addr result.streamOpen
|
||||||
|
result.tlsCtx.verify_certificate = addr result.verifyCertsCb
|
||||||
if ptls_load_certificates(addr result.tlsCtx, certChainPath.cstring) != 0:
|
if ptls_load_certificates(addr result.tlsCtx, certChainPath.cstring) != 0:
|
||||||
raise newException(ValueError, &"cannot load certificate chain {certChainPath}")
|
raise newException(ValueError, &"cannot load certificate chain {certChainPath}")
|
||||||
let pKeyFile = open(keyPath)
|
let pKeyFile = open(keyPath)
|
||||||
|
@ -143,9 +224,9 @@ proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
||||||
pkeyFile.close()
|
pkeyFile.close()
|
||||||
if privateKey == nil:
|
if privateKey == nil:
|
||||||
raise newException(ValueError, &"cannot load private key {keyPath}")
|
raise newException(ValueError, &"cannot load private key {keyPath}")
|
||||||
discard ptls_openssl_init_sign_certificate(addr result.signCertificate, privateKey)
|
discard ptls_openssl_init_sign_certificate(addr result.signCertCb, privateKey)
|
||||||
EVP_PKEY_free(privateKey)
|
EVP_PKEY_free(privateKey)
|
||||||
result.tlsCtx.signCertificate = addr result.signCertificate.super
|
result.tlsCtx.sign_certificate = addr result.signCertCb.super
|
||||||
|
|
||||||
proc sendPackets(ctx: QuicP2PContext) =
|
proc sendPackets(ctx: QuicP2PContext) =
|
||||||
if ctx.connections.len == 0:
|
if ctx.connections.len == 0:
|
||||||
|
@ -222,6 +303,7 @@ proc main() =
|
||||||
sock.bindAddr(Port(portNumber))
|
sock.bindAddr(Port(portNumber))
|
||||||
ctx = initContext(sock, serverCertChainPath, serverKeyPath,
|
ctx = initContext(sock, serverCertChainPath, serverKeyPath,
|
||||||
onServerStreamOpen)
|
onServerStreamOpen)
|
||||||
|
ctx.tlsCtx.require_client_authentication = 1
|
||||||
asyncCheck receive(ctx, true)
|
asyncCheck receive(ctx, true)
|
||||||
|
|
||||||
of 2:
|
of 2:
|
||||||
|
|
Loading…
Reference in New Issue