openlvc / portico

Portico is an open source, cross-platform, fully supported HLA RTI implementation. Designed with modularity and flexibility in mind, Portico is a production-grade RTI for the Simulation and Training Community, so come say hi!
http://www.porticoproject.org
151 stars 81 forks source link

Implement RTI/federate encryption key exchange handshake #242

Closed timpokorny closed 6 years ago

timpokorny commented 6 years ago

Summary

To support encrypted communications (#240), when enabled:

This requires a lot of key data to be exchanges, some of which will be randomly generated and not known ahead of time. As such, federates and an RTI must conduct a handshake on startup. This ticket will implement that process.

The general flow can be seen below, noting that we are only implementing the connect and join portion of this exchange on this milestone ticket. Control-channel encryption and session key encryption will be supported in subsequent tickets.

Key Exchange

Acceptance Criteria

Once complete, Portico shall:

timpokorny commented 6 years ago

This is now complete.

When enabled in config (see below), a federate must have access to a private key file (PEM) format for itself, and another PEM format file with the public key of the RTI. When connecting, it encrypts all non-federation messages with the RTI's public key to ensure only the RTI can decrypt them. It will send an Authenticate message that the RTI will grab. This contains the federate's public key. In turn, the RTI responds with a message that includes an authToken that the federate should use from there on in. It encrypts this message with the federate's public key.

When a federate joins a federation it is given access to a symmetric session key that is used for all exchange of federation-specific messages.

By default, the RTI will allow connection from non-authenticating federates. These messages will go unencrypted. Through the enforced config option this accepting and tolerant behaviour can turned off, and federates not using the PKI scheme will fail to be able to communicate with the RTI and thus never join a federation.

Configuration on RTI side (applied to any connection type):

    # (R.6a) Public Key Authentication and Encryption
    #
    #        Federates connect to the RTI using PKI. The federate must have the
    #        RTI's public key on file locally. When connecting to the RTI and
    #        performing non-federation calls (Create, Destroy, Join, Resign, ...),
    #        the messages are encrypted with the RTIs public key. On connect, they
    #        also exchange keys, with return messages from the RTI encrypted using
    #        the federates public key. Once a federation is joined, the federate
    #        is given access to a federation-wide shared key and that is used instead.
    #        (Less CPU load and needed so federates can read each other's broadcasts).
    #
    #        NOTE: Only one of Public Key or Symmetric options can be enabled at once.
    #
    #          Enabled: Is encryption on or off?
    #         Enforced: Enforce that all connections use a public key. When set to false,
    #                   federates can connect even if they do not provide creds. Only used by RTI.
    #       PrivateKey: Points to the file containing the federate's private key (PEM format)
    #        RtiPublic: Points to the file containing the RTI public key (PEM format) Only used by LRC.
    #    SessionCipher: Cipher configuration for federation messages
    #    SessionKeylen: Bit-length of shared key for federation messages
    #      
    rti.network.tcp.publickey.enabled       = false
    rti.network.tcp.publickey.enforced      = false
    rti.network.tcp.publickey.privatekey    = ./id_rsa
    rti.network.tcp.publickey.sessionCipher = AES/CFB/NoPadding
    rti.network.tcp.publickey.sessionKeylen = 128

Configuration on LRC side (applied to any connection):

    # (R.6a) Public Key Authentication and Encryption
    #
    #        Federates connect to the RTI using PKI. The federate must have the
    #        RTI's public key on file locally. When connecting to the RTI and
    #        performing non-federation calls (Create, Destroy, Join, Resign, ...),
    #        the messages are encrypted with the RTIs public key. On connect, they
    #        also exchange keys, with return messages from the RTI encrypted using
    #        the federates public key. Once a federation is joined, the federate
    #        is given access to a federation-wide shared key and that is used instead.
    #        (Less CPU load and needed so federates can read each other's broadcasts).
    #
    #        NOTE: Only one of Public Key or Shared Key options can be enabled at once.
    #
    #          Enabled: Is encryption on or off?
    #         Enforced: Enforce that all connections use a public key. When set to false,
    #                   federates can connect even if they do not provide creds. Only used by RTI.
    #       PrivateKey: Points to the file containing the federate's private key (PEM format)
    #        RtiPublic: Points to the file containing the RTI public key (PEM format) Only used by LRC.
    #    SessionCipher: Cipher configuration for federation messages
    #    SessionKeylen: Bit-length of shared key for federation messages
    #      
    lrc.network.multicast.publickey.enabled       = false
    lrc.network.multicast.publickey.privatekey    = ./id_rsa
    lrc.network.multicast.publickey.rtipublic     = ./rti_public.pem
    lrc.network.multicast.publickey.sessionCipher = AES/CFB/NoPadding
    lrc.network.multicast.publickey.sessionKeylen = 128