Skip to content

Commit

Permalink
Merge pull request #281 from TokenScript/fix/issue-276-add-validation
Browse files Browse the repository at this point in the history
fix: 🐛 issue#276 fix. Added eip712 domain validations
  • Loading branch information
oleggrib authored Jul 13, 2022
2 parents 02530ff + f22785e commit ac1827f
Show file tree
Hide file tree
Showing 9 changed files with 142 additions and 89 deletions.
Original file line number Diff line number Diff line change
@@ -1,27 +1,23 @@
package org.tokenscript.attestation.eip712;

import static org.tokenscript.attestation.Timestamp.DEFAULT_TIME_LIMIT_MS;
import static org.tokenscript.attestation.Timestamp.DEFAULT_TOKEN_TIME_LIMIT;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.tokenscript.attestation.AttestationRequestWithUsage;
import org.tokenscript.attestation.FullProofOfExponent;
import org.tokenscript.attestation.IdentifierAttestation.AttestationType;
import org.tokenscript.attestation.Timestamp;
import org.tokenscript.attestation.core.ExceptionUtil;
import org.tokenscript.attestation.core.SignatureUtility;
import org.tokenscript.attestation.core.URLUtility;
import org.tokenscript.attestation.core.Validateable;
import org.tokenscript.attestation.core.Verifiable;
import org.tokenscript.attestation.core.*;
import org.tokenscript.attestation.eip712.Eip712AttestationRequestWithUsageEncoder.AttestationRequestWUsageData;
import org.tokenscript.attestation.eip712.Eip712AttestationUsageEncoder.AttestationUsageData;
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.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Validator;
import org.tokenscript.eip712.JsonEncodable;

import java.io.IOException;

import static org.tokenscript.attestation.Timestamp.DEFAULT_TIME_LIMIT_MS;
import static org.tokenscript.attestation.Timestamp.DEFAULT_TOKEN_TIME_LIMIT;

public class Eip712AttestationRequestWithUsage extends Eip712Validator implements JsonEncodable,
Verifiable, Validateable, TokenValidateable {
private static final Logger logger = LogManager.getLogger(Eip712AttestationRequestWithUsage.class);
Expand Down Expand Up @@ -87,13 +83,13 @@ void constructorCheck() throws IllegalArgumentException {

String makeToken(String identifier, AttestationRequestWithUsage attestationRequestWithUsage,
AsymmetricKeyParameter signingKey) throws IOException {
Eip712Signer issuer = new Eip712Signer<AttestationUsageData>(signingKey, encoder);
Eip712Signer<AttestationRequestWUsageData> issuer = new Eip712Signer<>(signingKey, encoder);
String encodedUseAttestation = URLUtility.encodeData(attestationRequestWithUsage.getDerEncoding());
Timestamp now = new Timestamp();
Timestamp expirationTime = new Timestamp(now.getTime() + maxTokenValidityInMs);
AttestationRequestWUsageData data = new AttestationRequestWUsageData(
encoder.getUsageValue(), identifier, encodedUseAttestation, now, expirationTime);
return issuer.buildSignedTokenFromJsonObject(data, domain);
AttestationRequestWUsageData attReqData = new AttestationRequestWUsageData(
encoder.getUsageValue(), identifier, encodedUseAttestation, now, expirationTime);
return issuer.buildSignedTokenFromJsonObject(attReqData, domain);
}

public String getIdentifier() {
Expand Down Expand Up @@ -154,17 +150,21 @@ public boolean checkTokenValidity() {
}

private boolean testNonceAndDescription(long timeLimit) {
if (!data.getDescription().equals(encoder.getUsageValue())) {
logger.error("Description field is incorrect");
return false;
}
if (!validateDomain(jsonEncoding)) {
logger.error("Could not validate domain information");
return false;
}
long nonceMinTime = Timestamp.stringTimestampToLong(data.getTimestamp()) - timeLimit;
long nonceMaxTime = Timestamp.stringTimestampToLong(data.getTimestamp()) + timeLimit;
if (!Nonce.validateNonce(attestationRequestWithUsage.getPok().getUnpredictableNumber(),
SignatureUtility.addressFromKey(userPublicKey), domain, new Timestamp(nonceMinTime), new Timestamp(nonceMaxTime))) {
SignatureUtility.addressFromKey(userPublicKey), domain, new Timestamp(nonceMinTime), new Timestamp(nonceMaxTime))) {
logger.error("Nonce validation failed");
return false;
}
if (!data.getDescription().equals(encoder.getUsageValue())) {
logger.error("Description field is incorrect");
return false;
}
return true;
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,20 @@
package org.tokenscript.attestation.eip712;

import org.tokenscript.attestation.AttestationAndUsageValidator;
import org.tokenscript.attestation.FullProofOfExponent;
import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;
import org.bouncycastle.crypto.params.AsymmetricKeyParameter;
import org.tokenscript.attestation.*;
import org.tokenscript.attestation.IdentifierAttestation.AttestationType;
import org.tokenscript.attestation.SignedIdentifierAttestation;
import org.tokenscript.attestation.Timestamp;
import org.tokenscript.attestation.UseAttestation;
import org.tokenscript.attestation.core.ExceptionUtil;
import org.tokenscript.attestation.core.URLUtility;
import org.tokenscript.attestation.core.Verifiable;
import org.tokenscript.attestation.eip712.Eip712AttestationUsageEncoder.AttestationUsageData;
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.eip712.Eip712Signer;
import org.tokenscript.eip712.Eip712Validator;
import org.tokenscript.eip712.JsonEncodable;

import java.io.IOException;

/**
* Class for asserting and validating that a user who already has an Identifier Attestation wishes to
* use it at a given website. The assertion is linked to a public key, which can be used to validate
Expand Down Expand Up @@ -90,13 +87,13 @@ void constructorCheck() throws IllegalArgumentException {

String makeToken(String identifier, UseAttestation useAttestation,
AsymmetricKeyParameter signingKey) throws IOException {
Eip712Signer issuer = new Eip712Signer<AttestationUsageData>(signingKey, encoder);
Eip712Signer<AttestationUsageData> issuer = new Eip712Signer<>(signingKey, encoder);
String encodedUseAttestation = URLUtility.encodeData(useAttestation.getDerEncoding());
Timestamp now = new Timestamp();
Timestamp expirationTime = new Timestamp(now.getTime() + maxTokenValidityInMs);
AttestationUsageData data = new AttestationUsageData(
encoder.getUsageValue(), identifier, encodedUseAttestation, now, expirationTime);
return issuer.buildSignedTokenFromJsonObject(data, domain);
AttestationUsageData attUsageData = new AttestationUsageData(
encoder.getUsageValue(), identifier, encodedUseAttestation, now, expirationTime);
return issuer.buildSignedTokenFromJsonObject(attUsageData, domain);
}

public String getIdentifier() {
Expand Down Expand Up @@ -130,12 +127,14 @@ public String getJsonEncoding() {

@Override
public boolean checkTokenValidity() {
long nonceMinTime = Timestamp.stringTimestampToLong(data.getExpirationTime()) - maxTokenValidityInMs;
long nonceMaxTime = Timestamp.stringTimestampToLong(data.getExpirationTime());
if (!validator.checkTokenValidity()) {
logger.error("Could not validate underlying object");
return false;
}
if (!validateDomain(jsonEncoding)) {
logger.error("Could not validate domain information");
return false;
}
if (!data.getDescription().equals(encoder.getUsageValue())) {
logger.error("Description field incorrect");
return false;
Expand All @@ -146,8 +145,14 @@ public boolean checkTokenValidity() {
logger.error("Timestamp not valid");
return false;
}
if (!validateDomain(jsonEncoding)) {
logger.error("Could not validate domain information");
return false;
}
long nonceMinTime = Timestamp.stringTimestampToLong(data.getExpirationTime()) - maxTokenValidityInMs;
long nonceMaxTime = Timestamp.stringTimestampToLong(data.getExpirationTime());
if (!Nonce.validateNonce(getPok().getUnpredictableNumber(),
getAttestation().getUnsignedAttestation().getAddress(), domain, new Timestamp(nonceMinTime), new Timestamp(nonceMaxTime))) {
getAttestation().getUnsignedAttestation().getAddress(), domain, new Timestamp(nonceMinTime), new Timestamp(nonceMaxTime))) {
logger.error("Nonce validation failed");
return false;
}
Expand Down
12 changes: 8 additions & 4 deletions src/main/javascript/crypto/src/Authenticator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,6 @@ export class Authenticator {
}
logger(DEBUGLEVEL.HIGH,'attestation valid');


try {
let redeem: AttestedObject = new AttestedObject();
redeem.create(ticket, att,
Expand Down Expand Up @@ -197,9 +196,14 @@ export class Authenticator {

let pok = crypto.computeAttestationProof(secret, nonce);
let attRequest = AttestationRequest.fromData(crypto.getType(type), pok);
let attest = new Eip712AttestationRequest(userKey);
await attest.addData(attestorDomain, 20*1000, receiverId, attRequest);
let attestJson = attest.getJsonEncoding();
let attestationRequest = new Eip712AttestationRequest(userKey);
// undefined means that we will use default value in attestationRequest.addData() second parameter
await attestationRequest.addData(attestorDomain, undefined, receiverId, attRequest);

Authenticator.checkAttestRequestVerifiability(attestationRequest);
Authenticator.checkAttestRequestValidity(attestationRequest);

let attestJson = attestationRequest.getJsonEncoding();

return attestJson;

Expand Down
19 changes: 8 additions & 11 deletions src/main/javascript/crypto/src/libs/Eip712AttestationRequest.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export class Eip712AttestationRequest extends Eip712Token implements JsonEncodab
this.jsonEncoding = json;
let tokenData = JSON.parse(json);
let signatureInHex = tokenData.signatureInHex;

let jsonSigned = JSON.parse(tokenData.jsonSigned);
this.eip712DomainData = jsonSigned.domain;
this.data = jsonSigned.message;
Expand Down Expand Up @@ -130,24 +130,21 @@ export class Eip712AttestationRequest extends Eip712Token implements JsonEncodab
public verify(): boolean {

if (!this.attestationRequest.verify()) {
logger(DEBUGLEVEL.MEDIUM, "Could not verify proof");
return false;
}

// if (!this.verifySignature(this.jsonEncoding, this.data.address)) {
// return false;
// }

return this.verifyDomainData();

}
return true;

public verifyDomainData(): boolean{
return (this.eip712DomainData.name.toLowerCase() === this.getDomain().toLowerCase())
&& (this.eip712DomainData.version === SignatureUtility.Eip712Data['PROTOCOL_VERSION']);
}

public checkValidity(): boolean {

if (!this.validateDomain(this.eip712DomainData)){
logger(DEBUGLEVEL.MEDIUM, "Domain invalid");
return false;
}

if (this.data.description !== this.usageValue) {
logger(DEBUGLEVEL.MEDIUM,'Description is not correct. :' + this.data.description + ' !== ' + this.usageValue);
return false;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -221,6 +221,10 @@ export class Eip712AttestationRequestWithUsage extends Eip712Token implements Js
)) {
return false;
}
if (!this.validateDomain(this.eip712DomainData)){
logger(DEBUGLEVEL.MEDIUM, "Domain invalid");
return false;
}
if (this.data.description !== this.Eip712UserDataDescription ) {
return false;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -175,7 +175,10 @@ export class Eip712AttestationUsage extends Eip712Token implements JsonEncodable
logger(DEBUGLEVEL.LOW, 'useAttestation.checkValidity failed');
return false;
};

if (!this.validateDomain(this.eip712DomainData)){
logger(DEBUGLEVEL.MEDIUM, "Domain invalid");
return false;
}
if (this.data.description != this.Eip712Description) {
logger(DEBUGLEVEL.LOW, `wrong description: "${this.data.description}", must be "${this.Eip712Description}"`);
return false;
Expand Down
81 changes: 47 additions & 34 deletions src/main/javascript/crypto/src/libs/Eip712Validator.ts
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,10 @@ import {DEBUGLEVEL} from "../config";
export class Eip712Validator {
private XMLConfig: any;
protected domain: string;
protected verifyingContract: string;
protected chainId: number;
protected salt: string;
protected primaryName: string;

constructor() {
this.XMLConfig = XMLconfigData;
Expand Down Expand Up @@ -42,56 +46,65 @@ export class Eip712Validator {
return this.domain;
}

validateRequest(jsonInput: string) {
try {
let authenticationData = JSON.parse(jsonInput);
setSalt(salt: string){
this.salt = salt;
}

let authenticationRootNode = JSON.parse(authenticationData.jsonSigned);
getSalt(): string{
return this.salt;
}

let eip712Domain = authenticationRootNode.domain;
let eip712Message = authenticationRootNode.message;
//getPrimaryName

let attestedObject = this.retrieveAttestedObject(eip712Message);
setPrimaryName(primaryName: string){
this.primaryName = primaryName;
}

// TODO implement
return this.validateDomain(eip712Domain)
// && this.validateAuthentication(auth);
// accept &= verifySignature(authenticationData, attestedObject.getUserPublicKey());
// accept &= validateAttestedObject(attestedObject);
// return accept;
} catch (e) {
logger(DEBUGLEVEL.LOW, 'Validate error!', e);
return false;
}
getPrimaryName(): string{
return this.primaryName;
}


setChainId(chainId: number){
if (chainId < 1) throw new Error('ChainId should be a positive number');
this.chainId = chainId;
}

// TODO
// public boolean verifyTimeStamp(String timestamp) {
getChainId(): number{
return this.chainId;
}

validateDomain(domainToCheck: Eip712DomainInterface): boolean {
if (domainToCheck.name.toLowerCase() !== this.domain.toLowerCase()) {
if ( !domainToCheck ) {
logger(DEBUGLEVEL.LOW, "Input param domainToCheck required");
return false;
}

if (!domainToCheck.name || (domainToCheck.name.toLowerCase() !== this.domain.toLowerCase())) {
logger(DEBUGLEVEL.LOW, "Domain name is not valid");
return false;
}

if (domainToCheck.version !== SignatureUtility.Eip712Data['PROTOCOL_VERSION']) {
if (!domainToCheck.version || (domainToCheck.version !== SignatureUtility.Eip712Data['PROTOCOL_VERSION'])) {
logger(DEBUGLEVEL.LOW, "Protocol version is wrong");
return false;
}

// we dont use that fields at the moment. maybe have to uncomment and fix in the future
// if (domainToCheck.chainId !== encoder.getChainId())) {
// logger(DEBUGLEVEL.LOW, "Chain ID is wrong");
// return false;
// }
// if (domainToCheck.verifyingContract !== encoder.getVerifyingContract()) {
// logger(DEBUGLEVEL.LOW, "Verifying contract is wrong");
// return false;
// }
// if (domainToCheck.salt !== encoder.getSalt()) {
// logger(DEBUGLEVEL.LOW, "Salt is wrong");
// return false;
// }
if (this.chainId && (domainToCheck.chainId !== this.chainId)) {
logger(DEBUGLEVEL.LOW, "Chain ID is wrong");
return false;
}

if (this.verifyingContract && (domainToCheck.verifyingContract !== this.verifyingContract)) {
logger(DEBUGLEVEL.LOW, "Verifying contract is wrong");
return false;
}

if (this.salt && (domainToCheck.salt !== this.salt)) {
logger(DEBUGLEVEL.LOW, "Salt is wrong");
return false;
}

return true;

}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@
import static org.junit.jupiter.api.Assertions.*;

public class TestAttestationRequestWithUsageEip712 {
private static final Logger logger = LogManager.getLogger(TestAttestationRequestWithUsageEip712.class);
private static final String DOMAIN = "https://www.attestation.id";
private static final String MAIL = "email@test.com";
private static final AttestationType TYPE = AttestationType.EMAIL;
Expand Down Expand Up @@ -285,4 +284,18 @@ void validateWrongNonceKey() {
assertFalse(request.checkTokenValidity());
}

@Test
void wrongDomain() {
Eip712AttestationRequestWithUsage request = new Eip712AttestationRequestWithUsage(DOMAIN, MAIL,
requestWithUsage, userSigningKey);
assertTrue(request.verify());
assertTrue(request.checkValidity());
assertTrue(request.checkTokenValidity());
// Request with wrong chain
Eip712AttestationRequestWithUsage wrongRequest = new Eip712AttestationRequestWithUsage("http://www.nope.com", request.getJsonEncoding());
assertTrue(wrongRequest.verify());
assertFalse(wrongRequest.checkTokenValidity());
assertFalse(wrongRequest.checkTokenValidity());
}

}
Loading

2 comments on commit ac1827f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report for src/main/javascript/crypto/

St.
Category Percentage Covered / Total
🟡 Statements 72.25% 1906/2638
🔴 Branches 45.45% 255/561
🟡 Functions 78.48% 350/446
🟡 Lines 72.46% 1868/2578

Test suite run success

38 tests passing in 2 suites.

Report generated by 🧪jest coverage report action from ac1827f

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Coverage report for src/main/javascript/crypto/

St.
Category Percentage Covered / Total
🟡 Statements 72.21% 1905/2638
🔴 Branches 45.28% 254/561
🟡 Functions 78.48% 350/446
🟡 Lines 72.42% 1867/2578

Test suite run success

38 tests passing in 2 suites.

Report generated by 🧪jest coverage report action from ac1827f

Please sign in to comment.