mirror of
https://github.com/cryptomator/cryptomator.git
synced 2026-05-18 02:31:27 +00:00
chunk layout version 3 (random nonce per block)
This commit is contained in:
@@ -20,7 +20,8 @@ import org.cryptomator.crypto.engine.FileContentEncryptor;
|
||||
|
||||
public class FileContentCryptorImpl implements FileContentCryptor {
|
||||
|
||||
public static final int CHUNK_SIZE = 32 * 1024;
|
||||
public static final int PAYLOAD_SIZE = 32 * 1024;
|
||||
public static final int NONCE_SIZE = 16;
|
||||
public static final int MAC_SIZE = 32;
|
||||
|
||||
private final SecretKey encryptionKey;
|
||||
@@ -40,12 +41,12 @@ public class FileContentCryptorImpl implements FileContentCryptor {
|
||||
|
||||
@Override
|
||||
public long toCiphertextPos(long cleartextPos) {
|
||||
long chunkNum = cleartextPos / CHUNK_SIZE;
|
||||
long cleartextChunkStart = chunkNum * CHUNK_SIZE;
|
||||
long chunkNum = cleartextPos / PAYLOAD_SIZE;
|
||||
long cleartextChunkStart = chunkNum * PAYLOAD_SIZE;
|
||||
assert cleartextChunkStart <= cleartextPos;
|
||||
long chunkInternalDiff = cleartextPos - cleartextChunkStart;
|
||||
assert chunkInternalDiff >= 0 && chunkInternalDiff < CHUNK_SIZE;
|
||||
long ciphertextChunkStart = chunkNum * (CHUNK_SIZE + MAC_SIZE);
|
||||
assert chunkInternalDiff >= 0 && chunkInternalDiff < PAYLOAD_SIZE;
|
||||
long ciphertextChunkStart = chunkNum * (NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE);
|
||||
return ciphertextChunkStart + chunkInternalDiff;
|
||||
}
|
||||
|
||||
@@ -54,7 +55,7 @@ public class FileContentCryptorImpl implements FileContentCryptor {
|
||||
if (header.remaining() != getHeaderSize()) {
|
||||
throw new IllegalArgumentException("Invalid header.");
|
||||
}
|
||||
if (firstCiphertextByte % (CHUNK_SIZE + MAC_SIZE) != 0) {
|
||||
if (firstCiphertextByte % (NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE) != 0) {
|
||||
throw new IllegalArgumentException("Invalid starting point for decryption.");
|
||||
}
|
||||
return new FileContentDecryptorImpl(encryptionKey, macKey, header, firstCiphertextByte, authenticate);
|
||||
@@ -65,7 +66,7 @@ public class FileContentCryptorImpl implements FileContentCryptor {
|
||||
if (header.isPresent() && header.get().remaining() != getHeaderSize()) {
|
||||
throw new IllegalArgumentException("Invalid header.");
|
||||
}
|
||||
if (firstCleartextByte % CHUNK_SIZE != 0) {
|
||||
if (firstCleartextByte % PAYLOAD_SIZE != 0) {
|
||||
throw new IllegalArgumentException("Invalid starting point for encryption.");
|
||||
}
|
||||
return new FileContentEncryptorImpl(encryptionKey, macKey, randomSource, firstCleartextByte);
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE;
|
||||
import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.MAC_SIZE;
|
||||
import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.PAYLOAD_SIZE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
@@ -33,7 +33,7 @@ import org.cryptomator.io.ByteBuffers;
|
||||
|
||||
class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
|
||||
private static final int AES_BLOCK_LENGTH_IN_BYTES = 16;
|
||||
private static final int NONCE_SIZE = 16;
|
||||
private static final String HMAC_SHA256 = "HmacSHA256";
|
||||
private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors();
|
||||
private static final int READ_AHEAD = 2;
|
||||
@@ -42,7 +42,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
private final ThreadLocal<Mac> hmacSha256;
|
||||
private final FileHeader header;
|
||||
private final boolean authenticate;
|
||||
private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE);
|
||||
private ByteBuffer ciphertextBuffer = ByteBuffer.allocate(NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE);
|
||||
private long chunkNumber = 0;
|
||||
|
||||
public FileContentDecryptorImpl(SecretKey headerKey, SecretKey macKey, ByteBuffer header, long firstCiphertextByte, boolean authenticate) {
|
||||
@@ -50,7 +50,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
this.hmacSha256 = hmacSha256;
|
||||
this.header = FileHeader.decrypt(headerKey, hmacSha256, header);
|
||||
this.authenticate = authenticate;
|
||||
this.chunkNumber = firstCiphertextByte / CHUNK_SIZE; // floor() by int-truncation
|
||||
this.chunkNumber = firstCiphertextByte / PAYLOAD_SIZE; // floor() by int-truncation
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -81,7 +81,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
private void submitCiphertextBufferIfFull() throws InterruptedException {
|
||||
if (!ciphertextBuffer.hasRemaining()) {
|
||||
submitCiphertextBuffer();
|
||||
ciphertextBuffer = ByteBuffer.allocate(CHUNK_SIZE + MAC_SIZE);
|
||||
ciphertextBuffer = ByteBuffer.allocate(NONCE_SIZE + PAYLOAD_SIZE + MAC_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -119,25 +119,24 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
|
||||
private class DecryptionJob implements Callable<ByteBuffer> {
|
||||
|
||||
private final byte[] nonce;
|
||||
private final ByteBuffer ciphertextChunk;
|
||||
private final byte[] expectedMac;
|
||||
private final byte[] nonceAndCtr;
|
||||
|
||||
public DecryptionJob(ByteBuffer ciphertextChunk, long chunkNumber) {
|
||||
if (ciphertextChunk.remaining() < MAC_SIZE) {
|
||||
throw new IllegalArgumentException("Chunk must end with a MAC");
|
||||
if (ciphertextChunk.remaining() < NONCE_SIZE + MAC_SIZE) {
|
||||
throw new IllegalArgumentException("Chunk must at least contain a NONCE and a MAC");
|
||||
}
|
||||
this.nonce = new byte[NONCE_SIZE];
|
||||
ByteBuffer nonceBuf = ciphertextChunk.asReadOnlyBuffer();
|
||||
nonceBuf.position(0).limit(NONCE_SIZE);
|
||||
nonceBuf.get(nonce);
|
||||
this.ciphertextChunk = ciphertextChunk.asReadOnlyBuffer();
|
||||
this.ciphertextChunk.position(0).limit(ciphertextChunk.limit() - MAC_SIZE);
|
||||
this.ciphertextChunk.position(NONCE_SIZE).limit(ciphertextChunk.limit() - MAC_SIZE);
|
||||
this.expectedMac = new byte[MAC_SIZE];
|
||||
ByteBuffer macBuf = ciphertextChunk.asReadOnlyBuffer();
|
||||
macBuf.position(macBuf.limit() - MAC_SIZE);
|
||||
macBuf.get(expectedMac);
|
||||
|
||||
final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH_IN_BYTES);
|
||||
nonceAndCounterBuf.put(header.getNonce());
|
||||
nonceAndCounterBuf.putLong(chunkNumber * CHUNK_SIZE / AES_BLOCK_LENGTH_IN_BYTES);
|
||||
this.nonceAndCtr = nonceAndCounterBuf.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -145,6 +144,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
try {
|
||||
if (authenticate) {
|
||||
Mac mac = hmacSha256.get();
|
||||
mac.update(nonce);
|
||||
mac.update(ciphertextChunk.asReadOnlyBuffer());
|
||||
if (!MessageDigest.isEqual(expectedMac, mac.doFinal())) {
|
||||
throw new AuthenticationFailedException();
|
||||
@@ -152,7 +152,7 @@ class FileContentDecryptorImpl implements FileContentDecryptor {
|
||||
}
|
||||
|
||||
Cipher cipher = ThreadLocalAesCtrCipher.get();
|
||||
cipher.init(Cipher.DECRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonceAndCtr));
|
||||
cipher.init(Cipher.DECRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonce));
|
||||
ByteBuffer cleartextChunk = ByteBuffer.allocate(cipher.getOutputSize(ciphertextChunk.remaining()));
|
||||
cipher.update(ciphertextChunk, cleartextChunk);
|
||||
cleartextChunk.flip();
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*******************************************************************************/
|
||||
package org.cryptomator.crypto.engine.impl;
|
||||
|
||||
import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.CHUNK_SIZE;
|
||||
import static org.cryptomator.crypto.engine.impl.FileContentCryptorImpl.PAYLOAD_SIZE;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.io.UncheckedIOException;
|
||||
@@ -32,17 +32,18 @@ import org.cryptomator.io.ByteBuffers;
|
||||
|
||||
class FileContentEncryptorImpl implements FileContentEncryptor {
|
||||
|
||||
private static final int AES_BLOCK_LENGTH_IN_BYTES = 16;
|
||||
private static final int NONCE_SIZE = 16;
|
||||
private static final String HMAC_SHA256 = "HmacSHA256";
|
||||
private static final int NUM_THREADS = Runtime.getRuntime().availableProcessors();
|
||||
private static final int READ_AHEAD = 2;
|
||||
|
||||
private final FifoParallelDataProcessor<ByteBuffer> dataProcessor = new FifoParallelDataProcessor<>(NUM_THREADS, NUM_THREADS + READ_AHEAD);
|
||||
private final ThreadLocalMac hmacSha256;
|
||||
private final FileHeader header;
|
||||
private final SecretKey headerKey;
|
||||
private final FileHeader header;
|
||||
private final SecureRandom randomSource;
|
||||
private final LongAdder cleartextBytesEncrypted = new LongAdder();
|
||||
private ByteBuffer cleartextBuffer = ByteBuffer.allocate(CHUNK_SIZE);
|
||||
private ByteBuffer cleartextBuffer = ByteBuffer.allocate(PAYLOAD_SIZE);
|
||||
private long chunkNumber = 0;
|
||||
|
||||
public FileContentEncryptorImpl(SecretKey headerKey, SecretKey macKey, SecureRandom randomSource, long firstCleartextByte) {
|
||||
@@ -52,6 +53,7 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
|
||||
this.hmacSha256 = new ThreadLocalMac(macKey, HMAC_SHA256);
|
||||
this.headerKey = headerKey;
|
||||
this.header = new FileHeader(randomSource);
|
||||
this.randomSource = randomSource;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -89,7 +91,7 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
|
||||
private void submitCleartextBufferIfFull() throws InterruptedException {
|
||||
if (!cleartextBuffer.hasRemaining()) {
|
||||
submitCleartextBuffer();
|
||||
cleartextBuffer = ByteBuffer.allocate(CHUNK_SIZE);
|
||||
cleartextBuffer = ByteBuffer.allocate(PAYLOAD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -126,30 +128,36 @@ class FileContentEncryptorImpl implements FileContentEncryptor {
|
||||
private class EncryptionJob implements Callable<ByteBuffer> {
|
||||
|
||||
private final ByteBuffer cleartextChunk;
|
||||
private final byte[] nonceAndCtr;
|
||||
|
||||
public EncryptionJob(ByteBuffer cleartextChunk, long chunkNumber) {
|
||||
this.cleartextChunk = cleartextChunk;
|
||||
|
||||
final ByteBuffer nonceAndCounterBuf = ByteBuffer.allocate(AES_BLOCK_LENGTH_IN_BYTES);
|
||||
nonceAndCounterBuf.put(header.getNonce());
|
||||
nonceAndCounterBuf.putLong(chunkNumber * CHUNK_SIZE / AES_BLOCK_LENGTH_IN_BYTES);
|
||||
this.nonceAndCtr = nonceAndCounterBuf.array();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ByteBuffer call() {
|
||||
try {
|
||||
Cipher cipher = ThreadLocalAesCtrCipher.get();
|
||||
cipher.init(Cipher.ENCRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonceAndCtr));
|
||||
Mac mac = hmacSha256.get();
|
||||
ByteBuffer ciphertextChunk = ByteBuffer.allocate(cipher.getOutputSize(cleartextChunk.remaining()) + mac.getMacLength());
|
||||
final Cipher cipher = ThreadLocalAesCtrCipher.get();
|
||||
final Mac mac = hmacSha256.get();
|
||||
final ByteBuffer ciphertextChunk = ByteBuffer.allocate(NONCE_SIZE + cleartextChunk.remaining() + mac.getMacLength());
|
||||
|
||||
// nonce
|
||||
byte[] nonce = new byte[NONCE_SIZE];
|
||||
randomSource.nextBytes(nonce);
|
||||
ciphertextChunk.put(nonce);
|
||||
|
||||
// payload:
|
||||
cipher.init(Cipher.ENCRYPT_MODE, header.getPayload().getContentKey(), new IvParameterSpec(nonce));
|
||||
assert cipher.getOutputSize(cleartextChunk.remaining()) == cleartextChunk.remaining() : "input length should be equal to output length in CTR mode.";
|
||||
cipher.update(cleartextChunk, ciphertextChunk);
|
||||
|
||||
// mac:
|
||||
ByteBuffer ciphertextSoFar = ciphertextChunk.asReadOnlyBuffer();
|
||||
ciphertextSoFar.flip();
|
||||
mac.update(ciphertextSoFar);
|
||||
byte[] authenticationCode = mac.doFinal();
|
||||
ciphertextChunk.put(authenticationCode);
|
||||
|
||||
// flip and return:
|
||||
ciphertextChunk.flip();
|
||||
return ciphertextChunk;
|
||||
} catch (InvalidKeyException e) {
|
||||
|
||||
@@ -23,6 +23,6 @@ public class BlockAlignedFileSystemFactory {
|
||||
}
|
||||
|
||||
public FileSystem get(Folder root) {
|
||||
return new BlockAlignedFileSystem(root, FileContentCryptorImpl.CHUNK_SIZE);
|
||||
return new BlockAlignedFileSystem(root, FileContentCryptorImpl.PAYLOAD_SIZE);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -45,12 +45,12 @@ public class FileContentDecryptorImplTest {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
|
||||
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHQ==");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA==");
|
||||
final byte[] content = Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U=");
|
||||
|
||||
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59)));
|
||||
decryptor.append(FileContentCryptor.EOF);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext.
|
||||
@@ -68,12 +68,12 @@ public class FileContentDecryptorImplTest {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
|
||||
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq==");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA==");
|
||||
final byte[] content = Base64.decode("aAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U=");
|
||||
|
||||
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59)));
|
||||
decryptor.append(FileContentCryptor.EOF);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext.
|
||||
@@ -89,12 +89,12 @@ public class FileContentDecryptorImplTest {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "HmacSHA256");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
|
||||
final byte[] content = Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHq==");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA==");
|
||||
final byte[] content = Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8u=");
|
||||
|
||||
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, false)) {
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 0, 15)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 43)));
|
||||
decryptor.append(ByteBuffer.wrap(Arrays.copyOfRange(content, 15, 59)));
|
||||
decryptor.append(FileContentCryptor.EOF);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(11); // we just care about the first 11 bytes, as this is the ciphertext.
|
||||
@@ -112,7 +112,7 @@ public class FileContentDecryptorImplTest {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey headerKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final SecretKey macKey = new SecretKeySpec(keyBytes, "AES");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAwN74OFIGKQKgsI7bakfCYm1VXJZiKFLyhZkQCz0Ye/il0PmdZOYsSYEH9h6S00RsdHL3wLtB1FJsb9QLTtP00H8M2theZaZdlKTmjhXsmbc=");
|
||||
final byte[] header = Base64.decode("AAAAAAAAAAAAAAAAAAAAANyVwHiiQImjrUiiFJKEIIdTD4r7x0U2ualjtPHEy3OLzqdAPU1ga26lJzstK9RUv1hj5zDC4wC9FgMfoVE1mD0HnuENuYXkJA==");
|
||||
|
||||
try (FileContentDecryptor decryptor = new FileContentDecryptorImpl(headerKey, macKey, ByteBuffer.wrap(header), 0, true)) {
|
||||
decryptor.cancelWithException(new IOException("can not do"));
|
||||
@@ -120,7 +120,7 @@ public class FileContentDecryptorImplTest {
|
||||
}
|
||||
}
|
||||
|
||||
@Test(timeout = 2000)
|
||||
@Test(timeout = 200000)
|
||||
public void testPartialDecryption() throws InterruptedException {
|
||||
final byte[] keyBytes = new byte[32];
|
||||
final SecretKey encryptionKey = new SecretKeySpec(keyBytes, "AES");
|
||||
@@ -128,7 +128,7 @@ public class FileContentDecryptorImplTest {
|
||||
FileContentCryptor cryptor = new FileContentCryptorImpl(encryptionKey, macKey, RANDOM_MOCK);
|
||||
|
||||
ByteBuffer header = ByteBuffer.allocate(cryptor.getHeaderSize());
|
||||
ByteBuffer ciphertext = ByteBuffer.allocate(131200); // 4 * (32k + 32)
|
||||
ByteBuffer ciphertext = ByteBuffer.allocate(131264); // 4 * (16 + 32k + 32)
|
||||
try (FileContentEncryptor encryptor = cryptor.createFileContentEncryptor(Optional.empty(), 0)) {
|
||||
final Thread ciphertextWriter = new Thread(() -> {
|
||||
ByteBuffer buf;
|
||||
|
||||
@@ -48,16 +48,17 @@ public class FileContentEncryptorImplTest {
|
||||
encryptor.append(ByteBuffer.wrap("world".getBytes()));
|
||||
encryptor.append(FileContentCryptor.EOF);
|
||||
|
||||
ByteBuffer result = ByteBuffer.allocate(43); // 11 bytes ciphertext + 32 bytes mac.
|
||||
ByteBuffer result = ByteBuffer.allocate(59); // 16 bytes iv + 11 bytes ciphertext + 32 bytes mac.
|
||||
ByteBuffer buf;
|
||||
while ((buf = encryptor.ciphertext()) != FileContentCryptor.EOF) {
|
||||
ByteBuffers.copy(buf, result);
|
||||
}
|
||||
|
||||
// Ciphertext: echo -n "hello world" | openssl enc -aes-256-ctr -K 0000000000000000000000000000000000000000000000000000000000000000 -iv 00000000000000000000000000000000 | base64
|
||||
// MAC: echo -n "tPCsFM1g/ubfJMY=" | base64 --decode | openssl dgst -sha256 -mac HMAC -macopt hexkey:0000000000000000000000000000000000000000000000000000000000000000 -binary | base64
|
||||
// echo -n "tPCsFM1g/ubfJMY=" | base64 --decode > A; echo -n "vgKHHT4f1jx31zBUSXSM+j5C7kYo0iCF78Z+yFMFcx0=" | base64 --decode >> A; cat A | base64
|
||||
Assert.assertArrayEquals(Base64.decode("tPCsFM1g/ubfJMa+AocdPh/WPHfXMFRJdIz6PkLuRijSIIXvxn7IUwVzHQ=="), result.array());
|
||||
// MAC: echo -n "AAAAAAAAAAAAAAAAAAAAAA==" | base64 --decode > A; echo -n "tPCsFM1g/ubfJMY=" | base64 --decode >> A; cat A | openssl dgst -sha256 -mac HMAC -macopt
|
||||
// hexkey:0000000000000000000000000000000000000000000000000000000000000000 -binary | base64
|
||||
// echo -n "+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U=" | base64 --decode >> A; cat A | base64
|
||||
Assert.assertArrayEquals(Base64.decode("AAAAAAAAAAAAAAAAAAAAALTwrBTNYP7m3yTG+8Yv6jcvXJj89WiHAxAtgbZR7mpsskLFfGCVDm6NO8U="), result.array());
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user