diff --git a/data-modules/output/asn/SignedNFTAttestation.asn b/data-modules/output/asn/SignedNFTAttestation.asn
index 570ed9c9..bb17d164 100644
--- a/data-modules/output/asn/SignedNFTAttestation.asn
+++ b/data-modules/output/asn/SignedNFTAttestation.asn
@@ -9,6 +9,7 @@ IMPORTS
AlgorithmIdentifier
FROM AuthenticationFramework;
+-- Version 1 or 2, newer uses EIP712 --
SignedNFTAttestation ::= SEQUENCE {
nftAttestation NFTAttestation,
signingVersion INTEGER OPTIONAL,
diff --git a/data-modules/src/SignedNFTAttestation.asd b/data-modules/src/SignedNFTAttestation.asd
index ac4382a4..d5330f72 100644
--- a/data-modules/src/SignedNFTAttestation.asd
+++ b/data-modules/src/SignedNFTAttestation.asd
@@ -6,6 +6,7 @@
+
diff --git a/src/intTest/java/io/alchemynft/attestation/NFTSmartContractTest.java b/src/intTest/java/io/alchemynft/attestation/NFTSmartContractTest.java
index d42c0f07..1f68c978 100644
--- a/src/intTest/java/io/alchemynft/attestation/NFTSmartContractTest.java
+++ b/src/intTest/java/io/alchemynft/attestation/NFTSmartContractTest.java
@@ -27,7 +27,7 @@ public class NFTSmartContractTest {
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, issuerKeys);
*/
static SignedIdentifierAttestation attestation;
- private SignedNFTAttestation nftAttestation;
+ private LegacySignedNFTAttestation nftAttestation;
private final SmartContract contract = new SmartContract();
// the URL as King Mida's public ID, plus a label (in case of twitter, the permanent numeric ID)
static final String labeledURI = "https://twitter.com/zhangweiwu 205521676";
@@ -58,7 +58,7 @@ public void checkNFTSmartContract() throws Exception
NFTAttestation nftAtt = new NFTAttestation(attestation, myNFTs);
//construct SignedNFTAttestation using subject key
- nftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys); // <-- signing step, NFT attestation is signed by owner of identifier, referenced below
+ nftAttestation = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate()); // <-- signing step, NFT attestation is signed by owner of identifier, referenced below
System.out.println("DER: " + Numeric.toHexString(nftAttestation.getDerEncoding()));
@@ -69,7 +69,7 @@ public void checkNFTSmartContract() throws Exception
assertTrue(nftAttestation2.verify());
//Generate SignedNFTAttestation using the reconstructed NFTAttestation and the extracted Ethereum signature
- SignedNFTAttestation signedNFTAttestation2 = new SignedNFTAttestation(nftAttestation2, subjectKeys);
+ LegacySignedNFTAttestation signedNFTAttestation2 = new LegacySignedNFTAttestation(nftAttestation2, subjectKeys.getPrivate());
assertTrue(signedNFTAttestation2.checkValidity());
assertTrue(nftAttestation.checkValidity());
assertArrayEquals(signedNFTAttestation2.getUnsignedAttestation().getDerEncoding(), nftAtt.getDerEncoding());
diff --git a/src/main/java/io/alchemynft/attestation/Eip712SignedNFTAttestation.java b/src/main/java/io/alchemynft/attestation/Eip712SignedNFTAttestation.java
new file mode 100644
index 00000000..57ec012c
--- /dev/null
+++ b/src/main/java/io/alchemynft/attestation/Eip712SignedNFTAttestation.java
@@ -0,0 +1,98 @@
+package io.alchemynft.attestation;
+
+import java.io.IOException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.core.ExceptionUtil;
+import org.tokenscript.attestation.eip712.Eip712ObjectSigner;
+import org.tokenscript.attestation.eip712.Eip712ObjectValidator;
+import org.tokenscript.eip712.FullEip712InternalData;
+
+public class Eip712SignedNFTAttestation implements InternalSignedNFTAttestation {
+ private static final Logger logger = LogManager.getLogger(Eip712SignedNFTAttestation.class);
+ public static final String DEFAULT_DOMAIN = "https://autographnft.io";
+
+ private Eip712ObjectValidator validator;
+
+ private final NFTAttestation att;
+ private final String signature;
+ private final String signedEIP712;
+
+ public Eip712SignedNFTAttestation(NFTAttestation att, AsymmetricKeyParameter subjectSigningKey) {
+ try {
+ NFTAttestationDecoder decoder = new NFTAttestationDecoder(
+ att.getSignedIdentifierAttestation().getAttestationVerificationKey());
+ validator = new Eip712ObjectValidator(decoder, new NFTAttestationEncoder(),
+ DEFAULT_DOMAIN);
+ Eip712ObjectSigner eipSigner = new Eip712ObjectSigner(subjectSigningKey,
+ new NFTAttestationEncoder());
+ this.signedEIP712 = eipSigner.buildSignedToken(att, DEFAULT_DOMAIN);
+ this.att = validator.retrieveUnderlyingObject(signedEIP712);
+ this.signature = eipSigner.getSignatureFromJson(signedEIP712);
+ } catch (IOException e) {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Could not decode underlying NFTAttestation"));
+ }
+ constructorCheck();
+ }
+
+ public Eip712SignedNFTAttestation(String signedEIP712, AsymmetricKeyParameter identifierAttestationVerificationKey) throws IOException {
+ NFTAttestationDecoder decoder = new NFTAttestationDecoder(identifierAttestationVerificationKey);
+ validator = new Eip712ObjectValidator(decoder, new NFTAttestationEncoder(), DEFAULT_DOMAIN);
+ this.signedEIP712 = signedEIP712;
+ this.att = validator.retrieveUnderlyingObject(signedEIP712);
+ this.signature = validator.getSignatureFromJson(signedEIP712);
+ constructorCheck();
+ }
+
+ private void constructorCheck() {
+ if (!verify()) {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("The NFTAttestation is invalid"));
+ }
+ }
+
+ /**
+ * Returns the public key of the NFTattestation signer
+ */
+ public AsymmetricKeyParameter getNFTAttestationVerificationKey() {
+ return validator.retrieveUserPublicKey(signedEIP712, FullEip712InternalData.class);
+ }
+
+ public String getSignedEIP712() {
+ return signedEIP712;
+ }
+
+ @Override
+ public NFTAttestation getUnsignedAttestation() {
+ return att;
+ }
+
+ public String getSignature() {
+ return signature;
+ }
+
+ /**
+ * Checks the validity of the underlying NFTAttestation
+ */
+ @Override
+ public boolean checkValidity() {
+ return att.checkValidity();
+ }
+
+ /**
+ * Verifies the entire EIP712 request, including domain, timestamp, usage string, signature and the
+ * @return
+ */
+ @Override
+ public boolean verify() {
+ return validator.validateRequest(signedEIP712);
+ }
+
+ /**
+ * Returns the ASN encoding of the underlying NFTAttestation
+ */
+ @Override
+ public byte[] getDerEncoding() {
+ return att.getDerEncoding();
+ }
+}
diff --git a/src/main/java/io/alchemynft/attestation/InternalSignedNFTAttestation.java b/src/main/java/io/alchemynft/attestation/InternalSignedNFTAttestation.java
new file mode 100644
index 00000000..cf9b01ba
--- /dev/null
+++ b/src/main/java/io/alchemynft/attestation/InternalSignedNFTAttestation.java
@@ -0,0 +1,10 @@
+package io.alchemynft.attestation;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.CheckableObject;
+
+public interface InternalSignedNFTAttestation extends CheckableObject {
+ AsymmetricKeyParameter getNFTAttestationVerificationKey();
+ NFTAttestation getUnsignedAttestation();
+}
+
diff --git a/src/main/java/io/alchemynft/attestation/LegacySignedNFTAttestation.java b/src/main/java/io/alchemynft/attestation/LegacySignedNFTAttestation.java
new file mode 100644
index 00000000..1c25313f
--- /dev/null
+++ b/src/main/java/io/alchemynft/attestation/LegacySignedNFTAttestation.java
@@ -0,0 +1,160 @@
+package io.alchemynft.attestation;
+
+import java.io.IOException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.asn1.ASN1EncodableVector;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Integer;
+import org.bouncycastle.asn1.ASN1Primitive;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERBitString;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.core.CompressedMsgSignature;
+import org.tokenscript.attestation.core.ExceptionUtil;
+import org.tokenscript.attestation.core.PersonalSignature;
+import org.tokenscript.attestation.core.Signature;
+
+public class LegacySignedNFTAttestation implements InternalSignedNFTAttestation {
+ private static final Logger logger = LogManager.getLogger(LegacySignedNFTAttestation.class);
+ public static final int DEFAULT_SIGNING_VERSION = 2;
+
+ private final NFTAttestation nftAtt;
+ private final Signature signature;
+ private final int signingVersion;
+
+ public LegacySignedNFTAttestation(NFTAttestation nftAtt, AsymmetricKeyParameter subjectSigningKey) {
+ this(nftAtt, subjectSigningKey, DEFAULT_SIGNING_VERSION);
+ }
+
+ public LegacySignedNFTAttestation(NFTAttestation nftAtt, AsymmetricKeyParameter subjectSigningKey, int signingVersion) {
+ this.nftAtt = nftAtt;
+ this.signature = makeSignature(subjectSigningKey, signingVersion);
+ this.signingVersion = signingVersion;
+
+ if (!verify()) {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("The signature is not valid"));
+ }
+ }
+
+ /**
+ * Constructor used for when we supply the signature separately
+ */
+ public LegacySignedNFTAttestation(NFTAttestation NftAtt, Signature signature) {
+ this.nftAtt = NftAtt;
+ this.signature = signature;
+ this.signingVersion = determineSigningVersion();
+ if (!verify()) {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("The signature is not valid"));
+ }
+ }
+
+ public LegacySignedNFTAttestation(byte[] derEncoding, AsymmetricKeyParameter identifierAttestationVerificationKey) throws IOException {
+ ASN1InputStream input = new ASN1InputStream(derEncoding);
+ ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject());
+ input.close();
+ int currentPos = 0;
+ ASN1Sequence nftEncoding = ASN1Sequence.getInstance(asn1.getObjectAt(currentPos++));
+ this.nftAtt = new NFTAttestation(nftEncoding.getEncoded(), identifierAttestationVerificationKey);
+ if (asn1.getObjectAt(currentPos) instanceof ASN1Integer) {
+ this.signingVersion = ASN1Integer.getInstance(asn1.getObjectAt(currentPos++)).intValueExact();
+ } else {
+ // If signingVersion is not present we default to version 1
+ this.signingVersion = 1;
+ }
+ // todo this actually not used
+ AlgorithmIdentifier algorithmIdentifier = AlgorithmIdentifier.getInstance(asn1.getObjectAt(currentPos++));
+ DERBitString signatureEnc = DERBitString.getInstance(asn1.getObjectAt(currentPos++));
+ this.signature = makeSignature(signatureEnc.getBytes(), signingVersion);
+ }
+
+ private int determineSigningVersion() {
+ if (signature instanceof PersonalSignature) {
+ return 1;
+ }
+ else if (signature instanceof CompressedMsgSignature) {
+ return 2;
+ } else {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unexpected signature type used"));
+ }
+ }
+
+ Signature makeSignature(byte[] encodedBytes, int signingVersion) {
+ if (signingVersion == 1) {
+ return new PersonalSignature(encodedBytes);
+ }
+ else if (signingVersion == 2) {
+ return new CompressedMsgSignature(encodedBytes, SignedNFTAttestation.PREFIX_MSG, SignedNFTAttestation.POSTFIX_MSG);
+ } else {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unknown signing version"));
+ }
+ }
+
+ Signature makeSignature(AsymmetricKeyParameter key, int signingVersion) {
+ if (signingVersion == 1) {
+ return new PersonalSignature(key, nftAtt.getDerEncoding());
+ }
+ else if (signingVersion == 2) {
+ return new CompressedMsgSignature(key, nftAtt.getDerEncoding(), SignedNFTAttestation.PREFIX_MSG, SignedNFTAttestation.POSTFIX_MSG);
+ } else {
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unknown signing version"));
+ }
+ }
+
+ @Override
+ public NFTAttestation getUnsignedAttestation() {
+ return nftAtt;
+ }
+
+ public Signature getSignature() {
+ return signature;
+ }
+
+ /**
+ * Returns the public key of the NFTattestation signer
+ */
+ @Override
+ public AsymmetricKeyParameter getNFTAttestationVerificationKey() {
+ return nftAtt.getAttestedUserKey();
+ }
+
+ @Override
+ public byte[] getDerEncoding() {
+ return constructSignedAttestation(this.nftAtt, this.signature.getRawSignature());
+ }
+
+ byte[] constructSignedAttestation(NFTAttestation unsignedAtt, byte[] signature) {
+ try {
+ byte[] rawAtt = unsignedAtt.getDerEncoding();
+ ASN1EncodableVector res = new ASN1EncodableVector();
+ res.add(ASN1Primitive.fromByteArray(rawAtt));
+ // Only include version number if it is greater than 1
+ if (signingVersion > 1) {
+ res.add(new ASN1Integer(signingVersion));
+ }
+ res.add(unsignedAtt.getSigningAlgorithm());
+ res.add(new DERBitString(signature));
+ return new DERSequence(res).getEncoded();
+ } catch (Exception e) {
+ throw new RuntimeException(e);
+ }
+ }
+
+ @Override
+ public boolean checkValidity() {
+ return getUnsignedAttestation().checkValidity();
+ }
+
+ @Override
+ public boolean verify() {
+ if (!signature.verify(nftAtt.getDerEncoding(), getNFTAttestationVerificationKey())) {
+ return false;
+ }
+ if (!nftAtt.verify()) {
+ return false;
+ }
+ return true;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/io/alchemynft/attestation/NFTAttestation.java b/src/main/java/io/alchemynft/attestation/NFTAttestation.java
index acddad8b..96b3e548 100644
--- a/src/main/java/io/alchemynft/attestation/NFTAttestation.java
+++ b/src/main/java/io/alchemynft/attestation/NFTAttestation.java
@@ -10,12 +10,13 @@
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.tokenscript.attestation.AttestedKeyObject;
import org.tokenscript.attestation.ERC721Token;
import org.tokenscript.attestation.SignedIdentifierAttestation;
-import org.tokenscript.attestation.core.ASNEncodable;
-import org.tokenscript.attestation.core.Validateable;
+import org.tokenscript.attestation.core.ExceptionUtil;
-public class NFTAttestation implements ASNEncodable, Validateable {
+public class NFTAttestation extends AttestedKeyObject {
private static final Logger logger = LogManager.getLogger(NFTAttestation.class);
private final SignedIdentifierAttestation signedIdentifierAttestation;
@@ -80,4 +81,15 @@ public boolean checkValidity() {
public boolean verify() {
return signedIdentifierAttestation.verify();
}
+
+ @Override
+ public AsymmetricKeyParameter getAttestedUserKey() {
+ try {
+ return PublicKeyFactory.createKey(
+ getSignedIdentifierAttestation().getUnsignedAttestation()
+ .getSubjectPublicKeyInfo());
+ } catch (IOException e) {
+ throw ExceptionUtil.makeRuntimeException(logger, "Could not restore key from signed signed attestation", e);
+ }
+ }
}
diff --git a/src/main/java/io/alchemynft/attestation/NFTAttestationDecoder.java b/src/main/java/io/alchemynft/attestation/NFTAttestationDecoder.java
new file mode 100644
index 00000000..166d5569
--- /dev/null
+++ b/src/main/java/io/alchemynft/attestation/NFTAttestationDecoder.java
@@ -0,0 +1,34 @@
+package io.alchemynft.attestation;
+
+import java.io.IOException;
+import org.bouncycastle.asn1.ASN1InputStream;
+import org.bouncycastle.asn1.ASN1Sequence;
+import org.bouncycastle.asn1.DERSequence;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.ERC721Token;
+import org.tokenscript.attestation.ObjectDecoder;
+import org.tokenscript.attestation.SignedIdentifierAttestation;
+
+public class NFTAttestationDecoder implements ObjectDecoder {
+ private final AsymmetricKeyParameter identifierAttestationVerificationKey;
+ public NFTAttestationDecoder(AsymmetricKeyParameter identifierAttestationVerificationKey) {
+ this.identifierAttestationVerificationKey = identifierAttestationVerificationKey;
+ }
+
+ @Override
+ public NFTAttestation decode(byte[] encoding) throws IOException {
+ ASN1InputStream input = new ASN1InputStream(encoding);
+ ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject());
+ input.close();
+ ASN1Sequence attestationEnc = ASN1Sequence.getInstance(asn1.getObjectAt(0)); //root attestation, should be signed att
+ SignedIdentifierAttestation signedIdentifierAttestation = new SignedIdentifierAttestation(attestationEnc.getEncoded(), identifierAttestationVerificationKey);
+
+ ASN1Sequence tokensEnc = ASN1Sequence.getInstance(asn1.getObjectAt(1));
+ DERSequence tokens = DERSequence.convert(tokensEnc);
+ ERC721Token[] erc721Tokens = new ERC721Token[tokens.size()];
+ for (int i = 0; i< erc721Tokens.length; i++) {
+ erc721Tokens[i] = new ERC721Token(tokens.getObjectAt(i).toASN1Primitive().getEncoded());
+ }
+ return new NFTAttestation(signedIdentifierAttestation, erc721Tokens);
+ }
+}
diff --git a/src/main/java/io/alchemynft/attestation/NFTAttestationEncoder.java b/src/main/java/io/alchemynft/attestation/NFTAttestationEncoder.java
new file mode 100644
index 00000000..1e7c583d
--- /dev/null
+++ b/src/main/java/io/alchemynft/attestation/NFTAttestationEncoder.java
@@ -0,0 +1,21 @@
+package io.alchemynft.attestation;
+
+import com.alphawallet.token.web.Ethereum.web3j.StructuredData.Entry;
+import java.util.HashMap;
+import java.util.List;
+import org.tokenscript.eip712.Eip712Encoder;
+
+public class NFTAttestationEncoder extends Eip712Encoder {
+ private static final String PROTOCOL_VERSION = "0.1";
+ private static final String PRIMARY_NAME = "NFTAttestation";//"Signed request to be used only for";
+ private static final String USAGE_VALUE = "Single-use Alchemy NFT";
+
+ public NFTAttestationEncoder() {
+ super(USAGE_VALUE, PROTOCOL_VERSION, PRIMARY_NAME);
+ }
+
+ @Override
+ public HashMap> getTypes() {
+ return getDefaultTypes();
+ }
+}
diff --git a/src/main/java/io/alchemynft/attestation/SignedNFTAttestation.java b/src/main/java/io/alchemynft/attestation/SignedNFTAttestation.java
index 60b377fe..5482fadc 100644
--- a/src/main/java/io/alchemynft/attestation/SignedNFTAttestation.java
+++ b/src/main/java/io/alchemynft/attestation/SignedNFTAttestation.java
@@ -1,182 +1,98 @@
package io.alchemynft.attestation;
import java.io.IOException;
+import java.io.InvalidObjectException;
+import java.nio.charset.StandardCharsets;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
-import org.bouncycastle.asn1.ASN1EncodableVector;
-import org.bouncycastle.asn1.ASN1InputStream;
-import org.bouncycastle.asn1.ASN1Integer;
-import org.bouncycastle.asn1.ASN1Primitive;
-import org.bouncycastle.asn1.ASN1Sequence;
-import org.bouncycastle.asn1.DERBitString;
-import org.bouncycastle.asn1.DERSequence;
-import org.bouncycastle.asn1.x509.AlgorithmIdentifier;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.bouncycastle.crypto.util.PublicKeyFactory;
-import org.tokenscript.attestation.core.ASNEncodable;
+import org.tokenscript.attestation.core.CompressedMsgSignature;
import org.tokenscript.attestation.core.ExceptionUtil;
-import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.core.Validateable;
-import org.tokenscript.attestation.core.Verifiable;
-
-public class SignedNFTAttestation implements ASNEncodable, Verifiable, Validateable {
- private static final Logger logger = LogManager.getLogger(SignedNFTAttestation.class);
- public static final int DEFAULT_SIGNING_VERSION = 2;
- public static final String PREFIX_MSG = "The digest of the ERC721 tokens for AlchemyNFT is: ";
- public static final String POSTFIX_MSG = "";
-
- private final NFTAttestation att;
- private final int signingVersion;
- private final Signature signature;
- private final AsymmetricKeyParameter attestationVerificationKey;
-
- public SignedNFTAttestation(NFTAttestation att, AsymmetricCipherKeyPair subjectSigningKey) {
- this(att, subjectSigningKey, DEFAULT_SIGNING_VERSION);
+import org.tokenscript.attestation.core.PersonalSignature;
+import org.tokenscript.attestation.core.Signature;
+
+public class SignedNFTAttestation implements InternalSignedNFTAttestation {
+ private static final Logger logger = LogManager.getLogger(SignedNFTAttestation.class);
+ public static final String PREFIX_MSG = "The digest of the ERC721 tokens for AlchemyNFT is: ";
+ public static final String POSTFIX_MSG = "";
+
+ private final InternalSignedNFTAttestation internalNftAtt;
+
+ public SignedNFTAttestation(NFTAttestation nftAtt, Signature signature) {
+ if (signature instanceof PersonalSignature || signature instanceof CompressedMsgSignature) {
+ this.internalNftAtt = new LegacySignedNFTAttestation(nftAtt, signature);
+ } else {
+ throw ExceptionUtil.throwException(logger,
+ new IllegalArgumentException("Signature is not version 1 or 2, this constructor only works with version 1 or version 2"));
}
-
- public SignedNFTAttestation(NFTAttestation att, AsymmetricCipherKeyPair subjectSigningKey, int signingVersion) {
- this.att = att;
- this.attestationVerificationKey = subjectSigningKey.getPublic();
- this.signature = makeSignature(subjectSigningKey, signingVersion);
- this.signingVersion = signingVersion;
-
- if (!verify()) {
- throw ExceptionUtil.throwException(logger, new IllegalArgumentException("The signature is not valid"));
- }
+ }
+
+ public SignedNFTAttestation(NFTAttestation att, AsymmetricKeyParameter subjectSigningKey, int signingVersion) {
+ if (signingVersion == 1 || signingVersion == 2) {
+ this.internalNftAtt = new LegacySignedNFTAttestation(att, subjectSigningKey, signingVersion);
+ } else if (signingVersion == 3) {
+ this.internalNftAtt = new Eip712SignedNFTAttestation(att, subjectSigningKey);
+ } else {
+ throw ExceptionUtil.throwException(logger,
+ new IllegalArgumentException("Unknown signature version"));
}
+ }
- /**
- * Constructor used for when we supply the signature separately
- */
- public SignedNFTAttestation(NFTAttestation att, Signature signature) {
- this.att = att;
- this.attestationVerificationKey = getKeyFromAttestation();
- this.signature = signature;
- this.signingVersion = determineSigningVersion();
- if (!verify()) {
- throw ExceptionUtil.throwException(logger, new IllegalArgumentException("The signature is not valid"));
- }
+ public SignedNFTAttestation(byte[] derEncoding, AsymmetricKeyParameter identifierAttestationVerificationKey) throws IOException {
+ InternalSignedNFTAttestation tempAtt = constructLegacy(derEncoding, identifierAttestationVerificationKey);
+ if (tempAtt != null) {
+ this.internalNftAtt = tempAtt;
+ return;
}
-
- public SignedNFTAttestation(byte[] derEncoding, AsymmetricKeyParameter identifierAttestationVerificationKey) throws IOException {
- ASN1InputStream input = new ASN1InputStream(derEncoding);
- ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject());
- input.close();
- int currentPos = 0;
- ASN1Sequence nftEncoding = ASN1Sequence.getInstance(asn1.getObjectAt(currentPos++));
- this.att = new NFTAttestation(nftEncoding.getEncoded(), identifierAttestationVerificationKey);
- if (asn1.getObjectAt(currentPos) instanceof ASN1Integer) {
- this.signingVersion = ASN1Integer.getInstance(asn1.getObjectAt(currentPos++)).intValueExact();
- } else {
- // If signingVersion is not present we default to version 1
- this.signingVersion = 1;
- }
- // todo this actually not used
- AlgorithmIdentifier algorithmIdentifier = AlgorithmIdentifier.getInstance(asn1.getObjectAt(currentPos++));
- DERBitString signatureEnc = DERBitString.getInstance(asn1.getObjectAt(currentPos++));
- this.signature = makeSignature(signatureEnc.getBytes(), signingVersion);
- this.attestationVerificationKey = getKeyFromAttestation();
+ tempAtt = constructEip(derEncoding, identifierAttestationVerificationKey);
+ if (tempAtt != null) {
+ this.internalNftAtt = tempAtt;
+ return;
}
-
- private int determineSigningVersion() {
- if (signature instanceof PersonalSignature) {
- return 1;
- }
- else if (signature instanceof CompressedMsgSignature) {
- return 2;
- } else {
- throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unexpected signature type used"));
- }
- }
-
- Signature makeSignature(byte[] encodedBytes, int signingVersion) {
- if (signingVersion == 1) {
- return new PersonalSignature(encodedBytes);
- }
- else if (signingVersion == 2) {
- return new CompressedMsgSignature(encodedBytes, PREFIX_MSG, POSTFIX_MSG);
- } else {
- throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unknown signing version"));
- }
- }
-
- Signature makeSignature(AsymmetricCipherKeyPair keys, int signingVersion) {
- if (signingVersion == 1) {
- return new PersonalSignature(keys, att.getDerEncoding());
- }
- else if (signingVersion == 2) {
- return new CompressedMsgSignature(keys, att.getDerEncoding(), PREFIX_MSG, POSTFIX_MSG);
- } else {
- throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Unknown signing version"));
- }
- }
-
- private AsymmetricKeyParameter getKeyFromAttestation() {
- AsymmetricKeyParameter key = null;
- try {
- key = PublicKeyFactory.createKey(
- att.getSignedIdentifierAttestation().getUnsignedAttestation()
- .getSubjectPublicKeyInfo());
- } catch (IOException e) {
- throw ExceptionUtil.makeRuntimeException(logger, "Could not restore key from signed signed attestation", e);
- }
- return key;
+ throw ExceptionUtil.throwException(logger,
+ new IllegalArgumentException("Could not decode SignedNFTAttestation"));
+ }
+
+ private LegacySignedNFTAttestation constructLegacy(byte[] derEncoding, AsymmetricKeyParameter identifierAttestationVerificationKey) {
+ try {
+ return new LegacySignedNFTAttestation(derEncoding, identifierAttestationVerificationKey);
+ } catch (IllegalArgumentException|IOException e) {
+ // The NFTAttestation is not version 1
+ return null;
}
-
- public NFTAttestation getUnsignedAttestation() {
- return att;
- }
-
- public Signature getSignature() {
- return signature;
- }
-
- /**
- * Returns the public key of the attestation signer
- */
- public AsymmetricKeyParameter getAttestationVerificationKey() { return attestationVerificationKey; }
-
- @Override
- public byte[] getDerEncoding() {
- return constructSignedAttestation(this.att, this.signingVersion, this.signature.getRawSignature());
- }
-
- static byte[] constructSignedAttestation(NFTAttestation unsignedAtt, int signingVersion, byte[] signature) {
- try {
- byte[] rawAtt = unsignedAtt.getDerEncoding();
- ASN1EncodableVector res = new ASN1EncodableVector();
- res.add(ASN1Primitive.fromByteArray(rawAtt));
- // Only include version number if it is greater than 1
- if (signingVersion > 1) {
- res.add(new ASN1Integer(signingVersion));
- }
- res.add(unsignedAtt.getSigningAlgorithm());
- res.add(new DERBitString(signature));
- return new DERSequence(res).getEncoded();
- } catch (Exception e) {
- throw new RuntimeException(e);
- }
- }
-
- @Override
- public boolean checkValidity() {
- return getUnsignedAttestation().checkValidity();
- }
-
- @Override
- public boolean verify() {
- if (!signature.verify(att.getDerEncoding(), attestationVerificationKey)) {
- return false;
- }
- if (!att.verify()) {
- return false;
- }
- // Verify that signature is done using thew right key
- if (!SignatureUtility.addressFromKey(attestationVerificationKey).equals(
- SignatureUtility.addressFromKey(getKeyFromAttestation()))) {
- return false;
- }
- return true;
+ }
+
+ private Eip712SignedNFTAttestation constructEip(byte[] derEncoding, AsymmetricKeyParameter identifierAttestationVerificationKey) {
+ try {
+ return new Eip712SignedNFTAttestation(new String(derEncoding, StandardCharsets.UTF_8), identifierAttestationVerificationKey);
+ } catch (IllegalArgumentException|IOException e) {
+ // The NFTAttestation is not version 1
+ return null;
}
-}
\ No newline at end of file
+ }
+
+ @Override
+ public AsymmetricKeyParameter getNFTAttestationVerificationKey() {
+ return internalNftAtt.getNFTAttestationVerificationKey();
+ }
+
+ @Override
+ public NFTAttestation getUnsignedAttestation() {
+ return internalNftAtt.getUnsignedAttestation();
+ }
+
+ @Override
+ public byte[] getDerEncoding() throws InvalidObjectException {
+ return internalNftAtt.getDerEncoding();
+ }
+
+ @Override
+ public boolean checkValidity() {
+ return internalNftAtt.checkValidity();
+ }
+
+ @Override
+ public boolean verify() {
+ return internalNftAtt.verify();
+ }
+}
diff --git a/src/main/java/org/devcon/ticket/Ticket.java b/src/main/java/org/devcon/ticket/Ticket.java
index 9c386ac0..b37561a8 100644
--- a/src/main/java/org/devcon/ticket/Ticket.java
+++ b/src/main/java/org/devcon/ticket/Ticket.java
@@ -24,7 +24,7 @@
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
-public class Ticket implements Attestable {
+public class Ticket extends Attestable {
private static final Logger logger = LogManager.getLogger(Ticket.class);
private final BigInteger ticketId;
diff --git a/src/main/java/org/devcon/ticket/TicketDecoder.java b/src/main/java/org/devcon/ticket/TicketDecoder.java
index dccab9a9..120feaf5 100644
--- a/src/main/java/org/devcon/ticket/TicketDecoder.java
+++ b/src/main/java/org/devcon/ticket/TicketDecoder.java
@@ -18,11 +18,11 @@
import org.bouncycastle.asn1.x509.SubjectPublicKeyInfo;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
-import org.tokenscript.attestation.AttestableObjectDecoder;
+import org.tokenscript.attestation.ObjectDecoder;
import org.tokenscript.attestation.core.ExceptionUtil;
import org.tokenscript.attestation.core.SignatureUtility;
-public class TicketDecoder implements AttestableObjectDecoder {
+public class TicketDecoder implements ObjectDecoder {
private static final Logger logger = LogManager.getLogger(TicketDecoder.class);
private static final String DEFAULT = "default";
diff --git a/src/main/java/org/tokenscript/auth/UnpredictableNumberBundle.java b/src/main/java/org/devcon/ticket/UnpredictableNumberBundle.java
similarity index 97%
rename from src/main/java/org/tokenscript/auth/UnpredictableNumberBundle.java
rename to src/main/java/org/devcon/ticket/UnpredictableNumberBundle.java
index 8cca2cfe..ac90f665 100644
--- a/src/main/java/org/tokenscript/auth/UnpredictableNumberBundle.java
+++ b/src/main/java/org/devcon/ticket/UnpredictableNumberBundle.java
@@ -1,4 +1,4 @@
-package org.tokenscript.auth;
+package org.devcon.ticket;
import com.fasterxml.jackson.annotation.JsonPropertyOrder;
diff --git a/src/main/java/org/tokenscript/auth/UnpredictableNumberTool.java b/src/main/java/org/devcon/ticket/UnpredictableNumberTool.java
similarity index 99%
rename from src/main/java/org/tokenscript/auth/UnpredictableNumberTool.java
rename to src/main/java/org/devcon/ticket/UnpredictableNumberTool.java
index 4556197a..38c4a1e5 100644
--- a/src/main/java/org/tokenscript/auth/UnpredictableNumberTool.java
+++ b/src/main/java/org/devcon/ticket/UnpredictableNumberTool.java
@@ -1,4 +1,4 @@
-package org.tokenscript.auth;
+package org.devcon.ticket;
import org.tokenscript.attestation.core.AttestationCrypto;
import org.tokenscript.attestation.core.ExceptionUtil;
diff --git a/src/main/java/org/devcon/ticket/UseTicketBundle.java b/src/main/java/org/devcon/ticket/UseTicketBundle.java
index 4c42246c..046b9a4a 100644
--- a/src/main/java/org/devcon/ticket/UseTicketBundle.java
+++ b/src/main/java/org/devcon/ticket/UseTicketBundle.java
@@ -13,8 +13,6 @@
import org.tokenscript.attestation.core.SignatureUtility;
import org.tokenscript.attestation.core.Verifiable;
import org.tokenscript.attestation.Timestamp;
-import org.tokenscript.auth.UnpredictableNumberBundle;
-import org.tokenscript.auth.UnpredictableNumberTool;
public class UseTicketBundle implements Verifiable {
private static final Logger logger = LogManager.getLogger(UseTicketBundle.class);
@@ -113,7 +111,7 @@ public boolean verify() {
logger.error("UseTicket could not be verified");
return false;
}
- if (!SignatureUtility.verifyPersonalEthereumSignature(computeMessage(un), signature, useTicket.getUserPublicKey())) {
+ if (!SignatureUtility.verifyPersonalEthereumSignature(computeMessage(un), signature, useTicket.getAttestedUserKey())) {
logger.error("Signature could not be verified");
return false;
}
diff --git a/src/main/java/org/tokenscript/attestation/AttestedKeyObject.java b/src/main/java/org/tokenscript/attestation/AttestedKeyObject.java
new file mode 100644
index 00000000..9159ae34
--- /dev/null
+++ b/src/main/java/org/tokenscript/attestation/AttestedKeyObject.java
@@ -0,0 +1,7 @@
+package org.tokenscript.attestation;
+
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+
+public abstract class AttestedKeyObject implements CheckableObject {
+ public abstract AsymmetricKeyParameter getAttestedUserKey();
+}
diff --git a/src/main/java/org/tokenscript/attestation/AttestedObject.java b/src/main/java/org/tokenscript/attestation/AttestedObject.java
index febae5b3..a4d69f4f 100644
--- a/src/main/java/org/tokenscript/attestation/AttestedObject.java
+++ b/src/main/java/org/tokenscript/attestation/AttestedObject.java
@@ -1,11 +1,5 @@
package org.tokenscript.attestation;
-import org.tokenscript.attestation.core.ASNEncodable;
-import org.tokenscript.attestation.core.Attestable;
-import org.tokenscript.attestation.core.AttestationCrypto;
-import org.tokenscript.attestation.core.ExceptionUtil;
-import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.core.Verifiable;
import java.io.IOException;
import java.io.InvalidObjectException;
import java.math.BigInteger;
@@ -17,8 +11,12 @@
import org.bouncycastle.asn1.DERSequence;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.PublicKeyFactory;
+import org.tokenscript.attestation.core.Attestable;
+import org.tokenscript.attestation.core.AttestationCrypto;
+import org.tokenscript.attestation.core.ExceptionUtil;
+import org.tokenscript.attestation.core.SignatureUtility;
-public class AttestedObject implements ASNEncodable, Verifiable {
+public class AttestedObject extends AttestedKeyObject {
private static final Logger logger = LogManager.getLogger(AttestedObject.class);
private final T attestableObject;
private final SignedIdentifierAttestation att;
@@ -30,18 +28,18 @@ public class AttestedObject implements ASNEncodable, Verif
public AttestedObject(T attestableObject, SignedIdentifierAttestation att, AsymmetricKeyParameter userPublicKey,
BigInteger attestationSecret, BigInteger chequeSecret, AttestationCrypto crypto) {
- this(attestableObject, att, userPublicKey, attestationSecret, chequeSecret, new byte[0], crypto);
+ this(attestableObject, att, attestationSecret, chequeSecret, new byte[0], crypto);
}
- public AttestedObject(T attestableObject, SignedIdentifierAttestation att, AsymmetricKeyParameter userPublicKey,
- BigInteger attestationSecret, BigInteger chequeSecret, byte[] unpredictableNumber,
- AttestationCrypto crypto)
+ public AttestedObject(T attestableObject, SignedIdentifierAttestation att,
+ BigInteger attestationSecret, BigInteger chequeSecret, byte[] unpredictableNumber,
+ AttestationCrypto crypto)
{
this.attestableObject = attestableObject;
this.att = att;
- this.userPublicKey = userPublicKey;
try {
+ this.userPublicKey = PublicKeyFactory.createKey(att.getUnsignedAttestation().getSubjectPublicKeyInfo());
this.pok = makeProof(attestationSecret, chequeSecret, unpredictableNumber, crypto);
ASN1EncodableVector vec = new ASN1EncodableVector();
vec.add(ASN1Sequence.getInstance(this.attestableObject.getDerEncoding()));
@@ -72,14 +70,14 @@ public AttestedObject(T object, SignedIdentifierAttestation att, ProofOfExponent
constructorCheck();
}
- public AttestedObject(byte[] derEncoding, AttestableObjectDecoder decoder,
+ public AttestedObject(byte[] derEncoding, ObjectDecoder attestableObjectDecoder,
AsymmetricKeyParameter publicAttestationSigningKey) {
this.encoding = derEncoding;
try {
ASN1InputStream input = new ASN1InputStream(derEncoding);
ASN1Sequence asn1 = ASN1Sequence.getInstance(input.readObject());
input.close();
- this.attestableObject = decoder.decode(asn1.getObjectAt(0).toASN1Primitive().getEncoded());
+ this.attestableObject = attestableObjectDecoder.decode(asn1.getObjectAt(0).toASN1Primitive().getEncoded());
this.att = new SignedIdentifierAttestation(asn1.getObjectAt(1).toASN1Primitive().getEncoded(), publicAttestationSigningKey);
this.pok = new UsageProofOfExponent(asn1.getObjectAt(2).toASN1Primitive().getEncoded());
this.userPublicKey = PublicKeyFactory.createKey(att.getUnsignedAttestation().getSubjectPublicKeyInfo());
@@ -108,7 +106,8 @@ public ProofOfExponent getPok() {
return pok;
}
- public AsymmetricKeyParameter getUserPublicKey() {
+ @Override
+ public AsymmetricKeyParameter getAttestedUserKey() {
return userPublicKey;
}
@@ -116,6 +115,7 @@ public AsymmetricKeyParameter getUserPublicKey() {
* Verifies that the redeem request will be accepted by the smart contract
* @return true if the redeem request should be accepted by the smart contract
*/
+ @Override
public boolean checkValidity() {
// CHECK: that it is an identifier attestation otherwise not all the checks of validity needed gets carried out
try {
@@ -142,7 +142,7 @@ public boolean checkValidity() {
// CHECK: the Ethereum address on the attestation matches receivers signing key
String attestationEthereumAddress = getAtt().getUnsignedAttestation().getAddress();
- if (!attestationEthereumAddress.equals(SignatureUtility.addressFromKey(getUserPublicKey()))) {
+ if (!attestationEthereumAddress.equals(SignatureUtility.addressFromKey(getAttestedUserKey()))) {
logger.error("The attestation is not to the same Ethereum user who is sending this request");
return false;
}
diff --git a/src/main/java/org/tokenscript/attestation/AttestedObjectDecoder.java b/src/main/java/org/tokenscript/attestation/AttestedObjectDecoder.java
new file mode 100644
index 00000000..a63a54de
--- /dev/null
+++ b/src/main/java/org/tokenscript/attestation/AttestedObjectDecoder.java
@@ -0,0 +1,20 @@
+package org.tokenscript.attestation;
+
+import java.io.IOException;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.core.Attestable;
+
+public class AttestedObjectDecoder implements ObjectDecoder> {
+ private ObjectDecoder underlyingDecoder;
+ private AsymmetricKeyParameter publicAttestationSigningKey;
+
+ public AttestedObjectDecoder(ObjectDecoder underlyingObjectDecoder, AsymmetricKeyParameter publicAttestationSigningKey) {
+ this.underlyingDecoder = underlyingObjectDecoder;
+ this.publicAttestationSigningKey = publicAttestationSigningKey;
+ }
+
+ @Override
+ public AttestedObject decode(byte[] encoding) throws IOException {
+ return new AttestedObject(encoding, underlyingDecoder, publicAttestationSigningKey);
+ }
+}
diff --git a/src/main/java/org/tokenscript/attestation/CheckableObject.java b/src/main/java/org/tokenscript/attestation/CheckableObject.java
new file mode 100644
index 00000000..5f5d8b21
--- /dev/null
+++ b/src/main/java/org/tokenscript/attestation/CheckableObject.java
@@ -0,0 +1,11 @@
+package org.tokenscript.attestation;
+
+import org.tokenscript.attestation.core.ASNEncodable;
+import org.tokenscript.attestation.core.Validateable;
+import org.tokenscript.attestation.core.Verifiable;
+
+/**
+ * Interface consolidating ASNEncodable, Verifiable, Validateable which is needed to make multiple interfaces play nicely with generics
+ */
+public interface CheckableObject extends ASNEncodable, Verifiable, Validateable {
+}
diff --git a/src/main/java/org/tokenscript/attestation/IdentifierAttestation.java b/src/main/java/org/tokenscript/attestation/IdentifierAttestation.java
index fff04aa3..e4fb8fd2 100644
--- a/src/main/java/org/tokenscript/attestation/IdentifierAttestation.java
+++ b/src/main/java/org/tokenscript/attestation/IdentifierAttestation.java
@@ -41,7 +41,7 @@ public class IdentifierAttestation extends Attestation implements Validateable {
public enum AttestationType {
PHONE ("phone"),
EMAIL ("email"),
- INETPERSONA("InetPersona");
+ INETPERSONA("inetpersona");
private final String type;
@@ -144,7 +144,7 @@ public IdentifierAttestation(byte[] commitment, AsymmetricKeyParameter key) {
* issuer, smartcontracts.
* This is done using labeledURL, hence URL must be a valid URL
*/
- public IdentifierAttestation(String label, String URL, AsymmetricKeyParameter key) throws MalformedURLException {
+ public IdentifierAttestation(String label, String URL, AsymmetricKeyParameter subjectKey) throws MalformedURLException {
super();
super.setVersion(NFT_VERSION);
super.setSubject(makeLabeledURI(label, URL));
@@ -152,7 +152,7 @@ public IdentifierAttestation(String label, String URL, AsymmetricKeyParameter ke
super.setIssuer("CN=attestation.id");
super.setSerialNumber(1);
try {
- SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(key);
+ SubjectPublicKeyInfo spki = SubjectPublicKeyInfoFactory.createSubjectPublicKeyInfo(subjectKey);
super.setSubjectPublicKeyInfo(spki);
} catch (IOException e) {
throw ExceptionUtil.makeRuntimeException(logger, "Could not decode asn1", e);
diff --git a/src/main/java/org/tokenscript/attestation/AttestableObjectDecoder.java b/src/main/java/org/tokenscript/attestation/ObjectDecoder.java
similarity index 51%
rename from src/main/java/org/tokenscript/attestation/AttestableObjectDecoder.java
rename to src/main/java/org/tokenscript/attestation/ObjectDecoder.java
index e9504528..8be1c1fd 100644
--- a/src/main/java/org/tokenscript/attestation/AttestableObjectDecoder.java
+++ b/src/main/java/org/tokenscript/attestation/ObjectDecoder.java
@@ -1,8 +1,7 @@
package org.tokenscript.attestation;
-import org.tokenscript.attestation.core.Attestable;
import java.io.IOException;
-public interface AttestableObjectDecoder {
+public interface ObjectDecoder {
public T decode(byte[] encoding) throws IOException;
}
diff --git a/src/main/java/org/tokenscript/attestation/cheque/Cheque.java b/src/main/java/org/tokenscript/attestation/cheque/Cheque.java
index b8d1102b..f3d33268 100644
--- a/src/main/java/org/tokenscript/attestation/cheque/Cheque.java
+++ b/src/main/java/org/tokenscript/attestation/cheque/Cheque.java
@@ -25,7 +25,7 @@
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.util.SubjectPublicKeyInfoFactory;
-public class Cheque implements Attestable {
+public class Cheque extends Attestable {
private static final Logger logger = LogManager.getLogger(Cheque.class);
private final byte[] commitment;
private final long amount;
diff --git a/src/main/java/org/tokenscript/attestation/cheque/ChequeDecoder.java b/src/main/java/org/tokenscript/attestation/cheque/ChequeDecoder.java
index 22825e26..5c64efb5 100644
--- a/src/main/java/org/tokenscript/attestation/cheque/ChequeDecoder.java
+++ b/src/main/java/org/tokenscript/attestation/cheque/ChequeDecoder.java
@@ -1,6 +1,6 @@
package org.tokenscript.attestation.cheque;
-import org.tokenscript.attestation.AttestableObjectDecoder;
+import org.tokenscript.attestation.ObjectDecoder;
import org.tokenscript.attestation.core.ExceptionUtil;
import org.tokenscript.attestation.core.SignatureUtility;
import java.io.IOException;
@@ -15,7 +15,7 @@
import org.bouncycastle.asn1.DERBitString;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-public class ChequeDecoder implements AttestableObjectDecoder {
+public class ChequeDecoder implements ObjectDecoder {
private static final Logger logger = LogManager.getLogger(ChequeDecoder.class);
public ChequeDecoder() {}
diff --git a/src/main/java/io/alchemynft/attestation/AbstractSignature.java b/src/main/java/org/tokenscript/attestation/core/AbstractSignature.java
similarity index 69%
rename from src/main/java/io/alchemynft/attestation/AbstractSignature.java
rename to src/main/java/org/tokenscript/attestation/core/AbstractSignature.java
index f35f96f8..58366796 100644
--- a/src/main/java/io/alchemynft/attestation/AbstractSignature.java
+++ b/src/main/java/org/tokenscript/attestation/core/AbstractSignature.java
@@ -1,16 +1,14 @@
-package io.alchemynft.attestation;
+package org.tokenscript.attestation.core;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.attestation.core.SignatureUtility;
public abstract class AbstractSignature implements Signature {
private final String type;
private final byte[] rawSignature;
- public AbstractSignature(AsymmetricCipherKeyPair keys, byte[] unprocessedMessage, String type) {
+ public AbstractSignature(AsymmetricKeyParameter signingKey, byte[] unprocessedMessage, String type) {
this.type = type;
- this.rawSignature = sign(keys, unprocessedMessage);
+ this.rawSignature = sign(signingKey, unprocessedMessage);
}
public AbstractSignature(byte[] rawSignature, String type) {
@@ -18,8 +16,8 @@ public AbstractSignature(byte[] rawSignature, String type) {
this.rawSignature = rawSignature;
}
- protected byte[] sign(AsymmetricCipherKeyPair keys, byte[] unprocessedMessage) {
- return SignatureUtility.signWithEthereum(processMessage(unprocessedMessage), keys.getPrivate());
+ protected byte[] sign(AsymmetricKeyParameter keys, byte[] unprocessedMessage) {
+ return SignatureUtility.signWithEthereum(processMessage(unprocessedMessage), keys);
}
@Override
diff --git a/src/main/java/org/tokenscript/attestation/core/Attestable.java b/src/main/java/org/tokenscript/attestation/core/Attestable.java
index 7f094bff..7f63602b 100644
--- a/src/main/java/org/tokenscript/attestation/core/Attestable.java
+++ b/src/main/java/org/tokenscript/attestation/core/Attestable.java
@@ -1,5 +1,7 @@
package org.tokenscript.attestation.core;
-public interface Attestable extends ASNEncodable, Verifiable, Validateable {
- public byte[] getCommitment();
+import org.tokenscript.attestation.CheckableObject;
+
+public abstract class Attestable implements CheckableObject {
+ public abstract byte[] getCommitment();
}
diff --git a/src/main/java/io/alchemynft/attestation/CompressedMsgSignature.java b/src/main/java/org/tokenscript/attestation/core/CompressedMsgSignature.java
similarity index 73%
rename from src/main/java/io/alchemynft/attestation/CompressedMsgSignature.java
rename to src/main/java/org/tokenscript/attestation/core/CompressedMsgSignature.java
index 14b18544..80ef3212 100644
--- a/src/main/java/io/alchemynft/attestation/CompressedMsgSignature.java
+++ b/src/main/java/org/tokenscript/attestation/core/CompressedMsgSignature.java
@@ -1,11 +1,8 @@
-package io.alchemynft.attestation;
+package org.tokenscript.attestation.core;
import java.nio.charset.StandardCharsets;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.util.encoders.Hex;
-import org.tokenscript.attestation.core.AttestationCrypto;
-import org.tokenscript.attestation.core.SignatureUtility;
public class CompressedMsgSignature implements Signature {
private static final String TYPE_OF_SIGNATURE = "compressed";
@@ -23,21 +20,21 @@ public CompressedMsgSignature(byte[] rawSignature, String messagePrefix, String
this.rawSignature = rawSignature;
}
- public CompressedMsgSignature(AsymmetricCipherKeyPair keys, byte[] unprocessedMsg) {
- this(keys, unprocessedMsg, "", "");
+ public CompressedMsgSignature(AsymmetricKeyParameter signingKey, byte[] unprocessedMsg) {
+ this(signingKey, unprocessedMsg, "", "");
}
/**
* Constructs a compressed signature of the format @messagePrefix concatenated with Keccak(@unprocessedMsg) concatenated with @messagePostfix.
*/
- public CompressedMsgSignature(AsymmetricCipherKeyPair keys, byte[] unprocessedMsg, String messagePrefix, String messagePostfix) {
+ public CompressedMsgSignature(AsymmetricKeyParameter signingKey, byte[] unprocessedMsg, String messagePrefix, String messagePostfix) {
this.messagePrefix = messagePrefix;
this.messagePostfix = messagePostfix;
- this.rawSignature = sign(keys, unprocessedMsg);
+ this.rawSignature = sign(signingKey, unprocessedMsg);
}
- protected byte[] sign(AsymmetricCipherKeyPair keys, byte[] unprocessedMsg) {
- return SignatureUtility.signWithEthereum(processMessage(unprocessedMsg), keys.getPrivate());
+ protected byte[] sign(AsymmetricKeyParameter keys, byte[] unprocessedMsg) {
+ return SignatureUtility.signWithEthereum(processMessage(unprocessedMsg), keys);
}
@Override
diff --git a/src/main/java/io/alchemynft/attestation/PersonalSignature.java b/src/main/java/org/tokenscript/attestation/core/PersonalSignature.java
similarity index 56%
rename from src/main/java/io/alchemynft/attestation/PersonalSignature.java
rename to src/main/java/org/tokenscript/attestation/core/PersonalSignature.java
index c37a2fd5..d7efafbb 100644
--- a/src/main/java/io/alchemynft/attestation/PersonalSignature.java
+++ b/src/main/java/org/tokenscript/attestation/core/PersonalSignature.java
@@ -1,13 +1,12 @@
-package io.alchemynft.attestation;
+package org.tokenscript.attestation.core;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
-import org.tokenscript.attestation.core.SignatureUtility;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
public class PersonalSignature extends AbstractSignature {
private static final String TYPE_OF_SIGNATURE = "personal";
- public PersonalSignature(AsymmetricCipherKeyPair keys, byte[] unprocessedMsg) {
- super(keys, unprocessedMsg, TYPE_OF_SIGNATURE);
+ public PersonalSignature(AsymmetricKeyParameter signingKey, byte[] unprocessedMsg) {
+ super(signingKey, unprocessedMsg, TYPE_OF_SIGNATURE);
}
public PersonalSignature(byte[] rawSignature) {
diff --git a/src/main/java/io/alchemynft/attestation/RawSignature.java b/src/main/java/org/tokenscript/attestation/core/RawSignature.java
similarity index 55%
rename from src/main/java/io/alchemynft/attestation/RawSignature.java
rename to src/main/java/org/tokenscript/attestation/core/RawSignature.java
index 3090e0a6..1a3e3390 100644
--- a/src/main/java/io/alchemynft/attestation/RawSignature.java
+++ b/src/main/java/org/tokenscript/attestation/core/RawSignature.java
@@ -1,12 +1,12 @@
-package io.alchemynft.attestation;
+package org.tokenscript.attestation.core;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
public class RawSignature extends AbstractSignature {
private static final String TYPE_OF_SIGNATURE = "raw";
- public RawSignature(AsymmetricCipherKeyPair keys, byte[] unprocessedMsg) {
- super(keys, unprocessedMsg, TYPE_OF_SIGNATURE);
+ public RawSignature(AsymmetricKeyParameter signingKey, byte[] unprocessedMsg) {
+ super(signingKey, unprocessedMsg, TYPE_OF_SIGNATURE);
}
public RawSignature(byte[] signature) {
diff --git a/src/main/java/io/alchemynft/attestation/Signature.java b/src/main/java/org/tokenscript/attestation/core/Signature.java
similarity index 86%
rename from src/main/java/io/alchemynft/attestation/Signature.java
rename to src/main/java/org/tokenscript/attestation/core/Signature.java
index b7304c61..74c25d27 100644
--- a/src/main/java/io/alchemynft/attestation/Signature.java
+++ b/src/main/java/org/tokenscript/attestation/core/Signature.java
@@ -1,4 +1,4 @@
-package io.alchemynft.attestation;
+package org.tokenscript.attestation.core;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
diff --git a/src/main/java/org/tokenscript/auth/AuthenticatorEncoder.java b/src/main/java/org/tokenscript/attestation/eip712/AuthenticatorEncoder.java
similarity index 94%
rename from src/main/java/org/tokenscript/auth/AuthenticatorEncoder.java
rename to src/main/java/org/tokenscript/attestation/eip712/AuthenticatorEncoder.java
index 36253024..19944050 100644
--- a/src/main/java/org/tokenscript/auth/AuthenticatorEncoder.java
+++ b/src/main/java/org/tokenscript/attestation/eip712/AuthenticatorEncoder.java
@@ -1,4 +1,4 @@
-package org.tokenscript.auth;
+package org.tokenscript.attestation.eip712;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData.Entry;
import java.security.SecureRandom;
diff --git a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequest.java b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequest.java
index b28f90f9..6c40574f 100644
--- a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequest.java
+++ b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequest.java
@@ -1,5 +1,9 @@
package org.tokenscript.attestation.eip712;
+import com.fasterxml.jackson.core.JsonProcessingException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.tokenscript.attestation.AttestationRequest;
import org.tokenscript.attestation.FullProofOfExponent;
import org.tokenscript.attestation.IdentifierAttestation.AttestationType;
@@ -10,11 +14,8 @@
import org.tokenscript.attestation.core.Validateable;
import org.tokenscript.attestation.core.Verifiable;
import org.tokenscript.attestation.eip712.Eip712AttestationRequestEncoder.AttestationRequestInternalData;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.eip712.Eip712Encoder;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Validator;
import org.tokenscript.eip712.JsonEncodable;
@@ -67,7 +68,7 @@ public Eip712AttestationRequest(String attestorDomain, long acceptableTimeLimit,
this.attestationRequest = request;
this.jsonEncoding = makeToken(identifier, signingKey);
this.publicKey = retrieveUserPublicKey(jsonEncoding, AttestationRequestInternalData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationRequestInternalData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationRequestInternalData.class);
} catch (Exception e ) {
throw ExceptionUtil.throwException(logger,
new IllegalArgumentException("Could not encode object"));
@@ -91,7 +92,7 @@ public Eip712AttestationRequest(String attestorDomain, long acceptableTimeLimit,
this.acceptableTimeLimit = acceptableTimeLimit;
this.jsonEncoding = jsonEncoding;
this.publicKey = retrieveUserPublicKey(jsonEncoding, AttestationRequestInternalData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationRequestInternalData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationRequestInternalData.class);
this.attestationRequest = new AttestationRequest(URLUtility.decodeData(data.getPayload()));
} catch (Exception e ) {
throw ExceptionUtil.throwException(logger,
@@ -108,7 +109,7 @@ void constructorCheck() throws IllegalArgumentException {
}
String makeToken(String identifier, AsymmetricKeyParameter signingKey) throws JsonProcessingException {
- Eip712Issuer issuer = new Eip712Issuer(signingKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(signingKey, encoder);
String encodedAttestationRequest = URLUtility.encodeData(attestationRequest.getDerEncoding());
Timestamp timestamp = Nonce.getTimestamp(attestationRequest.getPok().getUnpredictableNumber());
AttestationRequestInternalData data = new AttestationRequestInternalData(
@@ -140,7 +141,7 @@ public String getJsonEncoding() {
@Override
public boolean verify() {
if (!attestationRequest.verify()) {
- logger.error("Could not verify signature");
+ logger.error("Could not verify proof");
return false;
}
return true;
@@ -148,6 +149,10 @@ public boolean verify() {
@Override
public boolean checkValidity() {
+ if (!validateDomain(jsonEncoding)) {
+ logger.error("Domain invalid");
+ return false;
+ }
if (!data.getDescription().equals(encoder.getUsageValue())){
logger.error("Description field is incorrect");
return false;
diff --git a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequestWithUsage.java b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequestWithUsage.java
index fb7478f3..8095273e 100644
--- a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequestWithUsage.java
+++ b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationRequestWithUsage.java
@@ -18,7 +18,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Validator;
import org.tokenscript.eip712.JsonEncodable;
@@ -49,7 +49,7 @@ public Eip712AttestationRequestWithUsage(String attestorDomain,
this.attestationRequestWithUsage = attestationRequestWithUsage;
this.jsonEncoding = makeToken(identifier, attestationRequestWithUsage, signingKey);
this.userPublicKey = retrieveUserPublicKey(jsonEncoding, AttestationRequestWUsageData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationRequestWUsageData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationRequestWUsageData.class);
} catch (Exception e ) {
throw ExceptionUtil.throwException(logger,
new IllegalArgumentException("Could not encode object"));
@@ -69,7 +69,7 @@ public Eip712AttestationRequestWithUsage(String attestorDomain,
this.maxTokenValidityInMs = maxTokenValidityInMs;
this.jsonEncoding = jsonEncoding;
this.userPublicKey = retrieveUserPublicKey(jsonEncoding, AttestationRequestWUsageData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationRequestWUsageData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationRequestWUsageData.class);
this.attestationRequestWithUsage = new AttestationRequestWithUsage(URLUtility.decodeData(data.getPayload()));
} catch (Exception e ) {
throw ExceptionUtil.throwException(logger,
@@ -87,7 +87,7 @@ void constructorCheck() throws IllegalArgumentException {
String makeToken(String identifier, AttestationRequestWithUsage attestationRequestWithUsage,
AsymmetricKeyParameter signingKey) throws IOException {
- Eip712Issuer issuer = new Eip712Issuer(signingKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(signingKey, encoder);
String encodedUseAttestation = URLUtility.encodeData(attestationRequestWithUsage.getDerEncoding());
Timestamp now = new Timestamp();
Timestamp expirationTime = new Timestamp(now.getTime() + maxTokenValidityInMs);
diff --git a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationUsage.java b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationUsage.java
index e2efee83..c322553e 100644
--- a/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationUsage.java
+++ b/src/main/java/org/tokenscript/attestation/eip712/Eip712AttestationUsage.java
@@ -14,7 +14,7 @@
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Validator;
import org.tokenscript.eip712.JsonEncodable;
@@ -49,7 +49,7 @@ public Eip712AttestationUsage(String attestorDomain, long maxTokenValidityInMs,
this.maxTokenValidityInMs = maxTokenValidityInMs;
this.jsonEncoding = makeToken(identifier, useAttestation, signingKey);
this.userPublicKey = retrieveUserPublicKey(jsonEncoding, AttestationUsageData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationUsageData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationUsageData.class);
this.validator = new AttestationAndUsageValidator(useAttestation, identifier, userPublicKey);
} catch (Exception e ) {
throw ExceptionUtil.throwException(logger,
@@ -71,7 +71,7 @@ public Eip712AttestationUsage(String attestorDomain, AsymmetricKeyParameter atte
this.maxTokenValidityInMs = maxTokenValidityInMs;
this.jsonEncoding = jsonEncoding;
this.userPublicKey = retrieveUserPublicKey(jsonEncoding, AttestationUsageData.class);
- this.data = retrieveUnderlyingObject(jsonEncoding, AttestationUsageData.class);
+ this.data = retrieveUnderlyingJson(jsonEncoding, AttestationUsageData.class);
UseAttestation useAttestation = new UseAttestation(URLUtility.decodeData(data.getPayload()), attestationIssuerVerificationKey);
this.validator = new AttestationAndUsageValidator(useAttestation, data.getIdentifier(), userPublicKey);
} catch (Exception e ) {
@@ -90,7 +90,7 @@ void constructorCheck() throws IllegalArgumentException {
String makeToken(String identifier, UseAttestation useAttestation,
AsymmetricKeyParameter signingKey) throws IOException {
- Eip712Issuer issuer = new Eip712Issuer(signingKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(signingKey, encoder);
String encodedUseAttestation = URLUtility.encodeData(useAttestation.getDerEncoding());
Timestamp now = new Timestamp();
Timestamp expirationTime = new Timestamp(now.getTime() + maxTokenValidityInMs);
diff --git a/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectSigner.java b/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectSigner.java
new file mode 100644
index 00000000..914bb82c
--- /dev/null
+++ b/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectSigner.java
@@ -0,0 +1,42 @@
+package org.tokenscript.attestation.eip712;
+
+import com.fasterxml.jackson.core.JsonProcessingException;
+import java.io.InvalidObjectException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.tokenscript.attestation.Timestamp;
+import org.tokenscript.attestation.core.ASNEncodable;
+import org.tokenscript.attestation.core.ExceptionUtil;
+import org.tokenscript.attestation.core.URLUtility;
+import org.tokenscript.eip712.Eip712Encoder;
+import org.tokenscript.eip712.Eip712Signer;
+import org.tokenscript.eip712.FullEip712InternalData;
+
+/**
+ * Class for issuing EIP712 tokens containing any ASNEncodable object.
+ */
+public class Eip712ObjectSigner extends
+ Eip712Signer {
+ private static final Logger logger = LogManager.getLogger(Eip712ObjectSigner.class);
+ private final Eip712Encoder encoder;
+
+ public Eip712ObjectSigner(AsymmetricKeyParameter signingKey, Eip712Encoder encoder) {
+ super(signingKey, encoder);
+ this.encoder = encoder;
+ }
+
+
+ public String buildSignedToken(ObjectT attestedObject, String webDomain) {
+ try {
+ String encodedObject = URLUtility.encodeData(attestedObject.getDerEncoding());
+ Timestamp time = new Timestamp();
+ FullEip712InternalData auth = new FullEip712InternalData(encoder.getUsageValue(), encodedObject, time);
+ return buildSignedTokenFromJsonObject(auth, webDomain);
+ } catch (InvalidObjectException e) {
+ throw ExceptionUtil.makeRuntimeException(logger, "Could not retrieve DER encoding of attested object", e);
+ } catch (JsonProcessingException e) {
+ throw ExceptionUtil.makeRuntimeException(logger, "Could not build json token", e);
+ }
+ }
+}
diff --git a/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectValidator.java b/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectValidator.java
new file mode 100644
index 00000000..8ae8d209
--- /dev/null
+++ b/src/main/java/org/tokenscript/attestation/eip712/Eip712ObjectValidator.java
@@ -0,0 +1,89 @@
+package org.tokenscript.attestation.eip712;
+
+import java.io.IOException;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
+import org.tokenscript.attestation.AttestedKeyObject;
+import org.tokenscript.attestation.ObjectDecoder;
+import org.tokenscript.attestation.Timestamp;
+import org.tokenscript.attestation.core.SignatureUtility;
+import org.tokenscript.attestation.core.URLUtility;
+import org.tokenscript.eip712.Eip712Encoder;
+import org.tokenscript.eip712.Eip712Validator;
+import org.tokenscript.eip712.FullEip712InternalData;
+
+/**
+ * Class for validating EIP712 tokens containing any ASNEncodable object.
+ */
+public class Eip712ObjectValidator extends Eip712Validator {
+ private static final Logger logger = LogManager.getLogger(Eip712ObjectValidator.class);
+ private final ObjectDecoder decoder;
+ private final long acceptableTimeLimit;
+
+ public Eip712ObjectValidator(ObjectDecoder decoder, Eip712Encoder authenticator,
+ String domain) {
+ this(decoder, authenticator, domain, Nonce.DEFAULT_NONCE_TIME_LIMIT_MS);
+ }
+
+ public Eip712ObjectValidator(ObjectDecoder decoder, Eip712Encoder authenticator,
+ String domain, long acceptableTimeLimit) {
+ super(domain, authenticator);
+ this.acceptableTimeLimit = acceptableTimeLimit;
+ this.decoder = decoder;
+ }
+
+ public boolean validateRequest(String jsonInput) {
+ try {
+ FullEip712InternalData eip712InternalData = retrieveUnderlyingJson(jsonInput, FullEip712InternalData.class);
+ T attestedObject = retrieveUnderlyingObject(jsonInput);
+ String signerAddress = SignatureUtility.addressFromKey(attestedObject.getAttestedUserKey());
+
+ if (!verifySignature(jsonInput, signerAddress, FullEip712InternalData.class)) {
+ logger.error("Could not verify signature");
+ return false;
+ }
+ if (!validateDomain(jsonInput)) {
+ logger.error("Could not validate the domain data");
+ return false;
+ }
+ if (!validateEip712InternalData(eip712InternalData)) {
+ logger.error("Could not validate authentication request data");
+ return false;
+ }
+ if (!attestedObject.checkValidity()) {
+ logger.error("Could not validate attested object");
+ return false;
+ }
+ if (!attestedObject.verify()) {
+ logger.error("Could not verify attested object");
+ return false;
+ }
+ } catch (Exception e) {
+ logger.error("Could not decode json request");
+ return false;
+ }
+ return true;
+ }
+
+ public T retrieveUnderlyingObject(String jsonInput) throws IOException {
+ FullEip712InternalData message = retrieveUnderlyingJson(jsonInput, FullEip712InternalData.class);
+ byte[] attestedObjectBytes = URLUtility.decodeData(message.getPayload());
+ T attestedObject = decoder.decode(attestedObjectBytes);
+ return attestedObject;
+ }
+
+ private boolean validateEip712InternalData(FullEip712InternalData eip712InternalData) {
+ if (!eip712InternalData.getDescription().equals(encoder.getUsageValue())) {
+ logger.error("Description is incorrect");
+ return false;
+ }
+ Timestamp timestamp = new Timestamp(eip712InternalData.getTimestamp());
+ timestamp.setValidity(acceptableTimeLimit);
+ if (!timestamp.validateTimestamp()) {
+ logger.error("Invalid timestamp");
+ return false;
+ }
+ return true;
+ }
+
+}
diff --git a/src/main/java/org/tokenscript/auth/Eip712AuthIssuer.java b/src/main/java/org/tokenscript/auth/Eip712AuthIssuer.java
deleted file mode 100644
index 43e9fffa..00000000
--- a/src/main/java/org/tokenscript/auth/Eip712AuthIssuer.java
+++ /dev/null
@@ -1,35 +0,0 @@
-package org.tokenscript.auth;
-
-import org.tokenscript.attestation.AttestedObject;
-import org.tokenscript.attestation.core.URLUtility;
-import org.tokenscript.attestation.Timestamp;
-import com.fasterxml.jackson.core.JsonProcessingException;
-import java.security.SecureRandom;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.eip712.Eip712Issuer;
-import org.tokenscript.eip712.FullEip712InternalData;
-
-/**
- * Class for issuing EIP712 tokens containing a useDevconTicket object.
- * The tokens are supposed to be issued by the user for consumption by a third party website.
- */
-public class Eip712AuthIssuer extends Eip712Issuer {
- private final AuthenticatorEncoder encoder;
-
- public Eip712AuthIssuer(AsymmetricKeyParameter signingKey, long chainId) {
- this(signingKey, new AuthenticatorEncoder(chainId, new SecureRandom()));
- }
-
- public Eip712AuthIssuer(AsymmetricKeyParameter signingKey, AuthenticatorEncoder encoder) {
- super(signingKey, encoder);
- this.encoder = encoder;
- }
-
-
- public String buildSignedToken(AttestedObject attestedObject, String webDomain) throws JsonProcessingException {
- String encodedObject = URLUtility.encodeData(attestedObject.getDerEncoding());
- Timestamp time = new Timestamp();
- FullEip712InternalData auth = new FullEip712InternalData(encoder.getUsageValue(), encodedObject, time);
- return buildSignedTokenFromJsonObject(auth, webDomain);
- }
-}
diff --git a/src/main/java/org/tokenscript/auth/Eip712AuthValidator.java b/src/main/java/org/tokenscript/auth/Eip712AuthValidator.java
deleted file mode 100644
index db267e5f..00000000
--- a/src/main/java/org/tokenscript/auth/Eip712AuthValidator.java
+++ /dev/null
@@ -1,95 +0,0 @@
-package org.tokenscript.auth;
-
-import org.tokenscript.attestation.AttestableObjectDecoder;
-import org.tokenscript.attestation.AttestedObject;
-import org.tokenscript.attestation.core.Attestable;
-import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.core.URLUtility;
-import org.tokenscript.attestation.eip712.Nonce;
-import org.tokenscript.attestation.Timestamp;
-import org.apache.logging.log4j.LogManager;
-import org.apache.logging.log4j.Logger;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.tokenscript.eip712.Eip712Validator;
-import org.tokenscript.eip712.FullEip712InternalData;
-
-/**
- * Class for validating EIP712 tokens containing a useDevconTicket object.
- * The tokens are supposed to be issued by the user for consumption by a third party website.
- */
-public class Eip712AuthValidator extends Eip712Validator {
- private static final Logger logger = LogManager.getLogger(Eip712AuthValidator.class);
- private final AsymmetricKeyParameter attestorPublicKey;
- private final AttestableObjectDecoder decoder;
- private final long acceptableTimeLimit;
-
- public Eip712AuthValidator(AttestableObjectDecoder decoder, AuthenticatorEncoder authenticator, AsymmetricKeyParameter attestorPublicKey, String domain) {
- this(decoder, authenticator, attestorPublicKey, domain, Nonce.DEFAULT_NONCE_TIME_LIMIT_MS);
- }
-
- public Eip712AuthValidator(AttestableObjectDecoder decoder, AuthenticatorEncoder authenticator, AsymmetricKeyParameter attestorPublicKey, String domain, long acceptableTimeLimit) {
- super(domain, authenticator);
- this.acceptableTimeLimit = acceptableTimeLimit;
- this.attestorPublicKey = attestorPublicKey;
- this.decoder = decoder;
- }
-
- public boolean validateRequest(String jsonInput) {
- try {
- FullEip712InternalData auth = retrieveUnderlyingObject(jsonInput, FullEip712InternalData.class);
- AttestedObject attestedObject = retrieveAttestedObject(auth);
- String signerAddress = SignatureUtility.addressFromKey(attestedObject.getUserPublicKey());
-
- if (!verifySignature(jsonInput, signerAddress, FullEip712InternalData.class)) {
- logger.error("Could not verify signature");
- return false;
- }
- if (!validateAuthentication(auth)) {
- logger.error("Could not validate authentication request data");
- return false;
- }
- if (!validateAttestedObject(attestedObject)) {
- logger.error("Could not validate attested object");
- return false;
- }
- } catch (Exception e) {
- logger.error("Could not decode json request");
- return false;
- }
- return true;
- }
-
- private AttestedObject retrieveAttestedObject(FullEip712InternalData message) {
- byte[] attestedObjectBytes = URLUtility.decodeData(message.getPayload());
- AttestedObject decodedAttestedObject = new AttestedObject<>(attestedObjectBytes, decoder, attestorPublicKey);
- return decodedAttestedObject;
- }
-
- private boolean validateAuthentication(FullEip712InternalData authentication) {
- if (!authentication.getDescription().equals(encoder.getUsageValue())) {
- logger.error("Description is incorrect");
- return false;
- }
- Timestamp timestamp = new Timestamp(authentication.getTimestamp());
- timestamp.setValidity(acceptableTimeLimit);
- if (!timestamp.validateTimestamp()) {
- logger.error("Invalid timestamp");
- return false;
- }
- return true;
- }
-
- private boolean validateAttestedObject(AttestedObject attestedObject) {
- // Validate useAttestableObject
- if (!attestedObject.verify()) {
- logger.error("Could not verify the attested object");
- return false;
- }
- if (!attestedObject.checkValidity()) {
- logger.error("Attested object is not valid");
- return false;
- }
- return true;
- }
-
-}
diff --git a/src/main/java/org/tokenscript/eip712/Eip712Common.java b/src/main/java/org/tokenscript/eip712/Eip712Common.java
index 8e4a5b61..1fcf34f5 100644
--- a/src/main/java/org/tokenscript/eip712/Eip712Common.java
+++ b/src/main/java/org/tokenscript/eip712/Eip712Common.java
@@ -6,7 +6,10 @@
import java.net.MalformedURLException;
import java.net.URL;
import java.security.Security;
+import org.apache.logging.log4j.LogManager;
+import org.apache.logging.log4j.Logger;
import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.tokenscript.attestation.core.ExceptionUtil;
import org.tokenscript.attestation.core.Validateable;
import org.tokenscript.attestation.core.Verifiable;
@@ -14,6 +17,7 @@
* Common class for EIP712 JSON issuance and validation
*/
public abstract class Eip712Common {
+ private static final Logger logger = LogManager.getLogger(Eip712Common.class);
protected final CryptoFunctions cryptoFunctions;
protected final ObjectMapper mapper;
protected final Eip712Encoder encoder;
@@ -26,6 +30,15 @@ public Eip712Common(Eip712Encoder encoder) {
this.encoder = encoder;
}
+ public String getSignatureFromJson(String signedJson) {
+ try {
+ Eip712ExternalData data = mapper.readValue(signedJson, Eip712ExternalData.class);
+ return data.getSignatureInHex();
+ } catch (Exception e) {
+ throw ExceptionUtil.makeRuntimeException(logger, "Could not recover signature from signed json", e);
+ }
+ }
+
public static boolean isDomainValid(String domain) {
try {
// Check if we get a malformed exception
diff --git a/src/main/java/org/tokenscript/eip712/Eip712Issuer.java b/src/main/java/org/tokenscript/eip712/Eip712Signer.java
similarity index 56%
rename from src/main/java/org/tokenscript/eip712/Eip712Issuer.java
rename to src/main/java/org/tokenscript/eip712/Eip712Signer.java
index 79b89bea..d4f9aa06 100644
--- a/src/main/java/org/tokenscript/eip712/Eip712Issuer.java
+++ b/src/main/java/org/tokenscript/eip712/Eip712Signer.java
@@ -1,6 +1,5 @@
package org.tokenscript.eip712;
-import org.tokenscript.attestation.core.SignatureUtility;
import com.alphawallet.token.entity.EthereumTypedMessage;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData.EIP712Domain;
@@ -9,11 +8,12 @@
import java.nio.charset.StandardCharsets;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.util.encoders.Hex;
+import org.tokenscript.attestation.core.SignatureUtility;
-public class Eip712Issuer extends Eip712Common {
+public class Eip712Signer extends Eip712Common {
protected final AsymmetricKeyParameter signingKey;
- public Eip712Issuer(AsymmetricKeyParameter signingKey, Eip712Encoder encoder) {
+ public Eip712Signer(AsymmetricKeyParameter signingKey, Eip712Encoder encoder) {
super(encoder);
this.signingKey = signingKey;
}
@@ -22,16 +22,19 @@ public String buildSignedTokenFromJsonObject(T jsonEncodableObject, String webDo
if (!Eip712Common.isDomainValid(webDomain)) {
throw new IllegalArgumentException("Invalid domain");
}
- // Construct a more compact version of the JSON that is more suited for human reading than the full data
- String jsonToSign = getEncodedObject(jsonEncodableObject.getSignableVersion(), webDomain);
- // Sign this compacted version
- EthereumTypedMessage ethereumMessage = new EthereumTypedMessage(jsonToSign, null, 0,
- cryptoFunctions);
- String signatureInHex = signEIP712Message(ethereumMessage);
- // Include the full version of the JSON in the external data
- Eip712ExternalData data = new Eip712ExternalData(signatureInHex,
- getEncodedObject(jsonEncodableObject, webDomain));
- return mapper.writeValueAsString(data);
+ byte[] processedMessage = buildTokenToBeSigned(jsonEncodableObject, webDomain);
+ // Include the full version of the JSON in the external data
+ Eip712ExternalData data = new Eip712ExternalData(signEIP712Message(processedMessage),
+ getEncodedObject(jsonEncodableObject, webDomain));
+ return mapper.writeValueAsString(data);
+ }
+
+ public byte[] buildTokenToBeSigned(T jsonEncodableObject, String webDomain) throws JsonProcessingException {
+ // Construct a more compact version of the JSON that is more suited for human reading than the full data
+ String jsonToSign = getEncodedObject(jsonEncodableObject.getSignableVersion(), webDomain);
+ EthereumTypedMessage ethereumMessage = new EthereumTypedMessage(jsonToSign, null, 0,
+ cryptoFunctions);
+ return ethereumMessage.getPrehash();
}
String getEncodedObject(Eip712InternalData jsonEncodableObject, String webDomain) throws JsonProcessingException {
@@ -42,8 +45,8 @@ String getEncodedObject(Eip712InternalData jsonEncodableObject, String webDomain
return mapper.writeValueAsString(message);
}
- private String signEIP712Message(EthereumTypedMessage msg) {
- byte[] signature = SignatureUtility.signWithEthereum(msg.getPrehash(), signingKey);
- return "0x" + new String(Hex.encode(signature), StandardCharsets.UTF_8);
+ private String signEIP712Message(byte[] msg) {
+ byte[] rawSignature = SignatureUtility.signWithEthereum(msg, signingKey);
+ return "0x" + new String(Hex.encode(rawSignature), StandardCharsets.UTF_8);
}
}
diff --git a/src/main/java/org/tokenscript/eip712/Eip712Validator.java b/src/main/java/org/tokenscript/eip712/Eip712Validator.java
index 9065fcc3..67886ab0 100644
--- a/src/main/java/org/tokenscript/eip712/Eip712Validator.java
+++ b/src/main/java/org/tokenscript/eip712/Eip712Validator.java
@@ -1,7 +1,5 @@
package org.tokenscript.eip712;
-import org.tokenscript.attestation.core.ExceptionUtil;
-import org.tokenscript.attestation.core.SignatureUtility;
import com.alphawallet.token.entity.EthereumTypedMessage;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData.EIP712Domain;
@@ -9,7 +7,6 @@
import com.alphawallet.token.web.Ethereum.web3j.StructuredData.Entry;
import com.alphawallet.token.web.Ethereum.web3j.StructuredDataEncoder;
import com.fasterxml.jackson.databind.JsonNode;
-import java.io.InvalidObjectException;
import java.util.HashMap;
import java.util.List;
import java.util.Objects;
@@ -18,6 +15,8 @@
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.bouncycastle.crypto.params.ECPublicKeyParameters;
import org.bouncycastle.util.encoders.Hex;
+import org.tokenscript.attestation.core.ExceptionUtil;
+import org.tokenscript.attestation.core.SignatureUtility;
public class Eip712Validator extends Eip712Common {
private static final Logger logger = LogManager.getLogger(Eip712Validator.class);
@@ -35,7 +34,7 @@ public Eip712Validator(String domain, Eip712Encoder encoder) {
/**
* Retrieve the underlying JSON object
*/
- public T retrieveUnderlyingObject(String signedJsonInput, Class type) {
+ public T retrieveUnderlyingJson(String signedJsonInput, Class type) {
try {
Eip712ExternalData allData = mapper.readValue(signedJsonInput, Eip712ExternalData.class);
// Use StructuredDataEncoder to ensure that the data structure gets verified
@@ -46,28 +45,35 @@ public T retrieveUnderlyingObject(String signedJs
}
}
- private boolean validateDomain(EIP712Domain domainToCheck) {
- if (!domainToCheck.getName().equals(domain)) {
- logger.error("Domain name is not valid");
- return false;
- }
- if (!domainToCheck.getVersion().equals(encoder.getProtocolVersion())) {
- logger.error("Protocol version is wrong");
- return false;
- }
- if (!Objects.equals(domainToCheck.getChainId(), encoder.getChainId())) {
- logger.error("Chain ID is wrong");
- return false;
- }
- if (!Objects.equals(domainToCheck.getVerifyingContract(), encoder.getVerifyingContract())) {
- logger.error("Verifying contract is wrong");
- return false;
+ public boolean validateDomain(String signedJsonInput) {
+ try {
+ EIP712Domain domainToCheck = restoreDomain(signedJsonInput);
+ if (!domainToCheck.getName().equals(domain)) {
+ logger.error("Domain name is not valid");
+ return false;
+ }
+ if (!domainToCheck.getVersion().equals(encoder.getProtocolVersion())) {
+ logger.error("Protocol version is wrong");
+ return false;
+ }
+ if (!Objects.equals(domainToCheck.getChainId(), encoder.getChainId())) {
+ logger.error("Chain ID is wrong");
+ return false;
+ }
+ if (!Objects.equals(domainToCheck.getVerifyingContract(), encoder.getVerifyingContract())) {
+ logger.error("Verifying contract is wrong");
+ return false;
+ }
+ if (!Objects.equals(domainToCheck.getSalt(), encoder.getSalt())) {
+ logger.error("Salt is wrong");
+ return false;
+ }
+ return true;
}
- if (!Objects.equals(domainToCheck.getSalt(), encoder.getSalt())) {
- logger.error("Salt is wrong");
- return false;
+ catch (Exception e) {
+ logger.error("Could not restore domain from json");
+ throw ExceptionUtil.makeRuntimeException(logger, "Could not restore domain from json", e);
}
- return true;
}
public boolean verifySignature(String signedJsonInput, String pkAddress, Class type) {
@@ -77,42 +83,42 @@ public boolean verifySignature(String signedJ
logger.error("Could not verify signature");
return false;
}
- } catch (InvalidObjectException e) {
- logger.error("Could not decode signature");
+ } catch (IllegalArgumentException e) {
+ logger.error("Could not recover the user key");
return false;
}
return true;
}
- public ECPublicKeyParameters retrieveUserPublicKey(String signedJsonInput, Class type) throws InvalidObjectException {
+ public ECPublicKeyParameters retrieveUserPublicKey(String signedJsonInput, Class type) {
try {
- byte[] signature = getSignatureFromJson(signedJsonInput);
+ // substring(2) is needed to remove the "0x" prefix
+ byte[] rawSignature = Hex.decode(getSignatureFromJson(signedJsonInput).substring(2));
String actuallySignedJson = restoreSignableJson(signedJsonInput, type);
EthereumTypedMessage ethereumMessage = new EthereumTypedMessage(actuallySignedJson,null, 0, cryptoFunctions);
byte[] messageSigned = ethereumMessage.getPrehash();
- return SignatureUtility.recoverEthPublicKeyFromSignature(messageSigned, signature);
+ return SignatureUtility.recoverEthPublicKeyFromSignature(messageSigned, rawSignature);
} catch (Exception e) {
- throw ExceptionUtil.throwException(logger, new InvalidObjectException("Could not recover a valid key"));
+ throw ExceptionUtil.throwException(logger, new IllegalArgumentException("Could not recover a valid key"));
}
}
- byte[] getSignatureFromJson(String signedJsonInput) throws Exception {
- Eip712ExternalData data = mapper.readValue(signedJsonInput, Eip712ExternalData.class);
- // Remove the "0x" prefix
- String prunedSignature = data.getSignatureInHex().substring(2);
- return Hex.decode(prunedSignature);
- }
-
- String restoreSignableJson(String signedJsonInput, Class type) throws Exception {
- T fullInternalData = retrieveUnderlyingObject(signedJsonInput, type);
+ public String restoreSignableJson(String signedJsonInput, Class type) throws Exception {
+ T fullInternalData = retrieveUnderlyingJson(signedJsonInput, type);
Eip712ExternalData data = mapper.readValue(signedJsonInput, Eip712ExternalData.class);
+ EIP712Domain eip712Domain = restoreDomain(signedJsonInput);
JsonNode rootNode = mapper.readTree(data.getJsonSigned());
- EIP712Domain eip712Domain = getDomainFromJson(rootNode);
StructuredData.EIP712Message message = new EIP712Message(getTypes(rootNode), getPrimaryType(rootNode),
fullInternalData.getSignableVersion(), eip712Domain);
return mapper.writeValueAsString(message);
}
+ private EIP712Domain restoreDomain(String signedJsonInput) throws Exception {
+ Eip712ExternalData data = mapper.readValue(signedJsonInput, Eip712ExternalData.class);
+ JsonNode rootNode = mapper.readTree(data.getJsonSigned());
+ return mapper.readValue(rootNode.get("domain").toString(), EIP712Domain.class);
+ }
+
HashMap> getTypes(JsonNode rootOfEip712) throws Exception {
return mapper.readValue(rootOfEip712.get("types").toString(), HashMap.class);
}
@@ -120,15 +126,4 @@ HashMap> getTypes(JsonNode rootOfEip712) throws Exception {
String getPrimaryType(JsonNode rootOfEip712) {
return rootOfEip712.get("primaryType").asText();
}
-
- /**
- * Retrieve and validate the domain
- */
- EIP712Domain getDomainFromJson(JsonNode rootOfEip712) throws Exception {
- EIP712Domain eip712Domain = mapper.readValue(rootOfEip712.get("domain").toString(), EIP712Domain.class);
- if (!validateDomain(eip712Domain)) {
- throw ExceptionUtil.throwException(logger, new InvalidObjectException("Could not verify message"));
- }
- return eip712Domain;
- }
}
diff --git a/src/test/java/io/alchemynft/attestation/NFTAttestationTest.java b/src/test/java/io/alchemynft/attestation/NFTAttestationTest.java
index c21c58ba..db8d53a5 100644
--- a/src/test/java/io/alchemynft/attestation/NFTAttestationTest.java
+++ b/src/test/java/io/alchemynft/attestation/NFTAttestationTest.java
@@ -6,7 +6,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.io.IOException;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
@@ -22,16 +21,18 @@
import org.tokenscript.attestation.HelperTest;
import org.tokenscript.attestation.IdentifierAttestation;
import org.tokenscript.attestation.SignedIdentifierAttestation;
+import org.tokenscript.attestation.core.PersonalSignature;
+import org.tokenscript.attestation.core.RawSignature;
+import org.tokenscript.attestation.core.Signature;
import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.core.URLUtility;
public class NFTAttestationTest {
private static AsymmetricCipherKeyPair subjectKeys;
- private static AsymmetricCipherKeyPair issuerKeys;
private static AsymmetricCipherKeyPair attestorKeys;
private static SecureRandom rand;
- static SignedIdentifierAttestation signedIdentifierAtt;
- private static SignedNFTAttestation signedNftAttestation;
+ private static SignedIdentifierAttestation signedIdentifierAtt;
+ private static NFTAttestation nftAtt;
+ private static IdentifierAttestation att;
private static ERC721Token[] nfts;
@Mock
@@ -50,41 +51,52 @@ public static void setupKeys() throws Exception {
rand.setSeed("seed".getBytes());
subjectKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
attestorKeys = SignatureUtility.constructECKeys(rand);
- issuerKeys = SignatureUtility.constructECKeys(rand);
- IdentifierAttestation att = new IdentifierAttestation("205521676", "https://twitter.com/zhangweiwu", subjectKeys.getPublic());
+ att = new IdentifierAttestation("205521676", "https://twitter.com/zhangweiwu", subjectKeys.getPublic());
assertTrue(att.checkValidity());
signedIdentifierAtt = new SignedIdentifierAttestation(att, attestorKeys);
nfts = new ERC721Token[] {
new ERC721Token("0xa567f5A165545Fa2639bBdA79991F105EADF8522", "25"),
new ERC721Token("0xa567f5A165545Fa2639bBdA79991F105EADF8522", "26")
};
+ nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
System.out.println("SubjectPublicKey's Fingerprint (summarised as Ethereum address):\n" + SignatureUtility.addressFromKey(subjectKeys.getPublic()));
}
@Test
- public void sunshine() {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- //construct SignedNFTAttestation using subject key
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- assertTrue(signedNftAttestation.verify());
- assertTrue(signedNftAttestation.checkValidity());
+ public void sunshineV1() {
+ sunshine(new SignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 1));
+ }
+
+ @Test
+ public void sunshineV2() {
+ sunshine(new SignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 2));
+ }
+
+ @Test
+ public void sunshineEip() {
+ sunshine(new SignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 3));
+ }
+
+ public void sunshine(InternalSignedNFTAttestation signedNFTAttestation) {
+ assertTrue(signedNFTAttestation.verify());
+ assertTrue(signedNFTAttestation.checkValidity());
+ assertEquals(SignatureUtility.addressFromKey(subjectKeys.getPublic()), SignatureUtility.addressFromKey(signedNFTAttestation.getNFTAttestationVerificationKey()));
+ assertArrayEquals(nftAtt.getDerEncoding(), signedNFTAttestation.getUnsignedAttestation().getDerEncoding());
}
@Test
- public void testNFTAttestation() throws Exception
+ public void testNFTAttestationV1() throws Exception
{
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- //construct SignedNFTAttestation using subject key
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
+ LegacySignedNFTAttestation signedNFTAttestation = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 1);
Path p = Files.createTempFile("unsigned_nftAttestation", ".der");
System.out.println("To check the unsigned NFT attestation, run this:");
System.out.println("$ openssl asn1parse -inform DER -in " + p.toString());
- Files.write(p, signedNftAttestation.getDerEncoding());
+ Files.write(p, signedNFTAttestation.getDerEncoding());
//Extract the Ethereum signature
- Signature sig = signedNftAttestation.getSignature();
+ Signature sig = signedNFTAttestation.getSignature();
//generate NFTAttestation from the NFTAttestation bytes
NFTAttestation nftAttestation2 = new NFTAttestation(nftAtt.getDerEncoding(),
@@ -94,151 +106,192 @@ public void testNFTAttestation() throws Exception
assertTrue(nftAttestation2.verify());
//Generate SignedNFTAttestation using the reconstructed NFTAttestation and the extracted Ethereum signature
- SignedNFTAttestation signedNFTAttestation2 = new SignedNFTAttestation(nftAttestation2, sig);
+ LegacySignedNFTAttestation signedNFTAttestation2 = new LegacySignedNFTAttestation(nftAttestation2, sig);
assertTrue(signedNFTAttestation2.checkValidity());
- assertTrue(signedNftAttestation.checkValidity());
+ assertTrue(signedNFTAttestation.checkValidity());
assertArrayEquals(signedNFTAttestation2.getUnsignedAttestation().getDerEncoding(), nftAtt.getDerEncoding());
- assertArrayEquals(signedNFTAttestation2.getDerEncoding(), signedNftAttestation.getDerEncoding());
+ assertArrayEquals(signedNFTAttestation2.getDerEncoding(), signedNFTAttestation.getDerEncoding());
}
@Test
- public void consistentEncoding() throws IOException {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- SignedNFTAttestation decodedNFTAtt = new SignedNFTAttestation(signedNftAttestation.getDerEncoding(), attestorKeys.getPublic());
+ public void testEipNFTAttestation() throws Exception {
+ InternalSignedNFTAttestation signedNFTAttestation = new Eip712SignedNFTAttestation(nftAtt, subjectKeys.getPrivate());
+ Path p = Files.createTempFile("unsigned_nftAttestation", ".der");
+
+ System.out.println("To check the unsigned NFT attestation, run this:");
+ System.out.println("$ openssl asn1parse -inform DER -in " + p.toString());
+ Files.write(p, signedNFTAttestation.getDerEncoding());
+
+ //generate NFTAttestation from the NFTAttestation bytes
+ NFTAttestation nftAttestation2 = new NFTAttestation(nftAtt.getDerEncoding(),
+ attestorKeys.getPublic());
+
+ //check recovered signed attestation within the wrapping
+ assertTrue(nftAttestation2.verify());
+ }
+
+ @Test
+ public void versionDiscovery() throws Exception {
+ LegacySignedNFTAttestation att = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 2);
+ SignedNFTAttestation otherAtt = new SignedNFTAttestation(att.getUnsignedAttestation(), att.getSignature());
+ assertTrue(otherAtt.checkValidity());
+ assertTrue(otherAtt.verify());
+ assertArrayEquals(att.getDerEncoding(), otherAtt.getDerEncoding());
+ }
+
+ @Test
+ public void consistentEncodingV1() throws Exception {
+ consistentEncodingLegacy(new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 1));
+ }
+
+ @Test
+ public void consistentEncodingV2() throws Exception {
+ consistentEncodingLegacy(new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 2));
+ }
+
+ private void consistentEncodingLegacy(LegacySignedNFTAttestation signedNFTAttestation) throws Exception {
+ LegacySignedNFTAttestation decodedNFTAtt = new LegacySignedNFTAttestation(signedNFTAttestation.getDerEncoding(), attestorKeys.getPublic());
assertTrue(decodedNFTAtt.verify());
assertTrue(decodedNFTAtt.checkValidity());
- assertArrayEquals(signedNftAttestation.getDerEncoding(), decodedNFTAtt.getDerEncoding());
+ assertArrayEquals(signedNFTAttestation.getSignature().getRawSignature(), decodedNFTAtt.getSignature().getRawSignature());
+ assertEquals(SignatureUtility.addressFromKey(signedNFTAttestation.getNFTAttestationVerificationKey()),
+ SignatureUtility.addressFromKey(signedNFTAttestation.getNFTAttestationVerificationKey()));
+ assertArrayEquals(signedNFTAttestation.getDerEncoding(), decodedNFTAtt.getDerEncoding());
}
@Test
- public void testGetters() throws IOException {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- //construct SignedNFTAttestation using subject key
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- assertEquals(SignatureUtility.addressFromKey(signedNftAttestation.getAttestationVerificationKey()),
- SignatureUtility.addressFromKey(subjectKeys.getPublic()));
- assertArrayEquals(nftAtt.getTokens(), nfts);
+ public void consistentEncodingEip() throws Exception {
+ Eip712SignedNFTAttestation signedNFTAttestation = new Eip712SignedNFTAttestation(nftAtt, subjectKeys.getPrivate());
+ Eip712SignedNFTAttestation decodedNFTAtt = new Eip712SignedNFTAttestation(signedNFTAttestation.getSignedEIP712(), attestorKeys.getPublic());
+ assertTrue(decodedNFTAtt.verify());
+ assertTrue(decodedNFTAtt.checkValidity());
+ assertEquals(signedNFTAttestation.getSignature(), decodedNFTAtt.getSignature());
+ assertEquals(SignatureUtility.addressFromKey(signedNFTAttestation.getNFTAttestationVerificationKey()),
+ SignatureUtility.addressFromKey(signedNFTAttestation.getNFTAttestationVerificationKey()));
+ assertEquals(signedNFTAttestation.getSignedEIP712(), decodedNFTAtt.getSignedEIP712());
+ assertArrayEquals(signedNFTAttestation.getUnsignedAttestation().getDerEncoding(),
+ decodedNFTAtt.getDerEncoding());
}
@Test
- public void testPublicAttestation() {
- assertTrue(signedIdentifierAtt.checkValidity());
- assertTrue(signedIdentifierAtt.verify());
- assertTrue(SignatureUtility.verifyEthereumSignature(
- signedIdentifierAtt.getUnsignedAttestation().getPrehash(), signedIdentifierAtt.getSignature(), attestorKeys.getPublic()));
+ public void APIWrapperV1() throws Exception {
+ LegacySignedNFTAttestation refAtt = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 1);
+ sunshine(new SignedNFTAttestation(refAtt.getDerEncoding(), attestorKeys.getPublic()));
+ sunshine(new SignedNFTAttestation(refAtt.getUnsignedAttestation(), refAtt.getSignature()));
+ }
+
+ @Test
+ public void APIWrapperV2() throws Exception {
+ LegacySignedNFTAttestation refAtt = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 2);
+ sunshine(new SignedNFTAttestation(refAtt.getDerEncoding(), attestorKeys.getPublic()));
+ sunshine(new SignedNFTAttestation(refAtt.getUnsignedAttestation(), refAtt.getSignature()));
+ }
+
+ @Test
+ public void APIWrapperV3() throws Exception {
+ Eip712SignedNFTAttestation refAtt = new Eip712SignedNFTAttestation(nftAtt, subjectKeys.getPrivate());
+ sunshine(new SignedNFTAttestation(refAtt.getSignedEIP712().getBytes(StandardCharsets.UTF_8), attestorKeys.getPublic()));
}
@Test
public void testDecoding() throws Exception {
IdentifierAttestation att = HelperTest.makeMaximalAtt(subjectKeys.getPublic());
- SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, issuerKeys);
- assertTrue(SignatureUtility.verifyEthereumSignature(att.getPrehash(), signed.getSignature(), issuerKeys.getPublic()));
+ SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
+ assertTrue(SignatureUtility.verifyEthereumSignature(att.getPrehash(), signed.getSignature(), attestorKeys.getPublic()));
assertArrayEquals(att.getPrehash(), signed.getUnsignedAttestation().getPrehash());
byte[] signedEncoded = signed.getDerEncoding();
- SignedIdentifierAttestation newSigned = new SignedIdentifierAttestation(signedEncoded, issuerKeys.getPublic());
+ SignedIdentifierAttestation newSigned = new SignedIdentifierAttestation(signedEncoded, attestorKeys.getPublic());
assertArrayEquals(signed.getDerEncoding(), newSigned.getDerEncoding());
}
@Test
- public void defaultSigningVersion() throws IOException {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- SignedNFTAttestation newSignedNftAtt = new SignedNFTAttestation(signedNftAttestation.getUnsignedAttestation(), signedNftAttestation.getSignature());
- assertArrayEquals(signedNftAttestation.getDerEncoding(), newSignedNftAtt.getDerEncoding());
- assertTrue(newSignedNftAtt.verify());
- assertTrue(newSignedNftAtt.checkValidity());
- SignedNFTAttestation otherConstructor = new SignedNFTAttestation(newSignedNftAtt.getDerEncoding(), attestorKeys.getPublic());
- assertArrayEquals(signedNftAttestation.getDerEncoding(), otherConstructor.getDerEncoding());
- assertTrue(otherConstructor.verify());
- assertTrue(otherConstructor.checkValidity());
- }
-
- @Test
- public void oldVersionSigning() throws IOException {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys, 1);
- SignedNFTAttestation newSignedNftAtt = new SignedNFTAttestation(signedNftAttestation.getUnsignedAttestation(), signedNftAttestation.getSignature());
- assertArrayEquals(signedNftAttestation.getDerEncoding(), newSignedNftAtt.getDerEncoding());
- assertTrue(newSignedNftAtt.verify());
- assertTrue(newSignedNftAtt.checkValidity());
- SignedNFTAttestation otherConstructor = new SignedNFTAttestation(newSignedNftAtt.getDerEncoding(), attestorKeys.getPublic());
- assertArrayEquals(signedNftAttestation.getDerEncoding(), otherConstructor.getDerEncoding());
- assertTrue(otherConstructor.verify());
- assertTrue(otherConstructor.checkValidity());
+ public void legacyDoesNotSupportV3() {
+ assertThrows(IllegalArgumentException.class, ()-> new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 3));
}
@Test
- public void unknownVersion() {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, subjectKeys, 42));
+ public void wrongSignatureType() {
+ assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, new RawSignature(new byte[] {0x00})));
}
@Test
- public void unknownVersionOtherConstructor() {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- Signature rawSig = new RawSignature(subjectKeys, nftAtt.getDerEncoding());
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, rawSig));
+ public void wrongSignatureVersion() {
+ assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, subjectKeys.getPrivate(), 4));
}
@Test
- public void badSignatureVersion() {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- assertThrows(IllegalArgumentException.class, ()-> signedNftAttestation.makeSignature(new byte[] {0x42}, 42));
+ public void testInvalidEncoding() {
+ Mockito.when(mockedNftAttestation.getDerEncoding()).thenReturn(new byte[] {0x42});
+ Mockito.when(mockedNftAttestation.getSignedIdentifierAttestation()).thenReturn(signedIdentifierAtt);
+ Exception e = assertThrows(IllegalArgumentException.class, ()-> new Eip712SignedNFTAttestation(mockedNftAttestation,
+ subjectKeys.getPrivate()));
+ assertEquals("Could not decode underlying NFTAttestation", e.getMessage());
}
@Test
- public void badSignatureVersionOtherVersion() {
- NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- assertThrows(IllegalArgumentException.class, ()-> signedNftAttestation.makeSignature(subjectKeys, 42));
+ public void testInvalidEipKey() {
+ Eip712SignedNFTAttestation refAtt = new Eip712SignedNFTAttestation(nftAtt, subjectKeys.getPrivate());
+ Exception e = assertThrows(IllegalArgumentException.class, ()->
+ new SignedNFTAttestation(refAtt.getSignedEIP712().getBytes(StandardCharsets.UTF_8), SignatureUtility.constructECKeys(rand).getPublic()));
+ assertEquals("Could not decode SignedNFTAttestation", e.getMessage());
}
@Test
- public void signingVersion1Included() throws IOException {
- String urlEncodedSignedNftAtt = "MIICqTCCAlMwggIXMIIBxKADAgETAgEBMAkGByqGSM49BAIwGTEXMBUGA1UEAwwOYXR0ZXN0YXRpb24uaWQwIhgPMjAyMTExMDkxNjIwMThaGA85OTk5MTIzMTIyNTk1OVowOTE3MDUGCSsGAQQBgXoBOQwoaHR0cHM6Ly90d2l0dGVyLmNvbS96aGFuZ3dlaXd1IDIwNTUyMTY3NjCCATMwgewGByqGSM49AgEwgeACAQEwLAYHKoZIzj0BAQIhAP____________________________________7___wvMEQEIAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABCAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAABwRBBHm-Zn753LusVaBilc6HCwcCm_zbLc4o2VnygVsW-BeYSDradyajxGVdpPv8DhEIqP0XtEimhVQZnEfQj_sQ1LgCIQD____________________-uq7c5q9IoDu_0l6M0DZBQQIBAQNCAASVDHwL7SPDysXMMbu5qtm7VTI4eIJnCsKxzfB5mrDrx2TCZ_cE6P3aB5arg5ek0hAQJNJMTv_2lbOkF_LtDkjNMAkGByqGSM49BAIDQgD8Wu2eGeRW1GNFxOk5Srdn4E968ML7MUINj55zBqhuOhUWmosV5d4VsarkmpCmlwAXxvIpt7UcFP4cK8QuwH89GzA2MBkEFKVn9aFlVF-iY5u9p5mR8QXq34UiBAEZMBkEFKVn9aFlVF-iY5u9p5mR8QXq34UiBAEaAgEBMAkGByqGSM49BAIDQgCrpY0RQ3LNfJd6YgYEC-etEU_oJKUAA6WP0TRfZITeQVNNm21BOFQc-iiXs053UcSy1y29tbUPt1wp4VRU8Qu4Gw==";
- signedNftAttestation = new SignedNFTAttestation(URLUtility.decodeData(urlEncodedSignedNftAtt), attestorKeys.getPublic());
- SignedNFTAttestation newSignedNftAtt = new SignedNFTAttestation(signedNftAttestation.getUnsignedAttestation(), signedNftAttestation.getSignature());
- assertTrue(newSignedNftAtt.verify());
- assertTrue(newSignedNftAtt.checkValidity());
+ public void badSignatureV1() {
+ Signature wrongSignature = new PersonalSignature(subjectKeys.getPrivate(), "something wrong".getBytes(
+ StandardCharsets.UTF_8));
+ assertThrows(IllegalArgumentException.class, ()-> new LegacySignedNFTAttestation(nftAtt, wrongSignature));
}
@Test
- public void badSignature() {
+ public void badSigningKeyV1() {
+ AsymmetricCipherKeyPair notAttestedKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- Signature wrongSignature = new PersonalSignature(subjectKeys, "something wrong".getBytes(
- StandardCharsets.UTF_8));
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, wrongSignature));
+ assertThrows(IllegalArgumentException.class, ()-> new LegacySignedNFTAttestation(nftAtt, notAttestedKeys.getPrivate()));
}
-
@Test
- public void badSigningKey() {
+ public void badSigningKeyV2() {
AsymmetricCipherKeyPair notAttestedKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, notAttestedKeys));
+ assertThrows(IllegalArgumentException.class, ()-> new Eip712SignedNFTAttestation(nftAtt, notAttestedKeys.getPrivate()));
}
@Test
- public void badNftAttestation() {
+ public void badNftAttestationV1() {
Mockito.when(mockedNftAttestation.verify()).thenReturn(false);
Mockito.when(mockedNftAttestation.getDerEncoding()).thenReturn(new byte[] {0x42});
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(mockedNftAttestation, subjectKeys));
+ assertThrows(RuntimeException.class, ()-> new LegacySignedNFTAttestation(mockedNftAttestation, subjectKeys.getPrivate()));
+ }
+ @Test
+ public void badNftAttestationV2() {
+ Mockito.when(mockedNftAttestation.verify()).thenReturn(false);
+ Mockito.when(mockedNftAttestation.getDerEncoding()).thenReturn(new byte[] {0x42});
+ assertThrows(RuntimeException.class, ()-> new Eip712SignedNFTAttestation(mockedNftAttestation, subjectKeys.getPrivate()));
}
@Test
- public void unverifiableSignedIdentifierAtt() {
- NFTAttestation realNftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
+ public void unverifiableSignedIdentifierAttV1() {
Mockito.when(mockedSignedIdentifierAtt.verify()).thenReturn(false);
- Mockito.when(mockedSignedIdentifierAtt.getDerEncoding()).thenReturn(realNftAtt.getDerEncoding());
- NFTAttestation nftAtt = new NFTAttestation(mockedSignedIdentifierAtt, nfts);
- assertThrows(IllegalArgumentException.class, ()-> new SignedNFTAttestation(nftAtt, subjectKeys));
+ Mockito.when(mockedSignedIdentifierAtt.getDerEncoding()).thenReturn(nftAtt.getDerEncoding());
+ Mockito.when(mockedSignedIdentifierAtt.getUnsignedAttestation()).thenReturn(
+ att);
+ NFTAttestation mockedNftAtt = new NFTAttestation(mockedSignedIdentifierAtt, nfts);
+ assertThrows(IllegalArgumentException.class, ()-> new LegacySignedNFTAttestation(mockedNftAtt, subjectKeys.getPrivate()));
+ }
+
+ @Test
+ public void unverifiableSignedIdentifierAttV2() {
+ Mockito.when(mockedSignedIdentifierAtt.verify()).thenReturn(false);
+ Mockito.when(mockedSignedIdentifierAtt.getDerEncoding()).thenReturn(nftAtt.getDerEncoding());
+ Mockito.when(mockedSignedIdentifierAtt.getUnsignedAttestation()).thenReturn(
+ att);
+ NFTAttestation mockedNftAtt = new NFTAttestation(mockedSignedIdentifierAtt, nfts);
+ assertThrows(IllegalArgumentException.class, ()-> new LegacySignedNFTAttestation(mockedNftAtt, subjectKeys.getPrivate()));
}
@Test
- public void invalidSignedIdentifierAtt() throws Exception {
+ public void invalidSignedIdentifierAttV1() throws Exception {
NFTAttestation realNftAtt = new NFTAttestation(signedIdentifierAtt, nfts);
IdentifierAttestation identifierAttestation = new IdentifierAttestation("205521676", "https://twitter.com/zhangweiwu", subjectKeys.getPublic());
Mockito.when(mockedSignedIdentifierAtt.verify()).thenReturn(true);
@@ -246,8 +299,18 @@ public void invalidSignedIdentifierAtt() throws Exception {
Mockito.when(mockedSignedIdentifierAtt.getDerEncoding()).thenReturn(realNftAtt.getDerEncoding());
Mockito.when(mockedSignedIdentifierAtt.getUnsignedAttestation()).thenReturn(identifierAttestation);
NFTAttestation nftAtt = new NFTAttestation(mockedSignedIdentifierAtt, nfts);
- signedNftAttestation = new SignedNFTAttestation(nftAtt, subjectKeys);
- assertTrue(signedNftAttestation.verify());
- assertFalse(signedNftAttestation.checkValidity());
+ InternalSignedNFTAttestation newSignedNftAttestation = new LegacySignedNFTAttestation(nftAtt, subjectKeys.getPrivate());
+ assertTrue(newSignedNftAttestation.verify());
+ assertFalse(newSignedNftAttestation.checkValidity());
+ }
+
+ @Test
+ public void invalidSignedIdentifierAttV2() throws Exception {
+ AsymmetricCipherKeyPair otherKeys = SignatureUtility.constructECKeys(rand);
+ IdentifierAttestation identifierAttestation = new IdentifierAttestation("205521676", "https://twitter.com/zhangweiwu", otherKeys.getPublic());
+ SignedIdentifierAttestation signedIdentifierAttestation = new SignedIdentifierAttestation(identifierAttestation, attestorKeys);
+ NFTAttestation nftAtt = new NFTAttestation(signedIdentifierAttestation, nfts);
+ Exception e = assertThrows(IllegalArgumentException.class, ()-> new Eip712SignedNFTAttestation(nftAtt, subjectKeys.getPrivate()));
+ assertEquals("The NFTAttestation is invalid", e.getMessage());
}
}
diff --git a/src/test/java/io/alchemynft/attestation/SignatureTest.java b/src/test/java/io/alchemynft/attestation/SignatureTest.java
deleted file mode 100644
index 728da85d..00000000
--- a/src/test/java/io/alchemynft/attestation/SignatureTest.java
+++ /dev/null
@@ -1,125 +0,0 @@
-package io.alchemynft.attestation;
-
-import static org.junit.jupiter.api.Assertions.assertArrayEquals;
-import static org.junit.jupiter.api.Assertions.assertEquals;
-import static org.junit.jupiter.api.Assertions.assertFalse;
-import static org.junit.jupiter.api.Assertions.assertNotNull;
-import static org.junit.jupiter.api.Assertions.assertTrue;
-
-import java.nio.charset.StandardCharsets;
-import java.security.SecureRandom;
-import java.util.Arrays;
-import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
-import org.junit.jupiter.api.BeforeAll;
-import org.junit.jupiter.api.Test;
-import org.tokenscript.attestation.core.SignatureUtility;
-
-public class SignatureTest {
-
- private static AsymmetricCipherKeyPair subjectKeys;
- private static SecureRandom rand;
- private static final byte[] MSG = "some message".getBytes(StandardCharsets.UTF_8);
-
- @BeforeAll
- public static void setupKeys() throws Exception {
- rand = SecureRandom.getInstance("SHA1PRNG", "SUN");
- rand.setSeed("seed".getBytes());
- subjectKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
- }
-
- @Test
- public void personal() {
- AbstractSignature sig = new PersonalSignature(subjectKeys, MSG);
- sunshine(sig);
- ensureProcessing(sig);
- wrongMessage(sig);
- wrongKeys(sig);
- }
-
- @Test
- public void raw() {
- AbstractSignature sig = new RawSignature(subjectKeys, MSG);
- sunshine(sig);
- wrongMessage(sig);
- wrongKeys(sig);
- }
-
- @Test
- public void compressed() {
- Signature sig = new CompressedMsgSignature(subjectKeys, MSG);
- sunshine(sig);
- wrongMessage(sig);
- wrongKeys(sig);
- }
-
-
- @Test
- public void expectedRaw() {
- AbstractSignature sig = new RawSignature(subjectKeys, MSG);
- assertArrayEquals(sig.getRawSignature(), SignatureUtility.signWithEthereum(MSG, subjectKeys.getPrivate()));
- assertEquals(sig.getTypeOfSignature(), "raw");
- Arrays.equals(sig.processMessage(MSG), MSG);
- }
-
- @Test
- public void expectedCompressedType() {
- Signature sig = new CompressedMsgSignature(subjectKeys, MSG, "prefix ", " postfix");
- assertEquals(sig.getTypeOfSignature(), "compressed");
- }
-
- @Test
- public void otherConstructorRaw() {
- Signature sig = new RawSignature(subjectKeys, MSG);
- Signature newSig = new RawSignature(sig.getRawSignature());
- sunshine(newSig);
- assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
- }
-
- @Test
- public void expectedPersonal() {
- Signature sig = new PersonalSignature(subjectKeys, MSG);
- assertArrayEquals(sig.getRawSignature(), SignatureUtility.signPersonalMsgWithEthereum(MSG, subjectKeys.getPrivate()));
- assertEquals(sig.getTypeOfSignature(), "personal");
- }
-
- @Test
- public void compressedReference() {
- CompressedMsgSignature sig = new CompressedMsgSignature(subjectKeys, MSG, "prefix ", " postfix");
- assertArrayEquals(sig.processMessage(MSG), "prefix 0x9DF8DBA3720D00BD48AD744722021EF91B035E273BCCFB78660CA8DF9574B086 postfix".getBytes(
- StandardCharsets.UTF_8));
- }
-
- @Test
- public void otherConstructorPersonal() {
- Signature sig = new PersonalSignature(subjectKeys, MSG);
- Signature newSig = new PersonalSignature(sig.getRawSignature());
- sunshine(newSig);
- assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
- }
-
- @Test
- public void otherConstructorCompressed() {
- Signature sig = new CompressedMsgSignature(subjectKeys, MSG);
- Signature newSig = new CompressedMsgSignature(sig.getRawSignature());
- sunshine(newSig);
- assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
- }
-
- public void sunshine(Signature sig) {
- assertNotNull(sig.getRawSignature());
- assertTrue(sig.getRawSignature().length > 5);
- assertTrue(sig.verify(MSG, subjectKeys.getPublic()));
- }
-
- public void ensureProcessing(AbstractSignature sig) {
- assertFalse(Arrays.equals(sig.processMessage(MSG), MSG));
- }
-
- public void wrongMessage(Signature sig) {
- assertFalse(sig.verify("some other message".getBytes(StandardCharsets.UTF_8), subjectKeys.getPublic()));
- }
-
- public void wrongKeys(Signature sig) {
- assertFalse(sig.verify(MSG, SignatureUtility.constructECKeysWithSmallestY(rand).getPublic()));
- }
-}
diff --git a/src/test/java/org/tokenscript/auth/UnpredictableNumberToolTest.java b/src/test/java/org/devcon/ticket/UnpredictableNumberToolTest.java
similarity index 95%
rename from src/test/java/org/tokenscript/auth/UnpredictableNumberToolTest.java
rename to src/test/java/org/devcon/ticket/UnpredictableNumberToolTest.java
index 5f972de6..4ce5c991 100644
--- a/src/test/java/org/tokenscript/auth/UnpredictableNumberToolTest.java
+++ b/src/test/java/org/devcon/ticket/UnpredictableNumberToolTest.java
@@ -1,4 +1,4 @@
-package org.tokenscript.auth;
+package org.devcon.ticket;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -12,8 +12,6 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.tokenscript.auth.UnpredictableNumberBundle;
-import org.tokenscript.auth.UnpredictableNumberTool;
public class UnpredictableNumberToolTest {
private static final String DOMAIN = "http://www.hotel-bogota.com";
diff --git a/src/test/java/org/devcon/ticket/UseTicketBundleTest.java b/src/test/java/org/devcon/ticket/UseTicketBundleTest.java
index 030afae0..2c6ea38d 100644
--- a/src/test/java/org/devcon/ticket/UseTicketBundleTest.java
+++ b/src/test/java/org/devcon/ticket/UseTicketBundleTest.java
@@ -24,8 +24,6 @@
import org.tokenscript.attestation.SignedIdentifierAttestation;
import org.tokenscript.attestation.core.AttestationCrypto;
import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.auth.UnpredictableNumberBundle;
-import org.tokenscript.auth.UnpredictableNumberTool;
public class UseTicketBundleTest {
private static final String DOMAIN = "http://www.hotel-bogota.com";
@@ -73,12 +71,12 @@ public void makeUseTicket() {
.makeUnsignedStandardAtt(subjectKeys.getPublic(), ATTESTATION_SECRET, MAIL );
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
- useTicket = new AttestedObject(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET,
+ useTicket = new AttestedObject(ticket, signed, ATTESTATION_SECRET,
TICKET_SECRET, un.getNumber().getBytes(StandardCharsets.UTF_8), crypto);
Mockito.when(mockedUseTicket.verify()).thenReturn(true);
Mockito.when(mockedUseTicket.getDerEncoding()).thenReturn(new byte[] {0x00});
- Mockito.when(mockedUseTicket.getUserPublicKey()).thenReturn(subjectKeys.getPublic());
+ Mockito.when(mockedUseTicket.getAttestedUserKey()).thenReturn(subjectKeys.getPublic());
Mockito.when(mockedUn.getDomain()).thenReturn(DOMAIN);
Mockito.when(mockedUn.getExpiration()).thenReturn(Long.MAX_VALUE);
@@ -151,7 +149,7 @@ public void unverifiableUseTicketConstructorFailure() throws Exception {
@Test
public void badChallengeSignatureConstructor() throws Exception {
// Return wrong key, it is supposed to be the subjectKey
- Mockito.when(mockedUseTicket.getUserPublicKey()).thenReturn(attestorKeys.getPublic());
+ Mockito.when(mockedUseTicket.getAttestedUserKey()).thenReturn(attestorKeys.getPublic());
assertThrows(IllegalArgumentException.class, ()-> new UseTicketBundle(mockedUseTicket, un, subjectKeys.getPrivate()));
}
diff --git a/src/test/java/org/devcon/ticket/UseTicketTest.java b/src/test/java/org/devcon/ticket/UseTicketTest.java
index 55a3af66..84af3f70 100644
--- a/src/test/java/org/devcon/ticket/UseTicketTest.java
+++ b/src/test/java/org/devcon/ticket/UseTicketTest.java
@@ -35,8 +35,6 @@
import org.tokenscript.attestation.core.DERUtility;
import org.tokenscript.attestation.core.SignatureUtility;
import org.tokenscript.attestation.demo.SmartContract;
-import org.tokenscript.auth.UnpredictableNumberBundle;
-import org.tokenscript.auth.UnpredictableNumberTool;
public class UseTicketTest {
private static final String MAIL = "test@test.ts";
@@ -75,7 +73,7 @@ public void makeAttestedTicket() {
IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(subjectKeys.getPublic(), ATTESTATION_SECRET, MAIL );
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
- attestedTicket = new AttestedObject(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
+ attestedTicket = new AttestedObject(ticket, signed, ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
assertTrue(attestedTicket.verify());
assertTrue(attestedTicket.checkValidity());
}
@@ -117,7 +115,8 @@ public void testWithUnpredictableNumberBundle() {
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
UnpredictableNumberTool unt = new UnpredictableNumberTool(rand, new byte[] { 0x01, 0x02}, "http://www.domain.com");
UnpredictableNumberBundle un = unt.getUnpredictableNumberBundle();
- AttestedObject attestedTicket = new AttestedObject(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, un.getNumber().getBytes(), crypto);
+ AttestedObject attestedTicket = new AttestedObject(ticket, signed,
+ ATTESTATION_SECRET, TICKET_SECRET, un.getNumber().getBytes(), crypto);
assertTrue(attestedTicket.verify());
assertTrue(attestedTicket.checkValidity());
// Validate the UN is correct
@@ -139,7 +138,8 @@ public void testDecoding() throws InvalidObjectException {
newAttestedTicket.getAttestableObject().getDerEncoding());
assertArrayEquals(attestedTicket.getAtt().getDerEncoding(), newAttestedTicket.getAtt().getDerEncoding());
assertArrayEquals(attestedTicket.getPok().getDerEncoding(), newAttestedTicket.getPok().getDerEncoding());
- assertEquals(attestedTicket.getUserPublicKey(), subjectKeys.getPublic());
+ assertEquals(SignatureUtility.addressFromKey(attestedTicket.getAttestedUserKey()),
+ SignatureUtility.addressFromKey(subjectKeys.getPublic()));
assertArrayEquals(attestedTicket.getDerEncoding(), newAttestedTicket.getDerEncoding());
AttestedObject newConstructor = new AttestedObject(attestedTicket.getAttestableObject(),
@@ -154,7 +154,7 @@ public void testSmartContractDecode() throws Exception {
IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(subjectKeys.getPublic(), ATTESTATION_SECRET, MAIL);
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
- AttestedObject useTicket = new AttestedObject<>(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
+ AttestedObject useTicket = new AttestedObject<>(ticket, signed, ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
//now attempt to dump data from contract:
TicketAttestationReturn tar = contract.callVerifyTicketAttestation(useTicket.getDerEncoding());
@@ -172,7 +172,7 @@ public void testRebuildComponents() {
IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(subjectKeys.getPublic(), ATTESTATION_SECRET, MAIL );
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
- AttestedObject useTicket = new AttestedObject<>(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
+ AttestedObject useTicket = new AttestedObject<>(ticket, signed, ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
AttestedObject newUseTicket = new AttestedObject(useTicket.getDerEncoding(), new TicketDecoder(
ticketIssuerKeys.getPublic()), attestorKeys.getPublic());
@@ -278,7 +278,7 @@ public void testNegativeConstruction() {
// Add an extra t in the mail
Ticket ticket = new Ticket("testt@test.ts", CONFERENCE_ID, TICKET_ID, TICKET_CLASS, subjectKeys, TICKET_SECRET);
try {
- AttestedObject current = new AttestedObject(ticket, signed, subjectKeys.getPublic(), ATTESTATION_SECRET,
+ AttestedObject current = new AttestedObject(ticket, signed, ATTESTATION_SECRET,
TICKET_SECRET, UN, crypto);
fail();
} catch (RuntimeException e) {
@@ -293,7 +293,7 @@ public void testNegativeConstruction2() {
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, subjectKeys, TICKET_SECRET);
try {
// Wrong subject secret
- AttestedObject current = new AttestedObject(ticket, signed, subjectKeys.getPublic(),
+ AttestedObject current = new AttestedObject(ticket, signed,
TICKET_SECRET.add(BigInteger.ONE), ATTESTATION_SECRET, UN, crypto);
fail();
} catch (RuntimeException e) {
@@ -301,7 +301,7 @@ public void testNegativeConstruction2() {
}
try {
// Wrong attestation secret
- AttestedObject current = new AttestedObject(ticket, signed, subjectKeys.getPublic(), TICKET_SECRET,
+ AttestedObject current = new AttestedObject(ticket, signed, TICKET_SECRET,
ATTESTATION_SECRET.add(BigInteger.ONE), UN, crypto);
fail();
} catch (RuntimeException e) {
@@ -309,22 +309,11 @@ public void testNegativeConstruction2() {
}
try {
// Correlated secrets
- AttestedObject current = new AttestedObject(ticket, signed, subjectKeys.getPublic(),
+ AttestedObject current = new AttestedObject(ticket, signed,
TICKET_SECRET.add(BigInteger.ONE), ATTESTATION_SECRET.add(BigInteger.ONE), UN, crypto);
fail();
} catch (RuntimeException e) {
// Expected not to be able to construct a proof for a wrong secret
}
}
-
- @Test
- public void testNonAttestedSigningKey() {
- IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(subjectKeys.getPublic(), ATTESTATION_SECRET, MAIL );
- SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
- Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketIssuerKeys, TICKET_SECRET);
- AsymmetricCipherKeyPair newKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
- attestedTicket = new AttestedObject(ticket, signed, newKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
- assertTrue(attestedTicket.verify());
- assertFalse(attestedTicket.checkValidity());
- }
}
diff --git a/src/test/java/org/tokenscript/attestation/cheque/TestRedeemCheque.java b/src/test/java/org/tokenscript/attestation/cheque/TestRedeemCheque.java
index 96a40c36..f9907ac4 100644
--- a/src/test/java/org/tokenscript/attestation/cheque/TestRedeemCheque.java
+++ b/src/test/java/org/tokenscript/attestation/cheque/TestRedeemCheque.java
@@ -107,7 +107,8 @@ public void testDecoding() throws InvalidObjectException {
attestedCheque.getAttestableObject().getDerEncoding(), newRedeem.getAttestableObject().getDerEncoding());
assertArrayEquals(attestedCheque.getAtt().getDerEncoding(), newRedeem.getAtt().getDerEncoding());
assertArrayEquals(attestedCheque.getPok().getDerEncoding(), newRedeem.getPok().getDerEncoding());
- assertEquals(attestedCheque.getUserPublicKey(), subjectKeys.getPublic());
+ assertEquals(SignatureUtility.addressFromKey(attestedCheque.getAttestedUserKey()),
+ SignatureUtility.addressFromKey(subjectKeys.getPublic()));
assertArrayEquals(attestedCheque.getDerEncoding(), newRedeem.getDerEncoding());
AttestedObject newConstructor = new AttestedObject(attestedCheque.getAttestableObject(), attestedCheque
diff --git a/src/test/java/org/tokenscript/attestation/core/SignatureTest.java b/src/test/java/org/tokenscript/attestation/core/SignatureTest.java
index b457a98b..7451e8b8 100644
--- a/src/test/java/org/tokenscript/attestation/core/SignatureTest.java
+++ b/src/test/java/org/tokenscript/attestation/core/SignatureTest.java
@@ -1,202 +1,124 @@
package org.tokenscript.attestation.core;
+import static org.junit.jupiter.api.Assertions.assertArrayEquals;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertNotNull;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
-import java.security.Security;
-import java.security.Signature;
-import java.security.interfaces.ECPrivateKey;
-import java.security.interfaces.ECPublicKey;
-import org.bouncycastle.asn1.sec.SECNamedCurves;
-import org.bouncycastle.asn1.x9.X9ECParameters;
+import java.util.Arrays;
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
-import org.bouncycastle.crypto.Digest;
-import org.bouncycastle.crypto.digests.KeccakDigest;
-import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
-import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
-import org.bouncycastle.crypto.signers.ECDSASigner;
-import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
-import org.bouncycastle.jce.provider.BouncyCastleProvider;
-import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
public class SignatureTest {
- private static final X9ECParameters SECP364R1 = SECNamedCurves.getByName("secp384r1");
- private AsymmetricCipherKeyPair largeKeys;
- private AsymmetricCipherKeyPair userKeys;
- private SecureRandom rand;
-
- @BeforeEach
- public void setupCrypto() throws Exception {
- Security.addProvider(new BouncyCastleProvider());
+
+ private static AsymmetricCipherKeyPair subjectKeys;
+ private static SecureRandom rand;
+ private static final byte[] MSG = "some message".getBytes(StandardCharsets.UTF_8);
+
+ @BeforeAll
+ public static void setupKeys() throws Exception {
rand = SecureRandom.getInstance("SHA1PRNG", "SUN");
rand.setSeed("seed".getBytes());
- largeKeys = SignatureUtility.constructECKeys(SECP364R1, rand);
- userKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
+ subjectKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
}
@Test
- public void testKeyConversion() throws Exception {
- byte[] message = "test".getBytes(StandardCharsets.UTF_8);
- byte[] digest = AttestationCrypto.hashWithSHA256(message);
- byte[] bcSignature = SignatureUtility.signHashedRandomized(digest, largeKeys.getPrivate());
-
- ECPrivateKey javaPriv = (ECPrivateKey) SignatureUtility.convertPrivateBouncyCastleKeyToJavaKey(largeKeys.getPrivate());
- Signature signer = Signature.getInstance("SHA256withECDSA");
- signer.initSign(javaPriv);
- signer.update(message);
- byte[] javaSignature = signer.sign();
-
- ECPublicKey javaPub = (ECPublicKey) SignatureUtility.convertPublicBouncyCastleKeyToJavaKey(largeKeys.getPublic());
- Signature verifier = Signature.getInstance("SHA256withECDSA");
- verifier.initVerify(javaPub);
- verifier.update(message);
- assertTrue(verifier.verify(javaSignature));
-
- verifier.initVerify(javaPub);
- verifier.update(message);
- assertTrue(verifier.verify(bcSignature));
-
- assertTrue(SignatureUtility.verifyHashed(digest, javaSignature, largeKeys.getPublic()));
- assertTrue(SignatureUtility.verifyHashed(digest, bcSignature, largeKeys.getPublic()));
+ public void personal() {
+ AbstractSignature sig = new PersonalSignature(subjectKeys.getPrivate(), MSG);
+ sunshine(sig);
+ ensureProcessing(sig);
+ wrongMessage(sig);
+ wrongKeys(sig);
}
@Test
- public void testSignDeterministic() {
- byte[] message = new byte[515];
- message[0] = 42;
- message[514] = 13;
-
- byte[] signature = SignatureUtility.signDeterministicSHA256(message, largeKeys.getPrivate());
- assertTrue(SignatureUtility.verifySHA256(message, signature, largeKeys.getPublic()));
+ public void raw() {
+ AbstractSignature sig = new RawSignature(subjectKeys.getPrivate(), MSG);
+ sunshine(sig);
+ wrongMessage(sig);
+ wrongKeys(sig);
}
@Test
- public void testSignRandomized() {
- for (int i = 0; i < 50; i++) {
- byte[] message = new byte[256];
- message[0] = 0x42;
- message[255] = (byte) i;
-
- byte[] signature = SignatureUtility.signHashedRandomized(message, largeKeys.getPrivate());
- assertTrue(SignatureUtility.verifyHashed(message, signature, largeKeys.getPublic()));
- }
+ public void compressed() {
+ Signature sig = new CompressedMsgSignature(subjectKeys.getPrivate(), MSG);
+ sunshine(sig);
+ wrongMessage(sig);
+ wrongKeys(sig);
}
+
@Test
- public void testEthereumSigning() {
- byte[] message = new byte[515];
- message[0] = 43;
- message[514] = 15;
- byte[] signature = SignatureUtility.signPersonalMsgWithEthereum(message, userKeys.getPrivate());
- assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message, signature, userKeys.getPublic()));
+ public void expectedRaw() {
+ AbstractSignature sig = new RawSignature(subjectKeys.getPrivate(), MSG);
+ assertArrayEquals(sig.getRawSignature(), SignatureUtility.signWithEthereum(MSG, subjectKeys.getPrivate()));
+ assertEquals(sig.getTypeOfSignature(), "raw");
+ Arrays.equals(sig.processMessage(MSG), MSG);
}
@Test
- public void testEthereumSigningNewChain() {
- byte[] message = new byte[515];
- message[0] = 41;
- message[514] = 45;
- byte[] signature = SignatureUtility.signPersonalMsgWithEthereum(message, 2, userKeys.getPrivate());
- assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message, signature,
- SignatureUtility.addressFromKey(userKeys.getPublic()), 2));
+ public void expectedCompressedType() {
+ Signature sig = new CompressedMsgSignature(subjectKeys.getPrivate(), MSG, "prefix ", " postfix");
+ assertEquals(sig.getTypeOfSignature(), "compressed");
}
@Test
- public void testEthereumSigningAgainstReference() {
- for (int i = 0; i < 50; i++) {
- // We make an extra long message and ensure that both the first and last bytes are not 0
- byte[] message = new byte[515];
- message[0] = 0x42;
- message[514] = (byte) i;
-
- BigInteger[] ourSig = SignatureUtility
- .computeInternalSignature(AttestationCrypto.hashWithKeccak(message), (ECPrivateKeyParameters) userKeys.getPrivate());
- BigInteger[] refSig = signDeterministic(message, userKeys.getPrivate());
- // We need to adjust the s part of the signature if it happens to be
- // less than N/2+1 since these are the only valid Ethereum signatures.
- if (refSig[1].compareTo(SignatureUtility.ECDSA_DOMAIN.getN().shiftRight(1)) > 0) {
- refSig[1] = SignatureUtility.ECDSA_DOMAIN.getN().subtract(refSig[1]);
- }
- assertEquals(refSig[0], ourSig[0]);
- assertEquals(refSig[1], ourSig[1]);
- }
+ public void otherConstructorRaw() {
+ Signature sig = new RawSignature(subjectKeys.getPrivate(), MSG);
+ Signature newSig = new RawSignature(sig.getRawSignature());
+ sunshine(newSig);
+ assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
}
@Test
- public void addressRecovery() {
- String address = SignatureUtility.addressFromKey(userKeys.getPublic());
- assertTrue(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), address));
- assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), address+"00"));
- assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), "0"+address));
- byte[] addressBytes = address.getBytes();
- addressBytes[5] ^= 0x01;
- assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), new String(addressBytes)));
+ public void expectedPersonal() {
+ Signature sig = new PersonalSignature(subjectKeys.getPrivate(), MSG);
+ assertArrayEquals(sig.getRawSignature(), SignatureUtility.signPersonalMsgWithEthereum(MSG, subjectKeys.getPrivate()));
+ assertEquals(sig.getTypeOfSignature(), "personal");
}
@Test
- public void recoverPublicKey() {
- byte[] message = new byte[] {0x42};
- byte[] testSignature = SignatureUtility.signWithEthereum(message, userKeys.getPrivate());
- String address = SignatureUtility.addressFromKey(userKeys.getPublic());
- AsymmetricKeyParameter key = SignatureUtility.recoverEthPublicKeyFromSignature(message, testSignature);
- assertEquals(address, SignatureUtility.addressFromKey(key));
+ public void compressedReference() {
+ CompressedMsgSignature sig = new CompressedMsgSignature(subjectKeys.getPrivate(), MSG, "prefix ", " postfix");
+ assertArrayEquals(sig.processMessage(MSG), "prefix 0x9DF8DBA3720D00BD48AD744722021EF91B035E273BCCFB78660CA8DF9574B086 postfix".getBytes(
+ StandardCharsets.UTF_8));
}
@Test
- public void personalSigning() {
- String message = "hello world";
- byte[] personalSignature = SignatureUtility.signPersonalMsgWithEthereum(message.getBytes(
- StandardCharsets.UTF_8), userKeys.getPrivate());
- assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
- personalSignature, userKeys.getPublic()));
- // A personal signature does not verify as a normal signature
- assertFalse(SignatureUtility.verifyEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
- personalSignature, userKeys.getPublic()));
- byte[] normalSignature = SignatureUtility.signWithEthereum(message.getBytes(
- StandardCharsets.UTF_8), userKeys.getPrivate());
- assertFalse(SignatureUtility.verifyPersonalEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
- normalSignature, userKeys.getPublic()));
- assertTrue(SignatureUtility.verifyEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
- normalSignature, userKeys.getPublic()));
+ public void otherConstructorPersonal() {
+ Signature sig = new PersonalSignature(subjectKeys.getPrivate(), MSG);
+ Signature newSig = new PersonalSignature(sig.getRawSignature());
+ sunshine(newSig);
+ assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
}
@Test
- public void verifyingChainId() {
- byte[] signature = new byte[65];
- signature[64] = 27;
- assertEquals(SignatureUtility.getChainIdFromSignature(signature), 0);
- signature[64] = 28;
- assertEquals(SignatureUtility.getChainIdFromSignature(signature), 0);
- signature[64] = 37;
- assertEquals(SignatureUtility.getChainIdFromSignature(signature), 1);
- signature[64] = 42;
- assertEquals(SignatureUtility.getChainIdFromSignature(signature), 3);
+ public void otherConstructorCompressed() {
+ Signature sig = new CompressedMsgSignature(subjectKeys.getPrivate(), MSG);
+ Signature newSig = new CompressedMsgSignature(sig.getRawSignature());
+ sunshine(newSig);
+ assertArrayEquals(sig.getRawSignature(), newSig.getRawSignature());
}
- @Test
- public void verifyWrongChain() {
- byte[] msgWithoutPrefix = new byte[] {0x42};
- byte[] personalSignature = SignatureUtility.signPersonalMsgWithEthereum(msgWithoutPrefix, 4, userKeys.getPrivate());
- assertTrue(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature,
- SignatureUtility.addressFromKey(userKeys.getPublic()), 4));
- assertFalse(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature, userKeys.getPublic()));
- assertFalse(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature,
- SignatureUtility.addressFromKey(userKeys.getPublic()), 5));
+ public void sunshine(Signature sig) {
+ assertNotNull(sig.getRawSignature());
+ assertTrue(sig.getRawSignature().length > 5);
+ assertTrue(sig.verify(MSG, subjectKeys.getPublic()));
+ }
+
+ public void ensureProcessing(AbstractSignature sig) {
+ assertFalse(Arrays.equals(sig.processMessage(MSG), MSG));
+ }
+
+ public void wrongMessage(Signature sig) {
+ assertFalse(sig.verify("some other message".getBytes(StandardCharsets.UTF_8), subjectKeys.getPublic()));
}
- private static BigInteger[] signDeterministic(byte[] toSign, AsymmetricKeyParameter key) {
- Digest keccak = new KeccakDigest(256);
- keccak.update(toSign, 0, toSign.length);
- HMacDSAKCalculator randomnessProvider = new HMacDSAKCalculator(keccak);
- byte[] digest = new byte[256/8];
- keccak.doFinal(digest, 0);
- ECDSASigner signer = new ECDSASigner(randomnessProvider);
- signer.init(true, key);
- return signer.generateSignature(digest);
+ public void wrongKeys(Signature sig) {
+ assertFalse(sig.verify(MSG, SignatureUtility.constructECKeysWithSmallestY(rand).getPublic()));
}
}
diff --git a/src/test/java/org/tokenscript/attestation/core/SignatureUtilityTest.java b/src/test/java/org/tokenscript/attestation/core/SignatureUtilityTest.java
new file mode 100644
index 00000000..3b66975d
--- /dev/null
+++ b/src/test/java/org/tokenscript/attestation/core/SignatureUtilityTest.java
@@ -0,0 +1,202 @@
+package org.tokenscript.attestation.core;
+
+import static org.junit.jupiter.api.Assertions.assertEquals;
+import static org.junit.jupiter.api.Assertions.assertFalse;
+import static org.junit.jupiter.api.Assertions.assertTrue;
+
+import java.math.BigInteger;
+import java.nio.charset.StandardCharsets;
+import java.security.SecureRandom;
+import java.security.Security;
+import java.security.Signature;
+import java.security.interfaces.ECPrivateKey;
+import java.security.interfaces.ECPublicKey;
+import org.bouncycastle.asn1.sec.SECNamedCurves;
+import org.bouncycastle.asn1.x9.X9ECParameters;
+import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
+import org.bouncycastle.crypto.Digest;
+import org.bouncycastle.crypto.digests.KeccakDigest;
+import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
+import org.bouncycastle.crypto.params.ECPrivateKeyParameters;
+import org.bouncycastle.crypto.signers.ECDSASigner;
+import org.bouncycastle.crypto.signers.HMacDSAKCalculator;
+import org.bouncycastle.jce.provider.BouncyCastleProvider;
+import org.junit.jupiter.api.BeforeEach;
+import org.junit.jupiter.api.Test;
+
+public class SignatureUtilityTest {
+ private static final X9ECParameters SECP364R1 = SECNamedCurves.getByName("secp384r1");
+ private AsymmetricCipherKeyPair largeKeys;
+ private AsymmetricCipherKeyPair userKeys;
+ private SecureRandom rand;
+
+ @BeforeEach
+ public void setupCrypto() throws Exception {
+ Security.addProvider(new BouncyCastleProvider());
+ rand = SecureRandom.getInstance("SHA1PRNG", "SUN");
+ rand.setSeed("seed".getBytes());
+ largeKeys = SignatureUtility.constructECKeys(SECP364R1, rand);
+ userKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
+ }
+
+ @Test
+ public void testKeyConversion() throws Exception {
+ byte[] message = "test".getBytes(StandardCharsets.UTF_8);
+ byte[] digest = AttestationCrypto.hashWithSHA256(message);
+ byte[] bcSignature = SignatureUtility.signHashedRandomized(digest, largeKeys.getPrivate());
+
+ ECPrivateKey javaPriv = (ECPrivateKey) SignatureUtility.convertPrivateBouncyCastleKeyToJavaKey(largeKeys.getPrivate());
+ Signature signer = Signature.getInstance("SHA256withECDSA");
+ signer.initSign(javaPriv);
+ signer.update(message);
+ byte[] javaSignature = signer.sign();
+
+ ECPublicKey javaPub = (ECPublicKey) SignatureUtility.convertPublicBouncyCastleKeyToJavaKey(largeKeys.getPublic());
+ Signature verifier = Signature.getInstance("SHA256withECDSA");
+ verifier.initVerify(javaPub);
+ verifier.update(message);
+ assertTrue(verifier.verify(javaSignature));
+
+ verifier.initVerify(javaPub);
+ verifier.update(message);
+ assertTrue(verifier.verify(bcSignature));
+
+ assertTrue(SignatureUtility.verifyHashed(digest, javaSignature, largeKeys.getPublic()));
+ assertTrue(SignatureUtility.verifyHashed(digest, bcSignature, largeKeys.getPublic()));
+ }
+
+ @Test
+ public void testSignDeterministic() {
+ byte[] message = new byte[515];
+ message[0] = 42;
+ message[514] = 13;
+
+ byte[] signature = SignatureUtility.signDeterministicSHA256(message, largeKeys.getPrivate());
+ assertTrue(SignatureUtility.verifySHA256(message, signature, largeKeys.getPublic()));
+ }
+
+ @Test
+ public void testSignRandomized() {
+ for (int i = 0; i < 50; i++) {
+ byte[] message = new byte[256];
+ message[0] = 0x42;
+ message[255] = (byte) i;
+
+ byte[] signature = SignatureUtility.signHashedRandomized(message, largeKeys.getPrivate());
+ assertTrue(SignatureUtility.verifyHashed(message, signature, largeKeys.getPublic()));
+ }
+ }
+
+ @Test
+ public void testEthereumSigning() {
+ byte[] message = new byte[515];
+ message[0] = 43;
+ message[514] = 15;
+ byte[] signature = SignatureUtility.signPersonalMsgWithEthereum(message, userKeys.getPrivate());
+ assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message, signature, userKeys.getPublic()));
+ }
+
+ @Test
+ public void testEthereumSigningNewChain() {
+ byte[] message = new byte[515];
+ message[0] = 41;
+ message[514] = 45;
+ byte[] signature = SignatureUtility.signPersonalMsgWithEthereum(message, 2, userKeys.getPrivate());
+ assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message, signature,
+ SignatureUtility.addressFromKey(userKeys.getPublic()), 2));
+ }
+
+ @Test
+ public void testEthereumSigningAgainstReference() {
+ for (int i = 0; i < 50; i++) {
+ // We make an extra long message and ensure that both the first and last bytes are not 0
+ byte[] message = new byte[515];
+ message[0] = 0x42;
+ message[514] = (byte) i;
+
+ BigInteger[] ourSig = SignatureUtility
+ .computeInternalSignature(AttestationCrypto.hashWithKeccak(message), (ECPrivateKeyParameters) userKeys.getPrivate());
+ BigInteger[] refSig = signDeterministic(message, userKeys.getPrivate());
+ // We need to adjust the s part of the signature if it happens to be
+ // less than N/2+1 since these are the only valid Ethereum signatures.
+ if (refSig[1].compareTo(SignatureUtility.ECDSA_DOMAIN.getN().shiftRight(1)) > 0) {
+ refSig[1] = SignatureUtility.ECDSA_DOMAIN.getN().subtract(refSig[1]);
+ }
+ assertEquals(refSig[0], ourSig[0]);
+ assertEquals(refSig[1], ourSig[1]);
+ }
+ }
+
+ @Test
+ public void addressRecovery() {
+ String address = SignatureUtility.addressFromKey(userKeys.getPublic());
+ assertTrue(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), address));
+ assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), address+"00"));
+ assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), "0"+address));
+ byte[] addressBytes = address.getBytes();
+ addressBytes[5] ^= 0x01;
+ assertFalse(SignatureUtility.verifyKeyAgainstAddress(userKeys.getPublic(), new String(addressBytes)));
+ }
+
+ @Test
+ public void recoverPublicKey() {
+ byte[] message = new byte[] {0x42};
+ byte[] testSignature = SignatureUtility.signWithEthereum(message, userKeys.getPrivate());
+ String address = SignatureUtility.addressFromKey(userKeys.getPublic());
+ AsymmetricKeyParameter key = SignatureUtility.recoverEthPublicKeyFromSignature(message, testSignature);
+ assertEquals(address, SignatureUtility.addressFromKey(key));
+ }
+
+ @Test
+ public void personalSigning() {
+ String message = "hello world";
+ byte[] personalSignature = SignatureUtility.signPersonalMsgWithEthereum(message.getBytes(
+ StandardCharsets.UTF_8), userKeys.getPrivate());
+ assertTrue(SignatureUtility.verifyPersonalEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
+ personalSignature, userKeys.getPublic()));
+ // A personal signature does not verify as a normal signature
+ assertFalse(SignatureUtility.verifyEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
+ personalSignature, userKeys.getPublic()));
+ byte[] normalSignature = SignatureUtility.signWithEthereum(message.getBytes(
+ StandardCharsets.UTF_8), userKeys.getPrivate());
+ assertFalse(SignatureUtility.verifyPersonalEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
+ normalSignature, userKeys.getPublic()));
+ assertTrue(SignatureUtility.verifyEthereumSignature(message.getBytes(StandardCharsets.UTF_8),
+ normalSignature, userKeys.getPublic()));
+ }
+
+ @Test
+ public void verifyingChainId() {
+ byte[] signature = new byte[65];
+ signature[64] = 27;
+ assertEquals(SignatureUtility.getChainIdFromSignature(signature), 0);
+ signature[64] = 28;
+ assertEquals(SignatureUtility.getChainIdFromSignature(signature), 0);
+ signature[64] = 37;
+ assertEquals(SignatureUtility.getChainIdFromSignature(signature), 1);
+ signature[64] = 42;
+ assertEquals(SignatureUtility.getChainIdFromSignature(signature), 3);
+ }
+
+ @Test
+ public void verifyWrongChain() {
+ byte[] msgWithoutPrefix = new byte[] {0x42};
+ byte[] personalSignature = SignatureUtility.signPersonalMsgWithEthereum(msgWithoutPrefix, 4, userKeys.getPrivate());
+ assertTrue(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature,
+ SignatureUtility.addressFromKey(userKeys.getPublic()), 4));
+ assertFalse(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature, userKeys.getPublic()));
+ assertFalse(SignatureUtility.verifyPersonalEthereumSignature(msgWithoutPrefix, personalSignature,
+ SignatureUtility.addressFromKey(userKeys.getPublic()), 5));
+ }
+
+ private static BigInteger[] signDeterministic(byte[] toSign, AsymmetricKeyParameter key) {
+ Digest keccak = new KeccakDigest(256);
+ keccak.update(toSign, 0, toSign.length);
+ HMacDSAKCalculator randomnessProvider = new HMacDSAKCalculator(keccak);
+ byte[] digest = new byte[256/8];
+ keccak.doFinal(digest, 0);
+ ECDSASigner signer = new ECDSASigner(randomnessProvider);
+ signer.init(true, key);
+ return signer.generateSignature(digest);
+ }
+}
diff --git a/src/test/java/org/tokenscript/auth/EIP712AuthenticationTest.java b/src/test/java/org/tokenscript/attestation/eip712/EIP712ObjectTest.java
similarity index 55%
rename from src/test/java/org/tokenscript/auth/EIP712AuthenticationTest.java
rename to src/test/java/org/tokenscript/attestation/eip712/EIP712ObjectTest.java
index d04eba11..8f02f8dd 100644
--- a/src/test/java/org/tokenscript/auth/EIP712AuthenticationTest.java
+++ b/src/test/java/org/tokenscript/attestation/eip712/EIP712ObjectTest.java
@@ -1,4 +1,4 @@
-package org.tokenscript.auth;
+package org.tokenscript.attestation.eip712;
import static org.junit.jupiter.api.Assertions.assertEquals;
import static org.junit.jupiter.api.Assertions.assertFalse;
@@ -6,7 +6,6 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import com.fasterxml.jackson.core.JsonProcessingException;
import java.math.BigInteger;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
@@ -18,20 +17,27 @@
import org.devcon.ticket.Ticket;
import org.devcon.ticket.TicketDecoder;
import org.junit.jupiter.api.BeforeAll;
+import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.Test;
-import org.tokenscript.attestation.AttestableObjectDecoder;
+import org.mockito.ArgumentMatchers;
+import org.mockito.Mock;
+import org.mockito.Mockito;
+import org.mockito.MockitoAnnotations;
import org.tokenscript.attestation.AttestedObject;
+import org.tokenscript.attestation.AttestedObjectDecoder;
import org.tokenscript.attestation.HelperTest;
import org.tokenscript.attestation.IdentifierAttestation;
+import org.tokenscript.attestation.ObjectDecoder;
import org.tokenscript.attestation.SignedIdentifierAttestation;
+import org.tokenscript.attestation.Timestamp;
+import org.tokenscript.attestation.core.ASNEncodable;
import org.tokenscript.attestation.core.AttestationCrypto;
import org.tokenscript.attestation.core.SignatureUtility;
import org.tokenscript.attestation.core.URLUtility;
-import org.tokenscript.attestation.Timestamp;
import org.tokenscript.eip712.Eip712Test;
import org.tokenscript.eip712.FullEip712InternalData;
-public class EIP712AuthenticationTest {
+public class EIP712ObjectTest {
private static final String validatorDomain = "http://www.hotelbogota.com";
private static final X9ECParameters SECP364R1 = SECNamedCurves.getByName("secp384r1");
@@ -46,10 +52,15 @@ public class EIP712AuthenticationTest {
private static AsymmetricCipherKeyPair userKeys, attestorKeys, ticketKeys;
private static SecureRandom rand;
private static AttestationCrypto crypto;
- private static Eip712AuthValidator validator;
- private static Eip712AuthIssuer issuer;
+ private static Eip712ObjectValidator validator;
+ private static Eip712ObjectSigner issuer;
private static AuthenticatorEncoder encoder;
+ @BeforeEach
+ public void init() {
+ MockitoAnnotations.initMocks(this);
+ }
+
@BeforeAll
public static void setupKeys() throws Exception {
rand = SecureRandom.getInstance("SHA1PRNG", "SUN");
@@ -58,17 +69,19 @@ public static void setupKeys() throws Exception {
userKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
attestorKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
ticketKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
- AttestableObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
- encoder = new AuthenticatorEncoder(1, rand);
- validator = new Eip712AuthValidator(decoder, encoder, attestorKeys.getPublic(), validatorDomain);
- issuer = new Eip712AuthIssuer(userKeys.getPrivate(), encoder);
+ ObjectDecoder ticketDecoder = new TicketDecoder(ticketKeys.getPublic());
+ ObjectDecoder> attestedObjectDecoder = new AttestedObjectDecoder(ticketDecoder,
+ attestorKeys.getPublic());
+ encoder = new AuthenticatorEncoder(0, rand);
+ validator = new Eip712ObjectValidator(attestedObjectDecoder, encoder, validatorDomain);
+ issuer = new Eip712ObjectSigner(userKeys.getPrivate(), encoder);
}
private static AttestedObject makeAttestedTicket() {
IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(userKeys.getPublic(), attestorKeys.getPublic(), ATTESTATION_SECRET, MAIL );
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketKeys, TICKET_SECRET);
- AttestedObject attestedTicket = new AttestedObject(ticket, signed, userKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
+ AttestedObject attestedTicket = new AttestedObject(ticket, signed, ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
assertTrue(attestedTicket.verify());
assertTrue(attestedTicket.checkValidity());
return attestedTicket;
@@ -88,14 +101,24 @@ public void eipEncoding() throws Exception {
Eip712Test.validateEncoding(encoder, token);
}
+ @Test
+ public void eipEncodingDefaultIssuer() throws Exception {
+ AttestedObject attestedTicket = makeAttestedTicket();
+ String token = issuer.buildSignedToken(attestedTicket, validatorDomain);
+ Eip712Test.validateEncoding(encoder, token);
+ }
+
@Test
public void testNewChainID() throws Exception {
AuthenticatorEncoder localAuthenticator = new AuthenticatorEncoder(42, rand);
- Eip712AuthIssuer localIssuer = new Eip712AuthIssuer(userKeys.getPrivate(), localAuthenticator);
+ Eip712ObjectSigner localIssuer = new Eip712ObjectSigner(userKeys.getPrivate(), localAuthenticator);
AttestedObject attestedTicket = makeAttestedTicket();
String token = localIssuer.buildSignedToken(attestedTicket, validatorDomain);
- AttestableObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
- Eip712AuthValidator localValidator = new Eip712AuthValidator(decoder, localAuthenticator, attestorKeys.getPublic(), validatorDomain);
+ ObjectDecoder ticketDecoder = new TicketDecoder(ticketKeys.getPublic());
+ ObjectDecoder> attestedObjectDecoder = new AttestedObjectDecoder(ticketDecoder,
+ attestorKeys.getPublic());
+ Eip712ObjectValidator localValidator = new Eip712ObjectValidator(attestedObjectDecoder, localAuthenticator,
+ validatorDomain);
assertTrue(localValidator.validateRequest(token));
assertFalse(validator.validateRequest(token));
}
@@ -104,7 +127,7 @@ public void testNewChainID() throws Exception {
public void testConsistency() throws Exception {
AttestedObject attestedTicket = makeAttestedTicket();
long testTimestamp = Clock.systemUTC().millis();
- Eip712AuthIssuer testIssuer = new TestEip712Authentication(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
+ Eip712ObjectSigner testIssuer = new TestEip712ObjectSigner(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
String newToken = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
assertEquals(token, newToken);
@@ -121,7 +144,7 @@ public void wrongAttestedKey() throws Exception {
IdentifierAttestation att = HelperTest.makeUnsignedStandardAtt(newKeys.getPublic(), attestorKeys.getPublic(), ATTESTATION_SECRET, MAIL );
SignedIdentifierAttestation signed = new SignedIdentifierAttestation(att, attestorKeys);
Ticket ticket = new Ticket(MAIL, CONFERENCE_ID, TICKET_ID, TICKET_CLASS, ticketKeys, TICKET_SECRET);
- AttestedObject attestedTicket = new AttestedObject(ticket, signed, newKeys.getPublic(), ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
+ AttestedObject attestedTicket = new AttestedObject(ticket, signed, ATTESTATION_SECRET, TICKET_SECRET, UN, crypto);
String token = issuer.buildSignedToken(attestedTicket, validatorDomain);
assertFalse(validator.validateRequest(token));
@@ -130,7 +153,7 @@ public void wrongAttestedKey() throws Exception {
@Test
public void wrongSignature() throws Exception {
AsymmetricCipherKeyPair newKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
- Eip712AuthIssuer newIssuer = new Eip712AuthIssuer(newKeys.getPrivate(), encoder.getChainId());
+ Eip712ObjectSigner newIssuer = new Eip712ObjectSigner(newKeys.getPrivate(), new AuthenticatorEncoder(encoder.getChainId(),rand));
AttestedObject attestedTicket = makeAttestedTicket();
String token = newIssuer.buildSignedToken(attestedTicket, validatorDomain);
assertFalse(validator.validateRequest(token));
@@ -140,7 +163,7 @@ public void wrongSignature() throws Exception {
public void tooNew() throws Exception {
AttestedObject attestedTicket = makeAttestedTicket();
long testTimestamp = Clock.systemUTC().millis() + 2 * Timestamp.ALLOWED_ROUNDING;
- Eip712AuthIssuer testIssuer = new TestEip712Authentication(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
+ Eip712ObjectSigner testIssuer = new TestEip712ObjectSigner(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
assertFalse(validator.validateRequest(token));
}
@@ -149,10 +172,10 @@ public void tooNew() throws Exception {
public void tooOld() throws Exception {
AttestedObject attestedTicket = makeAttestedTicket();
long testTimestamp = 10000;
- Eip712AuthIssuer testIssuer = new TestEip712Authentication(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
+ Eip712ObjectSigner testIssuer = new TestEip712ObjectSigner(userKeys.getPrivate(), new TestAuthenticatorEncoder(), testTimestamp);
String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
- AttestableObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
- Eip712AuthValidator newValidator = new Eip712AuthValidator(decoder, encoder, attestorKeys.getPublic(), validatorDomain);
+ ObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
+ Eip712ObjectValidator newValidator = new Eip712ObjectValidator(decoder, encoder, validatorDomain);
assertFalse(newValidator.validateRequest(token));
}
@@ -176,9 +199,9 @@ public void incorrectDomain() throws Exception {
@Test
public void invalidDomainVerifier() {
- AttestableObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
+ ObjectDecoder decoder = new TicketDecoder(ticketKeys.getPublic());
assertThrows( RuntimeException.class, () -> {
- new Eip712AuthValidator(decoder, encoder, attestorKeys.getPublic(), "www.noHttpPrefix.com");
+ new Eip712ObjectValidator(decoder, encoder, "www.noHttpPrefix.com");
});
}
@@ -187,7 +210,7 @@ public void invalidDomainIssuer() {
AttestedObject attestedTicket = makeAttestedTicket();
assertThrows( RuntimeException.class, () -> {
AuthenticatorEncoder authenticator = new AuthenticatorEncoder(1, rand);
- Eip712AuthIssuer issuer = new Eip712AuthIssuer(userKeys.getPrivate(), authenticator);
+ Eip712ObjectSigner issuer = new Eip712ObjectSigner(userKeys.getPrivate(), authenticator);
issuer.buildSignedToken(attestedTicket, "www.noHttpPrefix.com");
});
}
@@ -195,11 +218,61 @@ public void invalidDomainIssuer() {
@Test
public void invalidVersion() throws Exception {
AttestedObject attestedTicket = makeAttestedTicket();
- Eip712AuthIssuer testIssuer = new Eip712AuthIssuer(userKeys.getPrivate(), new TestAuthenticatorEncoder("2.2", 1));
+ Eip712ObjectSigner testIssuer = new Eip712ObjectSigner(userKeys.getPrivate(), new TestAuthenticatorEncoder("2.2", encoder.getChainId()));
+ String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
+ assertFalse(validator.validateRequest(token));
+ }
+
+ @Test
+ public void badDescription() {
+ AttestedObject attestedTicket = makeAttestedTicket();
+ Eip712ObjectSigner testIssuer = new Eip712ObjectSigner(userKeys.getPrivate(),
+ new TestAuthenticatorEncoder(encoder.getVerifyingContract(), encoder.getSalt(),
+ "Wrong description", encoder.getProtocolVersion(), encoder.getChainId()));
+ String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
+ assertFalse(validator.validateRequest(token));
+ }
+
+ @Test
+ public void badVerifyingContract() {
+ AttestedObject attestedTicket = makeAttestedTicket();
+ Eip712ObjectSigner testIssuer = new Eip712ObjectSigner(userKeys.getPrivate(),
+ new TestAuthenticatorEncoder("0xDEADBEEF", encoder.getSalt(),
+ "Wrong description", encoder.getProtocolVersion(), encoder.getChainId()));
String token = testIssuer.buildSignedToken(attestedTicket, validatorDomain);
assertFalse(validator.validateRequest(token));
}
+ @Mock
+ AttestedObject mockedAttestedObject;
+ @Mock
+ ObjectDecoder> mockedDecoder;
+ @Test
+ public void unvalidatableUnderlyingObject() throws Exception {
+ Mockito.when(mockedAttestedObject.checkValidity()).thenReturn(false);
+ Mockito.when(mockedAttestedObject.verify()).thenReturn(true);
+ Mockito.when(mockedAttestedObject.getDerEncoding()).thenReturn(new byte[] {0x42});
+ Mockito.when(mockedAttestedObject.getAttestedUserKey()).thenReturn(userKeys.getPublic());
+
+ Mockito.when(mockedDecoder.decode(ArgumentMatchers.any())).thenReturn(mockedAttestedObject);
+ Eip712ObjectValidator newValidator = new Eip712ObjectValidator(mockedDecoder, encoder, validatorDomain);
+ String token = issuer.buildSignedToken(mockedAttestedObject, validatorDomain);
+ assertFalse(newValidator.validateRequest(token));
+ }
+
+ @Test
+ public void unverifiableUnderlyingObject() throws Exception {
+ Mockito.when(mockedAttestedObject.checkValidity()).thenReturn(true);
+ Mockito.when(mockedAttestedObject.verify()).thenReturn(false);
+ Mockito.when(mockedAttestedObject.getDerEncoding()).thenReturn(new byte[] {0x42});
+ Mockito.when(mockedAttestedObject.getAttestedUserKey()).thenReturn(userKeys.getPublic());
+
+ Mockito.when(mockedDecoder.decode(ArgumentMatchers.any())).thenReturn(mockedAttestedObject);
+ Eip712ObjectValidator newValidator = new Eip712ObjectValidator(mockedDecoder, encoder, validatorDomain);
+ String token = issuer.buildSignedToken(mockedAttestedObject, validatorDomain);
+ assertFalse(newValidator.validateRequest(token));
+ }
+
@Test
public void consistentSalt() {
String salt = encoder.getSalt();
@@ -208,43 +281,67 @@ public void consistentSalt() {
assertEquals(salt, otherSalt);
}
- private class TestEip712Authentication extends Eip712AuthIssuer {
+ private class TestEip712ObjectSigner extends Eip712ObjectSigner {
private final Timestamp testTimestamp;
- public TestEip712Authentication(AsymmetricKeyParameter signingKey, AuthenticatorEncoder authenticator, long testTimestamp) {
+ public TestEip712ObjectSigner(AsymmetricKeyParameter signingKey, AuthenticatorEncoder authenticator, long testTimestamp) {
super(signingKey, authenticator);
this.testTimestamp = new Timestamp(testTimestamp);
}
@Override
- public String buildSignedToken(AttestedObject attestedObject, String webDomain) throws JsonProcessingException {
- String encodedObject = URLUtility.encodeData(attestedObject.getDerEncoding());
- FullEip712InternalData auth = new FullEip712InternalData(
- encoder.getUsageValue(), encodedObject, testTimestamp);
- return buildSignedTokenFromJsonObject(auth, webDomain);
+ public String buildSignedToken(T attestedObject, String webDomain) {
+ try {
+ String encodedObject = URLUtility.encodeData(attestedObject.getDerEncoding());
+ FullEip712InternalData auth = new FullEip712InternalData(
+ encoder.getUsageValue(), encodedObject, testTimestamp);
+ return buildSignedTokenFromJsonObject(auth, webDomain);
+ } catch (Exception e) {
+ throw new RuntimeException("", e);
+ }
}
}
private class TestAuthenticatorEncoder extends AuthenticatorEncoder {
- private String protoVersion = super.getProtocolVersion();
+ private String protoVersion;
+ private String usageValue;
+ private String salt;
+ private String verifyingContract;
+
+ public TestAuthenticatorEncoder() {
+ this(encoder.getProtocolVersion(), encoder.getChainId());
+ }
public TestAuthenticatorEncoder(String protoVersion, long chainId) {
+ this(encoder.getVerifyingContract(), encoder.getSalt(), encoder.getUsageValue(), protoVersion, chainId);
+ }
+
+ public TestAuthenticatorEncoder(String verifyingContract, String salt, String usageValue, String protoVersion, long chainId) {
super(chainId, new SecureRandom());
this.protoVersion = protoVersion;
+ this.usageValue = usageValue;
+ this.salt = salt;
+ this.verifyingContract = verifyingContract;
}
- public TestAuthenticatorEncoder() {
- super(1, new SecureRandom());
+ @Override
+ public String getUsageValue() {
+ return usageValue;
}
@Override
public String getSalt() {
- return "0102030405060708090001020304050607080900010203040506070809000102";
+ return salt;
}
@Override
public String getProtocolVersion() {
return protoVersion;
}
+
+ @Override
+ public String getVerifyingContract() {
+ return verifyingContract;
+ }
}
}
diff --git a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestEip712.java b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestEip712.java
index 64a114d5..74e3ed11 100644
--- a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestEip712.java
+++ b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestEip712.java
@@ -5,15 +5,8 @@
import static org.junit.jupiter.api.Assertions.assertFalse;
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
+import static org.mockito.Mockito.spy;
-import org.tokenscript.attestation.AttestationRequest;
-import org.tokenscript.attestation.FullProofOfExponent;
-import org.tokenscript.attestation.IdentifierAttestation.AttestationType;
-import org.tokenscript.attestation.Timestamp;
-import org.tokenscript.attestation.core.AttestationCrypto;
-import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.core.URLUtility;
-import org.tokenscript.attestation.eip712.Eip712AttestationRequestEncoder.AttestationRequestInternalData;
import java.math.BigInteger;
import java.security.SecureRandom;
import java.util.Objects;
@@ -26,7 +19,15 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.attestation.AttestationRequest;
+import org.tokenscript.attestation.FullProofOfExponent;
+import org.tokenscript.attestation.IdentifierAttestation.AttestationType;
+import org.tokenscript.attestation.Timestamp;
+import org.tokenscript.attestation.core.AttestationCrypto;
+import org.tokenscript.attestation.core.SignatureUtility;
+import org.tokenscript.attestation.core.URLUtility;
+import org.tokenscript.attestation.eip712.Eip712AttestationRequestEncoder.AttestationRequestInternalData;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Test;
public class TestAttestationRequestEip712 {
@@ -133,11 +134,27 @@ public void eipSignableEncoding() throws Exception {
AttestationRequestInternalData data = new AttestationRequestInternalData(
encoder.getUsageValue(),
MAIL, URLUtility.encodeData(attRequest.getDerEncoding()), new Timestamp());
- Eip712Issuer issuer = new Eip712Issuer(userSigningKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(userSigningKey, encoder);
String json = issuer.buildSignedTokenFromJsonObject(data.getSignableVersion(), DOMAIN);
Eip712Test.validateEncoding(encoder, json);
}
+ @Mock
+ Eip712AttestationRequestEncoder mockedEncoder;
+ @Test
+ public void badDescription() {
+ byte[] nonce = Nonce.makeNonce(userAddress, DOMAIN, new Timestamp());
+ FullProofOfExponent pok = crypto.computeAttestationProof(ATTESTATION_SECRET, nonce);
+ AttestationRequest attRequest = new AttestationRequest(TYPE, pok);
+ Eip712AttestationRequest request = new Eip712AttestationRequest(DOMAIN, MAIL, attRequest, userSigningKey);
+ Eip712AttestationRequestEncoder encoder = new Eip712AttestationRequestEncoder(Eip712AttestationRequestEncoder.LEGACY_USAGE_VALUE);
+ mockedEncoder = spy(encoder);
+ Mockito.when(mockedEncoder.getUsageValue()).thenReturn("Something wrong");
+ Eip712AttestationRequest badRequest = new Eip712AttestationRequest(DOMAIN, Timestamp.DEFAULT_TIME_LIMIT_MS, request.getJsonEncoding(), mockedEncoder);
+ assertTrue(badRequest.verify());
+ assertFalse(badRequest.checkValidity());
+ }
+
@Test
public void automaticDecoder() {
byte[] nonce = Nonce.makeNonce(userAddress, DOMAIN, new Timestamp());
@@ -160,7 +177,11 @@ public void badDomain() {
AttestationRequest attRequest = new AttestationRequest(TYPE, pok);
Eip712AttestationRequest request = new Eip712AttestationRequest(DOMAIN, MAIL,
attRequest, userSigningKey);
- assertThrows( IllegalArgumentException.class, () -> new Eip712AttestationRequest("http://www.someOtherDomain.com", request.getJsonEncoding()));
+ assertTrue(request.checkValidity());
+ assertTrue(request.verify());
+ Eip712AttestationRequest newRequest = new Eip712AttestationRequest("http://www.someOtherDomain.com", request.getJsonEncoding());
+ assertTrue(newRequest.verify());
+ assertFalse(newRequest.checkValidity());
}
@Test
diff --git a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestWithUsageEip712.java b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestWithUsageEip712.java
index 70bf372a..9e2af5a5 100644
--- a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestWithUsageEip712.java
+++ b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationRequestWithUsageEip712.java
@@ -31,7 +31,7 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Test;
public class TestAttestationRequestWithUsageEip712 {
@@ -147,7 +147,7 @@ public void eipSignableEncoding() throws Exception {
Timestamp expirationTime = new Timestamp(now.getTime() + 1000);
AttestationRequestWUsageData data = new AttestationRequestWUsageData(
encoder.getUsageValue(), MAIL, URLUtility.encodeData(requestWithUsage.getDerEncoding()), now, expirationTime);
- Eip712Issuer issuer = new Eip712Issuer(userSigningKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(userSigningKey, encoder);
String json = issuer.buildSignedTokenFromJsonObject(data.getSignableVersion(), DOMAIN);
Eip712Test.validateEncoding(encoder, json);
}
diff --git a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationUsageEip712.java b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationUsageEip712.java
index 5df217b3..7b22ed0d 100644
--- a/src/test/java/org/tokenscript/attestation/eip712/TestAttestationUsageEip712.java
+++ b/src/test/java/org/tokenscript/attestation/eip712/TestAttestationUsageEip712.java
@@ -31,7 +31,7 @@
import org.mockito.Mock;
import org.mockito.Mockito;
import org.mockito.MockitoAnnotations;
-import org.tokenscript.eip712.Eip712Issuer;
+import org.tokenscript.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Test;
public class TestAttestationUsageEip712 {
@@ -143,7 +143,7 @@ public void eipSignableEncoding() throws Exception {
AttestationUsageData data = new AttestationUsageData(
encoder.getUsageValue(),
MAIL, URLUtility.encodeData(usage.getDerEncoding()), now, expirationTime);
- Eip712Issuer issuer = new Eip712Issuer(userSigningKey, encoder);
+ Eip712Signer issuer = new Eip712Signer(userSigningKey, encoder);
String json = issuer.buildSignedTokenFromJsonObject(data.getSignableVersion(), DOMAIN);
Eip712Test.validateEncoding(encoder, json);
}
diff --git a/src/test/java/org/tokenscript/eip712/Eip712Test.java b/src/test/java/org/tokenscript/eip712/Eip712Test.java
index 58e3752c..58dfaa93 100644
--- a/src/test/java/org/tokenscript/eip712/Eip712Test.java
+++ b/src/test/java/org/tokenscript/eip712/Eip712Test.java
@@ -6,12 +6,9 @@
import static org.junit.jupiter.api.Assertions.assertThrows;
import static org.junit.jupiter.api.Assertions.assertTrue;
-import org.tokenscript.attestation.core.SignatureUtility;
-import org.tokenscript.attestation.Timestamp;
import com.alphawallet.token.web.Ethereum.web3j.StructuredData.Entry;
import com.fasterxml.jackson.databind.JsonNode;
import com.fasterxml.jackson.databind.ObjectMapper;
-import java.io.InvalidObjectException;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.HashMap;
@@ -19,6 +16,8 @@
import org.bouncycastle.crypto.AsymmetricCipherKeyPair;
import org.junit.jupiter.api.BeforeAll;
import org.junit.jupiter.api.Test;
+import org.tokenscript.attestation.Timestamp;
+import org.tokenscript.attestation.core.SignatureUtility;
public class Eip712Test {
private static final String testDomain = "http://www.test.com";
@@ -27,7 +26,7 @@ public class Eip712Test {
private static AsymmetricCipherKeyPair userKeys;
private static SecureRandom rand;
private static Eip712Validator validator;
- private static Eip712Issuer issuer;
+ private static Eip712Signer issuer;
private static Eip712Encoder encoder;
private static ObjectMapper mapper;
@@ -48,7 +47,7 @@ public static void setupKeys() throws Exception {
userKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
encoder = new TestEncoder();
validator = new Eip712Validator(testDomain, encoder);
- issuer = new Eip712Issuer(userKeys.getPrivate(), encoder);
+ issuer = new Eip712Signer(userKeys.getPrivate(), encoder);
mapper = new ObjectMapper();
}
@@ -62,7 +61,7 @@ private void checkEquality(FullEip712InternalData computedObject) {
@Test
public void testSunshine() throws Exception {
String token = issuer.buildSignedTokenFromJsonObject(testObject, testDomain);
- checkEquality(validator.retrieveUnderlyingObject(token, FullEip712InternalData.class));
+ checkEquality(validator.retrieveUnderlyingJson(token, FullEip712InternalData.class));
assertTrue(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
}
@@ -71,7 +70,7 @@ public void referenceEncoding() throws Exception {
String token = "{\"signatureInHex\":\"0x3a65f28b33d92973327ae616bb74c28a9c9f14ef5741a388d00449888fa2a7eb7602d1bf03cc89ff1c39b6cbbf4839bbed609161d36c364274a302e41e7aa0ae1c\",\"jsonSigned\":\"{\\\"types\\\":{\\\"Test\\\":[{\\\"name\\\":\\\"payload\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"timestamp\\\",\\\"type\\\":\\\"string\\\"}],\\\"EIP712Domain\\\":[{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"version\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"chainId\\\",\\\"type\\\":\\\"uint256\\\"},{\\\"name\\\":\\\"verifyingContract\\\",\\\"type\\\":\\\"address\\\"},{\\\"name\\\":\\\"salt\\\",\\\"type\\\":\\\"bytes32\\\"}]},\\\"primaryType\\\":\\\"Test\\\",\\\"message\\\":{\\\"payload\\\":\\\"payload\\\",\\\"description\\\":\\\"description\\\",\\\"timestamp\\\":\\\"Thu Jan 1 1970 00:00:00 GMT+0000\\\"},\\\"domain\\\":{\\\"name\\\":\\\"http://www.test.com\\\",\\\"version\\\":\\\"1.0\\\",\\\"chainId\\\":1,\\\"verifyingContract\\\":\\\"0x0123456789012345678901234567890123456789\\\",\\\"salt\\\":\\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\"}}\"}";
String recomputedToken = issuer.buildSignedTokenFromJsonObject(testObject, testDomain);
assertEquals(token, recomputedToken);
- checkEquality(validator.retrieveUnderlyingObject(token, FullEip712InternalData.class));
+ checkEquality(validator.retrieveUnderlyingJson(token, FullEip712InternalData.class));
assertTrue(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
}
@@ -81,7 +80,7 @@ public void referenceEncodingOtherOrder() throws Exception {
+ "\\\"types\\\":{\\\"Test\\\":[{\\\"name\\\":\\\"payload\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"description\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"timestamp\\\",\\\"type\\\":\\\"string\\\"}],\\\"EIP712Domain\\\":[{\\\"name\\\":\\\"name\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"version\\\",\\\"type\\\":\\\"string\\\"},{\\\"name\\\":\\\"chainId\\\",\\\"type\\\":\\\"uint256\\\"},{\\\"name\\\":\\\"verifyingContract\\\",\\\"type\\\":\\\"address\\\"},{\\\"name\\\":\\\"salt\\\",\\\"type\\\":\\\"bytes32\\\"}]},"
+ " \\\"message\\\":{\\\"description\\\":\\\"description\\\", \\\"payload\\\":\\\"payload\\\", \\\"timestamp\\\":\\\"Thu Jan 1 1970 00:00:00 GMT+0000\\\"},\\\"domain\\\":{\\\"name\\\":\\\"http://www.test.com\\\",\\\"version\\\":\\\"1.0\\\",\\\"chainId\\\":1,\\\"verifyingContract\\\":\\\"0x0123456789012345678901234567890123456789\\\",\\\"salt\\\":\\\"0x0000000000000000000000000000000000000000000000000000000000000000\\\"}}\","
+ " \"signatureInHex\":\"0x3a65f28b33d92973327ae616bb74c28a9c9f14ef5741a388d00449888fa2a7eb7602d1bf03cc89ff1c39b6cbbf4839bbed609161d36c364274a302e41e7aa0ae1c\" }";
- checkEquality(validator.retrieveUnderlyingObject(token, FullEip712InternalData.class));
+ checkEquality(validator.retrieveUnderlyingJson(token, FullEip712InternalData.class));
assertTrue(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
}
@@ -96,15 +95,17 @@ public void eipEncoding() throws Exception {
@Test
public void testNewChainID() throws Exception {
TestEncoder localTestEncoder = new TestEncoder("1", 42);
- Eip712Issuer localIssuer = new Eip712Issuer(userKeys.getPrivate(), localTestEncoder);
+ Eip712Signer localIssuer = new Eip712Signer(userKeys.getPrivate(), localTestEncoder);
String token = localIssuer.buildSignedTokenFromJsonObject(testObject, testDomain);
String otherToken = localIssuer.buildSignedTokenFromJsonObject(testObject, testDomain);
assertEquals(token, otherToken);
Eip712Validator localValidator = new Eip712Validator(testDomain, localTestEncoder);
- checkEquality(localValidator.retrieveUnderlyingObject(token, FullEip712InternalData.class));
+ checkEquality(localValidator.retrieveUnderlyingJson(token, FullEip712InternalData.class));
assertTrue(localValidator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
+ assertTrue(localValidator.validateDomain(token));
// Other chain ID in global validator
- assertFalse(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
+ assertTrue(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
+ assertFalse(validator.validateDomain(token));
}
@Test
@@ -116,14 +117,14 @@ public void testConsistency() throws Exception {
@Test
public void nullInput() {
- assertThrows( IllegalArgumentException.class, () -> validator.retrieveUnderlyingObject(null, FullEip712InternalData.class));
+ assertThrows( IllegalArgumentException.class, () -> validator.retrieveUnderlyingJson(null, FullEip712InternalData.class));
}
@Test
public void testDifferenceWithDifferentChainIds() throws Exception {
String token = issuer.buildSignedTokenFromJsonObject(testObject, testDomain);
TestEncoder localTestEncoder = new TestEncoder("1.0", 42);
- Eip712Issuer localIssuer = new Eip712Issuer(userKeys.getPrivate(), localTestEncoder);
+ Eip712Signer localIssuer = new Eip712Signer(userKeys.getPrivate(), localTestEncoder);
String newToken = localIssuer.buildSignedTokenFromJsonObject(testObject, testDomain);
assertFalse(token.equals(newToken));
}
@@ -131,9 +132,9 @@ public void testDifferenceWithDifferentChainIds() throws Exception {
@Test
public void wrongSignature() throws Exception {
AsymmetricCipherKeyPair newKeys = SignatureUtility.constructECKeysWithSmallestY(rand);
- Eip712Issuer newIssuer = new Eip712Issuer(newKeys.getPrivate(), encoder);
+ Eip712Signer newIssuer = new Eip712Signer(newKeys.getPrivate(), encoder);
String token = newIssuer.buildSignedTokenFromJsonObject(testObject, testDomain);
- checkEquality(validator.retrieveUnderlyingObject(token, FullEip712InternalData.class));
+ checkEquality(validator.retrieveUnderlyingJson(token, FullEip712InternalData.class));
assertTrue(validator.verifySignature(token, SignatureUtility.addressFromKey(newKeys.getPublic()), FullEip712InternalData.class));
assertFalse(validator.verifySignature(token, SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
}
@@ -145,15 +146,13 @@ public void incorrectModifiedToken() throws Exception {
// Flip a bit
tokenBytes[0] ^= 0x01;
assertFalse(validator.verifySignature(new String(tokenBytes, StandardCharsets.UTF_8), SignatureUtility.addressFromKey(userKeys.getPublic()), FullEip712InternalData.class));
- assertThrows(IllegalArgumentException.class, () -> validator.retrieveUnderlyingObject(new String(tokenBytes, StandardCharsets.UTF_8), FullEip712InternalData.class));
+ assertThrows(IllegalArgumentException.class, () -> validator.retrieveUnderlyingJson(new String(tokenBytes, StandardCharsets.UTF_8), FullEip712InternalData.class));
}
@Test
public void incorrectDomain() throws Exception {
String token = issuer.buildSignedTokenFromJsonObject(testObject, "http://www.not-test.com");
- Eip712ExternalData data = mapper.readValue(token, Eip712ExternalData.class);
- JsonNode rootNode = mapper.readTree(data.getJsonSigned());
- assertThrows(InvalidObjectException.class, () -> validator.getDomainFromJson(rootNode));
+ assertFalse(validator.validateDomain(token));
}
@Test
@@ -168,22 +167,26 @@ public void invalidDomainVerifier() {
@Test
public void invalidVersionIssuer() throws Exception {
- Eip712Issuer newIssuer = new Eip712Issuer(userKeys.getPrivate(), new TestEncoder("2.0", 1));
+ Eip712Signer newIssuer = new Eip712Signer(userKeys.getPrivate(), new TestEncoder("2.0", 1));
String token = newIssuer.buildSignedTokenFromJsonObject(testObject, testDomain);
- Eip712ExternalData data = mapper.readValue(token, Eip712ExternalData.class);
- JsonNode rootNode = mapper.readTree(data.getJsonSigned());
- assertThrows(InvalidObjectException.class, () -> validator.getDomainFromJson(rootNode));
+ assertFalse(validator.validateDomain(token));
}
@Test
public void invalidVersionValidator() throws Exception {
Eip712Validator newValidator = new Eip712Validator(testDomain, new TestEncoder("2.0", 1));
String token = issuer.buildSignedTokenFromJsonObject(testObject, testDomain);
- Eip712ExternalData data = mapper.readValue(token, Eip712ExternalData.class);
- JsonNode rootNode = mapper.readTree(data.getJsonSigned());
- assertThrows(InvalidObjectException.class, () -> newValidator.getDomainFromJson(rootNode));
+ assertFalse(newValidator.validateDomain(token));
}
+ @Test
+ public void invalidSalt() throws Exception {
+ byte[] salt = new byte[32];
+ salt[0] = (byte) 0xff;
+ Eip712Validator newValidator = new Eip712Validator(testDomain, new TestEncoder(encoder.getProtocolVersion(), encoder.getChainId(), encoder.getVerifyingContract(), salt));
+ String token = issuer.buildSignedTokenFromJsonObject(testObject, testDomain);
+ assertFalse(newValidator.validateDomain(token));
+ }
@Test
public void invalidAddressInEncoder() {