shithub: tlssrv.sni

Download patch

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");
+}