From 08308819a44bc4da1b143fcbb5098830d38d993a Mon Sep 17 00:00:00 2001 From: Sebastian Stenzel Date: Mon, 7 Feb 2022 16:29:05 +0100 Subject: [PATCH] Avoid `ByteBuffer#asReadOnlyBuffer()` (#29) --- .../cryptolib/v1/FileContentCryptorImpl.java | 35 +++++++------------ .../cryptolib/v1/FileHeaderCryptorImpl.java | 2 +- .../cryptolib/v2/FileContentCryptorImpl.java | 6 ++-- .../cryptolib/v2/FileHeaderCryptorImpl.java | 2 +- 4 files changed, 17 insertions(+), 28 deletions(-) diff --git a/src/main/java/org/cryptomator/cryptolib/v1/FileContentCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v1/FileContentCryptorImpl.java index cad0d0b..986e78f 100644 --- a/src/main/java/org/cryptomator/cryptolib/v1/FileContentCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v1/FileContentCryptorImpl.java @@ -112,12 +112,12 @@ void encryptChunk(ByteBuffer cleartextChunk, ByteBuffer ciphertextChunk, long ch final Cipher cipher = CipherSupplier.AES_CTR.forEncryption(fk, new IvParameterSpec(nonce)); ciphertextChunk.put(nonce); assert ciphertextChunk.remaining() >= cipher.getOutputSize(cleartextChunk.remaining()) + MAC_SIZE; - int bytesEncrypted = cipher.doFinal(cleartextChunk, ciphertextChunk); + cipher.doFinal(cleartextChunk, ciphertextChunk); // mac: - final ByteBuffer ciphertextBuf = ciphertextChunk.asReadOnlyBuffer(); - ciphertextBuf.position(NONCE_SIZE).limit(NONCE_SIZE + bytesEncrypted); - byte[] authenticationCode = calcChunkMac(headerNonce, chunkNumber, nonce, ciphertextBuf); + ByteBuffer nonceAndPayload = ciphertextChunk.duplicate(); + nonceAndPayload.flip(); + byte[] authenticationCode = calcChunkMac(headerNonce, chunkNumber, nonceAndPayload); assert authenticationCode.length == MAC_SIZE; ciphertextChunk.put(authenticationCode); } catch (ShortBufferException e) { @@ -134,12 +134,10 @@ void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, Destroy try (DestroyableSecretKey fk = fileKey.copy()) { // nonce: final byte[] nonce = new byte[NONCE_SIZE]; - final ByteBuffer chunkNonceBuf = ciphertextChunk.asReadOnlyBuffer(); - chunkNonceBuf.position(0).limit(NONCE_SIZE); - chunkNonceBuf.get(nonce); + ciphertextChunk.get(nonce, 0, NONCE_SIZE); // payload: - final ByteBuffer payloadBuf = ciphertextChunk.asReadOnlyBuffer(); + final ByteBuffer payloadBuf = ciphertextChunk.duplicate(); payloadBuf.position(NONCE_SIZE).limit(ciphertextChunk.limit() - MAC_SIZE); // payload: @@ -157,37 +155,30 @@ void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, Destroy boolean checkChunkMac(byte[] headerNonce, long chunkNumber, ByteBuffer chunkBuf) { assert chunkBuf.remaining() >= NONCE_SIZE + MAC_SIZE; - // get three components: nonce + payload + mac - final ByteBuffer chunkNonceBuf = chunkBuf.asReadOnlyBuffer(); - chunkNonceBuf.position(0).limit(NONCE_SIZE); - final ByteBuffer payloadBuf = chunkBuf.asReadOnlyBuffer(); - payloadBuf.position(NONCE_SIZE).limit(chunkBuf.limit() - MAC_SIZE); - final ByteBuffer expectedMacBuf = chunkBuf.asReadOnlyBuffer(); + // get nonce + payload + final ByteBuffer nonceAndPayload = chunkBuf.duplicate(); + nonceAndPayload.position(0).limit(chunkBuf.limit() - MAC_SIZE); + final ByteBuffer expectedMacBuf = chunkBuf.duplicate(); expectedMacBuf.position(chunkBuf.limit() - MAC_SIZE); - // get nonce: - final byte[] chunkNonce = new byte[NONCE_SIZE]; - chunkNonceBuf.get(chunkNonce); - // get expected MAC: final byte[] expectedMac = new byte[MAC_SIZE]; expectedMacBuf.get(expectedMac); // get actual MAC: - final byte[] calculatedMac = calcChunkMac(headerNonce, chunkNumber, chunkNonce, payloadBuf); + final byte[] calculatedMac = calcChunkMac(headerNonce, chunkNumber, nonceAndPayload); // time-constant equality check of two MACs: return MessageDigest.isEqual(expectedMac, calculatedMac); } - private byte[] calcChunkMac(byte[] headerNonce, long chunkNumber, byte[] chunkNonce, ByteBuffer ciphertext) { + private byte[] calcChunkMac(byte[] headerNonce, long chunkNumber, ByteBuffer nonceAndCiphertext) { try (DestroyableSecretKey mk = masterkey.getMacKey()) { final byte[] chunkNumberBigEndian = ByteBuffer.allocate(Long.SIZE / Byte.SIZE).order(ByteOrder.BIG_ENDIAN).putLong(chunkNumber).array(); final Mac mac = MacSupplier.HMAC_SHA256.withKey(mk); mac.update(headerNonce); mac.update(chunkNumberBigEndian); - mac.update(chunkNonce); - mac.update(ciphertext); + mac.update(nonceAndCiphertext); return mac.doFinal(); } } diff --git a/src/main/java/org/cryptomator/cryptolib/v1/FileHeaderCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v1/FileHeaderCryptorImpl.java index aeaa7ee..46e8d4f 100644 --- a/src/main/java/org/cryptomator/cryptolib/v1/FileHeaderCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v1/FileHeaderCryptorImpl.java @@ -88,7 +88,7 @@ public FileHeader decryptHeader(ByteBuffer ciphertextHeaderBuf) throws Authentic if (ciphertextHeaderBuf.remaining() < FileHeaderImpl.SIZE) { throw new IllegalArgumentException("Malformed ciphertext header"); } - ByteBuffer buf = ciphertextHeaderBuf.asReadOnlyBuffer(); + ByteBuffer buf = ciphertextHeaderBuf.duplicate(); byte[] nonce = new byte[FileHeaderImpl.NONCE_LEN]; buf.position(FileHeaderImpl.NONCE_POS); buf.get(nonce); diff --git a/src/main/java/org/cryptomator/cryptolib/v2/FileContentCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v2/FileContentCryptorImpl.java index a03eea9..e59beff 100644 --- a/src/main/java/org/cryptomator/cryptolib/v2/FileContentCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v2/FileContentCryptorImpl.java @@ -124,12 +124,10 @@ void decryptChunk(ByteBuffer ciphertextChunk, ByteBuffer cleartextChunk, long ch try (DestroyableSecretKey fk = fileKey.copy()) { // nonce: final byte[] nonce = new byte[GCM_NONCE_SIZE]; - final ByteBuffer chunkNonceBuf = ciphertextChunk.asReadOnlyBuffer(); - chunkNonceBuf.position(0).limit(GCM_NONCE_SIZE); - chunkNonceBuf.get(nonce); + ciphertextChunk.get(nonce, 0, GCM_NONCE_SIZE); // payload: - final ByteBuffer payloadBuf = ciphertextChunk.asReadOnlyBuffer(); + final ByteBuffer payloadBuf = ciphertextChunk.duplicate(); payloadBuf.position(GCM_NONCE_SIZE); assert payloadBuf.remaining() >= GCM_TAG_SIZE; diff --git a/src/main/java/org/cryptomator/cryptolib/v2/FileHeaderCryptorImpl.java b/src/main/java/org/cryptomator/cryptolib/v2/FileHeaderCryptorImpl.java index 509a5b9..2182e7b 100644 --- a/src/main/java/org/cryptomator/cryptolib/v2/FileHeaderCryptorImpl.java +++ b/src/main/java/org/cryptomator/cryptolib/v2/FileHeaderCryptorImpl.java @@ -81,7 +81,7 @@ public FileHeader decryptHeader(ByteBuffer ciphertextHeaderBuf) throws Authentic if (ciphertextHeaderBuf.remaining() < FileHeaderImpl.SIZE) { throw new IllegalArgumentException("Malformed ciphertext header"); } - ByteBuffer buf = ciphertextHeaderBuf.asReadOnlyBuffer(); + ByteBuffer buf = ciphertextHeaderBuf.duplicate(); byte[] nonce = new byte[FileHeaderImpl.NONCE_LEN]; buf.position(FileHeaderImpl.NONCE_POS); buf.get(nonce);