Jump to: navigation, search

Difference between revisions of "MessageSecurity"

m (Message Encryption)
Line 132: Line 132:
 
</pre>
 
</pre>
  
PyCrypto has all the code needed to implement this.
+
Various python crypto modules have all the code needed to implement this.
 +
 
 +
== Client Authentication and Key Exchange ==
 +
 
 +
Although not mandatory to implement or use we propose an authentication and key retrieval scheme to request and trasnfer Singing and Encryption Keys.
 +
 
 +
=== RESTful API ===

Revision as of 22:40, 22 April 2013

Message Security

Message Security in OpenStack is currently not implemented. Recently there have been a couple of proposals to implement signatures and eventually encryption for RPC messages.

Implementing this kind of security features is a delicate task as there are the usual conflicting trade off between security and performance as well as some peculiar issues with the nature of OpenStack distributed environment.

Public Crypto versus Shared Keys

One of the assumption in the present proposals is that Public Key crypto will be used to provide Integrity for messages, however a simple Shared Key crypto model is also well suited to handle Messaging Queues.

One reason why Public Key crypto is being proposed is the perceived lower overhead of the public key trust model. However let's analize what is required to use either model.

Public Key Infrastructure

The first point is that each service that needs to send messages will need to own a Public/Private Key pair, and this means some form of secure storage for the Private Key.

Next the Public Key also must be made available, there are 2 strategies to do so, a PKI model where a Public Key is signed by a Central Authority, or a Trusted Repository where all Public Key are deposited and guaranteed to be good by either cross signatures or a well know party that can authoritatively assert wheher a key is good or bad. This in turn require that quite regularly all clients check that their peers keys are still valid and not revoked. This is true with either a PKI style system where CRLs or OCSP responders are queried, or a central trust authority where Public Keys are checked for validity.

There is also the non-trivial task of deciding where keys are generated as virtual machine based systems tend to have poor entropy and sourcing enough to generate key pairs can be a pproblem at installation time. Following that there is the problem of how to communicate the public key to the CA for signing or to the trusted repository for depositing it.

Shared Key Infrastructure

In a shared key model ideally each actor would have a different shared key with each and every other peer, however this becomes very quickly impossible to achieve as the number of peers scales up, both in tem of storage and exchanges necessary. A Key Server approach is the only reasonable way.

With a Key Server each service only need a key shared with the Key Server itself, the actual key used to communicate between any 2 peers is provided on the fly by the key server, which needs to obe contacted by both peers before actually sending messages between them. The Key server wil provide Signing and Encryption keys that are bound to a specific peer-pair and allow them to communicate safely.

Once keys are obtained the two peers become indipendent from the Key Server and can send as many messages as required until the keys are valid. In such a system the keys shared between Services and Key Server have long term validity while the signing and encryption keys can have a relatively short validty period so that brute force attacks on the messages will not lead to gaining access to any long term secret and be of limited value.

Security considerations for the central trusted server

With either model the central server will have to store some keys and guarantee their validity.

In the Public Key model the central server needs to be able to provide proof of validity of a key and mark as revoked keys that are considered compromised. In order to do that a signing key needs to be handled by this server and signatures are used to mark public keys as valid or revoked via CRLs or OCSPs. In the case of short lived Public Keys instead an authentication system needs to be provided so that services can authenticate and store their public keys. In both cases the overall system will have to rely on some Public Key that identifies the trusted authority, be it in the form of the Public Key and Certificated representing a PKI or be it in the form of a Public Key and x509 Certificate used to secure the connection with the Trusted Repository. In either case, a compromise of this key will compromise the whole system.

In the shared key model the Key Server will hold a master key used to encrypt the Service keys in its storage, authentication between services and the Key Server is based on a shared Secret that can be easily rotated. Revocation is not necessary as all is needed is to remove the compromised secrets from the Key Server and generate new ones. Revocation is replaced by faster expiring signing/encryption keys.

With either system a statless service will have to contact the trusted authority, be it a PKI, Trusted Repository or Key Server in order to either get a session key or to check the revocation status. With both systems services that can keep state can cache the session key or the revocation checks until expiration of the keys or until the next validation interval expires so the required communication overhead between services and a central system is similar.

Shared Keys and Key Server Proposal

Public Key crypto is generally slow and complex, so given that the overhead for both systems in terms of security of the central server or communication requirements looks similar we put forward the proposal of using a Share Key system based on a Key Server.

To note is that the shared key between Services and Key Server is basically needed just for authentication, and this means that this part can be deferred to existing components, for example password based authentication over an HTTPS connection would be sufficient to authenticate a Service to the Key Server. Other methods that would work as well would be Kerberos keytabs and a KDC for authentication, x509 User Certificates and so on. Basically, the authentication part can be abstracted away if needed.

That said we will proceed to describe also an authentication method based on shared keys that will work for communication over pure HTTP (non encrypted) transport for completeness. We'll defer to the deployment strategy which method to use in preference.

Message Integrity and Confidentiality

Securing the message queue requires two distinct components:

  • Integrity or Signing and authentication of messages
  • Confidentiality or Encryption of the messages

Standards

The standard for providing message integrity is HMAC. For encryption the most respected alogrithm is currently AES, a block cipher with a fixed 128bits block size.

Because the current feeling is that encryption may not be necessary we will consider it optional. In order to avoid changing message formats this means it is more convenient to use an "encryption first, authentication later" approach, whereby the authentication step does not differ based on whether encryption is performed or not, rather the msg being authenticated can be either plain text or encrypted.

The next step is sketching out how to apply encryption and authentication to the message keeping in mind the Horton Principle.

Message Format

The data interchange format en vogue in the project is JSON so we will create a message format based on JSON syntax. The first thing we want to assure is that authentication covers all the message as well as the metadata tied to the message, this is important to avoid subsitution attacks where the metadata may be swapped out and replaced without afecting the signature. This means that Message and Metadata will be serialized objects contained in a simpler container..

Pseudo JSON notation:

MetaData = jsonutils.dumps({
    'source': <sender>,
    'destination': <receiver>,
    'timestamp': <unixtime>,
    'counter': <number>,
    'encryption': <true | false>
})
Message = jsonutils.dumps(raw_msg)

_METADATA_KEY = 'oslo.swcure.metadata'
_SIGNATURE_KEY = 'oslo.secure.hmac'

RPC_Message = {
    _VERSION_KEY: _RPC_ENVELOPE_VERSION,
    _METADATA_KEY: MetaData,
    _MESSAGE_KEY: Message,
    _SIGNATURE_KEY: Signature
}


Message Signature

The Signature is calulated over a concatenation of buffers that includes 2 parts with length prefixes

Version = null terminated string containing the version number
MetaData = serialized JSON Metadata
Message = serialized JSON Message

Signature = HMAC(Key, (Version || MetaData || Message))

We propose to use HMAC-SHA-256 as the authentication function as per RFC 6234.

NOTE: Particular care needs to be taken to make sure the RPC_Message obtained in input cannot be abused and the rest of the pipeline will use exclusively what has been authenticated. For this reason the output of the validation function should be a separate structure that p[rovides unserialized Metadata and Message, and further components should not have access to the original RPC_Message. If the same format needs to be maintained a new RPC_Message containing only the version and serialized message will be provided in output, rebuilt from the verified values.

Hashlib has all the code nded to implement this.

Message Encryption

Optionally the message may be encrypted, in this case the MetaData field 'encryption' will be set to True.

Because the use of nonces is particuarly difficult to get right, and the use of message queues may involve multiple parties using the same keys when they act in a cluster and because there is a desire to allow as much as possible stateless services, we propose the use of AES-128-CBC with a Random IV in order to encrypt the content. This requires the availability of a pseudo-random generator on the sender side, we do not expect this to be an issue in practice on the machines used in a typical OpenStack deployment.

Encryption:

Plain-Text = P1 || P2 || P3 || ...
C0 = Random IV (128bit)
for i in range(1, N):
   Ci = ENC(Key, Pi^Ci-1)
Encrypted-Message = C0 || C1 || C2 || C3 || ...

Decryption:

IV = C0
Cipher-Text = C1 || C2 || C3 || ...
for i in range (1, N):
    Pi = DEC(key, Ci)^Ci-1
Plain-Text = P1 || P2 || P3 || ...

Various python crypto modules have all the code needed to implement this.

Client Authentication and Key Exchange

Although not mandatory to implement or use we propose an authentication and key retrieval scheme to request and trasnfer Singing and Encryption Keys.

RESTful API