ref: 9f766b50b237544efccc96e1b229c16cf99e36a9
author: Igor Böhm <[email protected]>
date: Tue Sep 27 18:34:28 EDT 2022
Initial commit. Decode Server Name Indicator extension in tlshand.c:/^checkClientExtensions
--- /dev/null
+++ b/README
@@ -1,0 +1,21 @@
+# Key Functions
+tlshand.c:/^tlsClientExtensions
+tlshand.c:/^checkClientExtensions
+tlshand.c:/^tlsConnectionFree
+
+# Data Types
+tlshand.c:/^typedef struct TlsConnection
+/sys/include/libsec.h:/^typedef struct TLSconn
+
+# Approach
+
+The function tlshand.c:/^checkClientExtensions must
+be extended to decode the server name indication. For this
+the 'tlshand.c:/^typedef struct TlsConnection' needs to
+be extended to hold the server name.
+
+tlshand.c:/^tlsConnectionFree needs to free any potential
+`TlsConnection.serverName`.
+
+
+
\ No newline at end of file
--- /dev/null
+++ b/mkfile
@@ -1,0 +1,9 @@
+</$objtype/mkfile
+
+BIN=$home/bin/$objtype
+TARG=tlssrv
+OFILES=tlssrv.$O tlshand.$O
+HFILES=/sys/include/libsec.h
+
+</sys/src/cmd/mkone
+
--- /dev/null
+++ b/rfc6066
@@ -1,0 +1,1403 @@
+
+
+
+
+
+
+Internet Engineering Task Force (IETF) D. Eastlake 3rd
+Request for Comments: 6066 Huawei
+Obsoletes: 4366 January 2011
+Category: Standards Track
+ISSN: 2070-1721
+
+
+ Transport Layer Security (TLS) Extensions: Extension Definitions
+
+Abstract
+
+ This document provides specifications for existing TLS extensions.
+ It is a companion document for RFC 5246, "The Transport Layer
+ Security (TLS) Protocol Version 1.2". The extensions specified are
+ server_name, max_fragment_length, client_certificate_url,
+ trusted_ca_keys, truncated_hmac, and status_request.
+
+Status of This Memo
+
+ This is an Internet Standards Track document.
+
+ This document is a product of the Internet Engineering Task Force
+ (IETF). It represents the consensus of the IETF community. It has
+ received public review and has been approved for publication by the
+ Internet Engineering Steering Group (IESG). Further information on
+ Internet Standards is available in Section 2 of RFC 5741.
+
+ Information about the current status of this document, any errata,
+ and how to provide feedback on it may be obtained at
+ http://www.rfc-editor.org/info/rfc6066.
+
+Copyright Notice
+
+ Copyright (c) 2011 IETF Trust and the persons identified as the
+ document authors. All rights reserved.
+
+ This document is subject to BCP 78 and the IETF Trust's Legal
+ Provisions Relating to IETF Documents
+ (http://trustee.ietf.org/license-info) in effect on the date of
+ publication of this document. Please review these documents
+ carefully, as they describe your rights and restrictions with respect
+ to this document. Code Components extracted from this document must
+ include Simplified BSD License text as described in Section 4.e of
+ the Trust Legal Provisions and are provided without warranty as
+ described in the Simplified BSD License.
+
+
+
+
+
+
+Eastlake Standards Track [Page 1]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ This document may contain material from IETF Documents or IETF
+ Contributions published or made publicly available before November
+ 10, 2008. The person(s) controlling the copyright in some of this
+ material may not have granted the IETF Trust the right to allow
+ modifications of such material outside the IETF Standards Process.
+ Without obtaining an adequate license from the person(s) controlling
+ the copyright in such materials, this document may not be modified
+ outside the IETF Standards Process, and derivative works of it may
+ not be created outside the IETF Standards Process, except to format
+ it for publication as an RFC or to translate it into languages other
+ than English.
+
+Table of Contents
+
+ 1. Introduction ....................................................3
+ 1.1. Specific Extensions Covered ................................3
+ 1.2. Conventions Used in This Document ..........................5
+ 2. Extensions to the Handshake Protocol ............................5
+ 3. Server Name Indication ..........................................6
+ 4. Maximum Fragment Length Negotiation .............................8
+ 5. Client Certificate URLs .........................................9
+ 6. Trusted CA Indication ..........................................12
+ 7. Truncated HMAC .................................................13
+ 8. Certificate Status Request .....................................14
+ 9. Error Alerts ...................................................16
+ 10. IANA Considerations ...........................................17
+ 10.1. pkipath MIME Type Registration ...........................17
+ 10.2. Reference for TLS Alerts, TLS HandshakeTypes, and
+ ExtensionTypes ...........................................19
+ 11. Security Considerations .......................................19
+ 11.1. Security Considerations for server_name ..................19
+ 11.2. Security Considerations for max_fragment_length ..........20
+ 11.3. Security Considerations for client_certificate_url .......20
+ 11.4. Security Considerations for trusted_ca_keys ..............21
+ 11.5. Security Considerations for truncated_hmac ...............21
+ 11.6. Security Considerations for status_request ...............22
+ 12. Normative References ..........................................22
+ 13. Informative References ........................................23
+ Appendix A. Changes from RFC 4366 .................................24
+ Appendix B. Acknowledgements ......................................25
+
+
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 2]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+1. Introduction
+
+ The Transport Layer Security (TLS) Protocol Version 1.2 is specified
+ in [RFC5246]. That specification includes the framework for
+ extensions to TLS, considerations in designing such extensions (see
+ Section 7.4.1.4 of [RFC5246]), and IANA Considerations for the
+ allocation of new extension code points; however, it does not specify
+ any particular extensions other than Signature Algorithms (see
+ Section 7.4.1.4.1 of [RFC5246]).
+
+ This document provides the specifications for existing TLS
+ extensions. It is, for the most part, the adaptation and editing of
+ material from RFC 4366, which covered TLS extensions for TLS 1.0 (RFC
+ 2246) and TLS 1.1 (RFC 4346).
+
+1.1. Specific Extensions Covered
+
+ The extensions described here focus on extending the functionality
+ provided by the TLS protocol message formats. Other issues, such as
+ the addition of new cipher suites, are deferred.
+
+ The extension types defined in this document are:
+
+ enum {
+ server_name(0), max_fragment_length(1),
+ client_certificate_url(2), trusted_ca_keys(3),
+ truncated_hmac(4), status_request(5), (65535)
+ } ExtensionType;
+
+ Specifically, the extensions described in this document:
+
+ - Allow TLS clients to provide to the TLS server the name of the
+ server they are contacting. This functionality is desirable in
+ order to facilitate secure connections to servers that host
+ multiple 'virtual' servers at a single underlying network address.
+
+ - Allow TLS clients and servers to negotiate the maximum fragment
+ length to be sent. This functionality is desirable as a result of
+ memory constraints among some clients, and bandwidth constraints
+ among some access networks.
+
+ - Allow TLS clients and servers to negotiate the use of client
+ certificate URLs. This functionality is desirable in order to
+ conserve memory on constrained clients.
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 3]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ - Allow TLS clients to indicate to TLS servers which certification
+ authority (CA) root keys they possess. This functionality is
+ desirable in order to prevent multiple handshake failures
+ involving TLS clients that are only able to store a small number
+ of CA root keys due to memory limitations.
+
+ - Allow TLS clients and servers to negotiate the use of truncated
+ Message Authentication Codes (MACs). This functionality is
+ desirable in order to conserve bandwidth in constrained access
+ networks.
+
+ - Allow TLS clients and servers to negotiate that the server sends
+ the client certificate status information (e.g., an Online
+ Certificate Status Protocol (OCSP) [RFC2560] response) during a
+ TLS handshake. This functionality is desirable in order to avoid
+ sending a Certificate Revocation List (CRL) over a constrained
+ access network and therefore saving bandwidth.
+
+ TLS clients and servers may use the extensions described in this
+ document. The extensions are designed to be backwards compatible,
+ meaning that TLS clients that support the extensions can talk to TLS
+ servers that do not support the extensions, and vice versa.
+
+ Note that any messages associated with these extensions that are sent
+ during the TLS handshake MUST be included in the hash calculations
+ involved in "Finished" messages.
+
+ Note also that all the extensions defined in this document are
+ relevant only when a session is initiated. A client that requests
+ session resumption does not in general know whether the server will
+ accept this request, and therefore it SHOULD send the same extensions
+ as it would send if it were not attempting resumption. When a client
+ includes one or more of the defined extension types in an extended
+ client hello while requesting session resumption:
+
+ - The server name indication extension MAY be used by the server
+ when deciding whether or not to resume a session as described in
+ Section 3.
+
+ - If the resumption request is denied, the use of the extensions is
+ negotiated as normal.
+
+ - If, on the other hand, the older session is resumed, then the
+ server MUST ignore the extensions and send a server hello
+ containing none of the extension types. In this case, the
+ functionality of these extensions negotiated during the original
+ session initiation is applied to the resumed session.
+
+
+
+
+Eastlake Standards Track [Page 4]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+1.2. Conventions Used in This Document
+
+ The key words "MUST", "MUST NOT", "REQUIRED", "SHALL", "SHALL NOT",
+ "SHOULD", "SHOULD NOT", "RECOMMENDED", "NOT RECOMMENDED", "MAY", and
+ "OPTIONAL" in this document are to be interpreted as described in
+ [RFC2119].
+
+2. Extensions to the Handshake Protocol
+
+ This document specifies the use of two new handshake messages,
+ "CertificateURL" and "CertificateStatus". These messages are
+ described in Sections 5 and 8, respectively. The new handshake
+ message structure therefore becomes:
+
+ enum {
+ hello_request(0), client_hello(1), server_hello(2),
+ certificate(11), server_key_exchange (12),
+ certificate_request(13), server_hello_done(14),
+ certificate_verify(15), client_key_exchange(16),
+ finished(20), certificate_url(21), certificate_status(22),
+ (255)
+ } HandshakeType;
+
+ struct {
+ HandshakeType msg_type; /* handshake type */
+ uint24 length; /* bytes in message */
+ select (HandshakeType) {
+ case hello_request: HelloRequest;
+ case client_hello: ClientHello;
+ case server_hello: ServerHello;
+ case certificate: Certificate;
+ case server_key_exchange: ServerKeyExchange;
+ case certificate_request: CertificateRequest;
+ case server_hello_done: ServerHelloDone;
+ case certificate_verify: CertificateVerify;
+ case client_key_exchange: ClientKeyExchange;
+ case finished: Finished;
+ case certificate_url: CertificateURL;
+ case certificate_status: CertificateStatus;
+ } body;
+ } Handshake;
+
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 5]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+3. Server Name Indication
+
+ TLS does not provide a mechanism for a client to tell a server the
+ name of the server it is contacting. It may be desirable for clients
+ to provide this information to facilitate secure connections to
+ servers that host multiple 'virtual' servers at a single underlying
+ network address.
+
+ In order to provide any of the server names, clients MAY include an
+ extension of type "server_name" in the (extended) client hello. The
+ "extension_data" field of this extension SHALL contain
+ "ServerNameList" where:
+
+ struct {
+ NameType name_type;
+ select (name_type) {
+ case host_name: HostName;
+ } name;
+ } ServerName;
+
+ enum {
+ host_name(0), (255)
+ } NameType;
+
+ opaque HostName<1..2^16-1>;
+
+ struct {
+ ServerName server_name_list<1..2^16-1>
+ } ServerNameList;
+
+ The ServerNameList MUST NOT contain more than one name of the same
+ name_type. If the server understood the ClientHello extension but
+ does not recognize the server name, the server SHOULD take one of two
+ actions: either abort the handshake by sending a fatal-level
+ unrecognized_name(112) alert or continue the handshake. It is NOT
+ RECOMMENDED to send a warning-level unrecognized_name(112) alert,
+ because the client's behavior in response to warning-level alerts is
+ unpredictable. If there is a mismatch between the server name used
+ by the client application and the server name of the credential
+ chosen by the server, this mismatch will become apparent when the
+ client application performs the server endpoint identification, at
+ which point the client application will have to decide whether to
+ proceed with the communication. TLS implementations are encouraged
+ to make information available to application callers about warning-
+ level alerts that were received or sent during a TLS handshake. Such
+ information can be useful for diagnostic purposes.
+
+
+
+
+
+Eastlake Standards Track [Page 6]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ Note: Earlier versions of this specification permitted multiple
+ names of the same name_type. In practice, current client
+ implementations only send one name, and the client cannot
+ necessarily find out which name the server selected. Multiple
+ names of the same name_type are therefore now prohibited.
+
+ Currently, the only server names supported are DNS hostnames;
+ however, this does not imply any dependency of TLS on DNS, and other
+ name types may be added in the future (by an RFC that updates this
+ document). The data structure associated with the host_name NameType
+ is a variable-length vector that begins with a 16-bit length. For
+ backward compatibility, all future data structures associated with
+ new NameTypes MUST begin with a 16-bit length field. TLS MAY treat
+ provided server names as opaque data and pass the names and types to
+ the application.
+
+ "HostName" contains the fully qualified DNS hostname of the server,
+ as understood by the client. The hostname is represented as a byte
+ string using ASCII encoding without a trailing dot. This allows the
+ support of internationalized domain names through the use of A-labels
+ defined in [RFC5890]. DNS hostnames are case-insensitive. The
+ algorithm to compare hostnames is described in [RFC5890], Section
+ 2.3.2.4.
+
+ Literal IPv4 and IPv6 addresses are not permitted in "HostName".
+
+ It is RECOMMENDED that clients include an extension of type
+ "server_name" in the client hello whenever they locate a server by a
+ supported name type.
+
+ A server that receives a client hello containing the "server_name"
+ extension MAY use the information contained in the extension to guide
+ its selection of an appropriate certificate to return to the client,
+ and/or other aspects of security policy. In this event, the server
+ SHALL include an extension of type "server_name" in the (extended)
+ server hello. The "extension_data" field of this extension SHALL be
+ empty.
+
+ When the server is deciding whether or not to accept a request to
+ resume a session, the contents of a server_name extension MAY be used
+ in the lookup of the session in the session cache. The client SHOULD
+ include the same server_name extension in the session resumption
+ request as it did in the full handshake that established the session.
+ A server that implements this extension MUST NOT accept the request
+ to resume the session if the server_name extension contains a
+ different name. Instead, it proceeds with a full handshake to
+ establish a new session. When resuming a session, the server MUST
+ NOT include a server_name extension in the server hello.
+
+
+
+Eastlake Standards Track [Page 7]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ If an application negotiates a server name using an application
+ protocol and then upgrades to TLS, and if a server_name extension is
+ sent, then the extension SHOULD contain the same name that was
+ negotiated in the application protocol. If the server_name is
+ established in the TLS session handshake, the client SHOULD NOT
+ attempt to request a different server name at the application layer.
+
+4. Maximum Fragment Length Negotiation
+
+ Without this extension, TLS specifies a fixed maximum plaintext
+ fragment length of 2^14 bytes. It may be desirable for constrained
+ clients to negotiate a smaller maximum fragment length due to memory
+ limitations or bandwidth limitations.
+
+ In order to negotiate smaller maximum fragment lengths, clients MAY
+ include an extension of type "max_fragment_length" in the (extended)
+ client hello. The "extension_data" field of this extension SHALL
+ contain:
+
+ enum{
+ 2^9(1), 2^10(2), 2^11(3), 2^12(4), (255)
+ } MaxFragmentLength;
+
+ whose value is the desired maximum fragment length. The allowed
+ values for this field are: 2^9, 2^10, 2^11, and 2^12.
+
+ Servers that receive an extended client hello containing a
+ "max_fragment_length" extension MAY accept the requested maximum
+ fragment length by including an extension of type
+ "max_fragment_length" in the (extended) server hello. The
+ "extension_data" field of this extension SHALL contain a
+ "MaxFragmentLength" whose value is the same as the requested maximum
+ fragment length.
+
+ If a server receives a maximum fragment length negotiation request
+ for a value other than the allowed values, it MUST abort the
+ handshake with an "illegal_parameter" alert. Similarly, if a client
+ receives a maximum fragment length negotiation response that differs
+ from the length it requested, it MUST also abort the handshake with
+ an "illegal_parameter" alert.
+
+ Once a maximum fragment length other than 2^14 has been successfully
+ negotiated, the client and server MUST immediately begin fragmenting
+ messages (including handshake messages) to ensure that no fragment
+ larger than the negotiated length is sent. Note that TLS already
+ requires clients and servers to support fragmentation of handshake
+ messages.
+
+
+
+
+Eastlake Standards Track [Page 8]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ The negotiated length applies for the duration of the session
+ including session resumptions.
+
+ The negotiated length limits the input that the record layer may
+ process without fragmentation (that is, the maximum value of
+ TLSPlaintext.length; see [RFC5246], Section 6.2.1). Note that the
+ output of the record layer may be larger. For example, if the
+ negotiated length is 2^9=512, then, when using currently defined
+ cipher suites (those defined in [RFC5246] and [RFC2712]) and null
+ compression, the record-layer output can be at most 805 bytes: 5
+ bytes of headers, 512 bytes of application data, 256 bytes of
+ padding, and 32 bytes of MAC. This means that in this event a TLS
+ record-layer peer receiving a TLS record-layer message larger than
+ 805 bytes MUST discard the message and send a "record_overflow"
+ alert, without decrypting the message. When this extension is used
+ with Datagram Transport Layer Security (DTLS), implementations SHOULD
+ NOT generate record_overflow alerts unless the packet passes message
+ authentication.
+
+5. Client Certificate URLs
+
+ Without this extension, TLS specifies that when client authentication
+ is performed, client certificates are sent by clients to servers
+ during the TLS handshake. It may be desirable for constrained
+ clients to send certificate URLs in place of certificates, so that
+ they do not need to store their certificates and can therefore save
+ memory.
+
+ In order to negotiate sending certificate URLs to a server, clients
+ MAY include an extension of type "client_certificate_url" in the
+ (extended) client hello. The "extension_data" field of this
+ extension SHALL be empty.
+
+ (Note that it is necessary to negotiate the use of client certificate
+ URLs in order to avoid "breaking" existing TLS servers.)
+
+ Servers that receive an extended client hello containing a
+ "client_certificate_url" extension MAY indicate that they are willing
+ to accept certificate URLs by including an extension of type
+ "client_certificate_url" in the (extended) server hello. The
+ "extension_data" field of this extension SHALL be empty.
+
+ After negotiation of the use of client certificate URLs has been
+ successfully completed (by exchanging hellos including
+ "client_certificate_url" extensions), clients MAY send a
+ "CertificateURL" message in place of a "Certificate" message as
+ follows (see also Section 2):
+
+
+
+
+Eastlake Standards Track [Page 9]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ enum {
+ individual_certs(0), pkipath(1), (255)
+ } CertChainType;
+
+ struct {
+ CertChainType type;
+ URLAndHash url_and_hash_list<1..2^16-1>;
+ } CertificateURL;
+
+ struct {
+ opaque url<1..2^16-1>;
+ unint8 padding;
+ opaque SHA1Hash[20];
+ } URLAndHash;
+
+ Here, "url_and_hash_list" contains a sequence of URLs and hashes.
+ Each "url" MUST be an absolute URI reference according to [RFC3986]
+ that can be immediately used to fetch the certificate(s).
+
+ When X.509 certificates are used, there are two possibilities:
+
+ - If CertificateURL.type is "individual_certs", each URL refers to a
+ single DER-encoded X.509v3 certificate, with the URL for the
+ client's certificate first.
+
+ - If CertificateURL.type is "pkipath", the list contains a single
+ URL referring to a DER-encoded certificate chain, using the type
+ PkiPath described in Section 10.1.
+
+ When any other certificate format is used, the specification that
+ describes use of that format in TLS should define the encoding format
+ of certificates or certificate chains, and any constraint on their
+ ordering.
+
+ The "padding" byte MUST be 0x01. It is present to make the structure
+ backwards compatible.
+
+ The hash corresponding to each URL is the SHA-1 hash of the
+ certificate or certificate chain (in the case of X.509 certificates,
+ the DER-encoded certificate or the DER-encoded PkiPath).
+
+ Note that when a list of URLs for X.509 certificates is used, the
+ ordering of URLs is the same as that used in the TLS Certificate
+ message (see [RFC5246], Section 7.4.2), but opposite to the order in
+ which certificates are encoded in PkiPath. In either case, the self-
+ signed root certificate MAY be omitted from the chain, under the
+ assumption that the server must already possess it in order to
+ validate it.
+
+
+
+Eastlake Standards Track [Page 10]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ Servers receiving "CertificateURL" SHALL attempt to retrieve the
+ client's certificate chain from the URLs and then process the
+ certificate chain as usual. A cached copy of the content of any URL
+ in the chain MAY be used, provided that the SHA-1 hash matches the
+ hash of the cached copy.
+
+ Servers that support this extension MUST support the 'http' URI
+ scheme for certificate URLs and MAY support other schemes. Use of
+ other schemes than 'http', 'https', or 'ftp' may create unexpected
+ problems.
+
+ If the protocol used is HTTP, then the HTTP server can be configured
+ to use the Cache-Control and Expires directives described in
+ [RFC2616] to specify whether and for how long certificates or
+ certificate chains should be cached.
+
+ The TLS server MUST NOT follow HTTP redirects when retrieving the
+ certificates or certificate chain. The URLs used in this extension
+ MUST NOT be chosen to depend on such redirects.
+
+ If the protocol used to retrieve certificates or certificate chains
+ returns a MIME-formatted response (as HTTP does), then the following
+ MIME Content-Types SHALL be used: when a single X.509v3 certificate
+ is returned, the Content-Type is "application/pkix-cert" [RFC2585],
+ and when a chain of X.509v3 certificates is returned, the Content-
+ Type is "application/pkix-pkipath" (Section 10.1).
+
+ The server MUST check that the SHA-1 hash of the contents of the
+ object retrieved from that URL (after decoding any MIME Content-
+ Transfer-Encoding) matches the given hash. If any retrieved object
+ does not have the correct SHA-1 hash, the server MUST abort the
+ handshake with a bad_certificate_hash_value(114) alert. This alert
+ is always fatal.
+
+ Clients may choose to send either "Certificate" or "CertificateURL"
+ after successfully negotiating the option to send certificate URLs.
+ The option to send a certificate is included to provide flexibility
+ to clients possessing multiple certificates.
+
+ If a server is unable to obtain certificates in a given
+ CertificateURL, it MUST send a fatal certificate_unobtainable(111)
+ alert if it requires the certificates to complete the handshake. If
+ the server does not require the certificates, then the server
+ continues the handshake. The server MAY send a warning-level alert
+ in this case. Clients receiving such an alert SHOULD log the alert
+ and continue with the handshake if possible.
+
+
+
+
+
+Eastlake Standards Track [Page 11]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+6. Trusted CA Indication
+
+ Constrained clients that, due to memory limitations, possess only a
+ small number of CA root keys may wish to indicate to servers which
+ root keys they possess, in order to avoid repeated handshake
+ failures.
+
+ In order to indicate which CA root keys they possess, clients MAY
+ include an extension of type "trusted_ca_keys" in the (extended)
+ client hello. The "extension_data" field of this extension SHALL
+ contain "TrustedAuthorities" where:
+
+ struct {
+ TrustedAuthority trusted_authorities_list<0..2^16-1>;
+ } TrustedAuthorities;
+
+ struct {
+ IdentifierType identifier_type;
+ select (identifier_type) {
+ case pre_agreed: struct {};
+ case key_sha1_hash: SHA1Hash;
+ case x509_name: DistinguishedName;
+ case cert_sha1_hash: SHA1Hash;
+ } identifier;
+ } TrustedAuthority;
+
+ enum {
+ pre_agreed(0), key_sha1_hash(1), x509_name(2),
+ cert_sha1_hash(3), (255)
+ } IdentifierType;
+
+ opaque DistinguishedName<1..2^16-1>;
+
+ Here, "TrustedAuthorities" provides a list of CA root key identifiers
+ that the client possesses. Each CA root key is identified via
+ either:
+
+ - "pre_agreed": no CA root key identity supplied.
+
+ - "key_sha1_hash": contains the SHA-1 hash of the CA root key. For
+ Digital Signature Algorithm (DSA) and Elliptic Curve Digital
+ Signature Algorithm (ECDSA) keys, this is the hash of the
+ "subjectPublicKey" value. For RSA keys, the hash is of the big-
+ endian byte string representation of the modulus without any
+ initial zero-valued bytes. (This copies the key hash formats
+ deployed in other environments.)
+
+
+
+
+
+Eastlake Standards Track [Page 12]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ - "x509_name": contains the DER-encoded X.509 DistinguishedName of
+ the CA.
+
+ - "cert_sha1_hash": contains the SHA-1 hash of a DER-encoded
+ Certificate containing the CA root key.
+
+ Note that clients may include none, some, or all of the CA root keys
+ they possess in this extension.
+
+ Note also that it is possible that a key hash or a Distinguished Name
+ alone may not uniquely identify a certificate issuer (for example, if
+ a particular CA has multiple key pairs). However, here we assume
+ this is the case following the use of Distinguished Names to identify
+ certificate issuers in TLS.
+
+ The option to include no CA root keys is included to allow the client
+ to indicate possession of some pre-defined set of CA root keys.
+
+ Servers that receive a client hello containing the "trusted_ca_keys"
+ extension MAY use the information contained in the extension to guide
+ their selection of an appropriate certificate chain to return to the
+ client. In this event, the server SHALL include an extension of type
+ "trusted_ca_keys" in the (extended) server hello. The
+ "extension_data" field of this extension SHALL be empty.
+
+7. Truncated HMAC
+
+ Currently defined TLS cipher suites use the MAC construction HMAC
+ [RFC2104] to authenticate record-layer communications. In TLS, the
+ entire output of the hash function is used as the MAC tag. However,
+ it may be desirable in constrained environments to save bandwidth by
+ truncating the output of the hash function to 80 bits when forming
+ MAC tags.
+
+ In order to negotiate the use of 80-bit truncated HMAC, clients MAY
+ include an extension of type "truncated_hmac" in the extended client
+ hello. The "extension_data" field of this extension SHALL be empty.
+
+ Servers that receive an extended hello containing a "truncated_hmac"
+ extension MAY agree to use a truncated HMAC by including an extension
+ of type "truncated_hmac", with empty "extension_data", in the
+ extended server hello.
+
+ Note that if new cipher suites are added that do not use HMAC, and
+ the session negotiates one of these cipher suites, this extension
+ will have no effect. It is strongly recommended that any new cipher
+ suites using other MACs consider the MAC size an integral part of the
+
+
+
+
+Eastlake Standards Track [Page 13]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ cipher suite definition, taking into account both security and
+ bandwidth considerations.
+
+ If HMAC truncation has been successfully negotiated during a TLS
+ handshake, and the negotiated cipher suite uses HMAC, both the client
+ and the server pass this fact to the TLS record layer along with the
+ other negotiated security parameters. Subsequently during the
+ session, clients and servers MUST use truncated HMACs, calculated as
+ specified in [RFC2104]. That is, SecurityParameters.mac_length is 10
+ bytes, and only the first 10 bytes of the HMAC output are transmitted
+ and checked. Note that this extension does not affect the
+ calculation of the pseudo-random function (PRF) as part of
+ handshaking or key derivation.
+
+ The negotiated HMAC truncation size applies for the duration of the
+ session including session resumptions.
+
+8. Certificate Status Request
+
+ Constrained clients may wish to use a certificate-status protocol
+ such as OCSP [RFC2560] to check the validity of server certificates,
+ in order to avoid transmission of CRLs and therefore save bandwidth
+ on constrained networks. This extension allows for such information
+ to be sent in the TLS handshake, saving roundtrips and resources.
+
+ In order to indicate their desire to receive certificate status
+ information, clients MAY include an extension of type
+ "status_request" in the (extended) client hello. The
+ "extension_data" field of this extension SHALL contain
+ "CertificateStatusRequest" where:
+
+ struct {
+ CertificateStatusType status_type;
+ select (status_type) {
+ case ocsp: OCSPStatusRequest;
+ } request;
+ } CertificateStatusRequest;
+
+ enum { ocsp(1), (255) } CertificateStatusType;
+
+ struct {
+ ResponderID responder_id_list<0..2^16-1>;
+ Extensions request_extensions;
+ } OCSPStatusRequest;
+
+ opaque ResponderID<1..2^16-1>;
+ opaque Extensions<0..2^16-1>;
+
+
+
+
+Eastlake Standards Track [Page 14]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ In the OCSPStatusRequest, the "ResponderIDs" provides a list of OCSP
+ responders that the client trusts. A zero-length "responder_id_list"
+ sequence has the special meaning that the responders are implicitly
+ known to the server, e.g., by prior arrangement. "Extensions" is a
+ DER encoding of OCSP request extensions.
+
+ Both "ResponderID" and "Extensions" are DER-encoded ASN.1 types as
+ defined in [RFC2560]. "Extensions" is imported from [RFC5280]. A
+ zero-length "request_extensions" value means that there are no
+ extensions (as opposed to a zero-length ASN.1 SEQUENCE, which is not
+ valid for the "Extensions" type).
+
+ In the case of the "id-pkix-ocsp-nonce" OCSP extension, [RFC2560] is
+ unclear about its encoding; for clarification, the nonce MUST be a
+ DER-encoded OCTET STRING, which is encapsulated as another OCTET
+ STRING (note that implementations based on an existing OCSP client
+ will need to be checked for conformance to this requirement).
+
+ Servers that receive a client hello containing the "status_request"
+ extension MAY return a suitable certificate status response to the
+ client along with their certificate. If OCSP is requested, they
+ SHOULD use the information contained in the extension when selecting
+ an OCSP responder and SHOULD include request_extensions in the OCSP
+ request.
+
+ Servers return a certificate response along with their certificate by
+ sending a "CertificateStatus" message immediately after the
+ "Certificate" message (and before any "ServerKeyExchange" or
+ "CertificateRequest" messages). If a server returns a
+ "CertificateStatus" message, then the server MUST have included an
+ extension of type "status_request" with empty "extension_data" in the
+ extended server hello. The "CertificateStatus" message is conveyed
+ using the handshake message type "certificate_status" as follows (see
+ also Section 2):
+
+ struct {
+ CertificateStatusType status_type;
+ select (status_type) {
+ case ocsp: OCSPResponse;
+ } response;
+ } CertificateStatus;
+
+ opaque OCSPResponse<1..2^24-1>;
+
+ An "ocsp_response" contains a complete, DER-encoded OCSP response
+ (using the ASN.1 type OCSPResponse defined in [RFC2560]). Only one
+ OCSP response may be sent.
+
+
+
+
+Eastlake Standards Track [Page 15]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ Note that a server MAY also choose not to send a "CertificateStatus"
+ message, even if has received a "status_request" extension in the
+ client hello message and has sent a "status_request" extension in the
+ server hello message.
+
+ Note in addition that a server MUST NOT send the "CertificateStatus"
+ message unless it received a "status_request" extension in the client
+ hello message and sent a "status_request" extension in the server
+ hello message.
+
+ Clients requesting an OCSP response and receiving an OCSP response in
+ a "CertificateStatus" message MUST check the OCSP response and abort
+ the handshake if the response is not satisfactory with
+ bad_certificate_status_response(113) alert. This alert is always
+ fatal.
+
+9. Error Alerts
+
+ Four new error alerts are defined for use with the TLS extensions
+ defined in this document. To avoid "breaking" existing clients and
+ servers, these alerts MUST NOT be sent unless the sending party has
+ received an extended hello message from the party they are
+ communicating with. These error alerts are conveyed using the
+ following syntax. The new alerts are the last four, as indicated by
+ the comments on the same line as the error alert number.
+
+ enum {
+ close_notify(0),
+ unexpected_message(10),
+ bad_record_mac(20),
+ decryption_failed(21),
+ record_overflow(22),
+ decompression_failure(30),
+ handshake_failure(40),
+ /* 41 is not defined, for historical reasons */
+ bad_certificate(42),
+ unsupported_certificate(43),
+ certificate_revoked(44),
+ certificate_expired(45),
+ certificate_unknown(46),
+ illegal_parameter(47),
+ unknown_ca(48),
+ access_denied(49),
+ decode_error(50),
+ decrypt_error(51),
+ export_restriction(60),
+ protocol_version(70),
+ insufficient_security(71),
+
+
+
+Eastlake Standards Track [Page 16]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ internal_error(80),
+ user_canceled(90),
+ no_renegotiation(100),
+ unsupported_extension(110),
+ certificate_unobtainable(111), /* new */
+ unrecognized_name(112), /* new */
+ bad_certificate_status_response(113), /* new */
+ bad_certificate_hash_value(114), /* new */
+ (255)
+ } AlertDescription;
+
+ "certificate_unobtainable" is described in Section 5.
+ "unrecognized_name" is described in Section 3.
+ "bad_certificate_status_response" is described in Section 8.
+ "bad_certificate_hash_value" is described in Section 5.
+
+10. IANA Considerations
+
+ IANA Considerations for TLS extensions and the creation of a registry
+ are covered in Section 12 of [RFC5246] except for the registration of
+ MIME type application/pkix-pkipath, which appears below.
+
+ The IANA TLS extensions and MIME type application/pkix-pkipath
+ registry entries that reference RFC 4366 have been updated to
+ reference this document.
+
+10.1. pkipath MIME Type Registration
+
+ MIME media type name: application
+ MIME subtype name: pkix-pkipath
+ Required parameters: none
+
+ Optional parameters: version (default value is "1")
+
+ Encoding considerations:
+ Binary; this MIME type is a DER encoding of the ASN.1 type
+ PkiPath, defined as follows:
+ PkiPath ::= SEQUENCE OF Certificate
+ PkiPath is used to represent a certification path. Within the
+ sequence, the order of certificates is such that the subject of
+ the first certificate is the issuer of the second certificate,
+ etc.
+ This is identical to the definition published in [X509-4th-TC1];
+ note that it is different from that in [X509-4th].
+
+ All Certificates MUST conform to [RFC5280]. (This should be
+ interpreted as a requirement to encode only PKIX-conformant
+ certificates using this type. It does not necessarily require
+
+
+
+Eastlake Standards Track [Page 17]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ that all certificates that are not strictly PKIX-conformant must
+ be rejected by relying parties, although the security consequences
+ of accepting any such certificates should be considered
+ carefully.)
+
+ DER (as opposed to BER) encoding MUST be used. If this type is
+ sent over a 7-bit transport, base64 encoding SHOULD be used.
+
+ Security considerations:
+ The security considerations of [X509-4th] and [RFC5280] (or any
+ updates to them) apply, as well as those of any protocol that uses
+ this type (e.g., TLS).
+
+ Note that this type only specifies a certificate chain that can be
+ assessed for validity according to the relying party's existing
+ configuration of trusted CAs; it is not intended to be used to
+ specify any change to that configuration.
+
+ Interoperability considerations:
+ No specific interoperability problems are known with this type,
+ but for recommendations relating to X.509 certificates in general,
+ see [RFC5280].
+
+ Published specification: This document and [RFC5280].
+
+ Applications that use this media type:
+ TLS. It may also be used by other protocols or for general
+ interchange of PKIX certificate chains.
+
+ Additional information:
+ Magic number(s): DER-encoded ASN.1 can be easily recognized.
+ Further parsing is required to distinguish it from other ASN.1
+ types.
+ File extension(s): .pkipath
+ Macintosh File Type Code(s): not specified
+
+ Person & email address to contact for further information:
+ Magnus Nystrom <[email protected]>
+
+ Intended usage: COMMON
+
+ Change controller: IESG <[email protected]>
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 18]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+10.2. Reference for TLS Alerts, TLS HandshakeTypes, and ExtensionTypes
+
+ The following values in the TLS Alert Registry have been updated to
+ reference this document:
+
+ 111 certificate_unobtainable
+ 112 unrecognized_name
+ 113 bad_certificate_status_response
+ 114 bad_certificate_hash_value
+
+ The following values in the TLS HandshakeType Registry have been
+ updated to reference this document:
+
+ 21 certificate_url
+ 22 certificate_status
+
+ The following ExtensionType values have been updated to reference
+ this document:
+
+ 0 server_name
+ 1 max_fragment_length
+ 2 client_certificate_url
+ 3 trusted_ca_keys
+ 4 truncated_hmac
+ 5 status_request
+
+11. Security Considerations
+
+ General security considerations for TLS extensions are covered in
+ [RFC5246]. Security Considerations for particular extensions
+ specified in this document are given below.
+
+ In general, implementers should continue to monitor the state of the
+ art and address any weaknesses identified.
+
+11.1. Security Considerations for server_name
+
+ If a single server hosts several domains, then clearly it is
+ necessary for the owners of each domain to ensure that this satisfies
+ their security needs. Apart from this, server_name does not appear
+ to introduce significant security issues.
+
+ Since it is possible for a client to present a different server_name
+ in the application protocol, application server implementations that
+ rely upon these names being the same MUST check to make sure the
+ client did not present a different name in the application protocol.
+
+
+
+
+
+Eastlake Standards Track [Page 19]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ Implementations MUST ensure that a buffer overflow does not occur,
+ whatever the values of the length fields in server_name.
+
+11.2. Security Considerations for max_fragment_length
+
+ The maximum fragment length takes effect immediately, including for
+ handshake messages. However, that does not introduce any security
+ complications that are not already present in TLS, since TLS requires
+ implementations to be able to handle fragmented handshake messages.
+
+ Note that, as described in Section 4, once a non-null cipher suite
+ has been activated, the effective maximum fragment length depends on
+ the cipher suite and compression method, as well as on the negotiated
+ max_fragment_length. This must be taken into account when sizing
+ buffers and checking for buffer overflow.
+
+11.3. Security Considerations for client_certificate_url
+
+ Support for client_certificate_url involves the server's acting as a
+ client in another URI-scheme-dependent protocol. The server
+ therefore becomes subject to many of the same security concerns that
+ clients of the URI scheme are subject to, with the added concern that
+ the client can attempt to prompt the server to connect to some
+ (possibly weird-looking) URL.
+
+ In general, this issue means that an attacker might use the server to
+ indirectly attack another host that is vulnerable to some security
+ flaw. It also introduces the possibility of denial-of-service
+ attacks in which an attacker makes many connections to the server,
+ each of which results in the server's attempting a connection to the
+ target of the attack.
+
+ Note that the server may be behind a firewall or otherwise able to
+ access hosts that would not be directly accessible from the public
+ Internet. This could exacerbate the potential security and denial-
+ of-service problems described above, as well as allow the existence
+ of internal hosts to be confirmed when they would otherwise be
+ hidden.
+
+ The detailed security concerns involved will depend on the URI
+ schemes supported by the server. In the case of HTTP, the concerns
+ are similar to those that apply to a publicly accessible HTTP proxy
+ server. In the case of HTTPS, loops and deadlocks may be created,
+ and this should be addressed. In the case of FTP, attacks arise that
+ are similar to FTP bounce attacks.
+
+
+
+
+
+
+Eastlake Standards Track [Page 20]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ As a result of this issue, it is RECOMMENDED that the
+ client_certificate_url extension should have to be specifically
+ enabled by a server administrator, rather than be enabled by default.
+ It is also RECOMMENDED that URI schemes be enabled by the
+ administrator individually, and only a minimal set of schemes be
+ enabled. Unusual protocols that offer limited security or whose
+ security is not well understood SHOULD be avoided.
+
+ As discussed in [RFC3986], URLs that specify ports other than the
+ default may cause problems, as may very long URLs (which are more
+ likely to be useful in exploiting buffer overflow bugs).
+
+ This extension continues to use SHA-1 (as in RFC 4366) and does not
+ provide algorithm agility. The property required of SHA-1 in this
+ case is second pre-image resistance, not collision resistance.
+ Furthermore, even if second pre-image attacks against SHA-1 are found
+ in the future, an attack against client_certificate_url would require
+ a second pre-image that is accepted as a valid certificate by the
+ server and contains the same public key.
+
+ Also note that HTTP caching proxies are common on the Internet, and
+ some proxies do not check for the latest version of an object
+ correctly. If a request using HTTP (or another caching protocol)
+ goes through a misconfigured or otherwise broken proxy, the proxy may
+ return an out-of-date response.
+
+11.4. Security Considerations for trusted_ca_keys
+
+ Potentially, the CA root keys a client possesses could be regarded as
+ confidential information. As a result, the CA root key indication
+ extension should be used with care.
+
+ The use of the SHA-1 certificate hash alternative ensures that each
+ certificate is specified unambiguously. This context does not
+ require a cryptographic hash function, so the use of SHA-1 is
+ considered acceptable, and no algorithm agility is provided.
+
+11.5. Security Considerations for truncated_hmac
+
+ It is possible that truncated MACs are weaker than "un-truncated"
+ MACs. However, no significant weaknesses are currently known or
+ expected to exist for HMAC with MD5 or SHA-1, truncated to 80 bits.
+
+ Note that the output length of a MAC need not be as long as the
+ length of a symmetric cipher key, since forging of MAC values cannot
+ be done off-line: in TLS, a single failed MAC guess will cause the
+ immediate termination of the TLS session.
+
+
+
+
+Eastlake Standards Track [Page 21]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ Since the MAC algorithm only takes effect after all handshake
+ messages that affect extension parameters have been authenticated by
+ the hashes in the Finished messages, it is not possible for an active
+ attacker to force negotiation of the truncated HMAC extension where
+ it would not otherwise be used (to the extent that the handshake
+ authentication is secure). Therefore, in the event that any security
+ problems were found with truncated HMAC in the future, if either the
+ client or the server for a given session were updated to take the
+ problem into account, it would be able to veto use of this extension.
+
+11.6. Security Considerations for status_request
+
+ If a client requests an OCSP response, it must take into account that
+ an attacker's server using a compromised key could (and probably
+ would) pretend not to support the extension. In this case, a client
+ that requires OCSP validation of certificates SHOULD either contact
+ the OCSP server directly or abort the handshake.
+
+ Use of the OCSP nonce request extension (id-pkix-ocsp-nonce) may
+ improve security against attacks that attempt to replay OCSP
+ responses; see Section 4.4.1 of [RFC2560] for further details.
+
+12. Normative References
+
+ [RFC2104] Krawczyk, H., Bellare, M., and R. Canetti, "HMAC:
+ Keyed-Hashing for Message Authentication", RFC 2104,
+ February 1997.
+
+ [RFC2119] Bradner, S., "Key words for use in RFCs to Indicate
+ Requirement Levels", BCP 14, RFC 2119, March 1997.
+
+ [RFC2560] Myers, M., Ankney, R., Malpani, A., Galperin, S., and
+ C. Adams, "X.509 Internet Public Key Infrastructure
+ Online Certificate Status Protocol - OCSP", RFC 2560,
+ June 1999.
+
+ [RFC2585] Housley, R. and P. Hoffman, "Internet X.509 Public Key
+ Infrastructure Operational Protocols: FTP and HTTP",
+ RFC 2585, May 1999.
+
+ [RFC2616] Fielding, R., Gettys, J., Mogul, J., Frystyk, H.,
+ Masinter, L., Leach, P., and T. Berners-Lee,
+ "Hypertext Transfer Protocol -- HTTP/1.1", RFC 2616,
+ June 1999.
+
+ [RFC3986] Berners-Lee, T., Fielding, R., and L. Masinter,
+ "Uniform Resource Identifier (URI): Generic Syntax",
+ STD 66, RFC 3986, January 2005.
+
+
+
+Eastlake Standards Track [Page 22]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+ [RFC5246] Dierks, T. and E. Rescorla, "The Transport Layer
+ Security (TLS) Protocol Version 1.2", RFC 5246, August
+ 2008.
+
+ [RFC5280] Cooper, D., Santesson, S., Farrell, S., Boeyen, S.,
+ Housley, R., and W. Polk, "Internet X.509 Public Key
+ Infrastructure Certificate and Certificate Revocation
+ List (CRL) Profile", RFC 5280, May 2008.
+
+ [RFC5890] Klensin, J., "Internationalized Domain Names for
+ Applications (IDNA): Definitions and Document
+ Framework", RFC 5890, August 2010.
+
+13. Informative References
+
+ [RFC2712] Medvinsky, A. and M. Hur, "Addition of Kerberos Cipher
+ Suites to Transport Layer Security (TLS)", RFC 2712,
+ October 1999.
+
+ [X509-4th] ITU-T Recommendation X.509 (2000) | ISO/IEC
+ 9594-8:2001, "Information Systems - Open Systems
+ Interconnection - The Directory: Public key and
+ attribute certificate frameworks".
+
+ [X509-4th-TC1] ITU-T Recommendation X.509(2000) Corrigendum 1(2001) |
+ ISO/IEC 9594-8:2001/Cor.1:2002, Technical Corrigendum
+ 1 to ISO/IEC 9594:8:2001.
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 23]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+Appendix A. Changes from RFC 4366
+
+ The significant changes between RFC 4366 and this document are
+ described below.
+
+ RFC 4366 described both general extension mechanisms (for the TLS
+ handshake and client and server hellos) as well as specific
+ extensions. RFC 4366 was associated with RFC 4346, TLS 1.1. The
+ client and server hello extension mechanisms have been moved into RFC
+ 5246, TLS 1.2, so this document, which is associated with RFC 5246,
+ includes only the handshake extension mechanisms and the specific
+ extensions from RFC 4366. RFC 5246 also specifies the unknown
+ extension error and new extension specification considerations, so
+ that material has been removed from this document.
+
+ The Server Name extension now specifies only ASCII representation,
+ eliminating UTF-8. It is provided that the ServerNameList can
+ contain more than only one name of any particular name_type. If a
+ server name is provided but not recognized, the server should either
+ continue the handshake without an error or send a fatal error.
+ Sending a warning-level message is not recommended because client
+ behavior will be unpredictable. Provision was added for the user
+ using the server_name extension in deciding whether or not to resume
+ a session. Furthermore, this extension should be the same in a
+ session resumption request as it was in the full handshake that
+ established the session. Such a resumption request must not be
+ accepted if the server_name extension is different, but instead a
+ full handshake must be done to possibly establish a new session.
+
+ The Client Certificate URLs extension has been changed to make the
+ presence of a hash mandatory.
+
+ For the case of DTLS, the requirement to report an overflow of the
+ negotiated maximum fragment length is made conditional on passing
+ authentication.
+
+ TLS servers are now prohibited from following HTTP redirects when
+ retrieving certificates.
+
+ The material was also re-organized in minor ways. For example,
+ information as to which errors are fatal is moved from the "Error
+ Alerts" section to the individual extension specifications.
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 24]
+
+RFC 6066 TLS Extension Definitions January 2011
+
+
+Appendix B. Acknowledgements
+
+ This document is based on material from RFC 4366 for which the
+ authors were S. Blake-Wilson, M. Nystrom, D. Hopwood, J. Mikkelsen,
+ and T. Wright. Other contributors include Joseph Salowey, Alexey
+ Melnikov, Peter Saint-Andre, and Adrian Farrel.
+
+Author's Address
+
+ Donald Eastlake 3rd
+ Huawei
+ 155 Beaver Street
+ Milford, MA 01757 USA
+
+ Phone: +1-508-333-2270
+ EMail: [email protected]
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+Eastlake Standards Track [Page 25]
+
--- /dev/null
+++ b/tlshand.c
@@ -1,0 +1,3037 @@
+#include <u.h>
+#include <libc.h>
+#include <auth.h>
+#include <mp.h>
+#include <libsec.h>
+
+// The main groups of functions are:
+// client/server - main handshake protocol definition
+// message functions - formating handshake messages
+// cipher choices - catalog of digest and encrypt algorithms
+// security functions - PKCS#1, sslHMAC, session keygen
+// general utility functions - malloc, serialization
+// The handshake protocol builds on the TLS/SSL3 record layer protocol,
+// which is implemented in kernel device #a. See also /lib/rfc/rfc2246.
+
+enum {
+ TLSFinishedLen = 12,
+ SSL3FinishedLen = MD5dlen+SHA1dlen,
+ MaxKeyData = 160, // amount of secret we may need
+ MAXdlen = SHA2_512dlen,
+ RandomSize = 32,
+ MasterSecretSize = 48,
+ AQueue = 0,
+ AFlush = 1,
+};
+
+typedef struct Bytes{
+ int len;
+ uchar data[];
+} Bytes;
+
+typedef struct Ints{
+ int len;
+ int data[];
+} Ints;
+
+typedef struct Algs{
+ char *enc;
+ char *digest;
+ int nsecret;
+ int tlsid;
+ int ok;
+} Algs;
+
+typedef struct Namedcurve{
+ int tlsid;
+ void (*init)(mpint *p, mpint *a, mpint *b, mpint *x, mpint *y, mpint *n, mpint *h);
+} Namedcurve;
+
+typedef struct Finished{
+ uchar verify[SSL3FinishedLen];
+ int n;
+} Finished;
+
+typedef struct HandshakeHash {
+ MD5state md5;
+ SHAstate sha1;
+ SHA2_256state sha2_256;
+} HandshakeHash;
+
+typedef struct TlsSec TlsSec;
+struct TlsSec {
+ RSApub *rsapub;
+ AuthRpc *rpc; // factotum for rsa private key
+ uchar *psk; // pre-shared key
+ int psklen;
+ int clientVers; // version in ClientHello
+ uchar sec[MasterSecretSize]; // master secret
+ uchar srandom[RandomSize]; // server random
+ uchar crandom[RandomSize]; // client random
+
+ Namedcurve *nc; // selected curve for ECDHE
+ // diffie hellman state
+ DHstate dh;
+ struct {
+ ECdomain dom;
+ ECpriv Q;
+ } ec;
+ uchar X[32];
+
+ // byte generation and handshake checksum
+ void (*prf)(uchar*, int, uchar*, int, char*, uchar*, int);
+ void (*setFinished)(TlsSec*, HandshakeHash, uchar*, int);
+ int nfin;
+};
+
+typedef struct TlsConnection{
+ TlsSec sec[1]; // security management goo
+ int hand, ctl; // record layer file descriptors
+ int erred; // set when tlsError called
+ int (*trace)(char*fmt, ...); // for debugging
+ int version; // protocol we are speaking
+ Bytes *cert; // server certificate; only last - no chain
+
+ int cipher;
+ int nsecret; // amount of secret data to init keys
+ char *digest; // name of digest algorithm to use
+ char *enc; // name of encryption algorithm to use
+
+ char *serverName; // server name indication; extension
+
+ // for finished messages
+ HandshakeHash handhash;
+ Finished finished;
+
+ uchar *sendp;
+ uchar buf[1<<16];
+} TlsConnection;
+
+typedef struct Msg{
+ int tag;
+ union {
+ struct {
+ int version;
+ uchar random[RandomSize];
+ Bytes* sid;
+ Ints* ciphers;
+ Bytes* compressors;
+ Bytes* extensions;
+ } clientHello;
+ struct {
+ int version;
+ uchar random[RandomSize];
+ Bytes* sid;
+ int cipher;
+ int compressor;
+ Bytes* extensions;
+ } serverHello;
+ struct {
+ int ncert;
+ Bytes **certs;
+ } certificate;
+ struct {
+ Bytes *types;
+ Ints *sigalgs;
+ int nca;
+ Bytes **cas;
+ } certificateRequest;
+ struct {
+ Bytes *pskid;
+ Bytes *key;
+ } clientKeyExchange;
+ struct {
+ Bytes *pskid;
+ Bytes *dh_p;
+ Bytes *dh_g;
+ Bytes *dh_Ys;
+ Bytes *dh_parameters;
+ Bytes *dh_signature;
+ int sigalg;
+ int curve;
+ } serverKeyExchange;
+ struct {
+ int sigalg;
+ Bytes *signature;
+ } certificateVerify;
+ Finished finished;
+ } u;
+} Msg;
+
+
+enum {
+ SSL3Version = 0x0300,
+ TLS10Version = 0x0301,
+ TLS11Version = 0x0302,
+ TLS12Version = 0x0303,
+ ProtocolVersion = TLS12Version, // maximum version we speak
+ MinProtoVersion = 0x0300, // limits on version we accept
+ MaxProtoVersion = 0x03ff,
+};
+
+// handshake type
+enum {
+ HHelloRequest,
+ HClientHello,
+ HServerHello,
+ HSSL2ClientHello = 9, /* local convention; see devtls.c */
+ HCertificate = 11,
+ HServerKeyExchange,
+ HCertificateRequest,
+ HServerHelloDone,
+ HCertificateVerify,
+ HClientKeyExchange,
+ HFinished = 20,
+ HMax
+};
+
+// alerts
+enum {
+ ECloseNotify = 0,
+ EUnexpectedMessage = 10,
+ EBadRecordMac = 20,
+ EDecryptionFailed = 21,
+ ERecordOverflow = 22,
+ EDecompressionFailure = 30,
+ EHandshakeFailure = 40,
+ ENoCertificate = 41,
+ EBadCertificate = 42,
+ EUnsupportedCertificate = 43,
+ ECertificateRevoked = 44,
+ ECertificateExpired = 45,
+ ECertificateUnknown = 46,
+ EIllegalParameter = 47,
+ EUnknownCa = 48,
+ EAccessDenied = 49,
+ EDecodeError = 50,
+ EDecryptError = 51,
+ EExportRestriction = 60,
+ EProtocolVersion = 70,
+ EInsufficientSecurity = 71,
+ EInternalError = 80,
+ EInappropriateFallback = 86,
+ EUserCanceled = 90,
+ ENoRenegotiation = 100,
+ EUnknownPSKidentity = 115,
+ EMax = 256
+};
+
+// cipher suites
+enum {
+ TLS_RSA_WITH_3DES_EDE_CBC_SHA = 0X000A,
+ TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA = 0X0016,
+
+ TLS_RSA_WITH_AES_128_CBC_SHA = 0X002F,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA = 0X0033,
+ TLS_RSA_WITH_AES_256_CBC_SHA = 0X0035,
+ TLS_DHE_RSA_WITH_AES_256_CBC_SHA = 0X0039,
+ TLS_RSA_WITH_AES_128_CBC_SHA256 = 0X003C,
+ TLS_RSA_WITH_AES_256_CBC_SHA256 = 0X003D,
+ TLS_DHE_RSA_WITH_AES_128_CBC_SHA256 = 0X0067,
+
+ TLS_RSA_WITH_AES_128_GCM_SHA256 = 0x009C,
+ TLS_DHE_RSA_WITH_AES_128_GCM_SHA256 = 0x009E,
+
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA = 0xC013,
+ TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA = 0xC014,
+ TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256 = 0xC023,
+ TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256 = 0xC027,
+
+ TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256 = 0xC02B,
+ TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256 = 0xC02F,
+
+ GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC13,
+ GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCC14,
+ GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCC15,
+
+ TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCA8,
+ TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305 = 0xCCA9,
+ TLS_DHE_RSA_WITH_CHACHA20_POLY1305 = 0xCCAA,
+
+ TLS_PSK_WITH_CHACHA20_POLY1305 = 0xCCAB,
+ TLS_PSK_WITH_AES_128_CBC_SHA256 = 0x00AE,
+ TLS_PSK_WITH_AES_128_CBC_SHA = 0x008C,
+
+ TLS_FALLBACK_SCSV = 0x5600,
+};
+
+// compression methods
+enum {
+ CompressionNull = 0,
+ CompressionMax
+};
+
+
+// curves
+enum {
+ X25519 = 0x001d,
+};
+
+// extensions
+enum {
+ Extsni = 0x0000,
+ Extec = 0x000a,
+ Extecp = 0x000b,
+ Extsigalgs = 0x000d,
+};
+
+static Algs cipherAlgs[] = {
+ // ECDHE-ECDSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},
+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305},
+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256},
+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256},
+
+ // ECDHE-RSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305},
+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256},
+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256},
+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA},
+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA},
+
+ // DHE-RSA
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_DHE_RSA_WITH_CHACHA20_POLY1305},
+ {"ccpoly64_aead", "clear", 2*32, GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305},
+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_DHE_RSA_WITH_AES_128_GCM_SHA256},
+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA256},
+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_128_CBC_SHA},
+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_DHE_RSA_WITH_AES_256_CBC_SHA},
+ {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA},
+
+ // RSA
+ {"aes_128_gcm_aead", "clear", 2*(16+4), TLS_RSA_WITH_AES_128_GCM_SHA256},
+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_RSA_WITH_AES_128_CBC_SHA256},
+ {"aes_256_cbc", "sha256", 2*(32+16+SHA2_256dlen), TLS_RSA_WITH_AES_256_CBC_SHA256},
+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_RSA_WITH_AES_128_CBC_SHA},
+ {"aes_256_cbc", "sha1", 2*(32+16+SHA1dlen), TLS_RSA_WITH_AES_256_CBC_SHA},
+ {"3des_ede_cbc","sha1", 2*(4*8+SHA1dlen), TLS_RSA_WITH_3DES_EDE_CBC_SHA},
+
+ // PSK
+ {"ccpoly96_aead", "clear", 2*(32+12), TLS_PSK_WITH_CHACHA20_POLY1305},
+ {"aes_128_cbc", "sha256", 2*(16+16+SHA2_256dlen), TLS_PSK_WITH_AES_128_CBC_SHA256},
+ {"aes_128_cbc", "sha1", 2*(16+16+SHA1dlen), TLS_PSK_WITH_AES_128_CBC_SHA},
+};
+
+static uchar compressors[] = {
+ CompressionNull,
+};
+
+static Namedcurve namedcurves[] = {
+ X25519, nil,
+ 0x0017, secp256r1,
+ 0x0018, secp384r1,
+};
+
+static uchar pointformats[] = {
+ CompressionNull /* support of uncompressed point format is mandatory */
+};
+
+static struct {
+ DigestState* (*fun)(uchar*, ulong, uchar*, DigestState*);
+ int len;
+} hashfun[] = {
+/* [0x00] is reserved for MD5+SHA1 for < TLS1.2 */
+ [0x01] {md5, MD5dlen},
+ [0x02] {sha1, SHA1dlen},
+ [0x03] {sha2_224, SHA2_224dlen},
+ [0x04] {sha2_256, SHA2_256dlen},
+ [0x05] {sha2_384, SHA2_384dlen},
+ [0x06] {sha2_512, SHA2_512dlen},
+};
+
+// signature algorithms (only RSA and ECDSA at the moment)
+static int sigalgs[] = {
+ 0x0603, /* SHA512 ECDSA */
+ 0x0503, /* SHA384 ECDSA */
+ 0x0403, /* SHA256 ECDSA */
+ 0x0203, /* SHA1 ECDSA */
+
+ 0x0601, /* SHA512 RSA */
+ 0x0501, /* SHA384 RSA */
+ 0x0401, /* SHA256 RSA */
+ 0x0201, /* SHA1 RSA */
+};
+
+static TlsConnection *tlsServer2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ int (*trace)(char*fmt, ...), PEMChain *chain);
+static TlsConnection *tlsClient2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ uchar *ext, int extlen, int (*trace)(char*fmt, ...));
+static void msgClear(Msg *m);
+static char* msgPrint(char *buf, int n, Msg *m);
+static int msgRecv(TlsConnection *c, Msg *m);
+static int msgSend(TlsConnection *c, Msg *m, int act);
+static void tlsError(TlsConnection *c, int err, char *msg, ...);
+#pragma varargck argpos tlsError 3
+static int setVersion(TlsConnection *c, int version);
+static int setSecrets(TlsConnection *c, int isclient);
+static int finishedMatch(TlsConnection *c, Finished *f);
+static void tlsConnectionFree(TlsConnection *c);
+
+static int isDHE(int tlsid);
+static int isECDHE(int tlsid);
+static int isPSK(int tlsid);
+static int isECDSA(int tlsid);
+
+static int setAlgs(TlsConnection *c, int a);
+static int okCipher(Ints *cv, int ispsk, int canec);
+static int okCompression(Bytes *cv);
+static int initCiphers(void);
+static Ints* makeciphers(int ispsk);
+
+static AuthRpc* factotum_rsa_open(RSApub *rsapub);
+static mpint* factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher);
+static void factotum_rsa_close(AuthRpc *rpc);
+
+static void tlsSecInits(TlsSec *sec, int cvers, uchar *crandom);
+static int tlsSecRSAs(TlsSec *sec, Bytes *epm);
+static Bytes* tlsSecECDHEs1(TlsSec *sec);
+static int tlsSecECDHEs2(TlsSec *sec, Bytes *Yc);
+static void tlsSecInitc(TlsSec *sec, int cvers);
+static Bytes* tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert);
+static Bytes* tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys);
+static Bytes* tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys);
+static void tlsSecVers(TlsSec *sec, int v);
+static int tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient);
+static void setMasterSecret(TlsSec *sec, Bytes *pm);
+static int digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg);
+static char* verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg);
+
+static Bytes* pkcs1_encrypt(Bytes* data, RSApub* key);
+static Bytes* pkcs1_decrypt(TlsSec *sec, Bytes *data);
+static Bytes* pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg);
+
+static void* emalloc(int);
+static void* erealloc(void*, int);
+static void put32(uchar *p, u32int);
+static void put24(uchar *p, int);
+static void put16(uchar *p, int);
+static int get24(uchar *p);
+static int get16(uchar *p);
+static Bytes* newbytes(int len);
+static Bytes* makebytes(uchar* buf, int len);
+static Bytes* mptobytes(mpint* big, int len);
+static mpint* bytestomp(Bytes* bytes);
+static void freebytes(Bytes* b);
+static Ints* newints(int len);
+static void freeints(Ints* b);
+static int lookupid(Ints* b, int id);
+
+//================= client/server ========================
+
+// push TLS onto fd, returning new (application) file descriptor
+// or -1 if error.
+int
+tlsServer(int fd, TLSconn *conn)
+{
+ char buf[8];
+ char dname[64];
+ uchar seed[2*RandomSize];
+ int n, data, ctl, hand;
+ TlsConnection *tls;
+
+ if(conn == nil)
+ return -1;
+ ctl = open("#a/tls/clone", ORDWR|OCEXEC);
+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0){
+ close(ctl);
+ return -1;
+ }
+ buf[n] = 0;
+ snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+ snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR|OCEXEC);
+ if(hand < 0){
+ close(ctl);
+ return -1;
+ }
+ data = -1;
+ fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
+ tls = tlsServer2(ctl, hand,
+ conn->cert, conn->certlen,
+ conn->pskID, conn->psk, conn->psklen,
+ conn->trace, conn->chain);
+ if(tls != nil){
+ snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ }
+ close(hand);
+ close(ctl);
+ if(data < 0){
+ tlsConnectionFree(tls);
+ return -1;
+ }
+ free(conn->cert);
+ conn->cert = nil; // client certificates are not yet implemented
+ conn->certlen = 0;
+ conn->sessionIDlen = 0;
+ conn->sessionID = nil;
+ if(conn->sessionKey != nil
+ && conn->sessionType != nil
+ && strcmp(conn->sessionType, "ttls") == 0){
+ memmove(seed, tls->sec->crandom, RandomSize);
+ memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
+ tls->sec->prf(
+ conn->sessionKey, conn->sessionKeylen,
+ tls->sec->sec, MasterSecretSize,
+ conn->sessionConst,
+ seed, sizeof(seed));
+ }
+ tlsConnectionFree(tls);
+ close(fd);
+ return data;
+}
+
+static uchar*
+tlsClientExtensions(TLSconn *conn, int *plen)
+{
+ uchar *b, *p;
+ int i, n, m;
+
+ p = b = nil;
+
+ // RFC6066 - Server Name Identification
+ if(conn->serverName != nil && (n = strlen(conn->serverName)) > 0){
+ m = p - b;
+ b = erealloc(b, m + 2+2+2+1+2+n);
+ p = b + m;
+
+ put16(p, Extsni), p += 2; /* Type: server_name */
+ put16(p, 2+1+2+n), p += 2; /* Length */
+ put16(p, 1+2+n), p += 2; /* Server Name list length */
+ *p++ = 0; /* Server Name Type: host_name */
+ put16(p, n), p += 2; /* Server Name length */
+ memmove(p, conn->serverName, n);
+ p += n;
+ }
+
+ // Elliptic Curves (also called Supported Groups)
+ if(ProtocolVersion >= TLS10Version){
+ m = p - b;
+ b = erealloc(b, m + 2+2+2+nelem(namedcurves)*2 + 2+2+1+nelem(pointformats));
+ p = b + m;
+
+ n = nelem(namedcurves);
+ put16(p, Extec), p += 2; /* Type: elliptic_curves / supported_groups */
+ put16(p, (n+1)*2), p += 2; /* Length */
+ put16(p, n*2), p += 2; /* Elliptic Curves Length */
+ for(i=0; i < n; i++){ /* Elliptic Curves */
+ put16(p, namedcurves[i].tlsid);
+ p += 2;
+ }
+
+ n = nelem(pointformats);
+ put16(p, Extecp), p += 2; /* Type: ec_point_formats */
+ put16(p, n+1), p += 2; /* Length */
+ *p++ = n; /* EC point formats Length */
+ for(i=0; i < n; i++) /* EC point formats */
+ *p++ = pointformats[i];
+ }
+
+ // signature algorithms
+ if(ProtocolVersion >= TLS12Version){
+ n = nelem(sigalgs);
+
+ m = p - b;
+ b = erealloc(b, m + 2+2+2+n*2);
+ p = b + m;
+
+ put16(p, Extsigalgs), p += 2;
+ put16(p, n*2 + 2), p += 2;
+ put16(p, n*2), p += 2;
+ for(i=0; i < n; i++){
+ put16(p, sigalgs[i]);
+ p += 2;
+ }
+ }
+
+ *plen = p - b;
+ return b;
+}
+
+// push TLS onto fd, returning new (application) file descriptor
+// or -1 if error.
+int
+tlsClient(int fd, TLSconn *conn)
+{
+ char buf[8];
+ char dname[64];
+ uchar seed[2*RandomSize];
+ int n, data, ctl, hand;
+ TlsConnection *tls;
+ uchar *ext;
+
+ if(conn == nil)
+ return -1;
+ ctl = open("#a/tls/clone", ORDWR|OCEXEC);
+ if(ctl < 0)
+ return -1;
+ n = read(ctl, buf, sizeof(buf)-1);
+ if(n < 0){
+ close(ctl);
+ return -1;
+ }
+ buf[n] = 0;
+ snprint(conn->dir, sizeof(conn->dir), "#a/tls/%s", buf);
+ snprint(dname, sizeof(dname), "#a/tls/%s/hand", buf);
+ hand = open(dname, ORDWR|OCEXEC);
+ if(hand < 0){
+ close(ctl);
+ return -1;
+ }
+ snprint(dname, sizeof(dname), "#a/tls/%s/data", buf);
+ data = open(dname, ORDWR);
+ if(data < 0){
+ close(hand);
+ close(ctl);
+ return -1;
+ }
+ fprint(ctl, "fd %d 0x%x", fd, ProtocolVersion);
+ ext = tlsClientExtensions(conn, &n);
+ tls = tlsClient2(ctl, hand,
+ conn->cert, conn->certlen,
+ conn->pskID, conn->psk, conn->psklen,
+ ext, n, conn->trace);
+ free(ext);
+ close(hand);
+ close(ctl);
+ if(tls == nil){
+ close(data);
+ return -1;
+ }
+ free(conn->cert);
+ if(tls->cert != nil){
+ conn->certlen = tls->cert->len;
+ conn->cert = emalloc(conn->certlen);
+ memcpy(conn->cert, tls->cert->data, conn->certlen);
+ } else {
+ conn->certlen = 0;
+ conn->cert = nil;
+ }
+ conn->sessionIDlen = 0;
+ conn->sessionID = nil;
+ if(conn->sessionKey != nil
+ && conn->sessionType != nil
+ && strcmp(conn->sessionType, "ttls") == 0){
+ memmove(seed, tls->sec->crandom, RandomSize);
+ memmove(seed+RandomSize, tls->sec->srandom, RandomSize);
+ tls->sec->prf(
+ conn->sessionKey, conn->sessionKeylen,
+ tls->sec->sec, MasterSecretSize,
+ conn->sessionConst,
+ seed, sizeof(seed));
+ }
+ tlsConnectionFree(tls);
+ close(fd);
+ return data;
+}
+
+static int
+countchain(PEMChain *p)
+{
+ int i = 0;
+
+ while (p) {
+ i++;
+ p = p->next;
+ }
+ return i;
+}
+
+static int
+checkClientExtensions(TlsConnection *c, Bytes *ext)
+{
+ uchar *p, *e;
+ int i, j, n;
+
+ if(ext == nil)
+ return 0;
+
+ for(p = ext->data, e = p+ext->len; p < e; p += n){
+ if(e-p < 4)
+ goto Short;
+ p += 4;
+ if(e-p < (n = get16(p-2))) /* Length */
+ goto Short;
+ switch(get16(p-4)){ /* Type */
+ case Extsni:
+ if(n < 4 || get16(p) != (n -= 2))
+ goto Short;
+ p += 2;
+ if(*p++ != 0) /* Server Name Type: host_name */
+ break;
+ p += 2;
+ if(e-p < (n = get16(p-2)))
+ goto Short;
+ memmove(c->serverName, p, n);
+ break;
+ case Extec:
+ if(n < 4 || n % 2 || get16(p) != (n -= 2))
+ goto Short;
+ p += 2;
+ for(i = 0; i < nelem(namedcurves) && c->sec->nc == nil; i++)
+ for(j = 0; j < n; j += 2)
+ if(namedcurves[i].tlsid == get16(p+j)){
+ c->sec->nc = &namedcurves[i];
+ break;
+ }
+ break;
+ }
+ }
+
+ return 0;
+Short:
+ tlsError(c, EDecodeError, "clienthello extensions has invalid length");
+ return -1;
+}
+
+static TlsConnection *
+tlsServer2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ int (*trace)(char*fmt, ...), PEMChain *chp)
+{
+ int cipher, compressor, numcerts, i;
+ TlsConnection *c;
+ Msg m;
+
+ if(trace)
+ trace("tlsServer2\n");
+ if(!initCiphers())
+ return nil;
+
+ c = emalloc(sizeof(TlsConnection));
+ c->ctl = ctl;
+ c->hand = hand;
+ c->trace = trace;
+ c->version = ProtocolVersion;
+ c->sendp = c->buf;
+
+ memset(&m, 0, sizeof(m));
+ if(!msgRecv(c, &m)){
+ if(trace)
+ trace("initial msgRecv failed\n");
+ goto Err;
+ }
+ if(m.tag != HClientHello) {
+ tlsError(c, EUnexpectedMessage, "expected a client hello");
+ goto Err;
+ }
+ if(trace)
+ trace("ClientHello version %x\n", m.u.clientHello.version);
+ if(setVersion(c, m.u.clientHello.version) < 0) {
+ tlsError(c, EIllegalParameter, "incompatible version");
+ goto Err;
+ }
+ if(c->version < ProtocolVersion
+ && lookupid(m.u.clientHello.ciphers, TLS_FALLBACK_SCSV) >= 0){
+ tlsError(c, EInappropriateFallback, "inappropriate fallback");
+ goto Err;
+ }
+ tlsSecInits(c->sec, m.u.clientHello.version, m.u.clientHello.random);
+ tlsSecVers(c->sec, c->version);
+ if(psklen > 0){
+ c->sec->psk = psk;
+ c->sec->psklen = psklen;
+ }
+ if(certlen > 0){
+ /* server certificate */
+ c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+ if(c->sec->rsapub == nil){
+ tlsError(c, EHandshakeFailure, "invalid X509/rsa certificate");
+ goto Err;
+ }
+ c->sec->rpc = factotum_rsa_open(c->sec->rsapub);
+ if(c->sec->rpc == nil){
+ tlsError(c, EHandshakeFailure, "factotum_rsa_open: %r");
+ goto Err;
+ }
+ }
+ if(checkClientExtensions(c, m.u.clientHello.extensions) < 0)
+ goto Err;
+ if(trace)
+ trace("ClientHello server name indicator %s\n", c->serverName);
+ cipher = okCipher(m.u.clientHello.ciphers, psklen > 0, c->sec->nc != nil);
+ if(cipher < 0 || !setAlgs(c, cipher)) {
+ tlsError(c, EHandshakeFailure, "no matching cipher suite");
+ goto Err;
+ }
+ compressor = okCompression(m.u.clientHello.compressors);
+ if(compressor < 0) {
+ tlsError(c, EHandshakeFailure, "no matching compressor");
+ goto Err;
+ }
+ if(trace)
+ trace(" cipher %x, compressor %x\n", cipher, compressor);
+ msgClear(&m);
+
+ m.tag = HServerHello;
+ m.u.serverHello.version = c->version;
+ memmove(m.u.serverHello.random, c->sec->srandom, RandomSize);
+ m.u.serverHello.cipher = cipher;
+ m.u.serverHello.compressor = compressor;
+ m.u.serverHello.sid = makebytes(nil, 0);
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+
+ if(certlen > 0){
+ m.tag = HCertificate;
+ numcerts = countchain(chp);
+ m.u.certificate.ncert = 1 + numcerts;
+ m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+ m.u.certificate.certs[0] = makebytes(cert, certlen);
+ for (i = 0; i < numcerts && chp; i++, chp = chp->next)
+ m.u.certificate.certs[i+1] = makebytes(chp->pem, chp->pemlen);
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+ }
+
+ if(isECDHE(cipher)){
+ m.tag = HServerKeyExchange;
+ m.u.serverKeyExchange.curve = c->sec->nc->tlsid;
+ m.u.serverKeyExchange.dh_parameters = tlsSecECDHEs1(c->sec);
+ if(m.u.serverKeyExchange.dh_parameters == nil){
+ tlsError(c, EInternalError, "can't set DH parameters");
+ goto Err;
+ }
+
+ /* sign the DH parameters */
+ if(certlen > 0){
+ uchar digest[MAXdlen];
+ int digestlen;
+
+ if(c->version >= TLS12Version)
+ m.u.serverKeyExchange.sigalg = 0x0401; /* RSA SHA256 */
+ digestlen = digestDHparams(c->sec, m.u.serverKeyExchange.dh_parameters,
+ digest, m.u.serverKeyExchange.sigalg);
+ if((m.u.serverKeyExchange.dh_signature = pkcs1_sign(c->sec, digest, digestlen,
+ m.u.serverKeyExchange.sigalg)) == nil){
+ tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
+ goto Err;
+ }
+ }
+ if(!msgSend(c, &m, AQueue))
+ goto Err;
+ }
+
+ m.tag = HServerHelloDone;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HClientKeyExchange) {
+ tlsError(c, EUnexpectedMessage, "expected a client key exchange");
+ goto Err;
+ }
+ if(pskid != nil){
+ if(m.u.clientKeyExchange.pskid == nil
+ || m.u.clientKeyExchange.pskid->len != strlen(pskid)
+ || memcmp(pskid, m.u.clientKeyExchange.pskid->data, m.u.clientKeyExchange.pskid->len) != 0){
+ tlsError(c, EUnknownPSKidentity, "unknown or missing pskid");
+ goto Err;
+ }
+ }
+ if(isECDHE(cipher)){
+ if(tlsSecECDHEs2(c->sec, m.u.clientKeyExchange.key) < 0){
+ tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
+ goto Err;
+ }
+ } else if(certlen > 0){
+ if(tlsSecRSAs(c->sec, m.u.clientKeyExchange.key) < 0){
+ tlsError(c, EHandshakeFailure, "couldn't set keys: %r");
+ goto Err;
+ }
+ } else if(psklen > 0){
+ setMasterSecret(c->sec, newbytes(psklen));
+ } else {
+ tlsError(c, EInternalError, "no psk or certificate");
+ goto Err;
+ }
+
+ if(trace)
+ trace("tls secrets\n");
+ if(setSecrets(c, 0) < 0){
+ tlsError(c, EHandshakeFailure, "can't set secrets: %r");
+ goto Err;
+ }
+
+ /* no CertificateVerify; skip to Finished */
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){
+ tlsError(c, EInternalError, "can't set finished: %r");
+ goto Err;
+ }
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HFinished) {
+ tlsError(c, EUnexpectedMessage, "expected a finished");
+ goto Err;
+ }
+ if(!finishedMatch(c, &m.u.finished)) {
+ tlsError(c, EHandshakeFailure, "finished verification failed");
+ goto Err;
+ }
+ msgClear(&m);
+
+ /* change cipher spec */
+ if(fprint(c->ctl, "changecipher") < 0){
+ tlsError(c, EInternalError, "can't enable cipher: %r");
+ goto Err;
+ }
+
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){
+ tlsError(c, EInternalError, "can't set finished: %r");
+ goto Err;
+ }
+ m.tag = HFinished;
+ m.u.finished = c->finished;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ if(trace)
+ trace("tls finished\n");
+
+ if(fprint(c->ctl, "opened") < 0)
+ goto Err;
+ return c;
+
+Err:
+ msgClear(&m);
+ tlsConnectionFree(c);
+ return nil;
+}
+
+static Bytes*
+tlsSecDHEc(TlsSec *sec, Bytes *p, Bytes *g, Bytes *Ys)
+{
+ DHstate *dh = &sec->dh;
+ mpint *G, *P, *Y, *K;
+ Bytes *Yc;
+ int n;
+
+ if(p == nil || g == nil || Ys == nil)
+ return nil;
+ // reject dh primes that is susceptible to logjam
+ if(p->len <= 1024/8)
+ return nil;
+ Yc = nil;
+ P = bytestomp(p);
+ G = bytestomp(g);
+ Y = bytestomp(Ys);
+ K = nil;
+
+ if(dh_new(dh, P, nil, G) == nil)
+ goto Out;
+ n = (mpsignif(P)+7)/8;
+ Yc = mptobytes(dh->y, n);
+ K = dh_finish(dh, Y); /* zeros dh */
+ if(K == nil){
+ freebytes(Yc);
+ Yc = nil;
+ goto Out;
+ }
+ setMasterSecret(sec, mptobytes(K, n));
+
+Out:
+ mpfree(K);
+ mpfree(Y);
+ mpfree(G);
+ mpfree(P);
+
+ return Yc;
+}
+
+static Bytes*
+tlsSecECDHEc(TlsSec *sec, int curve, Bytes *Ys)
+{
+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ ECpub *pub;
+ ECpoint K;
+ Namedcurve *nc;
+ Bytes *Yc;
+ Bytes *Z;
+ int n;
+
+ if(Ys == nil)
+ return nil;
+
+ if(curve == X25519){
+ if(Ys->len != 32)
+ return nil;
+ Yc = newbytes(32);
+ curve25519_dh_new(sec->X, Yc->data);
+ Z = newbytes(32);
+ if(!curve25519_dh_finish(sec->X, Ys->data, Z->data)){
+ freebytes(Yc);
+ freebytes(Z);
+ return nil;
+ }
+ setMasterSecret(sec, Z);
+ }else{
+ for(nc = namedcurves; nc->tlsid != curve; nc++)
+ if(nc == &namedcurves[nelem(namedcurves)])
+ return nil;
+ ecdominit(dom, nc->init);
+ pub = ecdecodepub(dom, Ys->data, Ys->len);
+ if(pub == nil)
+ return nil;
+
+ memset(Q, 0, sizeof(*Q));
+ Q->x = mpnew(0);
+ Q->y = mpnew(0);
+ Q->d = mpnew(0);
+
+ memset(&K, 0, sizeof(K));
+ K.x = mpnew(0);
+ K.y = mpnew(0);
+
+ ecgen(dom, Q);
+ ecmul(dom, pub, Q->d, &K);
+
+ n = (mpsignif(dom->p)+7)/8;
+ setMasterSecret(sec, mptobytes(K.x, n));
+ Yc = newbytes(1 + 2*n);
+ Yc->len = ecencodepub(dom, Q, Yc->data, Yc->len);
+
+ mpfree(K.x);
+ mpfree(K.y);
+
+ ecpubfree(pub);
+ }
+ return Yc;
+}
+
+static TlsConnection *
+tlsClient2(int ctl, int hand,
+ uchar *cert, int certlen,
+ char *pskid, uchar *psk, int psklen,
+ uchar *ext, int extlen,
+ int (*trace)(char*fmt, ...))
+{
+ int creq, dhx, cipher;
+ TlsConnection *c;
+ Bytes *epm;
+ Msg m;
+
+ if(!initCiphers())
+ return nil;
+
+ epm = nil;
+ memset(&m, 0, sizeof(m));
+ c = emalloc(sizeof(TlsConnection));
+
+ c->ctl = ctl;
+ c->hand = hand;
+ c->trace = trace;
+ c->cert = nil;
+ c->sendp = c->buf;
+
+ c->version = ProtocolVersion;
+ tlsSecInitc(c->sec, c->version);
+ if(psklen > 0){
+ c->sec->psk = psk;
+ c->sec->psklen = psklen;
+ }
+ if(certlen > 0){
+ /* client certificate */
+ c->sec->rsapub = X509toRSApub(cert, certlen, nil, 0);
+ if(c->sec->rsapub == nil){
+ tlsError(c, EInternalError, "invalid X509/rsa certificate");
+ goto Err;
+ }
+ c->sec->rpc = factotum_rsa_open(c->sec->rsapub);
+ if(c->sec->rpc == nil){
+ tlsError(c, EInternalError, "factotum_rsa_open: %r");
+ goto Err;
+ }
+ }
+
+ /* client hello */
+ m.tag = HClientHello;
+ m.u.clientHello.version = c->version;
+ memmove(m.u.clientHello.random, c->sec->crandom, RandomSize);
+ m.u.clientHello.sid = makebytes(nil, 0);
+ m.u.clientHello.ciphers = makeciphers(psklen > 0);
+ m.u.clientHello.compressors = makebytes(compressors,sizeof(compressors));
+ m.u.clientHello.extensions = makebytes(ext, extlen);
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ /* server hello */
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag != HServerHello) {
+ tlsError(c, EUnexpectedMessage, "expected a server hello");
+ goto Err;
+ }
+ if(setVersion(c, m.u.serverHello.version) < 0) {
+ tlsError(c, EIllegalParameter, "incompatible version: %r");
+ goto Err;
+ }
+ tlsSecVers(c->sec, c->version);
+ memmove(c->sec->srandom, m.u.serverHello.random, RandomSize);
+
+ cipher = m.u.serverHello.cipher;
+ if((psklen > 0) != isPSK(cipher) || !setAlgs(c, cipher)) {
+ tlsError(c, EIllegalParameter, "invalid cipher suite");
+ goto Err;
+ }
+ if(m.u.serverHello.compressor != CompressionNull) {
+ tlsError(c, EIllegalParameter, "invalid compression");
+ goto Err;
+ }
+ dhx = isDHE(cipher) || isECDHE(cipher);
+ if(!msgRecv(c, &m))
+ goto Err;
+ if(m.tag == HCertificate){
+ if(m.u.certificate.ncert < 1) {
+ tlsError(c, EIllegalParameter, "runt certificate");
+ goto Err;
+ }
+ c->cert = makebytes(m.u.certificate.certs[0]->data, m.u.certificate.certs[0]->len);
+ if(!msgRecv(c, &m))
+ goto Err;
+ } else if(psklen == 0) {
+ tlsError(c, EUnexpectedMessage, "expected a certificate");
+ goto Err;
+ }
+ if(m.tag == HServerKeyExchange) {
+ if(dhx){
+ char *err = verifyDHparams(c->sec,
+ m.u.serverKeyExchange.dh_parameters,
+ c->cert,
+ m.u.serverKeyExchange.dh_signature,
+ c->version<TLS12Version ? 0x01 : m.u.serverKeyExchange.sigalg);
+ if(err != nil){
+ tlsError(c, EBadCertificate, "can't verify DH parameters: %s", err);
+ goto Err;
+ }
+ if(isECDHE(cipher))
+ epm = tlsSecECDHEc(c->sec,
+ m.u.serverKeyExchange.curve,
+ m.u.serverKeyExchange.dh_Ys);
+ else
+ epm = tlsSecDHEc(c->sec,
+ m.u.serverKeyExchange.dh_p,
+ m.u.serverKeyExchange.dh_g,
+ m.u.serverKeyExchange.dh_Ys);
+ if(epm == nil){
+ tlsError(c, EHandshakeFailure, "bad DH parameters");
+ goto Err;
+ }
+ } else if(psklen == 0){
+ tlsError(c, EUnexpectedMessage, "got an server key exchange");
+ goto Err;
+ }
+ if(!msgRecv(c, &m))
+ goto Err;
+ } else if(dhx){
+ tlsError(c, EUnexpectedMessage, "expected server key exchange");
+ goto Err;
+ }
+
+ /* certificate request (optional) */
+ creq = 0;
+ if(m.tag == HCertificateRequest) {
+ creq = 1;
+ if(!msgRecv(c, &m))
+ goto Err;
+ }
+
+ if(m.tag != HServerHelloDone) {
+ tlsError(c, EUnexpectedMessage, "expected a server hello done");
+ goto Err;
+ }
+ msgClear(&m);
+
+ if(!dhx){
+ if(c->cert != nil){
+ epm = tlsSecRSAc(c->sec, c->cert->data, c->cert->len);
+ if(epm == nil){
+ tlsError(c, EBadCertificate, "bad certificate: %r");
+ goto Err;
+ }
+ } else if(psklen > 0){
+ setMasterSecret(c->sec, newbytes(psklen));
+ } else {
+ tlsError(c, EInternalError, "no psk or certificate");
+ goto Err;
+ }
+ }
+
+ if(trace)
+ trace("tls secrets\n");
+ if(setSecrets(c, 1) < 0){
+ tlsError(c, EHandshakeFailure, "can't set secrets: %r");
+ goto Err;
+ }
+
+ if(creq) {
+ m.tag = HCertificate;
+ if(certlen > 0){
+ m.u.certificate.ncert = 1;
+ m.u.certificate.certs = emalloc(m.u.certificate.ncert * sizeof(Bytes*));
+ m.u.certificate.certs[0] = makebytes(cert, certlen);
+ }
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ }
+
+ /* client key exchange */
+ m.tag = HClientKeyExchange;
+ if(psklen > 0){
+ if(pskid == nil)
+ pskid = "";
+ m.u.clientKeyExchange.pskid = makebytes((uchar*)pskid, strlen(pskid));
+ }
+ m.u.clientKeyExchange.key = epm;
+ epm = nil;
+
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+
+ /* certificate verify */
+ if(creq && certlen > 0) {
+ HandshakeHash hsave;
+ uchar digest[MAXdlen];
+ int digestlen;
+
+ /* save the state for the Finish message */
+ hsave = c->handhash;
+ if(c->version < TLS12Version){
+ md5(nil, 0, digest, &c->handhash.md5);
+ sha1(nil, 0, digest+MD5dlen, &c->handhash.sha1);
+ digestlen = MD5dlen+SHA1dlen;
+ } else {
+ m.u.certificateVerify.sigalg = 0x0401; /* RSA SHA256 */
+ sha2_256(nil, 0, digest, &c->handhash.sha2_256);
+ digestlen = SHA2_256dlen;
+ }
+ c->handhash = hsave;
+
+ if((m.u.certificateVerify.signature = pkcs1_sign(c->sec, digest, digestlen,
+ m.u.certificateVerify.sigalg)) == nil){
+ tlsError(c, EHandshakeFailure, "pkcs1_sign: %r");
+ goto Err;
+ }
+
+ m.tag = HCertificateVerify;
+ if(!msgSend(c, &m, AFlush))
+ goto Err;
+ }
+
+ /* change cipher spec */
+ if(fprint(c->ctl, "changecipher") < 0){
+ tlsError(c, EInternalError, "can't enable cipher: %r");
+ goto Err;
+ }
+
+ // Cipherchange must occur immediately before Finished to avoid
+ // potential hole; see section 4.3 of Wagner Schneier 1996.
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 1) < 0){
+ tlsError(c, EInternalError, "can't set finished 1: %r");
+ goto Err;
+ }
+ m.tag = HFinished;
+ m.u.finished = c->finished;
+ if(!msgSend(c, &m, AFlush)) {
+ tlsError(c, EInternalError, "can't flush after client Finished: %r");
+ goto Err;
+ }
+
+ if(tlsSecFinished(c->sec, c->handhash, c->finished.verify, c->finished.n, 0) < 0){
+ tlsError(c, EInternalError, "can't set finished 0: %r");
+ goto Err;
+ }
+ if(!msgRecv(c, &m)) {
+ tlsError(c, EInternalError, "can't read server Finished: %r");
+ goto Err;
+ }
+ if(m.tag != HFinished) {
+ tlsError(c, EUnexpectedMessage, "expected a Finished msg from server");
+ goto Err;
+ }
+
+ if(!finishedMatch(c, &m.u.finished)) {
+ tlsError(c, EHandshakeFailure, "finished verification failed");
+ goto Err;
+ }
+ msgClear(&m);
+
+ if(fprint(c->ctl, "opened") < 0){
+ if(trace)
+ trace("unable to do final open: %r\n");
+ goto Err;
+ }
+ return c;
+
+Err:
+ free(epm);
+ msgClear(&m);
+ tlsConnectionFree(c);
+ return nil;
+}
+
+
+//================= message functions ========================
+
+static void
+msgHash(TlsConnection *c, uchar *p, int n)
+{
+ md5(p, n, 0, &c->handhash.md5);
+ sha1(p, n, 0, &c->handhash.sha1);
+ if(c->version >= TLS12Version)
+ sha2_256(p, n, 0, &c->handhash.sha2_256);
+}
+
+static int
+msgSend(TlsConnection *c, Msg *m, int act)
+{
+ uchar *p, *e; // sendp = start of new message; p = write pointer; e = end pointer
+ int n, i;
+
+ p = c->sendp;
+ e = &c->buf[sizeof(c->buf)];
+ if(c->trace)
+ c->trace("send %s", msgPrint((char*)p, e - p, m));
+
+ p[0] = m->tag; // header - fill in size later
+ p += 4;
+
+ switch(m->tag) {
+ default:
+ tlsError(c, EInternalError, "can't encode a %d", m->tag);
+ goto Err;
+ case HClientHello:
+ if(p+2+RandomSize > e)
+ goto Overflow;
+ put16(p, m->u.clientHello.version), p += 2;
+ memmove(p, m->u.clientHello.random, RandomSize);
+ p += RandomSize;
+
+ if(p+1+(n = m->u.clientHello.sid->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.clientHello.sid->data, n);
+ p += n;
+
+ if(p+2+(n = m->u.clientHello.ciphers->len) > e)
+ goto Overflow;
+ put16(p, n*2), p += 2;
+ for(i=0; i<n; i++)
+ put16(p, m->u.clientHello.ciphers->data[i]), p += 2;
+
+ if(p+1+(n = m->u.clientHello.compressors->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.clientHello.compressors->data, n);
+ p += n;
+
+ if(m->u.clientHello.extensions == nil
+ || (n = m->u.clientHello.extensions->len) == 0)
+ break;
+ if(p+2+n > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientHello.extensions->data, n);
+ p += n;
+ break;
+ case HServerHello:
+ if(p+2+RandomSize > e)
+ goto Overflow;
+ put16(p, m->u.serverHello.version), p += 2;
+ memmove(p, m->u.serverHello.random, RandomSize);
+ p += RandomSize;
+
+ if(p+1+(n = m->u.serverHello.sid->len) > e)
+ goto Overflow;
+ *p++ = n;
+ memmove(p, m->u.serverHello.sid->data, n);
+ p += n;
+
+ if(p+2+1 > e)
+ goto Overflow;
+ put16(p, m->u.serverHello.cipher), p += 2;
+ *p++ = m->u.serverHello.compressor;
+
+ if(m->u.serverHello.extensions == nil
+ || (n = m->u.serverHello.extensions->len) == 0)
+ break;
+ if(p+2+n > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverHello.extensions->data, n);
+ p += n;
+ break;
+ case HServerHelloDone:
+ break;
+ case HCertificate:
+ n = 0;
+ for(i = 0; i < m->u.certificate.ncert; i++)
+ n += 3 + m->u.certificate.certs[i]->len;
+ if(p+3+n > e)
+ goto Overflow;
+ put24(p, n), p += 3;
+ for(i = 0; i < m->u.certificate.ncert; i++){
+ n = m->u.certificate.certs[i]->len;
+ put24(p, n), p += 3;
+ memmove(p, m->u.certificate.certs[i]->data, n);
+ p += n;
+ }
+ break;
+ case HCertificateVerify:
+ if(p+2+2+(n = m->u.certificateVerify.signature->len) > e)
+ goto Overflow;
+ if(m->u.certificateVerify.sigalg != 0)
+ put16(p, m->u.certificateVerify.sigalg), p += 2;
+ put16(p, n), p += 2;
+ memmove(p, m->u.certificateVerify.signature->data, n);
+ p += n;
+ break;
+ case HServerKeyExchange:
+ if(m->u.serverKeyExchange.pskid != nil){
+ if(p+2+(n = m->u.serverKeyExchange.pskid->len) > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverKeyExchange.pskid->data, n);
+ p += n;
+ }
+ if(m->u.serverKeyExchange.dh_parameters == nil)
+ break;
+ if(p+(n = m->u.serverKeyExchange.dh_parameters->len) > e)
+ goto Overflow;
+ memmove(p, m->u.serverKeyExchange.dh_parameters->data, n);
+ p += n;
+ if(m->u.serverKeyExchange.dh_signature == nil)
+ break;
+ if(p+2+2+(n = m->u.serverKeyExchange.dh_signature->len) > e)
+ goto Overflow;
+ if(c->version >= TLS12Version)
+ put16(p, m->u.serverKeyExchange.sigalg), p += 2;
+ put16(p, n), p += 2;
+ memmove(p, m->u.serverKeyExchange.dh_signature->data, n);
+ p += n;
+ break;
+ case HClientKeyExchange:
+ if(m->u.clientKeyExchange.pskid != nil){
+ if(p+2+(n = m->u.clientKeyExchange.pskid->len) > e)
+ goto Overflow;
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientKeyExchange.pskid->data, n);
+ p += n;
+ }
+ if(m->u.clientKeyExchange.key == nil)
+ break;
+ if(p+2+(n = m->u.clientKeyExchange.key->len) > e)
+ goto Overflow;
+ if(isECDHE(c->cipher))
+ *p++ = n;
+ else if(isDHE(c->cipher) || c->version != SSL3Version)
+ put16(p, n), p += 2;
+ memmove(p, m->u.clientKeyExchange.key->data, n);
+ p += n;
+ break;
+ case HFinished:
+ if(p+m->u.finished.n > e)
+ goto Overflow;
+ memmove(p, m->u.finished.verify, m->u.finished.n);
+ p += m->u.finished.n;
+ break;
+ }
+
+ // go back and fill in size
+ n = p - c->sendp;
+ put24(c->sendp+1, n-4);
+
+ // remember hash of Handshake messages
+ if(m->tag != HHelloRequest)
+ msgHash(c, c->sendp, n);
+
+ c->sendp = p;
+ if(act == AFlush){
+ c->sendp = c->buf;
+ if(write(c->hand, c->buf, p - c->buf) < 0){
+ fprint(2, "write error: %r\n");
+ goto Err;
+ }
+ }
+ msgClear(m);
+ return 1;
+Overflow:
+ tlsError(c, EInternalError, "not enougth send buffer for message (%d)", m->tag);
+Err:
+ msgClear(m);
+ return 0;
+}
+
+static uchar*
+tlsReadN(TlsConnection *c, int n)
+{
+ uchar *p, *w, *e;
+
+ e = &c->buf[sizeof(c->buf)];
+ p = e - n;
+ if(n > sizeof(c->buf) || p < c->sendp){
+ tlsError(c, EDecodeError, "handshake message too long %d", n);
+ return nil;
+ }
+ for(w = p; w < e; w += n)
+ if((n = read(c->hand, w, e - w)) <= 0)
+ return nil;
+ return p;
+}
+
+static int
+msgRecv(TlsConnection *c, Msg *m)
+{
+ uchar *p, *s;
+ int type, n, nn, i;
+
+ msgClear(m);
+ for(;;) {
+ p = tlsReadN(c, 4);
+ if(p == nil)
+ return 0;
+ type = p[0];
+ n = get24(p+1);
+
+ if(type != HHelloRequest)
+ break;
+ if(n != 0) {
+ tlsError(c, EDecodeError, "invalid hello request during handshake");
+ return 0;
+ }
+ }
+
+ if(type == HSSL2ClientHello){
+ /* Cope with an SSL3 ClientHello expressed in SSL2 record format.
+ This is sent by some clients that we must interoperate
+ with, such as Java's JSSE and Microsoft's Internet Explorer. */
+ int nsid, nrandom, nciph;
+
+ p = tlsReadN(c, n);
+ if(p == nil)
+ return 0;
+ msgHash(c, p, n);
+ m->tag = HClientHello;
+ if(n < 22)
+ goto Short;
+ m->u.clientHello.version = get16(p+1);
+ p += 3;
+ n -= 3;
+ nn = get16(p); /* cipher_spec_len */
+ nsid = get16(p + 2);
+ nrandom = get16(p + 4);
+ p += 6;
+ n -= 6;
+ if(nsid != 0 /* no sid's, since shouldn't restart using ssl2 header */
+ || nrandom < 16 || nn % 3 || n - nrandom < nn)
+ goto Err;
+ /* ignore ssl2 ciphers and look for {0x00, ssl3 cipher} */
+ nciph = 0;
+ for(i = 0; i < nn; i += 3)
+ if(p[i] == 0)
+ nciph++;
+ m->u.clientHello.ciphers = newints(nciph);
+ nciph = 0;
+ for(i = 0; i < nn; i += 3)
+ if(p[i] == 0)
+ m->u.clientHello.ciphers->data[nciph++] = get16(&p[i + 1]);
+ p += nn;
+ m->u.clientHello.sid = makebytes(nil, 0);
+ if(nrandom > RandomSize)
+ nrandom = RandomSize;
+ memset(m->u.clientHello.random, 0, RandomSize - nrandom);
+ memmove(&m->u.clientHello.random[RandomSize - nrandom], p, nrandom);
+ m->u.clientHello.compressors = newbytes(1);
+ m->u.clientHello.compressors->data[0] = CompressionNull;
+ goto Ok;
+ }
+ msgHash(c, p, 4);
+
+ p = tlsReadN(c, n);
+ if(p == nil)
+ return 0;
+
+ msgHash(c, p, n);
+
+ m->tag = type;
+
+ switch(type) {
+ default:
+ tlsError(c, EUnexpectedMessage, "can't decode a %d", type);
+ goto Err;
+ case HClientHello:
+ if(n < 2)
+ goto Short;
+ m->u.clientHello.version = get16(p);
+ p += 2, n -= 2;
+
+ if(n < RandomSize)
+ goto Short;
+ memmove(m->u.clientHello.random, p, RandomSize);
+ p += RandomSize, n -= RandomSize;
+ if(n < 1 || n < p[0]+1)
+ goto Short;
+ m->u.clientHello.sid = makebytes(p+1, p[0]);
+ p += m->u.clientHello.sid->len+1;
+ n -= m->u.clientHello.sid->len+1;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+
+ if(nn % 2 || n < nn || nn < 2)
+ goto Short;
+ m->u.clientHello.ciphers = newints(nn >> 1);
+ for(i = 0; i < nn; i += 2)
+ m->u.clientHello.ciphers->data[i >> 1] = get16(&p[i]);
+ p += nn, n -= nn;
+
+ if(n < 1 || n < p[0]+1 || p[0] == 0)
+ goto Short;
+ nn = p[0];
+ m->u.clientHello.compressors = makebytes(p+1, nn);
+ p += nn + 1, n -= nn + 1;
+
+ if(n < 2)
+ break;
+ nn = get16(p);
+ if(nn > n-2)
+ goto Short;
+ m->u.clientHello.extensions = makebytes(p+2, nn);
+ n -= nn + 2;
+ break;
+ case HServerHello:
+ if(n < 2)
+ goto Short;
+ m->u.serverHello.version = get16(p);
+ p += 2, n -= 2;
+
+ if(n < RandomSize)
+ goto Short;
+ memmove(m->u.serverHello.random, p, RandomSize);
+ p += RandomSize, n -= RandomSize;
+
+ if(n < 1 || n < p[0]+1)
+ goto Short;
+ m->u.serverHello.sid = makebytes(p+1, p[0]);
+ p += m->u.serverHello.sid->len+1;
+ n -= m->u.serverHello.sid->len+1;
+
+ if(n < 3)
+ goto Short;
+ m->u.serverHello.cipher = get16(p);
+ m->u.serverHello.compressor = p[2];
+ p += 3, n -= 3;
+
+ if(n < 2)
+ break;
+ nn = get16(p);
+ if(nn > n-2)
+ goto Short;
+ m->u.serverHello.extensions = makebytes(p+2, nn);
+ n -= nn + 2;
+ break;
+ case HCertificate:
+ if(n < 3)
+ goto Short;
+ nn = get24(p);
+ p += 3, n -= 3;
+ if(nn == 0 && n > 0)
+ goto Short;
+ /* certs */
+ i = 0;
+ while(n > 0) {
+ if(n < 3)
+ goto Short;
+ nn = get24(p);
+ p += 3, n -= 3;
+ if(nn > n)
+ goto Short;
+ m->u.certificate.ncert = i+1;
+ m->u.certificate.certs = erealloc(m->u.certificate.certs, (i+1)*sizeof(Bytes*));
+ m->u.certificate.certs[i] = makebytes(p, nn);
+ p += nn, n -= nn;
+ i++;
+ }
+ break;
+ case HCertificateRequest:
+ if(n < 1)
+ goto Short;
+ nn = p[0];
+ p++, n--;
+ if(nn > n)
+ goto Short;
+ m->u.certificateRequest.types = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(c->version >= TLS12Version){
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn % 2)
+ goto Short;
+ m->u.certificateRequest.sigalgs = newints(nn>>1);
+ for(i = 0; i < nn; i += 2)
+ m->u.certificateRequest.sigalgs->data[i >> 1] = get16(&p[i]);
+ p += nn, n -= nn;
+
+ }
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ /* nn == 0 can happen; yahoo's servers do it */
+ if(nn != n)
+ goto Short;
+ /* cas */
+ i = 0;
+ while(n > 0) {
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.certificateRequest.nca = i+1;
+ m->u.certificateRequest.cas = erealloc(
+ m->u.certificateRequest.cas, (i+1)*sizeof(Bytes*));
+ m->u.certificateRequest.cas[i] = makebytes(p, nn);
+ p += nn, n -= nn;
+ i++;
+ }
+ break;
+ case HServerHelloDone:
+ break;
+ case HServerKeyExchange:
+ if(isPSK(c->cipher)){
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > n)
+ goto Short;
+ m->u.serverKeyExchange.pskid = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(n == 0)
+ break;
+ }
+ if(n < 2)
+ goto Short;
+ s = p;
+ if(isECDHE(c->cipher)){
+ nn = *p;
+ p++, n--;
+ if(nn != 3 || nn > n) /* not a named curve */
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ m->u.serverKeyExchange.curve = nn;
+
+ nn = *p++, n--;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+ p += nn, n -= nn;
+ }else if(isDHE(c->cipher)){
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_p = makebytes(p, nn);
+ p += nn, n -= nn;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_g = makebytes(p, nn);
+ p += nn, n -= nn;
+
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn < 1 || nn > n)
+ goto Short;
+ m->u.serverKeyExchange.dh_Ys = makebytes(p, nn);
+ p += nn, n -= nn;
+ } else {
+ /* should not happen */
+ goto Short;
+ }
+ m->u.serverKeyExchange.dh_parameters = makebytes(s, p - s);
+ if(n >= 2){
+ m->u.serverKeyExchange.sigalg = 0;
+ if(c->version >= TLS12Version){
+ m->u.serverKeyExchange.sigalg = get16(p);
+ p += 2, n -= 2;
+ if(n < 2)
+ goto Short;
+ }
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > 0 && nn <= n){
+ m->u.serverKeyExchange.dh_signature = makebytes(p, nn);
+ n -= nn;
+ }
+ }
+ break;
+ case HClientKeyExchange:
+ if(isPSK(c->cipher)){
+ if(n < 2)
+ goto Short;
+ nn = get16(p);
+ p += 2, n -= 2;
+ if(nn > n)
+ goto Short;
+ m->u.clientKeyExchange.pskid = makebytes(p, nn);
+ p += nn, n -= nn;
+ if(n == 0)
+ break;
+ }
+ if(n < 2)
+ goto Short;
+ if(isECDHE(c->cipher))
+ nn = *p++, n--;
+ else if(isDHE(c->cipher) || c->version != SSL3Version)
+ nn = get16(p), p += 2, n -= 2;
+ else
+ nn = n;
+ if(n < nn)
+ goto Short;
+ m->u.clientKeyExchange.key = makebytes(p, nn);
+ n -= nn;
+ break;
+ case HFinished:
+ m->u.finished.n = c->finished.n;
+ if(n < m->u.finished.n)
+ goto Short;
+ memmove(m->u.finished.verify, p, m->u.finished.n);
+ n -= m->u.finished.n;
+ break;
+ }
+
+ if(n != 0 && type != HClientHello && type != HServerHello)
+ goto Short;
+Ok:
+ if(c->trace)
+ c->trace("recv %s", msgPrint((char*)c->sendp, &c->buf[sizeof(c->buf)] - c->sendp, m));
+ return 1;
+Short:
+ tlsError(c, EDecodeError, "handshake message (%d) has invalid length", type);
+Err:
+ msgClear(m);
+ return 0;
+}
+
+static void
+msgClear(Msg *m)
+{
+ int i;
+
+ switch(m->tag) {
+ case HHelloRequest:
+ break;
+ case HClientHello:
+ freebytes(m->u.clientHello.sid);
+ freeints(m->u.clientHello.ciphers);
+ freebytes(m->u.clientHello.compressors);
+ freebytes(m->u.clientHello.extensions);
+ break;
+ case HServerHello:
+ freebytes(m->u.serverHello.sid);
+ freebytes(m->u.serverHello.extensions);
+ break;
+ case HCertificate:
+ for(i=0; i<m->u.certificate.ncert; i++)
+ freebytes(m->u.certificate.certs[i]);
+ free(m->u.certificate.certs);
+ break;
+ case HCertificateRequest:
+ freebytes(m->u.certificateRequest.types);
+ freeints(m->u.certificateRequest.sigalgs);
+ for(i=0; i<m->u.certificateRequest.nca; i++)
+ freebytes(m->u.certificateRequest.cas[i]);
+ free(m->u.certificateRequest.cas);
+ break;
+ case HCertificateVerify:
+ freebytes(m->u.certificateVerify.signature);
+ break;
+ case HServerHelloDone:
+ break;
+ case HServerKeyExchange:
+ freebytes(m->u.serverKeyExchange.pskid);
+ freebytes(m->u.serverKeyExchange.dh_p);
+ freebytes(m->u.serverKeyExchange.dh_g);
+ freebytes(m->u.serverKeyExchange.dh_Ys);
+ freebytes(m->u.serverKeyExchange.dh_parameters);
+ freebytes(m->u.serverKeyExchange.dh_signature);
+ break;
+ case HClientKeyExchange:
+ freebytes(m->u.clientKeyExchange.pskid);
+ freebytes(m->u.clientKeyExchange.key);
+ break;
+ case HFinished:
+ break;
+ }
+ memset(m, 0, sizeof(Msg));
+}
+
+static char *
+bytesPrint(char *bs, char *be, char *s0, Bytes *b, char *s1)
+{
+ int i;
+
+ if(s0)
+ bs = seprint(bs, be, "%s", s0);
+ if(b == nil)
+ bs = seprint(bs, be, "nil");
+ else {
+ bs = seprint(bs, be, "<%d> [ ", b->len);
+ for(i=0; i<b->len; i++)
+ bs = seprint(bs, be, "%.2x ", b->data[i]);
+ bs = seprint(bs, be, "]");
+ }
+ if(s1)
+ bs = seprint(bs, be, "%s", s1);
+ return bs;
+}
+
+static char *
+intsPrint(char *bs, char *be, char *s0, Ints *b, char *s1)
+{
+ int i;
+
+ if(s0)
+ bs = seprint(bs, be, "%s", s0);
+ if(b == nil)
+ bs = seprint(bs, be, "nil");
+ else {
+ bs = seprint(bs, be, "[ ");
+ for(i=0; i<b->len; i++)
+ bs = seprint(bs, be, "%x ", b->data[i]);
+ bs = seprint(bs, be, "]");
+ }
+ if(s1)
+ bs = seprint(bs, be, "%s", s1);
+ return bs;
+}
+
+static char*
+msgPrint(char *buf, int n, Msg *m)
+{
+ int i;
+ char *bs = buf, *be = buf+n;
+
+ switch(m->tag) {
+ default:
+ bs = seprint(bs, be, "unknown %d\n", m->tag);
+ break;
+ case HClientHello:
+ bs = seprint(bs, be, "ClientHello\n");
+ bs = seprint(bs, be, "\tversion: %.4x\n", m->u.clientHello.version);
+ bs = seprint(bs, be, "\trandom: ");
+ for(i=0; i<RandomSize; i++)
+ bs = seprint(bs, be, "%.2x", m->u.clientHello.random[i]);
+ bs = seprint(bs, be, "\n");
+ bs = bytesPrint(bs, be, "\tsid: ", m->u.clientHello.sid, "\n");
+ bs = intsPrint(bs, be, "\tciphers: ", m->u.clientHello.ciphers, "\n");
+ bs = bytesPrint(bs, be, "\tcompressors: ", m->u.clientHello.compressors, "\n");
+ if(m->u.clientHello.extensions != nil)
+ bs = bytesPrint(bs, be, "\textensions: ", m->u.clientHello.extensions, "\n");
+ break;
+ case HServerHello:
+ bs = seprint(bs, be, "ServerHello\n");
+ bs = seprint(bs, be, "\tversion: %.4x\n", m->u.serverHello.version);
+ bs = seprint(bs, be, "\trandom: ");
+ for(i=0; i<RandomSize; i++)
+ bs = seprint(bs, be, "%.2x", m->u.serverHello.random[i]);
+ bs = seprint(bs, be, "\n");
+ bs = bytesPrint(bs, be, "\tsid: ", m->u.serverHello.sid, "\n");
+ bs = seprint(bs, be, "\tcipher: %.4x\n", m->u.serverHello.cipher);
+ bs = seprint(bs, be, "\tcompressor: %.2x\n", m->u.serverHello.compressor);
+ if(m->u.serverHello.extensions != nil)
+ bs = bytesPrint(bs, be, "\textensions: ", m->u.serverHello.extensions, "\n");
+ break;
+ case HCertificate:
+ bs = seprint(bs, be, "Certificate\n");
+ for(i=0; i<m->u.certificate.ncert; i++)
+ bs = bytesPrint(bs, be, "\t", m->u.certificate.certs[i], "\n");
+ break;
+ case HCertificateRequest:
+ bs = seprint(bs, be, "CertificateRequest\n");
+ bs = bytesPrint(bs, be, "\ttypes: ", m->u.certificateRequest.types, "\n");
+ if(m->u.certificateRequest.sigalgs != nil)
+ bs = intsPrint(bs, be, "\tsigalgs: ", m->u.certificateRequest.sigalgs, "\n");
+ bs = seprint(bs, be, "\tcertificateauthorities\n");
+ for(i=0; i<m->u.certificateRequest.nca; i++)
+ bs = bytesPrint(bs, be, "\t\t", m->u.certificateRequest.cas[i], "\n");
+ break;
+ case HCertificateVerify:
+ bs = seprint(bs, be, "HCertificateVerify\n");
+ if(m->u.certificateVerify.sigalg != 0)
+ bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.certificateVerify.sigalg);
+ bs = bytesPrint(bs, be, "\tsignature: ", m->u.certificateVerify.signature,"\n");
+ break;
+ case HServerHelloDone:
+ bs = seprint(bs, be, "ServerHelloDone\n");
+ break;
+ case HServerKeyExchange:
+ bs = seprint(bs, be, "HServerKeyExchange\n");
+ if(m->u.serverKeyExchange.pskid != nil)
+ bs = bytesPrint(bs, be, "\tpskid: ", m->u.serverKeyExchange.pskid, "\n");
+ if(m->u.serverKeyExchange.dh_parameters == nil)
+ break;
+ if(m->u.serverKeyExchange.curve != 0){
+ bs = seprint(bs, be, "\tcurve: %.4x\n", m->u.serverKeyExchange.curve);
+ } else {
+ bs = bytesPrint(bs, be, "\tdh_p: ", m->u.serverKeyExchange.dh_p, "\n");
+ bs = bytesPrint(bs, be, "\tdh_g: ", m->u.serverKeyExchange.dh_g, "\n");
+ }
+ bs = bytesPrint(bs, be, "\tdh_Ys: ", m->u.serverKeyExchange.dh_Ys, "\n");
+ if(m->u.serverKeyExchange.sigalg != 0)
+ bs = seprint(bs, be, "\tsigalg: %.4x\n", m->u.serverKeyExchange.sigalg);
+ bs = bytesPrint(bs, be, "\tdh_parameters: ", m->u.serverKeyExchange.dh_parameters, "\n");
+ bs = bytesPrint(bs, be, "\tdh_signature: ", m->u.serverKeyExchange.dh_signature, "\n");
+ break;
+ case HClientKeyExchange:
+ bs = seprint(bs, be, "HClientKeyExchange\n");
+ if(m->u.clientKeyExchange.pskid != nil)
+ bs = bytesPrint(bs, be, "\tpskid: ", m->u.clientKeyExchange.pskid, "\n");
+ if(m->u.clientKeyExchange.key != nil)
+ bs = bytesPrint(bs, be, "\tkey: ", m->u.clientKeyExchange.key, "\n");
+ break;
+ case HFinished:
+ bs = seprint(bs, be, "HFinished\n");
+ for(i=0; i<m->u.finished.n; i++)
+ bs = seprint(bs, be, "%.2x", m->u.finished.verify[i]);
+ bs = seprint(bs, be, "\n");
+ break;
+ }
+ USED(bs);
+ return buf;
+}
+
+static void
+tlsError(TlsConnection *c, int err, char *fmt, ...)
+{
+ char msg[512];
+ va_list arg;
+
+ va_start(arg, fmt);
+ vseprint(msg, msg+sizeof(msg), fmt, arg);
+ va_end(arg);
+ if(c->trace)
+ c->trace("tlsError: %s\n", msg);
+ if(c->erred)
+ fprint(2, "double error: %r, %s", msg);
+ else
+ errstr(msg, sizeof(msg));
+ c->erred = 1;
+ fprint(c->ctl, "alert %d", err);
+}
+
+// commit to specific version number
+static int
+setVersion(TlsConnection *c, int version)
+{
+ if(version > MaxProtoVersion || version < MinProtoVersion)
+ return -1;
+ if(version > c->version)
+ version = c->version;
+ if(version == SSL3Version) {
+ c->version = version;
+ c->finished.n = SSL3FinishedLen;
+ }else {
+ c->version = version;
+ c->finished.n = TLSFinishedLen;
+ }
+ return fprint(c->ctl, "version 0x%x", version);
+}
+
+// confirm that received Finished message matches the expected value
+static int
+finishedMatch(TlsConnection *c, Finished *f)
+{
+ return tsmemcmp(f->verify, c->finished.verify, f->n) == 0;
+}
+
+// free memory associated with TlsConnection struct
+// (but don't close the TLS channel itself)
+static void
+tlsConnectionFree(TlsConnection *c)
+{
+ if(c == nil)
+ return;
+
+ dh_finish(&c->sec->dh, nil);
+
+ mpfree(c->sec->ec.Q.x);
+ mpfree(c->sec->ec.Q.y);
+ mpfree(c->sec->ec.Q.d);
+ ecdomfree(&c->sec->ec.dom);
+
+ factotum_rsa_close(c->sec->rpc);
+ rsapubfree(c->sec->rsapub);
+ freebytes(c->cert);
+
+ free(c->serverName);
+
+ memset(c, 0, sizeof(*c));
+ free(c);
+}
+
+
+//================= cipher choices ========================
+
+static int
+isDHE(int tlsid)
+{
+ switch(tlsid){
+ case TLS_DHE_RSA_WITH_AES_128_GCM_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_DHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_DHE_RSA_WITH_AES_256_CBC_SHA:
+ case TLS_DHE_RSA_WITH_3DES_EDE_CBC_SHA:
+ case TLS_DHE_RSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_DHE_RSA_WITH_CHACHA20_POLY1305:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isECDHE(int tlsid)
+{
+ switch(tlsid){
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case TLS_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+ case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_ECDHE_RSA_WITH_CHACHA20_POLY1305:
+
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_GCM_SHA256:
+
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA256:
+ case TLS_ECDHE_RSA_WITH_AES_128_CBC_SHA:
+ case TLS_ECDHE_RSA_WITH_AES_256_CBC_SHA:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isPSK(int tlsid)
+{
+ switch(tlsid){
+ case TLS_PSK_WITH_CHACHA20_POLY1305:
+ case TLS_PSK_WITH_AES_128_CBC_SHA256:
+ case TLS_PSK_WITH_AES_128_CBC_SHA:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+isECDSA(int tlsid)
+{
+ switch(tlsid){
+ case TLS_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case GOOGLE_ECDHE_ECDSA_WITH_CHACHA20_POLY1305:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_GCM_SHA256:
+ case TLS_ECDHE_ECDSA_WITH_AES_128_CBC_SHA256:
+ return 1;
+ }
+ return 0;
+}
+
+static int
+setAlgs(TlsConnection *c, int a)
+{
+ int i;
+
+ for(i = 0; i < nelem(cipherAlgs); i++){
+ if(cipherAlgs[i].tlsid == a){
+ c->cipher = a;
+ c->enc = cipherAlgs[i].enc;
+ c->digest = cipherAlgs[i].digest;
+ c->nsecret = cipherAlgs[i].nsecret;
+ if(c->nsecret > MaxKeyData)
+ return 0;
+ return 1;
+ }
+ }
+ return 0;
+}
+
+static int
+okCipher(Ints *cv, int ispsk, int canec)
+{
+ int i, c;
+
+ for(i = 0; i < nelem(cipherAlgs); i++) {
+ c = cipherAlgs[i].tlsid;
+ if(!cipherAlgs[i].ok || isECDSA(c) || isDHE(c))
+ continue;
+ if(isPSK(c) != ispsk)
+ continue;
+ if(isECDHE(c) && !canec)
+ continue;
+ if(lookupid(cv, c) >= 0)
+ return c;
+ }
+ return -1;
+}
+
+static int
+okCompression(Bytes *cv)
+{
+ int i, c;
+
+ for(i = 0; i < nelem(compressors); i++) {
+ c = compressors[i];
+ if(memchr(cv->data, c, cv->len) != nil)
+ return c;
+ }
+ return -1;
+}
+
+static Lock ciphLock;
+static int nciphers;
+
+static int
+initCiphers(void)
+{
+ enum {MaxAlgF = 1024, MaxAlgs = 10};
+ char s[MaxAlgF], *flds[MaxAlgs];
+ int i, j, n, ok;
+
+ lock(&ciphLock);
+ if(nciphers){
+ unlock(&ciphLock);
+ return nciphers;
+ }
+ j = open("#a/tls/encalgs", OREAD|OCEXEC);
+ if(j < 0){
+ werrstr("can't open #a/tls/encalgs: %r");
+ goto out;
+ }
+ n = read(j, s, MaxAlgF-1);
+ close(j);
+ if(n <= 0){
+ werrstr("nothing in #a/tls/encalgs: %r");
+ goto out;
+ }
+ s[n] = 0;
+ n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
+ for(i = 0; i < nelem(cipherAlgs); i++){
+ ok = 0;
+ for(j = 0; j < n; j++){
+ if(strcmp(cipherAlgs[i].enc, flds[j]) == 0){
+ ok = 1;
+ break;
+ }
+ }
+ cipherAlgs[i].ok = ok;
+ }
+
+ j = open("#a/tls/hashalgs", OREAD|OCEXEC);
+ if(j < 0){
+ werrstr("can't open #a/tls/hashalgs: %r");
+ goto out;
+ }
+ n = read(j, s, MaxAlgF-1);
+ close(j);
+ if(n <= 0){
+ werrstr("nothing in #a/tls/hashalgs: %r");
+ goto out;
+ }
+ s[n] = 0;
+ n = getfields(s, flds, MaxAlgs, 1, " \t\r\n");
+ for(i = 0; i < nelem(cipherAlgs); i++){
+ ok = 0;
+ for(j = 0; j < n; j++){
+ if(strcmp(cipherAlgs[i].digest, flds[j]) == 0){
+ ok = 1;
+ break;
+ }
+ }
+ cipherAlgs[i].ok &= ok;
+ if(cipherAlgs[i].ok)
+ nciphers++;
+ }
+out:
+ unlock(&ciphLock);
+ return nciphers;
+}
+
+static Ints*
+makeciphers(int ispsk)
+{
+ Ints *is;
+ int i, j;
+
+ is = newints(nciphers);
+ j = 0;
+ for(i = 0; i < nelem(cipherAlgs); i++)
+ if(cipherAlgs[i].ok && isPSK(cipherAlgs[i].tlsid) == ispsk)
+ is->data[j++] = cipherAlgs[i].tlsid;
+ is->len = j;
+ return is;
+}
+
+
+//================= security functions ========================
+
+// given a public key, set up connection to factotum
+// for using corresponding private key
+static AuthRpc*
+factotum_rsa_open(RSApub *rsapub)
+{
+ int afd;
+ char *s;
+ mpint *n;
+ AuthRpc *rpc;
+
+ // start talking to factotum
+ if((afd = open("/mnt/factotum/rpc", ORDWR|OCEXEC)) < 0)
+ return nil;
+ if((rpc = auth_allocrpc(afd)) == nil){
+ close(afd);
+ return nil;
+ }
+ s = "proto=rsa service=tls role=client";
+ if(auth_rpc(rpc, "start", s, strlen(s)) == ARok){
+ // roll factotum keyring around to match public key
+ n = mpnew(0);
+ while(auth_rpc(rpc, "read", nil, 0) == ARok){
+ if(strtomp(rpc->arg, nil, 16, n) != nil
+ && mpcmp(n, rsapub->n) == 0){
+ mpfree(n);
+ return rpc;
+ }
+ }
+ mpfree(n);
+ }
+ factotum_rsa_close(rpc);
+ return nil;
+}
+
+static mpint*
+factotum_rsa_decrypt(AuthRpc *rpc, mpint *cipher)
+{
+ char *p;
+ int rv;
+
+ if(cipher == nil)
+ return nil;
+ p = mptoa(cipher, 16, nil, 0);
+ mpfree(cipher);
+ if(p == nil)
+ return nil;
+ rv = auth_rpc(rpc, "write", p, strlen(p));
+ free(p);
+ if(rv != ARok || auth_rpc(rpc, "read", nil, 0) != ARok)
+ return nil;
+ return strtomp(rpc->arg, nil, 16, nil);
+}
+
+static void
+factotum_rsa_close(AuthRpc *rpc)
+{
+ if(rpc == nil)
+ return;
+ close(rpc->afd);
+ auth_freerpc(rpc);
+}
+
+// buf ^= prf
+static void
+tlsP(uchar *buf, int nbuf, uchar *key, int nkey, uchar *label, int nlabel, uchar *seed, int nseed,
+ DigestState* (*x)(uchar*, ulong, uchar*, ulong, uchar*, DigestState*), int xlen)
+{
+ uchar ai[SHA2_256dlen], tmp[SHA2_256dlen];
+ DigestState *s;
+ int n, i;
+
+ assert(xlen <= sizeof(ai) && xlen <= sizeof(tmp));
+ // generate a1
+ s = x(label, nlabel, key, nkey, nil, nil);
+ x(seed, nseed, key, nkey, ai, s);
+
+ while(nbuf > 0) {
+ s = x(ai, xlen, key, nkey, nil, nil);
+ s = x(label, nlabel, key, nkey, nil, s);
+ x(seed, nseed, key, nkey, tmp, s);
+ n = xlen;
+ if(n > nbuf)
+ n = nbuf;
+ for(i = 0; i < n; i++)
+ buf[i] ^= tmp[i];
+ buf += n;
+ nbuf -= n;
+ x(ai, xlen, key, nkey, tmp, nil);
+ memmove(ai, tmp, xlen);
+ }
+}
+
+
+// fill buf with md5(args)^sha1(args)
+static void
+tls10PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{
+ int nlabel = strlen(label);
+ int n = (nkey + 1) >> 1;
+
+ memset(buf, 0, nbuf);
+ tlsP(buf, nbuf, key, n, (uchar*)label, nlabel, seed, nseed,
+ hmac_md5, MD5dlen);
+ tlsP(buf, nbuf, key+nkey-n, n, (uchar*)label, nlabel, seed, nseed,
+ hmac_sha1, SHA1dlen);
+}
+
+static void
+tls12PRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{
+ memset(buf, 0, nbuf);
+ tlsP(buf, nbuf, key, nkey, (uchar*)label, strlen(label), seed, nseed,
+ hmac_sha2_256, SHA2_256dlen);
+}
+
+static void
+sslPRF(uchar *buf, int nbuf, uchar *key, int nkey, char *label, uchar *seed, int nseed)
+{
+ uchar sha1dig[SHA1dlen], md5dig[MD5dlen], tmp[26];
+ DigestState *s;
+ int i, n, len;
+
+ USED(label);
+ len = 1;
+ while(nbuf > 0){
+ if(len > 26)
+ return;
+ for(i = 0; i < len; i++)
+ tmp[i] = 'A' - 1 + len;
+ s = sha1(tmp, len, nil, nil);
+ s = sha1(key, nkey, nil, s);
+ sha1(seed, nseed, sha1dig, s);
+ s = md5(key, nkey, nil, nil);
+ md5(sha1dig, SHA1dlen, md5dig, s);
+ n = MD5dlen;
+ if(n > nbuf)
+ n = nbuf;
+ memmove(buf, md5dig, n);
+ buf += n;
+ nbuf -= n;
+ len++;
+ }
+}
+
+static void
+sslSetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{
+ DigestState *s;
+ uchar h0[MD5dlen], h1[SHA1dlen], pad[48];
+ char *label;
+
+ if(isclient)
+ label = "CLNT";
+ else
+ label = "SRVR";
+
+ md5((uchar*)label, 4, nil, &hsh.md5);
+ md5(sec->sec, MasterSecretSize, nil, &hsh.md5);
+ memset(pad, 0x36, 48);
+ md5(pad, 48, nil, &hsh.md5);
+ md5(nil, 0, h0, &hsh.md5);
+ memset(pad, 0x5C, 48);
+ s = md5(sec->sec, MasterSecretSize, nil, nil);
+ s = md5(pad, 48, nil, s);
+ md5(h0, MD5dlen, finished, s);
+
+ sha1((uchar*)label, 4, nil, &hsh.sha1);
+ sha1(sec->sec, MasterSecretSize, nil, &hsh.sha1);
+ memset(pad, 0x36, 40);
+ sha1(pad, 40, nil, &hsh.sha1);
+ sha1(nil, 0, h1, &hsh.sha1);
+ memset(pad, 0x5C, 40);
+ s = sha1(sec->sec, MasterSecretSize, nil, nil);
+ s = sha1(pad, 40, nil, s);
+ sha1(h1, SHA1dlen, finished + MD5dlen, s);
+}
+
+// fill "finished" arg with md5(args)^sha1(args)
+static void
+tls10SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{
+ uchar h[MD5dlen+SHA1dlen];
+ char *label;
+
+ // get current hash value, but allow further messages to be hashed in
+ md5(nil, 0, h, &hsh.md5);
+ sha1(nil, 0, h+MD5dlen, &hsh.sha1);
+
+ if(isclient)
+ label = "client finished";
+ else
+ label = "server finished";
+ tls10PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, h, sizeof(h));
+}
+
+static void
+tls12SetFinished(TlsSec *sec, HandshakeHash hsh, uchar *finished, int isclient)
+{
+ uchar seed[SHA2_256dlen];
+ char *label;
+
+ // get current hash value, but allow further messages to be hashed in
+ sha2_256(nil, 0, seed, &hsh.sha2_256);
+
+ if(isclient)
+ label = "client finished";
+ else
+ label = "server finished";
+ tls12PRF(finished, TLSFinishedLen, sec->sec, MasterSecretSize, label, seed, SHA2_256dlen);
+}
+
+static void
+tlsSecInits(TlsSec *sec, int cvers, uchar *crandom)
+{
+ memset(sec, 0, sizeof(*sec));
+ sec->clientVers = cvers;
+ memmove(sec->crandom, crandom, RandomSize);
+
+ // putting time()'s output to the first 4 bytes is no
+ // longer recommended and is not useful
+ genrandom(sec->srandom, RandomSize);
+}
+
+static int
+tlsSecRSAs(TlsSec *sec, Bytes *epm)
+{
+ Bytes *pm;
+
+ if(epm == nil){
+ werrstr("no encrypted premaster secret");
+ return -1;
+ }
+ // if the client messed up, just continue as if everything is ok,
+ // to prevent attacks to check for correctly formatted messages.
+ pm = pkcs1_decrypt(sec, epm);
+ if(pm == nil || pm->len != MasterSecretSize || get16(pm->data) != sec->clientVers){
+ freebytes(pm);
+ pm = newbytes(MasterSecretSize);
+ genrandom(pm->data, pm->len);
+ }
+ setMasterSecret(sec, pm);
+ return 0;
+}
+
+static Bytes*
+tlsSecECDHEs1(TlsSec *sec)
+{
+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ Bytes *par;
+ int n;
+
+ if(sec->nc == nil)
+ return nil;
+ if(sec->nc->tlsid == X25519){
+ par = newbytes(1+2+1+32);
+ par->data[0] = 3;
+ put16(par->data+1, X25519);
+ par->data[3] = 32;
+ curve25519_dh_new(sec->X, par->data+4);
+ }else{
+ ecdominit(dom, sec->nc->init);
+ memset(Q, 0, sizeof(*Q));
+ Q->x = mpnew(0);
+ Q->y = mpnew(0);
+ Q->d = mpnew(0);
+ ecgen(dom, Q);
+ n = 1 + 2*((mpsignif(dom->p)+7)/8);
+ par = newbytes(1+2+1+n);
+ par->data[0] = 3;
+ put16(par->data+1, sec->nc->tlsid);
+ n = ecencodepub(dom, Q, par->data+4, par->len-4);
+ par->data[3] = n;
+ par->len = 1+2+1+n;
+ }
+ return par;
+}
+
+static int
+tlsSecECDHEs2(TlsSec *sec, Bytes *Yc)
+{
+ ECdomain *dom = &sec->ec.dom;
+ ECpriv *Q = &sec->ec.Q;
+ ECpoint K;
+ ECpub *Y;
+ Bytes *Z;
+
+ if(Yc == nil){
+ werrstr("no public key");
+ return -1;
+ }
+
+ if(sec->nc->tlsid == X25519){
+ if(Yc->len != 32){
+ werrstr("bad public key");
+ return -1;
+ }
+ Z = newbytes(32);
+ if(!curve25519_dh_finish(sec->X, Yc->data, Z->data)){
+ werrstr("unlucky shared key");
+ freebytes(Z);
+ return -1;
+ }
+ setMasterSecret(sec, Z);
+ }else{
+ if((Y = ecdecodepub(dom, Yc->data, Yc->len)) == nil){
+ werrstr("bad public key");
+ return -1;
+ }
+
+ memset(&K, 0, sizeof(K));
+ K.x = mpnew(0);
+ K.y = mpnew(0);
+
+ ecmul(dom, Y, Q->d, &K);
+
+ setMasterSecret(sec, mptobytes(K.x, (mpsignif(dom->p)+7)/8));
+
+ mpfree(K.x);
+ mpfree(K.y);
+
+ ecpubfree(Y);
+ }
+ return 0;
+}
+
+static void
+tlsSecInitc(TlsSec *sec, int cvers)
+{
+ memset(sec, 0, sizeof(*sec));
+ sec->clientVers = cvers;
+ // see the comment on tlsSecInits
+ genrandom(sec->crandom, RandomSize);
+}
+
+static Bytes*
+tlsSecRSAc(TlsSec *sec, uchar *cert, int ncert)
+{
+ RSApub *pub;
+ Bytes *pm, *epm;
+
+ pub = X509toRSApub(cert, ncert, nil, 0);
+ if(pub == nil){
+ werrstr("invalid x509/rsa certificate");
+ return nil;
+ }
+ pm = newbytes(MasterSecretSize);
+ put16(pm->data, sec->clientVers);
+ genrandom(pm->data+2, MasterSecretSize - 2);
+ epm = pkcs1_encrypt(pm, pub);
+ setMasterSecret(sec, pm);
+ rsapubfree(pub);
+ return epm;
+}
+
+static int
+tlsSecFinished(TlsSec *sec, HandshakeHash hsh, uchar *fin, int nfin, int isclient)
+{
+ if(sec->nfin != nfin){
+ werrstr("invalid finished exchange");
+ return -1;
+ }
+ hsh.md5.malloced = 0;
+ hsh.sha1.malloced = 0;
+ hsh.sha2_256.malloced = 0;
+ (*sec->setFinished)(sec, hsh, fin, isclient);
+ return 0;
+}
+
+static void
+tlsSecVers(TlsSec *sec, int v)
+{
+ if(v == SSL3Version){
+ sec->setFinished = sslSetFinished;
+ sec->nfin = SSL3FinishedLen;
+ sec->prf = sslPRF;
+ }else if(v < TLS12Version) {
+ sec->setFinished = tls10SetFinished;
+ sec->nfin = TLSFinishedLen;
+ sec->prf = tls10PRF;
+ }else {
+ sec->setFinished = tls12SetFinished;
+ sec->nfin = TLSFinishedLen;
+ sec->prf = tls12PRF;
+ }
+}
+
+static int
+setSecrets(TlsConnection *c, int isclient)
+{
+ uchar kd[MaxKeyData], seed[2*RandomSize];
+ char *secrets;
+ int rv;
+
+ assert(c->nsecret <= sizeof(kd));
+ secrets = emalloc(2*c->nsecret);
+
+ memmove(seed, c->sec->srandom, RandomSize);
+ memmove(seed+RandomSize, c->sec->crandom, RandomSize);
+ /*
+ * generate secret keys from the master secret.
+ *
+ * different cipher selections will require different amounts
+ * of key expansion and use of key expansion data,
+ * but it's all generated using the same function.
+ */
+ (*c->sec->prf)(kd, c->nsecret, c->sec->sec, MasterSecretSize, "key expansion",
+ seed, sizeof(seed));
+
+ enc64(secrets, 2*c->nsecret, kd, c->nsecret);
+ memset(kd, 0, c->nsecret);
+
+ rv = fprint(c->ctl, "secret %s %s %d %s", c->digest, c->enc, isclient, secrets);
+ memset(secrets, 0, 2*c->nsecret);
+ free(secrets);
+
+ return rv;
+}
+
+/*
+ * set the master secret from the pre-master secret,
+ * destroys premaster.
+ */
+static void
+setMasterSecret(TlsSec *sec, Bytes *pm)
+{
+ uchar seed[2*RandomSize];
+
+ if(sec->psklen > 0){
+ Bytes *opm = pm;
+ uchar *p;
+
+ /* concatenate psk to pre-master secret */
+ pm = newbytes(4 + opm->len + sec->psklen);
+ p = pm->data;
+ put16(p, opm->len), p += 2;
+ memmove(p, opm->data, opm->len), p += opm->len;
+ put16(p, sec->psklen), p += 2;
+ memmove(p, sec->psk, sec->psklen);
+
+ memset(opm->data, 0, opm->len);
+ freebytes(opm);
+ }
+
+ memmove(seed, sec->crandom, RandomSize);
+ memmove(seed+RandomSize, sec->srandom, RandomSize);
+ (*sec->prf)(sec->sec, MasterSecretSize, pm->data, pm->len, "master secret",
+ seed, sizeof(seed));
+
+ memset(pm->data, 0, pm->len);
+ freebytes(pm);
+}
+
+static int
+digestDHparams(TlsSec *sec, Bytes *par, uchar digest[MAXdlen], int sigalg)
+{
+ int hashalg = (sigalg>>8) & 0xFF;
+ int digestlen;
+ Bytes *blob;
+
+ blob = newbytes(2*RandomSize + par->len);
+ memmove(blob->data+0*RandomSize, sec->crandom, RandomSize);
+ memmove(blob->data+1*RandomSize, sec->srandom, RandomSize);
+ memmove(blob->data+2*RandomSize, par->data, par->len);
+ if(hashalg == 0){
+ digestlen = MD5dlen+SHA1dlen;
+ md5(blob->data, blob->len, digest, nil);
+ sha1(blob->data, blob->len, digest+MD5dlen, nil);
+ } else {
+ digestlen = -1;
+ if(hashalg < nelem(hashfun) && hashfun[hashalg].fun != nil){
+ digestlen = hashfun[hashalg].len;
+ (*hashfun[hashalg].fun)(blob->data, blob->len, digest, nil);
+ }
+ }
+ freebytes(blob);
+ return digestlen;
+}
+
+static char*
+verifyDHparams(TlsSec *sec, Bytes *par, Bytes *cert, Bytes *sig, int sigalg)
+{
+ uchar digest[MAXdlen];
+ int digestlen;
+ ECdomain dom;
+ ECpub *ecpk;
+ RSApub *rsapk;
+ char *err;
+
+ if(par == nil || par->len <= 0)
+ return "no DH parameters";
+
+ if(sig == nil || sig->len <= 0){
+ if(sec->psklen > 0)
+ return nil;
+ return "no signature";
+ }
+
+ if(cert == nil)
+ return "no certificate";
+
+ digestlen = digestDHparams(sec, par, digest, sigalg);
+ if(digestlen <= 0)
+ return "unknown signature digest algorithm";
+
+ switch(sigalg & 0xFF){
+ case 0x01:
+ rsapk = X509toRSApub(cert->data, cert->len, nil, 0);
+ if(rsapk == nil)
+ return "bad certificate";
+ err = X509rsaverifydigest(sig->data, sig->len, digest, digestlen, rsapk);
+ rsapubfree(rsapk);
+ break;
+ case 0x03:
+ ecpk = X509toECpub(cert->data, cert->len, nil, 0, &dom);
+ if(ecpk == nil)
+ return "bad certificate";
+ err = X509ecdsaverifydigest(sig->data, sig->len, digest, digestlen, &dom, ecpk);
+ ecdomfree(&dom);
+ ecpubfree(ecpk);
+ break;
+ default:
+ err = "signaure algorithm not RSA or ECDSA";
+ }
+
+ return err;
+}
+
+// encrypt data according to PKCS#1, /lib/rfc/rfc2437 9.1.2.1
+static Bytes*
+pkcs1_encrypt(Bytes* data, RSApub* key)
+{
+ mpint *x, *y;
+
+ x = pkcs1padbuf(data->data, data->len, key->n, 2);
+ if(x == nil)
+ return nil;
+ y = rsaencrypt(key, x, nil);
+ mpfree(x);
+ data = newbytes((mpsignif(key->n)+7)/8);
+ mptober(y, data->data, data->len);
+ mpfree(y);
+ return data;
+}
+
+// decrypt data according to PKCS#1, with given key.
+static Bytes*
+pkcs1_decrypt(TlsSec *sec, Bytes *data)
+{
+ mpint *y;
+
+ if(data->len != (mpsignif(sec->rsapub->n)+7)/8)
+ return nil;
+ y = factotum_rsa_decrypt(sec->rpc, bytestomp(data));
+ if(y == nil)
+ return nil;
+ data = mptobytes(y, (mpsignif(y)+7)/8);
+ mpfree(y);
+ if((data->len = pkcs1unpadbuf(data->data, data->len, sec->rsapub->n, 2)) < 0){
+ freebytes(data);
+ return nil;
+ }
+ return data;
+}
+
+static Bytes*
+pkcs1_sign(TlsSec *sec, uchar *digest, int digestlen, int sigalg)
+{
+ int hashalg = (sigalg>>8)&0xFF;
+ mpint *signedMP;
+ Bytes *signature;
+ uchar buf[128];
+
+ if(hashalg > 0 && hashalg < nelem(hashfun) && hashfun[hashalg].len == digestlen)
+ digestlen = asn1encodedigest(hashfun[hashalg].fun, digest, buf, sizeof(buf));
+ else if(digestlen == MD5dlen+SHA1dlen)
+ memmove(buf, digest, digestlen);
+ else
+ digestlen = -1;
+ if(digestlen <= 0){
+ werrstr("bad digest algorithm");
+ return nil;
+ }
+
+ signedMP = factotum_rsa_decrypt(sec->rpc, pkcs1padbuf(buf, digestlen, sec->rsapub->n, 1));
+ if(signedMP == nil)
+ return nil;
+ signature = mptobytes(signedMP, (mpsignif(sec->rsapub->n)+7)/8);
+ mpfree(signedMP);
+ return signature;
+}
+
+
+//================= general utility functions ========================
+
+static void *
+emalloc(int n)
+{
+ void *p;
+ if(n==0)
+ n=1;
+ p = malloc(n);
+ if(p == nil)
+ sysfatal("out of memory");
+ memset(p, 0, n);
+ setmalloctag(p, getcallerpc(&n));
+ return p;
+}
+
+static void *
+erealloc(void *ReallocP, int ReallocN)
+{
+ if(ReallocN == 0)
+ ReallocN = 1;
+ if(ReallocP == nil)
+ ReallocP = emalloc(ReallocN);
+ else if((ReallocP = realloc(ReallocP, ReallocN)) == nil)
+ sysfatal("out of memory");
+ setrealloctag(ReallocP, getcallerpc(&ReallocP));
+ return(ReallocP);
+}
+
+static void
+put32(uchar *p, u32int x)
+{
+ p[0] = x>>24;
+ p[1] = x>>16;
+ p[2] = x>>8;
+ p[3] = x;
+}
+
+static void
+put24(uchar *p, int x)
+{
+ p[0] = x>>16;
+ p[1] = x>>8;
+ p[2] = x;
+}
+
+static void
+put16(uchar *p, int x)
+{
+ p[0] = x>>8;
+ p[1] = x;
+}
+
+static int
+get24(uchar *p)
+{
+ return (p[0]<<16)|(p[1]<<8)|p[2];
+}
+
+static int
+get16(uchar *p)
+{
+ return (p[0]<<8)|p[1];
+}
+
+static Bytes*
+newbytes(int len)
+{
+ Bytes* ans;
+
+ if(len < 0)
+ abort();
+ ans = emalloc(sizeof(Bytes) + len);
+ ans->len = len;
+ return ans;
+}
+
+/*
+ * newbytes(len), with data initialized from buf
+ */
+static Bytes*
+makebytes(uchar* buf, int len)
+{
+ Bytes* ans;
+
+ ans = newbytes(len);
+ memmove(ans->data, buf, len);
+ return ans;
+}
+
+static void
+freebytes(Bytes* b)
+{
+ free(b);
+}
+
+static mpint*
+bytestomp(Bytes* bytes)
+{
+ return betomp(bytes->data, bytes->len, nil);
+}
+
+/*
+ * Convert mpint* to Bytes, putting high order byte first.
+ */
+static Bytes*
+mptobytes(mpint *big, int len)
+{
+ Bytes* ans;
+
+ if(len == 0) len++;
+ ans = newbytes(len);
+ mptober(big, ans->data, ans->len);
+ return ans;
+}
+
+/* len is number of ints */
+static Ints*
+newints(int len)
+{
+ Ints* ans;
+
+ if(len < 0 || len > ((uint)-1>>1)/sizeof(int))
+ abort();
+ ans = emalloc(sizeof(Ints) + len*sizeof(int));
+ ans->len = len;
+ return ans;
+}
+
+static void
+freeints(Ints* b)
+{
+ free(b);
+}
+
+static int
+lookupid(Ints* b, int id)
+{
+ int i;
+
+ for(i=0; i<b->len; i++)
+ if(b->data[i] == id)
+ return i;
+ return -1;
+}
--- /dev/null
+++ b/tlssrv.c
@@ -1,0 +1,136 @@
+#include <u.h>
+#include <libc.h>
+#include <bio.h>
+#include <mp.h>
+#include <libsec.h>
+#include <auth.h>
+
+int debug, auth;
+char *keyspec = "";
+char *remotesys = "";
+char *logfile = nil;
+
+static int
+reporter(char *fmt, ...)
+{
+ va_list ap;
+ char buf[2000];
+
+ va_start(ap, fmt);
+ if(logfile){
+ vsnprint(buf, sizeof buf, fmt, ap);
+ syslog(0, logfile, "%s tls reports %s", remotesys, buf);
+ }else{
+ fprint(2, "%s: %s tls reports ", argv0, remotesys);
+ vfprint(2, fmt, ap);
+ fprint(2, "\n");
+ }
+ va_end(ap);
+ return 0;
+}
+
+void
+usage(void)
+{
+ fprint(2, "usage: tlssrv [-D] -[aA] [-k keyspec]] [-c cert] [-l logfile] [-r remotesys] cmd [args...]\n");
+ exits("usage");
+}
+
+void
+main(int argc, char *argv[])
+{
+ TLSconn *conn;
+ char *cert;
+ int fd;
+
+ cert = nil;
+ ARGBEGIN{
+ case 'D':
+ debug++;
+ break;
+ case 'a':
+ auth = 1;
+ break;
+ case 'A':
+ auth = -1; /* authenticate, but dont change user */
+ break;
+ case 'k':
+ keyspec = EARGF(usage());
+ break;
+ case 'c':
+ cert = EARGF(usage());
+ break;
+ case 'l':
+ logfile = EARGF(usage());
+ break;
+ case 'r':
+ remotesys = EARGF(usage());
+ break;
+ default:
+ usage();
+ }ARGEND
+
+ if(*argv == nil)
+ usage();
+
+ conn = (TLSconn*)mallocz(sizeof *conn, 1);
+ if(conn == nil)
+ sysfatal("out of memory");
+
+ if(auth){
+ AuthInfo *ai;
+
+ ai = auth_proxy(0, nil, "proto=p9any role=server %s", keyspec);
+ if(ai == nil)
+ sysfatal("auth_proxy: %r");
+
+ if(auth == 1){
+ Dir nd;
+
+ if(auth_chuid(ai, nil) < 0)
+ sysfatal("auth_chuid: %r");
+
+ /* chown network connection */
+ nulldir(&nd);
+ nd.mode = 0660;
+ nd.uid = ai->cuid;
+ dirfwstat(0, &nd);
+ }
+
+ conn->pskID = "p9secret";
+ conn->psk = ai->secret;
+ conn->psklen = ai->nsecret;
+ }
+
+ if(cert){
+ conn->chain = readcertchain(cert);
+ if(conn->chain == nil)
+ sysfatal("%r");
+ conn->cert = conn->chain->pem;
+ conn->certlen = conn->chain->pemlen;
+ conn->chain = conn->chain->next;
+ }
+
+ if(conn->cert == nil && conn->psklen == 0)
+ sysfatal("no certificate or shared secret");
+
+ if(debug)
+ conn->trace = reporter;
+
+ fd = tlsServer(0, conn);
+ if(fd < 0){
+ reporter("failed: %r");
+ exits(0);
+ }
+ if(debug)
+ reporter("open");
+
+ dup(fd, 0);
+ dup(fd, 1);
+ if(fd > 1)
+ close(fd);
+
+ exec(*argv, argv);
+ reporter("can't exec %s: %r", *argv);
+ exits("exec");
+}