[openstack-dev] [infra][security] Encryption in Zuul v3
clint at fewbar.com
Tue Mar 21 20:20:34 UTC 2017
Excerpts from corvus's message of 2017-03-21 09:36:41 -0700:
> In working on the implementation of the encrypted secrets feature of
> Zuul v3, I have found some things that warrant further discussion. It's
> important to be deliberate about this and I welcome any feedback.
Thanks for looking into this deeply.
> For reference, here is the relevant portion of the Zuul v3 spec:
> And here is an implementation of that:
> The short version is that we want to allow users to store private keys
> in the public git repos which Zuul uses to run jobs. To do this, we
> propose to use asymmetric cryptography (RSA) to encrypt the data. The
> specification suggests implementing PKCS#1-OAEP, a standard for
> implementing RSA encryption.
> Note that RSA is not able to encrypt a message longer than the key, and
> PKCS#1 includes some overhead which eats into that. If we use 4096 bit
> RSA keys in Zuul, we will be able to encrypt 3760 bits (or 470 bytes) of
Hm, I must have read the standard wrong, I thought it was 480 bytes. I
very much trust your reading of it and experimentation with it above my
> Further, note that value only holds if we use SHA-1. It has been
> suggested that we may want to consider using SHA-256 with PKCS#1. If we
> do, we will be able to encrypt slightly less data. However, I'm not
> sure that the Python cryptography library allows this (yet?). Also, see
> this answer for why it may not be necessary to use SHA-256 (and also,
> why we may want to anyway):
I think our hand will get forced into SHA256, and if we can land SHA256
support in cryptography's PKCS#1, we should. But until that's done, SHA1
is cryptographically sound, and I think we don't have time to get things
into perfect shape, so as long as we can accommodate SHA256 when it's
ready, seems like the mathemeticians think SHA1 is fine in PKCS#1.
> One thing to note is that the OpenSSL CLI utility uses SHA-1. Right
> now, I have a utility script which uses that to encrypt secrets so that
> it's easy for anyone to encrypt a secret without installing many
> dependencies. Switching to another hash function would probably mean we
> wouldn't be able to use that anymore. But that's also true for other
> systems (see below).
We may just have to require python and a recent cryptography if people
are unable to use a SHA1 PKCS#1.
> In short, PKCS#1 pros: Simple, nicely packaged asymmetric encryption,
> hides plaintext message length (up to its limit). Cons: limited to 470
> bytes (or less).
I'd list the confusion around SHA1 as a con as well.
> Generally, when faced with the prospect of encrypting longer messages,
> the advice is to adopt a hybrid encryption scheme (as opposed to, say,
> chaining RSA messages together, or increasing the RSA key size) which
> uses symmetric encryption with a single-use key for the message and
> asymmetric encryption to hide the key. If we want Zuul to support the
> encryption of longer secrets, we may want to adopt the hybrid approach.
> A frequent hybrid approach is to encrypt the message with AES, and then
> encrypt the AES key with RSA.
> The hiera-eyaml work which originally inspired some of this is based on
> PKCS#7 with AES as the cipher -- ultimately a hybrid approach. An
> interesting aspect of that implementation is that the use of PKCS#7 as a
> message passing format allows for multiple possible underlying ciphers
> since the message is wrapped in ASN.1 and is self-descriptive. We might
> have simply chosen to go with that except that there don't seem to be
> many good options for implementing this in Python, largely because of
> the nightmare that is ASN.1 parsing.
> The system we have devised for including encrypted content in our YAML
> files involves a YAML tag which specifies the encryption scheme. So we
> can evolve our use to add or remove systems as needed in the future.
> So to break this down into a series of actionable questions:
> 1) Do we want a system to support encrypting longer secrets? Our PKCS#1
> system supports up to 470 bytes. That should be sufficient for most
> passwords and API keys, but unlikely to be sufficient for some
> certificate related systems, etc.
Ultimately yes. The issue brought up earlier about SSH keys suggests
we'll likely need to be able to handle private keys that are 4096 bits
> 2) If so, what system should we use?
> 2.1a) GPG? This has hybrid encryption and transport combined.
> Implementation is likely to be a bit awkward, probably involving
> popen to external processes.
As awkward as popening the cli's can be, there are at least libraries
> 2.1b) RSA+AES? This recommendation from the pycryptodome
> documentation illustrates a typical hybrid approach:
> The transport protocol would likely just be the concatenation of
> the RSA and AES encrypted data, as it is in that example. We can
> port that example to use the python-cryptography primatives, or we
> can switch to pycryptodome and use it exactly.
I seem to recall there were some issues with enterprise compliance and
PyCrypto. I'm guessing PyCryptodome is still subject to those issues.
> 2.1c) RSA+Fernet? We can stay closer to the friendly recipes in
> python-cryptography. While there is no complete hybrid recipe,
> there is a symmetric recipe for "Fernet" which is essentially a
> recipe for AES encryption and transport. We could encode the
> Fernet key with RSA and concatenate the Fernet token.
Given that OpenStack has embraced Fernet's crypto for tokens, it at
least keeps us aligned with OpenStack's cryptographic interests. I think
this is worth experimenting with.
> 2.1d) NaCL? A "sealed box" in libsodium (which underlies PyNaCL)
> would do what we want with a completely different set of
Seems fine, though EC crypto has as many tinfoil hat wearing critics as
tinfoil hat wearing fans, and less time under its belt than RSA.
> 3) Do we think it is important to hide the length of the secret? AES
> will expose the approximate length of the secret up to the block size
> (16 bytes). This is probably not important for long secrets, but for
> short ones, it may at least indicate the order of magnitude of a
> password, for instance. If we want, we can pad the secret further
> before encrypting it.
I think it's a useful security feature to hide the length of secrets,
whether they be passwords or symmetric keys. I don't think I'd rank it
as a top priority, but very near the top given that this is the type of
cipher text that you post on a billboard (in your git repo).
> 4) If we adopt a system for longer secrets, do we still want to include
> PKCS#1 for shorter ones? The PKCS#1 ciphertext will be shorter than the
> same secret would be in a hybrid system. But either way, we're talking
> about a fairly big blob (about 9 lines of base64 for PKCS#1).
These things rarely resolve quickly. I'd expect the PKCS#1 implementation
we have now to survive a 3.0 release, and so, I think we should prepare
to maintain this for a long time.
> 5) If we keep PKCS#1, are we okay using SHA-1 or should we see about
> using SHA-256?
See above, but I think we should support both if we don't have a clear
plan for longer secrets. Once we have a plan for longer secrets, we'll
have a workaround for users who are constrained and cannot or will not
> 6) Considering all of that, should we:
> A) Stick with PKCS#1 for now (we can always add something else later)
Yes let's keep what we have and expand as-needed.
> B) Keep PKCS#1 and add something else now
I think this would take our focus off the important issues now. But we
shouldn't stop the conversation, just put it on the back burner while we
get zuulv3 up and operational.
> C) Drop PKCS#1 and replace it with something else
Let's keep it. There's nothing "wrong" about it, and it won't be a
massive chore to keep supporting it as long as we have computers that
let us use SHA-1. Worst case, we can have Zuul provide a one-time "fix
all my repos" utility if we find outselves painted into a corner and
have to remove PKCS#1.
More information about the OpenStack-dev