diff --git a/fastods-crypto/pom.xml b/fastods-crypto/pom.xml
index 87a9c28d..32caeec5 100644
--- a/fastods-crypto/pom.xml
+++ b/fastods-crypto/pom.xml
@@ -74,6 +74,17 @@
easymock
test
+
+ com.github.jferard
+ fastods-testlib
+ test
+
+
+ com.github.jferard
+ fastods-testlib
+ 0.8.2-SNAPSHOT
+ test
+
diff --git a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ProtectionFactory.java b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ProtectionFactory.java
index e87cc0b2..fa03af15 100644
--- a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ProtectionFactory.java
+++ b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ProtectionFactory.java
@@ -36,6 +36,9 @@
* 19.698: "Producers should use http://www.w3.org/2000/09/xmldsig#sha256."
*/
public class ProtectionFactory {
+ /** Do not instantiate */
+ private ProtectionFactory() {}
+
/**
* Create a new protection. **Beware: for security reasons, this fills the password array
* with 0's**
diff --git a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/Util.java b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/Util.java
index aa9edc07..941a0a36 100644
--- a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/Util.java
+++ b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/Util.java
@@ -24,7 +24,6 @@
package com.github.jferard.fastods.crypto;
-import com.github.jferard.fastods.util.CharsetUtil;
import org.bouncycastle.util.encoders.Base64;
import java.nio.ByteBuffer;
@@ -35,6 +34,9 @@
import java.util.Arrays;
public class Util {
+ /** Do not instantiate */
+ private Util() {}
+
/**
* Convert a char array to a byte array containing the SHA-256 digest. **Beware: for security
* reasons, this fills the password array with 0's**.
diff --git a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriter.java b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriter.java
index d622290c..8dda9a26 100644
--- a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriter.java
+++ b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriter.java
@@ -26,7 +26,6 @@
import com.github.jferard.fastods.annotation.Beta;
import com.github.jferard.fastods.odselement.OdsEntry;
-import com.github.jferard.fastods.util.CharsetUtil;
import com.github.jferard.fastods.util.ZipUTF8Writer;
import com.github.jferard.fastods.util.ZipUTF8WriterBuilder;
import org.bouncycastle.util.encoders.Base64;
@@ -51,6 +50,7 @@
public class ZipUTF8CryptoWriter implements ZipUTF8Writer {
/**
* **Beware: for security reasons, this fills the password array with 0's**
+ *
* @param password the password to encrypt data
* @return a builder
* @throws NoSuchAlgorithmException won't happen since SHA-256 is pretty common
@@ -62,11 +62,11 @@ public static ZipUTF8WriterBuilder builder(final char[] password)
private final ZipUTF8Writer zipUTF8Writer;
private final StandardEncrypter encrypter;
+ private final byte[] hashedPassword;
private ByteArrayOutputStream out;
private Writer writer;
private OdsEntry curEntry;
private boolean toRegister;
- private final byte[] hashedPassword;
public ZipUTF8CryptoWriter(final ZipUTF8Writer zipUTF8Writer,
final StandardEncrypter encrypter, final byte[] hashedPassword) {
@@ -81,7 +81,7 @@ public void setComment(final String comment) {
}
@Override
- public void putAndRegisterNextEntry(final OdsEntry entry) throws IOException {
+ public void putAndRegisterNextEntry(final OdsEntry entry) {
this.toRegister = true;
this.putNextEntry(entry);
}
@@ -124,17 +124,9 @@ private EntryAndData getEncryptedEntryAndData(final byte[] plainTextBytes) throw
final EntryAndData entryAndData;
try {
entryAndData = this.getEncryptedEntryAndDataUnchecked(plainTextBytes);
- } catch (final NoSuchAlgorithmException e) {
- throw new IOException("Can't encrypt file", e);
- } catch (final InvalidKeyException e) {
- throw new IOException("Can't encrypt file", e);
- } catch (final InvalidAlgorithmParameterException e) {
- throw new IOException("Can't encrypt file", e);
- } catch (final NoSuchPaddingException e) {
- throw new IOException("Can't encrypt file", e);
- } catch (final BadPaddingException e) {
- throw new IOException("Can't encrypt file", e);
- } catch (final IllegalBlockSizeException e) {
+ } catch (final NoSuchAlgorithmException | InvalidKeyException
+ | InvalidAlgorithmParameterException | NoSuchPaddingException
+ | BadPaddingException | IllegalBlockSizeException e) {
throw new IOException("Can't encrypt file", e);
}
return entryAndData;
@@ -154,7 +146,8 @@ private EntryAndData getEncryptedEntryAndDataUnchecked(final byte[] plainTextByt
final long crc32 = this.getCrc32(compressedThenEncryptedData);
final OdsEntry entry = this.curEntry.encryptParameters(
this.encrypter.buildParameters(
- plainTextBytes.length, compressedThenEncryptedData.length, crc32, compressedCheckSum,
+ plainTextBytes.length, compressedThenEncryptedData.length, crc32,
+ compressedCheckSum,
Base64.toBase64String(salt), Base64.toBase64String(iv)));
return new EntryAndData(entry, compressedThenEncryptedData);
}
diff --git a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterBuilder.java b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterBuilder.java
index 47ec8b22..d26fe951 100644
--- a/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterBuilder.java
+++ b/fastods-crypto/src/main/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterBuilder.java
@@ -41,6 +41,9 @@
* A builder for `ZipUTF8CryptoWriter`.
*/
public class ZipUTF8CryptoWriterBuilder implements ZipUTF8WriterBuilder {
+ public static final String AES_CBC_ISO_10126_PADDING = "AES/CBC/ISO10126Padding";
+ public static final String SHA_1_PRNG = "SHA1PRNG";
+
/**
* **Beware: for security reasons, this fills the password array with 0's**
*
@@ -76,15 +79,13 @@ public ZipUTF8CryptoWriterBuilder(final ZipUTF8WriterBuilderImpl writerBuilder,
}
@Override
- public ZipUTF8Writer build(final OutputStream outputStream) {
+ public ZipUTF8CryptoWriter build(final OutputStream outputStream) {
try {
return new ZipUTF8CryptoWriter(this.writerBuilder.build(outputStream),
- new StandardEncrypter(SecureRandom.getInstance("SHA1PRNG"),
- Cipher.getInstance("AES/CBC/ISO10126Padding"), 100000, 32, 32, this.parametersBuilder
+ new StandardEncrypter(SecureRandom.getInstance(SHA_1_PRNG),
+ Cipher.getInstance(AES_CBC_ISO_10126_PADDING), 100000, 32, 32, this.parametersBuilder
), this.hashedPassword);
- } catch (final NoSuchAlgorithmException e) {
- throw new RuntimeException(e);
- } catch (final NoSuchPaddingException e) {
+ } catch (final NoSuchAlgorithmException | NoSuchPaddingException e) {
throw new RuntimeException(e);
}
}
diff --git a/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ProtectionFactoryTest.java b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ProtectionFactoryTest.java
new file mode 100644
index 00000000..e8c79682
--- /dev/null
+++ b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ProtectionFactoryTest.java
@@ -0,0 +1,47 @@
+/*
+ * FastODS - A very fast and lightweight (no dependency) library for creating ODS
+ * (Open Document Spreadsheet, mainly for Calc) files in Java.
+ * It's a Martin Schulz's SimpleODS fork
+ * Copyright (C) 2016-2020 J. FĂ©rard
+ * SimpleODS - A lightweight java library to create simple OpenOffice spreadsheets
+ * Copyright (C) 2008-2013 Martin Schulz
+ *
+ * This file is part of FastODS.
+ *
+ * FastODS is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later
+ * version.
+ *
+ * FastODS is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
+ * for more details.
+ *
+ * You should have received a copy of the GNU General Public License along with
+ * this program. If not, see .
+ */
+
+package com.github.jferard.fastods.crypto;
+
+import com.github.jferard.fastods.util.Protection;
+import com.github.jferard.fastods.util.XMLUtil;
+import org.junit.Assert;
+import org.junit.Test;
+
+import java.io.IOException;
+import java.security.NoSuchAlgorithmException;
+
+public class ProtectionFactoryTest {
+ @Test
+ public void test() throws NoSuchAlgorithmException, IOException {
+ final Protection p = ProtectionFactory.createSha256("passwd".toCharArray());
+ final StringBuilder sb = new StringBuilder();
+ p.appendAttributes(XMLUtil.create(), sb);
+
+ Assert.assertEquals(
+ " table:protected=\"true\" table:protection-key=\"DWvmmyZHF/LdM2UuISsXMQS0pke3wRrnLpiF8RzTEvs=\" table:protection-key-digest-algorithm=\"http://www.w3.org/2000/09/xmldsig#sha256\"",
+ sb.toString());
+ }
+
+}
\ No newline at end of file
diff --git a/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/StandardEncrypterTest.java b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/StandardEncrypterTest.java
index 0f05a5d0..86e0cddd 100644
--- a/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/StandardEncrypterTest.java
+++ b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/StandardEncrypterTest.java
@@ -24,8 +24,11 @@
package com.github.jferard.fastods.crypto;
+import com.github.jferard.fastods.XMLConvertible;
import com.github.jferard.fastods.odselement.EncryptParameters;
+import com.github.jferard.fastods.testlib.DomTester;
import com.github.jferard.fastods.util.CharsetUtil;
+import com.github.jferard.fastods.util.XMLUtil;
import org.bouncycastle.util.encoders.Base64;
import org.easymock.EasyMock;
import org.junit.Assert;
@@ -112,4 +115,57 @@ public void testIVandSalt() throws NoSuchPaddingException, NoSuchAlgorithmExcept
PowerMock.verifyAll();
}
+
+ @Test
+ public void testBuildParameters()
+ throws NoSuchPaddingException, NoSuchAlgorithmException, IOException {
+ final SecureRandom sr = PowerMock.createMock(SecureRandom.class);
+ final StandardEncrypter encrypter = new StandardEncrypter(
+ sr, Cipher.getInstance("AES/CBC/ISO10126Padding"),
+ 100000, 32, 32, EncryptParameters.builder()
+ );
+
+ PowerMock.resetAll();
+
+ PowerMock.replayAll();
+ final EncryptParameters parameters =
+ encrypter.buildParameters(100, 10, 40L, "ccs", "salt", "vec");
+
+ PowerMock.verifyAll();
+ Assert.assertEquals(100, parameters.getPlainDataSize());
+ Assert.assertEquals(10, parameters.getCompressedThenEncryptedDataSize());
+ Assert.assertEquals(40L, parameters.getCrc32());
+
+ StandardEncrypterTest.assertXMLEquals("" +
+ "" +
+ "" +
+ "" +
+ "", parameters);
+ }
+
+ public static String toXML(final XMLConvertible o) throws IOException {
+ final StringBuilder sb = new StringBuilder();
+ o.appendXMLContent(XMLUtil.create(), sb);
+ return sb.toString();
+ }
+
+ /**
+ * Beware: sensitive to spaces (spaces are children)
+ *
+ * @param xml the xml string
+ * @param o an object convertible to XML
+ * @throws IOException if an I/O error occurs
+ */
+ public static void assertXMLEquals(final String xml, final XMLConvertible o)
+ throws IOException {
+ DomTester.assertEquals(xml, StandardEncrypterTest.toXML(o));
+ }
}
\ No newline at end of file
diff --git a/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterTest.java b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterTest.java
index 91918a3b..5e6a509e 100644
--- a/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterTest.java
+++ b/fastods-crypto/src/test/java/com/github/jferard/fastods/crypto/ZipUTF8CryptoWriterTest.java
@@ -25,6 +25,7 @@
package com.github.jferard.fastods.crypto;
import com.github.jferard.fastods.odselement.EncryptParameters;
+import com.github.jferard.fastods.odselement.EncryptParametersBuilder;
import com.github.jferard.fastods.odselement.StandardOdsEntry;
import com.github.jferard.fastods.odselement.UnregisteredOdsEntry;
import com.github.jferard.fastods.odselement.UnregisteredStoredEntry;
@@ -314,4 +315,74 @@ public void testAppend() throws IOException, NoSuchPaddingException, InvalidKeyE
// EOCD
'P', 'K', 5, 6, 0, 0, 0, 0, 2, 0, 2, 0, 117, 0, 0, 0, 6, 1, 0, 0, 0, 0}, bytes);
}
-}
\ No newline at end of file
+
+ @Test
+ public void testBuilder()
+ throws NoSuchAlgorithmException {
+ final ZipUTF8WriterBuilderImpl writerBuilder =
+ PowerMock.createMock(ZipUTF8WriterBuilderImpl.class);
+ final EncryptParametersBuilder parametersBuilder =
+ PowerMock.createMock(EncryptParametersBuilder.class);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+
+ PowerMock.resetAll();
+ EasyMock.expect(writerBuilder.build(bos)).andReturn(null);
+
+ PowerMock.replayAll();
+ final ZipUTF8CryptoWriterBuilder builder =
+ new ZipUTF8CryptoWriterBuilder(writerBuilder, parametersBuilder,
+ "passwd".toCharArray());
+ builder.build(bos);
+
+ PowerMock.verifyAll();
+ }
+
+ @Test
+ public void testExc()
+ throws IOException, InvalidAlgorithmParameterException, NoSuchPaddingException,
+ IllegalBlockSizeException, NoSuchAlgorithmException, BadPaddingException,
+ InvalidKeyException {
+ final StandardEncrypter encrypter = PowerMock.createMock(StandardEncrypter.class);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ final ZipUTF8Writer writer = PowerMock.createMock(ZipUTF8Writer.class);
+ final byte[] salt = "foo".getBytes(StandardCharsets.UTF_8);
+ final byte[] iv = "bar".getBytes(StandardCharsets.UTF_8);
+ final byte[] data = "baz".getBytes(StandardCharsets.UTF_8);
+ final byte[] password = "passwd".getBytes(StandardCharsets.UTF_8);
+
+ PowerMock.resetAll();
+ EasyMock.expect(encrypter.generateSalt()).andReturn(salt);
+ EasyMock.expect(encrypter.generateIV()).andReturn(iv);
+ EasyMock.expect(encrypter.compress(new byte[0])).andReturn(data);
+ EasyMock.expect(encrypter.encrypt(data, password, salt, iv))
+ .andThrow(new NoSuchAlgorithmException());
+
+ PowerMock.replayAll();
+ final ZipUTF8CryptoWriter cryptoWriter =
+ new ZipUTF8CryptoWriter(writer, encrypter, password);
+ cryptoWriter.putNextEntry(new UnregisteredOdsEntry("path"));
+ Assert.assertThrows(IOException.class, () -> cryptoWriter.closeEntry());
+
+ PowerMock.verifyAll();
+ }
+
+ @Test
+ public void testSetComment() {
+ final StandardEncrypter encrypter = PowerMock.createMock(StandardEncrypter.class);
+ final ByteArrayOutputStream bos = new ByteArrayOutputStream();
+ final ZipUTF8Writer writer = PowerMock.createMock(ZipUTF8Writer.class);
+ final byte[] password = "passwd".getBytes(StandardCharsets.UTF_8);
+ final StandardOdsEntry entry = new StandardOdsEntry("path", "mt", "v");
+
+ PowerMock.resetAll();
+ writer.setComment("Comment");
+ writer.registerEntry(entry);
+
+ PowerMock.replayAll();
+ final ZipUTF8CryptoWriter cryptoWriter =
+ new ZipUTF8CryptoWriter(writer, encrypter, password);
+ cryptoWriter.setComment("Comment");
+ cryptoWriter.registerEntry(entry);
+
+ PowerMock.verifyAll();
+ }}
\ No newline at end of file