Skip to content

net/asyncnet: Unable to access peer's certificate chain #13299

Closed
@royneary

Description

@royneary

Summary

I'm using asyncnet to establish TLS connections and would like to access the verified certificate chain of the peer I'm connected with (either client or server).

Description

Currently it's not possible to retrieve the certificate chain from an OpenSSL-wrapped AsyncSocket after the TLS-Handshake has completed. In the chat server example from the asyncnet module this would be in processClient::

...
var sslContext = newContext()

proc processClient(client: AsyncSocket) {.async.} =
  wrapConnectedSocket(sslContext, client, handshakeAsServer)
  while true:
    let line = await client.recvLine()
    let certs = client.getPeerCertificates() # not possible
    if line.len == 0: break
    for c in clients:
      await c.send(line & "\c\L")
...

OpenSSL provides SSL_get0_verified_chain, but it needs the SSL pointer of the current connection, which I can't access. There are two possible solutions that I can think of:

  1. Export the sslHandle field of AsyncSocket / Socket, so users can call SSL_get0_verified_chain on it

  2. add a proc getPeerCertificates to the standard library, e.g.:

type
  PSTACK_OF_X509* = SslPtr
  Certificate* = string

proc CRYPTO_free*(p: pointer, file: cstring, line: int) {.cdecl, dynlib: DLLSSLName, importc.}

proc i2d_X509*(x: PX509, output: ptr ptr cchar): int {.cdecl, dynlib: DLLSSLName, importc.}

proc OPENSSL_sk_num*(stack: SslPtr): int {.cdecl, dynlib: DLLSSLName, importc.}

proc OPENSSL_sk_value*(stack: SslPtr, index: int): pointer {.cdecl, dynlib: DLLSSLName, importc.}

proc SSL_get0_verified_chain*(ssl: SslPtr): PSTACK_OF_X509 {.cdecl, dynlib: DLLSSLName, importc.}

proc getPeerCertificates*(socket: AsyncSocket): seq[Certificate] =
  result = newSeq[Certificate]()
  let stack = SSL_get0_verified_chain(socket.sslHandle)
  if stack == nil:
    return
  let length = OPENSSL_sk_num(stack)
  if length == 0:
    return
  for i in 0 .. length - 1:
    var der: ptr cchar = nil
    let derLen = i2d_X509(cast[PX509](OPENSSL_sk_value(stack, i)), addr der)
    var cert = newString(derLen)
    copyMem(addr cert[0], der, derLen)
    CRYPTO_free(der, instantiationInfo().filename, instantiationInfo().line)
    result.add(cert)

Alternatives

I tried to register a callback using SSL_CTX_set_info_callback to retrieve the SSL pointer, but it seemed unnessecarily hard since it is already stored in the socket.

Additional Information

OpenSSL's SSL_get0_verified_chain

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions