From 6369d490678b035d6abe2e766ecda09fc45d480a Mon Sep 17 00:00:00 2001 From: James Cover jdcove2 Date: Wed, 15 Nov 2023 21:09:41 +0000 Subject: [PATCH 1/2] Hashes alternate views instead of base64 converting. --- .../core/IBaseDataObjectXmlCodecs.java | 68 ++++++++++++++++--- .../core/IBaseDataObjectXmlHelperTest.java | 6 ++ .../test/core/junit5/RegressionTest.java | 6 ++ 3 files changed, 69 insertions(+), 11 deletions(-) diff --git a/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java b/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java index ce3a5dcbec..8ad91959e1 100644 --- a/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java +++ b/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java @@ -475,9 +475,9 @@ public void encode(final List values, final Element * An implementation of an XML element encoder for SeekableByteChannel's that produces a SHA256 hash value. */ public static final ElementEncoder SHA256_SEEKABLE_BYTE_CHANNEL_FACTORY_ENCODER = - new HashSeekableByteChannelFactoryEncoder(); + new Sha256SeekableByteChannelFactoryEncoder(); - private static class HashSeekableByteChannelFactoryEncoder implements ElementEncoder { + private static class Sha256SeekableByteChannelFactoryEncoder implements ElementEncoder { @Override public void encode(final List values, final Element parentElement, final String childElementName) { for (final SeekableByteChannelFactory value : values) { @@ -571,7 +571,7 @@ private static class BooleanEncoder implements ElementEncoder { @Override public void encode(final List values, final Element parentElement, final String childElementName) { for (final boolean value : values) { - if ((Boolean) PRIMITVE_NAME_DEFAULT_MAP.get(childElementName) != value) { + if (!((Boolean) PRIMITVE_NAME_DEFAULT_MAP.get(childElementName)).equals(value)) { parentElement.addContent(AbstractJDOMUtil.simpleElement(childElementName, value)); } } @@ -620,6 +620,36 @@ public void encode(final List> values, final Element parentE } } + public static final ElementEncoder> SHA256_STRING_BYTE_ARRAY_ENCODER = new Sha256StringByteArrayEncoder(); + + private static class Sha256StringByteArrayEncoder implements ElementEncoder> { + @Override + public void encode(final List> values, final Element parentElement, final String childElementName) { + for (final Map value : values) { + for (final Entry view : value.entrySet()) { + final Element metaElement = new Element(IbdoXmlElementNames.VIEW); + + parentElement.addContent(metaElement); + metaElement.addContent(preserve(protectedElement(IbdoXmlElementNames.NAME, view.getKey()))); + metaElement.addContent(preserve(protectedElementHash(IbdoXmlElementNames.VALUE, view.getValue()))); + } + } + } + + private static Element protectedElementHash(final String name, final byte[] bytes) { + final Element element = new Element(name); + + if (ByteUtil.hasNonPrintableValues(bytes)) { + element.setAttribute(IBaseDataObjectXmlCodecs.ENCODING_ATTRIBUTE_NAME, IBaseDataObjectXmlCodecs.SHA256); + element.addContent(ByteUtil.sha256Bytes(bytes)); + } else { + element.addContent(new String(bytes, StandardCharsets.ISO_8859_1)); + } + + return element; + } + } + /** * The default set of XML element decoders. */ @@ -652,7 +682,7 @@ public void encode(final List> values, final Element parentE DEFAULT_BYTE_ARRAY_ENCODER, DEFAULT_INTEGER_ENCODER, SHA256_SEEKABLE_BYTE_CHANNEL_FACTORY_ENCODER, - DEFAULT_STRING_BYTE_ARRAY_ENCODER, + SHA256_STRING_BYTE_ARRAY_ENCODER, DEFAULT_STRING_ENCODER, DEFAULT_STRING_OBJECT_ENCODER); @@ -675,13 +705,26 @@ public static byte[] extractBytes(final String encoding, final String elementVal return elementValue.getBytes(StandardCharsets.UTF_8); } - private static Element preserve(final Element element) { + /** + * Adds preservation attributes to an XML element. + * + * @param element to add preservation attributes to. + * @return the element passed in with the preservation elements added. + */ + public static Element preserve(final Element element) { element.setAttribute("space", "preserve", XML_NAMESPACE); return element; } - private static Element protectedElement(final String name, final String string) { + /** + * Creates a protected XML string element. + * + * @param name of the XML element + * @param string value of the XML element + * @return the protected XML element. + */ + public static Element protectedElement(final String name, final String string) { return protectedElementBase64(name, string.getBytes(StandardCharsets.UTF_8)); } @@ -694,7 +737,7 @@ private static Element protectedElement(final String name, final String string) * @param bytes to wrap, if they contain unsafe characters * @return the created element */ - private static Element protectedElementBase64(final String name, final byte[] bytes) { + public static Element protectedElementBase64(final String name, final byte[] bytes) { final Element element = new Element(name); if (ByteUtil.hasNonPrintableValues(bytes)) { @@ -714,11 +757,14 @@ private static Element protectedElementBase64(final String name, final byte[] by /** * Gets the requested method object from the IBaseDataObject class. * - * @throws SecurityException - * @throws NoSuchMethodException + * @param methodName name of the ibdo method + * @param parameterTypes list of ibdo method parameter types + * @throws SecurityException if a security manager is present and encounters a problem. + * @throws NoSuchMethodException if a matching method is not found + * @return the ibdo method object */ - private static Method getIbdoMethod(final String name, final Class... parameterTypes) + public static Method getIbdoMethod(final String methodName, final Class... parameterTypes) throws NoSuchMethodException, SecurityException { - return IBaseDataObject.class.getDeclaredMethod(name, parameterTypes); + return IBaseDataObject.class.getDeclaredMethod(methodName, parameterTypes); } } diff --git a/src/test/java/emissary/core/IBaseDataObjectXmlHelperTest.java b/src/test/java/emissary/core/IBaseDataObjectXmlHelperTest.java index 475f122865..72b09bd923 100644 --- a/src/test/java/emissary/core/IBaseDataObjectXmlHelperTest.java +++ b/src/test/java/emissary/core/IBaseDataObjectXmlHelperTest.java @@ -21,7 +21,9 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.List; +import java.util.Map.Entry; import java.util.Random; +import java.util.TreeMap; import static emissary.core.IBaseDataObjectXmlCodecs.DEFAULT_ELEMENT_DECODERS; import static emissary.core.IBaseDataObjectXmlCodecs.DEFAULT_ELEMENT_ENCODERS; @@ -151,6 +153,10 @@ void testBase64Conversion() throws Exception { expectedIbdo.setData(ByteUtil.sha256Bytes(bytes).getBytes(StandardCharsets.ISO_8859_1)); + for (Entry entry : new TreeMap<>(expectedIbdo.getAlternateViews()).entrySet()) { + expectedIbdo.addAlternateView(entry.getKey(), ByteUtil.sha256Bytes(entry.getValue()).getBytes(StandardCharsets.ISO_8859_1)); + } + final String sha256Diff = PlaceComparisonHelper.checkDifferences(expectedIbdo, sha256ActualIbdo, expectedChildren, actualChildren, "testSha256Conversion", DiffCheckConfiguration.onlyCheckData()); diff --git a/src/test/java/emissary/test/core/junit5/RegressionTest.java b/src/test/java/emissary/test/core/junit5/RegressionTest.java index d164a3ced5..64e330e4cb 100644 --- a/src/test/java/emissary/test/core/junit5/RegressionTest.java +++ b/src/test/java/emissary/test/core/junit5/RegressionTest.java @@ -14,6 +14,8 @@ import java.nio.charset.StandardCharsets; import java.util.List; +import java.util.Map.Entry; +import java.util.TreeMap; import static org.junit.jupiter.api.Assertions.fail; @@ -143,6 +145,10 @@ protected void checkAnswersPreHook(final Document answers, final IBaseDataObject return; } + for (Entry entry : new TreeMap<>(payload.getAlternateViews()).entrySet()) { + payload.addAlternateView(entry.getKey(), ByteUtil.sha256Bytes(entry.getValue()).getBytes(StandardCharsets.ISO_8859_1)); + } + if (payload.data() != null && ByteUtil.hasNonPrintableValues(payload.data())) { final String hash = ByteUtil.sha256Bytes(payload.data()); From 7c4fa99af6c48eb897322f5bcea4aad6dec9f4c9 Mon Sep 17 00:00:00 2001 From: James Cover jdcove2 Date: Tue, 21 Nov 2023 18:43:09 +0000 Subject: [PATCH 2/2] Changes based on PR feedback. --- .../core/IBaseDataObjectXmlCodecs.java | 43 +++++++++++-------- 1 file changed, 26 insertions(+), 17 deletions(-) diff --git a/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java b/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java index 8ad91959e1..17d9a2339c 100644 --- a/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java +++ b/src/main/java/emissary/core/IBaseDataObjectXmlCodecs.java @@ -631,23 +631,10 @@ public void encode(final List> values, final Element parentE parentElement.addContent(metaElement); metaElement.addContent(preserve(protectedElement(IbdoXmlElementNames.NAME, view.getKey()))); - metaElement.addContent(preserve(protectedElementHash(IbdoXmlElementNames.VALUE, view.getValue()))); + metaElement.addContent(preserve(protectedElementSha256(IbdoXmlElementNames.VALUE, view.getValue()))); } } } - - private static Element protectedElementHash(final String name, final byte[] bytes) { - final Element element = new Element(name); - - if (ByteUtil.hasNonPrintableValues(bytes)) { - element.setAttribute(IBaseDataObjectXmlCodecs.ENCODING_ATTRIBUTE_NAME, IBaseDataObjectXmlCodecs.SHA256); - element.addContent(ByteUtil.sha256Bytes(bytes)); - } else { - element.addContent(new String(bytes, StandardCharsets.ISO_8859_1)); - } - - return element; - } } /** @@ -729,12 +716,12 @@ public static Element protectedElement(final String name, final String string) { } /** - * Creates a 'protected' element which can be encoded with base64 if it contains unsafe characters + * Creates a 'protected' element which can be encoded with base64 if it contains non-printable characters * - * See method source for specific definition of 'unsafe'. + * See method source for specific definition of 'non-printable'. * * @param name of the element - * @param bytes to wrap, if they contain unsafe characters + * @param bytes to wrap, if they contain non-printable characters * @return the created element */ public static Element protectedElementBase64(final String name, final byte[] bytes) { @@ -754,6 +741,28 @@ public static Element protectedElementBase64(final String name, final byte[] byt return element; } + /** + * Creates a 'protected' element which can be hashed with sha256 if it contains non-printable characters + * + * See method source for specific definition of 'non-printable'. + * + * @param name of the element + * @param bytes to wrap, if they contain non-printable characters. + * @return the created element + */ + public static Element protectedElementSha256(final String name, final byte[] bytes) { + final Element element = new Element(name); + + if (ByteUtil.hasNonPrintableValues(bytes)) { + element.setAttribute(IBaseDataObjectXmlCodecs.ENCODING_ATTRIBUTE_NAME, IBaseDataObjectXmlCodecs.SHA256); + element.addContent(ByteUtil.sha256Bytes(bytes)); + } else { + element.addContent(new String(bytes, StandardCharsets.ISO_8859_1)); + } + + return element; + } + /** * Gets the requested method object from the IBaseDataObject class. *