From 26af804c20e76594fad334a1e935751c00945cff Mon Sep 17 00:00:00 2001 From: Jan Bobolz Date: Wed, 27 Oct 2021 17:34:21 +0200 Subject: [PATCH 01/18] Fixed protocol error for witnesses without vars --- .../sigma/schnorr/SendThenDelegateFragment.java | 17 ++++++++++------- 1 file changed, 10 insertions(+), 7 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java b/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java index 59eeb711..694543cc 100644 --- a/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java +++ b/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java @@ -517,19 +517,22 @@ private void checkDuplicate(String name) { private WitnessValues buildWitnessValues() { //Populate the witnessForVariables map with znWitnesses and groupElemWitnesses (not possible earlier because user may have added variables by name before subprotocolSpec has been set up with the concrete SchnorrVariable objects) - znWitnesses.forEach((name, val) -> witnessesForVariables.put(name, new SchnorrZnVariableValue(val, (SchnorrZnVariable) subprotocolSpec.getVariable(name)))); - groupElemWitnesses.forEach((name, val) -> witnessesForVariables.put(name, new SchnorrGroupElemVariableValue(val, (SchnorrGroupElemVariable) subprotocolSpec.getVariable(name)))); + znWitnesses.forEach((name, val) -> { + if (!subprotocolSpec.containsVariable(name)) + throw new IllegalStateException("Variable "+name+" has not been registered in the subprotocol spec, but its witness has been given in the prover spec"); + witnessesForVariables.put(name, new SchnorrZnVariableValue(val, (SchnorrZnVariable) subprotocolSpec.getVariable(name))); + }); + groupElemWitnesses.forEach((name, val) -> { + if (!subprotocolSpec.containsVariable(name)) + throw new IllegalStateException("Variable "+name+" has not been registered in the subprotocol spec, but its witness has been given in the prover spec"); + witnessesForVariables.put(name, new SchnorrGroupElemVariableValue(val, (SchnorrGroupElemVariable) subprotocolSpec.getVariable(name))); + }); subprotocolSpec.forEachVariable((name, var) -> { if (!witnessesForVariables.containsKey(name)) throw new IllegalStateException("Witness for " + name + "is missing"); }); - witnessesForVariables.forEach((name, val) -> { - if (!subprotocolSpec.containsVariable(val.getVariable())) - throw new IllegalStateException("Variable "+name+" has not registered, but its witness has been given"); - }); - return new WitnessValues(witnessesForVariables); } From 67e15918033c2eeb047ac8b383dd49cae2d62954 Mon Sep 17 00:00:00 2001 From: Eric Ackermann Date: Wed, 24 Nov 2021 17:51:04 +0100 Subject: [PATCH 02/18] Fix exception message formatting error In SendThenDelegateFragment, a space between the name of a missing witness variable name and the text of the exception message was missing --- .../arguments/sigma/schnorr/SendThenDelegateFragment.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java b/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java index 694543cc..a8f3ad12 100644 --- a/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java +++ b/src/main/java/org/cryptimeleon/craco/protocols/arguments/sigma/schnorr/SendThenDelegateFragment.java @@ -530,7 +530,7 @@ private WitnessValues buildWitnessValues() { subprotocolSpec.forEachVariable((name, var) -> { if (!witnessesForVariables.containsKey(name)) - throw new IllegalStateException("Witness for " + name + "is missing"); + throw new IllegalStateException("Witness for " + name + " is missing"); }); return new WitnessValues(witnessesForVariables); From 489448105453940d686f5fa159832d35d74af19f Mon Sep 17 00:00:00 2001 From: Fabian Eidens Date: Fri, 25 Feb 2022 16:26:23 +0100 Subject: [PATCH 03/18] MapToPlaintext reworked for commitment schemes. Fixes #98. --- .../craco/commitment/CommitmentScheme.java | 15 +++- .../HashThenCommitCommitmentScheme.java | 18 +++-- .../pedersen/PedersenCommitmentScheme.java | 15 ++-- .../commitment/CommitmentSchemeTester.java | 71 +++++++------------ 4 files changed, 62 insertions(+), 57 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/commitment/CommitmentScheme.java b/src/main/java/org/cryptimeleon/craco/commitment/CommitmentScheme.java index e3309fa3..be93babc 100644 --- a/src/main/java/org/cryptimeleon/craco/commitment/CommitmentScheme.java +++ b/src/main/java/org/cryptimeleon/craco/commitment/CommitmentScheme.java @@ -50,7 +50,20 @@ public interface CommitmentScheme extends StandaloneRepresentable, Representatio * @throws IllegalArgumentException if there is no injective {@code PlainText} element corresponding to the given * bytes, for example if the byte array is too long */ - PlainText mapToPlainText(byte[] bytes); + PlainText mapToPlaintext(byte[] bytes); + + + /** + * Returns the maximal number of bytes that can be mapped injectively to a {@link PlainText} by + * {@link #mapToPlaintext(byte[])} + *

+ * As described in {@link #mapToPlaintext(byte[])} there might be no injective {@link PlainText} for some byte arrays, e.g. + * if the byte array is too long. Therefore, this method provides the maximal number of bytes that can be mapped + * injectively to a {@link PlainText}. + * + * @return maximal number of bytes that can be given to {@link #mapToPlaintext(byte[])}. + */ + int getMaxNumberOfBytesForMapToPlaintext(); default CommitmentPair restoreCommitmentPair(Representation repr) { return new CommitmentPair( diff --git a/src/main/java/org/cryptimeleon/craco/commitment/hashthencommit/HashThenCommitCommitmentScheme.java b/src/main/java/org/cryptimeleon/craco/commitment/hashthencommit/HashThenCommitCommitmentScheme.java index 0d0afe40..75ab3eb2 100644 --- a/src/main/java/org/cryptimeleon/craco/commitment/hashthencommit/HashThenCommitCommitmentScheme.java +++ b/src/main/java/org/cryptimeleon/craco/commitment/hashthencommit/HashThenCommitCommitmentScheme.java @@ -31,6 +31,9 @@ public class HashThenCommitCommitmentScheme implements CommitmentScheme { * @param hashFunction {@link HashFunction} used for hashing of the original message */ public HashThenCommitCommitmentScheme(CommitmentScheme encapsulatedScheme, HashFunction hashFunction) { + if (hashFunction.getOutputLength() > encapsulatedScheme.getMaxNumberOfBytesForMapToPlaintext()) { + throw new IllegalArgumentException("The given hash function is incompatible with the given commitment scheme! The output length is too large."); + } this.encapsulatedScheme = encapsulatedScheme; this.hashFunction = hashFunction; } @@ -54,13 +57,13 @@ public HashThenCommitCommitmentScheme(Representation repr) { public CommitmentPair commit(PlainText plainText) { ByteArrayImplementation pt; if (!(plainText instanceof ByteArrayImplementation)) { - pt = (ByteArrayImplementation) mapToPlainText(plainText.getUniqueByteRepresentation()); + pt = new ByteArrayImplementation(plainText.getUniqueByteRepresentation()); } else { pt = (ByteArrayImplementation) plainText; } // hash byte[] hashedBytes = hashFunction.hash(pt.getData()); - PlainText hashedPlainText = encapsulatedScheme.mapToPlainText(hashedBytes); + PlainText hashedPlainText = encapsulatedScheme.mapToPlaintext(hashedBytes); return encapsulatedScheme.commit(hashedPlainText); } @@ -78,21 +81,26 @@ public CommitmentPair commit(PlainText plainText) { public boolean verify(Commitment commitment, OpenValue openValue, PlainText plainText) { ByteArrayImplementation pt; if (!(plainText instanceof ByteArrayImplementation)) { - pt = (ByteArrayImplementation) mapToPlainText(plainText.getUniqueByteRepresentation()); + pt = (ByteArrayImplementation) mapToPlaintext(plainText.getUniqueByteRepresentation()); } else { pt = (ByteArrayImplementation) plainText; } // hash byte[] hashedBytes = hashFunction.hash(pt.getData()); - PlainText hashedPlainText = encapsulatedScheme.mapToPlainText(hashedBytes); + PlainText hashedPlainText = encapsulatedScheme.mapToPlaintext(hashedBytes); return encapsulatedScheme.verify(commitment, openValue, hashedPlainText); } @Override - public PlainText mapToPlainText(byte[] bytes) throws IllegalArgumentException { + public PlainText mapToPlaintext(byte[] bytes) throws IllegalArgumentException { return new ByteArrayImplementation(bytes); } + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return Integer.MAX_VALUE; + } + @Override public Commitment restoreCommitment(Representation repr) { return encapsulatedScheme.restoreCommitment(repr); diff --git a/src/main/java/org/cryptimeleon/craco/commitment/pedersen/PedersenCommitmentScheme.java b/src/main/java/org/cryptimeleon/craco/commitment/pedersen/PedersenCommitmentScheme.java index a928a1dc..6793532f 100644 --- a/src/main/java/org/cryptimeleon/craco/commitment/pedersen/PedersenCommitmentScheme.java +++ b/src/main/java/org/cryptimeleon/craco/commitment/pedersen/PedersenCommitmentScheme.java @@ -83,12 +83,15 @@ public boolean verify(Commitment commitment, OpenValue openValue, RingElementVec } @Override - public MessageBlock mapToPlainText(byte[] bytes) throws IllegalArgumentException { - RingElementPlainText zero = new RingElementPlainText(group.getZn().getZeroElement()); - return new MessageBlock( - Vector.of(new RingElementPlainText(group.getZn().injectiveValueOf(bytes))) - .pad(zero, h.length()) - ); + public MessageBlock mapToPlaintext(byte[] bytes) throws IllegalArgumentException { + //Result will be a vector (zp.injectiveValueOf(bytes), 0, ..., 0) + return new RingElementVector(group.getZn().injectiveValueOf(bytes)).pad(group.getZn().getZeroElement(), h.length()) + .map(RingElementPlainText::new, MessageBlock::new); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (group.size().bitLength() - 1) / 8; } @Override diff --git a/src/test/java/org/cryptimeleon/craco/commitment/CommitmentSchemeTester.java b/src/test/java/org/cryptimeleon/craco/commitment/CommitmentSchemeTester.java index 641428e2..ca5d3d95 100644 --- a/src/test/java/org/cryptimeleon/craco/commitment/CommitmentSchemeTester.java +++ b/src/test/java/org/cryptimeleon/craco/commitment/CommitmentSchemeTester.java @@ -1,9 +1,16 @@ package org.cryptimeleon.craco.commitment; +import org.cryptimeleon.craco.commitment.pedersen.PedersenCommitmentScheme; import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.SignatureScheme; +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.debug.DebugGroup; -import static org.junit.Assert.assertFalse; -import static org.junit.Assert.assertTrue; +import java.util.Arrays; + +import static org.junit.Assert.*; public class CommitmentSchemeTester { @@ -52,54 +59,28 @@ public static void testCommitmentSchemeVerifyWithWrongMessages(CommitmentScheme } /** - * This test checks whether the usage of {@link CommitmentScheme#mapToPlainText} works correctly according to its + * This test checks whether the usage of {@link CommitmentScheme#mapToPlaintext} works correctly according to its * contract. * * @param commitmentScheme single-message {@link CommitmentScheme} whose mapToPlainText() is tested - * @param message {@link PlainText} that is committed to in this test */ - public static void testCommitmentSchemeMapToPlaintext(CommitmentScheme commitmentScheme, PlainText message) { - CommitmentPair commitmentPair = - commitmentScheme.commit(commitmentScheme.mapToPlainText(message.getUniqueByteRepresentation())); - assertTrue(commitmentScheme.verify(commitmentPair.getCommitment(), - commitmentPair.getOpenValue(), commitmentScheme.mapToPlainText(message.getUniqueByteRepresentation()))); + public static void testCommitmentSchemeMapToPlaintext(CommitmentScheme commitmentScheme) { + byte[] randomBytes1 = RandomGenerator.getRandomBytes(commitmentScheme.getMaxNumberOfBytesForMapToPlaintext()); + byte[] randomBytes2; + do { + randomBytes2 = RandomGenerator.getRandomBytes(commitmentScheme.getMaxNumberOfBytesForMapToPlaintext()); + } while (Arrays.equals(randomBytes1, randomBytes2)); + + // different arrays of the same length yield different plaintext + assertNotEquals(commitmentScheme.mapToPlaintext(randomBytes1), commitmentScheme.mapToPlaintext(randomBytes2)); } - /** - * This test checks that the usage of {@link CommitmentScheme#mapToPlainText} works correctly according to its - * contract. - * Test checks that {@link CommitmentScheme#verify} returns false for the {@link Commitment} and - * {@link OpenValue} of - * a commitment to a message {@link CommitmentPair} if the ('announced') message ({@link PlainText}) does not equal - * the message that leads to the given {@link CommitmentPair}. For this test two inequal messages - * ({@link PlainText}s) are being committed to and then it is checked that the verify returns false for all - * combinations of {@link Commitment} and {@link OpenValue} that that do not match to the 'message'. - * - * @param commitmentScheme {@link CommitmentScheme} whose (negative) correctness is to be tested - * @param originalMessage {@link PlainText} that is committed to in this test - * @param wrongMessage Inequal {@link PlainText} to the originalMessage that is committed to in this test - */ - public static void testCommitmentSchemeMapToPlainTextWithWrongMessages(CommitmentScheme commitmentScheme, - PlainText originalMessage, - PlainText wrongMessage) { - CommitmentPair commitmentPair = - commitmentScheme.commit(commitmentScheme.mapToPlainText(originalMessage.getUniqueByteRepresentation())); - CommitmentPair commitmentPair2 = - commitmentScheme.commit(commitmentScheme.mapToPlainText(wrongMessage.getUniqueByteRepresentation())); - assertFalse(commitmentScheme.verify(commitmentPair.getCommitment(), - commitmentPair.getOpenValue(), - commitmentScheme.mapToPlainText(wrongMessage.getUniqueByteRepresentation()))); - assertFalse(commitmentScheme.verify(commitmentPair.getCommitment(), - commitmentPair2.getOpenValue(), - commitmentScheme.mapToPlainText(wrongMessage.getUniqueByteRepresentation()))); - assertFalse(commitmentScheme.verify(commitmentPair.getCommitment(), - commitmentPair2.getOpenValue(), - commitmentScheme.mapToPlainText(originalMessage.getUniqueByteRepresentation()))); - assertFalse(commitmentScheme.verify(commitmentPair2.getCommitment(), - commitmentPair.getOpenValue(), - commitmentScheme.mapToPlainText(wrongMessage.getUniqueByteRepresentation()))); - assertFalse(commitmentScheme.verify(commitmentPair2.getCommitment(), - commitmentPair2.getOpenValue(), - commitmentScheme.mapToPlainText(originalMessage.getUniqueByteRepresentation()))); + public static void main(String[] args) { + Group g = new DebugGroup("test", RandomGenerator.getRandomPrime(80)); + CommitmentScheme ped = new PedersenCommitmentScheme(g,10); + CommitmentSchemeTester.testCommitmentSchemeMapToPlaintext(ped); + + + } } From 12f4dc95d3101fbbc72b77a8ca165e15b4a4d973 Mon Sep 17 00:00:00 2001 From: Hanna Date: Sat, 29 Oct 2022 22:51:49 +0200 Subject: [PATCH 04/18] added AGHO11, KPW15 and AKOT15 structure-preserving signature schemes (#99) * added agho11 skeleton WIP * removed erroneous comment from Groth15 SigScheme * changed AGHO11 implementation (works now) * added KPW15 skeleton * Create SPSKPW15PublicParameterGen.java * removed matrix class * removed superfluous parameter * fixed KPW15 implementation (it works now) * added general SPS scheme tester * added XSIG SPS scheme * added AKOT15 partially one time signature scheme * started AKOT15 trapdoor commitment scheme * added trapdoor commitment interfaces * added comments and formatting to agho11 implementation * added TC components * Added SPSSchemeTester.java * added comments and formatting to agho11 implementation * updated the description of the agho11 scheme * Revert "Merge branch 'develop' into feature/agho11" This reverts commit f7fd72ed52b5b8f4ddf0e4ca86bae82b22b77930, reversing changes made to c8662880363fcc35f8440fafab3507bbf9255ae5. * removed the AGHO11 message type interface * reintroduced generic SPSPublicParameters * removed unnecessary todo message * improved message structure checks and feedback * fixed error when testing StandaloneRepresentation with SPSPublicParameters * updated AGHO11 scheme to cover 0-length messages * added note regarding AGHO11 message padding * revert AGHO11 merge * removed unused references to agho11 * reintroduced generic SPSPublicParameters * fixed error when testing StandaloneRepresentation with SPSPublicParameters * changed KPW15 parameters to use SPSPublicParameters interface * improved annotations and message handling * checked equals and hashCode methods for keys * slight javadoc improvements * slight refactor to MatrixUtility * updated padding note * moved MatrixUtility to inner class * added note on notation for public and secret key * added explanation for k constant * added general SPSSchemeTester * added XSIG SPS scheme * added AKOT15 partially one time signature scheme * started AKOT15 trapdoor commitment scheme * added trapdoor commitment interfaces * added TC components * added TCAKOT15 working version * moved public parameters of TC building blocks to shared interface * set up AKOT15 classes * fixed scheme functionality * added restore methods * fixed error when testing StandaloneRepresentation with SPSPublicParameters * removed unnecessary references * fixed various issues with representation * fixed mapToPlaintext issues * remove matrix utility class * expanded documentation for TCgamma * added more documentation * expanded message checks for TCgamma * added more message checks * moved akot15 public parameters to SPSPublicParameter superclass * removed superfluous parameter generator * improved XSIG documentation * added XSIG message check * improved POS documentation * removed duplicate message check * simplified POS implementation * added message check to TC * refactored FSPS2 * fixed public parameter spaghetti * added StandaloneRepr tests * fixed POS StandaloneRepresentation * fixed XSIG StandaloneRepresentation * fixed TCGamma StandaloneRepresentation * added missing methods (equals & Representation constructors) * updated TCgamma representation to use RepresentatbleRepresentations * fixed remaining StandaloneRepresentations * added remaining representation tests * Revert "Merge branch 'develop' into feature/akot15" This reverts commit 611e27bd1c67e67f0089f0986e42fd7d36968f67, reversing changes made to 8b12b5c3061f2cb364477e52586ec8d5a813d809. * fixed incorrect RepresentableRepresentation usages * Squashed commit of the following: fixed error when testing StandaloneRepresentation with SPSPublicParameters * fixed error when testing StandaloneRepresentation with SPSPublicParameters * removed print statement * fixed PublicParameter merge issues * re-added missing tests * re-added missing agho11 files * re-added missing agho11 repr tests * manually added akot15 signature scheme * fixed the merge from hell * added PublicParameterGenerators that take pre-made BilinearGroups * fixed a minor bug relating to the handeling of small messages in AGHO11 * added precomputations to sps public params * fixed sps schemes allowing subclasses when comparing * added missing xsig comment --- .../craco/commitment/CommitmentKey.java | 10 + .../sig/sps/SPSMessageSpaceVerifier.java | 81 +++ .../craco/sig/sps/SPSPublicParameters.java | 107 +++ .../craco/sig/sps/SPSPublicParametersGen.java | 38 + .../sps/agho11/SPSAGHO11PublicParameters.java | 67 ++ .../agho11/SPSAGHO11PublicParametersGen.java | 37 + .../sig/sps/agho11/SPSAGHO11Signature.java | 90 +++ .../sps/agho11/SPSAGHO11SignatureScheme.java | 507 ++++++++++++++ .../sig/sps/agho11/SPSAGHO11SigningKey.java | 101 +++ .../sps/agho11/SPSAGHO11VerificationKey.java | 99 +++ .../craco/sig/sps/agho11/package-info.java | 5 + .../akot15/AKOT15SharedPublicParameters.java | 100 +++ .../AKOT15SharedPublicParametersGen.java | 28 + .../sig/sps/akot15/fsp2/SPSFSP2Signature.java | 100 +++ .../akot15/fsp2/SPSFSP2SignatureScheme.java | 238 +++++++ .../akot15/fsp2/SPSFSP2VerificationKey.java | 78 +++ .../craco/sig/sps/akot15/package-info.java | 7 + .../sig/sps/akot15/pos/SPSPOSSignature.java | 77 +++ .../sps/akot15/pos/SPSPOSSignatureScheme.java | 298 ++++++++ .../sig/sps/akot15/pos/SPSPOSSigningKey.java | 88 +++ .../sps/akot15/pos/SPSPOSVerificationKey.java | 85 +++ .../sig/sps/akot15/pos/package-info.java | 6 + .../akot15/tc/TCAKOT15CommitmentScheme.java | 359 ++++++++++ .../sig/sps/akot15/tc/TCAKOT15OpenValue.java | 141 ++++ .../craco/sig/sps/akot15/tc/package-info.java | 6 + .../akot15/tcgamma/TCGAKOT15Commitment.java | 64 ++ .../tcgamma/TCGAKOT15CommitmentKey.java | 64 ++ .../tcgamma/TCGAKOT15CommitmentScheme.java | 422 ++++++++++++ .../akot15/tcgamma/TCGAKOT15OpenValue.java | 64 ++ .../tcgamma/TCGAKOT15XSIGCommitment.java | 93 +++ .../tcgamma/TCGAKOT15XSIGCommitmentKey.java | 72 ++ .../TCGAKOT15XSIGPublicParameters.java | 92 +++ .../sig/sps/akot15/tcgamma/package-info.java | 21 + .../akot15/xsig/SPSXSIGPublicParameters.java | 202 ++++++ .../xsig/SPSXSIGPublicParametersGen.java | 31 + .../sig/sps/akot15/xsig/SPSXSIGSignature.java | 88 +++ .../akot15/xsig/SPSXSIGSignatureScheme.java | 479 +++++++++++++ .../sps/akot15/xsig/SPSXSIGSigningKey.java | 113 +++ .../akot15/xsig/SPSXSIGVerificationKey.java | 147 ++++ .../sig/sps/akot15/xsig/package-info.java | 5 + .../sps/kpw15/SPSKPW15PublicParameterGen.java | 26 + .../sps/kpw15/SPSKPW15PublicParameters.java | 58 ++ .../sig/sps/kpw15/SPSKPW15Signature.java | 112 +++ .../sps/kpw15/SPSKPW15SignatureScheme.java | 647 ++++++++++++++++++ .../sig/sps/kpw15/SPSKPW15SigningKey.java | 120 ++++ .../sps/kpw15/SPSKPW15VerificationKey.java | 119 ++++ .../params/CommitmentStandaloneReprTests.java | 37 + .../params/SignatureStandaloneReprTests.java | 65 ++ .../craco/sig/sps/CommitmentSchemeParams.java | 49 ++ .../craco/sig/sps/SPSSchemeTester.java | 85 +++ .../agho11/SPSAGHO11SignatureSchemeTest.java | 25 + ...HO11SignatureSchemeTestParamGenerator.java | 89 +++ ...SignatureSchemeTestParameterGenerator.java | 64 ++ .../akot15/SPSAKOT15SignatureSchemeTests.java | 23 + ...SPOSSignatureSchemeTestParamGenerator.java | 57 ++ .../pos/SPSPOSSignatureSchemeTests.java | 103 +++ ...ommitmentSchemeTestParameterGenerator.java | 46 ++ .../tc/TCAKOT15CommitmentSchemeTester.java | 65 ++ ...ommitmentSchemeTestParameterGenerator.java | 43 ++ .../TCGAKOT15CommitmentSchemeTests.java | 59 ++ ...XSIGSignatureSchemeTestParamGenerator.java | 76 ++ .../xsig/SPSXSIGSignatureSchemeTests.java | 23 + 62 files changed, 6701 insertions(+) create mode 100644 src/main/java/org/cryptimeleon/craco/commitment/CommitmentKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParametersGen.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParametersGen.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11Signature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SigningKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11VerificationKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/agho11/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParametersGen.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2Signature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2SignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2VerificationKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSigningKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSVerificationKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15OpenValue.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15Commitment.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15OpenValue.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitment.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitmentKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGPublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParametersGen.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSigningKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGVerificationKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/package-info.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameterGen.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameters.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15Signature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SigningKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15VerificationKey.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/CommitmentSchemeParams.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/SPSSchemeTester.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTest.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTestParamGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTestParameterGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTests.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTestParamGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTests.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTestParameterGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTester.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTestParameterGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTests.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTestParamGenerator.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTests.java diff --git a/src/main/java/org/cryptimeleon/craco/commitment/CommitmentKey.java b/src/main/java/org/cryptimeleon/craco/commitment/CommitmentKey.java new file mode 100644 index 00000000..3e0a409f --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/commitment/CommitmentKey.java @@ -0,0 +1,10 @@ +package org.cryptimeleon.craco.commitment; + +import org.cryptimeleon.math.hash.UniqueByteRepresentable; +import org.cryptimeleon.math.serialization.Representable; + +/** + * Commitment key used to commit to messages + */ +public interface CommitmentKey extends Representable, UniqueByteRepresentable { +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java new file mode 100644 index 00000000..deae9c11 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java @@ -0,0 +1,81 @@ +package org.cryptimeleon.craco.sig.sps; + +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; + +/** + * Interface to define a message check for SPSSchemes. + * Provides a default implementation for schemes that simply sign a vector of group elements. + * May be overridden for schemes with more complicated message spaces + * + */ +public interface SPSMessageSpaceVerifier { + + /** + * Checks if the given plainText matches the structure expected by the scheme + * and throws detailed exception if the plainText fails any check. + * + * For this default implementation, the following properties of the parameter {@param plainText} are checked: + * * {@param plainText} is of type {@link org.cryptimeleon.craco.common.plaintexts.MessageBlock}. + * * The amount of PlainTexts matches {@param expectedMessageLength}. + * * The elements stored in {@param plainText} are of type {@link GroupElementPlainText}. + * * The elements stored in said {@link GroupElementPlainText}s are \in {@param expectedGroup} + * + * */ + default void doMessageChecks(PlainText plainText, int expectedMessageLength, Group expectedGroup) + throws IllegalArgumentException{ + + MessageBlock messageBlock; + + // The scheme expects a MessageBlock... + if(plainText instanceof MessageBlock) { + messageBlock = (MessageBlock) plainText; + } + else { + throw new IllegalArgumentException("The scheme requires its messages to a MessageBlock"); + } + + // ... with a size that matches the expected size... + if(messageBlock.length() != expectedMessageLength) { + throw new IllegalArgumentException(String.format( + "The scheme expected a message of length %d, but the size was: %d", + expectedMessageLength, messageBlock.length() + )); + } + + // ...where each message element... + for (int i = 0; i < messageBlock.length(); i++) { + + // ...is a group element... + if(!(messageBlock.get(i) instanceof GroupElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The scheme expected its Messages to contain GroupElements," + + " but element %d was of type: %s", + i, messageBlock.get(i).getClass().toString() + ) + ); + } + + // ...in the expected group. + GroupElementPlainText group1ElementPT = (GroupElementPlainText) messageBlock.get(i); + + if(!(group1ElementPT.get().getStructure().equals(expectedGroup))) { + throw new IllegalArgumentException( + String.format( + "The scheme expected GroupElements in %s," + + " but element %d was in: %s", + expectedGroup.toString(), + i, + group1ElementPT.get().getStructure().toString() + ) + ); + } + } + + // if no exception has been thrown at this point, we can assume the message matches the expected structure. + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParameters.java new file mode 100644 index 00000000..c7fe08b4 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParameters.java @@ -0,0 +1,107 @@ +package org.cryptimeleon.craco.sig.sps; + +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Objects; + +/** + * An interface containing generic components shared by most SPS schemes + * i.e. a bilinear group for evaluating pairing product equations and the associated + * group generators + * */ +public class SPSPublicParameters implements PublicParameters { + + /** + * The bilinear group containing map e in the paper. + */ + @Represented + protected BilinearGroup bilinearGroup; // G1 x G2 -> GT + + /** + * G \in G_1 in paper. + */ + @Represented(restorer = "bilinearGroup::getG1") + protected GroupElement group1ElementG; + + /** + * H \in G_2 in paper. + */ + @Represented(restorer = "bilinearGroup::getG2") + protected GroupElement group2ElementH; + + /** + * This constructor is required in order to allow deserialization of classes extending {@link SPSPublicParameters} + * */ + public SPSPublicParameters() { super(); } + + public SPSPublicParameters(BilinearGroup bilinearGroup) { + super(); + this.bilinearGroup = bilinearGroup; + this.group1ElementG = this.bilinearGroup.getG1().getUniformlyRandomNonNeutral(); + this.group2ElementH = this.bilinearGroup.getG2().getUniformlyRandomNonNeutral(); + + precompute(); + } + + public SPSPublicParameters(Representation repr) { + new ReprUtil(this).deserialize(repr); + } + + + /** + * Returns the group Zp (where p is the group order of G1, G2, and GT) + */ + public Zp getZp() { + return new Zp(bilinearGroup.getG1().size()); + } + + public GroupElement getG1GroupGenerator(){ + return group1ElementG; + } + + public GroupElement getG2GroupGenerator(){ + return group2ElementH; + } + + public BilinearMap getBilinearMap(){ return bilinearGroup.getBilinearMap(); } + + public Group getGT() {return bilinearGroup.getGT(); } + + /** + * precomputes the group elements of the public parameters. + */ + private void precompute() { + this.group1ElementG.precomputePow(); + this.group2ElementH.precomputePow(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (!(o instanceof SPSPublicParameters)) return false; + + SPSPublicParameters that = (SPSPublicParameters) o; + return Objects.equals(bilinearGroup, that.bilinearGroup) + && Objects.equals(group1ElementG, that.group1ElementG) + && Objects.equals(group2ElementH, that.group2ElementH); + } + + @Override + public int hashCode() { + return Objects.hash(bilinearGroup, group1ElementG, group2ElementH); + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParametersGen.java b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParametersGen.java new file mode 100644 index 00000000..a13d1226 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSPublicParametersGen.java @@ -0,0 +1,38 @@ +package org.cryptimeleon.craco.sig.sps; + +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.debug.DebugBilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.type3.bn.BarretoNaehrigBilinearGroup; + +import java.util.function.BiFunction; + +/** + * generates a set of generic public parameters to be used by SPS schemes. + * + */ +public class SPSPublicParametersGen { + + public static SPSPublicParameters generateParameters(int securityParameter, + boolean debugMode) { + BilinearGroup group; + if (debugMode) { + group = new DebugBilinearGroup(RandomGenerator.getRandomPrime(securityParameter), BilinearGroup.Type.TYPE_3); + } else { + group = new BarretoNaehrigBilinearGroup(securityParameter); + } + + return new SPSPublicParameters(group); + } + + /** + * generates a set of public parameters based on a pre-made {@link BilinearGroup}. + * This works for any subclass of SPSPublicParameters that provides a constructor that takes + * just a {@link BilinearGroup} as its parameter + */ + public static PPType generateParameters( + BiFunction constructor, BilinearGroup bGroup, int messageLength) { + return constructor.apply(bGroup, messageLength); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParameters.java new file mode 100644 index 00000000..6e331d2a --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParameters.java @@ -0,0 +1,67 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Class for the public parameters of the AGHO11 structure preserving signature scheme. + * Bilinear group type 3 + * + * + */ + +public class SPSAGHO11PublicParameters extends SPSPublicParameters { + + /** + * The number of expected G1/G2 elements per message respectively + * */ + @Represented(restorer = "[messageLengths]") + protected Integer[] messageLengths; + + + public SPSAGHO11PublicParameters(BilinearGroup bilinearGroup, Integer[] messageBlockLengths){ + // as SPSPublicParameters precompute G and H itself, we do not need to precompute here + super(bilinearGroup); + this.messageLengths = messageBlockLengths; + } + + public SPSAGHO11PublicParameters(Representation repr) + { + new ReprUtil(this).deserialize(repr); + } + + + public Integer[] getMessageLengths(){ return messageLengths; } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + SPSAGHO11PublicParameters that = (SPSAGHO11PublicParameters) o; + return Arrays.equals(messageLengths, that.messageLengths); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Arrays.hashCode(messageLengths); + return result; + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParametersGen.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParametersGen.java new file mode 100644 index 00000000..b4687705 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11PublicParametersGen.java @@ -0,0 +1,37 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.debug.DebugBilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.type3.bn.BarretoNaehrigBilinearGroup; + +import java.util.function.BiFunction; + + +public class SPSAGHO11PublicParametersGen { + /** + * @param securityParameter The security parameter. + * @param debugMode Enable debug mode (Makes the PPs insecure!). + * @param messageBlockLengths The k_M and k_N the instance is expected to sign + * @return The public parameters for the AGHO11 SPS scheme + */ + public static SPSAGHO11PublicParameters generatePublicParameters(int securityParameter, boolean debugMode, Integer[] messageBlockLengths) { + BilinearGroup group; + if (debugMode) { + group = new DebugBilinearGroup(RandomGenerator.getRandomPrime(securityParameter), BilinearGroup.Type.TYPE_3); + } else { + group = new BarretoNaehrigBilinearGroup(securityParameter); + } + + return new SPSAGHO11PublicParameters(group, messageBlockLengths); + } + + /** + * generates a set of {@code SPSAGHO11PublicParameters} based on a pre-made {@link BilinearGroup}. + */ + public static SPSAGHO11PublicParameters generateParameters(BilinearGroup bGroup, Integer[] messageBlockLengths) { + return new SPSAGHO11PublicParameters(bGroup, messageBlockLengths); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11Signature.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11Signature.java new file mode 100644 index 00000000..e9feaa5d --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11Signature.java @@ -0,0 +1,90 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.UniqueByteRepresentable; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.hash.annotations.UniqueByteRepresented; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +public class SPSAGHO11Signature implements Signature, UniqueByteRepresentable { + + /** + * First group element of the signature \in G_1. + */ + @UniqueByteRepresented + @Represented(restorer = "G1") + protected GroupElement group1ElementSigma1R; + + /** + * Second group element of the signature \in G_1. + */ + @UniqueByteRepresented + @Represented(restorer = "G1") + protected GroupElement group1ElementSigma2S; + + /** + * Third group element of the signature \in G_2. + */ + @UniqueByteRepresented + @Represented(restorer = "G2") + protected GroupElement group2ElementSigma3T; + + + public SPSAGHO11Signature(Representation repr, Group groupG1, Group groupG2) { + new ReprUtil(this).register(groupG1, "G1").register(groupG2, "G2"). deserialize(repr); + } + + public SPSAGHO11Signature(GroupElement group1ElementSigma1R, + GroupElement group1ElementSigma2S, + GroupElement group2ElementSigma3T) { + super(); + this.group1ElementSigma1R = group1ElementSigma1R; + this.group1ElementSigma2S = group1ElementSigma2S; + this.group2ElementSigma3T = group2ElementSigma3T; + } + + + public GroupElement getGroup1ElementSigma1R() { + return group1ElementSigma1R; + } + + public GroupElement getGroup1ElementSigma2S() { + return group1ElementSigma2S; + } + + public GroupElement getGroup2ElementSigma3T() { + return group2ElementSigma3T; + } + + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSAGHO11Signature that = (SPSAGHO11Signature) o; + return Objects.equals(group1ElementSigma1R, that.group1ElementSigma1R) + && Objects.equals(group1ElementSigma2S, that.group1ElementSigma2S) + && Objects.equals(group2ElementSigma3T, that.group2ElementSigma3T); + } + + @Override + public int hashCode() { + return Objects.hash(group1ElementSigma1R, group1ElementSigma2S, group2ElementSigma3T); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureScheme.java new file mode 100644 index 00000000..36f243d9 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureScheme.java @@ -0,0 +1,507 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.math.serialization.ListRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * An implementation of the scheme originally presented in [1] + * + * [1] Abe et. al.: Optimal Structure-Preserving Signatures in Asymmetric Bilinear Groups. + * CRYPTO 2011: Advances in Cryptology – CRYPTO 2011 pp. 649-666 + * https://www.iacr.org/archive/crypto2011/68410646/68410646.pdf + * + * Note: To ensure the scheme's security as described in [1], messages and keys will be padded to length + * for short messages (k_M = 0 or k_N \in {0,1}), such that at least one G_1 group element and + * at least two G_2 groupElements are signed. + * The messages are assumed to be composed of a tuple of two {@link MessageBlock}s; + * one containing {@link GroupElementPlainText}s of elements in G_1 and the other {@link GroupElementPlainText}s + * of elements in G_2. + * The message vectors are padded with the neutral element of they respective groups. + * The key generation function accounts for this edge-case automatically by calculating the extra elements + * that are required as a consequence of the padding. + * From a user perspective, any positive length tuple of two message-vectors (M \in G_1,N \in G_2) may be passed + * without the need for additional precautions. + * + */ +public class SPSAGHO11SignatureScheme implements MultiMessageStructurePreservingSignatureScheme { + + /** + * The public parameters used by the scheme + * */ + @Represented + protected SPSAGHO11PublicParameters pp; + + + protected SPSAGHO11SignatureScheme(){ super(); } + + public SPSAGHO11SignatureScheme(SPSAGHO11PublicParameters pp) { + super(); + this.pp = pp; + } + + public SPSAGHO11SignatureScheme(Representation repr) { + new ReprUtil(this).deserialize(repr); + } + + + @Override + public SignatureKeyPair generateKeyPair(int numberOfMessages) + { + return generateKeyPair(numberOfMessages, 0); + } + + /** + * Generates a key pair for signing n blocks of messages with {@code messageBlockLengths} + * with each signature. + * + * @param messageBlockLengths the length of the individual MessageBlocks this scheme accepts as input. + */ + public SignatureKeyPair generateKeyPair(int... messageBlockLengths) { + + Zp zp = pp.getZp(); + + if(!(messageBlockLengths.length == 2)){ + throw new IllegalArgumentException(String.format( + "The signature scheme AGHO11 expects to sign elements" + + " on two vectors G^M, H^N, but received: {0} vectors", messageBlockLengths.length) + ); + } + + for (int i = 0; i < messageBlockLengths.length; i++) { + if(!(messageBlockLengths[i] == pp.getMessageLengths()[i])){ + throw new IllegalArgumentException( + String.format( + "The given message length of the %s vector does not match the public parameters" + + " expected: %d, but was: %d", + (i == 0) ? "first" : "second", + pp.getMessageLengths()[i], + messageBlockLengths[i] + ) + ); + } + } + + // edge-case: if the expected messageBlockLengths are too short, add padding to the key + int firstMsgVectorLength = Math.max(1, messageBlockLengths[0]); // k_M will be padded to be at least 1 + int secondMsgVectorLength = Math.max(2, messageBlockLengths[1]); // k_N will be padded to be at least 2 + + ZpElement[] exponentsU = IntStream.range(0, secondMsgVectorLength).mapToObj( //note that u_1 ... u_k_N + x -> zp.getUniformlyRandomNonzeroElement()) + .toArray(ZpElement[]::new); + ZpElement[] exponentsW = IntStream.range(0, firstMsgVectorLength).mapToObj( //and that w_1 ... w_k_M + x -> zp.getUniformlyRandomNonzeroElement()) + .toArray(ZpElement[]::new); + + Zp.ZpElement exponentV = zp.getUniformlyRandomNonzeroElement(); + Zp.ZpElement exponentZ = zp.getUniformlyRandomNonzeroElement(); + + // Calculate Vectors for public key + GroupElement[] groupElementsU = Arrays.stream(exponentsU).map( + x -> pp.getG1GroupGenerator().pow(x).compute()) + .toArray(GroupElement[]::new); + GroupElement[] groupElementsW = Arrays.stream(exponentsW).map( + x -> pp.getG2GroupGenerator().pow(x).compute()) + .toArray(GroupElement[]::new); + + // Create public key (verification key) + SPSAGHO11VerificationKey pk = new SPSAGHO11VerificationKey( + groupElementsU, + pp.getG2GroupGenerator().pow(exponentV).compute(), + groupElementsW, + pp.getG2GroupGenerator().pow(exponentZ).compute() + ); + + // Create secret key (signing key) + SPSAGHO11SigningKey sk = new SPSAGHO11SigningKey(exponentsU, exponentV, exponentsW, exponentZ); + + return new SignatureKeyPair<>(pk, sk); + } + + @Override + public Signature sign(PlainText plainText, SigningKey secretKey) { + + // check if the plainText matches the expected message structure + // the scheme signs messages on G^(k_M) x H^(k_N), so we need a MessageBlock containing 2 MessageBlocks + doMessageChecks(plainText); + + if(!(secretKey.getClass() == SPSAGHO11SigningKey.class)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + MessageBlock containerBlock = (MessageBlock) plainText; + containerBlock = padMessageIfShort(containerBlock); // pad message if necessary + + MessageBlock messageGElements = (MessageBlock) containerBlock.get(0); + MessageBlock messageHElements = (MessageBlock) containerBlock.get(1); + + int k_M = messageGElements.length(); + int k_N = messageHElements.length(); + + //cast signing key + SPSAGHO11SigningKey sk = (SPSAGHO11SigningKey) secretKey; + + //pick randomness r \in Z*_p + ZpElement r = pp.getZp().getUniformlyRandomNonzeroElement(); + + //calculate signature components + GroupElement sigma1R = pp.getG1GroupGenerator().pow(r).compute(); + + // sub is actually needed here + GroupElement sigma2S1 = pp.getG1GroupGenerator().pow(sk.getExponentZ().sub(r.mul(sk.getExponentV()))); + + GroupElement sigma2S2 = pp.getG1GroupGenerator().getStructure().getNeutralElement(); + + for (int i = 0; i < k_M; i++) { + sigma2S2 = sigma2S2.op( + ((GroupElementPlainText) messageGElements.get(i)).get() // M_i + .pow(sk.getExponentsW()[i].neg()) // ^(-w_i) + ); + } + + GroupElement sigma2S = sigma2S1.op(sigma2S2).compute(); + + GroupElement sigma3T = pp.getG2GroupGenerator().getStructure().getNeutralElement(); + for (int i = 0; i < k_N; i++) { + sigma3T = sigma3T.op( + ((GroupElementPlainText) messageHElements.get(i)).get() // N_i + .pow(sk.getExponentsU()[i].neg()) // ^(-u_i) + ); + } + sigma3T = sigma3T.op(pp.getG2GroupGenerator()); // * H + sigma3T = sigma3T.pow(r.inv()).compute(); // ^1/r + + return new SPSAGHO11Signature(sigma1R, sigma2S, sigma3T); + } + + @Override + public Boolean verify(PlainText plainText, Signature signature, VerificationKey publicKey) { + + // check if the plainText matches the expected message structure + // the scheme signs messages on G^(k_M) x H^(k_N), so we need a MessageBlock containing 2 MessageBlocks + doMessageChecks(plainText); + + if(!(signature.getClass() == SPSAGHO11Signature.class)){ + throw new IllegalArgumentException("Not a valid signature for this scheme"); + } + + if(!(publicKey.getClass() == SPSAGHO11VerificationKey.class)){ + throw new IllegalArgumentException("Not a valid verification key for this scheme"); + } + + MessageBlock containerBlock = (MessageBlock) plainText; + containerBlock = padMessageIfShort(containerBlock); // pad message if necessary + + MessageBlock messageGElements = (MessageBlock) containerBlock.get(0); + MessageBlock messageHElements = (MessageBlock) containerBlock.get(1); + + + SPSAGHO11Signature sigma = (SPSAGHO11Signature) signature; + SPSAGHO11VerificationKey pk = (SPSAGHO11VerificationKey) publicKey; + + return evaluateFirstPPE(messageGElements, sigma, pk) + && evaluateSecondPPE(messageHElements, sigma, pk); + } + + /** + * Checks if the given combination of message, key and signature create a valid pairing product equation + * in regard to the scheme's first PPE defined in the paper. + */ + private boolean evaluateFirstPPE(MessageBlock messageBlock, SPSAGHO11Signature sigma, SPSAGHO11VerificationKey pk){ + + BilinearMap bMap = pp.getBilinearMap(); + + //left-hand side + GroupElement lhs1 = bMap.apply(sigma.getGroup1ElementSigma1R(), pk.getGroup2ElementV()); + lhs1 = lhs1.op(bMap.apply(sigma.getGroup1ElementSigma2S(), pp.getG2GroupGenerator())); + + GroupElement lhs2 = pp.getGT().getNeutralElement(); + + for (int i = 0; i < messageBlock.length(); i++) { + lhs2 = lhs2.op( + bMap.apply(((GroupElementPlainText)messageBlock.get(i)).get(), pk.getGroup2ElementsW()[i]) + ); + } + + GroupElement lhs = lhs1.op(lhs2); + lhs.compute(); + + // right-hand side + GroupElement rhs = bMap.apply(pp.getG1GroupGenerator(), pk.getGroup2ElementZ()); + rhs.compute(); + + return lhs.equals(rhs); + } + + /** + * Checks if the given combination of message, key and signature create a valid pairing product equation + * in regard to the scheme's second PPE defined in the paper. + */ + private boolean evaluateSecondPPE(MessageBlock messageBlock, SPSAGHO11Signature sigma, SPSAGHO11VerificationKey pk){ + + BilinearMap bMap = pp.getBilinearMap(); + + //left-hand side + GroupElement lhs1 = bMap.apply(sigma.getGroup1ElementSigma1R(), sigma.getGroup2ElementSigma3T()); + + GroupElement lhs2 = pp.getGT().getNeutralElement(); + + for (int i = 0; i < messageBlock.length(); i++) { + lhs2 = lhs2.op( + bMap.apply(pk.getGroup1ElementsU()[i], ((GroupElementPlainText)messageBlock.get(i)).get()) + ); + } + + GroupElement lhs = lhs1.op(lhs2).compute(); + + //right-hand side + GroupElement rhs = bMap.apply(pp.getG1GroupGenerator(), pp.getG2GroupGenerator()).compute(); + + return lhs.equals(rhs); + } + + @Override + public PlainText restorePlainText(Representation repr) { + // interpret repr as list of to message block representations (one for G1 elements and one for G2 elements) + ListRepresentation list = (ListRepresentation) repr; + + Representation g1Elements = (Representation) list.get(0); + Representation g2Elements = (Representation) list.get(1); + + // pull the actual group elements from the representations + MessageBlock g1 = new MessageBlock( + g1Elements, r -> new GroupElementPlainText(r, pp.getG1GroupGenerator().getStructure()) + ); + MessageBlock g2 = new MessageBlock( + g2Elements, r -> new GroupElementPlainText(r, pp.getG2GroupGenerator().getStructure()) + ); + + return new MessageBlock(g1, g2); + } + + @Override + public Signature restoreSignature(Representation repr) { + return new SPSAGHO11Signature(repr + , this.pp.getG1GroupGenerator().getStructure() + , this.pp.getG2GroupGenerator().getStructure()); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new SPSAGHO11SigningKey(repr, this.pp.getZp()); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new SPSAGHO11VerificationKey(this.pp.getG1GroupGenerator().getStructure(), + this.pp.getG2GroupGenerator().getStructure(), + repr); + } + + + @Override + public MessageBlock mapToPlaintext(byte[] bytes, VerificationKey pk) { + if(pp == null){ + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLengths()[0]); + } + + @Override + public MessageBlock mapToPlaintext(byte[] bytes, SigningKey sk) { + if(pp == null){ + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLengths()[0]); + } + + private MessageBlock mapToPlaintext(byte[] bytes, int messageBlockLength){ + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + // if the scheme uses zero length messages for G1, messages will be padded to at least 1 element + if(messageBlockLength == 0) { + messageBlockLength = 1; + } + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[messageBlockLength]; + msgBlock[0] = new GroupElementPlainText( + pp.getG1GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + + for (int i = 1; i < msgBlock.length; i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG1GroupGenerator()); + } + + return new MessageBlock(new MessageBlock(msgBlock), new MessageBlock()); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + + if(pp == null){ + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSAGHO11SignatureScheme that = (SPSAGHO11SignatureScheme) o; + return Objects.equals(pp, that.pp); + } + + @Override + public int hashCode() { + return Objects.hash(pp); + } + + /** + * Checks if the given {@link PlainText} matches the structure expected by the scheme and + * throws detailed exception if the plainText fails any check. + * The scheme expects a {@link MessageBlock} with two inner {@link MessageBlock}s, each holding + * {@link GroupElementPlainText}s in G1 and G2 respectively. + */ + private void doMessageChecks(PlainText plainText) throws IllegalArgumentException{ + + // check if the plainText is a MessageBlock... + if(!(plainText instanceof MessageBlock)) { + throw new IllegalArgumentException( + String.format("The plainText provided must be a MessageBlock, but was: %s", + plainText.getClass().toString() + ) + ); + } + + MessageBlock msgBlock = (MessageBlock) plainText; + + // ...with two inner elements... + if(msgBlock.length() != 2) { + throw new IllegalArgumentException( + String.format("The message provided must contain 2 inner MessageBlocks, but had: %d", + msgBlock.length() + ) + ); + } + + // ...that are messageBlocks... + for (int i = 0; i < 2; i++) { + if(!(msgBlock.get(i) instanceof MessageBlock)) { + throw new IllegalArgumentException( + String.format( + "The message provided must contain 2 inner MessageBlocks," + + " but element %d was not an instance of MessageBlock", i)); + } + } + + // test both inner message blocks + MessageBlock innerBlock1 = (MessageBlock) msgBlock.get(0); + MessageBlock innerBlock2 = (MessageBlock) msgBlock.get(1); + + for (int blockID = 0; blockID < 2; blockID++) { + MessageBlock innerBlock = (blockID == 0) ? innerBlock1 : innerBlock2; + int expectedLength = pp.messageLengths[blockID]; + Group expectedGroup = + (blockID == 0) ? pp.getG1GroupGenerator().getStructure() : pp.getG2GroupGenerator().getStructure(); + + // ...whose lengths match those defined in the public parameters... + if(innerBlock.length() != expectedLength) { + throw new IllegalArgumentException( + String.format( + "length of %s message vector does not match public parameters" + + " expected %d, but was: %d", + (blockID == 0) ? "first" : "second", + innerBlock1.length(), pp.messageLengths[0] + ) + ); + } + + // ...and hold GroupElementPlainTexts in G1 and G2 respectively... + for (int i = 0; i < innerBlock.length(); i++) { + if(!(innerBlock.get(i) instanceof GroupElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The inner message blocks may only contain GroupElementPlainTexts," + + " but element %d of inner block %d was of type: %s", + i, + blockID, + innerBlock.get(i).getClass().toString() + ) + ); + } + + GroupElement groupElement = ((GroupElementPlainText)innerBlock.get(i)).get(); + + if(!(groupElement.getStructure().equals(expectedGroup))) { + throw new IllegalArgumentException( + String.format( + "Element %d of inner message block %d does not match the expected group. " + + " expected: %s, but was: %s", + i, + blockID, + groupElement.getStructure().toString(), + expectedGroup.toString() + ) + ); + } + } + } + + // if no exception has been thrown at this point, we can assume the message matches the expected structure. + } + + /** + * Pads a given {@link MessageBlock} in case either of its inner MessageBlocks is too short. + * k_M (the first inner Block) must be at least 1 element long + * k_N (the second inner Block) must be at least 2 elements long + */ + private MessageBlock padMessageIfShort(MessageBlock messageBlock) { + + // at this point we assume the message is of the correct structure, as per {@code doMessageChecks()} + + MessageBlock firstInnerBlock = (MessageBlock) messageBlock.get(0); + MessageBlock secondInnerBlock = (MessageBlock) messageBlock.get(1); + + GroupElement g1Neutral = pp.getG1GroupGenerator().getStructure().getNeutralElement(); + GroupElement g2Neutral = pp.getG2GroupGenerator().getStructure().getNeutralElement(); + + //pad messages only if needed + if(pp.getMessageLengths()[0] < 1) { + firstInnerBlock = new MessageBlock( + firstInnerBlock.pad(new GroupElementPlainText(g1Neutral), 1) + ); + } + + if(pp.getMessageLengths()[1] < 2) { + secondInnerBlock = new MessageBlock( + secondInnerBlock.pad(new GroupElementPlainText(g2Neutral), 2) + ); + } + + return new MessageBlock(firstInnerBlock, secondInnerBlock); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SigningKey.java new file mode 100644 index 00000000..d249247d --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SigningKey.java @@ -0,0 +1,101 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + + +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.rings.cartesian.RingElementVector; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Class for the secret (signing) key of the AGHO11 signature scheme. + * + * + */ + +public class SPSAGHO11SigningKey implements SigningKey { + + /** + * u_1, ..., u_k_N in the paper + */ + @Represented(restorer = "[Zp]") + protected Zp.ZpElement exponentsU[]; + + /** + * v in the paper + */ + @Represented(restorer = "Zp") + protected Zp.ZpElement exponentV; + + /** + * w_1, ..., w_k_M in the paper + */ + @Represented(restorer = "[Zp]") + protected Zp.ZpElement exponentsW[]; + + /** + * z in the paper + */ + @Represented(restorer = "Zp") + protected Zp.ZpElement exponentZ; + + + public SPSAGHO11SigningKey() { super(); } + + public SPSAGHO11SigningKey(Representation representation, Zp zp){ + new ReprUtil(this).register(zp, "Zp").deserialize(representation); + } + + public SPSAGHO11SigningKey(Zp.ZpElement[] exponentsU, Zp.ZpElement exponentV, + Zp.ZpElement[] exponentsW, Zp.ZpElement exponentZ){ + super(); + this.exponentsU = exponentsU; + this.exponentV = exponentV; + this.exponentsW = exponentsW; + this.exponentZ = exponentZ; + } + + + public Zp.ZpElement[] getExponentsU() { + return exponentsU; + } + + public Zp.ZpElement getExponentV() { + return exponentV; + } + + public Zp.ZpElement[] getExponentsW() { + return exponentsW; + } + + public Zp.ZpElement getExponentZ() { + return exponentZ; + } + + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSAGHO11SigningKey that = (SPSAGHO11SigningKey) o; + return Arrays.equals(exponentsU, that.exponentsU) + && Objects.equals(exponentV, that.exponentV) + && Arrays.equals(exponentsW, that.exponentsW) + && Objects.equals(exponentZ, that.exponentZ); + } + + @Override + public int hashCode() { + return Objects.hash(exponentsU, exponentV, exponentsW, exponentZ); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11VerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11VerificationKey.java new file mode 100644 index 00000000..0f700ed1 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11VerificationKey.java @@ -0,0 +1,99 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; + + +/** + * Class for the public (verification) key of the AGHO11 signature scheme. + * + */ + +public class SPSAGHO11VerificationKey implements VerificationKey { + + /* Note that the generation key GK is included in the verification key in the paper. + it has been moved to the public parameters here to avoid redundancy */ + + /** + * U_1, ..., U_k_N \in G_1 in the paper. + */ + @Represented(restorer = "[G1]") + protected GroupElement[] group1ElementsU; + + /** + * V \in G_2 in the paper. + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV; + + /** + * W_1, ..., W_k_M \in G_2 in the paper. + */ + @Represented(restorer = "[G2]") + protected GroupElement[] group2ElementsW; + + /** + * Z \in G_2 in the paper. + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementZ; + + + public SPSAGHO11VerificationKey() { super(); } + + public SPSAGHO11VerificationKey(GroupElement[] group1ElementsU, + GroupElement group2ElementV, + GroupElement[] group2ElementsW, + GroupElement group2ElementZ) { + this.group1ElementsU = group1ElementsU; + this.group2ElementV = group2ElementV; + this.group2ElementsW = group2ElementsW; + this.group2ElementZ = group2ElementZ; + } + + public SPSAGHO11VerificationKey(Group G_1, Group G_2, Representation repr) { + new ReprUtil(this).register(G_1, "G1").register(G_2, "G2").deserialize(repr); + } + + + public GroupElement[] getGroup1ElementsU() { + return group1ElementsU; + } + + public GroupElement getGroup2ElementV() { + return group2ElementV; + } + + public GroupElement[] getGroup2ElementsW() { + return group2ElementsW; + } + + public GroupElement getGroup2ElementZ() { + return group2ElementZ; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SPSAGHO11VerificationKey that = (SPSAGHO11VerificationKey) o; + return Arrays.equals(group1ElementsU, that.group1ElementsU) + && Arrays.equals(group2ElementsW, that.group2ElementsW) + && Objects.equals(group2ElementV, that.group2ElementV) + && Objects.equals(group2ElementZ, that.group2ElementZ); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/package-info.java new file mode 100644 index 00000000..16d031ab --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/agho11/package-info.java @@ -0,0 +1,5 @@ +/** + * Contains an implementation of the structure-preserving signature scheme of Abe et. al., 2011. + */ + +package org.cryptimeleon.craco.sig.sps.agho11; \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParameters.java new file mode 100644 index 00000000..6052e972 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParameters.java @@ -0,0 +1,100 @@ +package org.cryptimeleon.craco.sig.sps.akot15; + +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Objects; + +/** + * The construction of the AKOT15 signature scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme} requires the {@link PublicParameters} + * to match up across building blocks. In order to simplify interactions between the schemes, + * this class holds these shared public parameters. + * + */ +public class AKOT15SharedPublicParameters extends SPSPublicParameters implements Cloneable { + + @Represented + protected Integer messageLength; + + + public AKOT15SharedPublicParameters(BilinearGroup bilinearGroup, int messageLength) { + // as SPSPublicParameters precompute G and H itself, we do not need to precompute here + super(bilinearGroup); + this.bilinearGroup = bilinearGroup; + this.messageLength = messageLength; + + this.group1ElementG = this.bilinearGroup.getG1().getUniformlyRandomNonNeutral(); + this.group2ElementH = this.bilinearGroup.getG2().getUniformlyRandomNonNeutral(); + } + + public AKOT15SharedPublicParameters(BilinearGroup bilinearGroup, + int messageLength, + GroupElement group1ElementG, + GroupElement group2ElementH) { + // as SPSPublicParameters precompute G and H itself, we do not need to precompute here + super(bilinearGroup); + this.bilinearGroup = bilinearGroup; + this.messageLength = messageLength; + + this.group1ElementG = group1ElementG; + this.group2ElementH = group2ElementH; + } + + public AKOT15SharedPublicParameters(Representation repr) { + super(repr); + } + + + public BilinearGroup getBilinearGroup() { + return bilinearGroup; + } + + public BilinearMap getBilinearMap(){ return bilinearGroup.getBilinearMap(); } + + public Integer getMessageLength() { + return messageLength; + } + + public void setMessageLength(int messageLength) { + this.messageLength = messageLength; + } + + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + @Override + public AKOT15SharedPublicParameters clone() { + + AKOT15SharedPublicParameters clone = new AKOT15SharedPublicParameters( + this.bilinearGroup, this.messageLength, this.group1ElementG, group2ElementH + ); + + return clone; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + AKOT15SharedPublicParameters that = (AKOT15SharedPublicParameters) o; + return Objects.equals(messageLength, that.messageLength); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), messageLength); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParametersGen.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParametersGen.java new file mode 100644 index 00000000..b378dfe1 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/AKOT15SharedPublicParametersGen.java @@ -0,0 +1,28 @@ +package org.cryptimeleon.craco.sig.sps.akot15; + +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.debug.DebugBilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.type3.bn.BarretoNaehrigBilinearGroup; + +/** + * generates a set of public parameters to be used by the schemes: + * {@link org.cryptimeleon.craco.sig.sps.akot15.pos.SPSPOSSignatureScheme}, + * {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme}, + * {@link org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15CommitmentScheme} + * + */ +public class AKOT15SharedPublicParametersGen { + + public static AKOT15SharedPublicParameters generateParameters(int securityParameter, int numberOfMessages, boolean debugMode) { + BilinearGroup group; + if (debugMode) { + group = new DebugBilinearGroup(RandomGenerator.getRandomPrime(securityParameter), BilinearGroup.Type.TYPE_3); + } else { + group = new BarretoNaehrigBilinearGroup(securityParameter); + } + + return new AKOT15SharedPublicParameters(group, numberOfMessages); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2Signature.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2Signature.java new file mode 100644 index 00000000..9ea6dc85 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2Signature.java @@ -0,0 +1,100 @@ +package org.cryptimeleon.craco.sig.sps.akot15.fsp2; + +import org.cryptimeleon.craco.commitment.CommitmentPair; +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15OpenValue; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15Commitment; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15XSIGCommitment; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignature; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.structures.groups.Group; + +import java.util.Objects; + +/** + * A signature as generated by the fully SPS scheme {@link SPSFSP2Signature}. + * + */ +public class SPSFSP2Signature implements Signature { + + /** + * The signature the building block {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * generated. + */ + SPSXSIGSignature sigmaXSIG; + + /** + * The commitment the building block {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme} + * generated. + */ + CommitmentPair commitmentPairTC; + + + public SPSFSP2Signature(SPSXSIGSignature sigmaXSIG, CommitmentPair commitmentPairTC) { + this.sigmaXSIG = sigmaXSIG; + this.commitmentPairTC = commitmentPairTC; + } + + public SPSFSP2Signature(Group g1, Group g2, Representation repr) { + ObjectRepresentation objRepr = (ObjectRepresentation) repr; + + this.sigmaXSIG = new SPSXSIGSignature(objRepr.get("sigma"), g1, g2); + + TCGAKOT15Commitment com; + + // if the representation contains an instance of TCGAKOT15XSIGCommitment, use that + if(objRepr.get("comXSIG") != null) { + com = new TCGAKOT15XSIGCommitment(g2, objRepr.get("comXSIG")); + } + else { + com = new TCGAKOT15Commitment(g2, objRepr.get("com")); + } + TCAKOT15OpenValue open = new TCAKOT15OpenValue(g1, g2, objRepr.get("open")); + + this.commitmentPairTC = new CommitmentPair(com, open); + } + + + @Override + public Representation getRepresentation() { + ObjectRepresentation objRepr = new ObjectRepresentation(); + + objRepr.put("sigma", sigmaXSIG.getRepresentation()); + + //if special XSIG variant is used, put that instead + if(commitmentPairTC.getCommitment() instanceof TCGAKOT15XSIGCommitment) { + objRepr.put("comXSIG", commitmentPairTC.getCommitment().getRepresentation()); + } + else { + objRepr.put("com", commitmentPairTC.getCommitment().getRepresentation()); + } + objRepr.put("open", commitmentPairTC.getOpenValue().getRepresentation()); + + return objRepr; + } + + public SPSXSIGSignature getSigmaXSIG() { + return sigmaXSIG; + } + + public CommitmentPair getCommitmentPairTC() { + return commitmentPairTC; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSFSP2Signature that = (SPSFSP2Signature) o; + return Objects.equals(sigmaXSIG, that.sigmaXSIG) + && Objects.equals(commitmentPairTC.getOpenValue(), that.commitmentPairTC.getOpenValue()) + && Objects.equals(commitmentPairTC.getCommitment(), that.commitmentPairTC.getCommitment()); + } + + @Override + public int hashCode() { + return Objects.hash(sigmaXSIG, commitmentPairTC.getOpenValue(), commitmentPairTC.getOpenValue()); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2SignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2SignatureScheme.java new file mode 100644 index 00000000..a5251be2 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2SignatureScheme.java @@ -0,0 +1,238 @@ +package org.cryptimeleon.craco.sig.sps.akot15.fsp2; + +import org.cryptimeleon.craco.commitment.Commitment; +import org.cryptimeleon.craco.commitment.CommitmentPair; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.pos.SPSPOSSignatureScheme; +import org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.*; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.*; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; + +import java.util.Objects; + +/** + * An implementation of the fully structure preserving signature scheme FSP2 presented in [1] + * The scheme is composed of several building blocks. + * + * + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ +public class SPSFSP2SignatureScheme implements MultiMessageStructurePreservingSignatureScheme { + + /** + * The public parameters used by the scheme + */ + @Represented + AKOT15SharedPublicParameters pp; + + /** + * An instance of {@link SPSXSIGSignatureScheme} used for signature calculation + */ + @Represented + SPSXSIGSignatureScheme xsigInstance; + + /** + * An instance of {@link TCAKOT15CommitmentScheme} used for signature calculation + */ + @Represented + TCAKOT15CommitmentScheme tcInstance; + + + public SPSFSP2SignatureScheme(AKOT15SharedPublicParameters pp) { + this.pp = pp; + + //instantiate nested building blocks + //Note: for this scheme to work, the public parameters must be set up in a very particular way: + + // first, calculate the public parameters for XSIG, making sure that G,H^{tilde} match this schemes G,H^{tilde} + // the message length must be set to one, as XSIG will not sign the messages of FSP2 directly, + // but rather the commitment generated by TC + SPSXSIGPublicParameters ppXSIG = new SPSXSIGPublicParameters(pp, 1); + + // set up the PublicParameters for TC based on those calculated by XSIG + // as tc_gamma will sign the verification key of posInstance, its expected messages are 2 elements longer + TCGAKOT15XSIGPublicParameters ppTC = new TCGAKOT15XSIGPublicParameters( + ppXSIG, + pp.getMessageLength() + 2 + ); + + xsigInstance = new SPSXSIGSignatureScheme(ppXSIG); + tcInstance = new TCAKOT15CommitmentScheme(pp, ppTC); + } + + public SPSFSP2SignatureScheme(Representation repr) { + new ReprUtil(this).deserialize(repr); + } + + @Override + public SignatureKeyPair generateKeyPair(int numberOfMessages) { + + // generate keys via building blocks + SignatureKeyPair key_xsig = + xsigInstance.generateKeyPair(xsigInstance.getPublicParameters().getMessageLength()); + TCGAKOT15CommitmentKey key_tc = tcInstance.generateKey(); + + return new SignatureKeyPair<>(new SPSFSP2VerificationKey(key_xsig.getVerificationKey(), key_tc), key_xsig.getSigningKey()); + } + + @Override + public Signature sign(PlainText plainText, SigningKey secretKey) { + + if(!(secretKey instanceof SPSXSIGSigningKey)) { + throw new IllegalArgumentException("this is not a valid signing key for this scheme"); + } + + if((plainText instanceof GroupElementPlainText)) { + plainText = new MessageBlock(plainText); + } + + if(!(plainText instanceof MessageBlock)) { + throw new IllegalArgumentException("this is not a valid message for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + SPSXSIGSigningKey sk = (SPSXSIGSigningKey) secretKey; + + CommitmentPair tcCommitmentPair = tcInstance.commit(messageBlock); + + // as defined by the public parameters, TCG returns a special variant of the commitment that includes the + // 2 additional values needed by XSIG + TCGAKOT15XSIGCommitment com = ((TCGAKOT15XSIGCommitment)tcCommitmentPair.getCommitment()); + + SPSXSIGSignature sigma = (SPSXSIGSignature) xsigInstance.sign(sk, com.toMessageBlock()); + + return new SPSFSP2Signature(sigma, tcCommitmentPair); + } + + @Override + public Boolean verify(PlainText plainText, Signature signature, VerificationKey publicKey) { + + if((plainText instanceof GroupElementPlainText)) { + plainText = new MessageBlock(plainText); + } + + if(!(plainText instanceof MessageBlock)) { + throw new IllegalArgumentException("this is not a valid message for this scheme"); + } + + if(!(publicKey instanceof SPSFSP2VerificationKey)) { + throw new IllegalArgumentException("this is not a valid verification key for this scheme"); + } + + if(!(signature instanceof SPSFSP2Signature)) { + throw new IllegalArgumentException("this is not a valid signature for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + SPSFSP2VerificationKey vk = (SPSFSP2VerificationKey) publicKey; + SPSFSP2Signature sigma = (SPSFSP2Signature) signature; + + CommitmentPair commitmentPair = sigma.getCommitmentPairTC(); + + return xsigInstance.verify(((TCGAKOT15XSIGCommitment)commitmentPair.getCommitment()).toMessageBlock(), sigma.getSigmaXSIG(), vk.getVkXSIG()) + && tcInstance.verify(sigma.getCommitmentPairTC().getCommitment(), sigma.getCommitmentPairTC().getOpenValue(), messageBlock); + } + + + @Override + public PlainText restorePlainText(Representation repr) { + // The message space for this scheme is a simple vector of GroupElements in G2 + return new MessageBlock(repr, r -> new GroupElementPlainText(r, pp.getG2GroupGenerator().getStructure())); + } + + @Override + public Signature restoreSignature(Representation repr) { + return new SPSFSP2Signature( + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure(), + repr); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new SPSXSIGSigningKey( + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure(), + repr); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new SPSFSP2VerificationKey( + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure(), + repr); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, VerificationKey pk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLength()); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, SigningKey sk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLength()); + } + + private MessageBlock mapToPlaintext(byte[] bytes, int messageBlockLength) { + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[messageBlockLength]; + msgBlock[0] = new GroupElementPlainText( + pp.getG1GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + + for (int i = 1; i < msgBlock.length; i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG1GroupGenerator()); + } + + return new MessageBlock(new MessageBlock(msgBlock), new MessageBlock()); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSFSP2SignatureScheme that = (SPSFSP2SignatureScheme) o; + return Objects.equals(pp, that.pp) + && Objects.equals(xsigInstance, that.xsigInstance) + && Objects.equals(tcInstance, that.tcInstance); + } + + @Override + public int hashCode() { + return Objects.hash(pp, xsigInstance, tcInstance); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2VerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2VerificationKey.java new file mode 100644 index 00000000..fc17a14e --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/fsp2/SPSFSP2VerificationKey.java @@ -0,0 +1,78 @@ +package org.cryptimeleon.craco.sig.sps.akot15.fsp2; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15CommitmentKey; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15XSIGCommitmentKey; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGVerificationKey; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; + +import java.util.Objects; + +/** + * A verification key as generated by the {@link SPSFSP2SignatureScheme}. + * + */ +public class SPSFSP2VerificationKey implements VerificationKey { + + /** + * The verification key used by the building block + * {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + */ + @Represented + protected SPSXSIGVerificationKey vkXSIG; + + /** + * The commitment key used by the building block + * {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme} + */ + @Represented + protected TCGAKOT15CommitmentKey ckTC; + + + public SPSFSP2VerificationKey(SPSXSIGVerificationKey vkXSIG, TCGAKOT15CommitmentKey ckTC) { + this.vkXSIG = vkXSIG; + this.ckTC = ckTC; + } + + public SPSFSP2VerificationKey(Group g1, Group g2, Representation repr) { + + ObjectRepresentation objRepr = (ObjectRepresentation) repr; + + this.vkXSIG = new SPSXSIGVerificationKey(g1,g2, objRepr.get("vkXSIG")); + this.ckTC = new TCGAKOT15XSIGCommitmentKey(g2, objRepr.get("ckTC")); + } + + + @Override + public Representation getRepresentation() { + + ObjectRepresentation objRepr = new ObjectRepresentation(); + + objRepr.put("vkXSIG", vkXSIG.getRepresentation()); + objRepr.put("ckTC", ckTC.getRepresentation()); + + return objRepr; + } + + public SPSXSIGVerificationKey getVkXSIG() { + return vkXSIG; + } + + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSFSP2VerificationKey that = (SPSFSP2VerificationKey) o; + return Objects.equals(vkXSIG, that.vkXSIG) && Objects.equals(ckTC, that.ckTC); + } + + @Override + public int hashCode() { + return Objects.hash(vkXSIG, ckTC); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/package-info.java new file mode 100644 index 00000000..353ee725 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/package-info.java @@ -0,0 +1,7 @@ +package org.cryptimeleon.craco.sig.sps.akot15; + +/** + * An implementation of the SPS scheme FSP2 presented in Abe et al. + * Fully Structure-Preserving Signatures and Shrinking Commitments + * https://eprint.iacr.org/2015/076.pdf + * */ \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignature.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignature.java new file mode 100644 index 00000000..0564431d --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignature.java @@ -0,0 +1,77 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.UniqueByteRepresentable; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * A signature as generated by the partially one-time SPS scheme {@link SPSPOSSignatureScheme}. + * + */ +public class SPSPOSSignature implements Signature, UniqueByteRepresentable { + + /** + * Z^{tilde} \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementZ; + + /** + * R^{tilde} \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementR; + + + public SPSPOSSignature(Representation repr, Group groupG2) { + new ReprUtil(this).register(groupG2, "G2").deserialize(repr); + } + + public SPSPOSSignature(GroupElement group2ElementZ, GroupElement group2ElementR) { + this.group2ElementR = group2ElementR; + this.group2ElementZ = group2ElementZ; + } + + + public GroupElement getGroup2ElementZ() { + return group2ElementZ; + } + + public GroupElement getGroup2ElementR() { + return group2ElementR; + } + + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSPOSSignature that = (SPSPOSSignature) o; + return Objects.equals(group2ElementZ, that.group2ElementZ) && Objects.equals(group2ElementR, that.group2ElementR); + } + + @Override + public int hashCode() { + return Objects.hash(group2ElementZ, group2ElementR); + } + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java new file mode 100644 index 00000000..cac3c81d --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java @@ -0,0 +1,298 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.craco.sig.sps.SPSMessageSpaceVerifier; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * An implementation of the partially one-time SPS scheme presented in [1] + * While the scheme is intended to be a building block of the larger SPS scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, + * the implementation can be used on its own, where it is one-time CMA secure + * under the Double Pairing assumption as defined in [1]. + * + * + * Note: The calculation of the commitments differs slightly when the scheme is used in the context of + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}: + * As the scheme combines {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme} -- which is + * based on this scheme -- with {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme}, + * the scheme must calculate 2 additional elements for its commitments (with are then signed by XSIG). + * + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ +public class SPSPOSSignatureScheme implements MultiMessageStructurePreservingSignatureScheme, SPSMessageSpaceVerifier { + + /** + * the public parameters for this scheme + */ + @Represented + private AKOT15SharedPublicParameters pp; + + + public SPSPOSSignatureScheme(AKOT15SharedPublicParameters pp) { + super(); + this.pp = pp; + } + + public SPSPOSSignatureScheme(Representation repr) { + new ReprUtil(this).deserialize(repr); + } + + + @Override + public SignatureKeyPair generateKeyPair(int numberOfMessages) { + + if(numberOfMessages != pp.getMessageLength()) { + throw new IllegalArgumentException( + String.format( + "The expected the message length %d, but was %d", + numberOfMessages, + pp.getMessageLength()) + ); + } + + //pick randomness + ZpElement exponentW = pp.getZp().getUniformlyRandomNonzeroElement(); + ZpElement[] exponentsChi = IntStream.range(0, numberOfMessages).mapToObj( + x-> pp.getZp().getUniformlyRandomNonzeroElement()).toArray(ZpElement[]::new); + + GroupElement group1ElementW = pp.getG1GroupGenerator().pow(exponentW).compute(); + GroupElement[] group1ElementsChi = Arrays.stream(exponentsChi).map( + x-> pp.getG1GroupGenerator().pow(x).compute()).toArray(GroupElement[]::new); + + SPSPOSSigningKey sk = new SPSPOSSigningKey(exponentsChi, exponentW); + SPSPOSVerificationKey vk = new SPSPOSVerificationKey(group1ElementsChi, group1ElementW); + + SignatureKeyPair keyPair = new SignatureKeyPair<>(vk, sk); + + // Set up initial one-time key + updateOneTimeKey(keyPair); + + return keyPair; + } + + /** + * Updates the given keyPair with a new set of one-time keys. + * + */ + public void updateOneTimeKey(SignatureKeyPair keyPair) { + + //pick randomness + ZpElement exponentA = pp.getZp().getUniformlyRandomElement(); + GroupElement group1ElementA = pp.getG1GroupGenerator().pow(exponentA).compute(); + + //put into keys + keyPair.getSigningKey().setOneTimeKey(exponentA); + keyPair.getVerificationKey().setOneTimeKey(group1ElementA); + } + + @Override + public SPSPOSSignature sign(PlainText plainText, SigningKey secretKey) { + + if(!(secretKey instanceof SPSPOSSigningKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + SPSPOSSigningKey sk = (SPSPOSSigningKey) secretKey; + + return sign(plainText, sk, sk.getOneTimeKey()); + } + + /** + * While a one-time key is stored in the {@param secretKey}, the scheme allows for a separate one-time key + * to be passed to the scheme. This makes it easier to use this scheme as a building block. + * + * Note: Implementations using this scheme are responsible for ensuring that the one-time keys are not reused. + */ + public SPSPOSSignature sign(PlainText plainText, SigningKey secretKey, ZpElement oneTimeKey) { + + if((plainText instanceof GroupElementPlainText)){ + plainText = new MessageBlock(plainText); + } + + // check if the message matches the expected structure (MessageBlock containing G_2 group elements) + doMessageChecks(plainText, pp.getMessageLength(), pp.getG2GroupGenerator().getStructure()); + + if(!(secretKey instanceof SPSPOSSigningKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + SPSPOSSigningKey sk = (SPSPOSSigningKey) secretKey; + + ZpElement exponentZeta = pp.getZp().getUniformlyRandomNonzeroElement(); + + GroupElement group1ElementSigmaZ = pp.getG2GroupGenerator().pow(exponentZeta).compute(); + + // calculate exponent of the left side of R + ZpElement lhsExponent = oneTimeKey; + lhsExponent = lhsExponent.sub(exponentZeta.mul(sk.getExponentW())); + + GroupElement group1ElementSigmaR = pp.getG2GroupGenerator().pow(lhsExponent); + + for (int i = 0; i < messageBlock.length(); i++) { + GroupElement m_i = ((GroupElementPlainText)messageBlock.get(i)).get(); + group1ElementSigmaR = group1ElementSigmaR.op(m_i.pow(sk.getExponentsChi()[i].neg())); + } + + group1ElementSigmaR.compute(); + + return new SPSPOSSignature(group1ElementSigmaZ, group1ElementSigmaR); + } + + @Override + public Boolean verify(PlainText plainText, Signature signature, VerificationKey publicKey) { + + if(!(publicKey instanceof SPSPOSVerificationKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + SPSPOSVerificationKey vk = (SPSPOSVerificationKey) publicKey; + + return verify(plainText, signature, publicKey, vk.getOneTimeKey()); + } + + /** + * While a one-time key is stored in the {@param publicKey}, the scheme allows for a separate one-time key + * to be passed to the scheme. This makes it easier to use this scheme as a building block. + * + * Note: Implementations using this scheme are responsible for ensuring that the one-time keys are not reused. + */ + public Boolean verify(PlainText plainText, + Signature signature, + VerificationKey publicKey, + GroupElement oneTimeVerificationKey) { + + //if plainText only contains a single element, wrap it in a MessageBlock + if((plainText instanceof GroupElementPlainText)){ + plainText = new MessageBlock(plainText); + } + + // check if the message matches the expected structure (MessageBlock containing G_2 group elements) + doMessageChecks(plainText, pp.getMessageLength(), pp.getG2GroupGenerator().getStructure()); + + if(!(publicKey instanceof SPSPOSVerificationKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + if(!(signature instanceof SPSPOSSignature)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + SPSPOSVerificationKey vk = (SPSPOSVerificationKey) publicKey; + SPSPOSSignature sigma = (SPSPOSSignature) signature; + + BilinearMap bMap = pp.getBilinearMap(); + + //check PPE + + GroupElement ppelhs = bMap.apply(oneTimeVerificationKey, pp.getG2GroupGenerator()).compute(); + + GroupElement pperhs = bMap.apply(vk.getGroup1ElementW(), sigma.getGroup2ElementZ()); + pperhs = pperhs.op(bMap.apply(pp.getG1GroupGenerator(), sigma.getGroup2ElementR())); + + for (int i = 0; i < messageBlock.length(); i++) { + GroupElement m_i = ((GroupElementPlainText)messageBlock.get(i)).get(); + pperhs = pperhs.op(bMap.apply(vk.getGroup1ElementsChi()[i],m_i)); + } + pperhs.compute(); + + return ppelhs.equals(pperhs); + } + + @Override + public PlainText restorePlainText(Representation repr) { + return new MessageBlock(repr, r -> new GroupElementPlainText(r, pp.getG2GroupGenerator().getStructure())); + } + + @Override + public Signature restoreSignature(Representation repr) { + return new SPSPOSSignature(repr, pp.getG2GroupGenerator().getStructure()); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new SPSPOSSigningKey(repr, pp.getZp()); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new SPSPOSVerificationKey(repr, pp.getG1GroupGenerator().getStructure()); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, VerificationKey pk) { + + if(pp == null) { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlainText(bytes, pp.getMessageLength()); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, SigningKey sk) { + + if(pp == null) { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlainText(bytes, pp.getMessageLength()); + } + + private MessageBlock mapToPlainText(byte[] bytes, int messageBlockLength) { + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[messageBlockLength]; + msgBlock[0] = new GroupElementPlainText( + pp.getG2GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + for (int i = 1; i < messageBlockLength; i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG2GroupGenerator()); + } + + return new MessageBlock(msgBlock); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG2GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSPOSSignatureScheme that = (SPSPOSSignatureScheme) o; + return Objects.equals(pp, that.pp); + } + + @Override + public int hashCode() { + return Objects.hash(pp); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSigningKey.java new file mode 100644 index 00000000..fabe1b44 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSigningKey.java @@ -0,0 +1,88 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.rings.zn.Zp; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A signing key as generated by the {@link SPSPOSSignatureScheme}. + * + */ +public class SPSPOSSigningKey implements SigningKey { + + /** + * \chi_1, ... \chi_l in the paper + * */ + @Represented(restorer = "[Zp]") + protected ZpElement[] exponentsChi; + + /** + * w_z in the paper + * */ + @Represented(restorer = "Zp") + protected ZpElement exponentW; + + /** + * a in the paper + * The one-time key used for signing + * */ + @Represented(restorer = "Zp") + protected ZpElement exponentA; + + + public SPSPOSSigningKey(ZpElement[] exponentsChi, ZpElement exponentW) { + super(); + this.exponentsChi = exponentsChi; + this.exponentW = exponentW; + } + + public SPSPOSSigningKey(Representation repr, Zp zp) { + new ReprUtil(this).register(zp, "Zp").deserialize(repr); + } + + + + + public ZpElement[] getExponentsChi() { + return exponentsChi; + } + + public ZpElement getExponentW() { + return exponentW; + } + + public void setOneTimeKey(ZpElement oneTimeKey) { this.exponentA = oneTimeKey; } + + public ZpElement getOneTimeKey() { + return exponentA; + } + + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSPOSSigningKey that = (SPSPOSSigningKey) o; + return Arrays.equals(exponentsChi, that.exponentsChi) && Objects.equals(exponentW, that.exponentW); + } + + @Override + public int hashCode() { + int result = Objects.hash(exponentW); + result = 31 * result + Arrays.hashCode(exponentsChi); + return result; + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSVerificationKey.java new file mode 100644 index 00000000..de6d49cc --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSVerificationKey.java @@ -0,0 +1,85 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A verification key as generated by the {@link SPSPOSSignatureScheme}. + * + */ +public class SPSPOSVerificationKey implements VerificationKey { + + /** + * G_i in group G_1 in the paper + * */ + @Represented(restorer = "[G1]") + protected GroupElement[] group1ElementsChi; + + /** + * G_z in the paper + * */ + @Represented(restorer = "G1") + protected GroupElement group1ElementW; + + /** + * A in the paper + * */ + @Represented(restorer = "G1") + protected GroupElement group1ElementA; + + + public SPSPOSVerificationKey(GroupElement[] group1ElementsChi, GroupElement group1ElementW) { + super(); + this.group1ElementsChi = group1ElementsChi; + this.group1ElementW = group1ElementW; + } + + public SPSPOSVerificationKey(Representation repr, Group G_1) { + new ReprUtil(this).register(G_1, "G1").deserialize(repr); + } + + + public GroupElement[] getGroup1ElementsChi() { + return group1ElementsChi; + } + + public GroupElement getGroup1ElementW() { + return group1ElementW; + } + + public void setOneTimeKey(GroupElement oneTimeKey) { this.group1ElementA = oneTimeKey; } + + public GroupElement getOneTimeKey() { + return this.group1ElementA; + } + + + @Override + public Representation getRepresentation() { + return ReprUtil.serialize(this); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSPOSVerificationKey that = (SPSPOSVerificationKey) o; + return Arrays.equals(group1ElementsChi, that.group1ElementsChi) + && Objects.equals(group1ElementW, that.group1ElementW); + } + + @Override + public int hashCode() { + int result = Objects.hash(group1ElementW); + result = 31 * result + Arrays.hashCode(group1ElementsChi); + return result; + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/package-info.java new file mode 100644 index 00000000..5a9802fc --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/package-info.java @@ -0,0 +1,6 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +/** + * An implementation of the partially one time signature presented in "Fully Structure-Preserving Signatures and + * Shrinking Commitments" + * */ \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentScheme.java new file mode 100644 index 00000000..8f57b1d5 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentScheme.java @@ -0,0 +1,359 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tc; + +import org.cryptimeleon.craco.commitment.*; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.common.plaintexts.RingElementPlainText; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.sps.SPSMessageSpaceVerifier; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.pos.*; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.*; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.RepresentableRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * An implementation of the structure preserving commitment scheme TC presented in [1] + * While the scheme is intended to be a building block of the larger SPS scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, + * the implementation can be used on its own, where it is chosen message target collision resistant + * under the assumptions that its building blocks {@link SPSPOSSignatureScheme} + * and {@link TCGAKOT15CommitmentScheme} make. + * + * Note: While the scheme is a trapdoor commitment scheme in the paper, its trapdoor functionality (SimCom, Equiv in + * the paper) has been omitted for this implementation, as it is not required for FSPS2 to work as intended. + * + * Note: This scheme does not possess its own implementation of an {@link Commitment} instance. It instead reuses + * {@link TCGAKOT15Commitment}, as the paper states com := com_gbc + * + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ +public class TCAKOT15CommitmentScheme implements CommitmentScheme, SPSMessageSpaceVerifier { + + /** + * The public parameters used by this scheme + */ + @Represented + AKOT15SharedPublicParameters pp; + + /** + * An instance of {@link SPSPOSSignatureScheme} used for commitment calculation + */ + @Represented + SPSPOSSignatureScheme posInstance; + + /** + * An instance of {@link TCGAKOT15CommitmentScheme} used for commitment calculation + */ + @Represented + TCGAKOT15CommitmentScheme gbcInstance; + + /** + * In order to match the {@link CommitmentScheme} interface, the scheme stores its own keys + * instead of the key passed as a parameter of commit() / verify(). + */ + @Represented + TCGAKOT15CommitmentKey commitmentKey; + + /** + * Instead of running several instances of {@link SPSPOSSignatureScheme}, the scheme calculates its own one-time + * keys and passes each of them to a single {@code posInstance} in sequence. + */ + //@Represented(restorer = "[Zp]") + ZpElement[] oneTimeSecretKeys; + + /** + * Instead of running several instances of {@link SPSPOSSignatureScheme}, the scheme calculates its own one-time + * keys and passes each of them to a single {@code posInstance} in sequence. + */ + //@Represented(restorer = "[G1]") + GroupElement[] oneTimePublicKeys; + + + public TCAKOT15CommitmentScheme(AKOT15SharedPublicParameters pp) { + super(); + this.pp = pp; + + AKOT15SharedPublicParameters pp_pos = pp.clone(); + pp_pos.setMessageLength(1); + + //create nested signature scheme instance (using the same public parameters) + this.posInstance = new SPSPOSSignatureScheme(pp_pos); + + // as tc gamma will sign the verification key of posInstance, its expected messages are 2 elements longer + AKOT15SharedPublicParameters pp_gbc = pp.clone(); + pp_gbc.setMessageLength(pp.getMessageLength() + 2); + + this.gbcInstance = new TCGAKOT15CommitmentScheme(pp_gbc); + + this.commitmentKey = generateKey(); + } + + /** + * Set up the scheme using a set of shared parameters, as well as special parameters for TC gamma, enabling + * inter-compatibility with {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * + * Note: The original paper contains a typo in the section: "Procedure: Matching C_gbc to M_xsig -- Setup" [1, p.17] + * The whole of TC must be initialized with F_1, F^{tilde}_1 as the default generators, not just TC_gamma. + */ + public TCAKOT15CommitmentScheme(AKOT15SharedPublicParameters sharedPublicParametersParameters, + TCGAKOT15XSIGPublicParameters tcGammaPublicParameters) { + super(); + + // set up general PublicParameters for this scheme + this.pp = new AKOT15SharedPublicParameters( + sharedPublicParametersParameters.getBilinearGroup(), + sharedPublicParametersParameters.getMessageLength(), + tcGammaPublicParameters.getG1GroupGenerator(), + tcGammaPublicParameters.getG2GroupGenerator()); + + // set up POS specific public parameters + AKOT15SharedPublicParameters pp_pos = new AKOT15SharedPublicParameters( + sharedPublicParametersParameters.getBilinearGroup(), + 1, + tcGammaPublicParameters.getG1GroupGenerator(), + tcGammaPublicParameters.getG2GroupGenerator()); + + //create nested signature scheme instance (using the same public parameters) + this.posInstance = new SPSPOSSignatureScheme(pp_pos); + + // use a special set of public parameters for tc_gamma if given (these are to be provided by FSP2) + this.gbcInstance = new TCGAKOT15CommitmentScheme(tcGammaPublicParameters); + + this.commitmentKey = generateKey(); + } + + public TCAKOT15CommitmentScheme(Representation repr) { + + super(); + + // manually deserialize the scheme + + ObjectRepresentation objRepr = (ObjectRepresentation) repr; + + this.pp = new AKOT15SharedPublicParameters(((RepresentableRepresentation)objRepr.get("pp")).getRepresentation()); + this.posInstance = new SPSPOSSignatureScheme(((RepresentableRepresentation)objRepr.get("posInstance")).getRepresentation()); + this.gbcInstance = new TCGAKOT15CommitmentScheme(((RepresentableRepresentation)objRepr.get("gbcInstance")).getRepresentation()); + + if(((RepresentableRepresentation)objRepr.get("commitmentKey")).getRepresentedTypeName() + .equals(TCGAKOT15XSIGCommitmentKey.class.getName())) { + this.commitmentKey = new TCGAKOT15XSIGCommitmentKey(pp.getG2GroupGenerator().getStructure(), + ((RepresentableRepresentation)objRepr.get("commitmentKey")).getRepresentation()); + } + else { + this.commitmentKey = new TCGAKOT15CommitmentKey(pp.getG2GroupGenerator().getStructure(), + ((RepresentableRepresentation)objRepr.get("commitmentKey")).getRepresentation()); + } + + // the one-time keys need not be represented, as they are only generated upon committing; + } + + + public TCGAKOT15CommitmentKey generateKey() { + return gbcInstance.getCommitmentKey(); + } + + + @Override + public CommitmentPair commit(PlainText plainText) { + return commit(plainText, commitmentKey); + } + + public CommitmentPair commit(PlainText plainText, CommitmentKey commitmentKey) { + + if(!(commitmentKey instanceof TCGAKOT15CommitmentKey)) { + throw new IllegalArgumentException("This is not a valid commitment key for this scheme"); + } + + //if plainText contains only a single group element, wrap it in a MessageBlock + if((plainText instanceof GroupElementPlainText)) { + plainText = new MessageBlock(plainText); + } + + // check if the plainText matches the structure expected by the scheme. It should be a MessageBlock composed + // of GroupElements in G_2 + doMessageChecks(plainText, pp.getMessageLength(), pp.getG2GroupGenerator().getStructure()); + + MessageBlock messageBlock = (MessageBlock) plainText; + TCGAKOT15CommitmentKey ck = (TCGAKOT15CommitmentKey) commitmentKey; + + SignatureKeyPair posKeyPair = + (SignatureKeyPair) posInstance.generateKeyPair(); + generateOneTimeKeys(); + + SPSPOSSignature[] sigmas = new SPSPOSSignature[pp.getMessageLength()]; + + for (int i = 0; i < sigmas.length; i++) { + sigmas[i] = posInstance.sign(new MessageBlock(messageBlock.get(i)), posKeyPair.getSigningKey(), oneTimeSecretKeys[i]); + } + + RingElementPlainText[] msg_com = new RingElementPlainText[pp.getMessageLength() + 2]; + + msg_com[0] = new RingElementPlainText(posKeyPair.getSigningKey().getExponentW()); + msg_com[1] = new RingElementPlainText(posKeyPair.getSigningKey().getExponentsChi()[0]); + + for (int i = 2; i < msg_com.length; i++) { + msg_com[i] = new RingElementPlainText(oneTimeSecretKeys[i - 2]); + } + + // commit using TC_gamma + CommitmentPair gbcCommitmentPair = gbcInstance.commit(new MessageBlock(msg_com)); + + TCAKOT15OpenValue open = new TCAKOT15OpenValue( + ((TCGAKOT15OpenValue)gbcCommitmentPair.getOpenValue()).getGroup1ElementR(), + posKeyPair.getVerificationKey(), + oneTimePublicKeys, + sigmas + ); + + return new CommitmentPair(gbcCommitmentPair.getCommitment(), open); + } + + /** + * Instead of running several instances of {@link SPSPOSSignatureScheme}, the scheme calculates its own one-time + * keys and passes each of them to a single {@code posInstance} in sequence. + * + * The individual one-time keys are generated the same way as in {@link SPSPOSSignatureScheme}. + */ + private void generateOneTimeKeys() { + oneTimeSecretKeys = new ZpElement[pp.getMessageLength()]; + oneTimePublicKeys = new GroupElement[pp.getMessageLength()]; + + for (int i = 0; i < oneTimePublicKeys.length; i++) { + oneTimeSecretKeys[i] = pp.getZp().getUniformlyRandomNonzeroElement(); + oneTimePublicKeys[i] = pp.getG1GroupGenerator().pow(oneTimeSecretKeys[i]).compute(); + } + } + + @Override + public boolean verify(Commitment commitment, OpenValue openValue, PlainText plainText) { + return verify(plainText, commitmentKey, commitment, openValue); + } + + public boolean verify(PlainText plainText, CommitmentKey commitmentKey, Commitment commitment, OpenValue openValue) { + + if(!(commitmentKey instanceof TCGAKOT15CommitmentKey)) { + throw new IllegalArgumentException("This is not a valid commitment key for this scheme"); + } + + if(!(commitment instanceof TCGAKOT15Commitment)) { + throw new IllegalArgumentException("This is not a valid commitment for this scheme"); + } + + if(!(openValue instanceof TCAKOT15OpenValue)) { + throw new IllegalArgumentException("This is not a valid commitment for this scheme"); + } + + //if plainText contains only a single group element, wrap it in a MessageBlock + if((plainText instanceof GroupElementPlainText)) { + plainText = new MessageBlock(plainText); + } + + // check if the plainText matches the structure expected by the scheme. It should be a MessageBlock composed + // of GroupElements in G_2 + doMessageChecks(plainText, pp.getMessageLength(), pp.getG2GroupGenerator().getStructure()); + + MessageBlock messageBlock = (MessageBlock) plainText; + TCGAKOT15Commitment com = (TCGAKOT15Commitment) commitment; + TCAKOT15OpenValue open = (TCAKOT15OpenValue) openValue; + + for (int i = 0; i < messageBlock.length(); i++) { + + if( !posInstance.verify(new MessageBlock( + messageBlock.get(i)), + open.getSpsPosSignatures()[i], + open.spsPosVerificationKey, oneTimePublicKeys[i])) { + return false; + } + } + + GroupElementPlainText[] msg_com = new GroupElementPlainText[pp.getMessageLength() + 2]; + + msg_com[0] = new GroupElementPlainText(open.getSpsPosVerificationKey().getGroup1ElementW()); + msg_com[1] = new GroupElementPlainText(open.getSpsPosVerificationKey().getGroup1ElementsChi()[0]); + + for (int i = 2; i < msg_com.length; i++) { + msg_com[i] = new GroupElementPlainText(oneTimePublicKeys[i - 2]); + } + + return gbcInstance.verify(com, new TCGAKOT15OpenValue(open.getGroup1ElementGamma()), new MessageBlock(msg_com)); + } + + + @Override + public MessageBlock mapToPlaintext(byte[] bytes) { + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[pp.getMessageLength()]; + msgBlock[0] = new GroupElementPlainText( + pp.getG2GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + for (int i = 1; i < pp.getMessageLength(); i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG2GroupGenerator()); + } + + return new MessageBlock(msgBlock); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Commitment restoreCommitment(Representation repr) { + return new TCGAKOT15Commitment(pp.getG2GroupGenerator().getStructure(), repr); + } + + @Override + public OpenValue restoreOpenValue(Representation repr) { + return new TCAKOT15OpenValue( + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure(), + repr); + } + + @Override + public Representation getRepresentation() { + ObjectRepresentation objRepr = (ObjectRepresentation) new ReprUtil(this).serialize(); + + // store specific class of commitment key + objRepr.put("commitmentKey", new RepresentableRepresentation(commitmentKey)); + + return objRepr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCAKOT15CommitmentScheme that = (TCAKOT15CommitmentScheme) o; + return Objects.equals(pp, that.pp) + && Objects.equals(posInstance, that.posInstance) + && Objects.equals(gbcInstance, that.gbcInstance) + && Objects.equals(commitmentKey, that.commitmentKey) + && Arrays.equals(oneTimeSecretKeys, that.oneTimeSecretKeys) + && Arrays.equals(oneTimePublicKeys, that.oneTimePublicKeys); + } + + @Override + public int hashCode() { + int result = Objects.hash(pp, posInstance, gbcInstance, commitmentKey); + result = 31 * result + Arrays.hashCode(oneTimeSecretKeys); + result = 31 * result + Arrays.hashCode(oneTimePublicKeys); + return result; + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15OpenValue.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15OpenValue.java new file mode 100644 index 00000000..f85f9e15 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15OpenValue.java @@ -0,0 +1,141 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tc; + +import org.cryptimeleon.craco.commitment.OpenValue; +import org.cryptimeleon.craco.sig.sps.akot15.pos.SPSPOSSignature; +import org.cryptimeleon.craco.sig.sps.akot15.pos.SPSPOSVerificationKey; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.serialization.ListRepresentation; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * An opening to a commitment used by the TCAKOT15 implementation. + * */ +public class TCAKOT15OpenValue implements OpenValue { + + /** + * Opening as defined in TC Gamma + */ + @Represented(restorer = "G1") + protected GroupElement group1ElementGamma; + + /** + * + */ + @Represented + protected SPSPOSVerificationKey spsPosVerificationKey; + + /** + * + */ + @Represented(restorer = "[G1]") + protected GroupElement[] spsPosOTVerificationKeys; + + /** + * sigma_{pos} in the paper. + */ + @Represented + protected SPSPOSSignature[] spsPosSignatures; + + + public TCAKOT15OpenValue(GroupElement group1ElementGamma, SPSPOSVerificationKey verificationKey, GroupElement[] oneTimeVerificationKeys, SPSPOSSignature[] signatures) { + this.group1ElementGamma = group1ElementGamma; + this.spsPosVerificationKey = verificationKey; + this.spsPosOTVerificationKeys = oneTimeVerificationKeys; + this.spsPosSignatures = signatures; + } + + public TCAKOT15OpenValue(Group g1, Group g2, Representation repr) { + ObjectRepresentation objRepr = (ObjectRepresentation) repr; + + this.group1ElementGamma = g1.restoreElement(objRepr.get("tcgOpen")); + this.spsPosVerificationKey = new SPSPOSVerificationKey(objRepr.get("posVk"), g1); + this.spsPosOTVerificationKeys = g1.restoreVector(objRepr.get("posOtVk")).stream().toArray(GroupElement[]::new); + + this.spsPosSignatures = objRepr.get("posSigmas").list().stream().map( + x -> new SPSPOSSignature(x, g2) + ).toArray(SPSPOSSignature[]::new); + } + + + + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + + @Override + public Representation getRepresentation() { + ObjectRepresentation objRepr = new ObjectRepresentation(); + + objRepr.put("tcgOpen", group1ElementGamma.getRepresentation()); + objRepr.put("posVk", spsPosVerificationKey.getRepresentation()); + + objRepr.put("posOtVk", new ListRepresentation( + Arrays.stream(spsPosOTVerificationKeys).sequential().map(x -> x.getRepresentation()).toArray(Representation[]::new) + )); + + objRepr.put("posSigmas", new ListRepresentation( + Arrays.stream(spsPosSignatures).sequential().map(x -> x.getRepresentation()).toArray(Representation[]::new) + )); + + return objRepr; + } + + public SPSPOSVerificationKey getSpsPosVerificationKey() { + return spsPosVerificationKey; + } + + public void setSpsPosVerificationKey(SPSPOSVerificationKey spsPosVerificationKey) { + this.spsPosVerificationKey = spsPosVerificationKey; + } + + public GroupElement[] getSpsPosOTVerificationKeys() { + return spsPosOTVerificationKeys; + } + + public void setSpsPosOTVerificationKeys(GroupElement[] spsPosOTVerificationKeys) { + this.spsPosOTVerificationKeys = spsPosOTVerificationKeys; + } + + public SPSPOSSignature[] getSpsPosSignatures() { + return spsPosSignatures; + } + + public void setSpsPosSignatures(SPSPOSSignature[] spsPosSignatures) { + this.spsPosSignatures = spsPosSignatures; + } + + public GroupElement getGroup1ElementGamma() { + return group1ElementGamma; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCAKOT15OpenValue that = (TCAKOT15OpenValue) o; + return Objects.equals(group1ElementGamma, that.group1ElementGamma) + && Objects.equals(spsPosVerificationKey, that.spsPosVerificationKey) + && Arrays.equals(spsPosOTVerificationKeys, that.spsPosOTVerificationKeys) + && Arrays.equals(spsPosSignatures, that.spsPosSignatures); + } + + @Override + public int hashCode() { + int result = Objects.hash(group1ElementGamma, spsPosVerificationKey); + result = 31 * result + Arrays.hashCode(spsPosOTVerificationKeys); + result = 31 * result + Arrays.hashCode(spsPosSignatures); + return result; + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/package-info.java new file mode 100644 index 00000000..e93eb4f7 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tc/package-info.java @@ -0,0 +1,6 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tc; + +/** + * An implementation of the trapdoor commitment scheme TC gamma. Originally presented in: + * + * */ \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15Commitment.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15Commitment.java new file mode 100644 index 00000000..5ae9e690 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15Commitment.java @@ -0,0 +1,64 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.commitment.Commitment; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * A commitment generated by the commitment scheme {@link TCGAKOT15CommitmentScheme}. + * + */ +public class TCGAKOT15Commitment implements Commitment { + + /** + * G_u in G2 in the paper + * */ + @Represented(restorer = "G2") + private GroupElement group2ElementGu; + + public TCGAKOT15Commitment(GroupElement group2ElementGu) { + this.group2ElementGu = group2ElementGu; + } + + public TCGAKOT15Commitment(Group group2, Representation repr) { + new ReprUtil(this).register(group2,"G2").deserialize(repr); + } + + + public GroupElement getGroup2ElementGu() { + return group2ElementGu; + } + + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCGAKOT15Commitment that = (TCGAKOT15Commitment) o; + return Objects.equals(group2ElementGu, that.group2ElementGu); + } + + @Override + public int hashCode() { + return Objects.hash(group2ElementGu); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentKey.java new file mode 100644 index 00000000..982a9dd0 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentKey.java @@ -0,0 +1,64 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.commitment.CommitmentKey; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; + +/** + * A commitment key generated by the commitment scheme {@link TCGAKOT15CommitmentScheme}. + * + */ +public class TCGAKOT15CommitmentKey implements CommitmentKey { + + /** + * X^{tilde}_i \in G_2 in the paper. + */ + @Represented(restorer = "[G2]") + private GroupElement[] group2ElementsXi; + + + public TCGAKOT15CommitmentKey(GroupElement[] group2ElementsXi) { + this.group2ElementsXi = group2ElementsXi; + } + + public TCGAKOT15CommitmentKey(Group group2, Representation repr) { + new ReprUtil(this).register(group2, "G2").deserialize(repr); + } + + + public GroupElement[] getGroup2ElementsXi() { + return group2ElementsXi; + } + + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCGAKOT15CommitmentKey that = (TCGAKOT15CommitmentKey) o; + return Arrays.equals(group2ElementsXi, that.group2ElementsXi); + } + + @Override + public int hashCode() { + return Arrays.hashCode(group2ElementsXi); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentScheme.java new file mode 100644 index 00000000..a282d801 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentScheme.java @@ -0,0 +1,422 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.commitment.Commitment; +import org.cryptimeleon.craco.commitment.CommitmentPair; +import org.cryptimeleon.craco.commitment.CommitmentScheme; +import org.cryptimeleon.craco.commitment.OpenValue; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.common.plaintexts.RingElementPlainText; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.RepresentableRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.cartesian.Vector; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.RingElement; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Objects; + +/** + * An implementation of the gamma binding commitment scheme presented in [1] + * While the scheme is intended to be a building block of the larger SPS scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, + * the implementation can be used on its own, where it is gamma-collision resistant + * under the Double Pairing assumption as defined in [1]. + * + * + * Note: The calculation of the commitments differs slightly when the scheme is used in the context of + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}: + * As the scheme combines {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme} -- which is + * based on this scheme -- with {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme}, + * the scheme must calculate 2 additional elements for its commitments (with are then signed by XSIG). + * + * Note: While the message space of the verification function is M := {M_i \in G_1}, this implementation will + * also accept messages containing elements \in Z_p and will calculate the appropriate G_1 elements automatically. + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ +public class TCGAKOT15CommitmentScheme implements CommitmentScheme { + + /** + * The public parameters for this scheme + */ + @Represented + public AKOT15SharedPublicParameters pp; + + /** + * In order to match the {@link CommitmentScheme} interface, the scheme stores its own keys + * instead of the key passed as a parameter of commit() / verify(). + */ + private TCGAKOT15CommitmentKey commitmentKey; + + public TCGAKOT15CommitmentScheme(AKOT15SharedPublicParameters pp) { + this.pp = pp; + commitmentKey = generateKey(); + } + + public TCGAKOT15CommitmentScheme(Representation repr) { + super(); + + ObjectRepresentation objRepr = (ObjectRepresentation) repr; + + // restore special xsig parameters if given + if(((RepresentableRepresentation)objRepr.get("pp")).getRepresentedTypeName() + .equals(TCGAKOT15XSIGPublicParameters.class.getName())) { + this.pp = new TCGAKOT15XSIGPublicParameters(((RepresentableRepresentation) objRepr.get("pp")) + .getRepresentation()); + } + else { + this.pp = new AKOT15SharedPublicParameters(((RepresentableRepresentation) objRepr.get("pp")) + .getRepresentation()); + } + + if(((RepresentableRepresentation)objRepr.get("ck")).getRepresentedTypeName(). + equals(TCGAKOT15XSIGCommitmentKey.class.getName())) { + this.commitmentKey = new TCGAKOT15XSIGCommitmentKey( + pp.getG2GroupGenerator().getStructure(), + ((RepresentableRepresentation) objRepr.get("ck")).getRepresentation()); + } + else { + this.commitmentKey = new TCGAKOT15CommitmentKey( + pp.getG2GroupGenerator().getStructure(), + ((RepresentableRepresentation) objRepr.get("ck")).getRepresentation()); + } + + } + + /** + * Generate a commitment key to be used by the scheme. + * */ + private TCGAKOT15CommitmentKey generateKey() { + + GroupElement[] group2ElementsXi = new GroupElement[pp.getMessageLength()]; + + //if XSIG specific parameters are passed, additional values are calculated + if(pp instanceof TCGAKOT15XSIGPublicParameters) { + + TCGAKOT15XSIGPublicParameters ppXSIG = (TCGAKOT15XSIGPublicParameters) pp; + + GroupElement[] group2ElementsXi2 = new GroupElement[pp.getMessageLength()]; + GroupElement[] group2ElementsXi3 = new GroupElement[pp.getMessageLength()]; + + for (int i = 0; i < group2ElementsXi.length; i++) { + + Zp.ZpElement rho = pp.getZp().getUniformlyRandomElement(); + + group2ElementsXi[i] = pp.getG2GroupGenerator().pow(rho).compute(); + group2ElementsXi2[i] = ppXSIG.getGroup2ElementF2().pow(rho).compute(); + group2ElementsXi3[i] = ppXSIG.getGroup2ElementU1().pow(rho).compute(); + } + + return new TCGAKOT15XSIGCommitmentKey(group2ElementsXi, group2ElementsXi2, group2ElementsXi3); + } + else { + + for (int i = 0; i < group2ElementsXi.length; i++) { + group2ElementsXi[i] = pp.getG2GroupGenerator().pow(pp.getZp().getUniformlyRandomElement()).compute(); + } + + return new TCGAKOT15CommitmentKey(group2ElementsXi); + } + } + + + /** + * Commit to a given message + * + * @param plainText the message block + * + * @return the commitment to the plaintext as would be calculated by TC-gamma + * */ + @Override + public CommitmentPair commit(PlainText plainText) { + + // check if the message matches the expected structure. commit() requires its messages to contain RingElements. + doMessageChecks(plainText, true); + + MessageBlock messageBlock = (MessageBlock) plainText; + Zp.ZpElement zeta = pp.getZp().getUniformlyRandomElement(); + + TCGAKOT15OpenValue open = new TCGAKOT15OpenValue(pp.getG1GroupGenerator().pow(zeta).compute()); + + //if XSIG message space is detected, compute additional values + if(pp instanceof TCGAKOT15XSIGPublicParameters) { + return commitXSIGVariant(messageBlock, zeta, open); + } + else { + GroupElement group2ElementGu = pp.getG2GroupGenerator().pow(zeta); + + for (int i = 0; i < messageBlock.length(); i++) { + GroupElement Xi = commitmentKey.getGroup2ElementsXi()[i]; + RingElement mi = ((RingElementPlainText)messageBlock.get(i)).getRingElement(); + group2ElementGu = group2ElementGu.op(Xi.pow(mi)); + } + group2ElementGu.compute(); + + return new CommitmentPair(new TCGAKOT15Commitment(group2ElementGu), open); + } + } + + /** + * if the scheme is used in the context of + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, the scheme is required to calculate + * two additional elements, so they can later be passed to + * {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme}. + * + */ + private CommitmentPair commitXSIGVariant(MessageBlock messageBlock, Zp.ZpElement zeta, TCGAKOT15OpenValue open) { + + TCGAKOT15XSIGCommitmentKey ck = (TCGAKOT15XSIGCommitmentKey) commitmentKey; + TCGAKOT15XSIGPublicParameters ppXSIG = (TCGAKOT15XSIGPublicParameters) pp; + + // compute G_u + + GroupElement group2ElementGu = pp.getG2GroupGenerator().pow(zeta); + GroupElement group2ElementGu2 = ppXSIG.getGroup2ElementF2().pow(zeta); + GroupElement group2ElementGu3 = ppXSIG.getGroup2ElementU1().pow(zeta); + + for (int i = 0; i < messageBlock.length(); i++) { + + RingElement mi = ((RingElementPlainText)messageBlock.get(i)).getRingElement(); + + GroupElement Xi = ck.getGroup2ElementsXi()[i]; + GroupElement Xi2 = ck.getGroup2ElementsXi2()[i]; + GroupElement Xi3 = ck.getGroup2ElementsXi3()[i]; + + group2ElementGu = group2ElementGu.op(Xi.pow(mi)); + group2ElementGu2 = group2ElementGu2.op(Xi2.pow(mi)); + group2ElementGu3 = group2ElementGu3.op(Xi3.pow(mi)); + } + + group2ElementGu.compute(); + group2ElementGu2.compute(); + group2ElementGu3.compute(); + + return new CommitmentPair( + new TCGAKOT15XSIGCommitment(group2ElementGu, group2ElementGu2, group2ElementGu3), + open); + } + + + @Override + public boolean verify(Commitment commitment, OpenValue openValue, PlainText plainText) { + + doMessageChecks(plainText, false); + + if(!(commitment instanceof TCGAKOT15Commitment)) { + throw new IllegalArgumentException("this is not a valid commitment for this scheme"); + } + + if(!(openValue instanceof TCGAKOT15OpenValue)) { + throw new IllegalArgumentException("this is not a valid opening for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + TCGAKOT15Commitment com = (TCGAKOT15Commitment) commitment; + TCGAKOT15OpenValue open = (TCGAKOT15OpenValue) openValue; + + if(!(pp.getMessageLength().equals(messageBlock.length()))){ + throw new IllegalArgumentException( + String.format( + "public parameters do not match given message length : %d vs. %d", + pp.getMessageLength(), + messageBlock.length()) + ); + } + + + GroupElement[] messageGroupElements = new GroupElement[messageBlock.length()]; + + //if RingElements are provided, transform the message to feature group elements + if(messageBlock.get(0) instanceof RingElementPlainText) { + messageGroupElements = messageBlock.stream().map( + x -> pp.getG1GroupGenerator().pow(((RingElementPlainText)x).getRingElement()).compute()) + .toArray(GroupElement[]::new); + }else if(messageBlock.get(0) instanceof GroupElementPlainText) { + messageGroupElements = messageBlock.stream().map(x -> ((GroupElementPlainText)x).get()) + .toArray(GroupElement[]::new); + } + + + BilinearMap bMap = pp.getBilinearMap(); + + GroupElement ppe_lhs = bMap.apply(pp.getG1GroupGenerator(), com.getGroup2ElementGu()).compute(); + GroupElement ppe_rhs = bMap.apply(open.getGroup1ElementR(), pp.getG2GroupGenerator()); + + for (int i = 0; i < messageBlock.length(); i++) { + ppe_rhs = ppe_rhs.op(bMap.apply( + messageGroupElements[i], + commitmentKey.getGroup2ElementsXi()[i]) + ); + } + ppe_rhs.compute(); + + return ppe_lhs.equals(ppe_rhs); + } + + /** + * Check if the given plainText matches the structure expected by the scheme + * and throws detailed exception if the plainText fails any check. + * + * Note that this scheme -- unlike every other scheme in the {@link org.cryptimeleon.craco.sig.sps.akot15} + * package -- operates on {@link org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement}s for its + * commit function. + * For this implementation, the verification function permits either RingElements or GroupElements + * + * @param requireZpElements defines if the function checking the message require the message + * to contain only ZpElements + */ + private void doMessageChecks(PlainText plainText, boolean requireZpElements) { + + MessageBlock messageBlock; + + // The scheme expects a MessageBlock... + if(plainText instanceof MessageBlock) { + messageBlock = (MessageBlock) plainText; + } + else { + throw new IllegalArgumentException("The scheme requires its messages to a MessageBlock"); + } + + // ... with a size that matches its public parameters. + if(messageBlock.length() != pp.getMessageLength()) { + throw new IllegalArgumentException(String.format( + "The scheme expected a message of length %d, but the size was: %d", + pp.getMessageLength(), messageBlock.length() + )); + } + + // if the function permits either RingElements or GroupElements, set the message space according to the first + // element of the message + if(!requireZpElements) { + if(messageBlock.get(0) instanceof RingElementPlainText) + requireZpElements = true; + } + + // if we expect RingElements, make sure all message elements are RingElements... + if(requireZpElements) { + for (int i = 0; i < messageBlock.length(); i++) { + if(!(messageBlock.get(i) instanceof RingElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The scheme expected its Messages to contain RingElements," + + " but element %d was of type: %s", + i, messageBlock.get(i).getClass().toString() + ) + ); + } + + // ... \in Zp as defined by the public parameters + RingElementPlainText ringElementPT = (RingElementPlainText) messageBlock.get(i); + + if(!(ringElementPT.getRingElement().getStructure().equals(pp.getZp()))) { + throw new IllegalArgumentException( + String.format( + "The scheme expected RingElements in %s," + + " but element %d was in: %s", + pp.getZp().toString(), + i, + ringElementPT.getRingElement().getStructure().toString() + ) + ); + } + + } + } + // if we expect GroupElements, make sure all message elements are GroupElements... + else { + for (int i = 0; i < messageBlock.length(); i++) { + if(!(messageBlock.get(i) instanceof GroupElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The scheme expected its Messages to contain GroupElements," + + " but element %d was of type: %s", + i, messageBlock.get(i).getClass().toString() + ) + ); + } + + // in G_1. + GroupElementPlainText group1ElementPT = (GroupElementPlainText) messageBlock.get(i); + + if(!(group1ElementPT.get().getStructure().equals(pp.getG1GroupGenerator().getStructure()))) { + throw new IllegalArgumentException( + String.format( + "The scheme expected GroupElements in %s," + + " but element %d was in: %s", + pp.getG1GroupGenerator().getStructure().toString(), + i, + group1ElementPT.get().getStructure().toString() + ) + ); + } + + } + } + + // if no exception has been thrown at this point, we can assume the message matches the expected structure. + } + + + public TCGAKOT15CommitmentKey getCommitmentKey() { + return commitmentKey; + } + + @Override + public PlainText mapToPlaintext(byte[] bytes) { + RingElementPlainText zero = new RingElementPlainText(pp.getZp().getZeroElement()); + return new MessageBlock( + Vector.of(new RingElementPlainText(pp.getZp().injectiveValueOf(bytes))) + .pad(zero, pp.getMessageLength()) + ); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Commitment restoreCommitment(Representation repr) { + return new TCGAKOT15Commitment(pp.getG2GroupGenerator().getStructure(), repr); + } + + @Override + public OpenValue restoreOpenValue(Representation repr) { + return new TCGAKOT15OpenValue(pp.getG1GroupGenerator().getStructure(), repr); + } + + @Override + public Representation getRepresentation() { + + ObjectRepresentation objRepr = new ObjectRepresentation(); + + objRepr.put("ck", new RepresentableRepresentation(commitmentKey)); + + objRepr.put("pp", new RepresentableRepresentation(pp)); + + return objRepr; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCGAKOT15CommitmentScheme that = (TCGAKOT15CommitmentScheme) o; + return Objects.equals(pp, that.pp) && Objects.equals(commitmentKey, that.commitmentKey); + } + + @Override + public int hashCode() { + return Objects.hash(pp, commitmentKey); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15OpenValue.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15OpenValue.java new file mode 100644 index 00000000..38f89f21 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15OpenValue.java @@ -0,0 +1,64 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.commitment.OpenValue; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * An opening as generated by the commitment scheme {@link TCGAKOT15CommitmentScheme}. + * + */ +public class TCGAKOT15OpenValue implements OpenValue { + + /** + * R in G1 in the paper + */ + @Represented(restorer = "G1") + private GroupElement group1ElementR; + + + public TCGAKOT15OpenValue(GroupElement group1ElementR) { + this.group1ElementR = group1ElementR; + } + + public TCGAKOT15OpenValue(Group group1, Representation repr) { + new ReprUtil(this).register(group1, "G1").deserialize(repr); + } + + + public GroupElement getGroup1ElementR() { + return group1ElementR; + } + + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + TCGAKOT15OpenValue that = (TCGAKOT15OpenValue) o; + return Objects.equals(group1ElementR, that.group1ElementR); + } + + @Override + public int hashCode() { + return Objects.hash(group1ElementR); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitment.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitment.java new file mode 100644 index 00000000..4e7a6c6b --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitment.java @@ -0,0 +1,93 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * A variant of {@link TCGAKOT15Commitment} that is compatible with + * the message space of {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * + * It adds two elements to the commitment which are required for XSIG to be able to sign this commitment. + * + */ +public class TCGAKOT15XSIGCommitment extends TCGAKOT15Commitment{ + + /** + * G^{tilde}_u2 in G2 in the paper + * */ + @Represented(restorer = "G2") + private GroupElement group2ElementGu2; + + /** + * G^{tilde}_u3 in G2 in the paper + * */ + @Represented(restorer = "G2") + private GroupElement group2ElementGu3; + + + public TCGAKOT15XSIGCommitment(GroupElement group2ElementGu, + GroupElement group2ElementGu2, + GroupElement group2ElementGu3) { + super(group2ElementGu); + this.group2ElementGu2 = group2ElementGu2; + this.group2ElementGu3 = group2ElementGu3; + } + + public TCGAKOT15XSIGCommitment(Group group2, Representation repr) + { + super(group2, repr); + } + + + public GroupElement getGroup2ElementGu2() { + return group2ElementGu2; + } + + public GroupElement getGroup2ElementGu3() { + return group2ElementGu3; + } + + /** + * generate a {@link MessageBlock} containing the {@link GroupElement}s stored in this commitment + * in a way that they match the message structure required by + * {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * + */ + public MessageBlock toMessageBlock() { + + MessageBlock triple = new MessageBlock(new GroupElementPlainText(getGroup2ElementGu()), + new GroupElementPlainText(group2ElementGu2), + new GroupElementPlainText( group2ElementGu3)); + + return new MessageBlock(new MessageBlock[] {triple}); + } + + @Override + public Representation getRepresentation() { + return new ReprUtil(this).serialize(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + TCGAKOT15XSIGCommitment that = (TCGAKOT15XSIGCommitment) o; + return Objects.equals(group2ElementGu2, that.group2ElementGu2) + && Objects.equals(group2ElementGu3, that.group2ElementGu3); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), group2ElementGu2, group2ElementGu3); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitmentKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitmentKey.java new file mode 100644 index 00000000..5d9f810f --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGCommitmentKey.java @@ -0,0 +1,72 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; + +/** + * A variant of {@link TCGAKOT15CommitmentKey} that is compatible with the message space + * of {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * + */ +public class TCGAKOT15XSIGCommitmentKey extends TCGAKOT15CommitmentKey{ + + /** + * X_i2 \in G2 in the paper + * Defined as F^{tilde}_2^{rho_i} + */ + @Represented(restorer = "[G2]") + private GroupElement[] group2ElementsXi2; + + /** + * X_i2 \in G2 in the paper + * Defined as U^{tilde}_1^{rho_i} + */ + @Represented(restorer = "[G2]") + private GroupElement[] group2ElementsXi3; + + + public TCGAKOT15XSIGCommitmentKey(GroupElement[] group2ElementsXi, + GroupElement[] group2ElementsXi2, + GroupElement[] group2ElementsXi3) { + super(group2ElementsXi); + this.group2ElementsXi2 = group2ElementsXi2; + this.group2ElementsXi3 = group2ElementsXi3; + } + + public TCGAKOT15XSIGCommitmentKey(Group group2, Representation repr) { + super(group2, repr); + } + + + public GroupElement[] getGroup2ElementsXi2() { + return group2ElementsXi2; + } + + public GroupElement[] getGroup2ElementsXi3() { + return group2ElementsXi3; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + TCGAKOT15XSIGCommitmentKey that = (TCGAKOT15XSIGCommitmentKey) o; + return Arrays.equals(group2ElementsXi2, that.group2ElementsXi2) + && Arrays.equals(group2ElementsXi3, that.group2ElementsXi3); + } + + @Override + public int hashCode() { + int result = super.hashCode(); + result = 31 * result + Arrays.hashCode(group2ElementsXi2); + result = 31 * result + Arrays.hashCode(group2ElementsXi3); + return result; + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGPublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGPublicParameters.java new file mode 100644 index 00000000..39e225ef --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15XSIGPublicParameters.java @@ -0,0 +1,92 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * The construction of the AKOT15 signature scheme FSPS2 requires the {@link PublicParameters} to match up + * across building blocks. + * This class extends these shared parameters with the elements required in TC_gamma's calculations if it is to be + * combined with {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme} + * + * This class represents a subset of {@link SPSXSIGPublicParameters} + * One should only be able to instantiate this class using an existing instance of {@link SPSXSIGPublicParameters}. + * + * Note: The original paper contains a typo in the section: "Procedure: Matching C_gbc to M_xsig -- Setup" [1, p.17] + * The additional generators (F_2, U_1) must be \in G_2 in order for the calculations to work + * + */ +public class TCGAKOT15XSIGPublicParameters extends AKOT15SharedPublicParameters { + + /** + * F^{tilde}_2 \in G_2 in the paper + */ + @Represented(restorer = "bilinearGroup::getG2") + protected GroupElement group2ElementF2; + + /** + * F^{tilde}_1 \in G_2 in the paper + */ + @Represented(restorer = "bilinearGroup::getG2") + protected GroupElement group2ElementU1; + + public TCGAKOT15XSIGPublicParameters(SPSXSIGPublicParameters xsigPublicParameters, int messageLength) { + + // set G,H^{tilde} as F_1, F^{tilde}_1 from xsigPublicParameters + super(xsigPublicParameters.getBilinearGroup(), messageLength); + + this.group1ElementG = xsigPublicParameters.getGroup1ElementF1(); + this.group2ElementH = xsigPublicParameters.getGroup2ElementF1(); + + // set additional parameters F^{tilde}_2, U^{tilde}_1 + + this.group2ElementF2 = xsigPublicParameters.getGroup2ElementF2(); + this.group2ElementU1 = xsigPublicParameters.getGroup2ElementsU()[0]; + + precompute(); + } + + public TCGAKOT15XSIGPublicParameters(Representation repr) { + super(repr); + } + + + public GroupElement getGroup2ElementF2() { + return group2ElementF2; + } + + public GroupElement getGroup2ElementU1() { + return group2ElementU1; + } + + + /** + * precomputes the group elements of the public parameters. + */ + private void precompute() { + this.group2ElementF2.precomputePow(); + this.group2ElementU1.precomputePow(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + TCGAKOT15XSIGPublicParameters that = (TCGAKOT15XSIGPublicParameters) o; + return Objects.equals(group2ElementF2, that.group2ElementF2) && Objects.equals(group2ElementU1, that.group2ElementU1); + } + + @Override + public int hashCode() { + return Objects.hash(super.hashCode(), group2ElementF2, group2ElementU1); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/package-info.java new file mode 100644 index 00000000..b8a920b6 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/package-info.java @@ -0,0 +1,21 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +/** + * An implementation of the gamma binding commitment scheme presented in [1] + * While the scheme is intended to be a building block of the larger SPS scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, + * the implementation can be used on its own, where it is gamma-collision resistant + * under the Double Pairing assumption as defined in [1]. + * + * + * Note: The calculation of the commitments differs slightly when the scheme is used in the context of + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}: + * As the scheme combines {@link org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme} -- which is + * based on this scheme -- with {@link org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme}, + * the scheme must calculate 2 additional elements for its commitments (with are then signed by XSIG). + * + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParameters.java new file mode 100644 index 00000000..30a4b3e9 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParameters.java @@ -0,0 +1,202 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * The construction of the AKOT15 signature scheme FSPS2 requires the {@link PublicParameters} to match up + * across building blocks. + * This class extends these shared parameters with the elements required in XSIGs calculations. + * + */ +public class SPSXSIGPublicParameters extends AKOT15SharedPublicParameters { + + /** + * F_1 \in G_1 in the paper + */ + @Represented(restorer = "bilinearGroup::getG1") + protected GroupElement group1ElementF1; + + /** + * F_2 \in G_1 in the paper + */ + @Represented(restorer = "bilinearGroup::getG1") + protected GroupElement group1ElementF2; + + /** + * F^{tilde}_1 \in G_2 in the paper + */ + @Represented(restorer = "bilinearGroup::getG2") + protected GroupElement group2ElementF1; + + /** + * F^{tilde}_2 \in G_2 in the paper + */ + @Represented(restorer = "bilinearGroup::getG2") + protected GroupElement group2ElementF2; + + /** + * U_i \in G_1 in the paper + */ + @Represented(restorer = "[bilinearGroup::getG1]") + protected GroupElement[] group1ElementsU; + + /** + * U_i \in G_2 in the paper + */ + @Represented(restorer = "[bilinearGroup::getG2]") + protected GroupElement[] group2ElementsU; + + + public SPSXSIGPublicParameters(BilinearGroup bilinearGroup, int messageLength){ + super(bilinearGroup, messageLength); + this.bilinearGroup = bilinearGroup; + this.messageLength = messageLength; + this.group1ElementG = this.bilinearGroup.getG1().getUniformlyRandomNonNeutral(); + this.group2ElementH = this.bilinearGroup.getG2().getUniformlyRandomNonNeutral(); + + generateRandomF(); + generateRandomU(); + + precompute(); + } + + public SPSXSIGPublicParameters(AKOT15SharedPublicParameters sharedPP, int messageLength) { + super(sharedPP.getBilinearGroup(), messageLength); + this.group1ElementG = sharedPP.getG1GroupGenerator(); + this.group2ElementH = sharedPP.getG2GroupGenerator(); + + generateRandomF(); + generateRandomU(); + + precompute(); + } + + public SPSXSIGPublicParameters(Representation repr) { super(repr); } + + + /** + * Generate the group elements F_1, F_2 for both groups + */ + private void generateRandomF() { + + ZpElement delta = getZp().getUniformlyRandomNonzeroElement(); + ZpElement phi = getZp().getUniformlyRandomNonzeroElement(); + + this.group1ElementF1 = group1ElementG.pow(phi).compute(); + this.group2ElementF1 = group2ElementH.pow(phi).compute(); + + this.group1ElementF2 = group1ElementG.pow(delta).compute(); + this.group2ElementF2 = group2ElementH.pow(delta).compute(); + + } + + /** + * Generate the group elements U_i, U^{tilde}_i for both groups + */ + private void generateRandomU() { + + this.group1ElementsU = new GroupElement[messageLength]; + this.group2ElementsU = new GroupElement[messageLength]; + + for (int i = 0; i < messageLength; i++) { + + ZpElement ui = getZp().getUniformlyRandomNonzeroElement(); + + group1ElementsU[i] = group1ElementG.pow(ui).compute(); + group2ElementsU[i] = group2ElementH.pow(ui).compute(); + } + + } + + /** + * precomputes the group elements of the public parameters. + */ + private void precompute() { + this.group1ElementF1.precomputePow(); + this.group2ElementF1.precomputePow(); + + this.group1ElementF2.precomputePow(); + this.group2ElementF2.precomputePow(); + + Arrays.stream(this.group1ElementsU).forEach(x -> x.precomputePow()); + Arrays.stream(this.group2ElementsU).forEach(x -> x.precomputePow()); + } + + + public BilinearGroup getBilinearGroup() { + return bilinearGroup; + } + + public void setBilinearGroup(BilinearGroup bilinearGroup) { + this.bilinearGroup = bilinearGroup; + } + + + public GroupElement getGroup1ElementG() { + return group1ElementG; + } + + public GroupElement getGroup2ElementH() { + return group2ElementH; + } + + public GroupElement getGroup1ElementF1() { + return group1ElementF1; + } + + public GroupElement getGroup1ElementF2() { + return group1ElementF2; + } + + public GroupElement getGroup2ElementF1() { + return group2ElementF1; + } + + public GroupElement getGroup2ElementF2() { + return group2ElementF2; + } + + public GroupElement[] getGroup1ElementsU() { + return group1ElementsU; + } + + public GroupElement[] getGroup2ElementsU() { + return group2ElementsU; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + if (!super.equals(o)) return false; + SPSXSIGPublicParameters that = (SPSXSIGPublicParameters) o; + return Objects.equals(group1ElementF1, that.group1ElementF1) + && Objects.equals(group1ElementF2, that.group1ElementF2) + && Objects.equals(group2ElementF1, that.group2ElementF1) + && Objects.equals(group2ElementF2, that.group2ElementF2) + && Arrays.equals(group1ElementsU, that.group1ElementsU) + && Arrays.equals(group2ElementsU, that.group2ElementsU); + } + + @Override + public int hashCode() { + int result = Objects.hash(super.hashCode(), group1ElementF1, group1ElementF2, + group2ElementF1, group2ElementF2); + result = 31 * result + Arrays.hashCode(group1ElementsU); + result = 31 * result + Arrays.hashCode(group2ElementsU); + return result; + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParametersGen.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParametersGen.java new file mode 100644 index 00000000..a4aa5f39 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGPublicParametersGen.java @@ -0,0 +1,31 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.debug.DebugBilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.type3.bn.BarretoNaehrigBilinearGroup; + +/** + * generates a set of public parameters to be used by the {@link SPSXSIGSignatureScheme}. + * + */ +public class SPSXSIGPublicParametersGen { + + public static SPSXSIGPublicParameters generatePublicParameters(int securityParameter, + int numberOfMessageBlocks, + boolean debugMode) { + BilinearGroup group; + if(debugMode){ + group = new DebugBilinearGroup( + RandomGenerator.getRandomPrime(securityParameter), + BilinearGroup.Type.TYPE_3 + ); + } + else{ + group = new BarretoNaehrigBilinearGroup(securityParameter); + } + + return new SPSXSIGPublicParameters(group, numberOfMessageBlocks); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignature.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignature.java new file mode 100644 index 00000000..44b98620 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignature.java @@ -0,0 +1,88 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.UniqueByteRepresentable; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.hash.annotations.UniqueByteRepresented; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A signature as generated by the SPS scheme {@link SPSXSIGSignatureScheme}. + * + */ +public class SPSXSIGSignature implements Signature, UniqueByteRepresentable { + + /** + * S^{tilde}_0 \in G_2 in the paper + */ + @UniqueByteRepresented + @Represented(restorer = "G2") + protected GroupElement group2ElementSigma0; + + /** + * S_i \in G_1 in the paper, i \in [1,5] + */ + @UniqueByteRepresented + @Represented(restorer = "[G1]") + protected GroupElement[] group1ElementsSigma; + + + public SPSXSIGSignature(Representation repr, Group G1, Group G2) { + new ReprUtil(this).register(G1, "G1").register(G2, "G2"). deserialize(repr); + } + + public SPSXSIGSignature(GroupElement group2ElementSigma0, GroupElement[] group1ElementsSigma) { + + //check if group1ElementsSigma has the appropriate amount of elements + if(group1ElementsSigma.length != 5) { + throw new IllegalArgumentException("The signature requires exactly 5 G1-GroupElements, but got: " + + group1ElementsSigma.length); + } + + this.group2ElementSigma0 = group2ElementSigma0; + this.group1ElementsSigma = group1ElementsSigma; + } + + + public GroupElement getGroup2ElementSigma0() { + return group2ElementSigma0; + } + + + public GroupElement[] getGroup1ElementsSigma() { + return group1ElementsSigma; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSXSIGSignature that = (SPSXSIGSignature) o; + return Objects.equals(group2ElementSigma0, that.group2ElementSigma0) + && Arrays.equals(group1ElementsSigma, that.group1ElementsSigma); + } + + @Override + public int hashCode() { + int result = Objects.hash(group2ElementSigma0); + result = 31 * result + Arrays.hashCode(group1ElementsSigma); + return result; + } + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { + return AnnotatedUbrUtil.autoAccumulate(accumulator, this); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureScheme.java new file mode 100644 index 00000000..c8fb2acb --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureScheme.java @@ -0,0 +1,479 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.craco.sig.sps.SPSMessageSpaceVerifier; +import org.cryptimeleon.math.serialization.ListRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Objects; + +/** + * An implementation of the xrma secure SPS scheme presented in [1] + * While the scheme is intended to be a building block of the larger SPS scheme + * {@link org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme}, + * the implementation can be used on its own, where it is secure under extended random message attacks [1]. + * + * + * [1] Abe et al.: Fully Structure-Preserving Signatures and Shrinking Commitments. + * https://eprint.iacr.org/2015/076.pdf + * + */ +public class SPSXSIGSignatureScheme implements MultiMessageStructurePreservingSignatureScheme, SPSMessageSpaceVerifier { + + @Represented + private SPSXSIGPublicParameters pp; + + + public SPSXSIGSignatureScheme(SPSXSIGPublicParameters pp) { + super(); + this.pp = pp; + } + + public SPSXSIGSignatureScheme(Representation repr) { new ReprUtil(this).deserialize(repr); } + + + public SPSXSIGPublicParameters getPublicParameters() { return pp; } + + + @Override + public SignatureKeyPair generateKeyPair(int numberOfMessages) { + + if(pp.getMessageLength() != numberOfMessages){ + throw new IllegalArgumentException(String.format( + "The scheme expected messageLength %d, but was: %d", + pp.getMessageLength(), numberOfMessages)); + } + + //pick randomness + + ZpElement r0 = pp.getZp().getUniformlyRandomElement(); + ZpElement r1 = pp.getZp().getUniformlyRandomElement(); + ZpElement r2 = pp.getZp().getUniformlyRandomElement(); + + ZpElement phi = pp.getZp().getUniformlyRandomElement(); + ZpElement alpha = pp.getZp().getUniformlyRandomElement(); + + ZpElement a = pp.getZp().getUniformlyRandomElement(); + ZpElement b = pp.getZp().getUniformlyRandomElement(); + + //calculate verification key elements + + GroupElement groupElementG = pp.getGroup1ElementG(); + GroupElement groupElementGHat = pp.getGroup2ElementH(); + + GroupElement group2ElementV1 = groupElementGHat.pow(b).compute(); + GroupElement group2ElementV2 = groupElementGHat.pow(a).compute(); + GroupElement group2ElementV3 = groupElementGHat.pow(a.mul(b)).compute(); + GroupElement group2ElementV4 = groupElementGHat.pow(r0.add(a.mul(r1))).compute(); + GroupElement group2ElementV5 = group2ElementV4.pow(b).compute(); + GroupElement group2ElementV6 = groupElementGHat.pow(r2).compute(); + + GroupElement group1ElementV7 = groupElementG.pow(phi).compute(); + + GroupElement group2ElementV8; + + if(!phi.isZero()) { + group2ElementV8 = groupElementGHat.pow(alpha.mul(b).div(phi)).compute(); + } + else { + group2ElementV8 = groupElementGHat.getStructure().getUniformlyRandomElement(); + } + + //calculate signing key elements + + GroupElement group1ElementK1 = groupElementG.pow(alpha).compute(); + GroupElement group1ElementK2 = groupElementG.pow(b).compute(); + GroupElement group1ElementK3 = groupElementG.pow(r0).compute(); + GroupElement group1ElementK4 = groupElementG.pow(r1).compute(); + + SPSXSIGVerificationKey vk = new SPSXSIGVerificationKey( + group2ElementV1, group2ElementV2, + group2ElementV3, group2ElementV4, + group2ElementV5, group2ElementV6, + group1ElementV7, group2ElementV8); + + SPSXSIGSigningKey sk = new SPSXSIGSigningKey( + group2ElementV6, + group1ElementK1, group1ElementK2, + group1ElementK3, group1ElementK4); + + return new SignatureKeyPair<>(vk, sk); + } + + + @Override + public Signature sign(PlainText plainText, SigningKey secretKey) { + + // check if the message to be signed matches the structure required by the implementation + doMessageChecks(plainText); + + MessageBlock messageBlock = (MessageBlock) plainText; + + if(!(secretKey instanceof SPSXSIGSigningKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + + SPSXSIGSigningKey sk = (SPSXSIGSigningKey) secretKey; + + + //pick randomness + + ZpElement r0 = pp.getZp().getUniformlyRandomElement(); + ZpElement r1 = pp.getZp().getUniformlyRandomElement(); + ZpElement r = r0.add(r1); + ZpElement z = pp.getZp().getUniformlyRandomElement(); + + // compute signature + + GroupElement group2ElementS0 = sk.getGroup2ElementV6(); + + for (int i = 0; i < messageBlock.length(); i++) { + GroupElementPlainText m_i3 = ((GroupElementPlainText)((MessageBlock)messageBlock.get(i)).get(2)); + group2ElementS0 = group2ElementS0.op(m_i3.get()); + } + + group2ElementS0 = group2ElementS0.pow(r0).compute(); + + GroupElement group1ElementS1 = sk.getGroup1ElementK1().op(sk.getGroup1ElementK3().pow(r)).compute(); + + GroupElement group1ElementS2 = sk.getGroup1ElementK4().pow(r); + GroupElement group1ElementS2rhs = pp.getGroup1ElementG().pow(z.neg()).compute(); + group1ElementS2 = group1ElementS2.op(group1ElementS2rhs).compute(); + + GroupElement group1ElementS3 = sk.getGroup1ElementK2().pow(z).compute(); + + GroupElement group1ElementS4 = sk.getGroup1ElementK2().pow(r1).compute(); + + GroupElement group1ElementS5 = pp.getGroup1ElementG().pow(r0).compute(); + + return new SPSXSIGSignature( + group2ElementS0, + new GroupElement[]{ + group1ElementS1, group1ElementS2, + group1ElementS3, group1ElementS4, + group1ElementS5 + }); + } + + @Override + public Boolean verify(PlainText plainText, Signature signature, VerificationKey publicKey) { + + // check if the message to be signed matches the structure required by the implementation + doMessageChecks(plainText); + + if(!(publicKey instanceof SPSXSIGVerificationKey)){ + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + if(!(signature instanceof SPSXSIGSignature)){ + throw new IllegalArgumentException("Not a valid signature for this scheme"); + } + + + MessageBlock messageBlock = (MessageBlock) plainText; + SPSXSIGVerificationKey vk = (SPSXSIGVerificationKey) publicKey; + SPSXSIGSignature sigma = (SPSXSIGSignature) signature; + + BilinearMap bMap = pp.getBilinearMap(); + + return verifyFirstPPE(bMap, sigma, vk, messageBlock) + && verifySecondPPE(bMap, sigma, vk) + && verifyThirdPPE(bMap, messageBlock) + && verifyFourthPPE(bMap, messageBlock); + } + + private boolean verifyFirstPPE(BilinearMap bMap, SPSXSIGSignature sigma, + SPSXSIGVerificationKey vk, MessageBlock messageBlock) { + + GroupElement ppe1lhs2 = vk.getGroup2ElementV6(); + + for (int i = 0; i < messageBlock.length(); i++) { + GroupElementPlainText m_i3 = ((GroupElementPlainText)((MessageBlock)messageBlock.get(i)).get(2)); + ppe1lhs2 = ppe1lhs2.op(m_i3.get()); + } + + ppe1lhs2 = ppe1lhs2.compute(); + + GroupElement ppe1lhs = bMap.apply(sigma.getGroup1ElementsSigma()[4], ppe1lhs2).compute(); + + GroupElement ppe1rhs = bMap.apply(pp.getGroup1ElementG(), sigma.getGroup2ElementSigma0()).compute(); + + return ppe1lhs.equals(ppe1rhs); + } + + private boolean verifySecondPPE(BilinearMap bMap, SPSXSIGSignature sigma, + SPSXSIGVerificationKey vk) { + + //left-hand side + + GroupElement ppe2lhs = bMap.apply(sigma.getGroup1ElementsSigma()[0], vk.getGroup2ElementV1()); + + ppe2lhs = ppe2lhs.op(bMap.apply(sigma.getGroup1ElementsSigma()[1], vk.getGroup2ElementV3())); + ppe2lhs = ppe2lhs.op(bMap.apply(sigma.getGroup1ElementsSigma()[2], vk.getGroup2ElementV2())); + + ppe2lhs.compute(); + + //right-hand side + + GroupElement ppe2rhs = bMap.apply(sigma.getGroup1ElementsSigma()[3], vk.getGroup2ElementV4()); + ppe2rhs = ppe2rhs.op(bMap.apply(sigma.getGroup1ElementsSigma()[4], vk.getGroup2ElementV5())); + ppe2rhs = ppe2rhs.op(bMap.apply(vk.getGroup1ElementV7(), vk.getGroup2ElementV8())); + ppe2rhs.compute(); + + return ppe2lhs.equals(ppe2rhs); + } + + private boolean verifyThirdPPE(BilinearMap bMap, MessageBlock messageBlock) { + + for (int i = 0; i < messageBlock.length(); i++) { + + MessageBlock innerBlock = (MessageBlock) messageBlock.get(i); + GroupElement m_i1 = ((GroupElementPlainText) innerBlock.get(0)).get(); + GroupElement m_i3 = ((GroupElementPlainText) innerBlock.get(2)).get(); + + GroupElement ppe3lhs = bMap.apply(pp.getGroup1ElementF1(), m_i3); + ppe3lhs.compute(); + + GroupElement ppe3rhs = bMap.apply(pp.getGroup1ElementsU()[i], m_i1); + ppe3rhs.compute(); + + if(!ppe3lhs.equals(ppe3rhs)) { + return false; + } + + } + + return true; + } + + private boolean verifyFourthPPE(BilinearMap bMap, MessageBlock messageBlock) { + + for (int i = 0; i < messageBlock.length(); i++) { + + MessageBlock innerBlock = (MessageBlock) messageBlock.get(i); + GroupElement m_i2 = ((GroupElementPlainText) innerBlock.get(1)).get(); + GroupElement m_i3 = ((GroupElementPlainText) innerBlock.get(2)).get(); + + GroupElement ppe3lhs = bMap.apply(pp.getGroup1ElementF2(), m_i3); + ppe3lhs.compute(); + + GroupElement ppe3rhs = bMap.apply(pp.getGroup1ElementsU()[i], m_i2); + ppe3rhs.compute(); + + if(!ppe3lhs.equals(ppe3rhs)) { + return false; + } + + } + + return true; + } + + @Override + public void doMessageChecks(PlainText plainText, int expectedMessageLength, Group expectedGroup) { + // use implementation specific to this scheme + doMessageChecks(plainText); + } + + /** + * Check if the given plainText matches the structure expected by the scheme + * and throws detailed exception if the plainText fails any check. + * Messages for this scheme require a unique structure. The message space is defined as + * M = {(M_11, M_12, M_13),...,(M_l1, M_l2, M_l3)} such that for all i there exists a m_i in Zp such that + * (M_i1, M_i2, M_i3) = (F1^mi, F2^mi, Ui^mi). (Note that all these group elements are \in G_2.) + * + * This results in the scheme expecting a {@link MessageBlock}, containing inner {@link MessageBlock}s, + * each of which holds 3 GroupElements in G_2. + */ + private void doMessageChecks(PlainText plainText) { + MessageBlock messageBlock; + + // The scheme expects a MessageBlock... + if(plainText instanceof MessageBlock) { + messageBlock = (MessageBlock) plainText; + } + else { + throw new IllegalArgumentException("The scheme requires its messages to a MessageBlock"); + } + + // ...with a size matching the public parameters... + if(messageBlock.length() != pp.getMessageLength()) { + throw new IllegalArgumentException(String.format( + "The scheme expected a message of length %d, but the size was: %d", + pp.getMessageLength(), messageBlock.length() + )); + } + + // ... containing more MessageBlocks... + for (int i = 0; i < messageBlock.length(); i++) { + if(!(messageBlock.get(i) instanceof MessageBlock)) { + throw new IllegalArgumentException(String.format( + "The scheme requires its messages to only contain inner MessageBlocks, " + + "but element %d was %s", + i, messageBlock.get(i).getClass() + )); + } + else { + // ...each containing three elements... + MessageBlock innerBlock = (MessageBlock) messageBlock.get(i); + if(innerBlock.length() != 3) { + throw new IllegalArgumentException(String.format( + "The scheme requires its inner MessageBlocks to contain three elements," + + " but element %d contained: %d elements", + i, innerBlock.length() + )); + } + else { + // ... each of which is a GroupElementPlaintext + for (int j = 0; j < innerBlock.length(); j++) { + if(!(innerBlock.get(j) instanceof GroupElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The scheme requires its inner MessageBlocks to contain GroupElements," + + " but element %d was of type: %s", + i, messageBlock.get(i).getClass().toString() + ) + ); + } + else { + // ... in G2. + GroupElementPlainText groupElementPT = (GroupElementPlainText) innerBlock.get(j); + if(!(groupElementPT.get().getStructure().equals(pp.getG2GroupGenerator().getStructure()))) { + throw new IllegalArgumentException( + String.format( + "Expected message elements to be in G_2," + + " but element %d in inner MessageBlock %d was in: %s", + j, i, groupElementPT.get().getStructure().toString() + ) + ); + } + } + } + } + } + } + + // if no exception has been thrown at this point, we can assume the message matches the expected structure. + } + + + + @Override + public PlainText restorePlainText(Representation repr) { + /* Messages for this scheme require a unique structure. The message space is defined as + M = {(M_11, M_12, M_13),...,(M_l1, M_l2, M_l3)} such that for all i there exists a mi in Zp such that + (M_i1, M_i2, M_i3) = (F1^mi, F2^mi, Ui^mi) + */ + + // we enforce this message structure by requiring the Plaintext to be a MessageBlock consisting + // of MessageBlocks, each containing a triplet of GroupElements + + ListRepresentation messageList = (ListRepresentation) repr; + + Representation[] messageTripletsRepr = new Representation[messageList.size()]; + + for (int i = 0; i < messageTripletsRepr.length; i++) { + messageTripletsRepr[i] = (Representation) messageList.get(i); + } + + MessageBlock[] messageTriplets = new MessageBlock[messageTripletsRepr.length]; + + for (int i = 0; i < messageTripletsRepr.length; i++) { + messageTriplets[i] = new MessageBlock( + messageTripletsRepr[i], + r -> new GroupElementPlainText(r, pp.getG2GroupGenerator().getStructure()) + ); + } + + return new MessageBlock(messageTriplets); + } + + @Override + public Signature restoreSignature(Representation repr) { + return new SPSXSIGSignature( + repr, + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure()); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new SPSXSIGSigningKey(pp.getG1GroupGenerator().getStructure(),pp.getG2GroupGenerator().getStructure(), repr); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new SPSXSIGVerificationKey( + pp.getG1GroupGenerator().getStructure(), + pp.getG2GroupGenerator().getStructure(), + repr); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, VerificationKey pk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLength()); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, SigningKey sk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + + return mapToPlaintext(bytes, pp.getMessageLength()); + } + + private MessageBlock mapToPlaintext(byte[] bytes, int messageBlockLength){ + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[messageBlockLength]; + msgBlock[0] = new GroupElementPlainText( + pp.getG1GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + + for (int i = 1; i < msgBlock.length; i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG1GroupGenerator()); + } + + return new MessageBlock(new MessageBlock(msgBlock), new MessageBlock()); + } + + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSXSIGSignatureScheme that = (SPSXSIGSignatureScheme) o; + return Objects.equals(pp, that.pp); + } + + @Override + public int hashCode() { + return Objects.hash(pp); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSigningKey.java new file mode 100644 index 00000000..be52caa1 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSigningKey.java @@ -0,0 +1,113 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * A signing key as generated by the {@link SPSXSIGSignatureScheme}. + * + * Note: V6 is part of the verification key, but as it is used for signature calculation, the groupElement + * is also stored here. + */ +public class SPSXSIGSigningKey implements SigningKey { + + /** + * K_1 \in G_1 in the paper + */ + @Represented(restorer = "G1") + protected GroupElement group1ElementK1; + + /** + * K_2 \in G_1 in the paper + */ + @Represented(restorer = "G1") + protected GroupElement group1ElementK2; + + /** + * K_3 \in G_1 in the paper + */ + @Represented(restorer = "G1") + protected GroupElement group1ElementK3; + + /** + * K_4 \in G_1 in the paper + */ + @Represented(restorer = "G1") + protected GroupElement group1ElementK4; + + /** + * V^{tilde}_6 \in G_2 in the paper + * Note: V6 is part of the verification key, but as it is used for signature calculation, the groupElement + * is also stored here. + * */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV6; + + + public SPSXSIGSigningKey(GroupElement group2ElementV6, + GroupElement group1ElementK1, + GroupElement group1ElementK2, + GroupElement group1ElementK3, + GroupElement group1ElementK4) { + + this.group1ElementK1 = group1ElementK1; + this.group1ElementK2 = group1ElementK2; + this.group1ElementK3 = group1ElementK3; + this.group1ElementK4 = group1ElementK4; + this.group2ElementV6 = group2ElementV6; + } + + public SPSXSIGSigningKey(Group G1, Group G2, Representation repr){ + new ReprUtil(this).register(G1, "G1").register(G2, "G2").deserialize(repr); + } + + + public GroupElement getGroup1ElementK1() { + return group1ElementK1; + } + + public GroupElement getGroup1ElementK2() { + return group1ElementK2; + } + + public GroupElement getGroup1ElementK3() { + return group1ElementK3; + } + + public GroupElement getGroup1ElementK4() { + return group1ElementK4; + } + + public GroupElement getGroup2ElementV6() { + return group2ElementV6; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSXSIGSigningKey that = (SPSXSIGSigningKey) o; + return Objects.equals(group1ElementK1, that.group1ElementK1) + && Objects.equals(group1ElementK2, that.group1ElementK2) + && Objects.equals(group1ElementK3, that.group1ElementK3) + && Objects.equals(group1ElementK4, that.group1ElementK4) + && Objects.equals(group2ElementV6, that.group2ElementV6); + } + + @Override + public int hashCode() { + return Objects.hash(group1ElementK1, group1ElementK2, group1ElementK3, group1ElementK4, group2ElementV6); + } + + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGVerificationKey.java new file mode 100644 index 00000000..473fb495 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGVerificationKey.java @@ -0,0 +1,147 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Objects; + +/** + * A verification key as generated by the {@link SPSXSIGSignatureScheme}. + * + */ +public class SPSXSIGVerificationKey implements VerificationKey { + + /** + * V^{tilde}_1 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV1; + + /** + * V^{tilde}_2 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV2; + + /** + * V^{tilde}_3 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV3; + + /** + * V^{tilde}_4 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV4; + + /** + * V^{tilde}_5 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV5; + + /** + * V^{tilde}_6 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV6; + + /** + * V_7 \in G_1(!) in the paper + */ + @Represented(restorer = "G1") //note: V7 is in Group G_1 + protected GroupElement group1ElementV7; + + /** + * V^{tilde}_8 \in G_2 in the paper + */ + @Represented(restorer = "G2") + protected GroupElement group2ElementV8; + + + public SPSXSIGVerificationKey(GroupElement group2ElementV1, GroupElement group2ElementV2, + GroupElement group2ElementV3, GroupElement group2ElementV4, + GroupElement group2ElementV5, GroupElement group2ElementV6, + GroupElement group1ElementV7, GroupElement group2ElementV8) { + + this.group2ElementV1 = group2ElementV1; + this.group2ElementV2 = group2ElementV2; + this.group2ElementV3 = group2ElementV3; + this.group2ElementV4 = group2ElementV4; + this.group2ElementV5 = group2ElementV5; + this.group2ElementV6 = group2ElementV6; + this.group1ElementV7 = group1ElementV7; + this.group2ElementV8 = group2ElementV8; + } + + public SPSXSIGVerificationKey(Group G1, Group G2, Representation repr){ + new ReprUtil(this).register(G1, "G1").register(G2, "G2").deserialize(repr); + } + + + public GroupElement getGroup2ElementV1() { + return group2ElementV1; + } + + public GroupElement getGroup2ElementV2() { + return group2ElementV2; + } + + public GroupElement getGroup2ElementV3() { + return group2ElementV3; + } + + public GroupElement getGroup2ElementV4() { + return group2ElementV4; + } + + public GroupElement getGroup2ElementV5() { + return group2ElementV5; + } + + public GroupElement getGroup2ElementV6() { + return group2ElementV6; + } + + public GroupElement getGroup1ElementV7() { + return group1ElementV7; + } + + public GroupElement getGroup2ElementV8() { + return group2ElementV8; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSXSIGVerificationKey that = (SPSXSIGVerificationKey) o; + return Objects.equals(group2ElementV1, that.group2ElementV1) + && Objects.equals(group2ElementV2, that.group2ElementV2) + && Objects.equals(group2ElementV3, that.group2ElementV3) + && Objects.equals(group2ElementV4, that.group2ElementV4) + && Objects.equals(group2ElementV5, that.group2ElementV5) + && Objects.equals(group2ElementV6, that.group2ElementV6) + && Objects.equals(group1ElementV7, that.group1ElementV7) + && Objects.equals(group2ElementV8, that.group2ElementV8); + } + + @Override + public int hashCode() { + return Objects.hash( + group2ElementV1, group2ElementV2, + group2ElementV3, group2ElementV4, + group2ElementV5, group2ElementV6, + group1ElementV7, group2ElementV8); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/package-info.java new file mode 100644 index 00000000..f0ab6d38 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/package-info.java @@ -0,0 +1,5 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +/** + * An implementation of the xSIG FSPS scheme. A building block of AKOT15 + * */ \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameterGen.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameterGen.java new file mode 100644 index 00000000..e0d4009f --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameterGen.java @@ -0,0 +1,26 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.math.random.RandomGenerator; +import org.cryptimeleon.math.structures.groups.debug.DebugBilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; +import org.cryptimeleon.math.structures.groups.elliptic.type3.bn.BarretoNaehrigBilinearGroup; + +public class SPSKPW15PublicParameterGen { + + /** + * @param securityParameter The security parameter. + * @param debugMode Enable debug mode (Makes the PPs insecure!). + * @param messageLength The message length the instance is expected to sign + * @return The public parameters for the KPW15 SPS scheme + */ + public SPSKPW15PublicParameters generatePublicParameter(int securityParameter, boolean debugMode, int messageLength) { + BilinearGroup group; + if (debugMode) { + group = new DebugBilinearGroup(RandomGenerator.getRandomPrime(securityParameter), BilinearGroup.Type.TYPE_3); + } else { + group = new BarretoNaehrigBilinearGroup(securityParameter); + } + + return new SPSKPW15PublicParameters(group, messageLength); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameters.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameters.java new file mode 100644 index 00000000..241a81c7 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15PublicParameters.java @@ -0,0 +1,58 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearGroup; + +import java.util.Objects; + +/** + * Class for the public parameters of the KPW15 structure preserving signature scheme. + * Uses Bilinear group type 3 + * + * */ +public class SPSKPW15PublicParameters extends SPSPublicParameters { + + /** + * The number of expected G_1 elements per message + * */ + @Represented(restorer = "messageLength") + protected Integer messageLength; + + public SPSKPW15PublicParameters(BilinearGroup bilinearGroup, int messageLength){ + super(bilinearGroup); + this.messageLength = messageLength; + + // as SPSPublicParameters precompute G and H itself, we do not need to precompute here + } + + public SPSKPW15PublicParameters(Representation repr) { + new ReprUtil(this).deserialize(repr); + } + + + public int getMessageLength() { return messageLength; } + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public int hashCode() { + return Objects.hash(bilinearGroup, group1ElementG, group2ElementH, messageLength); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + + SPSKPW15PublicParameters that = (SPSKPW15PublicParameters) o; + return Objects.equals(bilinearGroup, that.bilinearGroup) + && Objects.equals(group1ElementG, that.group1ElementG) + && Objects.equals(group2ElementH, that.group2ElementH) + && messageLength == that.messageLength; + } + +} \ No newline at end of file diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15Signature.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15Signature.java new file mode 100644 index 00000000..9ff3c10e --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15Signature.java @@ -0,0 +1,112 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.math.hash.ByteAccumulator; +import org.cryptimeleon.math.hash.UniqueByteRepresentable; +import org.cryptimeleon.math.hash.annotations.AnnotatedUbrUtil; +import org.cryptimeleon.math.hash.annotations.UniqueByteRepresented; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * A signature as defined by the KPW15 SPS scheme. + */ +public class SPSKPW15Signature implements Signature, UniqueByteRepresentable { + + /** + * First group element of the signature \in G_1. + */ + @UniqueByteRepresented + @Represented(restorer = "[G1]") + protected GroupElement group1ElementSigma1R[]; + + /** + * Second group element of the signature \in G_1. + */ + @UniqueByteRepresented + @Represented(restorer = "[G1]") + protected GroupElement group1ElementSigma2S[]; + + /** + * Third group element of the signature \in G_1. + */ + @UniqueByteRepresented + @Represented(restorer = "[G1]") + protected GroupElement group1ElementSigma3T[]; + + /** + * Fourth group element of the signature \in G_2. + */ + @UniqueByteRepresented + @Represented(restorer = "G2") + protected GroupElement group2ElementSigma4U; + + + public SPSKPW15Signature(Representation repr, Group groupG1, Group groupG2) { + new ReprUtil(this).register(groupG1,"G1").register(groupG2,"G2").deserialize(repr); + } + + public SPSKPW15Signature(GroupElement[] group1ElementSigma1R, + GroupElement[] group1ElementSigma2S, + GroupElement[] group1ElementSigma3T, + GroupElement group2ElementSigma4U) { + super(); + this.group1ElementSigma1R = group1ElementSigma1R; + this.group1ElementSigma2S = group1ElementSigma2S; + this.group1ElementSigma3T = group1ElementSigma3T; + this.group2ElementSigma4U = group2ElementSigma4U; + } + + + public GroupElement[] getGroup1ElementSigma1R() { + return group1ElementSigma1R; + } + + public GroupElement[] getGroup1ElementSigma2S() { + return group1ElementSigma2S; + } + + public GroupElement[] getGroup1ElementSigma3T() { + return group1ElementSigma3T; + } + + public GroupElement getGroup2ElementSigma4U() { + return group2ElementSigma4U; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public ByteAccumulator updateAccumulator(ByteAccumulator accumulator) { return AnnotatedUbrUtil.autoAccumulate(accumulator, this); } + + @Override + public byte[] getUniqueByteRepresentation() { + return UniqueByteRepresentable.super.getUniqueByteRepresentation(); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSKPW15Signature that = (SPSKPW15Signature) o; + + return Arrays.equals(group1ElementSigma1R, that.group1ElementSigma1R) + && Arrays.equals(group1ElementSigma2S, that.group1ElementSigma2S) + && Arrays.equals(group1ElementSigma3T, that.group1ElementSigma3T) + && Objects.equals(group2ElementSigma4U, that.group2ElementSigma4U); + } + + @Override + public int hashCode() { + return Objects.hash(group1ElementSigma1R, group1ElementSigma2S, group1ElementSigma3T, group2ElementSigma4U); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SignatureScheme.java new file mode 100644 index 00000000..2d7412ad --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SignatureScheme.java @@ -0,0 +1,647 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.cartesian.Vector; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.groups.cartesian.GroupElementVector; +import org.cryptimeleon.math.structures.groups.elliptic.BilinearMap; +import org.cryptimeleon.math.structures.rings.zn.Zp; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +import java.util.Arrays; +import java.util.Objects; +import java.util.stream.IntStream; + +/** + * A simplified implementation of the SPS scheme originally presented in [1] by Kiltz et al. as seen in [2] + * Signs a vector of {n} group elements in G_1. + * + * This simplification is achieved by setting the parameter "k" in the paper to the fixed value of 1. + * Consequently, this simplified scheme is secure under the SXDH assumption [1 , p. 3] + * + *

+ * Bilinear map type: 3 + *

+ * + * [1] Kiltz, E.,Pan, J., Wee, H.: + * Structure-Preserving Signatures from Standard Assumptions, Revisited + * https://eprint.iacr.org/2015/604.pdf + * + * [2] Sakai, Y., Attrapadung, N., Hanaoka, G.: + * Attribute-Based Signatures for Circuits from Bilinear Map + * https://eprint.iacr.org/2016/242.pdf + */ +public class SPSKPW15SignatureScheme implements MultiMessageStructurePreservingSignatureScheme { + + /** + * The public parameters used by the scheme + */ + @Represented + SPSKPW15PublicParameters pp; + + /** + * Performs a limited set of matrix operations required by the KPW15 SPS scheme. + */ + static class MatrixUtility { + + /** + * Interpret two arrays of {@link ZpElement}s as matrices and multiply them + */ + public static ZpElement[] matrixMul(ZpElement[] A, int rowsA, int columnsA, + ZpElement[] B, int rowsB, int columnsB) { + //check if matrices can be multiplied + if(A.length != rowsA * columnsA || B.length != rowsB * columnsB) { + throw new IllegalArgumentException("The given vector's length does not match its matrix dimensions" ); + } + + if(columnsA != rowsB) { + throw new IllegalArgumentException( + String.format("function is only defined for matrices where columns_A == rows_B : got %d vs. %d", + columnsA, + rowsB) + ); + } + + ZpElement[] multiplied = new ZpElement[rowsA * columnsB]; + + // calculate the individual elements + + for (int r = 1; r <= rowsA; r++) { + for (int c = 1; c <= columnsB; c++) { + + ZpElement value = B[0].getStructure().getZeroElement(); + + for (int i = 1; i <= columnsA; i++) { + value = value.add( + A[getMatrixIndex(rowsA, columnsA, r, i)].mul(B[getMatrixIndex(rowsB, columnsB, i, c)])); + } + + multiplied[getMatrixIndex(rowsA, columnsB, r,c)] = value; + } + } + + return multiplied; + } + + /** + * Kiltz et al. define e(A,B) for two matrices as AxB. + * We apply the bilinear map to each row/column in order to calculate the result. + */ + public static GroupElementVector matrixApplyMap(BilinearMap bMap, + GroupElementVector A, int rowsA, int columnsA, + GroupElementVector B, int rowsB, int columnsB) { + //check if matrices can be multiplied + if(A.length() != rowsA * columnsA || B.length() != rowsB * columnsB) { + throw new IllegalArgumentException("The given vectors length does not match its matrix dimensions"); + } + + if(columnsA != rowsB) { + throw new IllegalArgumentException( + String.format("function is only defined for matrices where columns_A == rows_B : got %d x %d", + columnsA, + rowsB) + ); + } + + GroupElement[] multiplied = new GroupElement[rowsA * columnsB]; + + // now, calculate the individual elements + + for (int r = 1; r <= rowsA; r++) { + for (int c = 1; c <= columnsB; c++) { + + GroupElement value = bMap.getGT().getNeutralElement(); + + for (int i = 1; i <= columnsA; i++) { + value = value.op( + bMap.apply(A.get(getMatrixIndex(rowsA, columnsA, r, i)), + B.get(getMatrixIndex(rowsB, columnsB, i, c)) + ) + ); + } + + value.compute(); + multiplied[getMatrixIndex(rowsA, columnsB, r,c)] = value; + } + } + + return new GroupElementVector(multiplied); + } + + /** + * Calculate a linear index for the given matrix position + */ + public static int getMatrixIndex(int rows, int columns, int row, int column) + { + return (rows * (column - 1)) + (row - 1); + } + + + /** + * Utility method that calculates e((1,m),K) as specified in the signing function. + */ + public static GroupElement[] calculateSigma1MatrixMxK(GroupElement[] message, ZpElement[] K) { + + // multiplying message(1 x n+1 matrix) and K(n+1 x 2 matrix) results in a 1 x 2 matrix + + int rows = 1; + int columns = 2; + + GroupElement[] multiplied = new GroupElement[rows * columns]; + + for (int c = 1; c <= columns; c++) { + GroupElement value = message[0].getStructure().getNeutralElement(); + + for (int i = 1; i <= message.length; i++) { + + // we may multiply here because K is in Zp. This would not work for two GroupElement matrices + ZpElement exponentK = K[getMatrixIndex(message.length, 2, i, c)]; + GroupElement messageElement = message[i - 1]; + + value = value.op(messageElement.pow(exponentK)); + } + + value.compute(); + multiplied[c-1] = value; + } + + return multiplied; + } + + } + + + public SPSKPW15SignatureScheme() { super(); } + + public SPSKPW15SignatureScheme(SPSKPW15PublicParameters pp) { + super(); + this.pp = pp; + } + + public SPSKPW15SignatureScheme(Representation repr) { new ReprUtil(this).deserialize(repr); } + + + @Override + public SignatureKeyPair generateKeyPair(int numberOfMessages) { + + Zp zp = pp.getZp(); + + if(numberOfMessages < 1){ + throw new IllegalArgumentException( + "The signature scheme KPW15 expects to sign at least 1 element" + ); + } + + // generate A,B (2 x 1 matrices) + + ZpElement[] A = new ZpElement[]{zp.getOneElement(), zp.getUniformlyRandomElement()}; // A: 2x1 + ZpElement[] B = new ZpElement[]{zp.getOneElement(), zp.getUniformlyRandomElement()}; // B: 2x1 + + // generate K ((n + 1) x 2 matrix) + + ZpElement[] K = IntStream.range(0, (numberOfMessages + 1) * 2).mapToObj( + x -> zp.getUniformlyRandomElement()) + .toArray(ZpElement[]::new); + + // generate K0,K1 (2 x 2 matrices) + + ZpElement[] K0 = IntStream.range(0, 2 * 2).mapToObj( + x -> zp.getUniformlyRandomElement()) + .toArray(ZpElement[]::new); + + ZpElement[] K1 = IntStream.range(0, 2 * 2).mapToObj( + x -> zp.getUniformlyRandomElement()) + .toArray(ZpElement[]::new); + + // calculate C ((n+1) x 1 matrix) + + ZpElement[] C = MatrixUtility.matrixMul( + K, (numberOfMessages + 1), 2, + A, 2, 1); //K.mul(A) + + // calculate C0, C1 (2 x 1 matrix) + + ZpElement[] C0 = MatrixUtility.matrixMul( + K0, 2, 2, + A, 2, 1); //K0.mul(A) + + ZpElement[] C1 = MatrixUtility.matrixMul( + K1, 2, 2, + A, 2, 1); //K1.mul(A) + + // calculate P0, P1 (1 x 2) + + //Note that we transpose B implicitly here, as it only contains 2 elements anyway + ZpElement[] P0 = MatrixUtility.matrixMul( + B, 1, 2, + K0, 2, 2 + ); //BT.mul(K0) + + ZpElement[] P1 = MatrixUtility.matrixMul( + B, 1, 2, + K1, 2, 2 + ); //BT.mul(K1) + + // pack keys + + SPSKPW15SigningKey sk = new SPSKPW15SigningKey( + K, + pp.getG1GroupGenerator().pow(new Vector(P0)).compute().stream().toArray(GroupElement[]::new), + pp.getG1GroupGenerator().pow(new Vector(P1)).compute().stream().toArray(GroupElement[]::new), + pp.getG1GroupGenerator().pow(B[1]).compute() + ); + + SPSKPW15VerificationKey vk = new SPSKPW15VerificationKey( + pp.getG2GroupGenerator().pow(new Vector(C0)).compute().stream().toArray(GroupElement[]::new), + pp.getG2GroupGenerator().pow(new Vector(C1)).compute().stream().toArray(GroupElement[]::new), + pp.getG2GroupGenerator().pow(new Vector(C)).compute().stream().toArray(GroupElement[]::new), + pp.getG2GroupGenerator().pow(A[1]).compute() + ); + + return new SignatureKeyPair(vk, sk); + } + + @Override + public Signature sign(PlainText plainText, SigningKey secretKey) { + + if((plainText instanceof GroupElementPlainText)){ + plainText = new MessageBlock(plainText); //if only a single element was given, wrap it in a MessageBlock + } + + // check if the plainText matches the structure required by the scheme + doMessageChecks(plainText); + + MessageBlock messageBlock = (MessageBlock) plainText; + messageBlock.prepend(new GroupElementPlainText(pp.getG1GroupGenerator())); + + if (!(secretKey instanceof SPSKPW15SigningKey)) { + throw new IllegalArgumentException("Not a valid signing key for this scheme"); + } + + SPSKPW15SigningKey sk = (SPSKPW15SigningKey) secretKey; + + //pick randomness r0, r1 + + ZpElement r0 = pp.getZp().getUniformlyRandomElement(); + + ZpElement r1 = pp.getZp().getUniformlyRandomElement(); + + + //calculate sigma1 (1 x 2 matrix) + + GroupElement[] message = new GroupElement[messageBlock.length()+1]; + + message[0] = pp.getG1GroupGenerator(); + + for (int i = 1; i <= messageBlock.length(); i++) { + message[i] = ((GroupElementPlainText) messageBlock.get(i-1)).get(); + } + + GroupElement[] sigma1lhs = MatrixUtility.calculateSigma1MatrixMxK(message, sk.getK()); + + GroupElement[] sigma1rhsInner = Arrays.stream(sk.getP1()).map( + x -> x.pow(r1).compute() + ).toArray(GroupElement[]::new); + + for (int i = 0; i < sigma1rhsInner.length; i++) { + sigma1rhsInner[i] = sk.getP0()[i].op(sigma1rhsInner[i]); + sigma1rhsInner[i] = sigma1rhsInner[i].pow(r0); + sigma1rhsInner[i].compute(); + } + + GroupElement[] sigma1 = new GroupElement[sigma1lhs.length]; + + for (int i = 0; i < sigma1.length; i++) { + sigma1[i] = sigma1lhs[i].op(sigma1rhsInner[i]).compute(); + } + + + //calculate sigma2 (1 x 2 matrix) + + GroupElement[] sigma2 = new Vector(pp.getG1GroupGenerator(), sk.getB()).stream().map( + x -> x.pow(r0).compute() + ).toArray(GroupElement[]::new); + + //calculate sigma3 ( 1 x 2 matrix) + + GroupElement[] sigma3 = Arrays.stream(sigma2).map( + x -> x.pow(r1) + ).toArray(GroupElement[]::new); + + //calculate sigma4 (single element) + + GroupElement sigma4 = pp.getG2GroupGenerator().pow(r1).compute(); + + //System.out.println("check sigma1: " + checkSigma1(sigma1, message, sk.getK(), r0, r1, sk.getP0(), sk.getP1())); + + return new SPSKPW15Signature(sigma1, sigma2, sigma3, sigma4); + } + + private boolean checkSigma1(GroupElement[] sigma1, + GroupElement[] paddedMessage, + ZpElement[] K, + ZpElement r0, ZpElement r1, + GroupElement[] P0, GroupElement[] P1 ) { + + // n = 1 + // padded message^T (1 x 2) x K (2 x 2) -> 1 x 2 + + GroupElement[] lhs = new GroupElement[1 * 2]; + + lhs[0] = paddedMessage[0].pow(K[0]).op(paddedMessage[1].pow(K[1])).compute(); + lhs[1] = paddedMessage[0].pow(K[2]).op(paddedMessage[1].pow(K[3])).compute(); + + // r0 (P0 + r1 * P1) + + GroupElement[] r1P1 = new GroupElement[1 * 2]; + + r1P1[0] = P1[0].pow(r1).compute(); + r1P1[1] = P1[1].pow(r1).compute(); + + GroupElement[] P0r1P1 = new GroupElement[2]; + + P0r1P1[0] = P0[0].op(r1P1[0]).compute(); + P0r1P1[1] = P0[1].op(r1P1[1]).compute(); + + GroupElement[] rhs = new GroupElement[2]; + + rhs[0] = P0r1P1[0].pow(r0).compute(); + rhs[1] = P0r1P1[1].pow(r0).compute(); + + GroupElement[] checkSig = new GroupElement[2]; + + checkSig[0] = lhs[0].op(rhs[0]).compute(); + checkSig[1] = lhs[1].op(rhs[1]).compute(); + + + return sigma1[0].equals(checkSig[0]) && sigma1[1].equals(checkSig[1]); + } + + @Override + public Boolean verify(PlainText plainText, Signature signature, VerificationKey publicKey) { + + if((plainText instanceof GroupElementPlainText)){ + plainText = new MessageBlock(plainText); //if only a single element was given, wrap it in a MessageBlock + } + + // check if the plainText matches the structure required by the scheme + doMessageChecks(plainText); + + if(!(signature instanceof SPSKPW15Signature)){ + throw new IllegalArgumentException("Not a valid signature for this scheme"); + } + + if(!(publicKey instanceof SPSKPW15VerificationKey)){ + throw new IllegalArgumentException("Not a valid verification key for this scheme"); + } + + MessageBlock messageBlock = (MessageBlock) plainText; + // we need the vector (1,m) for the PPEs + messageBlock = padMessage(messageBlock); + + SPSKPW15Signature sigma = (SPSKPW15Signature) signature; + SPSKPW15VerificationKey pk = (SPSKPW15VerificationKey) publicKey; + + //pull from pk + + GroupElementVector C0 = new GroupElementVector(pk.getC0()); + GroupElementVector C1 = new GroupElementVector(pk.getC1()); + GroupElementVector C = new GroupElementVector(pk.getC()); + + //pull from sigma + + GroupElementVector sigma1 = new GroupElementVector(sigma.getGroup1ElementSigma1R()); + GroupElementVector sigma2 = new GroupElementVector(sigma.getGroup1ElementSigma2S()); + GroupElementVector sigma3 = new GroupElementVector(sigma.getGroup1ElementSigma3T()); + //sigma4 is only a single group element + + + return evaluateFirstPPE(sigma1, sigma2, sigma3, messageBlock, C, C0, C1, pk.getA()) + && evaluateSecondPPE(sigma2, sigma.getGroup2ElementSigma4U(), sigma3); + } + + + /** + * Evaluates the first PPE as defined in the paper. + */ + private boolean evaluateFirstPPE(GroupElementVector sigma1, + GroupElementVector sigma2, + GroupElementVector sigma3, + MessageBlock paddedMessage, + GroupElementVector C, + GroupElementVector C0, + GroupElementVector C1, + GroupElement A) { + + BilinearMap bMap = pp.getBilinearMap(); + + GroupElement[] message = paddedMessage.stream().map( + x->((GroupElementPlainText)x).get() + ).toArray(GroupElement[]::new); + + //for matrices, Kiltz et al. define e(A,B) = AxB + //note how these all result in a 1x1 matrix / a single group element + + GroupElementVector ppe1lhs = MatrixUtility.matrixApplyMap( + bMap, + sigma1, 1, 2, + new GroupElementVector(pp.getG2GroupGenerator(), A), 2, 1 + ).compute(); + + GroupElementVector ppe1rhs1 = MatrixUtility.matrixApplyMap( + bMap, + new GroupElementVector(message), 1, message.length, + C, message.length, 1); + + GroupElementVector ppe1rhs2 = MatrixUtility.matrixApplyMap( + bMap, + sigma2, 1, 2, + C0, 2, 1); + + GroupElementVector ppe1rhs3 = MatrixUtility.matrixApplyMap(bMap, + sigma3, 1, 2, + C1, 2, 1); + + GroupElementVector ppe1rhs = ppe1rhs1.op(ppe1rhs2).op(ppe1rhs3).compute(); + + return ppe1lhs.equals(ppe1rhs); + } + + /** + * Evaluates the second PPE as defined in the paper. + */ + private boolean evaluateSecondPPE(GroupElementVector sigma2, GroupElement sigma4, GroupElementVector sigma3) { + + BilinearMap bMap = pp.getBilinearMap(); + + GroupElementVector ppe2lhs = MatrixUtility.matrixApplyMap( + bMap, + sigma2, 1, 2, + new GroupElementVector(sigma4, sigma4), 2, 1 + ); + + GroupElementVector ppe2rhs = MatrixUtility.matrixApplyMap( + bMap, + sigma3, 1, 2, + new GroupElementVector(pp.getG2GroupGenerator(), pp.getG2GroupGenerator()), 2, 1 + ); + + return ppe2lhs.equals(ppe2rhs); + } + + /** + * Messages given to this scheme must be padded with one instance of the G_1 generator set in the public parameters. + * This is required in order for the dimensions of the message and matrices to match. + */ + private MessageBlock padMessage(MessageBlock messageBlock) { + return new MessageBlock( + messageBlock.prepend( + new GroupElementPlainText(pp.getG1GroupGenerator()) + ) + ); + } + + /** + * Check if the given plainText matches the structure expected by the scheme + * and throws detailed exception if the plainText fails any check. + * The scheme expects messages containing a vector of{@link GroupElementPlainText}s in G_1. + * Size must match the public parameters. + * */ + private void doMessageChecks(PlainText plainText) { + + MessageBlock messageBlock; + + // The scheme expects a MessageBlock... + if(plainText instanceof MessageBlock) { + messageBlock = (MessageBlock) plainText; + } + else { + throw new IllegalArgumentException("The scheme requires its messages to be GroupElements"); + } + + // ...with a size matching the public parameters... + if(messageBlock.length() != pp.messageLength) { + throw new IllegalArgumentException(String.format( + "The scheme expected a message of length %d, but the size was: %d", + pp.messageLength, messageBlock.length() + )); + } + + // ...containing group elements... + for (int i = 0; i < messageBlock.length(); i++) { + if(!(messageBlock.get(i) instanceof GroupElementPlainText)) { + throw new IllegalArgumentException( + String.format( + "The scheme requires its messages to be GroupElements," + + " but element %d was of type: %s", + i, messageBlock.get(i).getClass().toString() + ) + ); + } + + // ... in G1. + GroupElementPlainText groupElementPT = (GroupElementPlainText) messageBlock.get(i); + if(!(groupElementPT.get().getStructure().equals(pp.getG1GroupGenerator().getStructure()))) { + throw new IllegalArgumentException( + String.format( + "Expected message to be in G_1, but element %d was in: %s", + i, groupElementPT.get().getStructure().toString() + ) + ); + } + } + + // if no exception has been thrown at this point, we can assume the message matches the expected structure. + } + + + @Override + public MessageBlock restorePlainText(Representation repr) { + return new MessageBlock(repr, r -> new GroupElementPlainText(r, pp.getG1GroupGenerator().getStructure())); + } + + @Override + public Signature restoreSignature(Representation repr) { + return new SPSKPW15Signature(repr, + this.pp.getG1GroupGenerator().getStructure(), + this.pp.getG2GroupGenerator().getStructure()); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new SPSKPW15SigningKey(repr, this.pp.getZp(), pp.getG1GroupGenerator().getStructure()); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new SPSKPW15VerificationKey(this.pp.getG1GroupGenerator().getStructure(), + this.pp.getG2GroupGenerator().getStructure(), + repr); + } + + + @Override + public PlainText mapToPlaintext(byte[] bytes, VerificationKey pk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + return mapToPlaintext(bytes, pp.messageLength); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, SigningKey sk) { + if(pp == null) + { + throw new NullPointerException("Number of messages is stored in public parameters but they are not set"); + } + return mapToPlaintext(bytes, pp.messageLength); + } + + private MessageBlock mapToPlaintext(byte[] bytes, int messageLength) { + // returns (P^m, P, ..., P) where m = Z_p.injectiveValueOf(bytes). + + GroupElementPlainText[] msgBlock = new GroupElementPlainText[messageLength]; + msgBlock[0] = new GroupElementPlainText( + pp.getG1GroupGenerator().pow(pp.getZp().injectiveValueOf(bytes)) + ); + for (int i = 1; i < msgBlock.length; i++) { + msgBlock[i] = new GroupElementPlainText(pp.getG1GroupGenerator()); + } + + return new MessageBlock(msgBlock); + } + + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return (pp.getG1GroupGenerator().getStructure().size().bitLength() - 1) / 8; + } + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + + @Override + public int hashCode() { + final int prime = 41; + int result = 1; + result = prime * result + ((pp == null) ? 0 : pp.hashCode()); + return result; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSKPW15SignatureScheme that = (SPSKPW15SignatureScheme) o; + return Objects.equals(pp, that.pp); + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SigningKey.java new file mode 100644 index 00000000..e9bd4d80 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15SigningKey.java @@ -0,0 +1,120 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.cartesian.Vector; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Class for the secret (signing) key of the KPW15 signature scheme. + * + * Note: the names of the elements have been carried over from the paper as-is. + */ +public class SPSKPW15SigningKey implements SigningKey { + + /** + * (n+1 x k+1) Matrix K in the paper + * */ + @Represented(restorer = "[Zp]") + protected Zp.ZpElement K[]; + + /** + * (k x k+1) Matrix P0 in the paper. + * */ + @Represented(restorer = "[G1]") + protected GroupElement P0[]; + + /** + * (k x k+1) Matrix P1 in the paper + * */ + @Represented(restorer = "[G1]") + protected GroupElement P1[]; + + /** + * B in the paper (note that since k = 1), B is just a single group element here + * */ + @Represented(restorer = "G1") + protected GroupElement B; + + + public SPSKPW15SigningKey() { super(); } + + public SPSKPW15SigningKey(Representation representation, Zp zp, Group G_1) { + new ReprUtil(this).register(zp, "Zp").register(G_1, "G1").deserialize(representation); + } + + public SPSKPW15SigningKey(Zp.ZpElement[] K, GroupElement[] P0, GroupElement[] P1, GroupElement B){ + super(); + this.K = K; + this.P0 = P0; + this.P1 = P1; + this.B = B; + } + + public SPSKPW15SigningKey(Vector K, + Vector P0, + Vector P1, + GroupElement B){ + + this( + K.stream().toArray(Zp.ZpElement[]::new), + P0.stream().toArray(GroupElement[]::new), + P1.stream().toArray(GroupElement[]::new), + B + ); + } + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + + public Zp.ZpElement[] getK() { + return K; + } + + public void setK(Zp.ZpElement[] K) { + this.K = K; + } + + public GroupElement[] getP0() { + return P0; + } + + public GroupElement[] getP1() { + return P1; + } + + public GroupElement getB() { + return B; + } + + public void setB(GroupElement B) { + this.B = B; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSKPW15SigningKey that = (SPSKPW15SigningKey) o; + return Arrays.equals(K, that.K) && Arrays.equals(P0, that.P0) + && Arrays.equals(P1, that.P1) && Objects.equals(B, that.B); + } + + @Override + public int hashCode() { + int result = Objects.hash(B); + result = 31 * result + Arrays.hashCode(K); + result = 31 * result + Arrays.hashCode(P0); + result = 31 * result + Arrays.hashCode(P1); + return result; + } + +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15VerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15VerificationKey.java new file mode 100644 index 00000000..b631dc14 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/kpw15/SPSKPW15VerificationKey.java @@ -0,0 +1,119 @@ +package org.cryptimeleon.craco.sig.sps.kpw15; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.annotations.ReprUtil; +import org.cryptimeleon.math.serialization.annotations.Represented; +import org.cryptimeleon.math.structures.cartesian.Vector; +import org.cryptimeleon.math.structures.groups.Group; +import org.cryptimeleon.math.structures.groups.GroupElement; + +import java.util.Arrays; +import java.util.Objects; + +/** + * Class for the public (verification) key of the KPW15 signature scheme. + * + * Note: the names of the elements have been carried over from the paper as-is. + */ +public class SPSKPW15VerificationKey implements VerificationKey { + + // C0, C1, C, A (all in G_2) + + /** + * (k+1 x k) Matrix C0 in the paper + * */ + @Represented(restorer = "[G2]") + protected GroupElement C0[]; + + /** + * (k+1 x k) Matrix C1 in the paper + * */ + @Represented(restorer = "[G2]") + protected GroupElement C1[]; + + /** + * (n+1 x k) Matrix C in the paper + * */ + @Represented(restorer = "[G2]") + protected GroupElement C[]; + + /** + * A in the paper (note that since k = 1), A is just a single group element here + * */ + @Represented(restorer = "G2") + protected GroupElement A; + + + public SPSKPW15VerificationKey() { super(); } + + public SPSKPW15VerificationKey(Group G_1, Group G_2, Representation repr) { + new ReprUtil(this).register(G_1, "G1").register(G_2, "G2").deserialize(repr); + } + + public SPSKPW15VerificationKey(Vector C0, + Vector C1, + Vector C, + GroupElement A) { + this( + C0.stream().toArray(GroupElement[]::new), + C1.stream().toArray(GroupElement[]::new), + C.stream().toArray(GroupElement[]::new), + A + ); + } + + public SPSKPW15VerificationKey(GroupElement[] C0, GroupElement[] C1, + GroupElement[] C, GroupElement A) { + this.C0 = C0; + this.C1 = C1; + this.C = C; + this.A = A; + } + + + public GroupElement[] getC0() { + return C0; + } + + public GroupElement[] getC1() { + return C1; + } + + public GroupElement[] getC() { return C; } + + public void setC(GroupElement[] c) { + C = c; + } + + public GroupElement getA() { + return A; + } + + public void setA(GroupElement a) { + A = a; + } + + + @Override + public Representation getRepresentation() { return ReprUtil.serialize(this); } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + SPSKPW15VerificationKey that = (SPSKPW15VerificationKey) o; + return Arrays.equals(C0, that.C0) && Arrays.equals(C1, that.C1) + && Arrays.equals(C, that.C) && Objects.equals(A, that.A); + } + + @Override + public int hashCode() { + int result = Objects.hash(A); + result = 31 * result + Arrays.hashCode(C0); + result = 31 * result + Arrays.hashCode(C1); + result = 31 * result + Arrays.hashCode(C); + return result; + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/CommitmentStandaloneReprTests.java b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/CommitmentStandaloneReprTests.java index 76ebe8da..8cb1deaf 100644 --- a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/CommitmentStandaloneReprTests.java +++ b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/CommitmentStandaloneReprTests.java @@ -2,6 +2,16 @@ import org.cryptimeleon.craco.commitment.hashthencommit.HashThenCommitCommitmentScheme; import org.cryptimeleon.craco.commitment.pedersen.PedersenCommitmentScheme; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentScheme; +import org.cryptimeleon.craco.sig.sps.akot15.tc.TCAKOT15CommitmentSchemeTestParameterGenerator; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15CommitmentScheme; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15CommitmentSchemeTestParameterGenerator; +import org.cryptimeleon.craco.sig.sps.CommitmentSchemeParams; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15XSIGPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParametersGen; import org.cryptimeleon.math.serialization.standalone.StandaloneReprSubTest; import org.cryptimeleon.math.hash.impl.SHA256HashFunction; import org.cryptimeleon.math.structures.rings.zn.Zn; @@ -17,6 +27,33 @@ public void testPedersen() { test(pedersen); } + public void testTCGAKOT15() { + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters( + 128, 20, true); + + TCGAKOT15CommitmentScheme scheme = new TCGAKOT15CommitmentScheme(pp); + + test(pp); + test(scheme); + + // test xSIG variant of the scheme + + SPSXSIGPublicParameters ppXSIG = SPSXSIGPublicParametersGen.generatePublicParameters(128, 20, true); + TCGAKOT15XSIGPublicParameters ppTCGXSIG = new TCGAKOT15XSIGPublicParameters(ppXSIG, 20); + + test(ppTCGXSIG); + } + + public void testTCAKOT15() { + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters( + 128, 20, true); + + TCAKOT15CommitmentScheme scheme = new TCAKOT15CommitmentScheme(pp); + + test(pp); + test(scheme); + } + public void testHashThenCommit() { test(new HashThenCommitCommitmentScheme(pedersen, new SHA256HashFunction())); } diff --git a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java index 5887e7c6..cea046cb 100644 --- a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java +++ b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java @@ -1,8 +1,23 @@ package org.cryptimeleon.craco.ser.standalone.params; +import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; +import org.cryptimeleon.craco.sig.sps.SPSPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.agho11.SPSAGHO11PublicParameters; +import org.cryptimeleon.craco.sig.sps.agho11.SPSAGHO11PublicParametersGen; +import org.cryptimeleon.craco.sig.sps.agho11.SPSAGHO11SignatureScheme; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme; +import org.cryptimeleon.craco.sig.sps.akot15.pos.SPSPOSSignatureScheme; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGSignatureScheme; import org.cryptimeleon.craco.sig.sps.groth15.SPSGroth15PublicParameters; import org.cryptimeleon.craco.sig.sps.groth15.SPSGroth15PublicParametersGen; import org.cryptimeleon.craco.sig.sps.groth15.SPSGroth15SignatureScheme; +import org.cryptimeleon.craco.sig.sps.kpw15.SPSKPW15PublicParameterGen; +import org.cryptimeleon.craco.sig.sps.kpw15.SPSKPW15PublicParameters; +import org.cryptimeleon.craco.sig.sps.kpw15.SPSKPW15SignatureScheme; import org.cryptimeleon.math.serialization.standalone.StandaloneReprSubTest; import org.cryptimeleon.craco.sig.bbs.BBSBKeyGen; import org.cryptimeleon.craco.sig.bbs.BBSBPublicParameter; @@ -44,6 +59,22 @@ public void testSPSGroth15() { test(pp); } + public void testSPSAGHO11() { + SPSAGHO11PublicParameters pp = SPSAGHO11PublicParametersGen.generatePublicParameters(128, true, new Integer[] {20,20}); + SPSAGHO11SignatureScheme scheme = new SPSAGHO11SignatureScheme(pp); + + test(scheme); + test(pp); + } + + public void testSPSKPW15() { + SPSKPW15PublicParameters pp = new SPSKPW15PublicParameterGen().generatePublicParameter(128, true, 20); + SPSKPW15SignatureScheme scheme = new SPSKPW15SignatureScheme(pp); + + test(scheme); + test(pp); + } + public void testPS() { test(pp); test(new PSSignatureScheme(pp)); @@ -60,4 +91,38 @@ public void testBBS() { test(pp); test(new BBSBSignatureScheme(pp)); } + + public void testSPSPublicParameters() { + SPSPublicParameters pp = SPSPublicParametersGen.generateParameters(128, true); + + test(pp); + } + + public void testPOS() { + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters( + 128, 20,true); + SPSPOSSignatureScheme scheme = new SPSPOSSignatureScheme(pp); + + test(scheme); + test(pp); + } + + public void testXSIG() { + SPSXSIGPublicParameters pp = SPSXSIGPublicParametersGen.generatePublicParameters( + 128, 20,true); + SPSXSIGSignatureScheme scheme = new SPSXSIGSignatureScheme(pp); + + test(scheme); + test(pp); + } + + public void testFSP2() { + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters( + 128, 20,true); + SPSFSP2SignatureScheme scheme = new SPSFSP2SignatureScheme(pp); + + test(scheme); + test(pp); + } + } diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/CommitmentSchemeParams.java b/src/test/java/org/cryptimeleon/craco/sig/sps/CommitmentSchemeParams.java new file mode 100644 index 00000000..a4c2f562 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/CommitmentSchemeParams.java @@ -0,0 +1,49 @@ +package org.cryptimeleon.craco.sig.sps; + +import org.cryptimeleon.craco.commitment.CommitmentKey; +import org.cryptimeleon.craco.commitment.CommitmentScheme; +import org.cryptimeleon.craco.common.PublicParameters; +import org.cryptimeleon.craco.common.plaintexts.PlainText; + +/** + * A set of parameters used for testing CommitmentSchemes + * + */ +public class CommitmentSchemeParams { + + private PublicParameters publicParameters; + private PlainText plainText; + private PlainText wrongPlainText; + private CommitmentScheme scheme; + private CommitmentKey commitmentKey; + + public CommitmentSchemeParams(PublicParameters publicParameters, PlainText plainText, PlainText wrongPlainText, CommitmentScheme scheme, CommitmentKey commitmentKey) { + this.publicParameters = publicParameters; + this.plainText = plainText; + this.wrongPlainText = wrongPlainText; + this.scheme = scheme; + this.commitmentKey = commitmentKey; + } + + + public PublicParameters getPublicParameters() { + return publicParameters; + } + + public PlainText getPlainText() { + return plainText; + } + + public PlainText getWrongPlainText() { + return wrongPlainText; + } + + public CommitmentScheme getScheme() { + return scheme; + } + + public CommitmentKey getCommitmentKey() { + return commitmentKey; + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/SPSSchemeTester.java b/src/test/java/org/cryptimeleon/craco/sig/sps/SPSSchemeTester.java new file mode 100644 index 00000000..d5995d1d --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/SPSSchemeTester.java @@ -0,0 +1,85 @@ +package org.cryptimeleon.craco.sig.sps; + +import org.cryptimeleon.craco.sig.*; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +/** + * Generic implementation of SPS scheme tests. + * */ +public abstract class SPSSchemeTester { + + protected static int testIterations = 5; + protected static int NUM_MESSAGES = 32; + protected static int SECURITY_PARAMETER = 128; + + protected SignatureSchemeParams params; + + protected abstract SignatureSchemeParams generateParameters(); + + + @Before + public void setUp() throws Exception { + params = generateParameters(); + } + + @Test + public void testSignatureAndVerify() { + // signing a block of messages + for (int i = 0; i < testIterations; i++) { + SignatureSchemeTester.testSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey() + ); + } + } + + @Test + public void testNegativeSignatureAndVerify() { + // signing a block of messages + for (int i = 0; i < testIterations; i++) { + SignatureSchemeTester.testNegativeWrongKeysSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey(), + params.getKeyPair2().getVerificationKey(), + params.getKeyPair2().getSigningKey() + ); + } + } + + @Test + public void testSignatureSchemeRepresentationText() { + // Test standard signature scheme representations + SignatureSchemeTester.testRepresentationSignatureScheme( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey() + ); + } + + @Test + public void testMapToPlaintext() { + SignatureSchemeTester.testMapToPlaintext( + params.getSignatureScheme(), + params.getKeyPair1().getVerificationKey()); + } + + @Test + public void testMapToPlaintextContract() { + SignatureSchemeTester.testMapToPlainTextContract( + params.getSignatureScheme(), + params.getKeyPair1() + ); + } + + @Test + public abstract void testPublicParameterRepresentation(); + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTest.java b/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTest.java new file mode 100644 index 00000000..921c3097 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTest.java @@ -0,0 +1,25 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + +import org.cryptimeleon.craco.sig.*; +import org.cryptimeleon.craco.sig.sps.SPSSchemeTester; + +import static org.junit.Assert.assertEquals; + +public class SPSAGHO11SignatureSchemeTest extends SPSSchemeTester { + + private Integer[] msgBlockLengths; + + @Override + protected SignatureSchemeParams generateParameters() { + msgBlockLengths = new Integer[] {NUM_MESSAGES, NUM_MESSAGES}; + return SPSAGHO11SignatureSchemeTestParamGenerator.generateParams(SECURITY_PARAMETER, msgBlockLengths); + } + + @Override + public void testPublicParameterRepresentation() { + SPSAGHO11PublicParameters ppTest; + ppTest = new SPSAGHO11PublicParameters(params.getPublicParameters().getRepresentation()); + + assertEquals(params.getPublicParameters(), ppTest); + } +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTestParamGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTestParamGenerator.java new file mode 100644 index 00000000..3fe16b09 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/agho11/SPSAGHO11SignatureSchemeTestParamGenerator.java @@ -0,0 +1,89 @@ +package org.cryptimeleon.craco.sig.sps.agho11; + + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; + +import java.util.Arrays; + +public class SPSAGHO11SignatureSchemeTestParamGenerator { + + /** + * Generate a set of parameters used for testing the scheme + * */ + public static SignatureSchemeParams generateParams(int securityParameter, Integer[] messageBlockLengths){ + + //setup scheme + SPSAGHO11PublicParametersGen ppSetup = new SPSAGHO11PublicParametersGen(); + SPSAGHO11PublicParameters pp = SPSAGHO11PublicParametersGen.generatePublicParameters(securityParameter, true, messageBlockLengths); + SPSAGHO11SignatureScheme scheme = new SPSAGHO11SignatureScheme(pp); + + //generate two different key pairs to test + int[] msgBlockLengths = Arrays.stream(messageBlockLengths).mapToInt(i->i).toArray(); + + SignatureKeyPair keyPair = + scheme.generateKeyPair(msgBlockLengths); + + SignatureKeyPair wrongKeyPair; + + do{ + wrongKeyPair = scheme.generateKeyPair(msgBlockLengths); + } + while (wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) + || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey())); + + //generate two different message blocks for testing + + // first element is the valid message, second the invalid message + MessageBlock[] testMessages = generateMessageBlocks(pp, messageBlockLengths); + + return new SignatureSchemeParams(scheme, pp, testMessages[0], testMessages[1], keyPair, wrongKeyPair); + } + + + /** + * Generate two message blocks of a given length to be used for testing. + * */ + private static MessageBlock[] generateMessageBlocks(SPSAGHO11PublicParameters pp, Integer[] messageBlockLengths) { + + MessageBlock[] groupElementVectors = new MessageBlock[2]; + + for (int i = 0; i < 2; i++) { + + GroupElementPlainText[] innerBlock = new GroupElementPlainText[messageBlockLengths[i]]; + + for (int j = 0; j < messageBlockLengths[i]; j++) { + if(i == 0) + innerBlock[j] = new GroupElementPlainText(pp.getG1GroupGenerator().getStructure().getUniformlyRandomElement()); + else + innerBlock[j] = new GroupElementPlainText(pp.getG2GroupGenerator().getStructure().getUniformlyRandomElement()); + } + + groupElementVectors[i] = new MessageBlock(innerBlock); + } + + MessageBlock[] wrongGroupElementVectors = new MessageBlock[2]; + + for (int i = 0; i < 2; i++) { + + GroupElementPlainText[] wrongInnerBlock = new GroupElementPlainText[messageBlockLengths[i]]; + + for (int j = 0; j < messageBlockLengths[i]; j++) { + do{ + if(i == 0) + wrongInnerBlock[j] = new GroupElementPlainText(pp.getG1GroupGenerator().getStructure().getUniformlyRandomElement()); + else + wrongInnerBlock[j] = new GroupElementPlainText(pp.getG2GroupGenerator().getStructure().getUniformlyRandomElement()); + } + while(wrongInnerBlock[j].equals(groupElementVectors[i].get(j))); + } + + wrongGroupElementVectors[i] = new MessageBlock(wrongInnerBlock); + } + + return new MessageBlock[] {new MessageBlock(groupElementVectors), new MessageBlock(wrongGroupElementVectors)}; + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTestParameterGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTestParameterGenerator.java new file mode 100644 index 00000000..4047669b --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTestParameterGenerator.java @@ -0,0 +1,64 @@ +package org.cryptimeleon.craco.sig.sps.akot15; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2SignatureScheme; +import org.cryptimeleon.craco.sig.sps.akot15.fsp2.SPSFSP2VerificationKey; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.*; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +public class SPSAKOT15SignatureSchemeTestParameterGenerator { + + public static SignatureSchemeParams generateParameters(int securityParameter, int messageLength) { + // setup scheme + + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters( + securityParameter, messageLength, true + ); + + SPSFSP2SignatureScheme scheme = new SPSFSP2SignatureScheme(pp); + + + SignatureKeyPair keyPair = scheme.generateKeyPair( + messageLength); + + SignatureKeyPair wrongKeyPair; + + do{ + wrongKeyPair = scheme.generateKeyPair(messageLength); + }while( + wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) + || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey()) + ); + + // generate two different messages + + GroupElementPlainText[] messages = new GroupElementPlainText[messageLength]; + + for (int i = 0; i < messages.length; i++) { + messages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().pow(pp.getZp().getUniformlyRandomElement()).compute()); + } + + GroupElementPlainText[] wrongMessages = new GroupElementPlainText[messageLength]; + + for (int i = 0; i < wrongMessages.length; i++) { + + do { + wrongMessages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().pow(pp.getZp().getUniformlyRandomElement()).compute()); + + }while(wrongMessages[i].equals(messages[i])); + } + + return new SignatureSchemeParams( + scheme, + pp, + new MessageBlock(messages), + new MessageBlock(wrongMessages), + keyPair, + wrongKeyPair + ); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTests.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTests.java new file mode 100644 index 00000000..b641c6f4 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/SPSAKOT15SignatureSchemeTests.java @@ -0,0 +1,23 @@ +package org.cryptimeleon.craco.sig.sps.akot15; + +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.craco.sig.sps.SPSSchemeTester; + +import static org.junit.Assert.assertEquals; + +public class SPSAKOT15SignatureSchemeTests extends SPSSchemeTester { + + @Override + protected SignatureSchemeParams generateParameters() { + return SPSAKOT15SignatureSchemeTestParameterGenerator.generateParameters(SECURITY_PARAMETER, NUM_MESSAGES); + } + + @Override + public void testPublicParameterRepresentation() { + // public parameter representation test + AKOT15SharedPublicParameters ppTest; + ppTest = new AKOT15SharedPublicParameters(params.getPublicParameters().getRepresentation()); + assertEquals(params.getPublicParameters(), ppTest); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTestParamGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTestParamGenerator.java new file mode 100644 index 00000000..7cc26b5f --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTestParamGenerator.java @@ -0,0 +1,57 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; + +public class SPSPOSSignatureSchemeTestParamGenerator { + + public static SignatureSchemeParams generateParameters(int securityParameter, int numberOfMessages) { + + // setup scheme + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters(securityParameter, numberOfMessages, true); + SPSPOSSignatureScheme scheme = new SPSPOSSignatureScheme(pp); + + SignatureKeyPair keyPair = scheme.generateKeyPair(numberOfMessages); + + SignatureKeyPair wrongKeyPair; + do{ + wrongKeyPair = scheme.generateKeyPair(numberOfMessages); + }while( + wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) + || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey()) + ); + + // generate two different messages + + GroupElementPlainText[] messages = new GroupElementPlainText[numberOfMessages]; + + for (int i = 0; i < messages.length; i++) { + messages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().getStructure().getUniformlyRandomElement()); + } + + GroupElementPlainText[] wrongMessages = new GroupElementPlainText[numberOfMessages]; + + for (int i = 0; i < wrongMessages.length; i++) { + + do{ + wrongMessages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().getStructure().getUniformlyRandomElement()); + }while( + wrongMessages[i].equals(messages[i]) + ); + } + + return new SignatureSchemeParams( + scheme, + pp, + new MessageBlock(messages), + new MessageBlock(wrongMessages), + keyPair, + wrongKeyPair); + + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTests.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTests.java new file mode 100644 index 00000000..a2833720 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureSchemeTests.java @@ -0,0 +1,103 @@ +package org.cryptimeleon.craco.sig.sps.akot15.pos; + +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.craco.sig.SignatureSchemeTester; +import org.cryptimeleon.craco.sig.sps.SPSSchemeTester; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.xsig.SPSXSIGPublicParameters; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.rings.zn.Zp; +import org.junit.Assert; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertTrue; + +public class SPSPOSSignatureSchemeTests extends SPSSchemeTester { + + @Override + protected SignatureSchemeParams generateParameters() { + return SPSPOSSignatureSchemeTestParamGenerator.generateParameters(SECURITY_PARAMETER, NUM_MESSAGES); + } + + @Override + public void testSignatureAndVerify() { + // signing a block of messages + for (int i = 0; i < testIterations; i++) { + SignatureSchemeTester.testSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey() + ); + // re-key one-time signature + ((SPSPOSSignatureScheme)params.getSignatureScheme()).updateOneTimeKey((SignatureKeyPair) params.getKeyPair1()); + ((SPSPOSSignatureScheme)params.getSignatureScheme()).updateOneTimeKey((SignatureKeyPair) params.getKeyPair2()); + } + } + + @Test + public void testSignatureAndVerifyWithFixedOTKeys() { + + SPSPOSSignatureScheme scheme = (SPSPOSSignatureScheme) params.getSignatureScheme(); + + Zp.ZpElement fixedSecretKey = ((AKOT15SharedPublicParameters)params.getPublicParameters()).getZp().getUniformlyRandomElement(); + GroupElement fixedPublicKey = ((AKOT15SharedPublicParameters)params.getPublicParameters()).getG1GroupGenerator().pow(fixedSecretKey).compute(); + + SPSPOSSignature sigma = scheme.sign(params.getMessage1(), params.getKeyPair1().getSigningKey(), fixedSecretKey); + + assertTrue(scheme.verify(params.getMessage1(), sigma, params.getKeyPair1().getVerificationKey(), fixedPublicKey)); + } + + + @Override + public void testNegativeSignatureAndVerify() { + // signing a block of messages + for (int i = 0; i < testIterations; i++) { + SignatureSchemeTester.testSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey() + ); + // re-key one-time signature + ((SPSPOSSignatureScheme)params.getSignatureScheme()).updateOneTimeKey((SignatureKeyPair) params.getKeyPair1()); + ((SPSPOSSignatureScheme)params.getSignatureScheme()).updateOneTimeKey((SignatureKeyPair) params.getKeyPair2()); + } + } + + @Test//(expected = IllegalStateException.class) + public void testNegativeExpiredOTKeySignatureAndVerify() { + // signing a block of messages, but without updating the one time key (which should give us an exception) + for (int i = 0; i < testIterations; i++) { + + SignatureSchemeTester.testSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey() + ); + + SignatureSchemeTester.testNegativeWrongKeysSignatureSchemeSignAndVerify( + params.getSignatureScheme(), + params.getMessage1(), + params.getKeyPair1().getVerificationKey(), + params.getKeyPair1().getSigningKey(), + params.getKeyPair2().getVerificationKey(), + params.getKeyPair2().getSigningKey() + ); + } + } + + + @Override + public void testPublicParameterRepresentation() { + // public parameter representation test + AKOT15SharedPublicParameters ppTest; + ppTest = new AKOT15SharedPublicParameters(params.getPublicParameters().getRepresentation()); + assertEquals(params.getPublicParameters(), ppTest); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTestParameterGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTestParameterGenerator.java new file mode 100644 index 00000000..3426a1f9 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTestParameterGenerator.java @@ -0,0 +1,46 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tc; + +import org.cryptimeleon.craco.commitment.CommitmentKey; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.CommitmentSchemeParams; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +public class TCAKOT15CommitmentSchemeTestParameterGenerator { + + public static CommitmentSchemeParams generateParameters(int securityParameter, int messageLength) { + + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters(securityParameter, messageLength, true); + + TCAKOT15CommitmentScheme scheme = new TCAKOT15CommitmentScheme(pp); + + CommitmentKey commitmentKey = scheme.generateKey(); + + //generate messages + + GroupElementPlainText[] messages = new GroupElementPlainText[messageLength]; + + Zp zp = pp.getZp(); + + for (int i = 0; i < messageLength; i++) { + messages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().pow(zp.getUniformlyRandomElement())); + } + + GroupElementPlainText[] wrongMessages = new GroupElementPlainText[messageLength]; + + for (int i = 0; i < messageLength; i++) { + + do { + wrongMessages[i] = new GroupElementPlainText(pp.getG2GroupGenerator().pow(zp.getUniformlyRandomElement())); + }while ( + messages[i].equals(wrongMessages[i]) + ); + } + + + return new CommitmentSchemeParams(pp, new MessageBlock(messages), new MessageBlock(wrongMessages), scheme, commitmentKey); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTester.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTester.java new file mode 100644 index 00000000..fe7e9e65 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tc/TCAKOT15CommitmentSchemeTester.java @@ -0,0 +1,65 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tc; + +import org.cryptimeleon.craco.commitment.CommitmentPair; +import org.cryptimeleon.craco.commitment.CommitmentSchemeTester; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.craco.sig.sps.akot15.tcgamma.TCGAKOT15Commitment; +import org.cryptimeleon.craco.sig.sps.CommitmentSchemeParams; +import org.cryptimeleon.math.serialization.ObjectRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.structures.groups.Group; +import org.junit.Before; +import org.junit.Test; + +import static org.junit.Assert.assertEquals; + +public class TCAKOT15CommitmentSchemeTester { + + private final int SECURITY_PARAMETER = 128; + private final int MESSAGE_LENGTH = 32; + + + CommitmentSchemeParams params; + + + @Before + public void generateParameters() { + params = TCAKOT15CommitmentSchemeTestParameterGenerator.generateParameters(SECURITY_PARAMETER, MESSAGE_LENGTH); + } + + @Test + public void testCommitAndVerify() { + CommitmentSchemeTester.testCommitmentSchemeVerify(params.getScheme(), params.getPlainText()); + } + + @Test + public void testCommitmentSchemeRepresentationText() { + + AKOT15SharedPublicParameters pp = (AKOT15SharedPublicParameters)params.getPublicParameters(); + + // Test representation of the scheme + CommitmentPair comPair = params.getScheme().commit(params.getPlainText()); + Representation comPairRepr = comPair.getRepresentation(); + + ObjectRepresentation objRepr = (ObjectRepresentation) comPairRepr; + + Group tcgG2 = ((TCGAKOT15Commitment) comPair.getCommitment()).getGroup2ElementGu().getStructure(); + Group tcG1 = ((TCAKOT15OpenValue)comPair.getOpenValue()).group1ElementGamma.getStructure(); + Group tcG2 = ((TCAKOT15OpenValue)comPair.getOpenValue()).spsPosSignatures[0].getGroup2ElementR().getStructure(); + + TCGAKOT15Commitment com = new TCGAKOT15Commitment(tcgG2, objRepr.get("com")); + TCAKOT15OpenValue open = new TCAKOT15OpenValue( + tcG1, + tcG2, + objRepr.get("open") + ); + assertEquals(comPair.getOpenValue(), open); + assertEquals(comPair.getCommitment(), com); + } + + @Test + public void testMapToPlaintext() { + CommitmentSchemeTester.testCommitmentSchemeMapToPlaintext(params.getScheme()); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTestParameterGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTestParameterGenerator.java new file mode 100644 index 00000000..93f2e8dc --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTestParameterGenerator.java @@ -0,0 +1,43 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.RingElementPlainText; +import org.cryptimeleon.craco.sig.sps.CommitmentSchemeParams; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParametersGen; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.cryptimeleon.math.structures.rings.zn.Zp; + +public class TCGAKOT15CommitmentSchemeTestParameterGenerator { + + public static CommitmentSchemeParams generateParameters(int securityParameter, int messageLength) { + + AKOT15SharedPublicParameters pp = AKOT15SharedPublicParametersGen.generateParameters(securityParameter, messageLength, true); + + TCGAKOT15CommitmentScheme scheme = new TCGAKOT15CommitmentScheme(pp); + + //generate messages + + RingElementPlainText[] messages = new RingElementPlainText[messageLength]; + + Zp zp = pp.getZp(); + + for (int i = 0; i < messageLength; i++) { + messages[i] = new RingElementPlainText(zp.getUniformlyRandomElement()); + } + + RingElementPlainText[] wrongMessages = new RingElementPlainText[messageLength]; + + for (int i = 0; i < messageLength; i++) { + + do { + wrongMessages[i] = new RingElementPlainText(zp.getUniformlyRandomElement()); + }while ( + messages[i].equals(wrongMessages[i]) + ); + } + + + return new CommitmentSchemeParams(pp, new MessageBlock(messages), new MessageBlock(wrongMessages), scheme, scheme.getCommitmentKey()); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTests.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTests.java new file mode 100644 index 00000000..adc3874e --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/tcgamma/TCGAKOT15CommitmentSchemeTests.java @@ -0,0 +1,59 @@ +package org.cryptimeleon.craco.sig.sps.akot15.tcgamma; + +import org.cryptimeleon.craco.commitment.CommitmentPair; +import org.cryptimeleon.craco.commitment.CommitmentSchemeTester; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.RingElementPlainText; +import org.cryptimeleon.craco.sig.sps.CommitmentSchemeParams; +import org.cryptimeleon.craco.sig.sps.akot15.AKOT15SharedPublicParameters; +import org.junit.Before; +import org.junit.Test; + +public class TCGAKOT15CommitmentSchemeTests { + + private final int SECURITY_PARAMETER = 128; + private final int MESSAGE_LENGTH = 1; + + + CommitmentSchemeParams params; + + + @Before + public void generateParameters() { + params = TCGAKOT15CommitmentSchemeTestParameterGenerator.generateParameters(SECURITY_PARAMETER, MESSAGE_LENGTH); + } + + @Test + public void testCommitAndVerify() { + CommitmentSchemeTester.testCommitmentSchemeVerify(params.getScheme(), params.getPlainText()); + } + + @Test + public void testCommitAndVerifyWithGroupElementMessage() { + + GroupElementPlainText[] gePlainText = ((MessageBlock)params.getPlainText()).stream().map + ( + x -> new GroupElementPlainText(((AKOT15SharedPublicParameters)params.getPublicParameters()).getG1GroupGenerator().pow(((RingElementPlainText)x).getRingElement()).compute()) + ).toArray(GroupElementPlainText[]::new); + + + MessageBlock groupElementPlainText = new MessageBlock(gePlainText); + + + CommitmentPair com = params.getScheme().commit(params.getPlainText()); + + params.getScheme().verify(com.getCommitment(), com.getOpenValue(), groupElementPlainText); + } + + @Test + public void testNegativeWrongCommitAndVerify() { + CommitmentSchemeTester.testCommitmentSchemeVerifyWithWrongMessages(params.getScheme(), params.getPlainText(), params.getWrongPlainText()); + } + + @Test + public void testMapToPlaintext() { + CommitmentSchemeTester.testCommitmentSchemeMapToPlaintext(params.getScheme()); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTestParamGenerator.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTestParamGenerator.java new file mode 100644 index 00000000..4d3e8acc --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTestParamGenerator.java @@ -0,0 +1,76 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.math.structures.groups.GroupElement; +import org.cryptimeleon.math.structures.rings.zn.Zp.ZpElement; + +public class SPSXSIGSignatureSchemeTestParamGenerator { + + public static SignatureSchemeParams generateParameters(int securityParameter, int numberOfMessageBlocks) { + + // setup scheme + + SPSXSIGPublicParameters pp = SPSXSIGPublicParametersGen.generatePublicParameters( + securityParameter, numberOfMessageBlocks, true + ); + + SPSXSIGSignatureScheme scheme = new SPSXSIGSignatureScheme(pp); + + + SignatureKeyPair keyPair = scheme.generateKeyPair( + numberOfMessageBlocks); + + SignatureKeyPair wrongKeyPair; + + do{ + wrongKeyPair = scheme.generateKeyPair(numberOfMessageBlocks); + }while( + wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) + || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey()) + ); + + // generate two different messages + + MessageBlock[] messageTriplets = new MessageBlock[numberOfMessageBlocks]; + + for (int i = 0; i < messageTriplets.length; i++) { + + ZpElement mi = pp.getZp().getUniformlyRandomElement(); + + GroupElementPlainText message1 = new GroupElementPlainText(pp.getGroup2ElementF1().pow(mi).compute()); + GroupElementPlainText message2 = new GroupElementPlainText(pp.getGroup2ElementF2().pow(mi).compute()); + GroupElementPlainText message3 = new GroupElementPlainText(pp.getGroup2ElementsU()[i].pow(mi).compute()); + + messageTriplets[i] = new MessageBlock(message1, message2, message3); + } + + MessageBlock[] wrongMessageTriplets = new MessageBlock[numberOfMessageBlocks]; + + for (int i = 0; i < wrongMessageTriplets.length; i++) { + + do { + ZpElement mi = pp.getZp().getUniformlyRandomElement(); + + GroupElementPlainText message1 = new GroupElementPlainText(pp.getGroup2ElementF1().pow(mi).compute()); + GroupElementPlainText message2 = new GroupElementPlainText(pp.getGroup2ElementF2().pow(mi).compute()); + GroupElementPlainText message3 = new GroupElementPlainText(pp.getGroup2ElementsU()[i].pow(mi).compute()); + + wrongMessageTriplets[i] = new MessageBlock(message1, message2, message3); + + }while(wrongMessageTriplets[i].equals(messageTriplets[i])); + } + + return new SignatureSchemeParams( + scheme, + pp, + new MessageBlock(messageTriplets), + new MessageBlock(wrongMessageTriplets), + keyPair, + wrongKeyPair + ); + } + +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTests.java b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTests.java new file mode 100644 index 00000000..0270b86b --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/sps/akot15/xsig/SPSXSIGSignatureSchemeTests.java @@ -0,0 +1,23 @@ +package org.cryptimeleon.craco.sig.sps.akot15.xsig; + +import org.cryptimeleon.craco.sig.SignatureSchemeParams; +import org.cryptimeleon.craco.sig.sps.SPSSchemeTester; + +import static org.junit.Assert.assertEquals; + +public class SPSXSIGSignatureSchemeTests extends SPSSchemeTester { + + @Override + protected SignatureSchemeParams generateParameters() { + return SPSXSIGSignatureSchemeTestParamGenerator.generateParameters(SECURITY_PARAMETER, NUM_MESSAGES); + } + + @Override + public void testPublicParameterRepresentation() { + // public parameter representation test + SPSXSIGPublicParameters ppTest; + ppTest = new SPSXSIGPublicParameters(params.getPublicParameters().getRepresentation()); + assertEquals(params.getPublicParameters(), ppTest); + } + +} From 78129073d297b54ca1e6ebda325b27f3b9d6cd40 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 11:24:39 +0100 Subject: [PATCH 05/18] Trigger pipeline From 48779e27f05f9a25da0491fb6064fd39bba671f1 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 13:20:47 +0100 Subject: [PATCH 06/18] Fix errors in docstring format. @param is only ment to be used in the parameter list --- .../sig/sps/SPSMessageSpaceVerifier.java | 19 +++++++++---------- .../sps/akot15/pos/SPSPOSSignatureScheme.java | 4 ++-- 2 files changed, 11 insertions(+), 12 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java index deae9c11..9f552d58 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/SPSMessageSpaceVerifier.java @@ -1,9 +1,9 @@ package org.cryptimeleon.craco.sig.sps; +import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; import org.cryptimeleon.craco.common.plaintexts.MessageBlock; import org.cryptimeleon.craco.common.plaintexts.PlainText; import org.cryptimeleon.math.structures.groups.Group; -import org.cryptimeleon.craco.common.plaintexts.GroupElementPlainText; /** * Interface to define a message check for SPSSchemes. @@ -15,15 +15,14 @@ public interface SPSMessageSpaceVerifier { /** * Checks if the given plainText matches the structure expected by the scheme - * and throws detailed exception if the plainText fails any check. - * - * For this default implementation, the following properties of the parameter {@param plainText} are checked: - * * {@param plainText} is of type {@link org.cryptimeleon.craco.common.plaintexts.MessageBlock}. - * * The amount of PlainTexts matches {@param expectedMessageLength}. - * * The elements stored in {@param plainText} are of type {@link GroupElementPlainText}. - * * The elements stored in said {@link GroupElementPlainText}s are \in {@param expectedGroup} - * - * */ + * and throws detailed exception if the plainText fails any check. + *

+ * For this default implementation, the following properties of the parameter {@code plainText} are checked: + * * {@code plainText} is of type {@link org.cryptimeleon.craco.common.plaintexts.MessageBlock}. + * * The amount of PlainTexts matches {@code expectedMessageLength}. + * * The elements stored in {@code plainText} are of type {@link GroupElementPlainText}. + * * The elements stored in said {@link GroupElementPlainText}s are \in {@code expectedGroup} + */ default void doMessageChecks(PlainText plainText, int expectedMessageLength, Group expectedGroup) throws IllegalArgumentException{ diff --git a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java index cac3c81d..45f70de0 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java +++ b/src/main/java/org/cryptimeleon/craco/sig/sps/akot15/pos/SPSPOSSignatureScheme.java @@ -115,7 +115,7 @@ public SPSPOSSignature sign(PlainText plainText, SigningKey secretKey) { } /** - * While a one-time key is stored in the {@param secretKey}, the scheme allows for a separate one-time key + * While a one-time key is stored in the {@code secretKey}, the scheme allows for a separate one-time key * to be passed to the scheme. This makes it easier to use this scheme as a building block. * * Note: Implementations using this scheme are responsible for ensuring that the one-time keys are not reused. @@ -169,7 +169,7 @@ public Boolean verify(PlainText plainText, Signature signature, VerificationKey } /** - * While a one-time key is stored in the {@param publicKey}, the scheme allows for a separate one-time key + * While a one-time key is stored in the {@code publicKey}, the scheme allows for a separate one-time key * to be passed to the scheme. This makes it easier to use this scheme as a building block. * * Note: Implementations using this scheme are responsible for ensuring that the one-time keys are not reused. From 450aaa43522a553e960c562027393df3a91329e1 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 10:41:32 +0100 Subject: [PATCH 07/18] Wrap ECDSA signature scheme in cryptimeleon classes. --- .../craco/sig/ecdsa/ECDSAKeyPair.java | 27 +++++ .../craco/sig/ecdsa/ECDSASignature.java | 40 +++++++ .../craco/sig/ecdsa/ECDSASignatureScheme.java | 112 ++++++++++++++++++ .../craco/sig/ecdsa/ECDSASigningKey.java | 59 +++++++++ .../craco/sig/ecdsa/ECDSAVerificationKey.java | 59 +++++++++ .../craco/sig/ecdsa/package-info.java | 4 + .../sig/ecdsa/ECDSASignatureSchemeTest.java | 50 ++++++++ .../ECDSASignatureSchemeTestParamGen.java | 33 ++++++ 8 files changed, 384 insertions(+) create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java create mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTest.java create mode 100644 src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java new file mode 100644 index 00000000..c90a9a36 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java @@ -0,0 +1,27 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import java.security.KeyPair; +import java.util.Objects; + +public class ECDSAKeyPair { + final ECDSAVerificationKey ecdsaVerificationKey; + final ECDSASigningKey ecdsaSigningKey; + + public ECDSAKeyPair(KeyPair keyPair) { + this.ecdsaVerificationKey = new ECDSAVerificationKey(keyPair.getPublic()); + this.ecdsaSigningKey = new ECDSASigningKey(keyPair.getPrivate()); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ECDSAKeyPair that = (ECDSAKeyPair) o; + return Objects.equals(ecdsaVerificationKey, that.ecdsaVerificationKey) && Objects.equals(ecdsaSigningKey, that.ecdsaSigningKey); + } + + @Override + public int hashCode() { + return Objects.hash(ecdsaVerificationKey, ecdsaSigningKey); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java new file mode 100644 index 00000000..da12c1ec --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java @@ -0,0 +1,40 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.sig.Signature; +import org.cryptimeleon.math.serialization.ByteArrayRepresentation; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.StandaloneRepresentable; + +import java.util.Arrays; + +public class ECDSASignature implements Signature, StandaloneRepresentable { + + final byte[] bytes; + + public ECDSASignature(byte[] signatureBytes) { + this.bytes = signatureBytes; + } + + public ECDSASignature(Representation repr) { + this.bytes = ((ByteArrayRepresentation) repr).get(); + } + + + @Override + public Representation getRepresentation() { + return new ByteArrayRepresentation(bytes); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ECDSASignature that = (ECDSASignature) o; + return Arrays.equals(bytes, that.bytes); + } + + @Override + public int hashCode() { + return Arrays.hashCode(bytes); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java new file mode 100644 index 00000000..c9b981b4 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java @@ -0,0 +1,112 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.common.ByteArrayImplementation; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.common.plaintexts.PlainText; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureScheme; +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.StringRepresentation; + +import java.security.*; +import java.security.spec.ECGenParameterSpec; + + +public class ECDSASignatureScheme implements SignatureScheme { + + static final String ALGORITHM = "EC"; + static final String CURVE = "secp256k1"; + private static final String SIGNING_ALGORITHM = "SHA256withECDSA"; + private final Signature signer; + + public ECDSASignatureScheme() { + try { + signer = Signature.getInstance(SIGNING_ALGORITHM); + } catch (NoSuchAlgorithmException e) { + throw new RuntimeException(e); + } + } + + public SignatureKeyPair keyGen() { + try { + ECGenParameterSpec ecSpec = new ECGenParameterSpec(CURVE); + KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM); + keyGen.initialize(ecSpec); + KeyPair keyPair = keyGen.generateKeyPair(); + return new SignatureKeyPair<>(new ECDSAVerificationKey(keyPair.getPublic()), new ECDSASigningKey(keyPair.getPrivate())); + } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + throw new RuntimeException(e); + } + + } + + + @Override + public org.cryptimeleon.craco.sig.Signature sign(PlainText plainText, SigningKey secretKey) { + ECDSASigningKey ecdsaSigningKey = (ECDSASigningKey) secretKey; + + try { + signer.initSign(ecdsaSigningKey.getKey()); + signer.update(plainText.getUniqueByteRepresentation()); + return new ECDSASignature(signer.sign()); + } catch (InvalidKeyException | SignatureException e) { + throw new RuntimeException(e); + } + } + + @Override + public Boolean verify(PlainText plainText, org.cryptimeleon.craco.sig.Signature signature, VerificationKey publicKey) { + ECDSAVerificationKey ecdsaVerificationKey = (ECDSAVerificationKey) publicKey; + ECDSASignature ecdsaSignature = (ECDSASignature) signature; + + try { + signer.initVerify(ecdsaVerificationKey.getKey()); + signer.update(plainText.getUniqueByteRepresentation()); + return signer.verify(ecdsaSignature.bytes); + } catch (InvalidKeyException | SignatureException e) { + throw new RuntimeException(e); + } + } + + @Override + public PlainText restorePlainText(Representation repr) { + return new MessageBlock(repr, ByteArrayImplementation::new); + } + + @Override + public org.cryptimeleon.craco.sig.Signature restoreSignature(Representation repr) { + return new ECDSASignature(repr); + } + + @Override + public SigningKey restoreSigningKey(Representation repr) { + return new ECDSASigningKey(repr); + } + + @Override + public VerificationKey restoreVerificationKey(Representation repr) { + return new ECDSAVerificationKey(repr); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, VerificationKey pk) { + return new ByteArrayImplementation(bytes); + } + + @Override + public PlainText mapToPlaintext(byte[] bytes, SigningKey sk) { + return new ByteArrayImplementation(bytes); + } + + @Override + public int getMaxNumberOfBytesForMapToPlaintext() { + return Integer.MAX_VALUE; + } + + @Override + public Representation getRepresentation() { + return new StringRepresentation(""); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java new file mode 100644 index 00000000..23a2ab18 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java @@ -0,0 +1,59 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.sig.SigningKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.StandaloneRepresentable; +import org.cryptimeleon.math.serialization.StringRepresentation; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PrivateKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.PKCS8EncodedKeySpec; +import java.util.Base64; +import java.util.Objects; + +import static org.cryptimeleon.craco.sig.ecdsa.ECDSASignatureScheme.ALGORITHM; + +public class ECDSASigningKey implements SigningKey, StandaloneRepresentable { + private final PrivateKey key; + + public ECDSASigningKey(PrivateKey secretKey) { + this.key = secretKey; + } + + public ECDSASigningKey(Representation repr) { + byte[] encodedKey = Base64.getDecoder().decode(((StringRepresentation) repr).get()); + PKCS8EncodedKeySpec privKeySpec = new PKCS8EncodedKeySpec(encodedKey); + + try { + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + this.key = keyFactory.generatePrivate(privKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + + PrivateKey getKey() { + return key; + } + + @Override + public Representation getRepresentation() { + String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded()); + return new StringRepresentation(encodedKey); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ECDSASigningKey that = (ECDSASigningKey) o; + return Objects.equals(key, that.key); + } + + @Override + public int hashCode() { + return Objects.hash(key); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java new file mode 100644 index 00000000..b25435eb --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java @@ -0,0 +1,59 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.sig.VerificationKey; +import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.StringRepresentation; + +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.PublicKey; +import java.security.spec.InvalidKeySpecException; +import java.security.spec.X509EncodedKeySpec; +import java.util.Base64; +import java.util.Objects; + +import static org.cryptimeleon.craco.sig.ecdsa.ECDSASignatureScheme.ALGORITHM; + +public class ECDSAVerificationKey implements VerificationKey { + + private final PublicKey key; + + public ECDSAVerificationKey(PublicKey privateKey) { + this.key = privateKey; + } + + public ECDSAVerificationKey(Representation repr) { + byte[] encodedKey = Base64.getDecoder().decode(((StringRepresentation) repr).get()); + X509EncodedKeySpec pubKeySpec = new X509EncodedKeySpec(encodedKey); + + try { + KeyFactory keyFactory = KeyFactory.getInstance(ALGORITHM); + this.key = keyFactory.generatePublic(pubKeySpec); + } catch (NoSuchAlgorithmException | InvalidKeySpecException e) { + throw new RuntimeException(e); + } + } + + PublicKey getKey() { + return key; + } + + @Override + public Representation getRepresentation() { + String encodedKey = Base64.getEncoder().encodeToString(key.getEncoded()); + return new StringRepresentation(encodedKey); + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + ECDSAVerificationKey that = (ECDSAVerificationKey) o; + return Objects.equals(key, that.key); + } + + @Override + public int hashCode() { + return Objects.hash(key); + } +} diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java new file mode 100644 index 00000000..d2693bc3 --- /dev/null +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java @@ -0,0 +1,4 @@ +/** + * Contains a cryptimeleon wrapper for the Java implementation of ECDSA. + */ +package org.cryptimeleon.craco.sig.ecdsa; \ No newline at end of file diff --git a/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTest.java b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTest.java new file mode 100644 index 00000000..be5f30b0 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTest.java @@ -0,0 +1,50 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.*; +import org.junit.Before; +import org.junit.Test; + +public class ECDSASignatureSchemeTest { + private ECDSASignatureScheme scheme; + private SignatureKeyPair keyPair; + private SignatureKeyPair wrongKeyPair; + private MessageBlock messageBlock; + private MessageBlock wrongMessageBlock; + + @Before + public void setUp() { + SignatureSchemeParams params = ECDSASignatureSchemeTestParamGen.generateParams(); + this.scheme = (ECDSASignatureScheme) params.getSignatureScheme(); + this.keyPair = params.getKeyPair1(); + this.wrongKeyPair = params.getKeyPair2(); + this.messageBlock = (MessageBlock) params.getMessage1(); + this.wrongMessageBlock = (MessageBlock) params.getMessage2(); + } + + @Test + public void testSignatureSchemeSignAndVerify() { + SignatureSchemeTester.testSignatureSchemeSignAndVerify(scheme, messageBlock, keyPair.getVerificationKey(), + keyPair.getSigningKey()); + } + + @Test + public void testSignatureSchemeRepresentationText() { + // Test standard signature scheme representations + SignatureSchemeTester.testRepresentationSignatureScheme(scheme, messageBlock, + keyPair.getVerificationKey(), keyPair.getSigningKey()); + } + + @Test + public void testNegativeWrongMessageSignatureSchemeSignAndVerify() { + SignatureSchemeTester.testNegativeWrongMessageSignatureSchemeSignAndVerify(scheme, + messageBlock, wrongMessageBlock, keyPair.getVerificationKey(), keyPair.getSigningKey()); + } + + @Test + public void testNegativeWrongKeySignatureSchemeSignAndVerify() { + SignatureSchemeTester.testNegativeWrongKeysSignatureSchemeSignAndVerify(scheme, messageBlock, + keyPair.getVerificationKey(), keyPair.getSigningKey(), wrongKeyPair.getVerificationKey(), + wrongKeyPair.getSigningKey()); + } +} diff --git a/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java new file mode 100644 index 00000000..6b102da8 --- /dev/null +++ b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java @@ -0,0 +1,33 @@ +package org.cryptimeleon.craco.sig.ecdsa; + +import org.cryptimeleon.craco.common.ByteArrayImplementation; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.SignatureSchemeParams; + +public class ECDSASignatureSchemeTestParamGen { + + /** + * Generates an instance of the {@link SignatureSchemeParams} for the + * {@link ECDSASignatureScheme}. + * + * @return Instance of the {@link SignatureSchemeParams}. + */ + public static SignatureSchemeParams generateParams() { + ECDSASignatureScheme ecdsaSignatureScheme = new ECDSASignatureScheme(); + + SignatureKeyPair keyPair = ecdsaSignatureScheme.keyGen(); + SignatureKeyPair wrongKeyPair; + do { + wrongKeyPair = ecdsaSignatureScheme.keyGen(); + } while (wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) + || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey())); + + MessageBlock plainText = new MessageBlock(new ByteArrayImplementation("Valid Message".getBytes())); + MessageBlock wrongPlainText = new MessageBlock(new ByteArrayImplementation("Invalid Message".getBytes())); + + + // ECDSA does not use PP since it is a fixed instance + return new SignatureSchemeParams(ecdsaSignatureScheme, null, plainText, wrongPlainText, keyPair, wrongKeyPair); + } +} From 77e2d64f91838a288ac909742d8d8196fd1444ab Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 10:45:20 +0100 Subject: [PATCH 08/18] Remove ECDSAKeyPair.java --- .../craco/sig/ecdsa/ECDSAKeyPair.java | 27 ------------------- 1 file changed, 27 deletions(-) delete mode 100644 src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java deleted file mode 100644 index c90a9a36..00000000 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAKeyPair.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.cryptimeleon.craco.sig.ecdsa; - -import java.security.KeyPair; -import java.util.Objects; - -public class ECDSAKeyPair { - final ECDSAVerificationKey ecdsaVerificationKey; - final ECDSASigningKey ecdsaSigningKey; - - public ECDSAKeyPair(KeyPair keyPair) { - this.ecdsaVerificationKey = new ECDSAVerificationKey(keyPair.getPublic()); - this.ecdsaSigningKey = new ECDSASigningKey(keyPair.getPrivate()); - } - - @Override - public boolean equals(Object o) { - if (this == o) return true; - if (o == null || getClass() != o.getClass()) return false; - ECDSAKeyPair that = (ECDSAKeyPair) o; - return Objects.equals(ecdsaVerificationKey, that.ecdsaVerificationKey) && Objects.equals(ecdsaSigningKey, that.ecdsaSigningKey); - } - - @Override - public int hashCode() { - return Objects.hash(ecdsaVerificationKey, ecdsaSigningKey); - } -} From ca0bc6a41333f45f148b365e1c7ee9f7914a3c40 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 10:45:56 +0100 Subject: [PATCH 09/18] Add missing newline --- .../java/org/cryptimeleon/craco/sig/ecdsa/package-info.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java index d2693bc3..a79ec36c 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/package-info.java @@ -1,4 +1,4 @@ /** * Contains a cryptimeleon wrapper for the Java implementation of ECDSA. */ -package org.cryptimeleon.craco.sig.ecdsa; \ No newline at end of file +package org.cryptimeleon.craco.sig.ecdsa; From cf813bb828cf47a589f00ab4bbbae0782ceef16e Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 11:05:25 +0100 Subject: [PATCH 10/18] Add docstrings for ECDSA classes. --- .../craco/sig/ecdsa/ECDSASignature.java | 3 +++ .../craco/sig/ecdsa/ECDSASignatureScheme.java | 15 +++++++++++++-- .../craco/sig/ecdsa/ECDSASigningKey.java | 6 ++++++ .../craco/sig/ecdsa/ECDSAVerificationKey.java | 5 +++++ 4 files changed, 27 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java index da12c1ec..04ee8cc4 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java @@ -7,6 +7,9 @@ import java.util.Arrays; +/** + * Class for a signature of the {@link ECDSASignatureScheme}. + */ public class ECDSASignature implements Signature, StandaloneRepresentable { final byte[] bytes; diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java index c9b981b4..0cb6c0c8 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java @@ -14,6 +14,10 @@ import java.security.spec.ECGenParameterSpec; +/** + * Cryptimeleon wrapper for the ECDSA signature scheme. + * Uses the curve and algorithms specified in the constants below. + */ public class ECDSASignatureScheme implements SignatureScheme { static final String ALGORITHM = "EC"; @@ -37,6 +41,7 @@ public SignatureKeyPair keyGen() { KeyPair keyPair = keyGen.generateKeyPair(); return new SignatureKeyPair<>(new ECDSAVerificationKey(keyPair.getPublic()), new ECDSASigningKey(keyPair.getPrivate())); } catch (NoSuchAlgorithmException | InvalidAlgorithmParameterException e) { + e.printStackTrace(); throw new RuntimeException(e); } @@ -51,7 +56,10 @@ public org.cryptimeleon.craco.sig.Signature sign(PlainText plainText, SigningKey signer.initSign(ecdsaSigningKey.getKey()); signer.update(plainText.getUniqueByteRepresentation()); return new ECDSASignature(signer.sign()); - } catch (InvalidKeyException | SignatureException e) { + } catch (InvalidKeyException e ) { + e.printStackTrace(); + throw new IllegalArgumentException("Input secretKey must a valid " + ALGORITHM + " secret key"); + } catch (SignatureException e) { throw new RuntimeException(e); } } @@ -65,7 +73,10 @@ public Boolean verify(PlainText plainText, org.cryptimeleon.craco.sig.Signature signer.initVerify(ecdsaVerificationKey.getKey()); signer.update(plainText.getUniqueByteRepresentation()); return signer.verify(ecdsaSignature.bytes); - } catch (InvalidKeyException | SignatureException e) { + } catch (InvalidKeyException e ) { + e.printStackTrace(); + throw new IllegalArgumentException("Input publicKey must a valid " + ALGORITHM + " public key"); + } catch (SignatureException e) { throw new RuntimeException(e); } } diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java index 23a2ab18..c8298c38 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java @@ -15,7 +15,13 @@ import static org.cryptimeleon.craco.sig.ecdsa.ECDSASignatureScheme.ALGORITHM; +/** + * Signing key of the {@link ECDSASignatureScheme}. + *
+ * Essentially a wrapper around Java's {@link PrivateKey} to fit into the Cryptimeleon API and support simple serialization. + */ public class ECDSASigningKey implements SigningKey, StandaloneRepresentable { + private final PrivateKey key; public ECDSASigningKey(PrivateKey secretKey) { diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java index b25435eb..3eeae5b7 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java @@ -14,6 +14,11 @@ import static org.cryptimeleon.craco.sig.ecdsa.ECDSASignatureScheme.ALGORITHM; +/** + * Verification key of the {@link ECDSASignatureScheme}. + *
+ * Essentially a wrapper around Java's {@link PublicKey} to fit into the Cryptimeleon API and support simple serialization. + */ public class ECDSAVerificationKey implements VerificationKey { private final PublicKey key; From 402ee342bdc00031503011302c3624faa997e0a3 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 11:11:16 +0100 Subject: [PATCH 11/18] Trigger re-enabled pipeline From 655b1b5123310600a04ee0f0b8d27ac0ef581bb3 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 11:15:29 +0100 Subject: [PATCH 12/18] Fix empty lines in docstrings --- .../java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java | 2 +- .../org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java index c8298c38..b4df865a 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASigningKey.java @@ -17,7 +17,7 @@ /** * Signing key of the {@link ECDSASignatureScheme}. - *
+ *

* Essentially a wrapper around Java's {@link PrivateKey} to fit into the Cryptimeleon API and support simple serialization. */ public class ECDSASigningKey implements SigningKey, StandaloneRepresentable { diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java index 3eeae5b7..04f8c2cd 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java @@ -16,7 +16,7 @@ /** * Verification key of the {@link ECDSASignatureScheme}. - *
+ *

* Essentially a wrapper around Java's {@link PublicKey} to fit into the Cryptimeleon API and support simple serialization. */ public class ECDSAVerificationKey implements VerificationKey { From f67a74b4d84ca9f986e1dcb1e12b07cdbabad107 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 11:29:29 +0100 Subject: [PATCH 13/18] Rename keygen to generateKeyPair --- .../cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java | 2 +- .../craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java index 0cb6c0c8..8cad2e96 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java @@ -33,7 +33,7 @@ public ECDSASignatureScheme() { } } - public SignatureKeyPair keyGen() { + public SignatureKeyPair generateKeyPair() { try { ECGenParameterSpec ecSpec = new ECGenParameterSpec(CURVE); KeyPairGenerator keyGen = KeyPairGenerator.getInstance(ALGORITHM); diff --git a/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java index 6b102da8..1d96de93 100644 --- a/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java +++ b/src/test/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureSchemeTestParamGen.java @@ -16,10 +16,10 @@ public class ECDSASignatureSchemeTestParamGen { public static SignatureSchemeParams generateParams() { ECDSASignatureScheme ecdsaSignatureScheme = new ECDSASignatureScheme(); - SignatureKeyPair keyPair = ecdsaSignatureScheme.keyGen(); + SignatureKeyPair keyPair = ecdsaSignatureScheme.generateKeyPair(); SignatureKeyPair wrongKeyPair; do { - wrongKeyPair = ecdsaSignatureScheme.keyGen(); + wrongKeyPair = ecdsaSignatureScheme.generateKeyPair(); } while (wrongKeyPair.getVerificationKey().equals(keyPair.getVerificationKey()) || wrongKeyPair.getSigningKey().equals(keyPair.getSigningKey())); From 1b56040fc978e9e15864f14691eda77173fc30e4 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 13:52:31 +0100 Subject: [PATCH 14/18] Add missing representation tests and add missing methods for SignatureScheme instances that are enforced by the test-cases. --- .../craco/sig/ecdsa/ECDSASignatureScheme.java | 20 +++++++++++++++++++ .../craco/sig/ecdsa/ECDSAVerificationKey.java | 3 ++- .../params/SignatureStandaloneReprTests.java | 17 ++++++++++++++++ 3 files changed, 39 insertions(+), 1 deletion(-) diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java index 8cad2e96..ce480b85 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignatureScheme.java @@ -12,6 +12,7 @@ import java.security.*; import java.security.spec.ECGenParameterSpec; +import java.util.Objects; /** @@ -120,4 +121,23 @@ public int getMaxNumberOfBytesForMapToPlaintext() { public Representation getRepresentation() { return new StringRepresentation(""); } + + /* + * We need equals, hashcode and the Representation constructor to satisfy the SignatureScheme bounds for automated tests + * All instances of ECDSASignatureSchemer are 'equal'. + */ + + public ECDSASignatureScheme(Representation repr) { + this(); + } + + @Override + public boolean equals(Object o) { + return o != null && getClass() == o.getClass(); + } + + @Override + public int hashCode() { + return Objects.hash(19817349853L); + } } diff --git a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java index 04f8c2cd..7a8d785e 100644 --- a/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java +++ b/src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSAVerificationKey.java @@ -2,6 +2,7 @@ import org.cryptimeleon.craco.sig.VerificationKey; import org.cryptimeleon.math.serialization.Representation; +import org.cryptimeleon.math.serialization.StandaloneRepresentable; import org.cryptimeleon.math.serialization.StringRepresentation; import java.security.KeyFactory; @@ -19,7 +20,7 @@ *

* Essentially a wrapper around Java's {@link PublicKey} to fit into the Cryptimeleon API and support simple serialization. */ -public class ECDSAVerificationKey implements VerificationKey { +public class ECDSAVerificationKey implements VerificationKey, StandaloneRepresentable { private final PublicKey key; diff --git a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java index cea046cb..b1ba681e 100644 --- a/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java +++ b/src/test/java/org/cryptimeleon/craco/ser/standalone/params/SignatureStandaloneReprTests.java @@ -1,5 +1,12 @@ package org.cryptimeleon.craco.ser.standalone.params; +import org.cryptimeleon.craco.common.ByteArrayImplementation; +import org.cryptimeleon.craco.common.plaintexts.MessageBlock; +import org.cryptimeleon.craco.sig.SignatureKeyPair; +import org.cryptimeleon.craco.sig.ecdsa.ECDSASignature; +import org.cryptimeleon.craco.sig.ecdsa.ECDSASignatureScheme; +import org.cryptimeleon.craco.sig.ecdsa.ECDSASigningKey; +import org.cryptimeleon.craco.sig.ecdsa.ECDSAVerificationKey; import org.cryptimeleon.craco.sig.sps.SPSPublicParameters; import org.cryptimeleon.craco.sig.sps.SPSPublicParametersGen; import org.cryptimeleon.craco.sig.sps.agho11.SPSAGHO11PublicParameters; @@ -37,6 +44,16 @@ public class SignatureStandaloneReprTests extends StandaloneReprSubTest { private final PSPublicParameters pp = new PSPublicParametersGen().generatePublicParameter(128, true); + public void testECDSA() { + ECDSASignatureScheme ecdsaSignatureScheme = new ECDSASignatureScheme(); + SignatureKeyPair keyPair = ecdsaSignatureScheme.generateKeyPair(); + ECDSASignature signature = (ECDSASignature) ecdsaSignatureScheme.sign(new MessageBlock(new ByteArrayImplementation("TestMessage".getBytes())), keyPair.getSigningKey()); + + test(keyPair.getSigningKey()); + test(keyPair.getVerificationKey()); + test(signature); + } + public void testSPSEQ() { SPSEQPublicParameters pp = new SPSEQPublicParametersGen().generatePublicParameter(128, true); SPSEQSignatureScheme signatureScheme = new SPSEQSignatureScheme(pp); From 7ebd0dfe55a37b5ae8608483f1b9b14cdc87cf28 Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 14:08:24 +0100 Subject: [PATCH 15/18] Version bump for commit --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index 202db06f..bd2ce53d 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { group = 'org.cryptimeleon' archivesBaseName = project.name boolean isRelease = project.hasProperty("release") -version = '3.0.2' + (isRelease ? "" : "-SNAPSHOT") +version = '3.1.0' + (isRelease ? "" : "-SNAPSHOT") sourceCompatibility = 1.8 From 2336303d88919d3e70e320154f944540fb42b6ab Mon Sep 17 00:00:00 2001 From: Paul Kramer Date: Thu, 12 Jan 2023 14:28:55 +0100 Subject: [PATCH 16/18] Update CHANGELOG.md for release --- CHANGELOG.md | 6 ++++++ 1 file changed, 6 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index a4e5602d..c7d26970 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -6,6 +6,12 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0 ## [Latest] +## [3.1.0] + +### Added +- ECDSA signatures from `java.security` (see [#102](https://github.com/cryptimeleon/craco/pull/102)) +- AGHO11, KPW15 and AKOT15 structure-preserving signature schemes (see [#99](https://github.com/cryptimeleon/craco/pull/99))) + ## [3.0.1] ### Changed From 86f10d4f0a838ded4ed193f535ffcd458278a4ab Mon Sep 17 00:00:00 2001 From: Jan Bobolz Date: Thu, 12 Jan 2023 15:02:45 +0100 Subject: [PATCH 17/18] Version bump 4.0.0 for breaking changes --- build.gradle | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/build.gradle b/build.gradle index bd2ce53d..fdd7b0d9 100644 --- a/build.gradle +++ b/build.gradle @@ -8,7 +8,7 @@ plugins { group = 'org.cryptimeleon' archivesBaseName = project.name boolean isRelease = project.hasProperty("release") -version = '3.1.0' + (isRelease ? "" : "-SNAPSHOT") +version = '4.0.0' + (isRelease ? "" : "-SNAPSHOT") sourceCompatibility = 1.8 From dd0fc74e669c46c32b9df50b8d23b69baed3810e Mon Sep 17 00:00:00 2001 From: Jan Bobolz Date: Thu, 12 Jan 2023 15:03:28 +0100 Subject: [PATCH 18/18] Version bump in readme --- README.md | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/README.md b/README.md index f2f81e7d..31eee35b 100644 --- a/README.md +++ b/README.md @@ -66,7 +66,7 @@ To add the newest Craco version as a dependency, add this to your project's POM: org.cryptimeleon craco - [3.0,) + [4.0,) ``` @@ -74,7 +74,7 @@ To add the newest Craco version as a dependency, add this to your project's POM: Craco is published via Maven Central. Therefore, you need to add `mavenCentral()` to the `repositories` section of your project's `build.gradle` file. -Then, add `implementation group: 'org.cryptimeleon', name: 'craco', version: '3.+'` to the `dependencies` section of your `build.gradle` file. +Then, add `implementation group: 'org.cryptimeleon', name: 'craco', version: '4.+'` to the `dependencies` section of your `build.gradle` file. For example: @@ -84,7 +84,7 @@ repositories { } dependencies { - implementation group: 'org.cryptimeleon', name: 'craco', version: '3.+' + implementation group: 'org.cryptimeleon', name: 'craco', version: '4.+' } ```