Architecture¶
Where the code comes from¶
The package is a trimmed copy of crypto/tls from Go 1.27.2. It keeps the record layer, the TLS 1.2 key schedule and the handshake message encoding. It replaces the key
exchange with SRP. Code that
SRP does not use is removed: TLS 1.3, session resumption, certificates, ALPN and QUIC.
| File | Content |
|---|---|
conn.go |
Conn, the record layer, CBC record protection, Handshake, Read, Write and Close |
common.go |
Config, ConnectionState, ClientHelloInfo and the sentinel errors |
tls.go |
Client, Server, Listen and NewListener |
handshake_server.go |
The server handshake |
handshake_client.go |
The client handshake, which follows the TLS 1.2 part of Go's client |
handshake_messages.go |
Encoding and parsing of the handshake messages, including the SRP structures |
cipher_suites.go |
The three SRP cipher suites |
prf.go |
The TLS 1.2 PRF, the master secret, Extended Master Secret and exported keying material |
alert.go |
TLS alerts |
srp.go |
Groups, verifiers, SimulatedVerifier and the SRP computations |
groups_rfc5054.go |
The primes and generators of RFC 5054, Appendix A |
alert.go, cipher_suites.go, common.go, conn.go, handshake_messages.go,
handshake_server.go, prf.go and tls.go are tracked against the same files in newer Go
releases, so that fixes to Go's TLS code are ported. Development
describes the check.
The SRP arithmetic uses filippo.io/bigmod, the
constant-time modular arithmetic of the Go standard library exported as a module. Messages are
parsed with golang.org/x/crypto/cryptobyte, as in crypto/tls.
The handshake¶
Every connection runs a full TLS 1.2 handshake with the SRP key exchange of RFC 5054, Section 2.2. There is no certificate message and no signature.
sequenceDiagram
participant C as Client
participant S as Server
C->>S: ClientHello with the username in the srp extension
Note over S: picks a cipher suite, calls GetVerifier
S->>C: ServerHello
S->>C: ServerKeyExchange with N, g, salt and B
S->>C: ServerHelloDone
Note over C: checks the group and B, computes A and the premaster secret
C->>S: ClientKeyExchange with A
C->>S: ChangeCipherSpec and Finished
Note over S: checks A, computes the premaster secret, verifies the Finished message
S->>C: ChangeCipherSpec and Finished
Note over C: verifies the Finished message
The server computes B = k*v + g^b mod N from the stored verifier v. The client computes the
premaster secret from the password and the server from the verifier
(RFC 5054, Section 2.6). Both arrive at the same
value only when the verifier was made from the password the client used. Nothing else in the
handshake checks the password.
With a wrong password the two sides derive different keys. The server cannot decrypt the client's
Finished message and sends a bad_record_mac alert, which the client reports as a wrong password. A
server that does not hold the verifier cannot produce a Finished message the client can decrypt, so
the client ends the handshake with its own bad_record_mac alert. That is how the client
authenticates the server without a certificate.
The random exponents a and b are 384 bits long. RFC 5054 requires that A and B are
not zero modulo N. The package also refuses values that are not below N or that give u = 0. A
failed check ends the handshake with an illegal_parameter alert.
When the handshake runs¶
The handshake runs when Handshake or HandshakeContext is called or on the first Read or Write.
It runs once. A failed handshake stores its error and every later call to Handshake, Read and
Write returns it. After a canceled HandshakeContext the stored error is the one from the closed
connection, not the context error.
On the server, GetVerifier is called once per handshake, after the cipher suite is chosen and before
any SRP computation. Its ClientHelloInfo.Context is canceled when the handshake ends.