tls
Secure TLS 1.3 and 1.2 connections and public key cryptography tools, with no OpenSSL needed.
import tls
The tls package is a complete implementation of TLS written in Salam and needs no outside library. A client connects with Dial and checks the server certificate against the system roots; a server reads its certificate and key with NewServerConfig and secures each connection with WrapServer.
The cryptography tools here are also useful for building protocols of your own: the two sides make a shared secret with ECDH, derive a key from it with HKDF, and encrypt and seal the message with AES-GCM. Keys and data are passed around as base64.
Types
Config
struct Config| Name | Type | Description |
|---|---|---|
server_name | str = "" | The hostname for SNI and for certificate verification. |
ca_file | str = "" | The file of root certificates; empty means the system roots. |
insecure_skip_verify | bool = false | Do not verify the certificate; for testing only, since it leaves the connection open to eavesdropping. |
alpn | str = "" | The protocol for ALPN, such as h2. |
aes_only | bool = false | Offer AES ciphers only. |
import tls
func main:
mut cfg := tls.NewConfig("example.com")
cfg.alpn = "http/1.1"
mut conn := tls.Dial("example.com", 443, cfg)
if not tls.ConnOk(conn):
println "error:", tls.ConnError(conn)
ret
end
println tls.Version(conn), tls.CipherName(conn), tls.PeerCertCount(conn)
_ := tls.Write(conn, "GET / HTTP/1.1\r\nHost: example.com\r\nConnection: close\r\n\r\n")
mut body := ""
until tls.ConnOk(conn):
chunk := tls.Read(conn, 4096)
if chunk.len() == 0:
break
end
body += chunk
end
println body.len() > 0
tls.Close(conn)
endConn
struct Conn| Name | Type | Description |
|---|---|---|
h | Handshake | The handshake state. |
host | str = "" | The host. |
open | bool = false | The connection is open. |
insecure | bool = false | Without certificate verification. |
pending | Buf | Data that has been read but not taken yet. |
err | str = "" | The error message. |
The example for this item is shown with the ones above: tls-Client.salam
ServerConfig
struct ServerConfig| Name | Type | Description |
|---|---|---|
chain | Roots | The certificate chain. |
key | PrivateKey | The private key. |
ok | bool = false | Both were read. |
import tls
import net.tcp
import mem
func main:
cfg := tls.NewServerConfig("cert.pem", "key.pem")
if not cfg.ok:
println "the certificate or key could not be read"
ret
end
server := tcp.Bind(8443)
until true:
sock := tcp.Accept(server)
mut conn := tls.WrapServer(sock.fd, cfg)
if tls.ConnOk(conn):
if tls.Readable(conn) or tls.Pending(conn) > 0:
println tls.Read(conn, 1024)
end
buffer := mem.Allocate(256 as u64)
n := tls.ReadBytes(conn, buffer, 256)
_ := tls.WriteBytes(conn, buffer, n)
_ := tls.Write(conn, "HTTP/1.1 200 OK\r\nContent-Length: 5\r\n\r\nhello")
mem.Free(buffer)
println tls.Fd(conn) >= 0
end
tls.Close(conn)
end
endConstants and variables
TLS10
const TLS10: i32 = 769import tls
import net.tcp
import encoding
import io
func main:
roots := tls.LoadRoots("/etc/ssl/cert.pem")
println tls.RootCount(roots)
chain := tls.LoadCertChainFile("cert.pem")
println tls.RootCount(chain)
mut key := tls.LoadPrivateKeyFile("key.pem")
mut key2 := tls.ParsePrivateKeyPEM("-----BEGIN ...")
tls.FreePrivateKey(key2)
cert := tls.HexDecodeBuf("3082...")
println tls.CertIsValid(cert), tls.CertSubjectCN(cert)
code := tls.VerifyCert(cert, roots, "example.com")
println code, tls.VerifyError(code)
digest := tls.HashDigest(false, "message")
sig := tls.NewBuf(512)
println tls.SignRSAPKCS1(key, false, digest, sig)
println tls.SignRSAPSS(key, false, digest, sig)
println tls.VerifyRSAPSSWithKey(key, false, digest, sig)
r := tls.NewBuf(32)
s := tls.NewBuf(32)
if tls.SignECDSA(key, digest, r, s):
der := tls.ECDSADerEncode(r, s, 32)
println tls.VerifyECDSADERWithKey(key, digest, der)
end
pem := io.ReadFile("rsa-key.pem")
mut good := false
println tls.RsaKeyBits(pem)
println tls.RsaSignPkcs1Sha256(pem, encoding.Base64Encode("..."), good)
mut cfg := tls.NewConfig("internal.example")
cfg.ca_file = "company-ca.pem"
cfg.aes_only = true
mut conn := tls.Dial("internal.example", 443, cfg)
println tls.ConnOk(conn)
tls.Close(conn)
sock := tcp.Dial("example.com", 443)
mut conn2 := tls.WrapClient(sock.fd, tls.NewConfig("example.com"))
println tls.Version(conn2) == "TLS1.3"
tls.Close(conn2)
println tls.TLS10, tls.TLS11, tls.TLS12, tls.TLS13
println tls.SelfTest()
tls.FreeRoots(roots)
tls.FreePrivateKey(key)
endTLS11
const TLS11: i32 = 770The example for this item is shown with the ones above: tls-Certs.salam
TLS12
const TLS12: i32 = 771The example for this item is shown with the ones above: tls-Certs.salam
TLS13
const TLS13: i32 = 772The example for this item is shown with the ones above: tls-Certs.salam
Functions
SelfTest
func SelfTest(): intThe example for this item is shown with the ones above: tls-Certs.salam
LoadCertChainFile
func LoadCertChainFile(path: str): RootsThe example for this item is shown with the ones above: tls-Certs.salam
EcdhP256Generate
func EcdhP256Generate(priv &: str, pub_spki &: str): boolimport tls
import encoding
func main:
mut priv1 := ""
mut pub1 := ""
mut priv2 := ""
mut pub2 := ""
println tls.EcdhP256Generate(priv1, pub1), tls.EcdhP256Generate(priv2, pub2)
mut good := false
secret1 := tls.EcdhP256Shared(priv1, pub2, good)
secret2 := tls.EcdhP256Shared(priv2, pub1, good)
println good, secret1 == secret2
point1 := tls.EcdhP256PublicPoint(priv1)
point2 := tls.EcdhP256PublicPoint(priv2)
println tls.EcdhP256SharedPoint(priv1, point2, good) == tls.EcdhP256SharedPoint(priv2, point1, good)
salt := tls.RandomSalt(16)
key := tls.HkdfSha256(secret1, salt, "chat v1", 32, good)
println good, key == tls.HkdfSha256(secret2, salt, "chat v1", 32, good)
println tls.HkdfSha256Raw(secret1, salt, encoding.Base64Encode("chat v1"), 32, good) == key
nonce := tls.GcmNonce(0)
sealed := tls.AesGcmSeal(key, nonce, "hello, this message is encrypted", good)
println good
println tls.AesGcmOpen(key, nonce, sealed, good), good
_ := tls.AesGcmOpen(key, tls.GcmNonce(1), sealed, good)
println good
binary := tls.AesGcmSealBytes(key, nonce, encoding.Base64Encode("abc"), good)
println encoding.Base64Decode(tls.AesGcmOpenBytes(key, nonce, binary, good))
digest := encoding.Base64Encode("0123456789abcdef0123456789abcdef")
sig := tls.P256Sign(priv1, digest, good)
println good, tls.P256Verify(point1, digest, sig), tls.P256Verify(point2, digest, sig)
b := tls.HashDigest(false, "abc")
println tls.BufLen(b), tls.HexEncodeBuf(b)
mut hex := tls.HexDecodeBuf("cafe")
tls.BufAppendStr(hex, "!")
println tls.BufLen(hex), tls.HexEncodeBuf(hex)
mut zeros := tls.NewZeroBuf(4)
println tls.HexEncodeBuf(zeros)
mut empty := tls.NewBuf(64)
println tls.BufLen(empty)
tls.WipeBuf(hex)
tls.FreeBuf(hex)
tls.FreeBuf(zeros)
tls.FreeBuf(empty)
endEcdhP256Shared
func EcdhP256Shared(priv_b64: str, peer_spki_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
HkdfSha256
func HkdfSha256(ikm_b64: str, salt_b64: str, info: str, out_len: int, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
AesGcmSeal
func AesGcmSeal(key_b64: str, nonce_b64: str, plaintext: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
AesGcmOpen
func AesGcmOpen(key_b64: str, nonce_b64: str, sealed_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
GcmNonce
func GcmNonce(counter: i64): strThe example for this item is shown with the ones above: tls-Crypto.salam
RandomSalt
func RandomSalt(n: int): strThe example for this item is shown with the ones above: tls-Crypto.salam
EcdhP256PublicPoint
func EcdhP256PublicPoint(priv_b64: str): strThe example for this item is shown with the ones above: tls-Crypto.salam
EcdhP256SharedPoint
func EcdhP256SharedPoint(priv_b64: str, peer_point_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
HkdfSha256Raw
func HkdfSha256Raw(ikm_b64: str, salt_b64: str, info_b64: str, out_len: int, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
AesGcmSealBytes
func AesGcmSealBytes(key_b64: str, nonce_b64: str, plaintext_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
AesGcmOpenBytes
func AesGcmOpenBytes(key_b64: str, nonce_b64: str, sealed_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
P256Sign
func P256Sign(priv_b64: str, digest_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Crypto.salam
P256Verify
func P256Verify(pub_point_b64: str, digest_b64: str, sig_b64: str): boolThe example for this item is shown with the ones above: tls-Crypto.salam
RsaSignPkcs1Sha256
func RsaSignPkcs1Sha256(pem: str, digest_b64: str, ok &: bool): strThe example for this item is shown with the ones above: tls-Certs.salam
RsaKeyBits
func RsaKeyBits(pem: str): intThe example for this item is shown with the ones above: tls-Certs.salam
RsaEncryptOaepSha1
func RsaEncryptOaepSha1(pem: str, plain_b64: str, ok &: bool): strimport tls
import encoding
import io
func main:
key := io.ReadFile("public.pem")
mut ok := false
cipher := tls.RsaEncryptOaepSha1(key, encoding.Base64Encode("hello"), ok)
if not ok:
println "the RSA public key could not be read"
ret
end
println cipher.len() > 0
endFreePrivateKey
func FreePrivateKey(k &: PrivateKey)The example for this item is shown with the ones above: tls-Certs.salam
ParsePrivateKeyPEM
func ParsePrivateKeyPEM(pem: str): PrivateKeyThe example for this item is shown with the ones above: tls-Certs.salam
LoadPrivateKeyFile
func LoadPrivateKeyFile(path: str): PrivateKeyThe example for this item is shown with the ones above: tls-Certs.salam
NewConfig
func NewConfig(server_name: str): ConfigThe example for this item is shown with the ones above: tls-Client.salam
Dial
func Dial(host: str, port: int, cfg: Config): ConnThe example for this item is shown with the ones above: tls-Client.salam
WrapClient
func WrapClient(fd: i64, cfg: Config): ConnThe example for this item is shown with the ones above: tls-Certs.salam
NewServerConfig
func NewServerConfig(cert_file: str, key_file: str): ServerConfigThe example for this item is shown with the ones above: tls-Server.salam
WrapServer
func WrapServer(fd: i64, cfg: ServerConfig): ConnThe example for this item is shown with the ones above: tls-Server.salam
ConnOk
func ConnOk(c: Conn): boolThe example for this item is shown with the ones above: tls-Client.salam
ConnError
func ConnError(c: Conn): strThe example for this item is shown with the ones above: tls-Client.salam
Write
func Write(c &: Conn, data: str): intThe example for this item is shown with the ones above: tls-Client.salam
WriteBytes
func WriteBytes(c &: Conn, ptr: void*, n: int): intThe example for this item is shown with the ones above: tls-Server.salam
Read
func Read(c &: Conn, max: int): strThe example for this item is shown with the ones above: tls-Client.salam
ReadBytes
func ReadBytes(c &: Conn, buf: void*, max: int): intThe example for this item is shown with the ones above: tls-Server.salam
Pending
func Pending(c: Conn): intThe example for this item is shown with the ones above: tls-Server.salam
Readable
func Readable(c: Conn): boolThe example for this item is shown with the ones above: tls-Server.salam
Fd
func Fd(c: Conn): i64The example for this item is shown with the ones above: tls-Server.salam
Detach
func Detach(c &: Conn)import tls
import net.tcp
func main:
sock := tcp.Dial("example.com", 443)
if not tcp.ConnOk(sock):
println "the connection was not made"
ret
end
mut conn := tls.WrapClient(sock.fd, tls.NewConfig("example.com"))
println tls.ConnOk(conn)
tls.Detach(conn)
tcp.Close(sock)
endClose
func Close(c &: Conn)The example for this item is shown with the ones above: tls-Client.salam
Version
func Version(c: Conn): strTLS1.3.The example for this item is shown with the ones above: tls-Client.salam
CipherName
func CipherName(c: Conn): strThe example for this item is shown with the ones above: tls-Client.salam
PeerCertCount
func PeerCertCount(c: Conn): intThe example for this item is shown with the ones above: tls-Client.salam
HexEncodeBuf
func HexEncodeBuf(b: Buf): strThe example for this item is shown with the ones above: tls-Crypto.salam
HexDecodeBuf
func HexDecodeBuf(s: str): BufThe example for this item is shown with the ones above: tls-Crypto.salam
NewBuf
func NewBuf(cap: int): BufThe example for this item is shown with the ones above: tls-Crypto.salam
NewZeroBuf
func NewZeroBuf(n: int): BufThe example for this item is shown with the ones above: tls-Crypto.salam
FreeBuf
func FreeBuf(b &: Buf)The example for this item is shown with the ones above: tls-Crypto.salam
WipeBuf
func WipeBuf(b &: Buf)The example for this item is shown with the ones above: tls-Crypto.salam
BufLen
func BufLen(b: Buf): intThe example for this item is shown with the ones above: tls-Crypto.salam
BufAppendStr
func BufAppendStr(b &: Buf, s: str)The example for this item is shown with the ones above: tls-Crypto.salam
LoadRoots
func LoadRoots(ca_file: str): RootsThe example for this item is shown with the ones above: tls-Certs.salam
RootCount
func RootCount(r: Roots): intThe example for this item is shown with the ones above: tls-Certs.salam
FreeRoots
func FreeRoots(r &: Roots)The example for this item is shown with the ones above: tls-Certs.salam
VerifyError
func VerifyError(code: int): strThe example for this item is shown with the ones above: tls-Certs.salam
VerifyCert
func VerifyCert(der: Buf, roots: Roots, host: str): intThe example for this item is shown with the ones above: tls-Certs.salam
CertIsValid
func CertIsValid(der: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam
CertSubjectCN
func CertSubjectCN(der: Buf): strThe example for this item is shown with the ones above: tls-Certs.salam
HashDigest
func HashDigest(sha384: bool, msg: str): BufThe example for this item is shown with the ones above: tls-Crypto.salam
SignRSAPKCS1
func SignRSAPKCS1(k: PrivateKey, sha384: bool, digest: Buf, sigout: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam
SignRSAPSS
func SignRSAPSS(k: PrivateKey, sha384: bool, digest: Buf, sigout: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam
SignECDSA
func SignECDSA(k: PrivateKey, digest: Buf, rout: Buf, sout: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam
ECDSADerEncode
func ECDSADerEncode(rbytes: Buf, sbytes: Buf, width: int): BufThe example for this item is shown with the ones above: tls-Certs.salam
VerifyRSAPSSWithKey
func VerifyRSAPSSWithKey(k: PrivateKey, sha384: bool, digest: Buf, sig: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam
VerifyECDSADERWithKey
func VerifyECDSADERWithKey(k: PrivateKey, digest: Buf, dersig: Buf): boolThe example for this item is shown with the ones above: tls-Certs.salam