diff --git a/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/Hashes.java b/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/Hashes.java new file mode 100644 index 000000000..98280fb88 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/Hashes.java @@ -0,0 +1,23 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.api.io; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.registry.Service; +import org.eclipse.passage.lic.internal.api.registry.StringServiceId; + +public interface Hashes extends Service { + + byte[] get(byte[] source) throws LicensingException; + +} diff --git a/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/HashesRegistry.java b/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/HashesRegistry.java new file mode 100644 index 000000000..663a54aef --- /dev/null +++ b/bundles/org.eclipse.passage.lic.api/src/org/eclipse/passage/lic/internal/api/io/HashesRegistry.java @@ -0,0 +1,22 @@ +/******************************************************************************* + * Copyright (c) 2020 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.api.io; + +import java.util.function.Supplier; + +import org.eclipse.passage.lic.internal.api.registry.Registry; +import org.eclipse.passage.lic.internal.api.registry.StringServiceId; + +public interface HashesRegistry extends Supplier> { + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/KeyContent.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/KeyContent.java new file mode 100644 index 000000000..6285906c8 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/KeyContent.java @@ -0,0 +1,43 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.io; + +import java.io.ByteArrayOutputStream; +import java.io.IOException; +import java.io.InputStream; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.io.KeyKeeper; + +public final class KeyContent { + + private final KeyKeeper keeper; + + public KeyContent(KeyKeeper keeper) { + this.keeper = keeper; + } + + public byte[] get() throws LicensingException { + try (InputStream in = keeper.productPublicKey(); ByteArrayOutputStream out = new ByteArrayOutputStream()) { + int acquired = -1; + byte[] buffer = new byte[1024]; + while ((acquired = in.read(buffer)) != -1) { + out.write(buffer, 0, acquired); + } + return out.toByteArray(); + } catch (IOException e) { + throw new LicensingException("Failed to read public key ", e); //$NON-NLS-1$ + } + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/MD5Hashes.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/MD5Hashes.java new file mode 100644 index 000000000..86ec74e41 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/io/MD5Hashes.java @@ -0,0 +1,40 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.base.io; + +import java.security.MessageDigest; +import java.security.NoSuchAlgorithmException; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.io.Hashes; +import org.eclipse.passage.lic.internal.api.registry.StringServiceId; + +public final class MD5Hashes implements Hashes { + + private final String algorithm = "MD5"; //$NON-NLS-1$ + + @Override + public StringServiceId id() { + return new StringServiceId(algorithm); + } + + @Override + public byte[] get(byte[] source) throws LicensingException { + try { + return MessageDigest.getInstance(algorithm).digest(source); + } catch (NoSuchAlgorithmException e) { + throw new LicensingException("Failed to build a hash ", e); //$NON-NLS-1$ + } + } + +} diff --git a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/registry/ReadOnlyRegistry.java b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/registry/ReadOnlyRegistry.java index b9cbdf51f..96da5f694 100644 --- a/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/registry/ReadOnlyRegistry.java +++ b/bundles/org.eclipse.passage.lic.base/src/org/eclipse/passage/lic/internal/base/registry/ReadOnlyRegistry.java @@ -28,4 +28,8 @@ public ReadOnlyRegistry(S service) { super(Collections.singleton(service)); } + public ReadOnlyRegistry() { + super(Collections.emptyList()); + } + } diff --git a/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SafePayload.java b/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SafePayload.java new file mode 100644 index 000000000..3205a303b --- /dev/null +++ b/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SafePayload.java @@ -0,0 +1,56 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.net.io; + +import java.util.Base64; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.io.Hashes; +import org.eclipse.passage.lic.internal.api.io.KeyKeeper; +import org.eclipse.passage.lic.internal.base.io.KeyContent; + +public final class SafePayload { + + private final Hashes hashes; + private final KeyKeeper keeper; + + public SafePayload(KeyKeeper keeper, Hashes hashes) { + this.keeper = keeper; + this.hashes = hashes; + } + + public byte[] encode(byte[] raw) throws LicensingException { + return transportable(coded(raw)); + } + + public byte[] decode(byte[] raw) throws LicensingException { + return coded(transported(raw)); + } + + private byte[] transportable(byte[] content) { + return Base64.getEncoder().encode(content); + } + + private byte[] transported(byte[] content) { + return Base64.getDecoder().decode(content); + } + + private byte[] coded(byte[] source) throws LicensingException { + return new SymmetricCode(key()).get(source); + } + + private byte[] key() throws LicensingException { + return hashes.get(new KeyContent(keeper).get()); + } + +} diff --git a/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SymmetricCode.java b/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SymmetricCode.java new file mode 100644 index 000000000..08dbde133 --- /dev/null +++ b/bundles/org.eclipse.passage.lic.net/src/org/eclipse/passage/lic/internal/net/io/SymmetricCode.java @@ -0,0 +1,41 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.internal.net.io; + +final class SymmetricCode { + + private final byte[] key; + + SymmetricCode(byte[] key) { + this.key = key; + } + + byte[] get(byte[] source) { + int length = source.length; + byte[] cypher = enoughKey(length); + byte[] coded = new byte[length]; + for (int i = 0; i < length; i++) { + coded[i] = (byte) (source[i] ^ cypher[i]); + } + return coded; + } + + private byte[] enoughKey(int length) { + byte[] elongated = new byte[length]; + for (int i = 0; i < length; i++) { + elongated[i] = key[i % key.length]; + } + return elongated; + } + +} diff --git a/tests/org.eclipse.passage.lic.net.tests/.settings/org.eclipse.core.resources.prefs b/tests/org.eclipse.passage.lic.net.tests/.settings/org.eclipse.core.resources.prefs new file mode 100644 index 000000000..9e945b514 --- /dev/null +++ b/tests/org.eclipse.passage.lic.net.tests/.settings/org.eclipse.core.resources.prefs @@ -0,0 +1,2 @@ +eclipse.preferences.version=1 +encoding//src/org/eclipse/passage/lic/net/tests/io/SafePayloadTest.java=UTF-8 diff --git a/tests/org.eclipse.passage.lic.net.tests/resource/io/key.pub b/tests/org.eclipse.passage.lic.net.tests/resource/io/key.pub new file mode 100644 index 000000000..629a69573 --- /dev/null +++ b/tests/org.eclipse.passage.lic.net.tests/resource/io/key.pub @@ -0,0 +1,19 @@ +-----BEGIN PGP PUBLIC KEY BLOCK----- +Version: BCPG v1.65 + +mI0EXz6mRAEEAMDncQ54z+JSaAc7zjfnojFCk9/jcPcZHbIENjGeUg/3NfI3GbJv +cGi//abYchzilMJS/h+kNPmKkABZ9FDvUFjdxhmjaqL1dgR4xJmd4SlczLAtPQhM +dNukFyWHIkhFL88W5yuqUtWdCPplzmLbe2wX6wLrqgwgKqchzISCi6qhABEBAAG0 +GGFudGktaHVtYW4tbWFnaWMucHJvZHVjdIiiBBMBAgAMBQJfPqZEBQkAAAPoAAoJ +EHbBbJDZeiPsEV0D/iPfuVsuGjt2Xa1tsJ1gAI75dTwiNHlFAMPSSa0xuQInrRln +wzNY4JNYz9ZSEp1IjA6rsAYCgh/vgITKrqvp/MHPfk6ojRz0ecT/UkblZCB/S4CI +64lQrVxGYttX8foexs6VDD43RIO0jUpLTX7lXzBMBP0ETzYT+E0Q/qgW/LiDuI0E +Xz6mRAEEAMDncQ54z+JSaAc7zjfnojFCk9/jcPcZHbIENjGeUg/3NfI3GbJvcGi/ +/abYchzilMJS/h+kNPmKkABZ9FDvUFjdxhmjaqL1dgR4xJmd4SlczLAtPQhMdNuk +FyWHIkhFL88W5yuqUtWdCPplzmLbe2wX6wLrqgwgKqchzISCi6qhABEBAAGIogQY +AQIADAUCXz6mRAUJAAAD6AAKCRB2wWyQ2Xoj7D69A/9dzrh61zov6cgSLUVR4p+W +AewU3yCRYHIx158K7jG2lmoKmiLcJKjcqWmoJcuxX8lPWHqeG2wEVqnONgLi4Yq1 +/ZWrc/CqszV4258zGu8pQxZOdovbzxgPf9eTPqRtIOnrtWcwlRWKkW3K8HgMCq9X +coWMvpxXfnEvN7VrS6sePw== +=eVWh +-----END PGP PUBLIC KEY BLOCK----- diff --git a/tests/org.eclipse.passage.lic.net.tests/src/org/eclipse/passage/lic/net/tests/io/SafePayloadTest.java b/tests/org.eclipse.passage.lic.net.tests/src/org/eclipse/passage/lic/net/tests/io/SafePayloadTest.java new file mode 100644 index 000000000..c43683423 --- /dev/null +++ b/tests/org.eclipse.passage.lic.net.tests/src/org/eclipse/passage/lic/net/tests/io/SafePayloadTest.java @@ -0,0 +1,48 @@ +/******************************************************************************* + * Copyright (c) 2021 ArSysOp + * + * This program and the accompanying materials are made available under the + * terms of the Eclipse Public License 2.0 which is available at + * https://www.eclipse.org/legal/epl-2.0/. + * + * SPDX-License-Identifier: EPL-2.0 + * + * Contributors: + * ArSysOp - initial API and implementation + *******************************************************************************/ +package org.eclipse.passage.lic.net.tests.io; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; +import static org.junit.Assert.fail; + +import java.nio.file.Paths; + +import org.eclipse.passage.lic.internal.api.LicensingException; +import org.eclipse.passage.lic.internal.api.io.KeyKeeper; +import org.eclipse.passage.lic.internal.base.io.FileKeyKeeper; +import org.eclipse.passage.lic.internal.base.io.MD5Hashes; +import org.eclipse.passage.lic.internal.net.io.SafePayload; +import org.junit.Test; + +public final class SafePayloadTest { + + @Test + public void symmetric() { + String original = "S0me sophisticäted Str!ng"; //$NON-NLS-1$ + try { + byte[] encoded = new SafePayload(keerper(), new MD5Hashes()).encode(original.getBytes()); + assertTrue(encoded.length > 0); + System.out.println(new String(encoded)); + byte[] decoded = new SafePayload(keerper(), new MD5Hashes()).decode(encoded); + assertTrue(decoded.length > 0); + assertEquals(original, new String(decoded)); + } catch (LicensingException e) { + fail("Not intended to fail on valid data"); //$NON-NLS-1$ + } + } + + private KeyKeeper keerper() throws LicensingException { + return new FileKeyKeeper(Paths.get("resource").resolve("io").resolve("key.pub")); //$NON-NLS-1$ //$NON-NLS-2$ //$NON-NLS-3$ + } +}