11
$\begingroup$

Do I need to worry about timing attacks in Base64 encoding/decoding of private keys? This is a common operation (ex. PEM keys) and is variable time in typical implementations.

$\endgroup$
  • $\begingroup$ It may be worth to notice that the private key material to be base64 encoded/decoded is commonly ASN.1 DER encoded. DER encoding may also reveal some information via timing differences, as well. $\endgroup$ – user4982 May 23 '16 at 18:40
  • $\begingroup$ @user4982 True, but DER is TLV encoding. The length of the value may be disclosed, but the value itself is not as likely to be exposed. Knowing the length of e.g. the CRT parameters may make it slightly easier to get to the key, but it would be very tough to get the key value that way. $\endgroup$ – Maarten Bodewes May 23 '16 at 20:37
7
$\begingroup$

Yes, kind of. The encoding does depend on the individual bits so there could very well be timing differences. Note that the differences would be pretty small; encoding a byte is likely much faster than e.g. modular exponentiation. But as even block ciphers are vulnerable it may very well be possible, especially since table lookup may be implemented.

The solution however is also very standard: before storing the keys you first wrap the keys with a symmetric cipher. The key can be derived from a strong passphrase. Most of the time when private keys are stored in PEM format (which is just the ASCII armor) they are encoded in PKCS#12. As the encoding after the wrapping and the decoding before, you should be secure against timing attacks against base 64. Obviously the block cipher is much more likely to be protected against timing attacks (but don't forget to verify that).

In principle PKCS#8 could also describe an encrypted private key, but that doesn't seem to be used much. PKCS#12 can be used to encrypt the private key and store the certificate (chain) for the public / private key pair.

If you directly store them in PKCS#1 or PKCS#8 format (without encryption) you should make sure the system itself is secure - especially with regards to file access.

$\endgroup$
  • $\begingroup$ PKCS12 is certainly common in DER, but not in PEM that I've seen. PKCS8 PEM is (fairly) common in things using or compatible with OpenSSL, but PKCS8 has optional pw-based encryption -- and the privatekey encryption in PKCS12 is PKCS8. $\endgroup$ – dave_thompson_085 May 23 '16 at 22:59
  • $\begingroup$ @dave_thompson_085 Usually the PKCS#8 structure is only the inner PKCS#8 structure. That doesn't contain encryption. PKCS#12 on the other hand is a software token format where password encryption is usually performed. But I'll edit the answer a bit to show this. Do you often encounter PKCS#8 encrypted keys? $\endgroup$ – Maarten Bodewes May 23 '16 at 23:12
  • $\begingroup$ @MaartenBodewes PKCS #8 for the most private key formats stores public key (as a part of the private key). RFC 5958 is an update to PKCS #8, which allows to store also public key explicitly (but this extension does not seem very common currently). PKCS #8 encrypted keys are seems from time to time, especially in applications where you would want to password protect the private key, but storing certificate is necessary or possible due to how the key is used. $\endgroup$ – user4982 May 24 '16 at 4:44
  • $\begingroup$ (@user4982) In OpenSSL req -newkey pkcs8 -topk8 pkcs12 (import) (since 2010) all generate PKCS8 encrypted (and PEM) by default; only genpkey defaults to unencrypted and I've not yet seen anyone else (other than OpenSSL site) even mention that. So yes I do see PKCS8 mostly encrypted, though certainly not always. But on the whole I do see PKCS12 more, which always encrypts the privatekey; it usually encrypts the cert(s) also, though weakly and unnecessarily. $\endgroup$ – dave_thompson_085 May 24 '16 at 19:24
-2
$\begingroup$

The only inevitable data dependence in Base 64 encoding or decoding is on the length of the value being encoded or decoded.

Encoding

  1. Extraction of 6-bit value by bit shift and masking: constant time.
  2. Conversion from 6-bit to a character: indexing into a 64-character table, constant time.

Decoding

  1. Conversion from a character into a 6-bit value: indexing into a 256-byte table, constant time.
  2. Insertion into decoded data by bit shift and ORing: constant time.
$\endgroup$
  • 1
    $\begingroup$ Indexing into a 256-byte (or even 64-byte) table is not necessarily constant time. CPU cache lines can be e.g. 32 or 64 bytes. A timing attack could tell the difference between using only parts of the table and using it all. $\endgroup$ – otus May 29 '16 at 13:24
  • $\begingroup$ You're right that length is the only inevitable timing side channel (at least as an upper-bound). But most implementations, including yours, are not constant time since they rely on array lookup with a secret index, which isn't guaranteed to be constant time on many CPUs. $\endgroup$ – CodesInChaos May 29 '16 at 15:15
  • $\begingroup$ This is not a constant-time implementation. Indexing into a 256-byte table is vulnerable to cache-based timing attacks. See Cache-timing attacks on AES by D. Bernstein. $\endgroup$ – Demi May 29 '16 at 18:23

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.