7

I'm writing software for the JVM (in Clojure) that performs a cryptographic operation. Specifically, given a secret input, secret key, non-secret salt, non-secret personalization, it uses BLAKE2 to derive 512 bits of key material. It then chops up that array into two 256 bit chunks, using tools from the Arrays class. (source)

The actual implementation of that operation lives in libsodium so it's implemented in C. I'm using caesium to access it, which is a wrapper over kalium, a library that uses jnr-ffi to call the underlying C implementation.

Since all of the buffers above have sensitive key material, I'd like to make sure it's purged from memory. I'm not sure how to do that safely on the JVM (heck, I'm not even sure I know how to do that safely in C). Given that the material is translated from C const char * to JVM byte[], and then some of my operations make new JVM byte arrays, key material is going to live in JVM byte arrays. That raises two concerns:

  • If I zerofill a byte[], which is not touched by any code afterwards, how can I be sure the byte[] was actually zeroed out? I'd assume the JVM is free to optimize that away.
  • Even if I was able to guarantee a byte[] gets zeroed out, how do I know the JVM hasn't decided to copy that array (e.g. in the context of a garbage collector) plenty of times without zeroing out the original location, hence leaving the keying material all over virtual memory anyway?

I'm guessing the answer will end up being "do it in C & ASM" or maybe even "do it in a HSM", but I'd love to hear if there are JVM-land ways of fixing this problem.

lvh
  • 740
  • 5
  • 14
  • 3
    If the reference to the `byte[]` has been published to outside of the scope of the method where it was created, then the JVM optimizer cannot decide to ignore your inter-thread write actions. However, your second point is fully valid and there is probably nothing you can do about it. – Marko Topolnik Sep 30 '14 at 12:44
  • 4
    If you coded against a direct `ByteBuffer`, which allocates non-heap memory, then you may have been in a better position regarding the point about GC. Something from the `Unsafe` class may also prove useful. – Marko Topolnik Sep 30 '14 at 12:47
  • Thanks for your comments, @MarkoTopolnik. Are you sure that the JVM is not allowed to optimize that away if the reference is visible in more than one place, even if it can prove nothing else touches that reference after the zeroing? Getting this behavior from C compilers, for example, ends up being quite tricky. (That is, preventing them from optimizing the zeroing-out away.) – lvh Oct 01 '14 at 20:45
  • My reading of the `ByteBuffer` docs suggests that all of its guarantees are very much optional; it's basically a performance optimization. I guess that's a moot point if every JVM you're going to find on a server does actually give you a real direct `ByteBuffer` if you ask for it. – lvh Oct 01 '14 at 20:49
  • Yes, I would have to retract that argument with optimizing code away due to the following: the method may be *inlined* on some code paths, and then the story is quite different. The JITC could then easily optimize away zeroing out if that makes sense within the inlining horizon. – Marko Topolnik Oct 02 '14 at 08:01
  • About `ByteBuffer` and other concerns, your requirement is definitely outside any wholesale guarantees and strictly specified behavior because that would go against the grain of the Java platform. But, as you already concluded, it can be verified on a case-by-case basis that the guarantees are stronger than those in the official specification. – Marko Topolnik Oct 02 '14 at 08:04
  • possible duplicate of [Java security: how to clear/zero-out memory associated with an object? (And/or ensure that is the only instance/copy of a particular variable)](http://stackoverflow.com/questions/6473352/java-security-how-to-clear-zero-out-memory-associated-with-an-object-and-or-e) – Gavin S. Yancey Oct 13 '14 at 20:49
  • You could write the zero’d direct `ByteBuffer` to a temporary file. That doesn’t give much room for optimizations to a JVM. – Holger Oct 21 '14 at 10:33

1 Answers1

1

If all you need is to clean an array, Arrays.fill does not allocate a new array but changes the values in the one passed as parameter. If you download the sources you can read it directly there.

dtortola
  • 768
  • 4
  • 6