Skip to content

Implement new Encryption interface WIP#499

Closed
milleruntime wants to merge 27 commits intoapache:masterfrom
milleruntime:crypto
Closed

Implement new Encryption interface WIP#499
milleruntime wants to merge 27 commits intoapache:masterfrom
milleruntime:crypto

Conversation

@milleruntime
Copy link
Copy Markdown
Contributor

  • 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;
Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I kept this because LogSorter uses both input and decryptingInput... although I am not sure why

Copy link
Copy Markdown
Member

@ctubbsii ctubbsii left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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) {
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is it possible to avoid overly broad catching of Exception here?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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

Copy link
Copy Markdown
Contributor Author

@milleruntime milleruntime Jun 11, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yeah, I think using an identifier that isn't (necessarily) the class name is best for this kind of thing.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

If there is no encryption strategy class in the config or the file then how do you know what class to load?

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

That should be determined by an overall crypto module, which is capable of supplying an appropriate decrypter for a given id.

Copy link
Copy Markdown
Contributor

@keith-turner keith-turner Jun 13, 2018

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.");
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be a more specific exception type.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Think throw IOException is appropriate here? I will update the message.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

These should throw a more narrow exception type.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🤷‍♂️

EncryptionStrategy.class);
return clazz.newInstance();
} catch (Exception e) {
throw new RuntimeException(e);
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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;
Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

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.

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I don't think we should ever fail quietly when it comes to encryption. 😺

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Haha fair enough. I will make it return void.

@milleruntime milleruntime changed the title Implement new Encryption interface Implement new Encryption interface WIP Jun 19, 2018
@milleruntime
Copy link
Copy Markdown
Contributor Author

I am going to rebase since there have been a lot of commits between @PircDef and I.

@milleruntime milleruntime deleted the crypto branch August 13, 2018 20:03
@ctubbsii ctubbsii added this to the 2.0.0 milestone Jul 12, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

4 participants