Sign in

    async-tls

    Native TLS client with PEM mTLS identity support, derived from moonbitlang/async 0.21.3

    Download zip
    Version
    0.1.0
    License
    Apache-2.0
    Last updated
    last month
    Downloads
    40

    #async-tls

    支持可选双向 TLS(客户端证书)的 MoonBit 原生 TLS 客户端。 基于 moonbitlang/async@0.21.3,C 符号采用独立前缀,可与官方 TLS 实现同时链接。 源码位于 moon-mqtt-client 的 third_party/async-tls。

    支持 macOS、Linux、PEM 证书链和未加密 PEM 私钥;不支持加密私钥、Windows、Wasm 或硬件密钥。

    #安装

    moon add Strangelight-Merser/async-tls

    #客户端身份

    let tls = @tls.Tls::client(
    tcp,
    host="broker.example",
    trust=@tls.CustomPemFile("server-ca.pem"),
    identity={
    certificate_chain_file: "client-chain.pem",
    private_key_file: "client.key",
    },
    )

    trust 指定如何验证服务器证书,identity 指定客户端提交的身份。 每次新握手重新读取文件;已建立的连接不会重新加载。TLS 包装器不拥有底层 TCP, 调用方须先关闭 TLS,再关闭 TCP。

    #源码测试

    仓库中的证书和私钥仅为公开测试样本,不用于生产。

    moon check --target native MOONBIT_ASYNC_CHECK_FD_LEAK=1 moon test --target native

    ./scripts/gen-testdata.sh 可重新生成测试样本。

    ConnectionClosed

    pub suberror ConnectionClosed derive(ToJson,
    Debug
    )

    TlsError

    pub suberror TlsError {
    TlsError(String)
    } derive(ToJson,
    Debug
    )

    ClientIdentity

    pub(all) struct ClientIdentity {
    certificate_chain_file : String
    private_key_file : String
    }

    PEM files presented by a TLS client during handshake.

    certificate_chain_file is the leaf certificate followed by any intermediate certificates required to reach a CA the peer trusts. private_key_file must be an unencrypted PEM private key. Encrypted keys are rejected without prompting for a passphrase.

    Tls

    type Tls

    A TLS-encrypted connection
    impl Reader for Tls
    impl Writer for Tls

    Tls::client

    async fn[Inner :
    Reader
    +
    Writer
    ] Tls::client(inner : Inner, verify? : Bool, host? : String, sni? : Bool, trust? : TrustedRoot, identity? : ClientIdentity) -> Tls

    Create a TLS client that read from write to inner. client will block until TLS handshake to remote server completed.

    trust specifies which servers are trusted and how certificate validation is performed. See TrustedRoot for more details. The default is SystemRoot.

    If host is present, it will be used to verify the peer's certificate.

    If host is present and sni is true (true by default), Server Name Indication (SNI) field of TLS will be set to host. sni=false is currently unsupported on Windows.

    identity is the optional client certificate used for mutual TLS. The files are read for this handshake only; later connections must pass the identity again.

    Tls::client_from_pair

    async fn[R :
    Reader
    , W :
    Writer
    ] Tls::client_from_pair(r : R, w : W, verify? : Bool, host? : String, sni? : Bool, trust? : TrustedRoot, identity? : ClientIdentity) -> Tls

    Create a TLS client that read from r and write to w. client_from_pair will block until TLS handshake to remote server completed.

    trust specifies which servers are trusted and how certificate validation is performed. See TrustedRoot for more details. The default is SystemRoot.

    If host is present, it will be used to verify the peer's certificate.

    If host is present and sni is true (true by default), Server Name Indication (SNI) field of TLS will be set to host.

    identity is the optional client certificate used for mutual TLS. Identity files are read for this handshake only.

    Tls::close

    fn Tls::close(self : Tls) -> Unit

    Close a TLS connection and release related resource. THIS FUNCTION MUST BE CALLED BEFORE CLOSING UNDERLYING TRANSPORT!!!

    Note that this function will not perform the TLS shutdown process, for graceful shutdown of a TLS connection, see TLS::shutdown.

    Tls::get_peer_certificate

    fn Tls::get_peer_certificate(self : Tls) -> Bytes? raise

    Get the certificate of remote peer for a TLS connection in DER format. Note that even if nothing goes wrong, the certificate may not exist:

    • although rare, some TLS algorithm choice does not have a certificate
    • for connection without client certificate, the server does not have any peer certificate

    Therefore the return type is Bytes? instead of Bytes.

    Tls::server

    async fn[Inner :
    Reader
    +
    Writer
    ] Tls::server(inner : Inner, private_key_file~ : String, private_key_type~ : X509FileType, certificate_file~ : String, certificate_type~ : X509FileType, client_ca_file? : String, require_client_certificate? : Bool, min_protocol_version? : Int) -> Tls

    WARNING: this API is currently for testing only, it may block the whole thread while reading certificate, and may break any time in the future. DO NOT USE.

    Create a TLS server connection that read from and write to inner. server will block until TLS handshake with client completed.

    private_key_file, private_key_type specifies the private key of the server. certificate_file and certificate_type specifies the certificate of the server.

    Tls::server_endpoint_channel_binding

    fn Tls::server_endpoint_channel_binding(self : Tls) -> Bytes raise

    Return tls-server-endpoint type of channel binding data for this TLS connection, according to RFC 5929. Not all TLS connection has such thing as a server certificate, so tls-unique is the more recommended approach when available.

    Tls::server_from_pair

    async fn[R :
    Reader
    , W :
    Writer
    ] Tls::server_from_pair(r : R, w : W, private_key_file~ : String, private_key_type~ : X509FileType, certificate_file~ : String, certificate_type~ : X509FileType, client_ca_file? : String, require_client_certificate? : Bool, min_protocol_version? : Int) -> Tls

    WARNING: this API is currently for testing only, it may block the whole thread while reading certificate, and may break any time in the future. DO NOT USE.

    Create a TLS server connection that read from r and write to w. server_from_pair will block until TLS handshake with client completed.

    private_key_file, private_key_type specifies the private key of the server. certificate_file and certificate_type specifies the certificate of the server.

    When require_client_certificate is true, the server requests a client certificate and fails the handshake if none is presented. client_ca_file is the PEM trust store used to verify that client certificate.

    Tls::shutdown

    async fn Tls::shutdown(self : Tls) -> Unit

    Shutdown a TLS connection gracefully. This function MUST be called before close, and MUST NOT be called if the TLS connection fail with other error.

    shutdown is used to initiate the closure of a TLS connection. So there is no need to call shutdown if you receive EOF from the peer.

    When calling shutdown, there may still be pending data sent by the peer on the wire. So to close a TLS connection cleanly, make sure you read from the connection until EOF after calling shutdown.

    Note that the main purpose of TLS shutdown is to ensure integrity before closing the underlying transport. So if your application protocol has its own way of ensuring integrity (e.g. Content-Length in HTTP/1.1), it is not necessary to call shutdown.

    Tls::unique_channel_binding

    fn Tls::unique_channel_binding(self : Tls) -> Bytes raise

    Return tls-unique type of channel binding data for this TLS connection, according to RFC 5929

    TrustedRoot

    pub(all) enum TrustedRoot {
    NoVerification
    SystemRoot
    CustomPemFile(String)
    }

    Specify the trusted root when performing TLS certificate validation.

    • NoVerification: disable certificate validation. This destroys the whole purpose of using TLS and should only be used for testing purpose.

    • SystemRoot: use the default trusted root of the system

    • CustomPemFile(name): use the certificates in the PEM file name exclusively as trusted root. Useful for connecting to services whose certificates are issued by a private or self-signed root.

    X509FileType

    pub(all) enum X509FileType {
    PEM
    ANS1
    }

    describe_tls_error

    fn describe_tls_error(err : Error) -> String

    Extract a TlsError message for tests and callers that caught Error.

    rand_bytes

    #internal(internal, "for internal use only")
    fn rand_bytes(num : Int) -> Bytes raise

    Generate cryptographically secure random bytes using OpenSSL's RAND_bytes Returns 1 on success, 0 on failure

    sha1

    #internal(internal, "for internal use only")
    fn sha1(src : Bytes) -> Bytes raise