Implement new Encryption interface WIP#499
Implement new Encryption interface WIP#499milleruntime wants to merge 27 commits intoapache:masterfrom
Conversation
milleruntime
commented
May 23, 2018
- Removed all old CryptoModule properties and code
- Created EncryptionStrategy interface
- Made default encryption NoEncryptionStrategy
- Created 3 experimental config properties for crypto, one of which is sensitive
- Modified BCFile to only write EncryptionStrategy
- Implmented usable example strategy AESCBCEncryptionStrategy
- Modified Initialize to use site config
* Removed all old CryptoModule properties and code * Created EncryptionStrategy interface * Made default encryption NoEncryptionStrategy * Created 3 experimental config properties for crypto, one of which is sensitive * Modified BCFile to only write EncryptionStrategy * Implmented usable example strategy AESCBCEncryptionStrategy * Modified Initialize to use site config
| decryptingInput = input; | ||
| } | ||
|
|
||
| decryptingInput = input; |
There was a problem hiding this comment.
I kept this because LogSorter uses both input and decryptingInput... although I am not sure why
ctubbsii
left a comment
There was a problem hiding this comment.
First pass. I may have misunderstood some stuff on the first pass (the role of the EncryptionStrategy class name being serialized; I think it's in place of the parameters). Overall, I like all the deleted code. 😺
| this.cipherOut = encryptionStrategy.encryptStream(fsBufferedOutput); | ||
| this.out = compressionAlgo.createCompressionStream(cipherOut, compressor, 0); | ||
| } catch (IOException e) { | ||
| } catch (Exception e) { |
There was a problem hiding this comment.
Is it possible to avoid overly broad catching of Exception here?
There was a problem hiding this comment.
Perhaps... if we make the encrypt/decrypt methods throw IOException, this would force the strategy to handle all the funky encryption exceptions. For instance, the AESCBC strategy I wrote can also throw NoSuchAlgorithmException, NoSuchPaddingException, InvalidKeyException, InvalidAlgorithmParameterException. A catch all for these that logs and returns an unencrypted stream is another approach.
There was a problem hiding this comment.
We definitely want to hard-throw the exception. Logging and proceeding without encryption is only marginally better than a completely silent failure.
| // secretKeyEncryptionStrategy.encryptSecretKey(cryptoParameters); | ||
|
|
||
| this.encryptionStrategy = EncryptionStrategyFactory | ||
| .setupConfiguredEncryption(accumuloConfiguration, EncryptionStrategy.Scope.RFILE); |
There was a problem hiding this comment.
For a new pluggable interface that we expect users to implement directly as (eventually) public API, we should avoid passing in AccumuloConfiguration, which is an internal-only object type.
There was a problem hiding this comment.
I would recommend passing in a Map<String, String> containing everything that start with CRYPTO_SENSITIVE_PREFIX with the prefix stripped off.
| } | ||
| long offsetCryptoParameter = out.position(); | ||
| String es = this.encryptionStrategy.getClass().getName(); | ||
| out.writeUTF(es); |
There was a problem hiding this comment.
I don't think the overall encryption strategy should be serialized into the files. The pluggable encryption strategy could change, and the current strategy should be required to be able to interpret anything previously written. So, I don't think it's necessary to serialize the name of the strategy here... but perhaps we should permit it some opportunity write some magic values/parameters that it (or its future replacement, if swapped out) could recognize?
There was a problem hiding this comment.
The use case of switching encryption strategies should be considered. Files written with previously configured encryption strategies may need different properties to be decrypted. One possible way to handle this is with a prefix per an encryption strategy and this prefix is persisted. This would look something like the following.
crypto.strategy.s201805.class=com.foo.Best
crypto.strategy.s201805.props.mykey=XU24GNYXE74VSGJM
table.file.crypto.strategy=s201805
tserver.wal.crypto.strategy=s201805
In the above example when files are written, s201805 would be persisted in the file and used for future decryption. Using this scheme only s201805 need be written to the file, the class names does not as it comes from config associated with s201805. This scheme also allows different tables to use different encryption.
For the use case of switching to a new encryption strategy, the config would look like the following.
crypto.strategy.s201805.class=com.foo.Best
crypto.strategy.s201805.props.mykey=XU24GNYXE74VSGJM
crypto.strategy.s201903.class=com.foo.Better
crypto.strategy.s201903.props.thekey=56GU5ET6TQKZZCKP
table.file.crypto.strategy=s201903
tserver.wal.crypto.strategy=s201903
The s201805 props need to be kept around as long as there are files encrypted with that config. New files that are written will persist s201903 and use that config.
There was a problem hiding this comment.
The table.file.crypto.strategy and tserver.wal.crypto.strategy were something I conjured up on a whim. Not sure if there are pitfalls to this approach. Could have system level config for selecting crypto like the following.
crypto.strategy.s201805.class=com.foo.Best
crypto.strategy.s201805.props.mykey=XU24GNYXE74VSGJM
crypto.strategy.s201903.class=com.foo.Better
crypto.strategy.s201903.props.thekey=56GU5ET6TQKZZCKP
crypto.current.strategy=s201903
There was a problem hiding this comment.
I like these ideas. I wrote the class name because it was easiest and wasn't sure how to handle changing encryption strategies.
@ctubbsii do @keith-turner 's ideas match up with your thoughts?
There was a problem hiding this comment.
Yeah, I think using an identifier that isn't (necessarily) the class name is best for this kind of thing.
There was a problem hiding this comment.
An alternative approach is to not have an id in the config. Also do not have the class name in the config. Instead the strategy can provide a versioning string at write time that is persisted in the rfile. At read time this versioning string is provided back to the strategy. This empowers the strategy to handle versioning in whatever what it likes.
There was a problem hiding this comment.
If there is no encryption strategy class in the config or the file then how do you know what class to load?
There was a problem hiding this comment.
That should be determined by an overall crypto module, which is capable of supplying an appropriate decrypter for a given id.
There was a problem hiding this comment.
Oh my bad I meant to write : An alternative approach is to not have an id in the config. Also do not have the class name in the rfile.
| out.writeUTF(es); | ||
|
|
||
| out.writeLong(offsetIndexMeta); | ||
| out.writeLong(offsetCryptoParameter); |
There was a problem hiding this comment.
These variables could use some docs, or better names, or something. It's not clear from this code what these values are, or why they are being written to the file in this location.
| WBlockState wbs = new WBlockState(compressAlgo, out, fsOutputBuffer, conf, cryptoModule, | ||
| cryptoParams); | ||
| WBlockState wbs = new WBlockState(compressAlgo, out, fsOutputBuffer, conf, | ||
| encryptionStrategy); |
There was a problem hiding this comment.
Did you run the formatter? This seems like it will collapse to one line.
| } catch (Exception e) { | ||
| compressAlgo.returnDecompressor(decompressor); | ||
| throw e; | ||
| throw new IOException(e); |
There was a problem hiding this comment.
This isn't necessary if the type was already IOException.
| // Do a version check - API_VERSION_2 used experimental crypto parameters, no longer supported | ||
| if (!version.compatibleWith(BCFile.API_VERSION_3) | ||
| && !version.compatibleWith(BCFile.API_VERSION_1)) { | ||
| throw new RuntimeException("Incompatible BCFile fileBCFileVersion."); |
There was a problem hiding this comment.
This should be a more specific exception type.
There was a problem hiding this comment.
Would be nice to include the version seen in the message. Also if its ver 2 could give a nice message about it not being supported.
There was a problem hiding this comment.
Think throw IOException is appropriate here? I will update the message.
There was a problem hiding this comment.
Do you need it to stay a RuntimeException? If so, there's an UncheckedIOException. Either IOException or a more specific RuntimeException would be fine with me.
| * @return true if initialization was successful | ||
| * @since 2.0 | ||
| */ | ||
| boolean init(Scope encryptionScope, AccumuloConfiguration configuration) throws Exception; |
There was a problem hiding this comment.
These should throw a more narrow exception type.
There was a problem hiding this comment.
Did you have something in mind? I wanted to make the interface flexible enough to handle whatever work needs to be done during init... which could be a lot of different things.
| EncryptionStrategy.class); | ||
| return clazz.newInstance(); | ||
| } catch (Exception e) { | ||
| throw new RuntimeException(e); |
There was a problem hiding this comment.
Multi-catch would work well here. Also, you should probably use the ConfigurationTypeHelper.getClassInstance() for consistent class loading and code reuse (as well as context support... which may not matter right now... but perhaps eventually).
|
|
||
| @Override | ||
| public boolean init(Scope encryptionScope, AccumuloConfiguration conf) { | ||
| return true; |
There was a problem hiding this comment.
What's this boolean for? Should probably succeed (void) or fail (throw), but not succeed with false return code... unless it's used for something else?
There was a problem hiding this comment.
I have code in the factory that returns a NoEncryptionStrategy when init returns false. I guess false is more of a fail quietly and throw exception is fail loudly.
There was a problem hiding this comment.
I don't think we should ever fail quietly when it comes to encryption. 😺
There was a problem hiding this comment.
Haha fair enough. I will make it return void.
…arameter-prints Added a custom exception and crypto info prints
Updated service to make decisions
Add initial gcm to crypto service
Got Findbugs passing - had to refactor AESCryptoService
|
I am going to rebase since there have been a lot of commits between @PircDef and I. |