introduce Connection type to store the peer's certchain; move openssl wrappers to dedicated module; add certificate utils
This commit is contained in:
parent
8441c02a57
commit
3f13c06a9f
|
@ -0,0 +1,29 @@
|
||||||
|
from posix import Tm, mktime
|
||||||
|
import
|
||||||
|
openssl,
|
||||||
|
openssl_additional,
|
||||||
|
times
|
||||||
|
|
||||||
|
type
|
||||||
|
Certificate* = string
|
||||||
|
|
||||||
|
proc getPublicKey*(cert: Certificate): string =
|
||||||
|
let x509 = d2i_X509(cert)
|
||||||
|
let pubKey = X509_get0_pubkey_bitstr(x509)
|
||||||
|
let pubKeyLen = ASN1_STRING_length(pubKey)
|
||||||
|
result = newString(pubKeyLen)
|
||||||
|
copyMem(addr result[0], ASN1_STRING_get0_data(pubKey), pubKeyLen)
|
||||||
|
X509_free(x509)
|
||||||
|
|
||||||
|
proc getValidityPeriod*(cert: Certificate): tuple[notBefore: Time, notAfter: Time] =
|
||||||
|
let x509 = d2i_X509(cert)
|
||||||
|
let notBeforeAsn1 = X509_get0_notBefore(x509)
|
||||||
|
let notAfterAsn1 = X509_get0_notAfter(x509)
|
||||||
|
var notBeforeTm, notAfterTm: Tm
|
||||||
|
discard ASN1_TIME_to_tm(notBeforeAsn1, addr notBeforeTm)
|
||||||
|
discard ASN1_TIME_to_tm(notAfterAsn1, addr notAfterTm)
|
||||||
|
let notBeforeUnix = cast[int64](mktime(notBeforeTm))
|
||||||
|
let notAfterUnix = cast[int64](mktime(notAfterTm))
|
||||||
|
result = (fromUnix(notBeforeUnix), fromUnix(notAfterUnix))
|
||||||
|
X509_free(x509)
|
||||||
|
|
|
@ -0,0 +1,57 @@
|
||||||
|
import openssl
|
||||||
|
from posix import Tm
|
||||||
|
|
||||||
|
const
|
||||||
|
X509_V_FLAG_CHECK_SS_SIGNATURE* = 0x00004000
|
||||||
|
|
||||||
|
type
|
||||||
|
PASN1_STRING* = SslPtr
|
||||||
|
PASN1_BIT_STRING* = PASN1_STRING
|
||||||
|
PASN1_TIME* = PASN1_STRING
|
||||||
|
PASN1_INTEGER* = PASN1_STRING
|
||||||
|
PX509_STORE_CTX* = SslPtr
|
||||||
|
PX509_VERIFY_PARAM* = SslPtr
|
||||||
|
|
||||||
|
proc X509_free*(a: PX509) {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_get0_pubkey_bitstr*(x: PX509): PASN1_BIT_STRING {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_get0_notBefore*(x: PX509): PASN1_TIME {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_get0_notAfter*(x: PX509): PASN1_TIME {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc ASN1_STRING_length*(x: PASN1_STRING): int {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc ASN1_STRING_get0_data*(x: PASN1_STRING): ptr cuchar {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc ASN1_TIME_to_tm*(s: PASN1_TIME, tm: ptr Tm): int {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_verify_cert*(ctx: PX509_STORE_CTX): int {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_get0_untrusted*(ctx: PX509_STORE_CTX): PSTACK {.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc X509_STORE_CTX_get0_store*(ctx: PX509_STORE_CTX): PX509_STORE {.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 PEM_read_PrivateKey*(fp: File, x: ptr EVP_PKEY,
|
||||||
|
cb: proc(buf: cstring, size: cint, rwflag: cint, u: pointer): cint {.cdecl.},
|
||||||
|
u: pointer): EVP_PKEY
|
||||||
|
{.importc, dynlib: DLLSSLName, cdecl.}
|
||||||
|
|
||||||
|
proc EVP_PKEY_free*(key: EVP_PKEY) {.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.}
|
99
quicp2p.nim
99
quicp2p.nim
|
@ -2,8 +2,12 @@
|
||||||
|
|
||||||
import asyncdispatch
|
import asyncdispatch
|
||||||
import asyncnet
|
import asyncnet
|
||||||
|
import certificate
|
||||||
import net
|
import net
|
||||||
import os
|
import os
|
||||||
|
import openssl_additional
|
||||||
|
import picotls/picotls
|
||||||
|
import picotls/openssl as ptls_openssl
|
||||||
import quicly/quicly
|
import quicly/quicly
|
||||||
import quicly/cid
|
import quicly/cid
|
||||||
import quicly/constants
|
import quicly/constants
|
||||||
|
@ -11,9 +15,8 @@ import quicly/defaults
|
||||||
import quicly/recvstate
|
import quicly/recvstate
|
||||||
import quicly/sendstate
|
import quicly/sendstate
|
||||||
import quicly/streambuf
|
import quicly/streambuf
|
||||||
import picotls/picotls
|
|
||||||
import picotls/openssl as ptls_openssl
|
|
||||||
import strformat
|
import strformat
|
||||||
|
import strutils
|
||||||
|
|
||||||
from nativesockets import SockAddr, Sockaddr_storage, SockLen, getHostByName
|
from nativesockets import SockAddr, Sockaddr_storage, SockLen, getHostByName
|
||||||
from posix import IOVec
|
from posix import IOVec
|
||||||
|
@ -33,12 +36,14 @@ from openssl import
|
||||||
|
|
||||||
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/client-certchain.pem"
|
||||||
const clientKeyPath = "./certs/server-cert.key"
|
const clientKeyPath = "./certs/client-cert.key"
|
||||||
|
|
||||||
const X509_V_FLAG_CHECK_SS_SIGNATURE = 0x00004000
|
|
||||||
|
|
||||||
type
|
type
|
||||||
|
Connection = ref object
|
||||||
|
conn: ptr quicly_conn_t
|
||||||
|
certs: seq[Certificate]
|
||||||
|
|
||||||
QuicP2PContext = ref object
|
QuicP2PContext = ref object
|
||||||
sock: AsyncSocket
|
sock: AsyncSocket
|
||||||
streamOpen: quicly_stream_open_t
|
streamOpen: quicly_stream_open_t
|
||||||
|
@ -47,43 +52,7 @@ type
|
||||||
verifyCertsCb: ptls_verify_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[Connection]
|
||||||
|
|
||||||
PX509_STORE_CTX = SslPtr
|
|
||||||
|
|
||||||
PX509_VERIFY_PARAM = SslPtr
|
|
||||||
|
|
||||||
proc PEM_read_PrivateKey(fp: File, x: ptr EVP_PKEY,
|
|
||||||
cb: proc(buf: cstring, size: cint, rwflag: cint, u: pointer): cint {.cdecl.},
|
|
||||||
u: pointer): 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
|
||||||
|
@ -92,7 +61,7 @@ proc getRelativeTimeout(ctx: QuicP2PContext): int32 =
|
||||||
var nextTimeout = int64.high
|
var nextTimeout = int64.high
|
||||||
var now = ctx.quiclyCtx.now.cb(ctx.quiclyCtx.now)
|
var now = ctx.quiclyCtx.now.cb(ctx.quiclyCtx.now)
|
||||||
for c in ctx.connections:
|
for c in ctx.connections:
|
||||||
let connTimeout = quicly_get_first_timeout(c)
|
let connTimeout = quicly_get_first_timeout(c.conn)
|
||||||
if connTimeout < nextTimeout:
|
if connTimeout < nextTimeout:
|
||||||
nextTimeout = connTimeout
|
nextTimeout = connTimeout
|
||||||
if now < nextTimeout:
|
if now < nextTimeout:
|
||||||
|
@ -114,7 +83,8 @@ proc onServerReceive(stream: ptr quicly_stream_t, offset: csize_t, src: pointer,
|
||||||
let input = quicly_streambuf_ingress_get(stream)
|
let input = quicly_streambuf_ingress_get(stream)
|
||||||
var msg = newString(input.len)
|
var msg = newString(input.len)
|
||||||
copyMem(addr msg[0], input.base, input.len)
|
copyMem(addr msg[0], input.base, input.len)
|
||||||
echo &"received message from client: \"{msg}\""
|
let conn = cast[Connection](quicly_get_data(stream.conn)[])
|
||||||
|
echo &"client {conn.certs[1].getPublicKey().toHex()} sends \"{msg}\""
|
||||||
if quicly_sendstate_is_open(addr stream.sendstate) != 0 and input.len > 0:
|
if quicly_sendstate_is_open(addr stream.sendstate) != 0 and input.len > 0:
|
||||||
discard quicly_streambuf_egress_write(stream, input.base, input.len)
|
discard quicly_streambuf_egress_write(stream, input.base, input.len)
|
||||||
if quicly_recvstate_transfer_complete(addr stream.recvstate) != 0:
|
if quicly_recvstate_transfer_complete(addr stream.recvstate) != 0:
|
||||||
|
@ -128,7 +98,8 @@ proc onClientReceive(stream: ptr quicly_stream_t, offset: csize_t,
|
||||||
let input = quicly_streambuf_ingress_get(stream)
|
let input = quicly_streambuf_ingress_get(stream)
|
||||||
let msg = newString(input.len)
|
let msg = newString(input.len)
|
||||||
copyMem(msg.cstring, input.base, input.len)
|
copyMem(msg.cstring, input.base, input.len)
|
||||||
echo &"received message from server: \"{msg}\""
|
let conn = cast[Connection](quicly_get_data(stream.conn)[])
|
||||||
|
echo &"server {conn.certs[1].getPublicKey().toHex()} sends \"{msg}\""
|
||||||
if quicly_recvstate_transfer_complete(addr stream.recvstate) != 0:
|
if quicly_recvstate_transfer_complete(addr stream.recvstate) != 0:
|
||||||
discard quicly_close(stream.conn, 0, "")
|
discard quicly_close(stream.conn, 0, "")
|
||||||
quicly_streambuf_ingress_shift(stream, input.len)
|
quicly_streambuf_ingress_shift(stream, input.len)
|
||||||
|
@ -198,6 +169,14 @@ proc verifyCerts(self: ptr ptls_verify_certificate_t, tls: ptr ptls_t,
|
||||||
discard ptls_openssl_init_verify_certificate(addr opensslVerifier, store)
|
discard ptls_openssl_init_verify_certificate(addr opensslVerifier, store)
|
||||||
result = opensslVerifier.super.cb(addr opensslVerifier.super, tls,
|
result = opensslVerifier.super.cb(addr opensslVerifier.super, tls,
|
||||||
verify_sign, verify_data, certs, num_certs)
|
verify_sign, verify_data, certs, num_certs)
|
||||||
|
if result == 0:
|
||||||
|
let quiclyConn = cast[ptr quicly_conn_t](ptls_get_data_ptr(tls)[])
|
||||||
|
let conn = cast[Connection](quicly_get_data(quiclyConn)[])
|
||||||
|
for i in 0 .. num_certs - 1:
|
||||||
|
let iovec = cast[ptr ptls_iovec_t](cast[ByteAddress](certs) + i.int * sizeof(ptls_iovec_t))
|
||||||
|
var cert = newString(iovec.len)
|
||||||
|
copyMem(cert.cstring, iovec.base, iovec.len)
|
||||||
|
conn.certs.add(cert)
|
||||||
ptls_openssl_dispose_verify_certificate(addr opensslVerifier)
|
ptls_openssl_dispose_verify_certificate(addr opensslVerifier)
|
||||||
X509_STORE_free(store)
|
X509_STORE_free(store)
|
||||||
X509_free(caCert)
|
X509_free(caCert)
|
||||||
|
@ -228,6 +207,19 @@ proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
||||||
EVP_PKEY_free(privateKey)
|
EVP_PKEY_free(privateKey)
|
||||||
result.tlsCtx.sign_certificate = addr result.signCertCb.super
|
result.tlsCtx.sign_certificate = addr result.signCertCb.super
|
||||||
|
|
||||||
|
proc addConnection(ctx: QuicP2PContext, connPtr: ptr quicly_conn_t) =
|
||||||
|
assert(not connPtr.isNil)
|
||||||
|
let data = quicly_get_data(connPtr)
|
||||||
|
var conn = Connection(conn: connPtr)
|
||||||
|
data[] = addr conn[]
|
||||||
|
ctx.connections.add(conn)
|
||||||
|
|
||||||
|
proc delConnection(ctx: QuicP2PContext, index: int) =
|
||||||
|
assert(index >= 0 and index < ctx.connections.len)
|
||||||
|
let c = ctx.connections[index]
|
||||||
|
ctx.connections.del(index)
|
||||||
|
quicly_free(c.conn)
|
||||||
|
|
||||||
proc sendPackets(ctx: QuicP2PContext) =
|
proc sendPackets(ctx: QuicP2PContext) =
|
||||||
if ctx.connections.len == 0:
|
if ctx.connections.len == 0:
|
||||||
return
|
return
|
||||||
|
@ -237,7 +229,7 @@ proc sendPackets(ctx: QuicP2PContext) =
|
||||||
var dgrams: array[10, IOVec]
|
var dgrams: array[10, IOVec]
|
||||||
var dgramCount = dgrams.len().csize_t
|
var dgramCount = dgrams.len().csize_t
|
||||||
var dgramsBuf = newString(dgramCount * ctx.quiclyCtx.transport_params.max_udp_payload_size)
|
var dgramsBuf = newString(dgramCount * ctx.quiclyCtx.transport_params.max_udp_payload_size)
|
||||||
let sendResult = quicly_send(conns[i], addr dstAddr, addr srcAddr,
|
let sendResult = quicly_send(conns[i].conn, addr dstAddr, addr srcAddr,
|
||||||
addr dgrams[0], addr dgramCount,
|
addr dgrams[0], addr dgramCount,
|
||||||
addr dgramsBuf[0], dgramsBuf.len().csize_t)
|
addr dgramsBuf[0], dgramsBuf.len().csize_t)
|
||||||
case sendResult:
|
case sendResult:
|
||||||
|
@ -249,9 +241,8 @@ proc sendPackets(ctx: QuicP2PContext) =
|
||||||
asyncCheck sendTo(ctx.sock.getFd().AsyncFD, dgrams[j].iov_base,
|
asyncCheck sendTo(ctx.sock.getFd().AsyncFD, dgrams[j].iov_base,
|
||||||
dgrams[j].iov_len.int, addr dstAddr.sa, sockLen)
|
dgrams[j].iov_len.int, addr dstAddr.sa, sockLen)
|
||||||
of QUICLY_ERROR_FREE_CONNECTION:
|
of QUICLY_ERROR_FREE_CONNECTION:
|
||||||
let c = ctx.connections[i]
|
echo "deleting connection"
|
||||||
ctx.connections.del(i)
|
ctx.delConnection(i)
|
||||||
quicly_free(c)
|
|
||||||
else:
|
else:
|
||||||
raise newException(ValueError, &"quicly_send returned {sendResult}")
|
raise newException(ValueError, &"quicly_send returned {sendResult}")
|
||||||
|
|
||||||
|
@ -267,15 +258,15 @@ proc handleMsg(ctx: QuicP2PContext, msg: string, peerAddr: ptr SockAddr,
|
||||||
return
|
return
|
||||||
var conn: ptr quicly_conn_t = nil
|
var conn: ptr quicly_conn_t = nil
|
||||||
for c in ctx.connections:
|
for c in ctx.connections:
|
||||||
if quicly_is_destination(c, nil, peerAddr, addr decoded) != 0:
|
if quicly_is_destination(c.conn, nil, peerAddr, addr decoded) != 0:
|
||||||
conn = c
|
conn = c.conn
|
||||||
break
|
break
|
||||||
if conn != nil:
|
if conn != nil:
|
||||||
discard quicly_receive(conn, nil, peerAddr, addr decoded)
|
discard quicly_receive(conn, nil, peerAddr, addr decoded)
|
||||||
elif isServer:
|
elif isServer:
|
||||||
discard quicly_accept(addr conn, addr ctx.quiclyCtx, nil, peerAddr,
|
discard quicly_accept(addr conn, addr ctx.quiclyCtx, nil, peerAddr,
|
||||||
addr decoded, nil, addr ctx.nextCid, nil)
|
addr decoded, nil, addr ctx.nextCid, nil)
|
||||||
ctx.connections.add(conn)
|
ctx.addConnection(conn)
|
||||||
|
|
||||||
proc receive(ctx: QuicP2PContext, isServer: bool) {.async.} =
|
proc receive(ctx: QuicP2PContext, isServer: bool) {.async.} =
|
||||||
while true:
|
while true:
|
||||||
|
@ -330,9 +321,9 @@ proc main() =
|
||||||
if connectResult != 0:
|
if connectResult != 0:
|
||||||
echo "quicly_connect failed: ", connectResult
|
echo "quicly_connect failed: ", connectResult
|
||||||
quit(3)
|
quit(3)
|
||||||
ctx.connections.add(conn)
|
|
||||||
var stream: ptr quicly_stream_t
|
var stream: ptr quicly_stream_t
|
||||||
discard quicly_open_stream(conn, addr stream, 0)
|
discard quicly_open_stream(conn, addr stream, 0)
|
||||||
|
ctx.addConnection(conn)
|
||||||
asyncCheck receive(ctx, false)
|
asyncCheck receive(ctx, false)
|
||||||
|
|
||||||
else:
|
else:
|
||||||
|
|
Loading…
Reference in New Issue