Skip to content

Commit

Permalink
Merge pull request #104 from cryptimeleon/develop
Browse files Browse the repository at this point in the history
Merge into main for Release 4.0.0
  • Loading branch information
this-kramer authored Jan 12, 2023
2 parents 6c9fc18 + dd0fc74 commit f2c4e07
Show file tree
Hide file tree
Showing 77 changed files with 7,203 additions and 69 deletions.
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
6 changes: 3 additions & 3 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -66,15 +66,15 @@ To add the newest Craco version as a dependency, add this to your project's POM:
<dependency>
<groupId>org.cryptimeleon</groupId>
<artifactId>craco</artifactId>
<version>[3.0,)</version>
<version>[4.0,)</version>
</dependency>
```

### Installation With Gradle

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:

Expand All @@ -84,7 +84,7 @@ repositories {
}
dependencies {
implementation group: 'org.cryptimeleon', name: 'craco', version: '3.+'
implementation group: 'org.cryptimeleon', name: 'craco', version: '4.+'
}
```

Expand Down
2 changes: 1 addition & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ plugins {
group = 'org.cryptimeleon'
archivesBaseName = project.name
boolean isRelease = project.hasProperty("release")
version = '3.0.2' + (isRelease ? "" : "-SNAPSHOT")
version = '4.0.0' + (isRelease ? "" : "-SNAPSHOT")


sourceCompatibility = 1.8
Expand Down
10 changes: 10 additions & 0 deletions src/main/java/org/cryptimeleon/craco/commitment/CommitmentKey.java
Original file line number Diff line number Diff line change
@@ -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 {
}
Original file line number Diff line number Diff line change
Expand Up @@ -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[])}
* <p>
* 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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
Expand All @@ -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);
}
Expand All @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -517,17 +517,20 @@ 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");
throw new IllegalStateException("Witness for " + name + " is missing");
});

return new WitnessValues(witnessesForVariables);
Expand Down
43 changes: 43 additions & 0 deletions src/main/java/org/cryptimeleon/craco/sig/ecdsa/ECDSASignature.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
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;

/**
* Class for a signature of the {@link ECDSASignatureScheme}.
*/
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);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,143 @@
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;
import java.util.Objects;


/**
* 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";
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<ECDSAVerificationKey, ECDSASigningKey> generateKeyPair() {
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) {
e.printStackTrace();
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 e ) {
e.printStackTrace();
throw new IllegalArgumentException("Input secretKey must a valid " + ALGORITHM + " secret key");
} catch (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 e ) {
e.printStackTrace();
throw new IllegalArgumentException("Input publicKey must a valid " + ALGORITHM + " public key");
} catch (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("");
}

/*
* 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);
}
}
Loading

0 comments on commit f2c4e07

Please sign in to comment.