-
Notifications
You must be signed in to change notification settings - Fork 171
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
SNOW-1858529 Implement FLOE #2006
base: master
Are you sure you want to change the base?
Conversation
dd4ff7d
to
52d061f
Compare
52d061f
to
68b11d2
Compare
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Just some quick early feedback so it's easy to incorporate if needed.
As a general note, I suspect this implementation will be less performant than the reference implementation due to extra byte copies and such. We'll want to measure and profile it to see how much we care.
@@ -41,8 +41,7 @@ public class EncryptionProvider { | |||
private static final String FILE_CIPHER = "AES/CBC/PKCS5Padding"; | |||
private static final String KEY_CIPHER = "AES/ECB/PKCS5Padding"; | |||
private static final int BUFFER_SIZE = 2 * 1024 * 1024; // 2 MB | |||
private static ThreadLocal<SecureRandom> secRnd = | |||
new ThreadLocal<>().withInitial(SecureRandom::new); | |||
private static ThreadLocal<SecureRandom> secRnd = ThreadLocal.withInitial(SecureRandom::new); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I'd make this final. No reason not to. Also, as it is now static final
it should probably also BE_IN_CAPS.
import net.snowflake.client.jdbc.MatDesc; | ||
import net.snowflake.common.core.RemoteStoreFileEncryptionMaterial; | ||
|
||
class GcmEncryptionProvider { | ||
@SnowflakeJdbcInternalApi | ||
public class GcmEncryptionProvider { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Is this ever being shipped? I thought we designed FLOE because we determined GCM wouldn't work?
@@ -99,7 +99,7 @@ private static CipherInputStream encryptContent( | |||
NoSuchAlgorithmException { | |||
SecretKey fileKey = new SecretKeySpec(keyBytes, 0, keyBytes.length, AES); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NitPick
SecretKey fileKey = new SecretKeySpec(keyBytes, 0, keyBytes.length, AES); | |
SecretKey fileKey = new SecretKeySpec(keyBytes, AES); |
Cipher fileCipher = Cipher.getInstance(FILE_CIPHER); | ||
Cipher fileCipher = Cipher.getInstance(JCE_CIPHER_NAME); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Why doesn't this method use decryptContentFromStream()
?
Also, decrypting the file in place seems risky. It would be far safer to decrypt to a temporary file and then rename the temporary file over the input file only upon completion/success.
private final byte[] aad; | ||
|
||
FloeAad(byte[] aad) { | ||
this.aad = Optional.ofNullable(aad).orElse(new byte[0]); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NitPick: It's often better to have a hidden constant EMPTY_ARRAY = new byte[0]
because otherwise creating and managing these empty arrays is unneeded GC pressure.
byte[] headerTag = | ||
keyDerivator.hkdfExpand( | ||
this.floeKey, floeIv, this.floeAad, HeaderTagFloePurpose.INSTANCE, headerTagLength); | ||
if (!Arrays.equals(headerTag, headerTagFromHeader)) { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This is insecure. You must use a constant-time method for this comparison. This is documented in the primary FLOE document and pseudo-code:
assert ExpectedHeaderTag == HeaderTag // Must be constant time
Please use MessageDigest.isEqual()
instead.
verifySegmentSizeMarker(inputBuf); | ||
AeadKey aeadKey = getKey(floeKey, floeIv, floeAad, segmentCounter); | ||
AeadIv aeadIv = AeadIv.from(inputBuf, parameterSpec.getAead().getIvLength()); | ||
AeadAad aeadAad = AeadAad.nonTerminal(segmentCounter++); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
NitPick: The psuedo-code only increments the counter after successful decryption while yours increments it before. This does mean that the reference implementation and the current JDBC implementation will act differently after a failed decryption if decryption attempts continue.
The reference implementation acts as if the failed decryption were never attempted (because state doesn't change). Yours increments the counter and thus cannot return decryption of the same segment.
I note this as a nitpick because it isn't actually a problem, but it is a deviation from the provided algorithm description.
AeadIv aeadIv = | ||
AeadIv.generateRandom( | ||
parameterSpec.getFloeRandom(), parameterSpec.getAead().getIvLength()); | ||
AeadAad aeadAad = AeadAad.nonTerminal(segmentCounter++); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Same nitpick as above related to where the counter is incremented.
Overview
SNOW-XXXXX
Pre-review self checklist
master
branchmvn -P check-style validate
)mvn verify
and inspecttarget/japicmp/japicmp.html
)SNOW-XXXX:
External contributors - please answer these questions before submitting a pull request. Thanks!
What GitHub issue is this PR addressing? Make sure that there is an accompanying issue to your PR.
Issue: #NNNN
Fill out the following pre-review checklist:
@SnowflakeJdbcInternalApi
(note that public/protected methods/fields in classes marked with this annotation are already internal)Please describe how your code solves the related issue.
Please write a short description of how your code change solves the related issue.