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 asyncnet
|
||||
import certificate
|
||||
import net
|
||||
import os
|
||||
import openssl_additional
|
||||
import picotls/picotls
|
||||
import picotls/openssl as ptls_openssl
|
||||
import quicly/quicly
|
||||
import quicly/cid
|
||||
import quicly/constants
|
||||
|
@ -11,9 +15,8 @@ import quicly/defaults
|
|||
import quicly/recvstate
|
||||
import quicly/sendstate
|
||||
import quicly/streambuf
|
||||
import picotls/picotls
|
||||
import picotls/openssl as ptls_openssl
|
||||
import strformat
|
||||
import strutils
|
||||
|
||||
from nativesockets import SockAddr, Sockaddr_storage, SockLen, getHostByName
|
||||
from posix import IOVec
|
||||
|
@ -33,12 +36,14 @@ from openssl import
|
|||
|
||||
const serverCertChainPath = "./certs/server-certchain.pem"
|
||||
const serverKeyPath = "./certs/server-cert.key"
|
||||
const clientCertChainPath = "./certs/server-certchain.pem"
|
||||
const clientKeyPath = "./certs/server-cert.key"
|
||||
|
||||
const X509_V_FLAG_CHECK_SS_SIGNATURE = 0x00004000
|
||||
const clientCertChainPath = "./certs/client-certchain.pem"
|
||||
const clientKeyPath = "./certs/client-cert.key"
|
||||
|
||||
type
|
||||
Connection = ref object
|
||||
conn: ptr quicly_conn_t
|
||||
certs: seq[Certificate]
|
||||
|
||||
QuicP2PContext = ref object
|
||||
sock: AsyncSocket
|
||||
streamOpen: quicly_stream_open_t
|
||||
|
@ -47,43 +52,7 @@ type
|
|||
verifyCertsCb: ptls_verify_certificate_t
|
||||
tlsCtx: ptls_context_t
|
||||
quiclyCtx: quicly_context_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,
|
||||
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.}
|
||||
connections: seq[Connection]
|
||||
|
||||
proc getRelativeTimeout(ctx: QuicP2PContext): int32 =
|
||||
## 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 now = ctx.quiclyCtx.now.cb(ctx.quiclyCtx.now)
|
||||
for c in ctx.connections:
|
||||
let connTimeout = quicly_get_first_timeout(c)
|
||||
let connTimeout = quicly_get_first_timeout(c.conn)
|
||||
if connTimeout < nextTimeout:
|
||||
nextTimeout = connTimeout
|
||||
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)
|
||||
var msg = newString(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:
|
||||
discard quicly_streambuf_egress_write(stream, input.base, input.len)
|
||||
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 msg = newString(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:
|
||||
discard quicly_close(stream.conn, 0, "")
|
||||
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)
|
||||
result = opensslVerifier.super.cb(addr opensslVerifier.super, tls,
|
||||
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)
|
||||
X509_STORE_free(store)
|
||||
X509_free(caCert)
|
||||
|
@ -228,6 +207,19 @@ proc initContext(sock: AsyncSocket, certChainPath: string, keyPath: string,
|
|||
EVP_PKEY_free(privateKey)
|
||||
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) =
|
||||
if ctx.connections.len == 0:
|
||||
return
|
||||
|
@ -237,7 +229,7 @@ proc sendPackets(ctx: QuicP2PContext) =
|
|||
var dgrams: array[10, IOVec]
|
||||
var dgramCount = dgrams.len().csize_t
|
||||
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 dgramsBuf[0], dgramsBuf.len().csize_t)
|
||||
case sendResult:
|
||||
|
@ -249,9 +241,8 @@ proc sendPackets(ctx: QuicP2PContext) =
|
|||
asyncCheck sendTo(ctx.sock.getFd().AsyncFD, dgrams[j].iov_base,
|
||||
dgrams[j].iov_len.int, addr dstAddr.sa, sockLen)
|
||||
of QUICLY_ERROR_FREE_CONNECTION:
|
||||
let c = ctx.connections[i]
|
||||
ctx.connections.del(i)
|
||||
quicly_free(c)
|
||||
echo "deleting connection"
|
||||
ctx.delConnection(i)
|
||||
else:
|
||||
raise newException(ValueError, &"quicly_send returned {sendResult}")
|
||||
|
||||
|
@ -267,15 +258,15 @@ proc handleMsg(ctx: QuicP2PContext, msg: string, peerAddr: ptr SockAddr,
|
|||
return
|
||||
var conn: ptr quicly_conn_t = nil
|
||||
for c in ctx.connections:
|
||||
if quicly_is_destination(c, nil, peerAddr, addr decoded) != 0:
|
||||
conn = c
|
||||
if quicly_is_destination(c.conn, nil, peerAddr, addr decoded) != 0:
|
||||
conn = c.conn
|
||||
break
|
||||
if conn != nil:
|
||||
discard quicly_receive(conn, nil, peerAddr, addr decoded)
|
||||
elif isServer:
|
||||
discard quicly_accept(addr conn, addr ctx.quiclyCtx, nil, peerAddr,
|
||||
addr decoded, nil, addr ctx.nextCid, nil)
|
||||
ctx.connections.add(conn)
|
||||
ctx.addConnection(conn)
|
||||
|
||||
proc receive(ctx: QuicP2PContext, isServer: bool) {.async.} =
|
||||
while true:
|
||||
|
@ -330,9 +321,9 @@ proc main() =
|
|||
if connectResult != 0:
|
||||
echo "quicly_connect failed: ", connectResult
|
||||
quit(3)
|
||||
ctx.connections.add(conn)
|
||||
var stream: ptr quicly_stream_t
|
||||
discard quicly_open_stream(conn, addr stream, 0)
|
||||
ctx.addConnection(conn)
|
||||
asyncCheck receive(ctx, false)
|
||||
|
||||
else:
|
||||
|
|
Loading…
Reference in New Issue