Source File
rawkey.go
Belonging Package
github.com/tmc/go-iroh/internal/itls/tls
// RFC 7250 raw public key support for TLS 1.3.//// This file adds the negotiation glue and certificate handling for the// client_certificate_type / server_certificate_type extensions. The wire-level// extension marshaling lives in handshake_messages.go; the per-message// negotiation hooks call into the helpers here. When raw public keys are// negotiated, the Certificate message carries a bare SubjectPublicKeyInfo// instead of an X.509 chain.//// This is the addition that makes the vendored crypto/tls wire-compatible with// iroh's raw-public-key P2P handshake; it is not present in upstream crypto/tls.package tlsimport ()// RawPublicKeys, when set on a [Config], makes the connection use RFC 7250 raw// public keys for both the local certificate and peer verification (TLS 1.3// only, mutual). The local [Config.Certificates] entry's leaf bytes must be a// DER SubjectPublicKeyInfo (see [MarshalRawPublicKeyCertificate]); peer// verification is delegated to [Config.VerifyConnection], where the peer's// public key is available as ConnectionState.PeerCertificates[0].PublicKey.//// Session resumption (including 0-RTT) is supported: a resumed handshake skips// the Certificate message, so the peer's raw public key is recovered from the// certificate stored in the session ticket. The ticket carries the bare// SubjectPublicKeyInfo, which [ParseSessionState] decodes via// parseSessionStateCertificate.type rawPublicKeysConfig struct {enabled bool}// rawPublicKeysEnabled reports whether c opts into RFC 7250 raw public keys.func ( *Config) () bool {return != nil && .RawPublicKeys}// MarshalRawPublicKeyCertificate returns a [Certificate] suitable for a raw// public key handshake: its single leaf entry is the DER SubjectPublicKeyInfo of// pub, and priv is the matching private key. pub/priv must be an ed25519 key for// iroh compatibility, though any key type x509.MarshalPKIXPublicKey supports// works.func ( any, any) (Certificate, error) {, := x509.MarshalPKIXPublicKey()if != nil {return Certificate{},}return Certificate{Certificate: [][]byte{}, PrivateKey: }, nil}// parseRawPublicKeyCert parses a DER SubjectPublicKeyInfo into a synthetic// *x509.Certificate carrying only the PublicKey and PublicKeyAlgorithm, so the// existing CertificateVerify path (which reads peerCertificates[0].PublicKey)// works unchanged.func parseRawPublicKeyCert( []byte) (*x509.Certificate, error) {, := x509.ParsePKIXPublicKey()if != nil {return nil,}:= &x509.Certificate{// Raw mirrors RawSubjectPublicKeyInfo so the cert survives the session-ticket// round trip: SessionState.Bytes serializes peer certificates by their Raw// bytes (ticket.go certificatesToBytesSlice). A raw-key "certificate" has no// DER body of its own, so we carry the SPKI; parseSessionStateCertificate// reads it back with parseRawPublicKeyCert when x509.ParseCertificate fails.Raw: ,RawSubjectPublicKeyInfo: ,PublicKey: ,PublicKeyAlgorithm: publicKeyAlgorithmFor(),// RFC 7250 raw public keys carry no validity period: the SubjectPublicKeyInfo// has no NotBefore/NotAfter, and none is sent on the wire. The TLS session// resumption logic (handshake_client.go loadSession, handshake_server_tls13.go// checkForResumption) nonetheless guards on peerCertificates[0].NotAfter, so an// unbounded window keeps cached raw-key sessions valid for 0-RTT instead of// being discarded as expired. These fields are purely local and never serialized.NotBefore: time.Unix(0, 0),NotAfter: time.Unix(1<<62, 0),}return , nil}// parseSessionStateCertificate reconstructs a peer certificate stored in a TLS// 1.3 session ticket (see [ParseSessionState]). Tickets serialize each peer// certificate by its Raw bytes; for an X.509 chain those are a DER certificate,// but for an RFC 7250 raw public key they are a bare SubjectPublicKeyInfo, which// x509.ParseCertificate cannot decode. When the normal parse fails, fall back to// parseRawPublicKeyCert so resumption and 0-RTT work for raw-key peers. The two// encodings are unambiguous: an SPKI is never a valid certificate and vice// versa, so the fallback only triggers for genuine raw keys.func parseSessionStateCertificate( []byte) (*x509.Certificate, error) {, := globalCertCache.newCert()if == nil {return , nil}, := parseRawPublicKeyCert()if != nil {// Surface the original X.509 error; the raw-key fallback is best-effort.return nil,}return , nil}// publicKeyAlgorithmFor maps a parsed public key to its x509.PublicKeyAlgorithm.func publicKeyAlgorithmFor( any) x509.PublicKeyAlgorithm {switch .(type) {case ed25519.PublicKey:return x509.Ed25519case *ecdsa.PublicKey:return x509.ECDSAcase *rsa.PublicKey:return x509.RSAdefault:return x509.UnknownPublicKeyAlgorithm}}// verifyServerRawPublicKey handles the client-side verification of a server that// presented a raw public key (RFC 7250). The peer's public key is exposed via// ConnectionState.PeerCertificates[0].PublicKey for the application's// VerifyConnection callback (which performs the iroh endpoint-id check); there is// no X.509 chain to validate.func ( *Conn) ( [][]byte) error {if len() != 1 {.sendAlert(alertBadCertificate)return errors.New("tls: raw public key handshake requires exactly one certificate entry")}, := parseRawPublicKeyCert([0])if != nil {.sendAlert(alertDecodeError)return errors.New("tls: failed to parse server raw public key: " + .Error())}.peerCertificates = []*x509.Certificate{}if .config.VerifyPeerCertificate != nil {if := .config.VerifyPeerCertificate(, nil); != nil {.sendAlert(alertBadCertificate)return}}if .config.VerifyConnection != nil {if := .config.VerifyConnection(.connectionStateLocked()); != nil {.sendAlert(alertBadCertificate)return}}return nil}
![]() |
The pages are generated with Golds v0.8.4. (GOOS=linux GOARCH=amd64) Golds is a Go 101 project developed by Tapir Liu. PR and bug reports are welcome and can be submitted to the issue list. Please follow @zigo_101 (reachable from the left QR code) to get the latest news of Golds. |