diff --git a/.gitignore b/.gitignore index a30b942..1e1e07f 100644 --- a/.gitignore +++ b/.gitignore @@ -78,6 +78,9 @@ nb-configuration.xml ############################## ## OS X ############################## -.DS_Store +**/*.DS_Store -*.pem \ No newline at end of file +############################## +## Misc +############################## +**/*.pem diff --git a/.run/AllTests.run.xml b/.run/AllTests.run.xml new file mode 100644 index 0000000..4785ae2 --- /dev/null +++ b/.run/AllTests.run.xml @@ -0,0 +1,25 @@ + + + + + + + false + true + false + + + \ No newline at end of file diff --git a/README.md b/README.md index b6fbf13..ecccd86 100644 --- a/README.md +++ b/README.md @@ -9,13 +9,12 @@ Since Torus nodes operate on a threshold assumption, we need to ensure that API This is to prevent malicious nodes from withholding shares, or deliberately slowing down the entire process. This utility library allows for early exits in optimistic scenarios, while handling rejection of invalid inputs from nodes in malicious/offline scenarios. -The general approach is to evaluate predicates against a list of (potentially incomplete) results, and exit when the predicate passes. +The general approach is to evaluate a threshold number of results instead of a list of (potentially incomplete) results, and then exit once a threshold number of valid results have been evaluated. README.md ## Features - Handles up to threshold number of failures. -- Optimistic early exit (eg. 5/9 nodes return valid shares = complete) -- All API's return `CompletableFutures` +- Optimistic early exit (eg. threshold number of nodes return valid shares = complete) ## Getting Started @@ -29,11 +28,11 @@ repositories { maven { url "https://jitpack.io" } } dependencies { - implementation 'org.torusresearch:torus-utils-java:3.1.2' + implementation 'org.torusresearch:torus-utils-java:4.0.0' } ``` ## Requirements - Android - API level 24 -- Java 8 / 1.8 \ No newline at end of file +- Java 11 \ No newline at end of file diff --git a/build.gradle b/build.gradle index 023e9af..fbb0acf 100644 --- a/build.gradle +++ b/build.gradle @@ -1,13 +1,14 @@ plugins { id 'java-library' id 'maven-publish' + id "org.gradle.test-retry" version "1.5.10" } group 'org.torusresearch' -version '3.2.2' +version '4.0.0' -sourceCompatibility = 1.8 -targetCompatibility = 1.8 +sourceCompatibility = 11 +targetCompatibility = 11 repositories { mavenCentral() @@ -19,14 +20,19 @@ dependencies { implementation 'org.web3j:core:4.8.8-android' implementation 'com.google.code.gson:gson:2.9.0' implementation 'com.squareup.okhttp3:okhttp:4.9.3' + implementation 'org.json:json:20240303' testImplementation 'org.junit.jupiter:junit-jupiter-api:5.8.2' testRuntimeOnly 'org.junit.jupiter:junit-jupiter-engine:5.8.2' - testImplementation 'org.slf4j:slf4j-simple:1.7.36' + testImplementation 'org.assertj:assertj-core:3.6.1' testImplementation 'com.auth0:java-jwt:3.19.2' testImplementation 'net.andreinc:mockneat:0.4.8' } test { + retry { + maxRetries = 3 + } + useJUnitPlatform() } diff --git a/gradle/wrapper/gradle-wrapper.properties b/gradle/wrapper/gradle-wrapper.properties index ffed3a2..59bc51a 100644 --- a/gradle/wrapper/gradle-wrapper.properties +++ b/gradle/wrapper/gradle-wrapper.properties @@ -1,5 +1,5 @@ distributionBase=GRADLE_USER_HOME distributionPath=wrapper/dists -distributionUrl=https\://services.gradle.org/distributions/gradle-7.2-bin.zip +distributionUrl=https\://services.gradle.org/distributions/gradle-8.1-bin.zip zipStoreBase=GRADLE_USER_HOME zipStorePath=wrapper/dists diff --git a/src/main/java/org/torusresearch/torusutils/TorusUtils.java b/src/main/java/org/torusresearch/torusutils/TorusUtils.java index 2a651e1..a5d4931 100644 --- a/src/main/java/org/torusresearch/torusutils/TorusUtils.java +++ b/src/main/java/org/torusresearch/torusutils/TorusUtils.java @@ -1,103 +1,99 @@ package org.torusresearch.torusutils; -import com.google.gson.Gson; -import com.google.gson.GsonBuilder; -import com.google.gson.JsonSyntaxException; +import static org.torusresearch.fetchnodedetails.types.Utils.METADATA_MAP; -import org.bouncycastle.jce.ECNamedCurveTable; import org.bouncycastle.jce.provider.BouncyCastleProvider; -import org.bouncycastle.jce.spec.ECNamedCurveParameterSpec; -import org.bouncycastle.math.ec.ECPoint; +import org.jetbrains.annotations.NotNull; import org.torusresearch.fetchnodedetails.types.TorusNodePub; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.apis.APIUtils; -import org.torusresearch.torusutils.apis.CommitmentRequestParams; -import org.torusresearch.torusutils.apis.JsonRPCResponse; -import org.torusresearch.torusutils.apis.KeyAssignResult; -import org.torusresearch.torusutils.apis.KeyAssignment; -import org.torusresearch.torusutils.apis.NodeSignature; -import org.torusresearch.torusutils.apis.PubKey; -import org.torusresearch.torusutils.apis.ShareRequestParams; -import org.torusresearch.torusutils.apis.VerifierLookupItem; -import org.torusresearch.torusutils.apis.VerifierLookupRequestResult; -import org.torusresearch.torusutils.helpers.AES256CBC; -import org.torusresearch.torusutils.helpers.Base64; -import org.torusresearch.torusutils.helpers.PredicateFailedException; -import org.torusresearch.torusutils.helpers.Some; -import org.torusresearch.torusutils.helpers.Utils; -import org.torusresearch.torusutils.types.DecryptedShare; -import org.torusresearch.torusutils.types.GetOrSetNonceError; -import org.torusresearch.torusutils.types.GetOrSetNonceResult; -import org.torusresearch.torusutils.types.MetadataParams; -import org.torusresearch.torusutils.types.MetadataPubKey; -import org.torusresearch.torusutils.types.MetadataResponse; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; -import org.web3j.crypto.ECDSASignature; -import org.web3j.crypto.ECKeyPair; -import org.web3j.crypto.Hash; -import org.web3j.crypto.Keys; +import org.torusresearch.torusutils.apis.JsonRPCErrorInfo; +import org.torusresearch.torusutils.apis.requests.GetMetadataParams; +import org.torusresearch.torusutils.apis.responses.GetMetadataResponse; +import org.torusresearch.torusutils.apis.responses.GetOrSetNonceResult; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.LegacyVerifierKey; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.LegacyVerifierLookupResponse; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.VerifierKey; +import org.torusresearch.torusutils.helpers.Common; +import org.torusresearch.torusutils.helpers.KeyUtils; +import org.torusresearch.torusutils.helpers.MetadataUtils; +import org.torusresearch.torusutils.helpers.NodeUtils; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.TorusUtilsExtraParams; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.common.ImportedShare; +import org.torusresearch.torusutils.types.common.KeyLookup.KeyLookupResult; +import org.torusresearch.torusutils.types.common.KeyLookup.KeyResult; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusKeyType; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import java.math.BigInteger; -import java.nio.charset.StandardCharsets; import java.security.Provider; import java.security.Security; import java.util.ArrayList; -import java.util.HashMap; +import java.util.Arrays; import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import okhttp3.internal.http2.Header; +import io.reactivex.annotations.Nullable; public class TorusUtils { - - public final TorusCtorOptions options; - - private final BigInteger secp256k1N = new BigInteger("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFEBAAEDCE6AF48A03BBFD25E8CD0364141", 16); + private final String defaultHost; + private final TorusOptions options; + private int sessionTime = 86400; + private final TorusKeyType keyType; + private String apiKey = "torus-default"; { setupBouncyCastle(); } - public TorusUtils(TorusCtorOptions options) { + public TorusUtils(TorusOptions options) throws TorusUtilError { this.options = options; + this.keyType = options.keyType; + if (options.legacyMetadataHost == null) { + if (isLegacyNetorkRouteMap(options.network)) { + this.defaultHost = METADATA_MAP.get(options.network); + } else { + if (options.network.name().equalsIgnoreCase("sapphire_mainnet")) { + this.defaultHost = "https://node-1.node.web3auth.io/metadata"; + } else if (options.network.name().equalsIgnoreCase("sapphire_devnet")) { + this.defaultHost = "https://node-1.dev-node.web3auth.io/metadata"; + } else { + throw TorusUtilError.INVALID_INPUT; + } + } + } else { + this.defaultHost = options.legacyMetadataHost; + } } - public static void setAPIKey(String apiKey) { - APIUtils.setApiKey(apiKey); + public static boolean isLegacyNetorkRouteMap(@NotNull Web3AuthNetwork network) { + // TODO: Fix this in fetchnodedetails, comparison should be against .legacy(network) + return !network.name().toLowerCase().contains("sapphire"); } - public static boolean isGetOrSetNonceError(Exception err) { - return err instanceof GetOrSetNonceError; + @SuppressWarnings("unused") + public void setApiKey(String apiKey) { + this.apiKey = apiKey; + APIUtils.setApiKey(apiKey); } + @SuppressWarnings("unused") + public void removeApiKey() { + this.apiKey = "torus-default"; + APIUtils.setApiKey("torus-default"); + } - BigInteger lagrangeInterpolation(BigInteger[] shares, BigInteger[] nodeIndex) { - if (shares.length != nodeIndex.length) { - return null; - } - BigInteger secret = new BigInteger("0"); - for (int i = 0; i < shares.length; i++) { - BigInteger upper = new BigInteger("1"); - BigInteger lower = new BigInteger("1"); - for (int j = 0; j < shares.length; j++) { - if (i != j) { - upper = upper.multiply(nodeIndex[j].negate()); - upper = upper.mod(secp256k1N); - BigInteger temp = nodeIndex[i].subtract(nodeIndex[j]); - temp = temp.mod(secp256k1N); - lower = lower.multiply(temp).mod(secp256k1N); - } - } - BigInteger delta = upper.multiply(lower.modInverse(secp256k1N)).mod(secp256k1N); - delta = delta.multiply(shares[i]).mod(secp256k1N); - secret = secret.add(delta); - } - return secret.mod(secp256k1N); + public void setSessionTime(int sessionTime) { + this.sessionTime = sessionTime; } private void setupBouncyCastle() { @@ -118,531 +114,208 @@ private void setupBouncyCastle() { Security.insertProviderAt(new BouncyCastleProvider(), 1); } - public CompletableFuture retrieveShares(String[] endpoints, BigInteger[] indexes, String verifier, HashMap verifierParams, String idToken, HashMap extraParams) { - try { - APIUtils.get(this.options.getAllowHost(), new Header[]{new Header("Origin", this.options.getOrigin()), new Header("verifier", verifier), new Header("verifier_id", verifierParams.get("verifier_id").toString()), new Header("network", this.options.getNetwork()), new Header("clientid", this.options.getClientId()), - new Header("enablegating", "true")}, true).get(); - List> promiseArr = new ArrayList<>(); - // generate temporary private and public key that is used to secure receive shares - ECKeyPair tmpKey = Keys.createEcKeyPair(); - String pubKey = Utils.padLeft(tmpKey.getPublicKey().toString(16), '0', 128); - String pubKeyX = pubKey.substring(0, pubKey.length() / 2); - String pubKeyY = pubKey.substring(pubKey.length() / 2); - String tokenCommitment = org.web3j.crypto.Hash.sha3String(idToken); - int t = endpoints.length / 4; - int k = t * 2 + 1; - - // make commitment requests to endpoints - for (int i = 0; i < endpoints.length; i++) { - CompletableFuture p = APIUtils.post(endpoints[i], APIUtils.generateJsonRPCObject("CommitmentRequest", new CommitmentRequestParams("mug00", tokenCommitment.substring(2), pubKeyX, pubKeyY, String.valueOf(System.currentTimeMillis()), verifier)), false); - promiseArr.add(i, p); - } - // send share request once k + t number of commitment requests have completed - return new Some<>(promiseArr, (resultArr, commitmentsResolved) -> { - List completedRequests = new ArrayList<>(); - for (String result : resultArr) { - if (result != null && !result.equals("")) { - completedRequests.add(result); - } - } - CompletableFuture> completableFuture = new CompletableFuture<>(); - if (completedRequests.size() >= k + t) { - completableFuture.complete(completedRequests); - } else { - completableFuture.completeExceptionally(new PredicateFailedException("insufficient responses for commitments")); - } - return completableFuture; - }).getCompletableFuture().thenComposeAsync(responses -> { - try { - List> promiseArrRequests = new ArrayList<>(); - List nodeSigs = new ArrayList<>(); - for (String respons : responses) { - if (respons != null && !respons.equals("")) { - Gson gson = new Gson(); - try { - JsonRPCResponse nodeSigResponse = gson.fromJson(respons, JsonRPCResponse.class); - if (nodeSigResponse != null && nodeSigResponse.getResult() != null) { - nodeSigs.add(Utils.convertToJsonObject(nodeSigResponse.getResult())); - } - } catch (JsonSyntaxException e) { - // discard this, we don't care - } - } - } - NodeSignature[] nodeSignatures = new NodeSignature[nodeSigs.size()]; - for (int l = 0; l < nodeSigs.size(); l++) { - Gson gson = new Gson(); - nodeSignatures[l] = gson.fromJson(nodeSigs.get(l), NodeSignature.class); - } - verifierParams.put("idtoken", idToken); - verifierParams.put("nodesignatures", nodeSignatures); - verifierParams.put("verifieridentifier", verifier); - if (extraParams != null) { - verifierParams.putAll(extraParams); - } - List> shareRequestItems = new ArrayList>() {{ - add(verifierParams); - }}; - for (String endpoint : endpoints) { - String req = APIUtils.generateJsonRPCObject("ShareRequest", new ShareRequestParams(shareRequestItems)); - promiseArrRequests.add(APIUtils.post(endpoint, req, false)); - } - return new Some<>(promiseArrRequests, (shareResponses, predicateResolved) -> { - try { - // check if threshold number of nodes have returned the same user public key - BigInteger privateKey = null; - List completedResponses = new ArrayList<>(); - Gson gson = new Gson(); - for (String shareResponse : shareResponses) { - if (shareResponse != null && !shareResponse.equals("")) { - try { - JsonRPCResponse shareResponseJson = gson.fromJson(shareResponse, JsonRPCResponse.class); - if (shareResponseJson != null && shareResponseJson.getResult() != null) { - completedResponses.add(Utils.convertToJsonObject(shareResponseJson.getResult())); - } - } catch (JsonSyntaxException e) { - // discard this, we don't care - } - } - } - List completedResponsesPubKeys = new ArrayList<>(); - for (String x : completedResponses) { - KeyAssignResult keyAssignResult = gson.fromJson(x, KeyAssignResult.class); - if (keyAssignResult == null || keyAssignResult.getKeys() == null || keyAssignResult.getKeys().length == 0) { - return null; - } - KeyAssignment keyAssignResultFirstKey = keyAssignResult.getKeys()[0]; - completedResponsesPubKeys.add(Utils.convertToJsonObject(keyAssignResultFirstKey.getPublicKey())); - } - String thresholdPublicKeyString = Utils.thresholdSame(completedResponsesPubKeys, k); - PubKey thresholdPubKey = null; - if (thresholdPublicKeyString != null && !thresholdPublicKeyString.equals("")) { - thresholdPubKey = gson.fromJson(thresholdPublicKeyString, PubKey.class); - } - if (completedResponses.size() >= k && thresholdPubKey != null) { - List decryptedShares = new ArrayList<>(); - for (int i = 0; i < shareResponses.length; i++) { - if (shareResponses[i] != null && !shareResponses[i].equals("")) { - try { - JsonRPCResponse currentJsonRPCResponse = gson.fromJson(shareResponses[i], JsonRPCResponse.class); - if (currentJsonRPCResponse != null && currentJsonRPCResponse.getResult() != null && !currentJsonRPCResponse.getResult().equals("")) { - KeyAssignResult currentShareResponse = gson.fromJson(Utils.convertToJsonObject(currentJsonRPCResponse.getResult()), KeyAssignResult.class); - if (currentShareResponse != null && currentShareResponse.getKeys() != null && currentShareResponse.getKeys().length > 0) { - KeyAssignment firstKey = currentShareResponse.getKeys()[0]; - if (firstKey.getMetadata() != null) { - try { - AES256CBC aes256cbc = new AES256CBC(tmpKey.getPrivateKey().toString(16), firstKey.getMetadata().getEphemPublicKey(), firstKey.getMetadata().getIv()); - // Implementation specific oddity - hex string actually gets passed as a base64 string - String hexUTF8AsBase64 = firstKey.getShare(); - String hexUTF8 = new String(Base64.decode(hexUTF8AsBase64), StandardCharsets.UTF_8); - byte[] encryptedShareBytes = AES256CBC.toByteArray(new BigInteger(hexUTF8, 16)); - BigInteger share = new BigInteger(1, aes256cbc.decrypt(Base64.encodeBytes(encryptedShareBytes))); - decryptedShares.add(new DecryptedShare(indexes[i], share)); - } catch (Exception e) { - e.printStackTrace(); - } - } - } - } - } catch (JsonSyntaxException e) { - continue; - } - } - } - if (predicateResolved.get()) return null; - List> allCombis = Utils.kCombinations(decryptedShares.size(), k); - for (List currentCombi : allCombis) { - List currentCombiSharesIndexes = new ArrayList<>(); - List currentCombiSharesValues = new ArrayList<>(); - for (int i = 0; i < decryptedShares.size(); i++) { - if (currentCombi.contains(i)) { - DecryptedShare decryptedShare = decryptedShares.get(i); - currentCombiSharesIndexes.add(decryptedShare.getIndex()); - currentCombiSharesValues.add(decryptedShare.getValue()); - } - } - BigInteger derivedPrivateKey = this.lagrangeInterpolation(currentCombiSharesValues.toArray(new BigInteger[0]), currentCombiSharesIndexes.toArray(new BigInteger[0])); - assert derivedPrivateKey != null; - ECKeyPair derivedECKeyPair = ECKeyPair.create(derivedPrivateKey); - String derivedPubKeyString = Utils.padLeft(derivedECKeyPair.getPublicKey().toString(16), '0', 128); - String derivedPubKeyX = derivedPubKeyString.substring(0, derivedPubKeyString.length() / 2); // this will be padded - String derivedPubKeyY = derivedPubKeyString.substring(derivedPubKeyString.length() / 2); // this will be padded - if (new BigInteger(derivedPubKeyX, 16).compareTo(new BigInteger(thresholdPubKey.getX(), 16)) == 0 && new BigInteger(derivedPubKeyY, 16).compareTo(new BigInteger(thresholdPubKey.getY(), 16)) == 0) { - privateKey = derivedPrivateKey; - break; - } - } - CompletableFuture response = new CompletableFuture<>(); - if (privateKey == null) { - response.completeExceptionally(new PredicateFailedException("could not derive private key")); - } else { - response.complete(privateKey); - } - return response; - } else { - CompletableFuture response = new CompletableFuture<>(); - response.completeExceptionally(new PredicateFailedException("could not get enough shares")); - return response; - } - } catch (Exception ex) { - CompletableFuture cfRes = new CompletableFuture<>(); - cfRes.completeExceptionally(new TorusException("Torus Internal Error", ex)); - return cfRes; - } - }).getCompletableFuture(); - } catch (Exception ex) { - CompletableFuture cfRes = new CompletableFuture<>(); - cfRes.completeExceptionally(new TorusException("Torus Internal Error", ex)); - return cfRes; - } - }).thenComposeAsync((privateKey) -> { - CompletableFuture cf = new CompletableFuture<>(); - if (privateKey == null) { - cf.completeExceptionally(new TorusException("could not get private key")); - return cf; - } - try { - ECKeyPair derivedECKeyPair = ECKeyPair.create(privateKey); - String derivedPubKeyString = Utils.padLeft(derivedECKeyPair.getPublicKey().toString(16), '0', 128); - String derivedPubKeyX = derivedPubKeyString.substring(0, derivedPubKeyString.length() / 2); - String derivedPubKeyY = derivedPubKeyString.substring(derivedPubKeyString.length() / 2); - BigInteger metadataNonce; - - if (this.options.isEnableOneKey()) { - GetOrSetNonceResult result = this.getNonce(privateKey).get(); - metadataNonce = new BigInteger(Utils.isEmpty(result.getNonce()) ? "0" : result.getNonce(), 16); - } else { - metadataNonce = this.getMetadata(new MetadataPubKey(derivedPubKeyX, derivedPubKeyY)).get(); - } - privateKey = privateKey.add(metadataNonce).mod(secp256k1N); - String ethAddress = this.generateAddressFromPrivKey(privateKey.toString(16)); - return CompletableFuture.completedFuture(new RetrieveSharesResponse(ethAddress, privateKey, metadataNonce)); - } catch (Exception ex) { - CompletableFuture cfRes = new CompletableFuture<>(); - cfRes.completeExceptionally(new TorusException("Torus Internal Error", ex)); - return cfRes; - } - }); - } catch (Exception e) { - e.printStackTrace(); - CompletableFuture cfRes = new CompletableFuture<>(); - cfRes.completeExceptionally(new TorusException("Torus Internal Error", e)); - return cfRes; + @SuppressWarnings("unused") + public static String getPostboxKey(TorusKey torusKey) { + if (torusKey.getMetadata().getTypeOfUser() == TypeOfUser.v1) { + return (torusKey.getFinalKeyData().getPrivKey() == null || torusKey.getFinalKeyData().getPrivKey().isEmpty()) ? torusKey.getoAuthKeyData().getPrivKey() : torusKey.getFinalKeyData().getPrivKey(); } + return torusKey.getoAuthKeyData().getPrivKey(); } - public CompletableFuture retrieveShares(String[] endpoints, BigInteger[] indexes, String verifier, HashMap verifierParams, String idToken) { - return this.retrieveShares(endpoints, indexes, verifier, verifierParams, idToken, null); + public TorusKey retrieveShares(@NotNull String[] endpoints, @NotNull String verifier, @NotNull VerifierParams verifierParams, @NotNull String idToken, @Nullable TorusUtilsExtraParams extraParams) throws Exception { + TorusUtilsExtraParams params = (extraParams == null) ? new TorusUtilsExtraParams() : extraParams; + if (params.session_token_exp_second == null) { + params.session_token_exp_second = this.sessionTime; + } + + return NodeUtils.retrieveOrImportShare(this.defaultHost, (options.serverTimeOffset == null) ? 0 : options.serverTimeOffset, this.options.enableOneKey, this.defaultHost, this.options.network, this.options.clientId, endpoints, verifier, verifierParams, idToken, null, this.apiKey, null, params); } - public CompletableFuture getMetadata(MetadataPubKey data) { - try { - Gson gson = new Gson(); - String metadata = gson.toJson(data, MetadataPubKey.class); - String metadataApiResponse = APIUtils.post(this.options.getMetadataHost() + "/get", metadata, true).get(); - MetadataResponse response = gson.fromJson(metadataApiResponse, MetadataResponse.class); - BigInteger finalResponse = new BigInteger(Utils.isEmpty(response.getMessage()) ? "0" : response.getMessage(), 16); - return CompletableFuture.supplyAsync(() -> finalResponse); - } catch (Exception e) { - e.printStackTrace(); - return CompletableFuture.supplyAsync(() -> new BigInteger("0")); - } + public TorusPublicKey getPublicAddress(@NotNull String[] endpoints, @NotNull String verifier, @NotNull String verifierId, @Nullable String extendedVerifierId) throws Exception { + return getNewPublicAddress(endpoints, verifier, verifierId, extendedVerifierId, getNetworkInfo(), this.options.enableOneKey); } - public MetadataParams generateMetadataParams(String message, BigInteger privateKey) { - long timeMillis = System.currentTimeMillis() / 1000L; - BigInteger timestamp = this.options.getServerTimeOffset().add(new BigInteger(String.valueOf(timeMillis))); - MetadataParams.MetadataSetData setData = new MetadataParams.MetadataSetData(message, timestamp.toString(16)); - ECKeyPair derivedECKeyPair = ECKeyPair.create(privateKey); - String derivedPubKeyString = Utils.padLeft(derivedECKeyPair.getPublicKey().toString(16), '0', 128); - String derivedPubKeyX = derivedPubKeyString.substring(0, derivedPubKeyString.length() / 2); - String derivedPubKeyY = derivedPubKeyString.substring(derivedPubKeyString.length() / 2); - if (!options.isLegacyNonce()) { - derivedPubKeyX = Utils.stripPaddingLeft(derivedPubKeyX, '0'); - derivedPubKeyY = Utils.stripPaddingLeft(derivedPubKeyY, '0'); + public TorusKey importPrivateKey( + @NotNull String[] endpoints, + @NotNull BigInteger[] nodeIndexes, + @NotNull TorusNodePub[] nodePubKeys, + @NotNull String verifier, + @NotNull VerifierParams verifierParams, + @NotNull String idToken, + @NotNull String newPrivateKey, + @Nullable TorusUtilsExtraParams extraParams + ) throws Exception { + TorusUtilsExtraParams params = (extraParams == null) ? new TorusUtilsExtraParams() : extraParams; + if (params.session_token_exp_second == null) { + params.session_token_exp_second = this.sessionTime; } - Gson gson = new Gson(); - String setDataString = gson.toJson(setData); - byte[] hashedData = Hash.sha3(setDataString.getBytes(StandardCharsets.UTF_8)); - ECDSASignature signature = derivedECKeyPair.sign(hashedData); - String sig = Utils.padLeft(signature.r.toString(16), '0', 64) + Utils.padLeft(signature.s.toString(16), '0', 64) + Utils.padLeft("", '0', 2); - byte[] sigBytes = AES256CBC.toByteArray(new BigInteger(sig, 16)); - String finalSig = new String(Base64.encodeBytesToBytes(sigBytes), StandardCharsets.UTF_8); - return new MetadataParams(derivedPubKeyX, derivedPubKeyY, setData, finalSig); - } - public String generateAddressFromPrivKey(String privateKey) { - BigInteger privKey = new BigInteger(privateKey, 16); - return Keys.toChecksumAddress(Keys.getAddress(ECKeyPair.create(privKey.toByteArray()))); + if (endpoints.length != nodeIndexes.length) { + throw TorusUtilError.RUNTIME_ERROR("Length of endpoints must be the same as length of nodeIndexes"); + } + + List shares = KeyUtils.generateShares(this.keyType, (options.serverTimeOffset == null) ? 0 : options.serverTimeOffset, Arrays.asList(nodeIndexes), Arrays.asList(nodePubKeys), newPrivateKey); + + return NodeUtils.retrieveOrImportShare(this.defaultHost, this.options.serverTimeOffset, this.options.enableOneKey, this.defaultHost, this.options.network, this.options.clientId, endpoints, verifier, verifierParams, idToken, shares.toArray(new ImportedShare[0]), this.apiKey, newPrivateKey, params); } - public String generateAddressFromPubKey(BigInteger pubKeyX, BigInteger pubKeyY) { - ECNamedCurveParameterSpec curve = ECNamedCurveTable.getParameterSpec("secp256k1"); - ECPoint rawPoint = curve.getCurve().createPoint(pubKeyX, pubKeyY); - String finalPubKey = Utils.padLeft(rawPoint.getAffineXCoord().toString(), '0', 64) + Utils.padLeft(rawPoint.getAffineYCoord().toString(), '0', 64); - return Keys.toChecksumAddress(Hash.sha3(finalPubKey).substring(64 - 38)); + public TorusPublicKey getUserTypeAndAddress(@NotNull String[] endpoints, @NotNull String verifier, @NotNull String verifierId, @Nullable String extendedVerifierId) throws Exception { + return getNewPublicAddress(endpoints, verifier, verifierId, extendedVerifierId, getNetworkInfo(), true); } - CompletableFuture _getPublicAddress(String[] endpoints, TorusNodePub[] torusNodePubs, VerifierArgs verifierArgs, boolean isExtended) { - AtomicBoolean isNewKey = new AtomicBoolean(false); - Gson gson = new Gson(); - return Utils.keyLookup(endpoints, verifierArgs.getVerifier(), verifierArgs.getVerifierId()).thenComposeAsync(keyLookupResult -> { - if (keyLookupResult.getErrResult() != null && keyLookupResult.getErrResult().contains("Verifier not supported")) { - CompletableFuture lookupCf = new CompletableFuture<>(); - lookupCf.completeExceptionally(new Exception("Verifier not supported. Check if you: \\n\n" + " 1. Are on the right network (Torus testnet/mainnet) \\n\n" + " 2. Have setup a verifier on dashboard.web3auth.io?")); - return lookupCf; - } else if (keyLookupResult.getErrResult() != null && keyLookupResult.getErrResult().contains("Verifier + VerifierID has not yet been assigned")) { - return Utils.keyAssign(endpoints, torusNodePubs, null, null, verifierArgs.getVerifier(), verifierArgs.getVerifierId(), this.options.getSignerHost(), this.options.getNetwork()).thenComposeAsync(k -> Utils.waitKeyLookup(endpoints, verifierArgs.getVerifier(), verifierArgs.getVerifierId(), 1000)).thenComposeAsync(res -> { - CompletableFuture lookupCf = new CompletableFuture<>(); - try { - if (res == null || res.getKeyResult() == null) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no results")); - return lookupCf; - } - VerifierLookupRequestResult verifierLookupRequestResult = gson.fromJson(res.getKeyResult(), VerifierLookupRequestResult.class); - if (verifierLookupRequestResult == null || verifierLookupRequestResult.getKeys() == null || verifierLookupRequestResult.getKeys().length == 0) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no keys" + res.getKeyResult() + res.getErrResult())); - return lookupCf; - } - VerifierLookupItem verifierLookupItem = verifierLookupRequestResult.getKeys()[0]; - lookupCf.complete(verifierLookupItem); - isNewKey.set(true); - } catch (Exception ex) { - lookupCf.completeExceptionally(ex); - } - return lookupCf; - }); - } - CompletableFuture lookupCf = new CompletableFuture<>(); - try { - if (keyLookupResult.getKeyResult() != null) { - VerifierLookupRequestResult verifierLookupRequestResult = gson.fromJson(keyLookupResult.getKeyResult(), VerifierLookupRequestResult.class); - if (verifierLookupRequestResult == null || verifierLookupRequestResult.getKeys() == null || verifierLookupRequestResult.getKeys().length == 0) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no keys" + keyLookupResult.getKeyResult() + keyLookupResult.getErrResult())); - return lookupCf; - } - VerifierLookupItem verifierLookupItem = verifierLookupRequestResult.getKeys()[0]; - lookupCf.complete(verifierLookupItem); - return lookupCf; - } - lookupCf.completeExceptionally(new Exception("could not get lookup, no valid key result or error result")); - } catch (Exception ex) { - lookupCf.completeExceptionally(ex); - } - return lookupCf; - }).thenComposeAsync(verifierLookupItem -> { - CompletableFuture keyCf = new CompletableFuture<>(); - try { - GetOrSetNonceResult nonceResult = null; - BigInteger nonce; - ECPoint modifiedPubKey; - TypeOfUser typeOfUser; - GetOrSetNonceResult.PubNonce pubNonce = null; - ECNamedCurveParameterSpec curve = ECNamedCurveTable.getParameterSpec("secp256k1"); - if (this.options.isEnableOneKey()) { - try { - nonceResult = this.getOrSetNonce(verifierLookupItem.getPub_key_X(), verifierLookupItem.getPub_key_Y(), !isNewKey.get()).get(); - nonce = new BigInteger(Utils.isEmpty(nonceResult.getNonce()) ? "0" : nonceResult.getNonce(), 16); - typeOfUser = nonceResult.getTypeOfUser(); - } catch (Exception e) { - // Sometimes we take special action if `get or set nonce` api is not available - keyCf.completeExceptionally(new GetOrSetNonceError(e)); - return keyCf; - } - - modifiedPubKey = curve.getCurve().createPoint(new BigInteger(verifierLookupItem.getPub_key_X(), 16), new BigInteger(verifierLookupItem.getPub_key_Y(), 16)); - if (nonceResult.getTypeOfUser() == TypeOfUser.v1) { - modifiedPubKey = modifiedPubKey.add(curve.getG().multiply(nonce)).normalize(); - } else if (nonceResult.getTypeOfUser() == TypeOfUser.v2) { - if (!nonceResult.isUpgraded()) { - assert nonceResult.getPubNonce() != null; - ECPoint oneKeyMetadataPoint = curve.getCurve().createPoint(new BigInteger(nonceResult.getPubNonce().getX(), 16), new BigInteger(nonceResult.getPubNonce().getY(), 16)); - modifiedPubKey = modifiedPubKey.add(oneKeyMetadataPoint).normalize(); - pubNonce = nonceResult.getPubNonce(); - } - } else { - keyCf.completeExceptionally(new Exception("getOrSetNonce should always return typeOfUser.")); - return keyCf; - } - } else { - typeOfUser = TypeOfUser.v1; - nonce = this.getMetadata(new MetadataPubKey(verifierLookupItem.getPub_key_X(), verifierLookupItem.getPub_key_Y())).get(); - modifiedPubKey = curve.getCurve().createPoint(new BigInteger(verifierLookupItem.getPub_key_X(), 16), new BigInteger(verifierLookupItem.getPub_key_Y(), 16)); - modifiedPubKey = modifiedPubKey.add(curve.getG().multiply(nonce)).normalize(); - } - String finalPubKey = Utils.padLeft(modifiedPubKey.getAffineXCoord().toString(), '0', 64) + Utils.padLeft(modifiedPubKey.getAffineYCoord().toString(), '0', 64); - String address = Keys.toChecksumAddress(Hash.sha3(finalPubKey).substring(64 - 38)); - if (!isExtended) { - keyCf.complete(new TorusPublicKey(address)); - } else { - TorusPublicKey key = new TorusPublicKey(finalPubKey.substring(0, finalPubKey.length() / 2), finalPubKey.substring(finalPubKey.length() / 2), address); - key.setTypeOfUser(typeOfUser); - key.setMetadataNonce(nonce); - key.setPubNonce(pubNonce); - key.setUpgraded(nonceResult != null && nonceResult.isUpgraded()); - keyCf.complete(key); - } - return keyCf; - } catch (Exception ex) { - keyCf.completeExceptionally(ex); - return keyCf; + private TorusPublicKey getNewPublicAddress(@NotNull String[] endpoints, @NotNull String verifier, @NotNull String verifierId, @Nullable String extendedVerifierId, Web3AuthNetwork network, @NotNull Boolean enableOneKey) throws Exception { + KeyLookupResult keyAssignResult = NodeUtils.getPubKeyOrKeyAssign(endpoints, network, verifier, verifierId, this.defaultHost, this.options.serverTimeOffset, extendedVerifierId); + + JsonRPCErrorInfo errorResult = keyAssignResult.errorResult; + if (errorResult != null) { + if (errorResult.message.toLowerCase().contains("verifier not supported")) { + throw TorusUtilError.RUNTIME_ERROR("Verifier not supported. Check if you:\n1. Are on the right network (Torus testnet/mainnet)\n2. Have setup a verifier on dashboard.web3auth.io?"); + } else { + throw TorusUtilError.RUNTIME_ERROR(errorResult.message); } - }); - } + } - public CompletableFuture getPublicAddress(String[] endpoints, TorusNodePub[] torusNodePubs, VerifierArgs verifierArgs, boolean isExtended) { - return _getPublicAddress(endpoints, torusNodePubs, verifierArgs, isExtended); - } + KeyResult keyResult = keyAssignResult.keyResult; + if (keyResult == null || keyResult.keys.length == 0) { + throw TorusUtilError.RUNTIME_ERROR("node results do not match at first lookup"); + } - public CompletableFuture getPublicAddress(String[] endpoints, TorusNodePub[] torusNodePubs, VerifierArgs verifierArgs) { - return _getPublicAddress(endpoints, torusNodePubs, verifierArgs, false); - } + GetOrSetNonceResult nonceResult = keyAssignResult.nonceResult; + if (nonceResult == null && extendedVerifierId == null && !isLegacyNetorkRouteMap(network)) { + throw TorusUtilError.RUNTIME_ERROR("metadata nonce is missing in share response"); + } + + String pubKey = KeyUtils.getPublicKeyFromCoords(keyResult.keys[0].pub_key_X, keyResult.keys[0].pub_key_Y, false); + + PubNonce pubNonce = null; + BigInteger nonce; + if (nonceResult != null && nonceResult.nonce != null && !nonceResult.nonce.isEmpty()) { + nonce = new BigInteger(nonceResult.nonce); + } else { + nonce = BigInteger.ZERO; + } - private CompletableFuture _getOrSetNonce(MetadataParams data) { - Gson gson = new GsonBuilder().disableHtmlEscaping().create(); - String finalData = gson.toJson(data); - CompletableFuture cf = new CompletableFuture<>(); - APIUtils.post(this.options.getMetadataHost() + "/get_or_set_nonce", finalData, true).whenCompleteAsync((res, ex) -> { - if (ex != null) { - cf.completeExceptionally(ex); - return; + String oAuthPubKey; + String finalPubKey; + + Integer finalServerTimeOffset = (this.options.serverTimeOffset != null) ? this.options.serverTimeOffset : keyAssignResult.server_time_offset; + + if (extendedVerifierId != null) { + finalPubKey = pubKey; + oAuthPubKey = finalPubKey; + } else if (isLegacyNetorkRouteMap(network)) { + ArrayList legacyKeys = new ArrayList<>(); + for (VerifierKey i : keyAssignResult.keyResult.keys) { + legacyKeys.add(new LegacyVerifierKey(i.pub_key_X, i.pub_key_Y, i.address)); } - try { - GetOrSetNonceResult result = gson.fromJson(res, GetOrSetNonceResult.class); - cf.complete(result); - } catch (Exception ex2) { - cf.completeExceptionally(ex2); + LegacyVerifierLookupResponse verifierLegacyLookupItem = + new LegacyVerifierLookupResponse(legacyKeys.toArray(new LegacyVerifierKey[0]), finalServerTimeOffset.toString()); + return formatLegacyPublicKeyData(verifierLegacyLookupItem, enableOneKey, keyAssignResult.keyResult.is_new_key, finalServerTimeOffset); + } else { + String[] pubKeyCoords = KeyUtils.getPublicKeyCoords(pubKey); + String _X = pubKeyCoords[0]; + String _Y = pubKeyCoords[1]; + PubNonce finalPubNonce = null; + if (nonceResult != null && nonceResult.pubNonce != null) { + finalPubNonce = nonceResult.pubNonce; } - }); - return cf; - } - - public CompletableFuture getOrSetNonce(String X, String Y, boolean getOnly) { - String msg = getOnly ? "getNonce" : "getOrSetNonce"; - MetadataParams data = new MetadataParams(X, Y, new MetadataParams.MetadataSetData(msg, null), null); - return this._getOrSetNonce(data); - } + oAuthPubKey = KeyUtils.getPublicKeyFromCoords(_X, _Y, true); + finalPubKey = oAuthPubKey; + pubNonce = finalPubNonce; + if (pubNonce != null && !pubNonce.x.isEmpty() && !pubNonce.y.isEmpty()) { + String pubNonceKey = KeyUtils.getPublicKeyFromCoords(pubNonce.x, pubNonce.y, true); + finalPubKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(oAuthPubKey, pubNonceKey), false); + + } else { + throw TorusUtilError.METADATA_NONCE_MISSING; + } + } - public CompletableFuture getOrSetNonce(BigInteger privKey, boolean getOnly) { - String msg = getOnly ? "getNonce" : "getOrSetNonce"; - MetadataParams data = this.generateMetadataParams(msg, privKey); - return this._getOrSetNonce(data); - } + if (oAuthPubKey == null || finalPubKey == null) { + throw new Error("could not derive private key"); + } + String[] oAuthPubKeyCoords = KeyUtils.getPublicKeyCoords(oAuthPubKey); + String[] finalPubKeyCoords = KeyUtils.getPublicKeyCoords(finalPubKey); - public CompletableFuture getNonce(String X, String Y) { - return this.getOrSetNonce(X, Y, true); - } + String oAuthPubKeyX = oAuthPubKeyCoords[0]; + String oAuthPubKeyY = oAuthPubKeyCoords[1]; + String finalPubKeyX = finalPubKeyCoords[0]; + String finalPubKeyY = finalPubKeyCoords[1]; - public CompletableFuture getNonce(BigInteger privKey) { - return this.getOrSetNonce(privKey, true); - } + String oAuthAddress = KeyUtils.generateAddressFromPubKey(oAuthPubKeyX, oAuthPubKeyY); + String finalAddresss = KeyUtils.generateAddressFromPubKey(finalPubKeyX, finalPubKeyY); - public CompletableFuture getUserTypeAndAddress(String[] endpoints, TorusNodePub[] torusNodePubs, VerifierArgs verifierArgs) { - return this.getUserTypeAndAddress(endpoints, torusNodePubs, verifierArgs, false); + return new TorusPublicKey(new OAuthPubKeyData(oAuthAddress, oAuthPubKeyX, oAuthPubKeyY), + new FinalPubKeyData(finalAddresss, finalPubKeyX, finalPubKeyY), + new Metadata(pubNonce, nonce, TypeOfUser.v2, ((nonceResult != null) && (nonceResult.upgraded != null) && (nonceResult.upgraded)), finalServerTimeOffset), + new NodesData(keyAssignResult.nodeIndexes)); } - public CompletableFuture getUserTypeAndAddress(String[] endpoints, TorusNodePub[] torusNodePubs, VerifierArgs verifierArgs, boolean doesKeyAssign) { - AtomicBoolean isNewKey = new AtomicBoolean(false); - Gson gson = new Gson(); - return Utils.keyLookup(endpoints, verifierArgs.getVerifier(), verifierArgs.getVerifierId()).thenComposeAsync(keyLookupResult -> { - if (keyLookupResult.getErrResult() != null && keyLookupResult.getErrResult().contains("Verifier not supported")) { - CompletableFuture lookupCf = new CompletableFuture<>(); - lookupCf.completeExceptionally(new Exception("Verifier not supported. Check if you: \\n\n" + " 1. Are on the right network (Torus testnet/mainnet) \\n\n" + " 2. Have setup a verifier on dashboard.web3auth.io?")); - return lookupCf; - } else if (keyLookupResult.getErrResult() != null && keyLookupResult.getErrResult().contains("Verifier + VerifierID has not yet been assigned")) { - if (!doesKeyAssign) { - CompletableFuture lookupCf = new CompletableFuture<>(); - lookupCf.completeExceptionally(new Exception("Verifier + VerifierID has not yet been assigned")); - return lookupCf; + private TorusPublicKey formatLegacyPublicKeyData(@NotNull LegacyVerifierLookupResponse finalKeyResult, boolean enableOneKey, boolean isNewKey, + @NotNull Integer serverTimeOffset) throws Exception { + LegacyVerifierKey key = finalKeyResult.keys[0]; + String X = key.pub_key_X; + String Y = key.pub_key_Y; + GetOrSetNonceResult nonceResult = null; + String finalPubKey; + BigInteger nonce; + TypeOfUser typeOfUser; + PubNonce pubNonce = null; + + String oAuthPubKey = KeyUtils.getPublicKeyFromCoords(X, Y, true); + Integer finalServerTimeOffset = (this.options.serverTimeOffset == null) ? serverTimeOffset : this.options.serverTimeOffset; + + if (enableOneKey) { + nonceResult = MetadataUtils.getOrSetNonce(this.defaultHost, X, Y, finalServerTimeOffset, null, !isNewKey, null); + nonce = (nonceResult.nonce == null) ? BigInteger.ZERO : new BigInteger(nonceResult.nonce, 16); + typeOfUser = (nonceResult.typeOfUser == null) ? TypeOfUser.v1 : nonceResult.typeOfUser; + + if (typeOfUser == TypeOfUser.v1) { + finalPubKey = oAuthPubKey; + GetMetadataResponse metadataResponse = MetadataUtils.getMetadata(this.defaultHost, new GetMetadataParams(X, Y)); + nonce = new BigInteger(Common.isEmpty(metadataResponse.message) ? "0" : metadataResponse.message, 16); + + if (nonce.compareTo(BigInteger.ZERO) > 0) { + String noncePublicKey = KeyUtils.privateToPublic(nonce); + finalPubKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(finalPubKey, noncePublicKey), false); } - return Utils.keyAssign(endpoints, torusNodePubs, null, null, verifierArgs.getVerifier(), verifierArgs.getVerifierId(), this.options.getSignerHost(), this.options.getNetwork()).thenComposeAsync(k -> Utils.waitKeyLookup(endpoints, verifierArgs.getVerifier(), verifierArgs.getVerifierId(), 1000)).thenComposeAsync(res -> { - CompletableFuture lookupCf = new CompletableFuture<>(); - try { - if (res == null || res.getKeyResult() == null) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no results")); - return lookupCf; - } - VerifierLookupRequestResult verifierLookupRequestResult = gson.fromJson(res.getKeyResult(), VerifierLookupRequestResult.class); - if (verifierLookupRequestResult == null || verifierLookupRequestResult.getKeys() == null || verifierLookupRequestResult.getKeys().length == 0) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no keys" + res.getKeyResult() + res.getErrResult())); - return lookupCf; - } - VerifierLookupItem verifierLookupItem = verifierLookupRequestResult.getKeys()[0]; - lookupCf.complete(verifierLookupItem); - isNewKey.set(true); - } catch (Exception ex) { - lookupCf.completeExceptionally(ex); - } - return lookupCf; - }); - } - CompletableFuture lookupCf = new CompletableFuture<>(); - try { - if (keyLookupResult.getKeyResult() != null) { - VerifierLookupRequestResult verifierLookupRequestResult = gson.fromJson(keyLookupResult.getKeyResult(), VerifierLookupRequestResult.class); - if (verifierLookupRequestResult == null || verifierLookupRequestResult.getKeys() == null || verifierLookupRequestResult.getKeys().length == 0) { - lookupCf.completeExceptionally(new Exception("could not get lookup, no keys" + keyLookupResult.getKeyResult() + keyLookupResult.getErrResult())); - return lookupCf; - } - VerifierLookupItem verifierLookupItem = verifierLookupRequestResult.getKeys()[0]; - lookupCf.complete(verifierLookupItem); - return lookupCf; + } else if (typeOfUser == TypeOfUser.v2) { + if (nonceResult.pubNonce == null) { + throw TorusUtilError.RUNTIME_ERROR("getOrSetNonce should always return typeOfUser."); } - lookupCf.completeExceptionally(new Exception("could not get lookup, no valid key result or error result")); - } catch (Exception ex) { - lookupCf.completeExceptionally(ex); + String pubNonceKey = KeyUtils.getPublicKeyFromCoords(nonceResult.pubNonce.x, nonceResult.pubNonce.y, true); + finalPubKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(oAuthPubKey, pubNonceKey), false); + pubNonce = nonceResult.pubNonce; + } else { + throw TorusUtilError.RUNTIME_ERROR("getOrSetNonce should always return typeOfUser."); } - return lookupCf; - }).thenComposeAsync(verifierLookupItem -> { - CompletableFuture keyCf = new CompletableFuture<>(); - try { - GetOrSetNonceResult nonceResult; - BigInteger nonce; - ECPoint modifiedPubKey; - TypeOfUser typeOfUser; - GetOrSetNonceResult.PubNonce pubNonce = null; - ECNamedCurveParameterSpec curve = ECNamedCurveTable.getParameterSpec("secp256k1"); - - try { - nonceResult = this.getOrSetNonce(verifierLookupItem.getPub_key_X(), verifierLookupItem.getPub_key_Y(), !isNewKey.get()).get(); - nonce = new BigInteger(Utils.isEmpty(nonceResult.getNonce()) ? "0" : nonceResult.getNonce(), 16); - typeOfUser = nonceResult.getTypeOfUser(); - } catch (Exception e) { - // Sometimes we take special action if `get or set nonce` api is not available - keyCf.completeExceptionally(new GetOrSetNonceError(e)); - return keyCf; - } + } else { + typeOfUser = TypeOfUser.v1; + finalPubKey = oAuthPubKey; + GetMetadataResponse metadataResponse = MetadataUtils.getMetadata(this.defaultHost, new GetMetadataParams(X, Y)); + nonce = new BigInteger(Common.isEmpty(metadataResponse.message) ? "0" : metadataResponse.message, 16); + if (nonce.compareTo(BigInteger.ZERO) > 0) { + String noncePublicKey = KeyUtils.privateToPublic(nonce); + finalPubKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(finalPubKey, noncePublicKey), false); + } + } - modifiedPubKey = curve.getCurve().createPoint(new BigInteger(verifierLookupItem.getPub_key_X(), 16), new BigInteger(verifierLookupItem.getPub_key_Y(), 16)); - if (nonceResult.getTypeOfUser() == TypeOfUser.v1) { - modifiedPubKey = modifiedPubKey.add(curve.getG().multiply(nonce)).normalize(); - } else if (nonceResult.getTypeOfUser() == TypeOfUser.v2) { - // pubNonce is never deleted, so we can use it to always get the tkey - assert nonceResult.getPubNonce() != null; - ECPoint oneKeyMetadataPoint = curve.getCurve().createPoint(new BigInteger(nonceResult.getPubNonce().getX(), 16), new BigInteger(nonceResult.getPubNonce().getY(), 16)); - modifiedPubKey = modifiedPubKey.add(oneKeyMetadataPoint).normalize(); - pubNonce = nonceResult.getPubNonce(); - } else { - keyCf.completeExceptionally(new Exception("getOrSetNonce should always return typeOfUser.")); - return keyCf; - } + String oAuthAddress = KeyUtils.generateAddressFromPubKey(Common.padLeft(X, '0', 64), Common.padLeft(Y, '0', 64)); - String finalPubKey = Utils.padLeft(modifiedPubKey.getAffineXCoord().toString(), '0', 64) + Utils.padLeft(modifiedPubKey.getAffineYCoord().toString(), '0', 64); - String address = Keys.toChecksumAddress(Hash.sha3(finalPubKey).substring(64 - 38)); + if (typeOfUser == TypeOfUser.v2 && finalPubKey == null) { + throw TorusUtilError.PRIVATE_KEY_DERIVE_FAILED; + } - TorusPublicKey key = new TorusPublicKey(finalPubKey.substring(0, finalPubKey.length() / 2), finalPubKey.substring(finalPubKey.length() / 2), address); - key.setTypeOfUser(typeOfUser); - key.setMetadataNonce(nonce); - key.setPubNonce(pubNonce); - key.setUpgraded(nonceResult.isUpgraded()); - keyCf.complete(key); + String[] finalPubKeyCoords = KeyUtils.getPublicKeyCoords(finalPubKey); + String finalAddress = KeyUtils.generateAddressFromPubKey(finalPubKeyCoords[0], finalPubKeyCoords[1]); - return keyCf; - } catch (Exception ex) { - keyCf.completeExceptionally(ex); - return keyCf; - } - }); + return new TorusPublicKey(new OAuthPubKeyData(oAuthAddress, Common.padLeft(X, '0', 64), Common.padLeft(Y, '0', 64)), + new FinalPubKeyData(finalAddress, finalPubKeyCoords[0], finalPubKeyCoords[1]), + new Metadata(pubNonce, nonce, typeOfUser, (nonceResult != null && nonceResult.upgraded != null) ? nonceResult.upgraded : false, serverTimeOffset), + new NodesData(new ArrayList<>())); } + private Web3AuthNetwork getNetworkInfo() { + return this.options.network; + } } diff --git a/src/main/java/org/torusresearch/torusutils/apis/APIUtils.java b/src/main/java/org/torusresearch/torusutils/apis/APIUtils.java index d0ce906..56c267f 100644 --- a/src/main/java/org/torusresearch/torusutils/apis/APIUtils.java +++ b/src/main/java/org/torusresearch/torusutils/apis/APIUtils.java @@ -3,7 +3,7 @@ import com.google.gson.Gson; import org.jetbrains.annotations.NotNull; -import org.torusresearch.torusutils.helpers.Utils; +import org.torusresearch.torusutils.helpers.Common; import java.io.IOException; import java.util.Objects; @@ -29,7 +29,7 @@ public class APIUtils { .connectionPool(new ConnectionPool(64, 5, TimeUnit.SECONDS)) .dispatcher(createDispatcher()) .build(); - private static String apiKey; + public static String apiKey; private static Dispatcher createDispatcher() { final Dispatcher dispatcher = new Dispatcher(Executors.newCachedThreadPool()); @@ -41,17 +41,13 @@ private static Dispatcher createDispatcher() { private APIUtils() { } - public static String getApiKey() { - return apiKey; - } - public static void setApiKey(String apiKey) { APIUtils.apiKey = apiKey; } public static String generateJsonRPCObject(String method, Object params) { Gson gson = new Gson(); - return gson.toJson(new JsonRPCCall(method, params)); + return gson.toJson(new JsonRPCRequest(method, params)); } public static Callback toCallback(CompletableFuture future) { @@ -77,6 +73,7 @@ public static CompletableFuture post(String url, String data, Boolean us return _post(url, data, new Header[0], useApiKey); } + @SuppressWarnings("unused") public static CompletableFuture post(String url, String data, Header[] headers, Boolean useApiKey) { return _post(url, data, headers, useApiKey); } @@ -97,7 +94,7 @@ private static CompletableFuture _post(String url, String data, Header[] for (Header header : headers) { requestBuilder.addHeader(header.name.utf8(), header.value.utf8()); } - if (useApiKey && !Utils.isEmpty(apiKey)) + if (useApiKey && !Common.isEmpty(apiKey)) requestBuilder.addHeader("x-api-key", apiKey); Request request = requestBuilder.build(); CompletableFuture future = new CompletableFuture<>(); @@ -110,7 +107,7 @@ private static CompletableFuture _get(String url, Header[] headers, Bool for (Header header : headers) { requestBuilder.addHeader(header.name.utf8(), header.value.utf8()); } - if (useApiKey && !Utils.isEmpty(apiKey)) + if (useApiKey && !Common.isEmpty(apiKey)) requestBuilder.addHeader("x-api-key", apiKey); Request request = requestBuilder.build(); CompletableFuture future = new CompletableFuture<>(); diff --git a/src/main/java/org/torusresearch/torusutils/apis/CommitmentRequestParams.java b/src/main/java/org/torusresearch/torusutils/apis/CommitmentRequestParams.java deleted file mode 100644 index 18d224b..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/CommitmentRequestParams.java +++ /dev/null @@ -1,43 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class CommitmentRequestParams { - private final String messageprefix; - private final String tokencommitment; - private final String temppubx; - private final String temppuby; - private final String timestamp; - private final String verifieridentifier; - - public CommitmentRequestParams(String _messageprefix, String _tokencommitment, String _temppubx, String _temppuby, String _timestamp, String _verifieridentifier) { - messageprefix = _messageprefix; - tokencommitment = _tokencommitment; - temppubx = _temppubx; - temppuby = _temppuby; - timestamp = _timestamp; - verifieridentifier = _verifieridentifier; - } - - public String getMessageprefix() { - return messageprefix; - } - - public String getTokencommitment() { - return tokencommitment; - } - - public String getTemppubx() { - return temppubx; - } - - public String getTemppuby() { - return temppuby; - } - - public String getTimestamp() { - return timestamp; - } - - public String getVerifieridentifier() { - return verifieridentifier; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCCall.java b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCCall.java deleted file mode 100644 index 9596413..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCCall.java +++ /dev/null @@ -1,33 +0,0 @@ -package org.torusresearch.torusutils.apis; - -import java.util.Random; - -public class JsonRPCCall { - private final String jsonrpc; - private final String method; - private final Integer id; - private final Object params; - - public JsonRPCCall(String _method, Object _params) { - jsonrpc = "2.0"; - method = _method; - id = new Random().nextInt(1000); - params = _params; - } - - public String getJsonrpc() { - return jsonrpc; - } - - public String getMethod() { - return method; - } - - public Integer getId() { - return id; - } - - public Object getParams() { - return params; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCError.java b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCError.java deleted file mode 100644 index 8eb4b78..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCError.java +++ /dev/null @@ -1,26 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class JsonRPCError { - private final int code; - private final String message; - private final String data; - - public int getCode() { - return code; - } - - public String getMessage() { - return message; - } - - public String getData() { - return data; - } - - public JsonRPCError(int _code, String _message, String _data) { - code = _code; - message = _message; - data = _data; - } - -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCErrorInfo.java b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCErrorInfo.java new file mode 100644 index 0000000..46e7866 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCErrorInfo.java @@ -0,0 +1,13 @@ +package org.torusresearch.torusutils.apis; + +public class JsonRPCErrorInfo { + public final int code; + public final String message; + public final Object data; + + public JsonRPCErrorInfo(int code, String message, Object data) { + this.code = code; + this.message = message; + this.data = data; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCRequest.java b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCRequest.java new file mode 100644 index 0000000..87f47f0 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCRequest.java @@ -0,0 +1,17 @@ +package org.torusresearch.torusutils.apis; + +import java.util.Random; + +public class JsonRPCRequest { + public final String jsonrpc; + public final String method; + public final Integer id; + public final Object params; + + public JsonRPCRequest(String _method, Object _params) { + jsonrpc = "2.0"; + method = _method; + id = new Random().nextInt(1000); + params = _params; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCResponse.java b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCResponse.java index a5c0bdf..d140245 100644 --- a/src/main/java/org/torusresearch/torusutils/apis/JsonRPCResponse.java +++ b/src/main/java/org/torusresearch/torusutils/apis/JsonRPCResponse.java @@ -1,20 +1,25 @@ package org.torusresearch.torusutils.apis; +import com.google.gson.Gson; +import com.google.gson.reflect.TypeToken; -public class JsonRPCResponse { - private final JsonRPCError error; - private final Object result; +public class JsonRPCResponse { - public JsonRPCError getError() { + private final JsonRPCErrorInfo error; + public final T result; + + + public JsonRPCErrorInfo getError() { return error; } - public Object getResult() { - return result; + public T getTypedResult(Class clazz) { + Gson gson = new Gson(); + return gson.fromJson(gson.toJson(result), TypeToken.get(clazz).getType()); } - public JsonRPCResponse(JsonRPCError _error, Object _result) { - error = _error; - result = _result; + public JsonRPCResponse(JsonRPCErrorInfo error, T result) { + this.error = error; + this.result = result; } } diff --git a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignParams.java b/src/main/java/org/torusresearch/torusutils/apis/KeyAssignParams.java deleted file mode 100644 index ff4c114..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignParams.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class KeyAssignParams { - private final String verifier; - private final String verifier_id; - - public String getVerifier() { - return verifier; - } - - public String getVerifier_id() { - return verifier_id; - } - - public KeyAssignParams(String _verifier, String _verifier_id) { - verifier = _verifier; - verifier_id = _verifier_id; - } - -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignResult.java b/src/main/java/org/torusresearch/torusutils/apis/KeyAssignResult.java deleted file mode 100644 index 904861f..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignResult.java +++ /dev/null @@ -1,8 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class KeyAssignResult { - private KeyAssignment[] keys; - public KeyAssignment[] getKeys() { - return keys; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignment.java b/src/main/java/org/torusresearch/torusutils/apis/KeyAssignment.java deleted file mode 100644 index c300a12..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/KeyAssignment.java +++ /dev/null @@ -1,36 +0,0 @@ -package org.torusresearch.torusutils.apis; - -import java.util.HashMap; - -public class KeyAssignment { - private String Index; - private PubKey PublicKey; - private Integer Threshold; - private HashMap Verifiers; - private String Share; - private ShareMetadata Metadata; - - public String getIndex() { - return Index; - } - - public PubKey getPublicKey() { - return PublicKey; - } - - public Integer getThreshold() { - return Threshold; - } - - public HashMap getVerifiers() { - return Verifiers; - } - - public String getShare() { - return Share; - } - - public ShareMetadata getMetadata() { - return Metadata; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/KeyLookupResult.java b/src/main/java/org/torusresearch/torusutils/apis/KeyLookupResult.java deleted file mode 100644 index 908abc3..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/KeyLookupResult.java +++ /dev/null @@ -1,20 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class KeyLookupResult { - private final String keyResult; - private final String errResult; - - public String getKeyResult() { - return keyResult; - } - - public String getErrResult() { - return errResult; - } - - public KeyLookupResult(String _keyResult, String _errResult) { - keyResult = _keyResult; - errResult = _errResult; - } - -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/NodeSignature.java b/src/main/java/org/torusresearch/torusutils/apis/NodeSignature.java deleted file mode 100644 index 1002f37..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/NodeSignature.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class NodeSignature { - private final String signature; - private final String data; - private final String nodepubx; - private final String nodepuby; - - public NodeSignature(String _signature, String _data, String _nodepubx, String _nodepuby) { - signature = _signature; - data = _data; - nodepubx = _nodepubx; - nodepuby = _nodepuby; - } - - public String getSignature() { - return signature; - } - - public String getData() { - return data; - } - - public String getNodepubx() { - return nodepubx; - } - - public String getNodepuby() { - return nodepuby; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/PubKey.java b/src/main/java/org/torusresearch/torusutils/apis/PubKey.java index c8f9c29..500716d 100644 --- a/src/main/java/org/torusresearch/torusutils/apis/PubKey.java +++ b/src/main/java/org/torusresearch/torusutils/apis/PubKey.java @@ -1,12 +1,14 @@ package org.torusresearch.torusutils.apis; +import org.jetbrains.annotations.NotNull; + public class PubKey { private final String X; private final String Y; - public PubKey(String _X, String _Y) { - X = _X; - Y = _Y; + public PubKey(@NotNull String X, @NotNull String Y) { + this.X = X; + this.Y = Y; } public String getX() { diff --git a/src/main/java/org/torusresearch/torusutils/apis/ShareMetadata.java b/src/main/java/org/torusresearch/torusutils/apis/ShareMetadata.java deleted file mode 100644 index 2af6af5..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/ShareMetadata.java +++ /dev/null @@ -1,24 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class ShareMetadata { - private String iv; - private String ephemPublicKey; - private String mac; - private String mode; - - public String getIv() { - return iv; - } - - public String getEphemPublicKey() { - return ephemPublicKey; - } - - public String getMac() { - return mac; - } - - public String getMode() { - return mode; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/ShareRequestParams.java b/src/main/java/org/torusresearch/torusutils/apis/ShareRequestParams.java deleted file mode 100644 index b9d8715..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/ShareRequestParams.java +++ /dev/null @@ -1,18 +0,0 @@ -package org.torusresearch.torusutils.apis; - -import java.util.HashMap; -import java.util.List; - -public class ShareRequestParams { - private final String encrypted; - private final List> item; - - public ShareRequestParams(List> _item) { - encrypted = "yes"; - item = _item; - } - - public List> getItem() { - return item; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/SignerResponse.java b/src/main/java/org/torusresearch/torusutils/apis/SignerResponse.java deleted file mode 100644 index ff120db..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/SignerResponse.java +++ /dev/null @@ -1,30 +0,0 @@ -package org.torusresearch.torusutils.apis; - -import com.google.gson.annotations.SerializedName; - -public class SignerResponse { - @SerializedName("torus-timestamp") - private final String torus_timestamp; - @SerializedName("torus-nonce") - private final String torus_nonce; - @SerializedName("torus-signature") - private final String torus_signature; - - public SignerResponse(String _torus_timestamp, String _torus_nonce, String _torus_signature) { - torus_timestamp = _torus_timestamp; - torus_nonce = _torus_nonce; - torus_signature = _torus_signature; - } - - public String getTorus_timestamp() { - return torus_timestamp; - } - - public String getTorus_nonce() { - return torus_nonce; - } - - public String getTorus_signature() { - return torus_signature; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupItem.java b/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupItem.java deleted file mode 100644 index 4c1ae64..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupItem.java +++ /dev/null @@ -1,32 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class VerifierLookupItem { - private final String key_index; - private final String pub_key_X; - private final String pub_key_Y; - private final String address; - - public String getKey_index() { - return key_index; - } - - public String getPub_key_X() { - return pub_key_X; - } - - public String getPub_key_Y() { - return pub_key_Y; - } - - public String getAddress() { - return address; - } - - public VerifierLookupItem(String _key_index, String _pub_key_X, String _pub_key_Y, String _address) { - key_index = _key_index; - pub_key_X = _pub_key_X; - pub_key_Y = _pub_key_Y; - address = _address; - } - -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestParams.java b/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestParams.java deleted file mode 100644 index 0b1b200..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestParams.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class VerifierLookupRequestParams { - private final String verifier; - private final String verifier_id; - - public VerifierLookupRequestParams(String _verifier, String _verifier_id) { - verifier = _verifier; - verifier_id = _verifier_id; - } - - public String getVerifier() { - return verifier; - } - - public String getVerifier_id() { - return verifier_id; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestResult.java b/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestResult.java deleted file mode 100644 index 85a7b96..0000000 --- a/src/main/java/org/torusresearch/torusutils/apis/VerifierLookupRequestResult.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.torusresearch.torusutils.apis; - -public class VerifierLookupRequestResult { - private final VerifierLookupItem[] keys; - public VerifierLookupRequestResult(VerifierLookupItem[] _keys) { - keys = _keys; - } - public VerifierLookupItem[] getKeys() { - return keys; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/CommitmentRequestParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/CommitmentRequestParams.java new file mode 100644 index 0000000..7bc64d1 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/CommitmentRequestParams.java @@ -0,0 +1,22 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class CommitmentRequestParams { + public final String messageprefix; + public final String tokencommitment; + public final String temppubx; + public final String temppuby; + public final String timestamp; + public final String verifieridentifier; + + public CommitmentRequestParams(@NotNull String messageprefix, @NotNull String tokencommitment, @NotNull String temppubx, @NotNull String temppuby, @Nullable String timestamp, @NotNull String verifieridentifier) { + this.messageprefix = messageprefix; + this.tokencommitment = tokencommitment; + this.temppubx = temppubx; + this.temppuby = temppuby; + this.timestamp = timestamp; + this.verifieridentifier = verifieridentifier; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/GetMetadataParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/GetMetadataParams.java new file mode 100644 index 0000000..09ca111 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/GetMetadataParams.java @@ -0,0 +1,13 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; + +public class GetMetadataParams { + public final String pub_key_X; + public final String pub_key_Y; + + public GetMetadataParams(@NotNull String pub_key_X, @NotNull String pub_key_Y) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceParams.java new file mode 100644 index 0000000..b234ec0 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceParams.java @@ -0,0 +1,15 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; + +public class GetNonceParams { + public final String pub_key_X; + public final String pub_key_Y; + public final GetNonceSetDataParams set_data; + + public GetNonceParams(@NotNull String pub_key_X, @NotNull String pub_key_Y, @NotNull GetNonceSetDataParams set_data) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + this.set_data = set_data; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceSetDataParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceSetDataParams.java new file mode 100644 index 0000000..dc6785e --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/GetNonceSetDataParams.java @@ -0,0 +1,11 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; + +public class GetNonceSetDataParams { + public final String data; + + public GetNonceSetDataParams(@NotNull String data) { + this.data = data; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/GetOrSetKeyParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/GetOrSetKeyParams.java new file mode 100644 index 0000000..12b1a02 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/GetOrSetKeyParams.java @@ -0,0 +1,26 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class GetOrSetKeyParams { + public final Boolean distributed_metadata; + public final String verifier; + public final String verifier_id; + @Nullable + public final String extended_verifier_id; + public final Boolean one_key_flow; + public final Boolean fetch_node_index; + @Nullable + public final String client_time; + + public GetOrSetKeyParams(@NotNull Boolean distributed_metadata, @NotNull String verifier, @NotNull String verifier_id, @Nullable String extended_verifier_id, @NotNull Boolean one_key_flow, @NotNull Boolean fetch_node_index, @Nullable String client_time) { + this.distributed_metadata = distributed_metadata; + this.verifier = verifier; + this.verifier_id = verifier_id; + this.extended_verifier_id = extended_verifier_id; + this.one_key_flow = one_key_flow; + this.fetch_node_index = fetch_node_index; + this.client_time = client_time; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/MetadataParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/MetadataParams.java new file mode 100644 index 0000000..ac26920 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/MetadataParams.java @@ -0,0 +1,25 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.types.common.TorusKeyType; + +public class MetadataParams { + @Nullable + public final String namespace; + public final String pub_key_X; + public final String pub_key_Y; + @Nullable + public final TorusKeyType key_type; + public final SetData set_data; + public final String signature; + + public MetadataParams(@NotNull String pub_key_X, @NotNull String pub_key_Y, @NotNull SetData set_data, @NotNull String signature, @Nullable String namespace, @Nullable TorusKeyType key_type) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + this.set_data = set_data; + this.signature = signature; + this.key_type = key_type; + this.namespace = namespace; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/NonceMetadataParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/NonceMetadataParams.java new file mode 100644 index 0000000..a2e8c10 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/NonceMetadataParams.java @@ -0,0 +1,32 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.types.common.TorusKeyType; + +public class NonceMetadataParams { + @Nullable + public final String namespace; + public final String pub_key_X; + public final String pub_key_Y; + public final SetNonceData set_data; + @Nullable + public final TorusKeyType keyType; + + public final String signature; + public final String encodedData; + @Nullable + public final String seed; + + public NonceMetadataParams(@NotNull String pub_key_X, @NotNull String pub_key_Y, @NotNull SetNonceData set_data, @NotNull String encodedData, @NotNull String signature, @Nullable String namespace, @Nullable TorusKeyType keyType, @Nullable String seed) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + this.set_data = set_data; + this.encodedData = encodedData; + this.signature = signature; + this.namespace = namespace; + this.keyType = keyType; + this.seed = seed; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/SetData.java b/src/main/java/org/torusresearch/torusutils/apis/requests/SetData.java new file mode 100644 index 0000000..fef2b76 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/SetData.java @@ -0,0 +1,13 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; + +public class SetData { + public final String data; + public final String timestamp; + + public SetData(@NotNull String data, @NotNull String timestamp) { + this.data = data; + this.timestamp = timestamp; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/SetNonceData.java b/src/main/java/org/torusresearch/torusutils/apis/requests/SetNonceData.java new file mode 100644 index 0000000..74b8722 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/SetNonceData.java @@ -0,0 +1,23 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.Nullable; + +public class SetNonceData { + @Nullable + public final String data; + @Nullable + public final String operation; + + public String seed = ""; + @Nullable + public final String timestamp; + + public SetNonceData(@Nullable String operation, @Nullable String data, @Nullable String seed, @Nullable String timestamp) { + this.data = data; + this.operation = operation; + if (seed != null) { + this.seed = seed; + } + this.timestamp = timestamp; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestItem.java b/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestItem.java new file mode 100644 index 0000000..077115b --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestItem.java @@ -0,0 +1,104 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.apis.responses.CommitmentRequestResult; +import org.torusresearch.torusutils.types.TorusUtilsExtraParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.TorusKeyType; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; + +public class ShareRequestItem { + public final String verifieridentifier; + @Nullable + public final String verifier_id; + @Nullable + public final String extended_verifier_id; + public final String idtoken; + @Nullable + public final CommitmentRequestResult[] nodesignatures; + @Nullable + public final String pub_key_x; + @Nullable + public final String pub_key_y; + @Nullable + public final String signing_pub_key_x; + @Nullable + public final String signing_pub_key_y; + @Nullable + public final String encrypted_share; + @Nullable + public final EciesHexOmitCipherText encrypted_share_metadata; + @Nullable + public final Integer node_index; + @Nullable + public final TorusKeyType key_type; + @Nullable + public final String nonce_data; + @Nullable + public final String nonce_signature; + @Nullable + public final String[] sub_verifier_ids; + public Integer session_token_exp_second = 86400; + @Nullable + public final VerifyParams[] verify_params; + @Nullable + public final String sss_endpoint; + @Nullable + public final String nonce; + @Nullable + public final String message; + @Nullable + public final String signature; + @Nullable + public final String clientDataJson; + @Nullable + public final String authenticatorData; + @Nullable + public final String publicKey; + @Nullable + public final String challenge; + @Nullable + public final String rpOrigin; + @Nullable + public final String rpId; + @Nullable + public final Integer timestamp; + + public ShareRequestItem(@NotNull String verifieridentifier, @Nullable String verifier_id, @Nullable String extended_verifier_id, @NotNull String idtoken, @NotNull TorusUtilsExtraParams extraParams, @Nullable CommitmentRequestResult[] nodesignatures, @Nullable String pub_key_x, + @Nullable String pub_key_y, @Nullable String signing_pub_key_x, @Nullable String signing_pub_key_y, @Nullable String encrypted_share, @Nullable EciesHexOmitCipherText encrypted_share_metadata, @Nullable Integer node_index, + @Nullable TorusKeyType key_type, @Nullable String nonce_data, @Nullable String nonce_signature, @Nullable String[] sub_verifier_ids, @Nullable VerifyParams[] verifyParams, @Nullable String sss_endpoint ) { + this.verifieridentifier = verifieridentifier; + this.verifier_id = verifier_id; + this.extended_verifier_id = extended_verifier_id; + this.idtoken = idtoken; + this.nodesignatures = nodesignatures; + this.pub_key_x = pub_key_x; + this.pub_key_y = pub_key_y; + this.signing_pub_key_x = signing_pub_key_x; + this.signing_pub_key_y = signing_pub_key_y; + this.encrypted_share = encrypted_share; + this.encrypted_share_metadata = encrypted_share_metadata; + this.node_index = node_index; + this.key_type = key_type; + this.nonce_data = nonce_data; + this.nonce_signature = nonce_signature; + this.sub_verifier_ids = sub_verifier_ids; + if (extraParams.session_token_exp_second == null) { + this.session_token_exp_second = extraParams.session_token_exp_second; + } + this.verify_params = verifyParams; + this.sss_endpoint = sss_endpoint; + + this.nonce = extraParams.nonce; + this.message = extraParams.message; + this.signature = extraParams.signature; + this.clientDataJson = extraParams.clientDataJson; + this.authenticatorData = extraParams.authenticatorData; + this.publicKey = extraParams.publicKey; + this.challenge = extraParams.challenge; + this.rpOrigin = extraParams.rpOrigin; + this.rpId = extraParams.rpId; + this.timestamp = extraParams.timestamp; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestParams.java b/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestParams.java new file mode 100644 index 0000000..6c0ebc8 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/requests/ShareRequestParams.java @@ -0,0 +1,21 @@ +package org.torusresearch.torusutils.apis.requests; + +import org.jetbrains.annotations.NotNull; + +public class ShareRequestParams { + @SuppressWarnings("unused") + public final String encrypted = "yes"; + @SuppressWarnings("unused") + public final boolean one_key_flow = true; + @SuppressWarnings("unused") + public final boolean use_temp = true; + @SuppressWarnings("unused") + public final boolean distributed_metadata = true; + public final String client_time; + public final ShareRequestItem[] item; + + public ShareRequestParams(@NotNull ShareRequestItem[] item, @NotNull String client_time) { + this.item = item; + this.client_time = client_time; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/CommitmentRequestResult.java b/src/main/java/org/torusresearch/torusutils/apis/responses/CommitmentRequestResult.java new file mode 100644 index 0000000..81c5815 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/CommitmentRequestResult.java @@ -0,0 +1,19 @@ +package org.torusresearch.torusutils.apis.responses; + +import org.jetbrains.annotations.NotNull; + +public class CommitmentRequestResult { + public final String signature; + public final String data; + public final String nodepubx; + public final String nodepuby; + public final String nodeindex; + + public CommitmentRequestResult(@NotNull String data, @NotNull String nodepubx, @NotNull String nodepuby, @NotNull String signature, @NotNull String nodeindex) { + this.data = data; + this.nodeindex = nodeindex; + this.signature = signature; + this.nodepubx = nodepubx; + this.nodepuby = nodepuby; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/GetMetadataResponse.java b/src/main/java/org/torusresearch/torusutils/apis/responses/GetMetadataResponse.java new file mode 100644 index 0000000..fc29455 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/GetMetadataResponse.java @@ -0,0 +1,11 @@ +package org.torusresearch.torusutils.apis.responses; + +import org.jetbrains.annotations.NotNull; + +public class GetMetadataResponse { + public final String message; + + public GetMetadataResponse(@NotNull String message) { + this.message = message; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/GetOrSetNonceResult.java b/src/main/java/org/torusresearch/torusutils/apis/responses/GetOrSetNonceResult.java new file mode 100644 index 0000000..51866bb --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/GetOrSetNonceResult.java @@ -0,0 +1,27 @@ +package org.torusresearch.torusutils.apis.responses; + +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.types.common.TypeOfUser; +import org.torusresearch.torusutils.types.common.PubNonce; + +public class GetOrSetNonceResult { + @Nullable + public final TypeOfUser typeOfUser; + @Nullable + public String nonce; // This is not final as in one case the nonce needs to be removed. + + @Nullable + public final PubNonce pubNonce; + @Nullable + public final String ipfs; + @Nullable + public final Boolean upgraded; + + public GetOrSetNonceResult(@Nullable TypeOfUser typeOfUser, @Nullable String nonce, @Nullable PubNonce pubNonce, @Nullable String ipfs, @Nullable Boolean upgraded) { + this.typeOfUser = typeOfUser; + this.nonce = nonce; + this.pubNonce = pubNonce; + this.ipfs = ipfs; + this.upgraded = upgraded; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/KeyAssignment.java b/src/main/java/org/torusresearch/torusutils/apis/responses/KeyAssignment.java new file mode 100644 index 0000000..5857992 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/KeyAssignment.java @@ -0,0 +1,28 @@ +package org.torusresearch.torusutils.apis.responses; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.apis.PubKey; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; + +public class KeyAssignment { + public final String index; + public final PubKey public_key; + public final Integer threshold; + public final Integer node_index; + public final String share; // Note: This is in base64, must be decoded before decrypting + public final EciesHexOmitCipherText share_metadata; + @Nullable + public final GetOrSetNonceResult nonce_data; + + public KeyAssignment(@NotNull String index, @NotNull PubKey public_key, @NotNull Integer threshold, @NotNull Integer node_index, @NotNull String share, @NotNull EciesHexOmitCipherText share_metadata, @Nullable GetOrSetNonceResult nonce_data) { + this.index = index; + this.public_key = public_key; + this.threshold = threshold; + this.node_index = node_index; + this.share = share; + this.share_metadata = share_metadata; + this.nonce_data = nonce_data; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/ShareRequestResult.java b/src/main/java/org/torusresearch/torusutils/apis/responses/ShareRequestResult.java new file mode 100644 index 0000000..8085825 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/ShareRequestResult.java @@ -0,0 +1,35 @@ +package org.torusresearch.torusutils.apis.responses; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; + +public class ShareRequestResult { + public final KeyAssignment[] keys; + public final String[] session_tokens; + public final EciesHexOmitCipherText[] session_token_metadata; + public final String[] session_token_sigs; + public final EciesHexOmitCipherText[] session_token_sig_metadata; + public final String node_pubx; + public final String node_puby; + public final Boolean is_new_key; + public String server_time_offset; // this is not final, it is modified by code + + public ShareRequestResult(@NotNull KeyAssignment[] keys, @NotNull String[] session_tokens, @NotNull EciesHexOmitCipherText[] session_token_metadata, + @NotNull String[] session_token_sigs, @NotNull EciesHexOmitCipherText[] session_token_sig_metadata, @NotNull String node_pubx, + @NotNull String node_puby, @NotNull Boolean is_new_key, @Nullable String server_time_offset) { + this.keys = keys; + this.session_tokens = session_tokens; + this.session_token_metadata = session_token_metadata; + this.session_token_sigs = session_token_sigs; + this.session_token_sig_metadata = session_token_sig_metadata; + this.node_pubx = node_pubx; + this.node_puby = node_puby; + this.is_new_key = is_new_key; + if (server_time_offset == null) { + this.server_time_offset = "0"; + } else{ + this.server_time_offset = server_time_offset; + } + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierKey.java b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierKey.java new file mode 100644 index 0000000..f20ca13 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierKey.java @@ -0,0 +1,16 @@ +package org.torusresearch.torusutils.apis.responses.VerifierLookupResponse; + +import org.jetbrains.annotations.NotNull; + +public class LegacyVerifierKey { + public final String pub_key_X; + public final String pub_key_Y; + public final String address; + + public LegacyVerifierKey(@NotNull String pub_key_X, @NotNull String pub_key_Y, @NotNull String address) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + this.address = address; + + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierLookupResponse.java b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierLookupResponse.java new file mode 100644 index 0000000..38eaecc --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/LegacyVerifierLookupResponse.java @@ -0,0 +1,15 @@ +package org.torusresearch.torusutils.apis.responses.VerifierLookupResponse; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class LegacyVerifierLookupResponse { + public final LegacyVerifierKey[] keys; + @Nullable + public final String server_time_offset; + + public LegacyVerifierLookupResponse(@NotNull LegacyVerifierKey[] keys, @Nullable String server_time_offset) { + this.server_time_offset = server_time_offset; + this.keys = keys; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierKey.java b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierKey.java new file mode 100644 index 0000000..6635d18 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierKey.java @@ -0,0 +1,23 @@ +package org.torusresearch.torusutils.apis.responses.VerifierLookupResponse; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.apis.responses.GetOrSetNonceResult; + +public class VerifierKey { + public final String pub_key_X; + public final String pub_key_Y; + public final String address; + @Nullable + public final GetOrSetNonceResult nonce_data; + @Nullable + public final String created_at; + + public VerifierKey(@NotNull String pub_key_X, @NotNull String pub_key_Y, @NotNull String address, @Nullable GetOrSetNonceResult nonce_data, @Nullable String created_at) { + this.pub_key_X = pub_key_X; + this.pub_key_Y = pub_key_Y; + this.address = address; + this.nonce_data = nonce_data; + this.created_at = created_at; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierLookupResponse.java b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierLookupResponse.java new file mode 100644 index 0000000..4741234 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/apis/responses/VerifierLookupResponse/VerifierLookupResponse.java @@ -0,0 +1,21 @@ +package org.torusresearch.torusutils.apis.responses.VerifierLookupResponse; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class VerifierLookupResponse { + public final VerifierKey[] keys; + @Nullable + public final Boolean is_new_key; + + public final String node_index; + @Nullable + public final String server_time_offset; + + public VerifierLookupResponse(@NotNull VerifierKey[] keys, @Nullable Boolean is_new_key, @NotNull String node_index, @Nullable String server_time_offset) { + this.node_index = node_index; + this.keys = keys; + this.is_new_key = is_new_key; + this.server_time_offset = server_time_offset; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/AES256CBC.java b/src/main/java/org/torusresearch/torusutils/helpers/AES256CBC.java deleted file mode 100644 index 2c762ed..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/AES256CBC.java +++ /dev/null @@ -1,98 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import org.torusresearch.torusutils.types.TorusException; - -import javax.crypto.Cipher; -import javax.crypto.spec.IvParameterSpec; -import javax.crypto.spec.SecretKeySpec; -import java.math.BigInteger; -import java.security.Key; -import java.security.NoSuchAlgorithmException; -import java.security.spec.AlgorithmParameterSpec; -import java.security.spec.ECFieldFp; -import java.security.spec.EllipticCurve; -import java.util.Arrays; - -public class AES256CBC { - private static final String TRANSFORMATION = "AES/CBC/PKCS5Padding"; - private final byte[] AES_ENCRYPTION_KEY; - private final byte[] ENCRYPTION_IV; - - public AES256CBC(String privateKeyHex, String ephemPublicKeyHex, String encryptionIvHex) throws NoSuchAlgorithmException { - byte[] hash = SHA512.digest(toByteArray(ecdh(privateKeyHex, ephemPublicKeyHex))); - byte[] encKeyBytes = Arrays.copyOfRange(hash, 0, 32); - AES_ENCRYPTION_KEY = encKeyBytes; - ENCRYPTION_IV = toByteArray(encryptionIvHex); - } - - /** - * Utility method to convert a BigInteger to a byte array in unsigned - * format as needed in the handshake messages. BigInteger uses - * 2's complement format, i.e. it prepends an extra zero if the MSB - * is set. We remove that. - */ - public static byte[] toByteArray(BigInteger bi) { - byte[] b = bi.toByteArray(); - if (b.length > 1 && b[0] == 0) { - int n = b.length - 1; - byte[] newArray = new byte[n]; - System.arraycopy(b, 1, newArray, 0, n); - b = newArray; - } - return b; - } - - public static byte[] toByteArray(String s) { - int len = s.length(); - byte[] data = new byte[len / 2]; - for (int i = 0; i < len; i += 2) { - data[i / 2] = (byte) ((Character.digit(s.charAt(i), 16) << 4) - + Character.digit(s.charAt(i+1), 16)); - } - return data; - } - - public String encrypt(byte[] src) throws TorusException { - Cipher cipher; - try { - cipher = Cipher.getInstance(TRANSFORMATION); - cipher.init(Cipher.ENCRYPT_MODE, makeKey(), makeIv()); - return Base64.encodeBytes(cipher.doFinal(src)); - } catch (Exception e) { - e.printStackTrace(); - throw new TorusException("Torus Internal Error", e); - } - } - - public byte[] decrypt(String src) throws TorusException { - Cipher cipher; - try { - cipher = Cipher.getInstance(TRANSFORMATION); - cipher.init(Cipher.DECRYPT_MODE, makeKey(), makeIv()); - return cipher.doFinal(Base64.decode(src)); - } catch (Exception e) { - e.printStackTrace(); - throw new TorusException("Torus Internal Error", e); - } - - } - - private BigInteger ecdh(String privateKeyHex, String ephemPublicKeyHex) { - String affineX = ephemPublicKeyHex.substring(2, 66); - String affineY = ephemPublicKeyHex.substring(66); - - ECPointArithmetic ecPoint = new ECPointArithmetic(new EllipticCurve( - new ECFieldFp(new BigInteger("115792089237316195423570985008687907853269984665640564039457584007908834671663")), - new BigInteger("0"), - new BigInteger("7")), new BigInteger(affineX, 16), new BigInteger(affineY, 16), null); - return ecPoint.multiply(new BigInteger(privateKeyHex, 16)).getX(); - } - - private Key makeKey() { - return new SecretKeySpec(AES_ENCRYPTION_KEY, "AES"); - } - - private AlgorithmParameterSpec makeIv() { - return new IvParameterSpec(ENCRYPTION_IV); - } -} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Base64.java b/src/main/java/org/torusresearch/torusutils/helpers/Base64.java deleted file mode 100644 index 4040918..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/Base64.java +++ /dev/null @@ -1,1953 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import java.nio.charset.StandardCharsets; - -public class Base64 { - - /* ******** P U B L I C F I E L D S ******** */ - - - /** - * No options specified. Value is zero. - */ - public final static int NO_OPTIONS = 0; - - /** - * Specify encoding in first bit. Value is one. - */ - public final static int ENCODE = 1; - - - /** - * Specify decoding in first bit. Value is zero. - */ - public final static int DECODE = 0; - - - /** - * Specify that data should be gzip-compressed in second bit. Value is two. - */ - public final static int GZIP = 2; - - /** - * Specify that gzipped data should not be automatically gunzipped. - */ - public final static int DONT_GUNZIP = 4; - - - /** - * Do break lines when encoding. Value is 8. - */ - public final static int DO_BREAK_LINES = 8; - - /** - * Encode using Base64-like encoding that is URL- and Filename-safe as described - * in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * It is important to note that data encoded this way is not officially valid Base64, - * or at the very least should not be called Base64 without also specifying that is - * was encoded using the URL- and Filename-safe dialect. - */ - public final static int URL_SAFE = 16; - - - /** - * Encode using the special "ordered" dialect of Base64 described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - public final static int ORDERED = 32; - - - /* ******** P R I V A T E F I E L D S ******** */ - - - /** - * Maximum line length (76) of Base64 output. - */ - private final static int MAX_LINE_LENGTH = 76; - - - /** - * The equals sign (=) as a byte. - */ - private final static byte EQUALS_SIGN = (byte) '='; - - - /** - * The new line character (\n) as a byte. - */ - private final static byte NEW_LINE = (byte) '\n'; - - - /** - * Preferred encoding. - */ - private final static String PREFERRED_ENCODING = "US-ASCII"; - - - private final static byte WHITE_SPACE_ENC = -5; // Indicates white space in encoding - private final static byte EQUALS_SIGN_ENC = -1; // Indicates equals sign in encoding - - - /* ******** S T A N D A R D B A S E 6 4 A L P H A B E T ******** */ - - /** - * The 64 valid Base64 values. - */ - /* Host platform me be something funny like EBCDIC, so we hardcode these values. */ - private final static byte[] _STANDARD_ALPHABET = { - (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', - (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', - (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', - (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', - (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', - (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', - (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', - (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '+', (byte) '/' - }; - - - /** - * Translates a Base64 value to either its 6-bit reconstruction value - * or a negative number indicating some other meaning. - **/ - private final static byte[] _STANDARD_DECODABET = { - -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - 62, // Plus sign at decimal 43 - -9, -9, -9, // Decimal 44 - 46 - 63, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, -9, -9, // Decimal 91 - 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9, -9 // Decimal 123 - 127 - , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 - }; - - - /* ******** U R L S A F E B A S E 6 4 A L P H A B E T ******** */ - - /** - * Used in the URL- and Filename-safe dialect described in Section 4 of RFC3548: - * http://www.faqs.org/rfcs/rfc3548.html. - * Notice that the last two bytes become "hyphen" and "underscore" instead of "plus" and "slash." - */ - private final static byte[] _URL_SAFE_ALPHABET = { - (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', - (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', - (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', - (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', - (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', - (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', - (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z', - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', (byte) '5', - (byte) '6', (byte) '7', (byte) '8', (byte) '9', (byte) '-', (byte) '_' - }; - - /** - * Used in decoding URL- and Filename-safe dialects of Base64. - */ - private final static byte[] _URL_SAFE_DECODABET = { - -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 62, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, // Letters 'A' through 'N' - 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, // Letters 'O' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 63, // Underscore at decimal 95 - -9, // Decimal 96 - 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, // Letters 'a' through 'm' - 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, // Letters 'n' through 'z' - -9, -9, -9, -9, -9 // Decimal 123 - 127 - , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 - }; - - - - /* ******** O R D E R E D B A S E 6 4 A L P H A B E T ******** */ - - /** - * I don't get the point of this technique, but someone requested it, - * and it is described here: - * http://www.faqs.org/qa/rfcc-1940.html. - */ - private final static byte[] _ORDERED_ALPHABET = { - (byte) '-', - (byte) '0', (byte) '1', (byte) '2', (byte) '3', (byte) '4', - (byte) '5', (byte) '6', (byte) '7', (byte) '8', (byte) '9', - (byte) 'A', (byte) 'B', (byte) 'C', (byte) 'D', (byte) 'E', (byte) 'F', (byte) 'G', - (byte) 'H', (byte) 'I', (byte) 'J', (byte) 'K', (byte) 'L', (byte) 'M', (byte) 'N', - (byte) 'O', (byte) 'P', (byte) 'Q', (byte) 'R', (byte) 'S', (byte) 'T', (byte) 'U', - (byte) 'V', (byte) 'W', (byte) 'X', (byte) 'Y', (byte) 'Z', - (byte) '_', - (byte) 'a', (byte) 'b', (byte) 'c', (byte) 'd', (byte) 'e', (byte) 'f', (byte) 'g', - (byte) 'h', (byte) 'i', (byte) 'j', (byte) 'k', (byte) 'l', (byte) 'm', (byte) 'n', - (byte) 'o', (byte) 'p', (byte) 'q', (byte) 'r', (byte) 's', (byte) 't', (byte) 'u', - (byte) 'v', (byte) 'w', (byte) 'x', (byte) 'y', (byte) 'z' - }; - - /** - * Used in decoding the "ordered" dialect of Base64. - */ - private final static byte[] _ORDERED_DECODABET = { - -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 0 - 8 - -5, -5, // Whitespace: Tab and Linefeed - -9, -9, // Decimal 11 - 12 - -5, // Whitespace: Carriage Return - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 14 - 26 - -9, -9, -9, -9, -9, // Decimal 27 - 31 - -5, // Whitespace: Space - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 33 - 42 - -9, // Plus sign at decimal 43 - -9, // Decimal 44 - 0, // Minus sign at decimal 45 - -9, // Decimal 46 - -9, // Slash at decimal 47 - 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, // Numbers zero through nine - -9, -9, -9, // Decimal 58 - 60 - -1, // Equals sign at decimal 61 - -9, -9, -9, // Decimal 62 - 64 - 11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, // Letters 'A' through 'M' - 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, // Letters 'N' through 'Z' - -9, -9, -9, -9, // Decimal 91 - 94 - 37, // Underscore at decimal 95 - -9, // Decimal 96 - 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, // Letters 'a' through 'm' - 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, // Letters 'n' through 'z' - -9, -9, -9, -9, -9 // Decimal 123 - 127 - , -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 128 - 139 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 140 - 152 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 153 - 165 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 166 - 178 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 179 - 191 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 192 - 204 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 205 - 217 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 218 - 230 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, // Decimal 231 - 243 - -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9, -9 // Decimal 244 - 255 - }; - - - /* ******** D E T E R M I N E W H I C H A L H A B E T ******** */ - - - /** - * Defeats instantiation. - */ - private Base64() { - } - - /** - * Returns one of the _SOMETHING_ALPHABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URLSAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private static byte[] getAlphabet(int options) { - if ((options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_ALPHABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_ALPHABET; - } else { - return _STANDARD_ALPHABET; - } - } // end getAlphabet - - /** - * Returns one of the _SOMETHING_DECODABET byte arrays depending on - * the options specified. - * It's possible, though silly, to specify ORDERED and URL_SAFE - * in which case one of them will be picked, though there is - * no guarantee as to which one will be picked. - */ - private static byte[] getDecodabet(int options) { - if ((options & URL_SAFE) == URL_SAFE) { - return _URL_SAFE_DECODABET; - } else if ((options & ORDERED) == ORDERED) { - return _ORDERED_DECODABET; - } else { - return _STANDARD_DECODABET; - } - } // end getAlphabet - - - - - /* ******** E N C O D I N G M E T H O D S ******** */ - - /** - * Encodes up to the first three bytes of array threeBytes - * and returns a four-byte array in Base64 notation. - * The actual number of significant bytes in your array is - * given by numSigBytes. - * The array threeBytes needs only be as big as - * numSigBytes. - * Code can reuse a byte array by passing a four-byte array as b4. - * - * @param b4 A reusable byte array to reduce array instantiation - * @param threeBytes the array to convert - * @param numSigBytes the number of significant bytes in your array - * @return four byte array in Base64 notation. - * @since 1.5.1 - */ - private static byte[] encode3to4(byte[] b4, byte[] threeBytes, int numSigBytes, int options) { - encode3to4(threeBytes, 0, numSigBytes, b4, 0, options); - return b4; - } // end encode3to4 - - - /** - *

Encodes up to three bytes of the array source - * and writes the resulting four Base64 bytes to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 3 for - * the source array or destOffset + 4 for - * the destination array. - * The actual number of significant bytes in your array is - * given by numSigBytes.

- *

This is the lowest level of the encoding methods with - * all possible parameters.

- * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param numSigBytes the number of significant bytes in your array - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @return the destination array - * @since 1.3 - */ - private static byte[] encode3to4( - byte[] source, int srcOffset, int numSigBytes, - byte[] destination, int destOffset, int options) { - - byte[] ALPHABET = getAlphabet(options); - - // 1 2 3 - // 01234567890123456789012345678901 Bit position - // --------000000001111111122222222 Array position from threeBytes - // --------| || || || | Six bit groups to index ALPHABET - // >>18 >>12 >> 6 >> 0 Right shift necessary - // 0x3f 0x3f 0x3f Additional AND - - // Create buffer with zero-padding if there are only one or two - // significant bytes passed in the array. - // We have to shift left 24 in order to flush out the 1's that appear - // when Java treats a value as negative that is cast from a byte to an int. - int inBuff = (numSigBytes > 0 ? ((source[srcOffset] << 24) >>> 8) : 0) - | (numSigBytes > 1 ? ((source[srcOffset + 1] << 24) >>> 16) : 0) - | (numSigBytes > 2 ? ((source[srcOffset + 2] << 24) >>> 24) : 0); - - switch (numSigBytes) { - case 3: - destination[destOffset] = ALPHABET[(inBuff >>> 18)]; - destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; - destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; - destination[destOffset + 3] = ALPHABET[(inBuff) & 0x3f]; - return destination; - - case 2: - destination[destOffset] = ALPHABET[(inBuff >>> 18)]; - destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; - destination[destOffset + 2] = ALPHABET[(inBuff >>> 6) & 0x3f]; - destination[destOffset + 3] = EQUALS_SIGN; - return destination; - - case 1: - destination[destOffset] = ALPHABET[(inBuff >>> 18)]; - destination[destOffset + 1] = ALPHABET[(inBuff >>> 12) & 0x3f]; - destination[destOffset + 2] = EQUALS_SIGN; - destination[destOffset + 3] = EQUALS_SIGN; - return destination; - - default: - return destination; - } // end switch - } // end encode3to4 - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded ByteBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode(java.nio.ByteBuffer raw, java.nio.ByteBuffer encoded) { - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while (raw.hasRemaining()) { - int rem = Math.min(3, raw.remaining()); - raw.get(raw3, 0, rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS); - encoded.put(enc4); - } // end input remaining - } - - - /** - * Performs Base64 encoding on the raw ByteBuffer, - * writing it to the encoded CharBuffer. - * This is an experimental feature. Currently it does not - * pass along any options (such as {@link #DO_BREAK_LINES} - * or {@link #GZIP}. - * - * @param raw input buffer - * @param encoded output buffer - * @since 2.3 - */ - public static void encode(java.nio.ByteBuffer raw, java.nio.CharBuffer encoded) { - byte[] raw3 = new byte[3]; - byte[] enc4 = new byte[4]; - - while (raw.hasRemaining()) { - int rem = Math.min(3, raw.remaining()); - raw.get(raw3, 0, rem); - Base64.encode3to4(enc4, raw3, rem, Base64.NO_OPTIONS); - for (int i = 0; i < 4; i++) { - encoded.put((char) (enc4[i] & 0xFF)); - } - } // end input remaining - } - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- *

- * The object is not GZip-compressed before being encoded. - * - * @param serializableObject The object to encode - * @return The Base64-encoded object - * @throws java.io.IOException if there is an error - * @throws NullPointerException if serializedObject is null - * @since 1.4 - */ - public static String encodeObject(java.io.Serializable serializableObject) - throws java.io.IOException { - return encodeObject(serializableObject, NO_OPTIONS); - } // end encodeObject - - - /** - * Serializes an object and returns the Base64-encoded - * version of that serialized object. - * - *

As of v 2.3, if the object - * cannot be serialized or there is another error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- *

- * The object is not GZip-compressed before being encoded. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     * 
- *

- * Example: encodeObject( myObj, Base64.GZIP ) or - *

- * Example: encodeObject( myObj, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * @param serializableObject The object to encode - * @param options Specified options - * @return The Base64-encoded object - * @throws java.io.IOException if there is an error - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public static String encodeObject(java.io.Serializable serializableObject, int options) - throws java.io.IOException { - - if (serializableObject == null) { - throw new NullPointerException("Cannot serialize a null object."); - } // end if: null - - // Streams - java.io.ByteArrayOutputStream baos = null; - java.io.OutputStream b64os = null; - java.util.zip.GZIPOutputStream gzos = null; - java.io.ObjectOutputStream oos = null; - - - try { - // ObjectOutputStream -> (GZIP) -> Base64 -> ByteArrayOutputStream - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream(baos, ENCODE | options); - if ((options & GZIP) != 0) { - // Gzip - gzos = new java.util.zip.GZIPOutputStream(b64os); - oos = new java.io.ObjectOutputStream(gzos); - } else { - // Not gzipped - oos = new java.io.ObjectOutputStream(b64os); - } - oos.writeObject(serializableObject); - } // end try - catch (java.io.IOException e) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try { - oos.close(); - } catch (Exception ignored) { - } - try { - gzos.close(); - } catch (Exception e) { - } - try { - b64os.close(); - } catch (Exception e) { - } - try { - baos.close(); - } catch (Exception e) { - } - } // end finally - - // Return value according to relevant encoding. - try { - return new String(baos.toByteArray(), PREFERRED_ENCODING); - } // end try - catch (java.io.UnsupportedEncodingException uue) { - // Fall back to some Java default - return new String(baos.toByteArray()); - } // end catch - - } // end encode - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - * @param source The data to convert - * @return The data in Base64-encoded form - * @throws NullPointerException if source array is null - * @since 1.4 - */ - public static String encodeBytes(byte[] source) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes(source, 0, source.length, NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param source The data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes(byte[] source, int options) throws java.io.IOException { - return encodeBytes(source, 0, source.length, options); - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - * Does not GZip-compress data. - * - *

As of v 2.3, if there is an error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @return The Base64-encoded data as a String - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @since 1.4 - */ - public static String encodeBytes(byte[] source, int off, int len) { - // Since we're not going to have the GZIP encoding turned on, - // we're not going to have an java.io.IOException thrown, so - // we should not force the user to have to catch it. - String encoded = null; - try { - encoded = encodeBytes(source, off, len, NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : ex.getMessage(); - } // end catch - assert encoded != null; - return encoded; - } // end encodeBytes - - - /** - * Encodes a byte array into Base64 notation. - *

- * Example options:

-     *   GZIP: gzip-compresses object before encoding it.
-     *   DO_BREAK_LINES: break lines at 76 characters
-     *     Note: Technically, this makes your encoding non-compliant.
-     * 
- *

- * Example: encodeBytes( myData, Base64.GZIP ) or - *

- * Example: encodeBytes( myData, Base64.GZIP | Base64.DO_BREAK_LINES ) - * - * - *

As of v 2.3, if there is an error with the GZIP stream, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned a null value, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public static String encodeBytes(byte[] source, int off, int len, int options) throws java.io.IOException { - byte[] encoded = encodeBytesToBytes(source, off, len, options); - - // Return value according to relevant encoding. - try { - return new String(encoded, PREFERRED_ENCODING); - } // end try - catch (java.io.UnsupportedEncodingException uue) { - return new String(encoded); - } // end catch - - } // end encodeBytes - - - /** - * Similar to {@link #encodeBytes(byte[])} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * @param source The data to convert - * @return The Base64-encoded data as a byte[] (of ASCII characters) - * @throws NullPointerException if source array is null - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes(byte[] source) { - byte[] encoded = null; - try { - encoded = encodeBytesToBytes(source, 0, source.length, Base64.NO_OPTIONS); - } catch (java.io.IOException ex) { - assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); - } - return encoded; - } - - - /** - * Similar to {@link #encodeBytes(byte[], int, int, int)} but returns - * a byte array instead of instantiating a String. This is more efficient - * if you're working with I/O streams and have large data sets to encode. - * - * @param source The data to convert - * @param off Offset in array where conversion should begin - * @param len Length of data to convert - * @param options Specified options - * @return The Base64-encoded data as a String - * @throws java.io.IOException if there is an error - * @throws NullPointerException if source array is null - * @throws IllegalArgumentException if source array, offset, or length are invalid - * @see Base64#GZIP - * @see Base64#DO_BREAK_LINES - * @since 2.3.1 - */ - public static byte[] encodeBytesToBytes(byte[] source, int off, int len, int options) throws java.io.IOException { - - if (source == null) { - throw new NullPointerException("Cannot serialize a null array."); - } // end if: null - - if (off < 0) { - throw new IllegalArgumentException("Cannot have negative offset: " + off); - } // end if: off < 0 - - if (len < 0) { - throw new IllegalArgumentException("Cannot have length offset: " + len); - } // end if: len < 0 - - if (off + len > source.length) { - throw new IllegalArgumentException( - String.format("Cannot have offset of %d and length of %d with array of length %d", off, len, source.length)); - } // end if: off < 0 - - - // Compress? - if ((options & GZIP) != 0) { - java.io.ByteArrayOutputStream baos = null; - java.util.zip.GZIPOutputStream gzos = null; - Base64.OutputStream b64os = null; - - try { - // GZip -> Base64 -> ByteArray - baos = new java.io.ByteArrayOutputStream(); - b64os = new Base64.OutputStream(baos, ENCODE | options); - gzos = new java.util.zip.GZIPOutputStream(b64os); - - gzos.write(source, off, len); - gzos.close(); - } // end try - catch (java.io.IOException e) { - // Catch it and then throw it immediately so that - // the finally{} block is called for cleanup. - throw e; - } // end catch - finally { - try { - gzos.close(); - } catch (Exception e) { - } - try { - b64os.close(); - } catch (Exception e) { - } - try { - baos.close(); - } catch (Exception e) { - } - } // end finally - - return baos.toByteArray(); - } // end if: compress - - // Else, don't compress. Better not to use streams at all then. - else { - boolean breakLines = (options & DO_BREAK_LINES) != 0; - - //int len43 = len * 4 / 3; - //byte[] outBuff = new byte[ ( len43 ) // Main 4:3 - // + ( (len % 3) > 0 ? 4 : 0 ) // Account for padding - // + (breakLines ? ( len43 / MAX_LINE_LENGTH ) : 0) ]; // New lines - // Try to determine more precisely how big the array needs to be. - // If we get it right, we don't have to do an array copy, and - // we save a bunch of memory. - int encLen = (len / 3) * 4 + (len % 3 > 0 ? 4 : 0); // Bytes needed for actual encoding - if (breakLines) { - encLen += encLen / MAX_LINE_LENGTH; // Plus extra newline characters - } - byte[] outBuff = new byte[encLen]; - - - int d = 0; - int e = 0; - int len2 = len - 2; - int lineLength = 0; - for (; d < len2; d += 3, e += 4) { - encode3to4(source, d + off, 3, outBuff, e, options); - - lineLength += 4; - if (breakLines && lineLength >= MAX_LINE_LENGTH) { - outBuff[e + 4] = NEW_LINE; - e++; - lineLength = 0; - } // end if: end of line - } // en dfor: each piece of array - - if (d < len) { - encode3to4(source, d + off, len - d, outBuff, e, options); - e += 4; - } // end if: some padding needed - - - // Only resize array if we didn't guess it right. - if (e <= outBuff.length - 1) { - // If breaking lines and the last byte falls right at - // the line length (76 bytes per line), there will be - // one extra byte, and the array will need to be resized. - // Not too bad of an estimate on array size, I'd say. - byte[] finalOut = new byte[e]; - System.arraycopy(outBuff, 0, finalOut, 0, e); - //System.err.println("Having to resize array from " + outBuff.length + " to " + e ); - return finalOut; - } else { - //System.err.println("No need to resize array."); - return outBuff; - } - - } // end else: don't compress - - } // end encodeBytesToBytes - - - - - - /* ******** D E C O D I N G M E T H O D S ******** */ - - - /** - * Decodes four bytes from array source - * and writes the resulting bytes (up to three of them) - * to destination. - * The source and destination arrays can be manipulated - * anywhere along their length by specifying - * srcOffset and destOffset. - * This method does not check to make sure your arrays - * are large enough to accomodate srcOffset + 4 for - * the source array or destOffset + 3 for - * the destination array. - * This method returns the actual number of bytes that - * were converted from the Base64 encoding. - *

This is the lowest level of the decoding methods with - * all possible parameters.

- * - * @param source the array to convert - * @param srcOffset the index where conversion begins - * @param destination the array to hold the conversion - * @param destOffset the index where output will be put - * @param options alphabet type is pulled from this (standard, url-safe, ordered) - * @return the number of decoded bytes converted - * @throws NullPointerException if source or destination arrays are null - * @throws IllegalArgumentException if srcOffset or destOffset are invalid - * or there is not enough room in the array. - * @since 1.3 - */ - private static int decode4to3( - byte[] source, int srcOffset, - byte[] destination, int destOffset, int options) { - - // Lots of error checking and exception throwing - if (source == null) { - throw new NullPointerException("Source array was null."); - } // end if - if (destination == null) { - throw new NullPointerException("Destination array was null."); - } // end if - if (srcOffset < 0 || srcOffset + 3 >= source.length) { - throw new IllegalArgumentException(String.format( - "Source array with length %d cannot have offset of %d and still process four bytes.", source.length, srcOffset)); - } // end if - if (destOffset < 0 || destOffset + 2 >= destination.length) { - throw new IllegalArgumentException(String.format( - "Destination array with length %d cannot have offset of %d and still store three bytes.", destination.length, destOffset)); - } // end if - - - byte[] DECODABET = getDecodabet(options); - - // Example: Dk== - if (source[srcOffset + 2] == EQUALS_SIGN) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1] ] << 24 ) >>> 12 ); - int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) - | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12); - - destination[destOffset] = (byte) (outBuff >>> 16); - return 1; - } - - // Example: DkL= - else if (source[srcOffset + 3] == EQUALS_SIGN) { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ); - int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) - | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) - | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6); - - destination[destOffset] = (byte) (outBuff >>> 16); - destination[destOffset + 1] = (byte) (outBuff >>> 8); - return 2; - } - - // Example: DkLE - else { - // Two ways to do the same thing. Don't know which way I like best. - //int outBuff = ( ( DECODABET[ source[ srcOffset ] ] << 24 ) >>> 6 ) - // | ( ( DECODABET[ source[ srcOffset + 1 ] ] << 24 ) >>> 12 ) - // | ( ( DECODABET[ source[ srcOffset + 2 ] ] << 24 ) >>> 18 ) - // | ( ( DECODABET[ source[ srcOffset + 3 ] ] << 24 ) >>> 24 ); - int outBuff = ((DECODABET[source[srcOffset]] & 0xFF) << 18) - | ((DECODABET[source[srcOffset + 1]] & 0xFF) << 12) - | ((DECODABET[source[srcOffset + 2]] & 0xFF) << 6) - | ((DECODABET[source[srcOffset + 3]] & 0xFF)); - - - destination[destOffset] = (byte) (outBuff >> 16); - destination[destOffset + 1] = (byte) (outBuff >> 8); - destination[destOffset + 2] = (byte) (outBuff); - - return 3; - } - } // end decodeToBytes - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @return decoded data - * @since 2.3.1 - */ - public static byte[] decode(byte[] source) - throws java.io.IOException { - byte[] decoded = null; -// try { - decoded = decode(source, 0, source.length, Base64.NO_OPTIONS); -// } catch( java.io.IOException ex ) { -// assert false : "IOExceptions only come from GZipping, which is turned off: " + ex.getMessage(); -// } - return decoded; - } - - - /** - * Low-level access to decoding ASCII characters in - * the form of a byte array. Ignores GUNZIP option, if - * it's set. This is not generally a recommended method, - * although it is used internally as part of the decoding process. - * Special case: if len = 0, an empty array is returned. Still, - * if you need more speed and reduced memory footprint (and aren't - * gzipping), consider this method. - * - * @param source The Base64 encoded data - * @param off The offset of where to begin decoding - * @param len The length of characters to decode - * @param options Can specify options such as alphabet type to use - * @return decoded data - * @throws java.io.IOException If bogus characters exist in source data - * @since 1.3 - */ - public static byte[] decode(byte[] source, int off, int len, int options) - throws java.io.IOException { - - // Lots of error checking and exception throwing - if (source == null) { - throw new NullPointerException("Cannot decode null source array."); - } // end if - if (off < 0 || off + len > source.length) { - throw new IllegalArgumentException(String.format( - "Source array with length %d cannot have offset of %d and process %d bytes.", source.length, off, len)); - } // end if - - if (len == 0) { - return new byte[0]; - } else if (len < 4) { - throw new IllegalArgumentException( - "Base64-encoded string must have at least four characters, but length specified was " + len); - } // end if - - byte[] DECODABET = getDecodabet(options); - - int len34 = len * 3 / 4; // Estimate on array size - byte[] outBuff = new byte[len34]; // Upper limit on size of output - int outBuffPosn = 0; // Keep track of where we're writing - - byte[] b4 = new byte[4]; // Four byte buffer from source, eliminating white space - int b4Posn = 0; // Keep track of four byte input buffer - int i = 0; // Source array counter - byte sbiDecode = 0; // Special value from DECODABET - - for (i = off; i < off + len; i++) { // Loop through source - - sbiDecode = DECODABET[source[i] & 0xFF]; - - // White space, Equals sign, or legit Base64 character - // Note the values such as -5 and -9 in the - // DECODABETs at the top of the file. - if (sbiDecode >= WHITE_SPACE_ENC) { - if (sbiDecode >= EQUALS_SIGN_ENC) { - b4[b4Posn++] = source[i]; // Save non-whitespace - if (b4Posn > 3) { // Time to decode? - outBuffPosn += decode4to3(b4, 0, outBuff, outBuffPosn, options); - b4Posn = 0; - - // If that was the equals sign, break out of 'for' loop - if (source[i] == EQUALS_SIGN) { - break; - } // end if: equals sign - } // end if: quartet built - } // end if: equals sign or better - } // end if: white space, equals sign or better - else { - // There's a bad input character in the Base64 stream. - throw new java.io.IOException(String.format( - "Bad Base64 input character decimal %d in array position %d", ((int) source[i]) & 0xFF, i)); - } // end else: - } // each input character - - byte[] out = new byte[outBuffPosn]; - System.arraycopy(outBuff, 0, out, 0, outBuffPosn); - return out; - } // end decode - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @return the decoded data - * @throws java.io.IOException If there is a problem - * @since 1.4 - */ - public static byte[] decode(String s) throws java.io.IOException { - return decode(s, NO_OPTIONS); - } - - - /** - * Decodes data from Base64 notation, automatically - * detecting gzip-compressed data and decompressing it. - * - * @param s the string to decode - * @param options encode options such as URL_SAFE - * @return the decoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if s is null - * @since 1.4 - */ - public static byte[] decode(String s, int options) throws java.io.IOException { - - if (s == null) { - throw new NullPointerException("Input string was null."); - } // end if - - byte[] bytes; - try { - bytes = s.getBytes(PREFERRED_ENCODING); - } // end try - catch (java.io.UnsupportedEncodingException uee) { - bytes = s.getBytes(); - } // end catch - // - - // Decode - bytes = decode(bytes, 0, bytes.length, options); - - // Check to see if it's gzip-compressed - // GZIP Magic Two-Byte Number: 0x8b1f (35615) - boolean dontGunzip = (options & DONT_GUNZIP) != 0; - if ((bytes != null) && (bytes.length >= 4) && (!dontGunzip)) { - - int head = ((int) bytes[0] & 0xff) | ((bytes[1] << 8) & 0xff00); - if (java.util.zip.GZIPInputStream.GZIP_MAGIC == head) { - java.io.ByteArrayInputStream bais = null; - java.util.zip.GZIPInputStream gzis = null; - java.io.ByteArrayOutputStream baos = null; - byte[] buffer = new byte[2048]; - int length = 0; - - try { - baos = new java.io.ByteArrayOutputStream(); - bais = new java.io.ByteArrayInputStream(bytes); - gzis = new java.util.zip.GZIPInputStream(bais); - - while ((length = gzis.read(buffer)) >= 0) { - baos.write(buffer, 0, length); - } // end while: reading input - - // No error? Get new bytes. - bytes = baos.toByteArray(); - - } // end try - catch (java.io.IOException e) { - e.printStackTrace(); - // Just return originally-decoded bytes - } // end catch - finally { - try { - baos.close(); - } catch (Exception e) { - } - try { - gzis.close(); - } catch (Exception e) { - } - try { - bais.close(); - } catch (Exception e) { - } - } // end finally - - } // end if: gzipped - } // end if: bytes.length >= 2 - - return bytes; - } // end decode - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * - * @param encodedObject The Base64 data to decode - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 1.5 - */ - public static Object decodeToObject(String encodedObject) - throws java.io.IOException, ClassNotFoundException { - return decodeToObject(encodedObject, NO_OPTIONS, null); - } - - - /** - * Attempts to decode Base64 data and deserialize a Java - * Object within. Returns null if there was an error. - * If loader is not null, it will be the class loader - * used when deserializing. - * - * @param encodedObject The Base64 data to decode - * @param options Various parameters related to decoding - * @param loader Optional class loader to use in deserializing classes. - * @return The decoded and deserialized object - * @throws NullPointerException if encodedObject is null - * @throws java.io.IOException if there is a general error - * @throws ClassNotFoundException if the decoded object is of a - * class that cannot be found by the JVM - * @since 2.3.4 - */ - public static Object decodeToObject( - String encodedObject, int options, final ClassLoader loader) - throws java.io.IOException, ClassNotFoundException { - - // Decode and gunzip if necessary - byte[] objBytes = decode(encodedObject, options); - - java.io.ByteArrayInputStream bais = null; - java.io.ObjectInputStream ois = null; - Object obj = null; - - try { - bais = new java.io.ByteArrayInputStream(objBytes); - - // If no custom class loader is provided, use Java's builtin OIS. - if (loader == null) { - ois = new java.io.ObjectInputStream(bais); - } // end if: no loader provided - - // Else make a customized object input stream that uses - // the provided class loader. - else { - ois = new java.io.ObjectInputStream(bais) { - @Override - public Class resolveClass(java.io.ObjectStreamClass streamClass) - throws java.io.IOException, ClassNotFoundException { - Class c = Class.forName(streamClass.getName(), false, loader); - if (c == null) { - return super.resolveClass(streamClass); - } else { - return c; // Class loader knows of this class. - } // end else: not null - } // end resolveClass - }; // end ois - } // end else: no custom class loader - - obj = ois.readObject(); - } // end try - catch (java.io.IOException e) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - catch (ClassNotFoundException e) { - throw e; // Catch and throw in order to execute finally{} - } // end catch - finally { - try { - bais.close(); - } catch (Exception e) { - } - try { - ois.close(); - } catch (Exception e) { - } - } // end finally - - return obj; - } // end decodeObject - - - /** - * Convenience method for encoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToEncode byte array of data to encode in base64 form - * @param filename Filename for saving encoded data - * @throws java.io.IOException if there is an error - * @throws NullPointerException if dataToEncode is null - * @since 2.1 - */ - public static void encodeToFile(byte[] dataToEncode, String filename) - throws java.io.IOException { - - if (dataToEncode == null) { - throw new NullPointerException("Data to encode was null."); - } // end iff - - Base64.OutputStream bos = null; - try { - bos = new Base64.OutputStream( - new java.io.FileOutputStream(filename), Base64.ENCODE); - bos.write(dataToEncode); - } // end try - catch (java.io.IOException e) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try { - bos.close(); - } catch (Exception e) { - } - } // end finally - - } // end encodeToFile - - - /** - * Convenience method for decoding data to a file. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param dataToDecode Base64-encoded data as a string - * @param filename Filename for saving decoded data - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static void decodeToFile(String dataToDecode, String filename) - throws java.io.IOException { - - Base64.OutputStream bos = null; - try { - bos = new Base64.OutputStream( - new java.io.FileOutputStream(filename), Base64.DECODE); - bos.write(dataToDecode.getBytes(PREFERRED_ENCODING)); - } // end try - catch (java.io.IOException e) { - throw e; // Catch and throw to execute finally{} block - } // end catch: java.io.IOException - finally { - try { - bos.close(); - } catch (Exception e) { - } - } // end finally - - } // end decodeToFile - - - /** - * Convenience method for reading a base64-encoded - * file and decoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading encoded data - * @return decoded byte array - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static byte[] decodeFromFile(String filename) - throws java.io.IOException { - - byte[] decodedData = null; - Base64.InputStream bis = null; - try { - // Set up some useful variables - java.io.File file = new java.io.File(filename); - byte[] buffer = null; - int length = 0; - int numBytes = 0; - - // Check for size of file - if (file.length() > Integer.MAX_VALUE) { - throw new java.io.IOException("File is too big for this convenience method (" + file.length() + " bytes)."); - } // end if: file too big for int index - buffer = new byte[(int) file.length()]; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream(file)), Base64.DECODE); - - // Read until done - while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { - length += numBytes; - } // end while - - // Save in a variable to return - decodedData = new byte[length]; - System.arraycopy(buffer, 0, decodedData, 0, length); - - } // end try - catch (java.io.IOException e) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try { - bis.close(); - } catch (Exception e) { - } - } // end finally - - return decodedData; - } // end decodeFromFile - - - /** - * Convenience method for reading a binary file - * and base64-encoding it. - * - *

As of v 2.3, if there is a error, - * the method will throw an java.io.IOException. This is new to v2.3! - * In earlier versions, it just returned false, but - * in retrospect that's a pretty poor way to handle it.

- * - * @param filename Filename for reading binary data - * @return base64-encoded string - * @throws java.io.IOException if there is an error - * @since 2.1 - */ - public static String encodeFromFile(String filename) - throws java.io.IOException { - - String encodedData = null; - Base64.InputStream bis = null; - try { - // Set up some useful variables - java.io.File file = new java.io.File(filename); - byte[] buffer = new byte[Math.max((int) (file.length() * 1.4 + 1), 40)]; // Need max() for math on small files (v2.2.1); Need +1 for a few corner cases (v2.3.5) - int length = 0; - int numBytes = 0; - - // Open a stream - bis = new Base64.InputStream( - new java.io.BufferedInputStream( - new java.io.FileInputStream(file)), Base64.ENCODE); - - // Read until done - while ((numBytes = bis.read(buffer, length, 4096)) >= 0) { - length += numBytes; - } // end while - - // Save in a variable to return - encodedData = new String(buffer, 0, length, Base64.PREFERRED_ENCODING); - - } // end try - catch (java.io.IOException e) { - throw e; // Catch and release to execute finally{} - } // end catch: java.io.IOException - finally { - try { - bis.close(); - } catch (Exception e) { - } - } // end finally - - return encodedData; - } // end encodeFromFile - - /** - * Reads infile and encodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void encodeFileToFile(String infile, String outfile) - throws java.io.IOException { - - String encoded = Base64.encodeFromFile(infile); - java.io.OutputStream out = null; - try { - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream(outfile)); - out.write(encoded.getBytes(StandardCharsets.US_ASCII)); // Strict, 7-bit output. - } // end try - catch (java.io.IOException e) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { - out.close(); - } catch (Exception ex) { - } - } // end finally - } // end encodeFileToFile - - - /** - * Reads infile and decodes it to outfile. - * - * @param infile Input file - * @param outfile Output file - * @throws java.io.IOException if there is an error - * @since 2.2 - */ - public static void decodeFileToFile(String infile, String outfile) - throws java.io.IOException { - - byte[] decoded = Base64.decodeFromFile(infile); - java.io.OutputStream out = null; - try { - out = new java.io.BufferedOutputStream( - new java.io.FileOutputStream(outfile)); - out.write(decoded); - } // end try - catch (java.io.IOException e) { - throw e; // Catch and release to execute finally{} - } // end catch - finally { - try { - out.close(); - } catch (Exception ex) { - } - } // end finally - } // end decodeFileToFile - - - /* ******** I N N E R C L A S S I N P U T S T R E A M ******** */ - - - /** - * A {@link Base64.InputStream} will read data from another - * java.io.InputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class InputStream extends java.io.FilterInputStream { - - private final boolean encode; // Encoding or decoding - private final byte[] buffer; // Small buffer holding converted data - private final int bufferLength; // Length of buffer (3 or 4) - private final boolean breakLines; // Break lines at less than 80 characters - private final int options; // Record options used to create the stream. - private final byte[] decodabet; // Local copies to avoid extra method calls - private int position; // Current position in the buffer - private int numSigBytes; // Number of meaningful bytes in the buffer - private int lineLength; - - - /** - * Constructs a {@link Base64.InputStream} in DECODE mode. - * - * @param in the java.io.InputStream from which to read data. - * @since 1.3 - */ - public InputStream(java.io.InputStream in) { - this(in, DECODE); - } // end constructor - - - /** - * Constructs a {@link Base64.InputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.InputStream( in, Base64.DECODE ) - * - * @param in the java.io.InputStream from which to read data. - * @param options Specified options - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 2.0 - */ - public InputStream(java.io.InputStream in, int options) { - - super(in); - this.options = options; // Record for later - this.breakLines = (options & DO_BREAK_LINES) > 0; - this.encode = (options & ENCODE) > 0; - this.bufferLength = encode ? 4 : 3; - this.buffer = new byte[bufferLength]; - this.position = -1; - this.lineLength = 0; - this.decodabet = getDecodabet(options); - } // end constructor - - /** - * Reads enough of the input stream to convert - * to/from Base64 and returns the next byte. - * - * @return next byte - * @since 1.3 - */ - @Override - public int read() throws java.io.IOException { - - // Do we need to get data? - if (position < 0) { - if (encode) { - byte[] b3 = new byte[3]; - int numBinaryBytes = 0; - for (int i = 0; i < 3; i++) { - int b = in.read(); - - // If end of stream, b is -1. - if (b >= 0) { - b3[i] = (byte) b; - numBinaryBytes++; - } else { - break; // out of for loop - } // end else: end of stream - - } // end for: each needed input byte - - if (numBinaryBytes > 0) { - encode3to4(b3, 0, numBinaryBytes, buffer, 0, options); - position = 0; - numSigBytes = 4; - } // end if: got data - else { - return -1; // Must be end of stream - } // end else - } // end if: encoding - - // Else decoding - else { - byte[] b4 = new byte[4]; - int i = 0; - for (i = 0; i < 4; i++) { - // Read four "meaningful" bytes: - int b = 0; - do { - b = in.read(); - } - while (b >= 0 && decodabet[b & 0x7f] <= WHITE_SPACE_ENC); - - if (b < 0) { - break; // Reads a -1 if end of stream - } // end if: end of stream - - b4[i] = (byte) b; - } // end for: each needed input byte - - if (i == 4) { - numSigBytes = decode4to3(b4, 0, buffer, 0, options); - position = 0; - } // end if: got four characters - else if (i == 0) { - return -1; - } // end else if: also padded correctly - else { - // Must have broken out from above. - throw new java.io.IOException("Improperly padded Base64 input."); - } // end - - } // end else: decode - } // end else: get data - - // Got data? - if (position >= 0) { - // End of relevant data? - if ( /*!encode &&*/ position >= numSigBytes) { - return -1; - } // end if: got data - - if (encode && breakLines && lineLength >= MAX_LINE_LENGTH) { - lineLength = 0; - return '\n'; - } // end if - else { - lineLength++; // This isn't important when decoding - // but throwing an extra "if" seems - // just as wasteful. - - int b = buffer[position++]; - - if (position >= bufferLength) { - position = -1; - } // end if: end - - return b & 0xFF; // This is how you "cast" a byte that's - // intended to be unsigned. - } // end else - } // end if: position >= 0 - - // Else error - else { - throw new java.io.IOException("Error in Base64 code reading stream."); - } // end else - } // end read - - - /** - * Calls {@link #read()} repeatedly until the end of stream - * is reached or len bytes are read. - * Returns number of bytes read into array or -1 if - * end of stream is encountered. - * - * @param dest array to hold values - * @param off offset for array - * @param len max number of bytes to read into array - * @return bytes read into array or -1 if end of stream is encountered. - * @since 1.3 - */ - @Override - public int read(byte[] dest, int off, int len) - throws java.io.IOException { - int i; - int b; - for (i = 0; i < len; i++) { - b = read(); - - if (b >= 0) { - dest[off + i] = (byte) b; - } else if (i == 0) { - return -1; - } else { - break; // Out of 'for' loop - } // Out of 'for' loop - } // end for: each byte read - return i; - } // end read - - } // end inner class InputStream - - - - - - - /* ******** I N N E R C L A S S O U T P U T S T R E A M ******** */ - - - /** - * A {@link Base64.OutputStream} will write data to another - * java.io.OutputStream, given in the constructor, - * and encode/decode to/from Base64 notation on the fly. - * - * @see Base64 - * @since 1.3 - */ - public static class OutputStream extends java.io.FilterOutputStream { - - private final boolean encode; - private final int bufferLength; - private final boolean breakLines; - private final byte[] b4; // Scratch used in a few places - private final int options; // Record for later - private final byte[] decodabet; // Local copies to avoid extra method calls - private int position; - private boolean suspendEncoding; - private byte[] buffer; - private int lineLength; - - /** - * Constructs a {@link Base64.OutputStream} in ENCODE mode. - * - * @param out the java.io.OutputStream to which data will be written. - * @since 1.3 - */ - public OutputStream(java.io.OutputStream out) { - this(out, ENCODE); - } // end constructor - - - /** - * Constructs a {@link Base64.OutputStream} in - * either ENCODE or DECODE mode. - *

- * Valid options:

-         *   ENCODE or DECODE: Encode or Decode as data is read.
-         *   DO_BREAK_LINES: don't break lines at 76 characters
-         *     (only meaningful when encoding)
-         * 
- *

- * Example: new Base64.OutputStream( out, Base64.ENCODE ) - * - * @param out the java.io.OutputStream to which data will be written. - * @param options Specified options. - * @see Base64#ENCODE - * @see Base64#DECODE - * @see Base64#DO_BREAK_LINES - * @since 1.3 - */ - public OutputStream(java.io.OutputStream out, int options) { - super(out); - this.breakLines = (options & DO_BREAK_LINES) != 0; - this.encode = (options & ENCODE) != 0; - this.bufferLength = encode ? 3 : 4; - this.buffer = new byte[bufferLength]; - this.position = 0; - this.lineLength = 0; - this.suspendEncoding = false; - this.b4 = new byte[4]; - this.options = options; - this.decodabet = getDecodabet(options); - } // end constructor - - - /** - * Writes the byte to the output stream after - * converting to/from Base64 notation. - * When encoding, bytes are buffered three - * at a time before the output stream actually - * gets a write() call. - * When decoding, bytes are buffered four - * at a time. - * - * @param theByte the byte to write - * @since 1.3 - */ - @Override - public void write(int theByte) - throws java.io.IOException { - // Encoding suspended? - if (suspendEncoding) { - this.out.write(theByte); - return; - } // end if: supsended - - // Encode? - if (encode) { - buffer[position++] = (byte) theByte; - if (position >= bufferLength) { // Enough to encode. - - this.out.write(encode3to4(b4, buffer, bufferLength, options)); - - lineLength += 4; - if (breakLines && lineLength >= MAX_LINE_LENGTH) { - this.out.write(NEW_LINE); - lineLength = 0; - } // end if: end of line - - position = 0; - } // end if: enough to output - } // end if: encoding - - // Else, Decoding - else { - // Meaningful Base64 character? - if (decodabet[theByte & 0x7f] > WHITE_SPACE_ENC) { - buffer[position++] = (byte) theByte; - if (position >= bufferLength) { // Enough to output. - - int len = Base64.decode4to3(buffer, 0, b4, 0, options); - out.write(b4, 0, len); - position = 0; - } // end if: enough to output - } // end if: meaningful base64 character - else if (decodabet[theByte & 0x7f] != WHITE_SPACE_ENC) { - throw new java.io.IOException("Invalid character in Base64 data."); - } // end else: not white space either - } // end else: decoding - } // end write - - - /** - * Calls {@link #write(int)} repeatedly until len - * bytes are written. - * - * @param theBytes array from which to read bytes - * @param off offset for array - * @param len max number of bytes to read into array - * @since 1.3 - */ - @Override - public void write(byte[] theBytes, int off, int len) - throws java.io.IOException { - // Encoding suspended? - if (suspendEncoding) { - this.out.write(theBytes, off, len); - return; - } // end if: supsended - - for (int i = 0; i < len; i++) { - write(theBytes[off + i]); - } // end for: each byte written - - } // end write - - - /** - * Method added by PHIL. [Thanks, PHIL. -Rob] - * This pads the buffer without closing the stream. - * - * @throws java.io.IOException if there's an error. - */ - public void flushBase64() throws java.io.IOException { - if (position > 0) { - if (encode) { - out.write(encode3to4(b4, buffer, position, options)); - position = 0; - } // end if: encoding - else { - throw new java.io.IOException("Base64 input not properly padded."); - } // end else: decoding - } // end if: buffer partially full - - } // end flush - - - /** - * Flushes and closes (I think, in the superclass) the stream. - * - * @since 1.3 - */ - @Override - public void close() throws java.io.IOException { - // 1. Ensure that pending characters are written - flushBase64(); - - // 2. Actually close the stream - // Base class both flushes and closes. - super.close(); - - buffer = null; - out = null; - } // end close - - - /** - * Suspends encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @throws java.io.IOException if there's an error flushing - * @since 1.5.1 - */ - public void suspendEncoding() throws java.io.IOException { - flushBase64(); - this.suspendEncoding = true; - } // end suspendEncoding - - - /** - * Resumes encoding of the stream. - * May be helpful if you need to embed a piece of - * base64-encoded data in a stream. - * - * @since 1.5.1 - */ - public void resumeEncoding() { - this.suspendEncoding = false; - } // end resumeEncoding - - - } // end inner class OutputStream - - -} // end class Base64 \ No newline at end of file diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Common.java b/src/main/java/org/torusresearch/torusutils/helpers/Common.java new file mode 100644 index 0000000..32da76c --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/Common.java @@ -0,0 +1,111 @@ +package org.torusresearch.torusutils.helpers; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.VerifierKey; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.VerifierLookupResponse; +import org.torusresearch.torusutils.types.common.KeyLookup.KeyResult; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +public class Common { + private Common() { + } + + public static KeyResult normalizeKeyResult(@NotNull VerifierLookupResponse result) { + Boolean isNewKey = false; + if (result.is_new_key != null) { + isNewKey = result.is_new_key; + } + KeyResult finalResult = new KeyResult(isNewKey); + if (result.keys.length > 0) { + VerifierKey finalKey = result.keys[0]; + finalResult.keys = new VerifierKey[]{ finalKey }; + } + return finalResult; + } + + public static String padLeft(@NotNull String inputString, @NotNull Character padChar, int length) { + StringBuilder sb = new StringBuilder(inputString); + int count = length - sb.length(); + while(count > 0) { + sb.insert(0, padChar); + count--; + } + return sb.toString(); + } + + public static String trimLeadingZeros(String source) { + for (int i = 0; i < source.length(); ++i) { + char c = source.charAt(i); + if (c != '0') { + return source.substring(i); + } + } + return "0"; + } + + public static List> kCombinations(@NotNull List set, int k) { + List> combs = new ArrayList<>(); + + if ((k == 0) || k > set.size()) + { + return combs; + } + + if (k == set.size()) { + combs.add(set); + return combs; + } + + if (k == 1) { + for (Integer i : set) { + ArrayList arrList = new ArrayList<>(); + arrList.add(i); + combs.add(arrList); + } + return combs; + } + + for (int i = 0; i < ((set.size() - k) + 1); i++) { + List> tailCombs = kCombinations(set.subList(i + 1, set.size()), k - 1); + for (List tailComb : tailCombs) { + List prependedComb = new ArrayList<>(); + prependedComb.add(set.get(i)); + prependedComb.addAll(tailComb); + combs.add(prependedComb); + } + } + return combs; + } + + public static Integer calculateMedian(@NotNull List arr) { + int arrSize = arr.size(); + + if (arrSize == 0) return 0; + + Collections.sort(arr); + + // odd length + if (arrSize % 2 != 0) { + return arr.get(arrSize / 2); + } + + // return average of two mid values in case of even arrSize + Integer mid1 = arr.get(arrSize / 2 - 1); + Integer mid2 = arr.get(arrSize / 2); + return (mid1+mid2)/2; + } + + public static String strip04Prefix(@NotNull String pubKey) { + if (pubKey.startsWith("04")) { + return pubKey.substring(2); + } + return pubKey; + } + + public static boolean isEmpty(@org.jetbrains.annotations.Nullable final CharSequence cs) { + return cs == null || cs.length() == 0; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/ECPointArithmetic.java b/src/main/java/org/torusresearch/torusutils/helpers/ECPointArithmetic.java deleted file mode 100644 index 3d1a349..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/ECPointArithmetic.java +++ /dev/null @@ -1,198 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import java.math.BigInteger; -import java.security.spec.ECFieldFp; -import java.security.spec.EllipticCurve; - -// http://stackoverflow.com/questions/11190860/point-multiplication-in-elliptic-curvves -public class ECPointArithmetic { - EllipticCurve ec; - ECFieldFp ef; - private final BigInteger x; - private final BigInteger y; - private final BigInteger z; - private BigInteger zinv; - private final BigInteger one = BigInteger.ONE; - private final BigInteger zero = BigInteger.ZERO; - private boolean infinity; - - public ECPointArithmetic(EllipticCurve ec, BigInteger x, BigInteger y, BigInteger z) { - this.ec = ec; - this.x = x; - this.y = y; - this.ef = (ECFieldFp) ec.getField(); - - // Projective coordinates: either zinv == null or z * zinv == 1 - // z and zinv are just BigIntegers, not fieldElements - if (z == null) { - this.z = BigInteger.ONE; - } else { - this.z = z; - } - this.zinv = null; - infinity = false; - } - - public BigInteger getX() { - if (this.zinv == null) { - this.zinv = this.z.modInverse(this.ef.getP()); - } - return this.x.multiply(this.zinv).mod(this.ef.getP()); - } - - public BigInteger getY() { - if (this.zinv == null) { - this.zinv = this.z.modInverse(this.ef.getP()); - } - return this.y.multiply(this.zinv).mod(this.ef.getP()); - } - - public boolean pointEquals(ECPointArithmetic other) { - if (other == this) { - return true; - } - if (this.isInfinity()) { - return other.isInfinity(); - } - if (other.isInfinity()) { - return this.isInfinity(); - } - BigInteger u, v; - // u = Y2 * Z1 - Y1 * Z2 - u = other.y.multiply(this.z).subtract(this.y.multiply(other.z)).mod(this.ef.getP()); - if (!u.equals(BigInteger.ZERO)) { - return false; - } - // v = X2 * Z1 - X1 * Z2 - v = other.x.multiply(this.z).subtract(this.x.multiply(other.z)).mod(this.ef.getP()); - return v.equals(BigInteger.ZERO); - } - - public boolean isInfinity() { - - if ((this.x == zero) && (this.y == zero)) { - return true; - } - return this.z.equals(BigInteger.ZERO) && !this.y.equals(BigInteger.ZERO); - - } - - public ECPointArithmetic negate() { - return new ECPointArithmetic(this.ec, this.x, this.y.negate(), this.z); - } - - public ECPointArithmetic add(ECPointArithmetic b) { - if (this.isInfinity()) { - return b; - } - if (b.isInfinity()) { - return this; - } - ECPointArithmetic R = new ECPointArithmetic(this.ec, zero, zero, null); - // u = Y2 * Z1 - Y1 * Z2 - BigInteger u = b.y.multiply(this.z).subtract(this.y.multiply(b.z)).mod(this.ef.getP()); - // v = X2 * Z1 - X1 * Z2 - BigInteger v = b.x.multiply(this.z).subtract(this.x.multiply(b.z)).mod(this.ef.getP()); - - if (BigInteger.ZERO.equals(v)) { - if (BigInteger.ZERO.equals(u)) { - return this.twice(); // this == b, so double - } - - infinity = true; // this = -b, so infinity - return R; - } - - BigInteger THREE = new BigInteger("3"); - BigInteger x1 = this.x; - BigInteger y1 = this.y; - BigInteger x2 = b.x; - BigInteger y2 = b.y; - - BigInteger v2 = v.pow(2); - BigInteger v3 = v2.multiply(v); - BigInteger x1v2 = x1.multiply(v2); - BigInteger zu2 = u.pow(2).multiply(this.z); - - // x3 = v * (z2 * (z1 * u^2 - 2 * x1 * v^2) - v^3) - BigInteger x3 = zu2.subtract(x1v2.shiftLeft(1)).multiply(b.z).subtract(v3).multiply(v).mod(this.ef.getP()); - - // y3 = z2 * (3 * x1 * u * v^2 - y1 * v^3 - z1 * u^3) + u * v^3 - BigInteger y3 = x1v2.multiply(THREE).multiply(u).subtract(y1.multiply(v3)).subtract(zu2.multiply(u)).multiply(b.z).add(u.multiply(v3)).mod(this.ef.getP()); - - // z3 = v^3 * z1 * z2 - BigInteger z3 = v3.multiply(this.z).multiply(b.z).mod(this.ef.getP()); - - return new ECPointArithmetic(this.ec, x3, y3, z3); - } - - public ECPointArithmetic twice() { - if (this.isInfinity()) { - return this; - } - ECPointArithmetic R = new ECPointArithmetic(this.ec, zero, zero, null); - if (this.y.signum() == 0) { - infinity = true; - return R; - } - - BigInteger THREE = new BigInteger("3"); - BigInteger x1 = this.x; - BigInteger y1 = this.y; - - BigInteger y1z1 = y1.multiply(this.z); - BigInteger y1sqz1 = y1z1.multiply(y1).mod(this.ef.getP()); - BigInteger a = this.ec.getA(); - - // w = 3 * x1^2 + a * z1^2 - BigInteger w = x1.pow(2).multiply(THREE); - - if (!BigInteger.ZERO.equals(a)) { - w = w.add(this.z.pow(2).multiply(a)); - } - - w = w.mod(this.ef.getP()); - - // x3 = 2 * y1 * z1 * (w^2 - 8 * x1 * y1^2 * z1) - BigInteger x3 = w.pow(2).subtract(x1.shiftLeft(3).multiply(y1sqz1)).shiftLeft(1).multiply(y1z1).mod(this.ef.getP()); - - // y3 = 4 * y1^2 * z1 * (3 * w * x1 - 2 * y1^2 * z1) - w^3 - BigInteger y3 = (w.multiply(THREE).multiply(x1).subtract(y1sqz1.shiftLeft(1))).shiftLeft(2).multiply(y1sqz1).subtract(w.pow(2).multiply(w)).mod(this.ef.getP()); - - // z3 = 8 * (y1 * z1)^3 - BigInteger z3 = y1z1.pow(2).multiply(y1z1).shiftLeft(3).mod(this.ef.getP()); - - return new ECPointArithmetic(this.ec, x3, y3, z3); - } - - public ECPointArithmetic multiply(BigInteger k) { - if (this.isInfinity()) { - return this; - } - - ECPointArithmetic R = new ECPointArithmetic(this.ec, zero, zero, null); - if (k.signum() == 0) { - infinity = true; - return R; - } - - BigInteger e = k; - BigInteger h = e.multiply(new BigInteger("3")); - - ECPointArithmetic neg = this.negate(); - R = this; - - int i; - for (i = h.bitLength() - 2; i > 0; --i) { - R = R.twice(); - boolean hBit = h.testBit(i); - boolean eBit = e.testBit(i); - - if (hBit != eBit) { - R = R.add(hBit ? this : neg); - } - } - - return R; - } -} \ No newline at end of file diff --git a/src/main/java/org/torusresearch/torusutils/helpers/KeyUtils.java b/src/main/java/org/torusresearch/torusutils/helpers/KeyUtils.java new file mode 100644 index 0000000..e80de63 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/KeyUtils.java @@ -0,0 +1,272 @@ +package org.torusresearch.torusutils.helpers; +import com.google.gson.Gson; +import org.bouncycastle.crypto.ec.CustomNamedCurves; +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.interfaces.ECPrivateKey; +import org.bouncycastle.jce.interfaces.ECPublicKey; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.jce.spec.ECPrivateKeySpec; +import org.bouncycastle.jce.spec.ECPublicKeySpec; +import org.bouncycastle.math.ec.ECPoint; +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.fetchnodedetails.types.TorusNodePub; +import org.torusresearch.torusutils.apis.requests.NonceMetadataParams; +import org.torusresearch.torusutils.apis.requests.SetNonceData; +import org.torusresearch.torusutils.helpers.encryption.Encryption; +import org.torusresearch.torusutils.types.Point; +import org.torusresearch.torusutils.types.Polynomial; +import org.torusresearch.torusutils.types.Share; +import org.torusresearch.torusutils.types.common.ImportedShare; +import org.torusresearch.torusutils.types.common.PrivateKeyData; +import org.torusresearch.torusutils.types.common.TorusKeyType; +import org.torusresearch.torusutils.types.common.ecies.Ecies; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; +import org.web3j.crypto.ECKeyPair; +import org.web3j.crypto.Hash; +import org.web3j.crypto.Keys; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.InvalidAlgorithmParameterException; +import java.security.KeyFactory; +import java.security.KeyPair; +import java.security.KeyPairGenerator; +import java.security.NoSuchAlgorithmException; +import java.security.NoSuchProviderException; +import java.security.PrivateKey; +import java.security.Provider; +import java.security.PublicKey; +import java.security.SecureRandom; +import java.security.spec.ECGenParameterSpec; +import java.util.ArrayList; +import java.util.Base64; +import java.util.List; +import java.util.Map; + +public class KeyUtils { + static final protected Provider provider = new BouncyCastleProvider(); + static final protected ECParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1"); + + + public static KeyPair generateKeyPair() throws NoSuchAlgorithmException, InvalidAlgorithmParameterException { + KeyPairGenerator kpgen = KeyPairGenerator.getInstance("ECDH", provider); + kpgen.initialize(new ECGenParameterSpec("secp256k1"), new SecureRandom()); + return kpgen.generateKeyPair(); + } + + public static byte [] serializePublicKey (@NotNull PublicKey key, @NotNull Boolean compressed) { + org.bouncycastle.jce.interfaces.ECPublicKey eckey = (ECPublicKey)key; + return eckey.getQ().getEncoded(compressed); + } + + public static PublicKey deserializePublicKey (@NotNull byte [] data) throws Exception + { + ECPublicKeySpec pubKey = new ECPublicKeySpec( + params.getCurve().decodePoint(data), params); + KeyFactory kf = KeyFactory.getInstance("ECDH", provider); + return kf.generatePublic(pubKey); + } + + public static byte [] serializePrivateKey (@NotNull PrivateKey key) { + ECPrivateKey eckey = (ECPrivateKey)key; + return eckey.getD().toByteArray(); + } + public static PrivateKey deserializePrivateKey (@NotNull byte [] data) throws Exception { + ECPrivateKeySpec prvkey = new org.bouncycastle.jce.spec.ECPrivateKeySpec(new BigInteger(data), params); + KeyFactory kf = KeyFactory.getInstance("ECDH", provider); + return kf.generatePrivate(prvkey); + } + + private static String randomNonce() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + KeyPair keyPair = generateKeyPair(); + return Common.padLeft(Hex.toHexString(KeyUtils.serializePrivateKey(keyPair.getPrivate())), '0', 64); + } + + public static BigInteger getOrderOfCurve() { + return params.getN(); + } + + public static String privateToPublic(@NotNull BigInteger key) { + return "04" + Common.padLeft(ECKeyPair.create(key).getPublicKey().toString(16), '0', 128); + } + + @SuppressWarnings("unused") + public static BigInteger generatePrivate() throws InvalidAlgorithmParameterException, NoSuchAlgorithmException, NoSuchProviderException { + return Keys.createEcKeyPair().getPrivateKey(); + } + + @SuppressWarnings("unused") + public static String generateAddressFromPrivKey(@NotNull String privateKey) throws Exception { + PrivateKey privKey = deserializePrivateKey(Hex.decode(privateKey)); + return Keys.toChecksumAddress(Keys.getAddress(ECKeyPair.create(privKey.getEncoded()))); + } + + public static String generateAddressFromPubKey(@NotNull String publicKeyX, @NotNull String publicKeyY) { + String finalPublicKey = publicKeyX + publicKeyY; + return Keys.toChecksumAddress(Keys.getAddress(finalPublicKey)); + } + + public static String[] getPublicKeyCoords(@NotNull String pubKey) throws TorusUtilError { + String publicKeyUnprefixed = pubKey; + if (publicKeyUnprefixed.length() > 128) { + publicKeyUnprefixed = Common.strip04Prefix(publicKeyUnprefixed); + } + + if (publicKeyUnprefixed.length() <= 128) { + Common.padLeft(publicKeyUnprefixed, '0', 128); + } else { + throw new TorusUtilError("Invalid public key size"); + } + + String xCoord = publicKeyUnprefixed.substring(0, 64); + String yCoord = publicKeyUnprefixed.substring(64); + + return new String[]{xCoord, yCoord}; + } + + public static String getPublicKeyFromCoords(@NotNull String pubKeyX, @NotNull String pubKeyY, boolean prefixed) { + String X = Common.padLeft(pubKeyX, '0', 64); + String Y = Common.padLeft(pubKeyY,'0', 64); + + return prefixed ? "04" + X + Y : X + Y; + } + + public static String combinePublicKeysFromStrings(@NotNull List keys, boolean compressed) throws TorusUtilError { + List collection = new ArrayList<>(); + + for (String item : keys) { + ECPoint point = CustomNamedCurves.getByName("secp256k1").getCurve().decodePoint(Hex.decode(item)); + collection.add(point); + } + + return combinePublicKeys(collection, compressed); + } + + public static String combinePublicKeys(@NotNull List keys, boolean compressed) throws TorusUtilError { + if (keys.isEmpty()) { + throw new TorusUtilError("The keys list cannot be empty"); + } + + ECPoint combinedPoint = keys.get(0); + for (int i = 1; i < keys.size(); i++) { + combinedPoint = combinedPoint.add(keys.get(i)); + } + + byte[] serializedPoint = compressed ? combinedPoint.getEncoded(true) : combinedPoint.getEncoded(false); + return Hex.toHexString(serializedPoint); + } + + public static PrivateKeyData generateKeyData(@NotNull String privateKey) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + BigInteger scalar = new BigInteger(privateKey,16); + BigInteger randomNonce = new BigInteger(randomNonce(), 16); + + BigInteger oAuthKey = scalar.subtract(randomNonce).mod(getOrderOfCurve()); + + return new PrivateKeyData( + Common.padLeft(oAuthKey.toString(16), '0', 64), + KeyUtils.privateToPublic(oAuthKey), + Common.padLeft(randomNonce.toString(16), '0', 64), + Common.padLeft(oAuthKey.toString(16), '0', 64), + KeyUtils.privateToPublic(oAuthKey), + Common.padLeft(scalar.toString(16), '0', 64), + KeyUtils.privateToPublic(scalar) + ); + } + + public static NonceMetadataParams generateNonceMetadataParams(@NotNull String operation, @NotNull BigInteger privateKey, @Nullable BigInteger nonce, @NotNull Integer serverTimeOffset) { + BigInteger timeSeconds = BigInteger.valueOf(System.currentTimeMillis() / 1000L); + BigInteger timestamp = timeSeconds.add(BigInteger.valueOf(serverTimeOffset)); + + // Serialize public key into padded X and Y coordinates + String derivedPubKeyString = KeyUtils.privateToPublic(privateKey); + String derivedPubKeyX = derivedPubKeyString.substring(2, 66); + String derivedPubKeyY = derivedPubKeyString.substring(66); + + // Create SetNonceData object with operation and timestamp + SetNonceData setNonceData = new SetNonceData(operation, (nonce != null) ? Common.padLeft(nonce.toString(16), '0', 64) : null, null, timestamp.toString(16)); + + // Convert SetNonceData object to JSON string + Gson gson = new Gson(); + String encodedData = gson.toJson(setNonceData); + + // Hash the JSON string using keccak256 (SHA-3) + byte[] hashedData = Hash.sha3(encodedData.getBytes(StandardCharsets.UTF_8)); + + // Sign the hashed data using ECDSA with the private key + SecureRandom random = new SecureRandom(); + ECDSASigner signer = new ECDSASigner(); + ECDomainParameters domainParams = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed()); + ECPrivateKeyParameters privKeyParams = new ECPrivateKeyParameters(privateKey, domainParams); + signer.init(true, new ParametersWithRandom(privKeyParams, random)); + BigInteger[] signature = signer.generateSignature(hashedData); + + // Format the signature into a padded hexadecimal string + String sig = Common.padLeft(signature[0].toString(16), '0', 64) + + Common.padLeft(signature[1].toString(16), '0', 64) + + Common.padLeft("", '0', 2); // Assuming padding to ensure consistent length + + // Convert the hexadecimal signature string to bytes + byte[] sigBytes = Hex.decode(sig); + + // Encode the signature bytes to Base64 + String finalSig = new String(Base64.getEncoder().encode(sigBytes), StandardCharsets.UTF_8); + + // Return a new NonceMetadataParams object with the derived values + return new NonceMetadataParams(derivedPubKeyX, derivedPubKeyY, setNonceData, + Base64.getEncoder().encodeToString(encodedData.getBytes(StandardCharsets.UTF_8)), finalSig, null, null, null); + } + + public static List generateShares(@NotNull TorusKeyType keyType, @NotNull Integer serverTimeOffset, @NotNull List nodeIndexes, @NotNull List nodePubKeys, @NotNull String privateKey) throws Exception { + if (keyType != TorusKeyType.secp256k1) { + throw TorusUtilError.RUNTIME_ERROR("Unsupported key type"); + } + + PrivateKeyData keyData = generateKeyData(privateKey); + + int threshold = (nodePubKeys.size() / 2) + 1; + int degree = threshold - 1; + List nodeIndexesBN = new ArrayList<>(nodeIndexes); + + Polynomial poly = Lagrange.generateRandomPolynomial(degree, new BigInteger(keyData.getOAuthKey(), 16), null); + Map shares = poly.generateShares(nodeIndexesBN.toArray(new BigInteger[0])); + + NonceMetadataParams nonceParams = KeyUtils.generateNonceMetadataParams("getOrSetNonce", new BigInteger(keyData.getSigningKey(), 16), new BigInteger(keyData.getNonce(), 16), serverTimeOffset); + + List encShares = new ArrayList<>(); + for (int i = 0; i < nodePubKeys.size(); i++) { + String indexHex = String.format("%064x", nodeIndexes.get(i)); + Share shareInfo = shares.get(indexHex); + String nodePub = KeyUtils.getPublicKeyFromCoords(nodePubKeys.get(i).getX(), nodePubKeys.get(i).getY(), true); + String share = Common.padLeft(shareInfo.getShare().toString(16), '0', 64); + Ecies encryptedMsg = Encryption.encrypt(Hex.decode(nodePub), share); + encShares.add(encryptedMsg); + } + + List sharesData = new ArrayList<>(); + for (int i = 0; i < nodePubKeys.size(); i++) { + Ecies encrypted = encShares.get(i); + String[] oAuthPub = getPublicKeyCoords(keyData.getOAuthPubKey()); + String[] signingPub = getPublicKeyCoords(keyData.getSigningPubKey()); + String[] finalPub = getPublicKeyCoords(keyData.getFinalPubKey()); + Point finalPoint = new Point(finalPub[0], finalPub[1]); + + ImportedShare importShare = new ImportedShare( + oAuthPub[0], oAuthPub[1], finalPoint, + signingPub[0], signingPub[1], encrypted.ciphertext, + new EciesHexOmitCipherText(encrypted.iv, encrypted.ephemPublicKey, encrypted.mac, "AES256"), null, + Integer.parseInt(nodeIndexes.get(i).toString(16), 16), keyType, + nonceParams.encodedData, nonceParams.signature); + + sharesData.add(importShare); + } + return sharesData; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Lagrange.java b/src/main/java/org/torusresearch/torusutils/helpers/Lagrange.java new file mode 100644 index 0000000..9ea28fe --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/Lagrange.java @@ -0,0 +1,206 @@ +package org.torusresearch.torusutils.helpers; + +import static org.torusresearch.torusutils.helpers.KeyUtils.getOrderOfCurve; + +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.types.Point; +import org.torusresearch.torusutils.types.Polynomial; +import org.torusresearch.torusutils.types.Share; + +import java.math.BigInteger; +import java.security.InvalidAlgorithmParameterException; +import java.security.NoSuchAlgorithmException; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Comparator; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import io.reactivex.annotations.Nullable; + +public class Lagrange { + + public static BigInteger generatePrivateExcludingIndexes(@NotNull List shareIndexes) throws InvalidAlgorithmParameterException, NoSuchAlgorithmException { + BigInteger key = new BigInteger(Common.padLeft(Hex.toHexString(KeyUtils.serializePrivateKey(KeyUtils.generateKeyPair().getPrivate())), '0', 64), 16); + + if (shareIndexes.contains(key)) { + return generatePrivateExcludingIndexes(shareIndexes); + } + + return key; + } + + public static BigInteger[] generateEmptyBNArray(int length) { + BigInteger[] array = new BigInteger[length]; + Arrays.fill(array, BigInteger.ZERO); + return array; + } + + public static BigInteger denominator(int i, @NotNull Point[] innerPoints) { + BigInteger result = BigInteger.ONE; + BigInteger xi = innerPoints[i].getX(); + for (int j = 0; j < innerPoints.length; j++) { + if (i != j) { + BigInteger tmp = xi; + tmp = (tmp.subtract(innerPoints[j].getX()).mod(getOrderOfCurve())); + result = result.multiply(tmp).mod(getOrderOfCurve()); + } + } + return result; + } + + public static BigInteger[] interpolationPoly(int i, @NotNull Point[] innerPoints) { + BigInteger[] coefficients = generateEmptyBNArray(innerPoints.length); + BigInteger d = denominator(i, innerPoints); + if (d.equals(BigInteger.ZERO)) { + throw new IllegalArgumentException("Denominator for interpolationPoly is 0"); + } + + coefficients[0] = d.modInverse(getOrderOfCurve()); + + for (int k = 0; k < innerPoints.length; k++) { + BigInteger[] newCoefficients = generateEmptyBNArray(innerPoints.length); + if (k != i) { + int j = (k < i) ? k + 1 : k; + j -= 1; + + while (j >= 0) { + newCoefficients[j + 1] = newCoefficients[j + 1].add(coefficients[j]).mod(getOrderOfCurve()); + BigInteger tmp = innerPoints[k].getX(); + tmp = tmp.multiply(coefficients[j]).mod(getOrderOfCurve()); + newCoefficients[j] = newCoefficients[j].subtract(tmp).mod(getOrderOfCurve()); + j -= 1; + } + coefficients = newCoefficients; + } + } + + return coefficients; + } + + public static Point[] pointSort(@NotNull Point[] innerPoints) { + Point[] pointsClone = Arrays.copyOf(innerPoints, innerPoints.length); + Arrays.sort(pointsClone, Comparator.comparing(Point::getX)); + return pointsClone; + } + @SuppressWarnings("unused") + private static List pointSort(@NotNull List innerPoints) { + List pointArrClone = new ArrayList<>(innerPoints); + pointArrClone.sort(Comparator.comparing(Point::getX)); + return pointArrClone; + } + + public static Polynomial lagrange(@NotNull Point[] unsortedPoints) { + Point[] sortedPoints = pointSort(unsortedPoints); + BigInteger[] polynomial = generateEmptyBNArray(sortedPoints.length); + for (int i = 0; i < sortedPoints.length; i++) { + BigInteger[] coefficients = interpolationPoly(i, sortedPoints); + for (int k = 0; k < sortedPoints.length; k++) { + BigInteger tmp = sortedPoints[i].getY(); + tmp = tmp.multiply(coefficients[k]).mod(getOrderOfCurve()); + polynomial[k] = polynomial[k].add(tmp).mod(getOrderOfCurve()); + } + } + return new Polynomial(polynomial); + } + + public static Polynomial lagrangeInterpolatePolynomial(@NotNull Point[] points) { + return lagrange(points); + } + + public static BigInteger lagrangeInterpolation(@NotNull BigInteger[] shares, @NotNull BigInteger[] nodeIndex) throws TorusUtilError { + if (shares.length != nodeIndex.length) { + return null; + } + + int sharesDecrypt = 0; + + BigInteger secret = BigInteger.ZERO; + for (int i = 0; i < shares.length; i++) { + BigInteger upper = BigInteger.ONE; + BigInteger lower = BigInteger.ONE; + for (int j = 0; j < shares.length; j++) { + if (i != j) { + upper = upper.multiply(nodeIndex[j].negate()).mod(getOrderOfCurve()); + BigInteger temp = nodeIndex[i].subtract(nodeIndex[j]).mod(getOrderOfCurve()); + lower = lower.multiply(temp).mod(getOrderOfCurve()); + } + } + BigInteger inverse = lower.modInverse(getOrderOfCurve()); + BigInteger delta = upper.multiply(inverse).mod(getOrderOfCurve()); + delta = delta.multiply(shares[i]).mod(getOrderOfCurve()); + secret = secret.add(delta).mod(getOrderOfCurve()); + sharesDecrypt += 1; + } + + if (secret.equals(BigInteger.ZERO)) { + throw TorusUtilError.INTERPOLATION_FAILED; + } + + if (sharesDecrypt == shares.length) { + return secret; + } + + throw TorusUtilError.INTERPOLATION_FAILED; + } + + public static Polynomial generateRandomPolynomial(int degree, @Nullable BigInteger secret, @Nullable List deterministicShares) throws Exception { + BigInteger actualS = secret; + if (actualS == null) { + List excludeList = new ArrayList<>(); + excludeList.add(actualS); + actualS = generatePrivateExcludingIndexes(excludeList); + } + + // If no deterministic shares provided, generate random shares + if (deterministicShares == null) { + List poly = new ArrayList<>(); + poly.add(actualS); + + for (int i = 0; i < degree; i++) { + BigInteger share = generatePrivateExcludingIndexes(poly); + poly.add(share); + } + + return new Polynomial(poly.toArray(new BigInteger[0])); + } + + // Validate deterministic shares count + if (deterministicShares.size() > degree) { + throw new Exception("Deterministic shares in generateRandomPolynomial should be less or equal than degree to ensure an element of randomness"); + } + + // Initialize points map + Map points = new LinkedHashMap<>(); + + // Add deterministic shares to points map + for (Share share : deterministicShares) { + points.put(Common.padLeft(share.getShareIndex().toString(16), '0', 64), new Point(share.getShareIndex(), share.getShare())); + } + + // Calculate remaining shares to fill the polynomial + int remainingDegree = degree - deterministicShares.size(); + for (int i = 0; i < remainingDegree; i++) { + List excludeList = new ArrayList<>(); + excludeList.add(BigInteger.ZERO); + BigInteger shareIndex = generatePrivateExcludingIndexes(excludeList); + + // Ensure unique share index + while (points.containsKey(Common.padLeft(shareIndex.toString(16),'0', 64))) { + shareIndex = generatePrivateExcludingIndexes(excludeList); + } + + String serializedKey = Common.padLeft(Hex.toHexString(KeyUtils.serializePrivateKey(KeyUtils.generateKeyPair().getPrivate())),'0',64); + points.put(Common.padLeft(shareIndex.toString(16),'0',64), + new Point(shareIndex, new BigInteger(serializedKey, 16))); + } + + // Add point for zero index + points.put("0", new Point(BigInteger.ZERO, actualS)); + + // Interpolate polynomial using Lagrange interpolation + return lagrangeInterpolatePolynomial(new ArrayList<>(points.values()).toArray(new Point[0])); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/MetadataUtils.java b/src/main/java/org/torusresearch/torusutils/helpers/MetadataUtils.java new file mode 100644 index 0000000..ef8bef9 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/MetadataUtils.java @@ -0,0 +1,119 @@ +package org.torusresearch.torusutils.helpers; + +import static org.torusresearch.torusutils.helpers.encryption.Encryption.decrypt; + +import com.google.gson.Gson; +import com.google.gson.GsonBuilder; + +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.crypto.params.ECPrivateKeyParameters; +import org.bouncycastle.crypto.params.ParametersWithRandom; +import org.bouncycastle.crypto.signers.ECDSASigner; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.json.JSONObject; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; +import org.torusresearch.torusutils.apis.APIUtils; +import org.torusresearch.torusutils.apis.requests.GetMetadataParams; +import org.torusresearch.torusutils.apis.requests.GetNonceParams; +import org.torusresearch.torusutils.apis.requests.GetNonceSetDataParams; +import org.torusresearch.torusutils.apis.requests.MetadataParams; +import org.torusresearch.torusutils.apis.requests.SetData; +import org.torusresearch.torusutils.apis.responses.GetMetadataResponse; +import org.torusresearch.torusutils.apis.responses.GetOrSetNonceResult; +import org.torusresearch.torusutils.types.common.TorusKeyType; +import org.torusresearch.torusutils.types.common.ecies.Ecies; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; +import org.web3j.crypto.Hash; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.SecureRandom; +import java.util.Base64; +import java.util.concurrent.ExecutionException; + +public class MetadataUtils { + private MetadataUtils() { + } + + public static String decryptNodeData(@NotNull EciesHexOmitCipherText eciesData, @NotNull String ciphertextHex, @NotNull String privKey) throws Exception { + Ecies eciesOpts = new Ecies( + eciesData.iv, + eciesData.ephemPublicKey, + ciphertextHex, + eciesData.mac + ); + return Hex.toHexString(decrypt(privKey, eciesOpts)); + } + + + public static MetadataParams generateMetadataParams(@NotNull Integer serverTimeOffset, @NotNull String message, @NotNull String privateKey, @NotNull String X, @NotNull String Y, @Nullable TorusKeyType keyType) { + int timeStamp = serverTimeOffset + (int) (System.currentTimeMillis() / 1000L); + SetData setData = new SetData(message, String.valueOf(timeStamp)); + + Gson gson = new Gson(); + String setDataString = gson.toJson(setData); + ECParameterSpec params = ECNamedCurveTable.getParameterSpec("secp256k1"); + BigInteger key = new BigInteger(privateKey, 16); + byte[] hashedData = Hash.sha3(setDataString.getBytes(StandardCharsets.UTF_8)); + // Sign the hashed data using ECDSA with the private key + SecureRandom random = new SecureRandom(); + ECDSASigner signer = new ECDSASigner(); + ECDomainParameters domainParams = new ECDomainParameters(params.getCurve(), params.getG(), params.getN(), params.getH(), params.getSeed()); + ECPrivateKeyParameters privKeyParams = new ECPrivateKeyParameters(key, domainParams); + signer.init(true, new ParametersWithRandom(privKeyParams, random)); + BigInteger[] signature = signer.generateSignature(hashedData); + + String sig = Common.padLeft(signature[0].toString(16), '0', 64) + Common.padLeft(signature[1].toString(16), '0', 64) + Common.padLeft("", '0', 2); + byte[] sigBytes = Base64.getEncoder().encode(Hex.decode(sig)); + String finalSig = new String(sigBytes, StandardCharsets.UTF_8); + return new MetadataParams(X, Y, setData, finalSig, null, keyType); + } + + public static GetMetadataResponse getMetadata(String legacyMetadataHost, GetMetadataParams data) throws ExecutionException, InterruptedException { + Gson gson = new Gson(); + String metadata = gson.toJson(data, GetMetadataParams.class); + String metadataApiResponse = APIUtils.post(legacyMetadataHost + "/get", metadata, true).get(); + return gson.fromJson(metadataApiResponse, GetMetadataResponse.class); + } + + public static GetOrSetNonceResult getOrSetNonce(@NotNull String legacyMetadataHost, @NotNull String X, @NotNull String Y, @NotNull Integer serverTimeOffset, @Nullable String privateKey, Boolean getOnly, @Nullable TorusKeyType keyType) throws Exception { + String msg = getOnly ? "getNonce": "getOrSetNonce"; + Gson gson = new GsonBuilder().disableHtmlEscaping().create(); + String data; + if (privateKey != null) { + MetadataParams params = generateMetadataParams(serverTimeOffset, msg, privateKey, X, Y, keyType); + data = gson.toJson(params); + } else { + GetNonceParams params = new GetNonceParams(X, Y , new GetNonceSetDataParams(msg)); + data = gson.toJson(params); + } + + String postResult = APIUtils.post(legacyMetadataHost + "/get_or_set_nonce", data, true).get(); + JSONObject jsonObject = new JSONObject(postResult); + if (jsonObject.has("ipfs")) { + jsonObject.remove("ipfs"); + } + return gson.fromJson(jsonObject.toString(), GetOrSetNonceResult.class); + } + + + public static GetOrSetNonceResult getOrSetSapphireMetadataNonce(@NotNull String metadataHost, @NotNull Web3AuthNetwork network, @NotNull String X, @NotNull String Y, @Nullable Integer serverTimeOffset, @Nullable String privateKey, Boolean getOnly, @Nullable TorusKeyType keyType) throws Exception { + // fix this comparison in fetchnodedetails, comparison should be against .sapphire() + int timeOffset = 0; + if (serverTimeOffset != null) { + timeOffset = serverTimeOffset; + } + + timeOffset += (int) (System.currentTimeMillis() / 1000); + + if (network.name().contains("sapphire")) { + return getOrSetNonce(metadataHost, X, Y, timeOffset, privateKey, getOnly, keyType); + } else { + throw TorusUtilError.METADATA_NONCE_MISSING; + } + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/NodeUtils.java b/src/main/java/org/torusresearch/torusutils/helpers/NodeUtils.java new file mode 100644 index 0000000..fed27b2 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/NodeUtils.java @@ -0,0 +1,573 @@ +package org.torusresearch.torusutils.helpers; + +import static org.torusresearch.torusutils.helpers.KeyUtils.getOrderOfCurve; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.SerializationFeature; +import com.google.gson.Gson; + +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; +import org.torusresearch.torusutils.TorusUtils; +import org.torusresearch.torusutils.apis.APIUtils; +import org.torusresearch.torusutils.apis.JsonRPCErrorInfo; +import org.torusresearch.torusutils.apis.JsonRPCResponse; +import org.torusresearch.torusutils.apis.PubKey; +import org.torusresearch.torusutils.apis.requests.CommitmentRequestParams; +import org.torusresearch.torusutils.apis.requests.GetMetadataParams; +import org.torusresearch.torusutils.apis.requests.GetOrSetKeyParams; +import org.torusresearch.torusutils.apis.requests.ShareRequestItem; +import org.torusresearch.torusutils.apis.requests.ShareRequestParams; +import org.torusresearch.torusutils.apis.responses.CommitmentRequestResult; +import org.torusresearch.torusutils.apis.responses.GetOrSetNonceResult; +import org.torusresearch.torusutils.apis.responses.KeyAssignment; +import org.torusresearch.torusutils.apis.responses.ShareRequestResult; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.VerifierLookupResponse; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.TorusUtilsExtraParams; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.common.ImportedShare; +import org.torusresearch.torusutils.types.common.KeyLookup.KeyLookupResult; +import org.torusresearch.torusutils.types.common.KeyLookup.KeyResult; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.SessionToken; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; +import org.web3j.crypto.Hash; + +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Base64; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.concurrent.CompletableFuture; +import java.util.stream.Collectors; + +import okhttp3.internal.http2.Header; + +public class NodeUtils { + private NodeUtils() { + } + + public static KeyLookupResult getPubKeyOrKeyAssign(@NotNull String[] endpoints, @NotNull Web3AuthNetwork network, @NotNull String verifier, @NotNull String verifierId, @NotNull String legacyMetdadataHost, @Nullable Integer serverTimeOffset, @Nullable String extendedVerifierId) throws Exception { + int threshold = (endpoints.length / 2) + 1; + + BigInteger timeOffset = BigInteger.ZERO; + if (serverTimeOffset != null) { + timeOffset = BigInteger.valueOf(serverTimeOffset); + } + timeOffset = timeOffset.add( new BigInteger(String.valueOf(System.currentTimeMillis() / 1000))); + + GetOrSetKeyParams params = new GetOrSetKeyParams(true, verifier, verifierId, extendedVerifierId, true, true, timeOffset.toString()); + List> lookupPromises = new ArrayList<>(); + for (int i = 0; i < endpoints.length; i++) { + lookupPromises.add(i, APIUtils.post(endpoints[i], APIUtils.generateJsonRPCObject("GetPubKeyOrKeyAssign", + params), true)); + } + + ArrayList> collected = new ArrayList<>(); + + JsonRPCErrorInfo errResult = null; + KeyResult key = null; + List> lookupPubKeys = null; + GetOrSetNonceResult nonce = null; + + Gson json = new Gson(); + for (CompletableFuture lookup: lookupPromises) { + try { + String result = lookup.get(); + + @SuppressWarnings({"unchecked"}) // Due to Type Erasure of Generic Types at Runtime. Java does this to ensure code is compatible with pre-generic versions of Java. + JsonRPCResponse response = json.fromJson(result, JsonRPCResponse.class); + collected.add(response); + lookupPubKeys = collected.stream().filter(item -> item.getError() == null && item.getTypedResult(VerifierLookupResponse.class) != null).collect(Collectors.toList()); + errResult = (JsonRPCErrorInfo) NodeUtils.thresholdSame(collected.stream().filter(item -> item.getError() != null).toArray(), threshold); + ArrayList normalizedKeys = new ArrayList<>(); + for (JsonRPCResponse item : lookupPubKeys) { + VerifierLookupResponse vlr = item.getTypedResult(VerifierLookupResponse.class); + normalizedKeys.add(Common.normalizeKeyResult(vlr)); + } + key = (KeyResult) NodeUtils.thresholdSame(normalizedKeys.toArray(), threshold); + if (key != null) { + break; + } + } catch (Exception e) { + collected.add(null); + } + } + + if (key != null && nonce == null && extendedVerifierId == null && !TorusUtils.isLegacyNetorkRouteMap(network)) { + for (int i = 0; i < lookupPubKeys.size(); i++) { + JsonRPCResponse x1 = lookupPubKeys.get(i); + if (x1 != null && x1.getError() == null) { + VerifierLookupResponse x1Result = x1.getTypedResult(VerifierLookupResponse.class); + String currentNodePubKeyX = Common.padLeft(x1Result.keys[0].pub_key_X,'0',64).toLowerCase(); + String thresholdPubKeyX = Common.padLeft(key.keys[0].pub_key_X,'0',64).toLowerCase(); + if (x1Result.keys[0].nonce_data != null) { + PubNonce pubNonce = x1Result.keys[0].nonce_data.pubNonce; + if (pubNonce != null && currentNodePubKeyX.equals(thresholdPubKeyX)) { + nonce = x1Result.keys[0].nonce_data; + break; + } + } + } + } + + if (nonce == null) { + nonce = MetadataUtils.getOrSetSapphireMetadataNonce(legacyMetdadataHost, network, key.keys[0].pub_key_X,key.keys[0].pub_key_Y, null, null, false, null); + if (nonce.nonce != null) { + nonce.nonce = null; + } + } + } + + ArrayList serverTimeOffsets = new ArrayList<>(); + ArrayList nodeIndexes = new ArrayList<>(); + if (key != null && (nonce != null || extendedVerifierId != null || TorusUtils.isLegacyNetorkRouteMap(network) || errResult != null)) { + for (int i = 0; i < lookupPubKeys.size(); i++) { + JsonRPCResponse x1 = lookupPubKeys.get(i); + VerifierLookupResponse x1Result = x1.getTypedResult(VerifierLookupResponse.class); + if (x1 != null && x1Result != null) { + String currentNodePubKey = x1Result.keys[0].pub_key_X.toLowerCase(); + String thresholdPubKey = key.keys[0].pub_key_X.toLowerCase(); + if (currentNodePubKey.equals(thresholdPubKey)) { + if (x1Result.node_index != null) + { + nodeIndexes.add(Integer.valueOf(x1Result.node_index)); + } + } + if (x1Result.server_time_offset != null) { + serverTimeOffsets.add(Integer.valueOf(x1Result.server_time_offset)); + } else { + serverTimeOffsets.add(0); + } + } + } + } + + Integer finalServerTimeOffset = 0; + if (key != null) { + finalServerTimeOffset = Common.calculateMedian(serverTimeOffsets); + } + return new KeyLookupResult(key, nodeIndexes, finalServerTimeOffset, nonce, errResult); + } + + public static TorusKey retrieveOrImportShare(@NotNull String legacyMetadataHost, @Nullable Integer serverTimeOffset, + @NotNull Boolean enableOneKey, @NotNull String allowHost, @NotNull Web3AuthNetwork network, + @NotNull String clientId, @NotNull String[] endpoints, @NotNull String verifier, @NotNull VerifierParams verifierParams, + @NotNull String idToken, @Nullable ImportedShare[] importedShares, @NotNull String apiKey, @Nullable String newPrivateKey, @NotNull TorusUtilsExtraParams extraParams + ) throws Exception { + int threshold = (endpoints.length / 2) + 1; + + try { + APIUtils.get(allowHost, new Header[]{new Header("x-api-key", apiKey), new Header("Origin", verifier), new Header("verifier", verifier), new Header("verifierid", verifierParams.verifier_id), new Header("network", network.name().toLowerCase()), + new Header("clientid", clientId), new Header("enablegating", "true")}, true).get(); + } catch (Exception e) { + throw TorusUtilError.GATING_ERROR; + } + + KeyPair sessionAuthKey = KeyUtils.generateKeyPair(); + String sessionAuthKeySerialized = Common.padLeft(Hex.toHexString(KeyUtils.serializePrivateKey(sessionAuthKey.getPrivate())),'0', 64); + String pubKey = Hex.toHexString(KeyUtils.serializePublicKey(sessionAuthKey.getPublic(), false)); + String[] pubKeyCoords = KeyUtils.getPublicKeyCoords(pubKey); + String pubKeyX = pubKeyCoords[0]; + String pubKeyY = pubKeyCoords[1]; + String tokenCommitment = Hash.sha3String(idToken); + + boolean isImportShareReq = false; + int importedShareCount = 0; + + if (importedShares != null && importedShares.length > 0) { + if (importedShares.length != endpoints.length) { + throw new Error("Invalid imported shares length"); + } + isImportShareReq = true; + importedShareCount = importedShares.length; + } + + int minRequiredCommitmments = (endpoints.length * 3 / 4) + 1; + + + List> CommitmentRequests = new ArrayList<>(); + + // make commitment requests to endpoints + for (int i = 0; i < endpoints.length; i++) { + CompletableFuture commitmentRequest = APIUtils.post(endpoints[i], APIUtils.generateJsonRPCObject("CommitmentRequest", new CommitmentRequestParams("mug00", tokenCommitment.replace("0x", ""), pubKeyX, pubKeyY, String.valueOf(System.currentTimeMillis()), verifier)), true); + CommitmentRequests.add(i, commitmentRequest); + } + + List nodeSigs = new ArrayList<>(); + int received = 0; // might need to be atomic + + Gson json = new Gson(); + for (CompletableFuture commitment : CommitmentRequests) { + try { + String result = commitment.get(); + @SuppressWarnings({"unchecked"}) // Due to Type Erasure of Generic Types at Runtime. Java does this to ensure code is compatible with pre-generic versions of Java. + JsonRPCResponse response = json.fromJson(result, JsonRPCResponse.class); + if (response != null && response.getError() == null) { + nodeSigs.add(response.getTypedResult(CommitmentRequestResult.class)); + received++; + if (!isImportShareReq) { + if (received >= minRequiredCommitmments) { + break; + } + } + } else { + if (isImportShareReq) { + // cannot continue. all must pass for import + break; + } + } + } catch (Exception e) { + if (isImportShareReq) { + // cannot continue. all must pass for import + break; + } + } + } + + if (importedShareCount > 0 && (nodeSigs.size() != endpoints.length)) { + throw TorusUtilError.COMMITMENT_REQUEST_FAILED; + } + + GetOrSetNonceResult thresholdNonceData = null; + boolean shareImportSuccess = false; + + List shareResponses = new ArrayList<>(); + PubKey thresholdPublicKey = null; + + String clientTime = String.valueOf((serverTimeOffset == null) ? 0 : serverTimeOffset) + (System.currentTimeMillis() / 1000L); + + if (isImportShareReq) { + ArrayList importedItems = new ArrayList<>(); + for (int j = 0; j < endpoints.length; j++) { + ImportedShare importShare = importedShares[j]; + + ShareRequestItem shareRequestItem = new ShareRequestItem(verifier, verifierParams.verifier_id, verifierParams.extended_verifier_id, + idToken, extraParams, nodeSigs.toArray(new CommitmentRequestResult[0]), importShare.oauth_pub_key_x, importShare.oauth_pub_key_y, + importShare.signing_pub_key_x, importShare.signing_pub_key_y, importShare.encryptedShare, + importShare.encryptedShareMetadata, importShare.node_index, importShare.key_type, + importShare.nonce_data, importShare.nonce_signature, verifierParams.sub_verifier_ids, verifierParams.verify_params, endpoints[j] + ); + importedItems.add(shareRequestItem); + } + String req = APIUtils.generateJsonRPCObject("ImportShares", new ShareRequestParams(importedItems.toArray(new ShareRequestItem[0]), clientTime)); + String result = APIUtils.post(endpoints[NodeUtils.getProxyCoordinatorEndpointIndex(endpoints, verifier, verifierParams.verifier_id)], req, true).get(); + @SuppressWarnings({"unchecked"}) // Due to Type Erasure of Generic Types at Runtime. Java does this to ensure code is compatible with pre-generic versions of Java. + JsonRPCResponse response = json.fromJson(result, JsonRPCResponse.class); + if (response != null && response.getError() == null) { + shareImportSuccess = true; + } + + if (isImportShareReq && !shareImportSuccess) { + throw TorusUtilError.IMPORT_SHARE_FAILED; + } + + ShareRequestResult[] shares = response.getTypedResult(ShareRequestResult[].class); + shareResponses.addAll(Arrays.asList(shares)); + thresholdPublicKey = NodeUtils.thresholdSame(Arrays.stream(shares).filter(item -> item.keys.length > 0).map(item -> item.keys[0].public_key).toArray(PubKey[]::new), threshold); + } else { + ArrayList> shareRequests = new ArrayList<>(); + for (String endpoint : endpoints) { + ShareRequestItem shareRequestItem = new ShareRequestItem(verifier, verifierParams.verifier_id, verifierParams.extended_verifier_id, + idToken, extraParams, nodeSigs.toArray(new CommitmentRequestResult[0]), null, null, + null, null, null, + null, null, null, + null, null, verifierParams.sub_verifier_ids, verifierParams.verify_params, null); + + List shareRequestItems = new ArrayList<>(); + shareRequestItems.add(shareRequestItem); + String req = APIUtils.generateJsonRPCObject("GetShareOrKeyAssign", new ShareRequestParams(shareRequestItems.toArray(new ShareRequestItem[0]), clientTime)); + shareRequests.add(APIUtils.post(endpoint, req, true)); + } + + for (CompletableFuture item : shareRequests) { + try { + @SuppressWarnings({"unchecked"}) // Due to Type Erasure of Generic Types at Runtime. Java does this to ensure code is compatible with pre-generic versions of Java. + JsonRPCResponse response = json.fromJson(item.get(), JsonRPCResponse.class); + + if (response != null && response.getError() == null) { + shareResponses.add(response.getTypedResult(ShareRequestResult.class)); + } + + thresholdPublicKey = NodeUtils.thresholdSame(shareResponses.stream().filter(res -> res.keys.length > 0).map(res -> res.keys[0].public_key).toArray(PubKey[]::new), threshold); + + if (thresholdPublicKey != null) { + break; + } + } catch (Exception e) { + // Continue to try next result + } + } + } + + if (thresholdPublicKey == null) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } + + ArrayList serverTimeOffsets = new ArrayList<>(); + + for (ShareRequestResult item : shareResponses) { + if (thresholdNonceData == null && verifierParams.extended_verifier_id == null) { + String currentPubKeyX = Common.padLeft(item.keys[0].public_key.getX(), '0', 64); + String thresholdPubKeyX = Common.padLeft(thresholdPublicKey.getX(), '0', 64); + if (item.keys[0].nonce_data != null) { + GetOrSetNonceResult pubnonce = item.keys[0].nonce_data; + if (pubnonce != null && currentPubKeyX.equalsIgnoreCase(thresholdPubKeyX)) { + thresholdNonceData = pubnonce; + } + } + } + + serverTimeOffsets.add((item.server_time_offset != null && !item.server_time_offset.isEmpty()) ? item.server_time_offset : "0"); + } + + List serverOffsetTimes = serverTimeOffsets.stream().map(Integer::parseInt).collect(Collectors.toList()); + Integer serverOffsetResponse = (serverTimeOffset != null) ? serverTimeOffset : Common.calculateMedian(serverOffsetTimes); + + if (thresholdNonceData == null && verifierParams.extended_verifier_id == null && !TorusUtils.isLegacyNetorkRouteMap(network)) { + thresholdNonceData = MetadataUtils.getOrSetSapphireMetadataNonce(legacyMetadataHost, network, thresholdPublicKey.getX(), thresholdPublicKey.getY(), serverOffsetResponse, null, false, null); + } + + int thresholdReqCount = (importedShares != null && importedShares.length > 0) ? endpoints.length : threshold; + + if (!(shareResponses.size() >= thresholdReqCount && thresholdPublicKey != null && (thresholdNonceData != null || verifierParams.extended_verifier_id != null || TorusUtils.isLegacyNetorkRouteMap(network)))) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } + + ArrayList shares = new ArrayList<>(); + ArrayList sessionTokenSigs = new ArrayList<>(); + ArrayList sessionTokens = new ArrayList<>(); + ArrayList nodeIndexes = new ArrayList<>(); + ArrayList sessionTokenDatas = new ArrayList<>(); + ArrayList isNewKeys = new ArrayList<>(); + + for (ShareRequestResult item : shareResponses) { + isNewKeys.add(item.is_new_key.toString()); + + if (item.session_token_sigs != null && item.session_token_sigs.length > 0) { + if (item.session_token_sig_metadata != null && item.session_token_sig_metadata.length > 0) { + String decrypted = MetadataUtils.decryptNodeData(item.session_token_sig_metadata[0], item.session_token_sigs[0], sessionAuthKeySerialized); + sessionTokenSigs.add(decrypted); + } else { + sessionTokenSigs.add(item.session_token_sigs[0]); + } + } + + if (item.session_token_sigs != null && item.session_tokens.length > 0) { + if (item.session_token_metadata != null && item.session_token_metadata.length > 0) { + String decrypted = MetadataUtils.decryptNodeData(item.session_token_metadata[0], item.session_tokens[0], sessionAuthKeySerialized); + sessionTokens.add(decrypted); + } else { + sessionTokens.add(item.session_tokens[0]); + } + } + + if (item.keys.length > 0) { + KeyAssignment latestKey = item.keys[0]; + nodeIndexes.add(latestKey.node_index); + String decoded = new String(Base64.getDecoder().decode(latestKey.share.getBytes(StandardCharsets.UTF_8))); + String decryped = MetadataUtils.decryptNodeData(latestKey.share_metadata, decoded, sessionAuthKeySerialized); + shares.add(decryped); + } + } + + if (verifierParams.extended_verifier_id == null && sessionTokenSigs.size() < threshold) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } + + if (verifierParams.extended_verifier_id == null && sessionTokens.size() < threshold) { + throw TorusUtilError.RUNTIME_ERROR("Insufficient number of signatures from nodes"); + } + + for (int i = 0; i < sessionTokens.size(); i++) { + String token = sessionTokens.get(i); + if (token != null) { + // decode token, can be either hex or base64 + try { + byte[] tokenBytes = Hex.decode(token); + String tokenBase64 = Base64.getEncoder().encodeToString(tokenBytes); + sessionTokenDatas.add(new SessionToken(tokenBase64, sessionTokenSigs.get(i), shareResponses.get(i).node_pubx, shareResponses.get(i).node_puby)); + } catch (Exception e) { + sessionTokenDatas.add(new SessionToken(token, sessionTokenSigs.get(i), shareResponses.get(i).node_pubx, shareResponses.get(i).node_puby)); + } + } + } + + Map decryptedShares = new HashMap<>(); + for (int i = 0; i < shares.size(); i++) { + if (shares.get(i) != null) { + decryptedShares.put(nodeIndexes.get(i), shares.get(i)); + } + } + + List elements = new ArrayList<>(); + for (int i = 0; i <= Collections.max(decryptedShares.keySet()); i++) { + elements.add(i); + } + + List> allCombis = Common.kCombinations(elements, threshold); + + BigInteger privateKey = null; + for (int j = 0; j < allCombis.size(); j++) { + List currentCombi = allCombis.get(j); + Map currentCombiShares = decryptedShares.entrySet().stream().filter(entry -> currentCombi.contains(entry.getKey())).collect(Collectors.toMap(Map.Entry::getKey, Map.Entry::getValue)); + BigInteger derivedPrivateKey = Lagrange.lagrangeInterpolation(new ArrayList<>(currentCombiShares.values()).stream().map(cs -> new BigInteger(cs, 16)).toArray(BigInteger[]::new), new ArrayList<>(currentCombiShares.keySet()).stream().map(is -> new BigInteger(String.valueOf(is))).toArray(BigInteger[]::new)); + if (derivedPrivateKey == null) { + continue; + } + String decryptedPublicKey = KeyUtils.privateToPublic(derivedPrivateKey); + String[] derivedPublicKeyCoords = KeyUtils.getPublicKeyCoords(decryptedPublicKey); + String thresholdPubKeyX = Common.padLeft(thresholdPublicKey.getX(), '0', 64); + String thresholdPubKeyY = Common.padLeft(thresholdPublicKey.getY(), '0', 64); + if (derivedPublicKeyCoords[0].equalsIgnoreCase(thresholdPubKeyX) && derivedPublicKeyCoords[1].equalsIgnoreCase(thresholdPubKeyY)) { + privateKey = derivedPrivateKey; + break; + } + } + + if (privateKey == null) { + throw TorusUtilError.PRIVATE_KEY_DERIVE_FAILED; + } + + String thesholdIsNewKey = thresholdSame(isNewKeys.toArray(new String[0]), threshold); + + String oAuthKey = Common.padLeft(privateKey.toString(16), '0', 64); + String oAuthPublicKey = KeyUtils.privateToPublic(privateKey); + String[] oAuthPublicKeyCoords = KeyUtils.getPublicKeyCoords(oAuthPublicKey); + BigInteger metadataNonce = (thresholdNonceData != null && thresholdNonceData.nonce != null) ? new BigInteger(Common.padLeft(thresholdNonceData.nonce, '0', 64), 16) : BigInteger.ZERO; + String finalPublicKey; + PubNonce pubNonce = null; + TypeOfUser typeOfUser = TypeOfUser.v1; + + if (verifierParams.extended_verifier_id != null) { + typeOfUser = TypeOfUser.v2; + finalPublicKey = oAuthPublicKey; + } else if (TorusUtils.isLegacyNetorkRouteMap(network)) { + if (enableOneKey) { + Boolean isNewKey = (!(thesholdIsNewKey != null && thesholdIsNewKey.equalsIgnoreCase("true"))); + GetOrSetNonceResult nonce = MetadataUtils.getOrSetNonce(legacyMetadataHost, thresholdPublicKey.getX(), thresholdPublicKey.getY(), serverOffsetResponse, oAuthKey, isNewKey, null); + metadataNonce = (nonce.nonce != null) ? new BigInteger(Common.padLeft(nonce.nonce, '0', 64), 16) : BigInteger.ZERO; + typeOfUser = (nonce.typeOfUser != null) ? nonce.typeOfUser : TypeOfUser.v1; + + if (typeOfUser == TypeOfUser.v2) { + pubNonce = nonce.pubNonce; + if (pubNonce != null && !pubNonce.x.isEmpty() && !pubNonce.y.isEmpty()) { + String pubNonceKey = KeyUtils.getPublicKeyFromCoords(pubNonce.x, pubNonce.y, true); + finalPublicKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(oAuthPublicKey, pubNonceKey), false); + } else { + throw TorusUtilError.RUNTIME_ERROR("Public nonce is missing"); + } + } else { + typeOfUser = TypeOfUser.v1; + metadataNonce = new BigInteger(Common.padLeft(MetadataUtils.getMetadata(legacyMetadataHost, new GetMetadataParams(oAuthPublicKeyCoords[0], oAuthPublicKeyCoords[1])).message, '0', 64), 16); + BigInteger privateKeyWithNonce = new BigInteger(Common.padLeft(oAuthKey, '0', 64), 16).add(metadataNonce); + finalPublicKey = KeyUtils.privateToPublic(privateKeyWithNonce); + } + } else { + // typeOfUser = TypeOfUser.v1; Already assigned previously, left here for clarity + metadataNonce = new BigInteger(Common.padLeft(MetadataUtils.getMetadata(legacyMetadataHost, new GetMetadataParams(oAuthPublicKeyCoords[0], oAuthPublicKeyCoords[1])).message, '0', 64), 16); + BigInteger privateKeyWithNonce = new BigInteger(Common.padLeft(oAuthKey, '0', 64), 16).add(metadataNonce); + finalPublicKey = KeyUtils.privateToPublic(privateKeyWithNonce); + } + } else { + typeOfUser = TypeOfUser.v2; + finalPublicKey = oAuthPublicKey; + if (thresholdNonceData != null && thresholdNonceData.pubNonce != null && (!(thresholdNonceData.pubNonce.x.isEmpty() || thresholdNonceData.pubNonce.y.isEmpty()))) { + PubNonce pubNonceObject = thresholdNonceData.pubNonce; + String pubNonceKey = KeyUtils.getPublicKeyFromCoords(pubNonceObject.x, pubNonceObject.y, true); + finalPublicKey = KeyUtils.combinePublicKeysFromStrings(Arrays.asList(oAuthPublicKey, pubNonceKey), false); + pubNonce = pubNonceObject; + } else { + throw TorusUtilError.PUB_NONCE_MISSING; + } + } + + if (finalPublicKey == null) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } + + String oAuthKeyAddress = KeyUtils.generateAddressFromPubKey(oAuthPublicKeyCoords[0], oAuthPublicKeyCoords[1]); + String[] finalPubKeyCoords = KeyUtils.getPublicKeyCoords(finalPublicKey); + + String finalEvmAddress = KeyUtils.generateAddressFromPubKey(finalPubKeyCoords[0], finalPubKeyCoords[1]); + + String finalPrivKey = ""; + + if (typeOfUser == TypeOfUser.v1 || (typeOfUser == TypeOfUser.v2 && metadataNonce.compareTo(BigInteger.ZERO) > 0)) { + BigInteger privateKeyWithNonce = privateKey.add(metadataNonce).mod(getOrderOfCurve()); + finalPrivKey = Common.padLeft(privateKeyWithNonce.toString(16), '0', 64); + + } + + // This is a sanity check to make doubly sure we are returning the correct private key after importing a share + if (isImportShareReq) { + if (newPrivateKey == null) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } else { + if (!finalPrivKey.equalsIgnoreCase(Common.padLeft(newPrivateKey, '0', 64))) { + throw TorusUtilError.RETRIEVE_OR_IMPORT_SHARE_ERROR; + } + } + } + + Boolean isUpgraded = null; + + if (typeOfUser == TypeOfUser.v2) { + isUpgraded = metadataNonce.equals(BigInteger.ZERO); + } + + return new TorusKey( + new FinalKeyData(finalEvmAddress, finalPubKeyCoords[0], finalPubKeyCoords[1], finalPrivKey), + new OAuthKeyData(oAuthKeyAddress, oAuthPublicKeyCoords[0], oAuthPublicKeyCoords[1], oAuthKey), + new SessionData(sessionTokenDatas, sessionAuthKeySerialized), + new Metadata(pubNonce, metadataNonce, typeOfUser, isUpgraded, serverOffsetResponse), + new NodesData(nodeIndexes)); + } + + public static T thresholdSame(@NotNull T[] arr, int threshold) throws JsonProcessingException { + HashMap hashMap = new HashMap<>(); + for (T s : arr) { + ObjectMapper objectMapper = new ObjectMapper() + .configure(SerializationFeature.ORDER_MAP_ENTRIES_BY_KEYS, true); + String value = objectMapper.writeValueAsString(s); + Integer index = hashMap.get(value); + if (index != null) { + hashMap.put(value, index+1); + } else { + hashMap.put(value, 0); + } + if (hashMap.get(value) != null && hashMap.get(value) == threshold) { + return s; + } + } + return null; + } + + @SuppressWarnings("unused") + public static String thresholdSame(@NotNull List list, int threshold) throws JsonProcessingException { + String[] arr = new String[list.size()]; + list.toArray(arr); + return NodeUtils.thresholdSame(arr, threshold); + } + + public static int getProxyCoordinatorEndpointIndex(@NotNull String[] endpoints, @NotNull String verifier, @NotNull String verifierId) { + String verifierIdString = verifier + verifierId; + String hashedVerifierId = Hash.sha3(verifierIdString).replace("0x", ""); + BigInteger proxyEndPointNum = new BigInteger(hashedVerifierId, 16).mod(BigInteger.valueOf(endpoints.length)); + return proxyEndPointNum.intValue(); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Predicate.java b/src/main/java/org/torusresearch/torusutils/helpers/Predicate.java deleted file mode 100644 index e25065a..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/Predicate.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import java.util.concurrent.CompletableFuture; - -import java.util.concurrent.atomic.AtomicBoolean; - -public interface Predicate { - CompletableFuture call(String[] resultArr, AtomicBoolean resolved) throws PredicateFailedException; -} \ No newline at end of file diff --git a/src/main/java/org/torusresearch/torusutils/helpers/PredicateFailedException.java b/src/main/java/org/torusresearch/torusutils/helpers/PredicateFailedException.java deleted file mode 100644 index 1c7e815..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/PredicateFailedException.java +++ /dev/null @@ -1,9 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import java.util.concurrent.CompletionException; - -public class PredicateFailedException extends CompletionException { - public PredicateFailedException(String errMessage) { - super(errMessage); - } -} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Some.java b/src/main/java/org/torusresearch/torusutils/helpers/Some.java deleted file mode 100644 index 553ae0a..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/Some.java +++ /dev/null @@ -1,59 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import org.torusresearch.torusutils.types.SomeException; - -import java.util.List; -import java.util.concurrent.CompletableFuture; -import java.util.concurrent.atomic.AtomicBoolean; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.concurrent.atomic.AtomicReference; - - -public class Some { - private final AtomicInteger finishedCount = new AtomicInteger(0); - private final String[] resultArr; - private final String[] errorArr; - private final CompletableFuture completableFuture; - private final AtomicBoolean resolved = new AtomicBoolean(false); - private final AtomicReference predicateError = new AtomicReference<>(""); - - public Some(List> promises, Predicate predicate) { - resultArr = new String[promises.size()]; - errorArr = new String[promises.size()]; - completableFuture = new CompletableFuture<>(); - for (int i = 0; i < promises.size(); i++) { - int index = i; - promises.get(index).whenCompleteAsync((response, e) -> { - int count = finishedCount.incrementAndGet(); - if (e != null) { - errorArr[index] = e.getMessage(); - // swallow exceptions due to threshold assumptions - if (count == promises.size() && !completableFuture.isDone() && !resolved.get()) { - completableFuture.completeExceptionally(new SomeException(errorArr, resultArr, predicateError.get())); - } - return; - } - resultArr[index] = response; - if (resolved.get()) { - return; - } - try { - T intermediateResult = predicate.call(resultArr.clone(), resolved).get(); - resolved.set(true); - completableFuture.complete(intermediateResult); - } catch (Exception e2) { - predicateError.set(e2.getMessage()); - // swallow exceptions due to threshold assumptions - // if none of the predicates succeed, we throw at the end - if (count == promises.size() && !completableFuture.isDone() && !resolved.get()) { - completableFuture.completeExceptionally(new SomeException(errorArr, resultArr, predicateError.get())); - } - } - }); - } - } - - public CompletableFuture getCompletableFuture() { - return completableFuture; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/TorusUtilError.java b/src/main/java/org/torusresearch/torusutils/helpers/TorusUtilError.java new file mode 100644 index 0000000..50befb6 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/TorusUtilError.java @@ -0,0 +1,49 @@ +package org.torusresearch.torusutils.helpers; + +import org.jetbrains.annotations.NotNull; + +public class TorusUtilError extends Exception { + + // Define error cases + public static final TorusUtilError CONFIGURATION_ERROR = new TorusUtilError("SDK Configuration incorrect. Network is probably incorrect"); + public static final TorusUtilError COMMITMENT_REQUEST_FAILED = new TorusUtilError("commitment request failed"); + public static final TorusUtilError DECRYPTION_FAILED = new TorusUtilError("Decryption Failed"); + public static final TorusUtilError ENCODING_FAILED = new TorusUtilError("Could not encode data"); + public static final TorusUtilError DECODING_FAILED = new TorusUtilError("JSON Decoding error"); + public static final TorusUtilError IMPORT_SHARE_FAILED = new TorusUtilError("import share failed"); + public static final TorusUtilError PRIVATE_KEY_DERIVE_FAILED = new TorusUtilError("could not derive private key"); + public static final TorusUtilError INTERPOLATION_FAILED = new TorusUtilError("lagrange interpolation failed"); + public static final TorusUtilError INVALID_KEY_SIZE = new TorusUtilError("Invalid key size. Expected 32 bytes"); + public static final TorusUtilError INVALID_PUB_KEY_SIZE = new TorusUtilError("Invalid key size. Expected 64 bytes"); + public static final TorusUtilError INVALID_INPUT = new TorusUtilError("Input was found to be invalid"); + + public static TorusUtilError RUNTIME_ERROR(@NotNull String msg) { + return new TorusUtilError(msg); + } + public static final TorusUtilError RETRIEVE_OR_IMPORT_SHARE_ERROR = new TorusUtilError("retrieve or import share failed"); + public static final TorusUtilError METADATA_NONCE_MISSING = new TorusUtilError("Unable to fetch metadata nonce"); + public static final TorusUtilError GATING_ERROR = new TorusUtilError("could not process request"); + public static final TorusUtilError PUB_NONCE_MISSING = new TorusUtilError("public nonce is missing"); + + private final String message; + + public TorusUtilError(@NotNull String message) { + super(message); + this.message = message; + } + + @Override + public String toString() { + return message; + } + + @SuppressWarnings("unused") + public String debugDescription() { + return message; + } + + public String getLocalizedMessage() { + return message; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/helpers/Utils.java b/src/main/java/org/torusresearch/torusutils/helpers/Utils.java deleted file mode 100644 index de96588..0000000 --- a/src/main/java/org/torusresearch/torusutils/helpers/Utils.java +++ /dev/null @@ -1,251 +0,0 @@ -package org.torusresearch.torusutils.helpers; - -import com.google.gson.Gson; - -import org.torusresearch.fetchnodedetails.types.TorusNodePub; -import org.torusresearch.torusutils.apis.APIUtils; -import org.torusresearch.torusutils.apis.JsonRPCResponse; -import org.torusresearch.torusutils.apis.KeyAssignParams; -import org.torusresearch.torusutils.apis.KeyLookupResult; -import org.torusresearch.torusutils.apis.SignerResponse; -import org.torusresearch.torusutils.apis.VerifierLookupRequestParams; - -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Random; -import java.util.concurrent.CompletableFuture; - -import okhttp3.internal.http2.Header; - -public class Utils { - private Utils() { - } - - public static String thresholdSame(String[] arr, int threshold) { - HashMap hashMap = new HashMap<>(); - for (String s : arr) { - Integer currentCount = hashMap.get(s); - if (currentCount == null) currentCount = 0; - int incrementedCount = currentCount + 1; - if (incrementedCount == threshold) { - return s; - } - hashMap.put(s, currentCount + 1); - } - return null; - } - - public static String thresholdSame(List list, int threshold) { - String[] arr = new String[list.size()]; - list.toArray(arr); - return Utils.thresholdSame(arr, threshold); - } - - public static List> kCombinations(int s, int k) { - List set = new ArrayList<>(); - for (int i = 0; i < s; i++) { - set.add(i); - } - return kCombinations(set, k); - } - - public static List> kCombinations(List set, int k) { - List> combs = new ArrayList<>(); - if (k > set.size()) { - return combs; - } - if (k == set.size()) { - combs.add(set); - return combs; - } - if (k == 1) { - for (Integer i : set) { - ArrayList arrList = new ArrayList<>(); - arrList.add(i); - combs.add(arrList); - } - return combs; - } - for (int i = 0; i < set.size() - k + 1; i++) { - List> tailCombs = Utils.kCombinations(set.subList(i + 1, set.size()), k - 1); - for (List tailComb : tailCombs) { - List prependedComb = new ArrayList<>(); - prependedComb.add(set.get(i)); - prependedComb.addAll(tailComb); - combs.add(prependedComb); - } - } - return combs; - } - - public static CompletableFuture waitKeyLookup(String[] endpoints, String verifier, String verifierId, int timeout) { - CompletableFuture completableFuture = new CompletableFuture<>(); - try { - Thread.sleep(timeout); - } catch (InterruptedException e) { - completableFuture.completeExceptionally(e); - } - Utils.keyLookup(endpoints, verifier, verifierId).whenComplete((res, err) -> { - if (err != null) { - completableFuture.completeExceptionally(err); - } - completableFuture.complete(res); - }); - return completableFuture; - } - - public static CompletableFuture keyLookup(String[] endpoints, String verifier, String verifierId) { - int k = endpoints.length / 2 + 1; - List> lookupPromises = new ArrayList<>(); - for (int i = 0; i < endpoints.length; i++) { - lookupPromises.add(i, APIUtils.post(endpoints[i], APIUtils.generateJsonRPCObject("VerifierLookupRequest", new VerifierLookupRequestParams(verifier, verifierId)), false)); - } - return new Some<>(lookupPromises, (lookupResults, resolved) -> { - try { - List errorResults = new ArrayList<>(); - List keyResults = new ArrayList<>(); - Gson gson = new Gson(); - for (String x : lookupResults) { - if (x != null && !x.equals("")) { - try { - JsonRPCResponse response = gson.fromJson(x, JsonRPCResponse.class); - keyResults.add(Utils.convertToJsonObject(response.getResult())); - } catch (Exception e) { - keyResults.add(""); - } - } - } - for (String x : lookupResults) { - if (x != null && !x.equals("")) { - try { - JsonRPCResponse response = gson.fromJson(x, JsonRPCResponse.class); - errorResults.add(response.getError().getData()); - } catch (Exception e) { - errorResults.add(""); - } - } - } - String errorResult = thresholdSame(errorResults, k); - String keyResult = thresholdSame(keyResults, k); - if ((errorResult != null && !errorResult.equals("")) || (keyResult != null && !keyResult.equals(""))) { - return CompletableFuture.completedFuture(new KeyLookupResult(keyResult, errorResult)); - } - CompletableFuture failedFuture = new CompletableFuture<>(); - failedFuture.completeExceptionally(new Exception("invalid results from KeyLookup " + gson.toJson(lookupResults))); - return failedFuture; - } catch (Exception e) { - return null; - } - }).getCompletableFuture(); - } - - public static CompletableFuture keyAssign(String[] endpoints, TorusNodePub[] torusNodePubs, Integer lastPoint, Integer firstPoint, String verifier, String verifierId, String signerHost, String network) { - Integer nodeNum, initialPoint = null; - CompletableFuture completableFuture = new CompletableFuture<>(); - - if (lastPoint == null) { - nodeNum = new Random().nextInt(endpoints.length); - initialPoint = nodeNum; - } else { - nodeNum = lastPoint % endpoints.length; - } - if (nodeNum.equals(firstPoint)) { - completableFuture.completeExceptionally(new Exception("Looped through all. No node available for key assignment")); - return completableFuture; - } - if (firstPoint != null) { - initialPoint = firstPoint; - } - String data = APIUtils.generateJsonRPCObject("KeyAssign", new KeyAssignParams(verifier, verifierId)); - Header[] headers = new Header[3]; - headers[0] = new Header("pubkeyx", torusNodePubs[nodeNum].getX()); - headers[1] = new Header("pubkeyy", torusNodePubs[nodeNum].getY()); - headers[2] = new Header("network", network); - Integer finalInitialPoint = initialPoint; - CompletableFuture apir = APIUtils.post(signerHost, data, headers, true); - apir.whenCompleteAsync((signedData, err) -> { - if (err != null) { - // if signer fails, we just return - completableFuture.completeExceptionally(err); - return; - } - try { - Gson gson = new Gson(); - SignerResponse signerResponse = gson.fromJson(signedData, SignerResponse.class); - Header[] signerHeaders = new Header[3]; - if (signerResponse.getTorus_timestamp() == null || signerResponse.getTorus_nonce() == null || signerResponse.getTorus_signature() == null) { - completableFuture.completeExceptionally(new Exception("Invalid signer response. Please retry!")); - return; - } - signerHeaders[0] = new Header("torus-timestamp", signerResponse.getTorus_timestamp()); - signerHeaders[1] = new Header("torus-nonce", signerResponse.getTorus_nonce()); - signerHeaders[2] = new Header("torus-signature", signerResponse.getTorus_signature()); - - CompletableFuture cf = APIUtils.post(endpoints[nodeNum], data, signerHeaders, false); - cf.whenCompleteAsync((resp, keyAssignErr) -> { - try { - // we only retry if keyassign api fails.. - // All other cases, we just complete exceptionally - if (keyAssignErr != null) { - Utils.keyAssign(endpoints, torusNodePubs, nodeNum + 1, finalInitialPoint, verifier, verifierId, signerHost, network).whenCompleteAsync((res2, err2) -> { - if (err2 != null) { - completableFuture.completeExceptionally(err2); - return; - } - completableFuture.complete(res2); - }); - return; - } - JsonRPCResponse jsonRPCResponse = gson.fromJson(resp, JsonRPCResponse.class); - String result = jsonRPCResponse.getResult().toString(); - if (result != null && !result.equals("")) { - completableFuture.complete(new KeyLookupResult(result, null)); - } else { - Utils.keyAssign(endpoints, torusNodePubs, nodeNum + 1, finalInitialPoint, verifier, verifierId, signerHost, network).whenCompleteAsync((res2, err2) -> { - if (err2 != null) { - completableFuture.completeExceptionally(err2); - return; - } - completableFuture.complete(res2); - }); - } - } catch (Exception ex) { - completableFuture.completeExceptionally(ex); - } - }); - } catch (Exception e) { - completableFuture.completeExceptionally(e); - } - }); - return completableFuture; - - } - - public static boolean isEmpty(final CharSequence cs) { - return cs == null || cs.length() == 0; - } - - public static String padLeft(String inputString, Character padChar, int length) { - if (inputString.length() >= length) return inputString; - StringBuilder sb = new StringBuilder(); - while (sb.length() < length - inputString.length()) { - sb.append(padChar); - } - sb.append(inputString); - return sb.toString(); - } - - public static String stripPaddingLeft(String inputString, Character padChar) { - StringBuilder sb = new StringBuilder(inputString); - while (sb.length() > 1 && sb.charAt(0) == padChar) { - sb.deleteCharAt(0); - } - return sb.toString(); - } - - public static String convertToJsonObject(Object obj) { - Gson gson = new Gson(); - return obj == null ? "" : gson.toJson(obj); - } -} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/encryption/Encryption.java b/src/main/java/org/torusresearch/torusutils/helpers/encryption/Encryption.java new file mode 100644 index 0000000..0f77888 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/helpers/encryption/Encryption.java @@ -0,0 +1,82 @@ +package org.torusresearch.torusutils.helpers.encryption; + +import org.bouncycastle.crypto.digests.SHA256Digest; +import org.bouncycastle.crypto.macs.HMac; +import org.bouncycastle.crypto.params.KeyParameter; +import org.bouncycastle.jce.provider.BouncyCastleProvider; +import org.bouncycastle.util.Arrays; +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.helpers.KeyUtils; +import org.torusresearch.torusutils.helpers.hashing.SHA512; +import org.torusresearch.torusutils.types.common.ecies.Ecies; + +import java.io.ByteArrayOutputStream; +import java.security.KeyPair; +import java.security.Provider; +import java.security.SecureRandom; + +import javax.crypto.Cipher; +import javax.crypto.KeyAgreement; +import javax.crypto.spec.IvParameterSpec; +import javax.crypto.spec.SecretKeySpec; + +public class Encryption { + static final protected Provider provider = new BouncyCastleProvider(); + + public static byte[] ecdh (@NotNull byte[] dataPrv, @NotNull byte[] dataPub) throws Exception + { + KeyAgreement ka = KeyAgreement.getInstance("ECDH", provider); + ka.init(KeyUtils.deserializePrivateKey(dataPrv)); + ka.doPhase(KeyUtils.deserializePublicKey(dataPub), true); + return ka.generateSecret(); + } + + public static Ecies encrypt(@NotNull byte[] publicKey, @NotNull String plaintext) throws Exception { + KeyPair ephemeral = KeyUtils.generateKeyPair(); + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", provider); + + SecureRandom random = new SecureRandom(); + byte[] iv = new byte[16]; + random.nextBytes(iv); + + byte[] shared = ecdh(KeyUtils.serializePrivateKey(ephemeral.getPrivate()), publicKey); + byte[] hash = SHA512.digest(shared); + byte[] aesKey = new byte[32]; + System.arraycopy(hash, 0, aesKey, 0, 32); + byte[] macKey = new byte[32]; + System.arraycopy(hash, 32, macKey, 0, 32); + cipher.init(Cipher.ENCRYPT_MODE, new SecretKeySpec(aesKey, "AES"), new IvParameterSpec(iv)); + + byte[] cipherText = cipher.doFinal(Hex.decode(plaintext)); + + ByteArrayOutputStream outputStream = new ByteArrayOutputStream(); + outputStream.write(iv); // 16 bytes + outputStream.write(publicKey); // 65 bytes + outputStream.write(cipherText); + + byte[] dataToMac = outputStream.toByteArray(); + + HMac hmac = new HMac(new SHA256Digest()); + hmac.init(new KeyParameter(macKey)); + byte[] finalMac = new byte[hmac.getMacSize()]; + hmac.update(dataToMac, 0, dataToMac.length); + hmac.doFinal(finalMac, 0); + return new Ecies(Hex.toHexString(iv), Hex.toHexString(KeyUtils.serializePublicKey(ephemeral.getPublic(),false)),Hex.toHexString(cipherText), Hex.toHexString(finalMac)); + } + + public static byte[] decrypt(@NotNull String privateKeyHex, @NotNull Ecies ecies) throws Exception { + byte[] shared = ecdh(Hex.decode(privateKeyHex), Hex.decode(ecies.ephemPublicKey)); + byte[] sha512hash = SHA512.digest(shared); + SecretKeySpec aesKey = new SecretKeySpec(Arrays.copyOf(sha512hash, 32), "AES"); + + byte[] iv = Hex.decode(ecies.iv); + IvParameterSpec ivSpec = new IvParameterSpec(iv); + + byte[] cipherText = Hex.decode(ecies.ciphertext); + + Cipher cipher = Cipher.getInstance("AES/CBC/PKCS5Padding", provider); + cipher.init(Cipher.DECRYPT_MODE, aesKey, ivSpec); + return cipher.doFinal(cipherText); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/helpers/SHA512.java b/src/main/java/org/torusresearch/torusutils/helpers/hashing/SHA512.java similarity index 57% rename from src/main/java/org/torusresearch/torusutils/helpers/SHA512.java rename to src/main/java/org/torusresearch/torusutils/helpers/hashing/SHA512.java index 415afb9..d579b47 100644 --- a/src/main/java/org/torusresearch/torusutils/helpers/SHA512.java +++ b/src/main/java/org/torusresearch/torusutils/helpers/hashing/SHA512.java @@ -1,10 +1,12 @@ -package org.torusresearch.torusutils.helpers; +package org.torusresearch.torusutils.helpers.hashing; + +import org.jetbrains.annotations.NotNull; import java.security.MessageDigest; import java.security.NoSuchAlgorithmException; public class SHA512 { - public static byte[] digest(byte[] buf) throws NoSuchAlgorithmException { + public static byte[] digest(@NotNull byte[] buf) throws NoSuchAlgorithmException { MessageDigest digest = MessageDigest.getInstance("SHA-512"); digest.update(buf); return digest.digest(); diff --git a/src/main/java/org/torusresearch/torusutils/types/DecryptedShare.java b/src/main/java/org/torusresearch/torusutils/types/DecryptedShare.java deleted file mode 100644 index 6a805ab..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/DecryptedShare.java +++ /dev/null @@ -1,21 +0,0 @@ -package org.torusresearch.torusutils.types; - -import java.math.BigInteger; - -public class DecryptedShare { - private final BigInteger index; - private final BigInteger value; - - public DecryptedShare(BigInteger _index, BigInteger _value) { - index = _index; - value = _value; - } - - public BigInteger getIndex() { - return index; - } - - public BigInteger getValue() { - return value; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/FinalKeyData.java b/src/main/java/org/torusresearch/torusutils/types/FinalKeyData.java new file mode 100644 index 0000000..26ad98b --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/FinalKeyData.java @@ -0,0 +1,19 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; + +public class FinalKeyData extends FinalPubKeyData { + @Nullable + private final String privKey; + + public FinalKeyData(@NotNull String walletAddress, @NotNull String X, @NotNull String Y, @Nullable String privKey) { + super(walletAddress,X,Y); + this.privKey = privKey; + } + + @Nullable + public String getPrivKey() { + return privKey; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/FinalPubKeyData.java b/src/main/java/org/torusresearch/torusutils/types/FinalPubKeyData.java new file mode 100644 index 0000000..e739c4b --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/FinalPubKeyData.java @@ -0,0 +1,28 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; + +public class FinalPubKeyData { + + private final String walletAddress; + private final String x; + private final String y; + + public FinalPubKeyData(@NotNull String walletAddress, @NotNull String x, @NotNull String y) { + this.walletAddress = walletAddress; + this.x = x; + this.y = y; + } + + public String getWalletAddress() { + return walletAddress; + } + + public String getX() { + return x; + } + + public String getY() { + return y; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceError.java b/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceError.java deleted file mode 100644 index 7b6994c..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceError.java +++ /dev/null @@ -1,7 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class GetOrSetNonceError extends Exception { - public GetOrSetNonceError(Exception e) { - super(e); - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceResult.java b/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceResult.java deleted file mode 100644 index 34310ea..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/GetOrSetNonceResult.java +++ /dev/null @@ -1,66 +0,0 @@ -package org.torusresearch.torusutils.types; - - -import org.jetbrains.annotations.Nullable; - -public class GetOrSetNonceResult { - private final TypeOfUser typeOfUser; - @Nullable - private String nonce; - @Nullable - private PubNonce pubNonce; - private boolean upgraded; - - public GetOrSetNonceResult(TypeOfUser typeOfUser) { - this.typeOfUser = typeOfUser; - } - - @Nullable - public String getNonce() { - return nonce; - } - - public void setNonce(@Nullable String nonce) { - this.nonce = nonce; - } - - @Nullable - public PubNonce getPubNonce() { - return pubNonce; - } - - public void setPubNonce(@Nullable PubNonce pubNonce) { - this.pubNonce = pubNonce; - } - - public boolean isUpgraded() { - return upgraded; - } - - public void setUpgraded(boolean upgraded) { - this.upgraded = upgraded; - } - - public TypeOfUser getTypeOfUser() { - return typeOfUser; - } - - - public static class PubNonce { - private final String x; - private final String y; - - public PubNonce(String x, String y) { - this.x = x; - this.y = y; - } - - public String getX() { - return x; - } - - public String getY() { - return y; - } - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/Metadata.java b/src/main/java/org/torusresearch/torusutils/types/Metadata.java new file mode 100644 index 0000000..43ff3c5 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/Metadata.java @@ -0,0 +1,57 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TypeOfUser; + +import java.math.BigInteger; + +import io.reactivex.annotations.Nullable; + +public class Metadata { + + @Nullable + private final PubNonce pubNonce; + private final BigInteger nonce; + @Nullable + private final Boolean upgraded; + private final TypeOfUser typeOfUser; + + private final Integer serverTimeOffset; + + public Metadata(@Nullable PubNonce pubNonce, @NotNull BigInteger nonce, @NotNull TypeOfUser typeOfUser, @Nullable Boolean upgraded, @NotNull Integer serverTimeOffset) { + this.pubNonce = pubNonce; + this.nonce = nonce; + this.typeOfUser = typeOfUser; + this.upgraded = upgraded; + this.serverTimeOffset = serverTimeOffset; + } + + public Metadata(@Nullable PubNonce pubNonce, @NotNull BigInteger nonce, @NotNull TypeOfUser typeOfUser, @NotNull Boolean upgraded) { + this.pubNonce = pubNonce; + this.nonce = nonce; + this.typeOfUser = typeOfUser; + this.upgraded = upgraded; + this.serverTimeOffset = 0; + } + + public PubNonce getPubNonce() { + return pubNonce; + } + + public BigInteger getNonce() { + return nonce; + } + + public boolean isUpgraded() { + return upgraded; + } + + public TypeOfUser getTypeOfUser() { + return typeOfUser; + } + + public Integer getServerTimeOffset() { + return serverTimeOffset; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/MetadataParams.java b/src/main/java/org/torusresearch/torusutils/types/MetadataParams.java deleted file mode 100644 index b459d8f..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/MetadataParams.java +++ /dev/null @@ -1,39 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class MetadataParams extends MetadataPubKey { - private final MetadataSetData set_data; - private final String signature; - - public MetadataParams(String pub_key_X, String pub_key_Y, MetadataSetData set_data, String signature) { - super(pub_key_X, pub_key_Y); - this.set_data = set_data; - this.signature = signature; - } - - public MetadataSetData getSet_data() { - return set_data; - } - - public String getSignature() { - return signature; - } - - public static class MetadataSetData { - private final String data; - // timestamp in hex - private final String timestamp; - - public MetadataSetData(String data, String timestamp) { - this.data = data; - this.timestamp = timestamp; - } - - public String getData() { - return data; - } - - public String getTimestamp() { - return timestamp; - } - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/MetadataPubKey.java b/src/main/java/org/torusresearch/torusutils/types/MetadataPubKey.java deleted file mode 100644 index 10c05d6..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/MetadataPubKey.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class MetadataPubKey { - private final String pub_key_X; - private final String pub_key_Y; - - public MetadataPubKey(String pub_key_X, String pub_key_Y) { - this.pub_key_X = pub_key_X; - this.pub_key_Y = pub_key_Y; - } - - public String getPub_key_X() { - return pub_key_X; - } - - public String getPub_key_Y() { - return pub_key_Y; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/MetadataResponse.java b/src/main/java/org/torusresearch/torusutils/types/MetadataResponse.java deleted file mode 100644 index 8179550..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/MetadataResponse.java +++ /dev/null @@ -1,13 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class MetadataResponse { - private final String message; - - public MetadataResponse(String message) { - this.message = message; - } - - public String getMessage() { - return message; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/NodesData.java b/src/main/java/org/torusresearch/torusutils/types/NodesData.java new file mode 100644 index 0000000..c2c9843 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/NodesData.java @@ -0,0 +1,18 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; + +import java.util.List; + +public class NodesData { + + private final List nodeIndexes; + + public NodesData(@NotNull List nodeIndexes) { + this.nodeIndexes = nodeIndexes; + } + + public List getNodeIndexes() { + return nodeIndexes; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/OAuthKeyData.java b/src/main/java/org/torusresearch/torusutils/types/OAuthKeyData.java new file mode 100644 index 0000000..edc6b5e --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/OAuthKeyData.java @@ -0,0 +1,9 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; + +public class OAuthKeyData extends FinalKeyData { + public OAuthKeyData(@NotNull String walletAddress, @NotNull String X, @NotNull String Y, @NotNull String privKey) { + super(walletAddress, X, Y, privKey); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/OAuthPubKeyData.java b/src/main/java/org/torusresearch/torusutils/types/OAuthPubKeyData.java new file mode 100644 index 0000000..d139269 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/OAuthPubKeyData.java @@ -0,0 +1,10 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; + + +public class OAuthPubKeyData extends FinalPubKeyData { + public OAuthPubKeyData(@NotNull String walletAddress, @NotNull String x, @NotNull String y) { + super(walletAddress, x, y); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/Point.java b/src/main/java/org/torusresearch/torusutils/types/Point.java new file mode 100644 index 0000000..9380b64 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/Point.java @@ -0,0 +1,55 @@ +package org.torusresearch.torusutils.types; + +import org.bouncycastle.crypto.params.ECDomainParameters; +import org.bouncycastle.jce.ECNamedCurveTable; +import org.bouncycastle.jce.spec.ECParameterSpec; +import org.bouncycastle.util.encoders.Hex; +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.helpers.Common; +import org.torusresearch.torusutils.helpers.KeyUtils; + +import java.math.BigInteger; + +public class Point { + private final BigInteger x; + private final BigInteger y; + @SuppressWarnings("unused") + private final ECDomainParameters ecCurve; + + public Point(@NotNull String x, @NotNull String y) { + this.x = new BigInteger(x, 16); + this.y = new BigInteger(y, 16); + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + this.ecCurve = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); + } + + public Point(@NotNull BigInteger x, @NotNull BigInteger y) { + this.x = x; + this.y = y; + ECParameterSpec spec = ECNamedCurveTable.getParameterSpec("secp256k1"); + this.ecCurve = new ECDomainParameters(spec.getCurve(), spec.getG(), spec.getN(), spec.getH(), spec.getSeed()); + } + + public BigInteger getX() { + return x; + } + + public BigInteger getY() { + return y; + } + + @SuppressWarnings("unused") + public byte[] encode(@NotNull String enc) throws Exception { + String xPadded = Common.padLeft(this.x.toString(16),'0', 64); + String yPadded = Common.padLeft(this.y.toString(16),'0', 64); + switch (enc) { + case "arr": + return Hex.decode("04"+ xPadded + yPadded); + case "elliptic-compressed": + return KeyUtils.serializePublicKey(KeyUtils.deserializePublicKey(Hex.decode("04"+ xPadded + yPadded)), true); + default: + throw new IllegalArgumentException("Invalid encoding in Point"); + } + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/types/Polynomial.java b/src/main/java/org/torusresearch/torusutils/types/Polynomial.java new file mode 100644 index 0000000..524f234 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/Polynomial.java @@ -0,0 +1,46 @@ +package org.torusresearch.torusutils.types; + +import static org.torusresearch.torusutils.helpers.KeyUtils.getOrderOfCurve; + +import org.jetbrains.annotations.NotNull; + +import java.math.BigInteger; +import java.util.LinkedHashMap; +import java.util.Map; + +public class Polynomial { + private final BigInteger[] polynomial; + + public Polynomial(@NotNull BigInteger[] polynomial) { + this.polynomial = polynomial; + } + + @SuppressWarnings("unused") + public int getThreshold() { + return polynomial.length; + } + + public BigInteger polyEval(@NotNull BigInteger x) { + BigInteger xi = x; + BigInteger sum = BigInteger.ZERO; + sum = sum.add(polynomial[0]); + + for (int i = 1; i < polynomial.length; i++) { + BigInteger tmp = xi.multiply(polynomial[i]); + sum = sum.add(tmp).mod(getOrderOfCurve()); + xi = xi.multiply(x).mod(getOrderOfCurve()); + } + + return sum; + } + + public Map generateShares(@NotNull BigInteger[] shareIndexes) { + Map shares = new LinkedHashMap<>(); + for (BigInteger shareIndex : shareIndexes) { + String hexString = String.format("%064x", shareIndex); + shares.put(hexString, new Share(shareIndex, polyEval(shareIndex))); + } + return shares; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/types/RetrieveSharesResponse.java b/src/main/java/org/torusresearch/torusutils/types/RetrieveSharesResponse.java deleted file mode 100644 index 89e730e..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/RetrieveSharesResponse.java +++ /dev/null @@ -1,28 +0,0 @@ -package org.torusresearch.torusutils.types; - -import java.math.BigInteger; - -public class RetrieveSharesResponse { - private final String ethAddress; - private final BigInteger privKey; - - private final BigInteger nonce; - - public RetrieveSharesResponse(String _ethAddress, BigInteger _privKey, BigInteger _nonce) { - ethAddress = _ethAddress; - privKey = _privKey; - nonce = _nonce; - } - - public String getEthAddress() { - return ethAddress; - } - - public BigInteger getPrivKey() { - return privKey; - } - - public BigInteger getNonce() { - return nonce; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/SessionData.java b/src/main/java/org/torusresearch/torusutils/types/SessionData.java new file mode 100644 index 0000000..4888622 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/SessionData.java @@ -0,0 +1,24 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.types.common.SessionToken; + +import java.util.List; + +public class SessionData { + + private final List sessionTokenData; + private final String sessionAuthKey; + + public SessionData(@NotNull List sessionTokenData, @NotNull String sessionAuthKey) { + this.sessionTokenData = sessionTokenData; + this.sessionAuthKey = sessionAuthKey; + } + + public List getSessionTokenData() { + return sessionTokenData; + } + public String getSessionAuthKey() { + return sessionAuthKey; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/Share.java b/src/main/java/org/torusresearch/torusutils/types/Share.java new file mode 100644 index 0000000..112f68c --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/Share.java @@ -0,0 +1,48 @@ +package org.torusresearch.torusutils.types; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.helpers.TorusUtilError; + +import java.math.BigInteger; + +public class Share { + private final BigInteger share; + private final BigInteger shareIndex; + + @SuppressWarnings("unused") + public Share(@NotNull String shareIndex, @NotNull String share) throws Exception { + try { + this.shareIndex = new BigInteger(shareIndex, 16); + } catch (NumberFormatException e) { + throw new TorusUtilError("Invalid input"); + } + + try { + this.share = new BigInteger(share, 16); + } catch (NumberFormatException e) { + throw new TorusUtilError("Invalid input"); + } + } + + public Share(@NotNull BigInteger shareIndex, @NotNull BigInteger share) { + this.shareIndex = shareIndex; + this.share = share; + } + + public BigInteger getShare() { + return share; + } + + public BigInteger getShareIndex() { + return shareIndex; + } + + // toString method for debugging purposes + @Override + public String toString() { + return "Share{" + + "share=" + share.toString(16) + + ", shareIndex=" + shareIndex.toString(16) + + '}'; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/SomeException.java b/src/main/java/org/torusresearch/torusutils/types/SomeException.java deleted file mode 100644 index 70d6ed9..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/SomeException.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.torusresearch.torusutils.types; - -import com.google.gson.Gson; - -public class SomeException extends Exception { - public String[] errors; - - public String[] responses; - - public String predicate; - - public SomeException(String[] errors ,String[] responses, String predicate) { - super("Unable to resolve enough promises."); - this.errors = errors; - this.responses = responses; - this.predicate = predicate; - } - - @Override - public String toString() { - Gson gson = new Gson(); - return gson.toJson(this.errors) + ", responses: " + - gson.toJson(this.responses) + this.predicate; - } -} \ No newline at end of file diff --git a/src/main/java/org/torusresearch/torusutils/types/TorusCtorOptions.java b/src/main/java/org/torusresearch/torusutils/types/TorusCtorOptions.java deleted file mode 100644 index b419328..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/TorusCtorOptions.java +++ /dev/null @@ -1,94 +0,0 @@ -package org.torusresearch.torusutils.types; - -import java.math.BigInteger; - -public class TorusCtorOptions { - private String metadataHost = "https://metadata.tor.us"; - private String allowHost = "https://signer.tor.us/api/allow"; - private boolean enableOneKey = false; - private String signerHost = "https://signer.tor.us/api/sign"; - // in seconds - private BigInteger serverTimeOffset = new BigInteger("0"); - private String origin; - private String network = "mainnet"; - - private boolean legacyNonce = false; - private String clientId; - - public TorusCtorOptions(String origin, String clientid) { - this.origin = origin; - this.clientId = clientid; - } - - public String getNetwork() { - return network; - } - - public void setNetwork(String network) { - this.network = network; - } - - public String getOrigin() { - return origin; - } - - public void setOrigin(String origin) { - this.origin = origin; - } - - public BigInteger getServerTimeOffset() { - return serverTimeOffset; - } - - public void setServerTimeOffset(BigInteger serverTimeOffset) { - this.serverTimeOffset = serverTimeOffset; - } - - public String getSignerHost() { - return signerHost; - } - - public void setSignerHost(String signerHost) { - this.signerHost = signerHost; - } - - public boolean isEnableOneKey() { - return enableOneKey; - } - - public void setEnableOneKey(boolean enableOneKey) { - this.enableOneKey = enableOneKey; - } - - public String getAllowHost() { - return allowHost; - } - - public void setAllowHost(String allowHost) { - this.allowHost = allowHost; - } - - public String getMetadataHost() { - return metadataHost; - } - - public void setMetadataHost(String _metadataHost) { - this.metadataHost = _metadataHost; - } - - public boolean isLegacyNonce() { - return legacyNonce; - } - - public void setLegacyNonce(boolean legacyNonce) { - this.legacyNonce = legacyNonce; - } - - public String getClientId() { - return clientId; - } - - public void setClientId(String clientId) { - this.clientId = clientId; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/TorusException.java b/src/main/java/org/torusresearch/torusutils/types/TorusException.java deleted file mode 100644 index 6daf4d2..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/TorusException.java +++ /dev/null @@ -1,11 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class TorusException extends Exception { - public TorusException(String msg) { - super(msg); - } - - public TorusException(String msg, Throwable err) { - super(msg, err); - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/TorusPublicKey.java b/src/main/java/org/torusresearch/torusutils/types/TorusPublicKey.java deleted file mode 100644 index 0e07aa8..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/TorusPublicKey.java +++ /dev/null @@ -1,61 +0,0 @@ -package org.torusresearch.torusutils.types; - -import org.jetbrains.annotations.Nullable; -import org.torusresearch.fetchnodedetails.types.TorusNodePub; - -import java.math.BigInteger; - -public class TorusPublicKey extends TorusNodePub { - private final String address; - private TypeOfUser typeOfUser; - private BigInteger metadataNonce; - @Nullable - private GetOrSetNonceResult.PubNonce pubNonce; - private boolean upgraded; - - public TorusPublicKey(String _X, String _Y, String _address) { - super(_X, _Y); - address = _address; - } - - public TorusPublicKey(String _address) { - super(null, null); - address = _address; - } - - public String getAddress() { - return address; - } - - public TypeOfUser getTypeOfUser() { - return typeOfUser; - } - - public void setTypeOfUser(TypeOfUser typeOfUser) { - this.typeOfUser = typeOfUser; - } - - public BigInteger getMetadataNonce() { - return metadataNonce; - } - - public void setMetadataNonce(BigInteger metadataNonce) { - this.metadataNonce = metadataNonce; - } - - public GetOrSetNonceResult.PubNonce getPubNonce() { - return pubNonce; - } - - public void setPubNonce(GetOrSetNonceResult.PubNonce pubNonce) { - this.pubNonce = pubNonce; - } - - public boolean getUpgraded() { - return upgraded; - } - - public void setUpgraded(boolean upgraded) { - this.upgraded = upgraded; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/TorusUtilsExtraParams.java b/src/main/java/org/torusresearch/torusutils/types/TorusUtilsExtraParams.java new file mode 100644 index 0000000..4893231 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/TorusUtilsExtraParams.java @@ -0,0 +1,64 @@ +package org.torusresearch.torusutils.types; + +// TODO: This class is a bit of a mess for legacy reasons and should be cleaned up in future. + +import org.jetbrains.annotations.Nullable; + +public class TorusUtilsExtraParams { + @Nullable + public final String nonce; // farcaster + @Nullable + public final String message; // farcaster + @Nullable + public final String signature; // farcaster, passkey, webauthn + @Nullable + public final String clientDataJson; // passkey, webauthn + @Nullable + public final String authenticatorData; // passkey, webauthn + @Nullable + public final String publicKey; // passkey, webauthn + @Nullable + public final String challenge; // passkey, webauthn + @Nullable + public final String rpOrigin; // passkey, webauthn + @Nullable + public final String rpId; // passkey, webauthn + @Nullable + public Integer session_token_exp_second; + @Nullable + public final Integer timestamp; // signature + + // Default constructor + public TorusUtilsExtraParams() { + this.nonce = null; + this.message = null; + this.signature = null; + this.clientDataJson = null; + this.authenticatorData = null; + this.publicKey = null; + this.challenge = null; + this.rpOrigin = null; + this.rpId = null; + this.session_token_exp_second = null; + this.timestamp = null; + } + + @SuppressWarnings("unused") + public TorusUtilsExtraParams( + @Nullable Integer session_token_exp_second, + @Nullable String nonce, @Nullable String message, @Nullable String signature, @Nullable String clientDataJson, @Nullable String authenticatorData, + @Nullable String publicKey, @Nullable String challenge, @Nullable String rpOrigin, @Nullable String rpId, @Nullable Integer timestamp) { + this.nonce = nonce; + this.message = message; + this.signature = signature; + this.clientDataJson = clientDataJson; + this.authenticatorData = authenticatorData; + this.publicKey = publicKey; + this.challenge = challenge; + this.rpOrigin = rpOrigin; + this.rpId = rpId; + this.session_token_exp_second = session_token_exp_second; + this.timestamp = timestamp; + } +} + diff --git a/src/main/java/org/torusresearch/torusutils/types/TypeOfUser.java b/src/main/java/org/torusresearch/torusutils/types/TypeOfUser.java deleted file mode 100644 index ee1fde6..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/TypeOfUser.java +++ /dev/null @@ -1,6 +0,0 @@ -package org.torusresearch.torusutils.types; - -public enum TypeOfUser { - v1, - v2 -} diff --git a/src/main/java/org/torusresearch/torusutils/types/VerifierArgs.java b/src/main/java/org/torusresearch/torusutils/types/VerifierArgs.java deleted file mode 100644 index b67b0dd..0000000 --- a/src/main/java/org/torusresearch/torusutils/types/VerifierArgs.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.torusresearch.torusutils.types; - -public class VerifierArgs { - private final String verifier; - private final String verifierId; - - public VerifierArgs(String _verifier, String _verifierId) { - verifier = _verifier; - verifierId = _verifierId; - } - - public String getVerifier() { - return verifier; - } - - public String getVerifierId() { - return verifierId; - } -} diff --git a/src/main/java/org/torusresearch/torusutils/types/VerifierParams.java b/src/main/java/org/torusresearch/torusutils/types/VerifierParams.java index 7ddc125..44a8b81 100644 --- a/src/main/java/org/torusresearch/torusutils/types/VerifierParams.java +++ b/src/main/java/org/torusresearch/torusutils/types/VerifierParams.java @@ -1,13 +1,23 @@ package org.torusresearch.torusutils.types; -public class VerifierParams { - private final String verifierId; +import org.jetbrains.annotations.NotNull; - public VerifierParams(String _verifierId) { - verifierId = _verifierId; - } +import io.reactivex.annotations.Nullable; - public String getVerifierId() { - return verifierId; +public class VerifierParams { + public final String verifier_id; + @Nullable + public final String extended_verifier_id; + @Nullable + public final String[] sub_verifier_ids; + @Nullable + public final VerifyParams[] verify_params; + + public VerifierParams(@NotNull String verifierId, @Nullable String extendedVerifierId, @Nullable String[] subVerifierIds, @Nullable VerifyParams[] verifyParams) { + this.verifier_id = verifierId; + this.extended_verifier_id = extendedVerifierId; + this.verify_params = verifyParams; + this.sub_verifier_ids = subVerifierIds; } } + diff --git a/src/main/java/org/torusresearch/torusutils/types/VerifyParams.java b/src/main/java/org/torusresearch/torusutils/types/VerifyParams.java new file mode 100644 index 0000000..6e36394 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/VerifyParams.java @@ -0,0 +1,16 @@ +package org.torusresearch.torusutils.types; + + +import org.jetbrains.annotations.Nullable; + +public class VerifyParams { + @Nullable + public final String verifier_id; + @Nullable + public final String idtoken; + + public VerifyParams(@Nullable String verifier_id, @Nullable String idtoken) { + this.verifier_id = verifier_id; + this.idtoken = idtoken; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/ImportedShare.java b/src/main/java/org/torusresearch/torusutils/types/common/ImportedShare.java new file mode 100644 index 0000000..1269081 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/ImportedShare.java @@ -0,0 +1,41 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.types.Point; +import org.torusresearch.torusutils.types.common.ecies.EciesHexOmitCipherText; + +public class ImportedShare { + + public final String oauth_pub_key_x; + public final String oauth_pub_key_y; + public final Point final_user_point; + public final String signing_pub_key_x; + public final String signing_pub_key_y; + public final String encryptedShare; + public final EciesHexOmitCipherText encryptedShareMetadata; + @Nullable + public final String encryptedSeed; + public final int node_index; + @Nullable + public final TorusKeyType key_type; + public final String nonce_data; + public final String nonce_signature; + + public ImportedShare(@NotNull String oauth_pub_key_x, @NotNull String oauth_pub_key_y, @NotNull Point final_user_point, @NotNull String signing_pub_key_x, @NotNull String signing_pub_key_y, @NotNull String encryptedShare, + @NotNull EciesHexOmitCipherText encryptedShareMetadata, @Nullable String encryptedSeed, int node_index, @Nullable TorusKeyType key_type, + @NotNull String nonce_data, @NotNull String nonce_signature) { + this.oauth_pub_key_x = oauth_pub_key_x; + this.oauth_pub_key_y = oauth_pub_key_y; + this.final_user_point = final_user_point; + this.signing_pub_key_x = signing_pub_key_x; + this.signing_pub_key_y = signing_pub_key_y; + this.encryptedShare = encryptedShare; + this.encryptedShareMetadata = encryptedShareMetadata; + this.encryptedSeed = encryptedSeed; + this.node_index = node_index; + this.key_type = key_type; + this.nonce_data = nonce_data; + this.nonce_signature = nonce_signature; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyLookupResult.java b/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyLookupResult.java new file mode 100644 index 0000000..bcc3d04 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyLookupResult.java @@ -0,0 +1,29 @@ +package org.torusresearch.torusutils.types.common.KeyLookup; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.torusutils.apis.JsonRPCErrorInfo; +import org.torusresearch.torusutils.apis.responses.GetOrSetNonceResult; + +import java.util.List; + +public class KeyLookupResult { + @Nullable + public final KeyResult keyResult; + + public final List nodeIndexes; + public final Integer server_time_offset; + @Nullable + public final GetOrSetNonceResult nonceResult; + + @Nullable + public final JsonRPCErrorInfo errorResult; + + public KeyLookupResult(@Nullable KeyResult keyResult, @NotNull List nodeIndexes, @NotNull Integer server_time_offset, @Nullable GetOrSetNonceResult nonceResult, @Nullable JsonRPCErrorInfo errorResult) { + this.keyResult = keyResult; + this.nodeIndexes = nodeIndexes; + this.server_time_offset = server_time_offset; + this.nonceResult = nonceResult; + this.errorResult = errorResult; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyResult.java b/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyResult.java new file mode 100644 index 0000000..b926052 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/KeyLookup/KeyResult.java @@ -0,0 +1,20 @@ +package org.torusresearch.torusutils.types.common.KeyLookup; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.apis.responses.VerifierLookupResponse.VerifierKey; + +public class KeyResult { + public VerifierKey[] keys; + public final Boolean is_new_key; + + @SuppressWarnings("unused") + public KeyResult(@NotNull VerifierKey[] keys, @NotNull Boolean is_new_key) { + this.keys = keys; + this.is_new_key = is_new_key; + } + + public KeyResult(@NotNull Boolean is_new_key) { + this.keys = new VerifierKey[]{}; + this.is_new_key = is_new_key; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/PrivateKeyData.java b/src/main/java/org/torusresearch/torusutils/types/common/PrivateKeyData.java new file mode 100644 index 0000000..c6c89e8 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/PrivateKeyData.java @@ -0,0 +1,65 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; + +public class PrivateKeyData { + private final String oAuthKey; + private final String oAuthPubKey; + private final String nonce; + private final String signingKey; + private final String signingPubKey; + private final String finalKey; + private final String finalPubKey; + + public PrivateKeyData(@NotNull String oAuthKey, @NotNull String oAuthPubKey, @NotNull String nonce, @NotNull String signingKey, @NotNull String signingPubKey, @NotNull String finalKey, @NotNull String finalPubKey) { + this.oAuthKey = oAuthKey; + this.oAuthPubKey = oAuthPubKey; + this.nonce = nonce; + this.signingKey = signingKey; + this.signingPubKey = signingPubKey; + this.finalKey = finalKey; + this.finalPubKey = finalPubKey; + } + + public String getOAuthKey() { + return oAuthKey; + } + + public String getOAuthPubKey() { + return oAuthPubKey; + } + + public String getNonce() { + return nonce; + } + + public String getSigningKey() { + return signingKey; + } + + public String getSigningPubKey() { + return signingPubKey; + } + + @SuppressWarnings("unused") + public String getFinalKey() { + return finalKey; + } + + public String getFinalPubKey() { + return finalPubKey; + } + + @Override + public String toString() { + return "PrivateKeyData{" + + "oAuthKey='" + oAuthKey + '\'' + + ", oAuthPubKey='" + oAuthPubKey + '\'' + + ", nonce='" + nonce + '\'' + + ", signingKey='" + signingKey + '\'' + + ", signingPubKey='" + signingPubKey + '\'' + + ", finalKey='" + finalKey + '\'' + + ", finalPubKey='" + finalPubKey + '\'' + + '}'; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/PubNonce.java b/src/main/java/org/torusresearch/torusutils/types/common/PubNonce.java new file mode 100644 index 0000000..405380d --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/PubNonce.java @@ -0,0 +1,13 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; + +public class PubNonce { + public final String x; + public final String y; + + public PubNonce(@NotNull String x, @NotNull String y) { + this.x = x; + this.y = y; + } +} \ No newline at end of file diff --git a/src/main/java/org/torusresearch/torusutils/types/common/SessionToken.java b/src/main/java/org/torusresearch/torusutils/types/common/SessionToken.java new file mode 100644 index 0000000..79bf686 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/SessionToken.java @@ -0,0 +1,37 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; + +public class SessionToken { + + private final String token; + private final String signature; + private final String node_pubx; + private final String node_puby; + + public SessionToken(@NotNull String token, @NotNull String signature, @NotNull String node_pubx, @NotNull String node_puby) { + this.token = token; + this.signature = signature; + this.node_pubx = node_pubx; + this.node_puby = node_puby; + } + + public String getToken() { + return token; + } + + @SuppressWarnings("unused") + public String getSignature() { + return signature; + } + + @SuppressWarnings("unused") + public String getNode_pubx() { + return node_pubx; + } + + @SuppressWarnings("unused") + public String getNode_puby() { + return node_puby; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/TorusKey.java b/src/main/java/org/torusresearch/torusutils/types/common/TorusKey.java new file mode 100644 index 0000000..9c7c351 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/TorusKey.java @@ -0,0 +1,46 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.Metadata; + +public class TorusKey { + + private final FinalKeyData finalKeyData; + private final OAuthKeyData oAuthKeyData; + private final SessionData sessionData; + + private final Metadata metadata; + private final NodesData nodesData; + + public TorusKey(@NotNull FinalKeyData finalKeyData, @NotNull OAuthKeyData oAuthKeyData, @NotNull SessionData sessionData, @NotNull Metadata metadata, @NotNull NodesData nodesData) { + this.finalKeyData = finalKeyData; + this.oAuthKeyData = oAuthKeyData; + this.sessionData = sessionData; + this.metadata = metadata; + this.nodesData = nodesData; + } + + public FinalKeyData getFinalKeyData() { + return finalKeyData; + } + + public OAuthKeyData getoAuthKeyData() { + return oAuthKeyData; + } + + public SessionData getSessionData() { + return sessionData; + } + + public Metadata getMetadata() { + return metadata; + } + + public NodesData getNodesData() { + return nodesData; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/TorusKeyType.java b/src/main/java/org/torusresearch/torusutils/types/common/TorusKeyType.java new file mode 100644 index 0000000..c18c4a9 --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/TorusKeyType.java @@ -0,0 +1,7 @@ +package org.torusresearch.torusutils.types.common; + +public enum TorusKeyType { + secp256k1, + @SuppressWarnings("unused") + ed25519 +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/TorusOptions.java b/src/main/java/org/torusresearch/torusutils/types/common/TorusOptions.java new file mode 100644 index 0000000..919668b --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/TorusOptions.java @@ -0,0 +1,35 @@ +package org.torusresearch.torusutils.types.common; + +import static org.torusresearch.fetchnodedetails.types.Utils.METADATA_MAP; + +import org.jetbrains.annotations.NotNull; +import org.jetbrains.annotations.Nullable; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; + +public class TorusOptions { + public final String legacyMetadataHost; + // in seconds + public final Integer serverTimeOffset; + public final Web3AuthNetwork network; + public final String clientId; + + public boolean enableOneKey; + + public final TorusKeyType keyType = TorusKeyType.secp256k1; + + public TorusOptions(@NotNull String clientId, @NotNull Web3AuthNetwork network, @Nullable String legacyMetadataHost, @Nullable Integer serverTimeOffset, @NotNull Boolean enableOneKey) { + this.clientId = clientId; + this.network = network; + if (legacyMetadataHost == null) { + this.legacyMetadataHost = METADATA_MAP.get(network); + } else { + this.legacyMetadataHost = legacyMetadataHost; + } + if (serverTimeOffset != null ) { + this.serverTimeOffset = serverTimeOffset; + } else { + this.serverTimeOffset = 0; + } + this.enableOneKey = enableOneKey; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/TorusPublicKey.java b/src/main/java/org/torusresearch/torusutils/types/common/TorusPublicKey.java new file mode 100644 index 0000000..8500fad --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/TorusPublicKey.java @@ -0,0 +1,38 @@ +package org.torusresearch.torusutils.types.common; + +import org.jetbrains.annotations.NotNull; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; + +public class TorusPublicKey { + + private final OAuthPubKeyData oAuthKeyData; + private final FinalPubKeyData finalKeyData; + private final Metadata metadata; + private final NodesData nodesData; + + public TorusPublicKey(@NotNull OAuthPubKeyData oAuthKeyData, @NotNull FinalPubKeyData finalKeyData, @NotNull Metadata metadata, @NotNull NodesData nodesData) { + this.oAuthKeyData = oAuthKeyData; + this.finalKeyData = finalKeyData; + this.metadata = metadata; + this.nodesData = nodesData; + } + + public OAuthPubKeyData getoAuthKeyData() { + return oAuthKeyData; + } + + public FinalPubKeyData getFinalKeyData() { + return finalKeyData; + } + + public Metadata getMetadata() { + return metadata; + } + + public NodesData getNodesData() { + return nodesData; + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/TypeOfUser.java b/src/main/java/org/torusresearch/torusutils/types/common/TypeOfUser.java new file mode 100644 index 0000000..485227a --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/TypeOfUser.java @@ -0,0 +1,6 @@ +package org.torusresearch.torusutils.types.common; + +public enum TypeOfUser { + v1, + v2 +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/ecies/Ecies.java b/src/main/java/org/torusresearch/torusutils/types/common/ecies/Ecies.java new file mode 100644 index 0000000..da72b2e --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/ecies/Ecies.java @@ -0,0 +1,22 @@ +package org.torusresearch.torusutils.types.common.ecies; + +import org.jetbrains.annotations.NotNull; + +public class Ecies extends EciesHexOmitCipherText { + public final String ciphertext; + + @SuppressWarnings("unused") + public Ecies(@NotNull String iv, @NotNull String ephemPublicKey, @NotNull String ciphertext, @NotNull String mac, @NotNull String mode) { + super(iv, ephemPublicKey, mac, mode); + this.ciphertext = ciphertext; + } + + public Ecies(@NotNull String iv, @NotNull String ephemPublicKey, @NotNull String ciphertext, @NotNull String mac) { + super(iv,ephemPublicKey,mac,"AES256"); + this.ciphertext = ciphertext; + } + + public EciesHexOmitCipherText omitCipherText() { + return new EciesHexOmitCipherText(iv, ephemPublicKey, mac, mode); + } +} diff --git a/src/main/java/org/torusresearch/torusutils/types/common/ecies/EciesHexOmitCipherText.java b/src/main/java/org/torusresearch/torusutils/types/common/ecies/EciesHexOmitCipherText.java new file mode 100644 index 0000000..e433c6c --- /dev/null +++ b/src/main/java/org/torusresearch/torusutils/types/common/ecies/EciesHexOmitCipherText.java @@ -0,0 +1,25 @@ +package org.torusresearch.torusutils.types.common.ecies; + +import org.jetbrains.annotations.NotNull; + +public class EciesHexOmitCipherText { + public final String iv; + public final String ephemPublicKey; + public final String mac; + public final String mode; + + public EciesHexOmitCipherText(@NotNull String iv, @NotNull String ephemPublicKey, @NotNull String mac, @NotNull String mode) { + this.iv = iv; + this.ephemPublicKey = ephemPublicKey; + this.mac = mac; + this.mode = mode; + } + + @SuppressWarnings("unused") + public EciesHexOmitCipherText(@NotNull String iv, @NotNull String ephemPublicKey, @NotNull String mac) { + this.iv = iv; + this.ephemPublicKey = ephemPublicKey; + this.mac = mac; + this.mode = "AES256"; + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/AquaTest.java b/src/test/java/org/torusresearch/torusutilstest/AquaTest.java index 7b9c8f2..c2aac66 100644 --- a/src/test/java/org/torusresearch/torusutilstest/AquaTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/AquaTest.java @@ -1,26 +1,38 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -31,8 +43,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class AquaTest { @@ -46,14 +56,10 @@ public class AquaTest { static String TORUS_TEST_EMAIL = "hello@tor.us"; - @BeforeAll - static void setup() throws ExecutionException, InterruptedException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Starting"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.AQUA); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("aqua"); - opts.setSignerHost("https://signer-polygon.tor.us/api/sign"); - opts.setAllowHost("https://signer-polygon.tor.us/api/allow"); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.AQUA, null, 0, false); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), @@ -63,56 +69,155 @@ static void setup() throws ExecutionException, InterruptedException, IOException @DisplayName("Gets Public Address") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("tkey-google-aqua", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0xDfA967285AC699A70DA340F60d00DB19A272639d", publicAddress.getAddress()); + public void shouldGetPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-aqua", TORUS_TEST_EMAIL).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-aqua", TORUS_TEST_EMAIL, null); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); + assertEquals("0xDfA967285AC699A70DA340F60d00DB19A272639d", publicAddress.getFinalKeyData().getWalletAddress()); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xDfA967285AC699A70DA340F60d00DB19A272639d", + "4fc8db5d3fe164a3ab70fd6348721f2be848df2cc02fd2db316a154855a7aa7d", + "f76933cbf5fe2916681075bb6cb4cde7d5f6b6ce290071b1b7106747d906457c"), + new FinalPubKeyData("0xDfA967285AC699A70DA340F60d00DB19A272639d", + "4fc8db5d3fe164a3ab70fd6348721f2be848df2cc02fd2db316a154855a7aa7d", + "f76933cbf5fe2916681075bb6cb4cde7d5f6b6ce290071b1b7106747d906457c"), + new Metadata(publicAddress.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("Fetch User Type and Public Address") + @Test + public void shouldFetchUserTypeAndPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-aqua", TORUS_TEST_EMAIL).get(); + TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-aqua", TORUS_TEST_EMAIL, null); + assertEquals("0x79F06350eF34Aeed4BE68e26954D405D573f1438", key.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key.getMetadata().getTypeOfUser()); + assertThat(key).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xDfA967285AC699A70DA340F60d00DB19A272639d", + "4fc8db5d3fe164a3ab70fd6348721f2be848df2cc02fd2db316a154855a7aa7d", + "f76933cbf5fe2916681075bb6cb4cde7d5f6b6ce290071b1b7106747d906457c"), + new FinalPubKeyData("0x79F06350eF34Aeed4BE68e26954D405D573f1438", + "99df45abc8e6ee03d2f94df33be79e939eadfbed20c6b88492782fdc3ef1dfd3", + "12bf3e54599a177fdb88f8b22419df7ddf1622e1d2344301edbe090890a72b16"), + new Metadata(key.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v2, false, key.getMetadata().getServerTimeOffset()), + new NodesData(key.getNodesData().getNodeIndexes()) + )); + + String v2Verifier = "tkey-google-aqua"; + // 1/1 user + String v2TestEmail = "somev2user@gmail.com"; + TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2TestEmail, null); + assertEquals("0xBc32f315515AdE7010cabC5Fd68c966657A570BD", key2.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key2.getMetadata().getTypeOfUser()); + assertThat(key2).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x4ea5260fF85678A2a326D08DF9C44d1f559a5828", + "0e6febe33a9d4eeb680cc6b63ff6237ad1971f27adcd7f104a3b1de18eda9337", + "a5a915561f3543688e71281a850b9ee10b9690f305d9e79028dfc8359192b82d"), + new FinalPubKeyData("0xBc32f315515AdE7010cabC5Fd68c966657A570BD", + "4897f120584ee18a72b9a6bb92c3ef6e45fc5fdff70beae7dc9325bd01332022", + "2066dbef2fcdded4573e3c04d1c04edd5d44662168e636ed9d0b0cbe2e67c968"), + new Metadata(new PubNonce("1601cf4dc4362b219260663d5ec5119699fbca185d08b7acb2e36cad914340d5", + "c2f7871f61ee71b4486ac9fb40ec759099800e737139dc5dfaaaed8c9d77c3c1"), + BigInteger.ZERO, TypeOfUser.v2, + false, + key2.getMetadata().getServerTimeOffset()), + new NodesData(key2.getNodesData().getNodeIndexes()) + )); + + // 2/n user + String v2nTestEmail = "caspertorus@gmail.com"; + TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2nTestEmail, null); + assertEquals("0x5469C5aCB0F30929226AfF4622918DA8E1424a8D", key3.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key3.getMetadata().getTypeOfUser()); + assertThat(key3).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x4ce0D09C3989eb3cC9372cC27fa022D721D737dD", + "e76d2f7fa2c0df324b4ab74629c3af47aa4609c35f1d2b6b90b77a47ab9a1281", + "b33b35148d72d357070f66372e07fec436001bdb15c098276b120b9ed64c1e5f"), + new FinalPubKeyData("0x5469C5aCB0F30929226AfF4622918DA8E1424a8D", + "c20fac685bb67169e92f1d5d8894d4eea18753c0ef3b7b1b2224233b2dfa3539", + "c4f080b5c8d5c55c8eaba4bec70f668f36db4126f358b491d631fefea7c19d21"), + new Metadata(key3.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v2, false, key3.getMetadata().getServerTimeOffset()), + new NodesData(key3.getNodesData().getNodeIndexes()) + )); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-aqua", email).get(); TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), - nodeDetails.getTorusNodePub(), new VerifierArgs("tkey-google-aqua", email)).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); + "tkey-google-aqua", email, ""); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertFalse(publicAddress.getMetadata().isUpgraded()); } @DisplayName("Login test") @Test - public void shouldLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldLogin() throws Exception { NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), - nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, - JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey()); - BigInteger requiredPrivateKey = new BigInteger("f726ce4ac79ae4475d72633c94769a8817aff35eebe2d4790aed7b5d8a84aa1d", 16); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0x9EBE51e49d8e201b40cAA4405f5E0B86d9D27195", retrieveSharesResponse.getEthAddress()); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, verifierParams, + JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("f726ce4ac79ae4475d72633c94769a8817aff35eebe2d4790aed7b5d8a84aa1d")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x9EBE51e49d8e201b40cAA4405f5E0B86d9D27195", + "c7bcc239f0957bb05bda94757eb4a5f648339424b22435da5cf7a0f2b2323664", + "63795690a33e575ee12d832935d563c2b5f2e1b1ffac63c32a4674152f68cb3f", + "f726ce4ac79ae4475d72633c94769a8817aff35eebe2d4790aed7b5d8a84aa1d"), + new OAuthKeyData("0x9EBE51e49d8e201b40cAA4405f5E0B86d9D27195", + "c7bcc239f0957bb05bda94757eb4a5f648339424b22435da5cf7a0f2b2323664", + "63795690a33e575ee12d832935d563c2b5f2e1b1ffac63c32a4674152f68cb3f", + "f726ce4ac79ae4475d72633c94769a8817aff35eebe2d4790aed7b5d8a84aa1d"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), - nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{ - new VerifyParams(idToken, TORUS_TEST_EMAIL) - }); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, - hashedIdToken).get(); - assertEquals("0x5b58d8a16fDA79172cd42Dc3068d5CEf26a5C81D", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, verifierParams, + hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x5b58d8a16fDA79172cd42Dc3068d5CEf26a5C81D", torusKey.getoAuthKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x5b58d8a16fDA79172cd42Dc3068d5CEf26a5C81D", + "37a4ac8cbef68e88bcec5909d9b6fffb539187365bb723f3d7bffe56ae80e31d", + "f963f2d08ed4dd0da9b8a8d74c6fdaeef7bdcde31f84fcce19fa2173d40b2c10", + "488d39ac548e15cfb0eaf161d86496e1645b09437df21311e24a56c4efd76355"), + new OAuthKeyData("0x5b58d8a16fDA79172cd42Dc3068d5CEf26a5C81D", + "37a4ac8cbef68e88bcec5909d9b6fffb539187365bb723f3d7bffe56ae80e31d", + "f963f2d08ed4dd0da9b8a8d74c6fdaeef7bdcde31f84fcce19fa2173d40b2c10", + "488d39ac548e15cfb0eaf161d86496e1645b09437df21311e24a56c4efd76355"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("RetrieveShares Some Nodes Down") + @Test + public void testRetrieveSharesSomeNodesDown() throws Exception { + String verifier = TORUS_TEST_VERIFIER; + String verifierId = TORUS_TEST_EMAIL; + VerifierParams verifierParams = new VerifierParams(verifierId, null, null, null); + String jwt = JwtUtils.generateIdToken(verifierId, algorithmRs); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, verifierId).get(); + String[] endpoints = nodeDetails.getTorusNodeEndpoints(); + endpoints[endpoints.length - 1] = "https://ndjnfjbfrj/random"; + TorusKey torusKey = torusUtils.retrieveShares(endpoints, verifier, verifierParams, jwt, null); + assertEquals("f726ce4ac79ae4475d72633c94769a8817aff35eebe2d4790aed7b5d8a84aa1d", torusKey.getFinalKeyData().getPrivKey()); } } diff --git a/src/test/java/org/torusresearch/torusutilstest/CelesteTest.java b/src/test/java/org/torusresearch/torusutilstest/CelesteTest.java index 8bddabc..13a9ab6 100644 --- a/src/test/java/org/torusresearch/torusutilstest/CelesteTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/CelesteTest.java @@ -1,27 +1,38 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -32,8 +43,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class CelesteTest { @@ -47,89 +56,155 @@ public class CelesteTest { static String TORUS_TEST_EMAIL = "hello@tor.us"; - @BeforeAll - static void setup() throws ExecutionException, InterruptedException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Startingg"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.CELESTE); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("celeste"); - opts.setSignerHost("https://signer-polygon.tor.us/api/sign"); - opts.setAllowHost("https://signer-polygon.tor.us/api/allow"); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.CELESTE, null, 0, false); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); - ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); + ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), + privateKey.getParams())); algorithmRs = Algorithm.ECDSA256(publicKey, privateKey); } @DisplayName("Gets Public Address") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("tkey-google-celeste", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", publicAddress.getAddress()); + public void shouldGetPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-celeste", TORUS_TEST_EMAIL).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-celeste", TORUS_TEST_EMAIL, null); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); + assertEquals("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", publicAddress.getFinalKeyData().getWalletAddress()); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", + "b89b9d66b247d7294a98616b95b7bfa1675aa85a1df4d89f2780283864f1b6e9", + "65422a8ccd66e638899fc53497e468a9a0bf50d45c9cb85ae0ffcfc13f433ffb"), + new FinalPubKeyData("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", + "b89b9d66b247d7294a98616b95b7bfa1675aa85a1df4d89f2780283864f1b6e9", + "65422a8ccd66e638899fc53497e468a9a0bf50d45c9cb85ae0ffcfc13f433ffb"), + new Metadata(publicAddress.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); } - @DisplayName("Fetch User Type and Public Address") @Test - public void shouldFetchUserTypeAndPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("tkey-google-celeste", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", key.getAddress()); - assertEquals(TypeOfUser.v1, key.getTypeOfUser()); + public void shouldFetchUserTypeAndPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-celeste", TORUS_TEST_EMAIL).get(); + TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-celeste", TORUS_TEST_EMAIL, null); + assertEquals("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", key.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v1, key.getMetadata().getTypeOfUser()); + assertThat(key).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", + "b89b9d66b247d7294a98616b95b7bfa1675aa85a1df4d89f2780283864f1b6e9", + "65422a8ccd66e638899fc53497e468a9a0bf50d45c9cb85ae0ffcfc13f433ffb"), + new FinalPubKeyData("0xC3115b9d6FaB99739b23DA9dfcBA47A4Ec4Cd113", + "b89b9d66b247d7294a98616b95b7bfa1675aa85a1df4d89f2780283864f1b6e9", + "65422a8ccd66e638899fc53497e468a9a0bf50d45c9cb85ae0ffcfc13f433ffb"), + new Metadata(key.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, key.getMetadata().getServerTimeOffset()), + new NodesData(key.getNodesData().getNodeIndexes()) + )); String v2Verifier = "tkey-google-celeste"; // 1/1 user String v2TestEmail = "somev2user@gmail.com"; - TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2TestEmail)).get(); - assertEquals("0x8d69CE354DA39413f205FdC8680dE1F3FBBb36e2", key2.getAddress()); - assertEquals(TypeOfUser.v2, key2.getTypeOfUser()); + TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2TestEmail, null); + assertEquals("0x8d69CE354DA39413f205FdC8680dE1F3FBBb36e2", key2.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key2.getMetadata().getTypeOfUser()); + assertThat(key2).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xda4afB35493094Dd2C05b186Ca0FABAD96491B21", + "cfa646a2949ebe559205c5c407d734d1b6927f2ea5fbeabfcbc31ab9a985a336", + "8f988eb8b59515293820aa38af172b153e8d25307db8d5f410407c20e062b6e6"), + new FinalPubKeyData("0x8d69CE354DA39413f205FdC8680dE1F3FBBb36e2", + "5962144e03b993b0e503eb4e6e0196427f9fc9472f0dfd1be2ca5d4939f91680", + "f6e81f01f483110badab18371237d15834f9ecf31c3588c165dae32ec446ac38"), + new Metadata(new PubNonce("2f630074151394ba1f715986a9215f4e36c9f22fc264ff880ef6d162c1300aa8", + "704cb63e5f7a291735c54e22242ef53673642ec1660da00f1abc2e7909da03d7"), + BigInteger.ZERO, TypeOfUser.v2, + false, + key2.getMetadata().getServerTimeOffset()), + new NodesData(key2.getNodesData().getNodeIndexes()) + )); // 2/n user String v2nTestEmail = "caspertorus@gmail.com"; - TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2nTestEmail)).get(); - assertEquals("0x8108c29976C458e76f797AD55A3715Ce80a3fe78", key3.getAddress()); - assertEquals(TypeOfUser.v2, key3.getTypeOfUser()); + TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2nTestEmail, null); + assertEquals("0x8108c29976C458e76f797AD55A3715Ce80a3fe78", key3.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key3.getMetadata().getTypeOfUser()); + assertThat(key3).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xc8c4748ec135196fb482C761da273C31Ec48B099", + "0cc857201e6c304dd893b243e323fe95982e5a99c0994cf902efa2432a672eb4", + "37a2f53c250b3e1186e38ece3dfcbcb23e325913038703531831b96d3e7b54cc"), + new FinalPubKeyData("0x8108c29976C458e76f797AD55A3715Ce80a3fe78", + "e95fe2d595ade03f56d9c9a147fbb67705041704f147576fa4a8afbe7dc69470", + "3e20e4b331466769c4dd78f4561bfb2849010b4005b09c2ed082380326724ebe"), + new Metadata(new PubNonce("f8ff2c44cc0abf512d35b35c3c5cbc0eda700d49bc13b72c5492b0cdb2ca3619", + "88fb3087cec269c8c39d25b04f15298d33712f13b0f9665821328dfc7a567afb"), BigInteger.ZERO, + TypeOfUser.v2, false, key3.getMetadata().getServerTimeOffset()), + new NodesData(key3.getNodesData().getNodeIndexes()) + )); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-celeste", email).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs("tkey-google-celeste", email)).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), + "tkey-google-celeste", email, ""); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertFalse(publicAddress.getMetadata().isUpgraded()); } @DisplayName("Login test") @Test - public void shouldLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldLogin() throws Exception { NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey()); - BigInteger requiredPrivateKey = new BigInteger("0ae056aa938080c9e8bf6641261619e09fd510c91bb5aad14b0de9742085a914", 16); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0x58420FB83971C4490D8c9B091f8bfC890D716617", retrieveSharesResponse.getEthAddress()); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, verifierParams, + JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getWalletAddress() != null) && torusKey.getFinalKeyData().getWalletAddress().equals("0x58420FB83971C4490D8c9B091f8bfC890D716617")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x58420FB83971C4490D8c9B091f8bfC890D716617", + "73b82ce0f8201a962636d404fe7a683f37c2267a9528576e1dac9964940add74", + "6d28c46c5385b90322bde74d6c5096e154eae2838399f4d6e8d752f7b0c449c1", + "0ae056aa938080c9e8bf6641261619e09fd510c91bb5aad14b0de9742085a914"), + new OAuthKeyData("0x58420FB83971C4490D8c9B091f8bfC890D716617", + "73b82ce0f8201a962636d404fe7a683f37c2267a9528576e1dac9964940add74", + "6d28c46c5385b90322bde74d6c5096e154eae2838399f4d6e8d752f7b0c449c1", + "0ae056aa938080c9e8bf6641261619e09fd510c91bb5aad14b0de9742085a914"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x", ""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{new VerifyParams(idToken, TORUS_TEST_EMAIL)}); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, hashedIdToken).get(); - assertEquals("0x535Eb1AefFAc6f699A2a1A5846482d7b5b2BD564", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, verifierParams, + hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x535Eb1AefFAc6f699A2a1A5846482d7b5b2BD564", torusKey.getoAuthKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x535Eb1AefFAc6f699A2a1A5846482d7b5b2BD564", + "df6eb11d52e76b388a44896e9442eda17096c2b67b0be957a4ba0b68a70111ca", + "bfd29ab1e97b3f7c444bb3e7ad0acb39d72589371387436c7d623d1e83f3d6eb", + "356305761eca57f27b09700d76456ad627b084152725dbfdfcfa0abcd9d4f17e"), + new OAuthKeyData("0x535Eb1AefFAc6f699A2a1A5846482d7b5b2BD564", + "df6eb11d52e76b388a44896e9442eda17096c2b67b0be957a4ba0b68a70111ca", + "bfd29ab1e97b3f7c444bb3e7ad0acb39d72589371387436c7d623d1e83f3d6eb", + "356305761eca57f27b09700d76456ad627b084152725dbfdfcfa0abcd9d4f17e"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } } - diff --git a/src/test/java/org/torusresearch/torusutilstest/CyanTest.java b/src/test/java/org/torusresearch/torusutilstest/CyanTest.java index a5e6a9b..d0aa716 100644 --- a/src/test/java/org/torusresearch/torusutilstest/CyanTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/CyanTest.java @@ -1,27 +1,38 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -32,8 +43,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class CyanTest { @@ -47,14 +56,10 @@ public class CyanTest { static String TORUS_TEST_EMAIL = "hello@tor.us"; - @BeforeAll - static void setup() throws ExecutionException, InterruptedException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Starting"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.CYAN); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("cyan"); - opts.setSignerHost("https://signer-polygon.tor.us/api/sign"); - opts.setAllowHost("https://signer-polygon.tor.us/api/allow"); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.CYAN, null, 0, false); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); @@ -63,71 +68,139 @@ static void setup() throws ExecutionException, InterruptedException, IOException @DisplayName("Gets Public Address") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("tkey-google-cyan", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0xA3767911A84bE6907f26C572bc89426dDdDB2825", publicAddress.getAddress()); + public void shouldGetPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-cyan", TORUS_TEST_EMAIL).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-cyan", TORUS_TEST_EMAIL, null); + assertEquals("0xA3767911A84bE6907f26C572bc89426dDdDB2825", publicAddress.getFinalKeyData().getWalletAddress()); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xA3767911A84bE6907f26C572bc89426dDdDB2825", + "2853f323437da98ce021d06854f4b292db433c0ad03b204ef223ac2583609a6a", + "f026b4788e23523e0c8fcbf0bdcf1c1a62c9cde8f56170309607a7a52a19f7c1"), + new FinalPubKeyData("0xA3767911A84bE6907f26C572bc89426dDdDB2825", + "2853f323437da98ce021d06854f4b292db433c0ad03b204ef223ac2583609a6a", + "f026b4788e23523e0c8fcbf0bdcf1c1a62c9cde8f56170309607a7a52a19f7c1"), + new Metadata(publicAddress.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); } @DisplayName("Fetch User Type and Public Address") @Test - public void shouldFetchUserTypeAndPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("tkey-google-cyan", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0x3507F0d192a44E436B8a6C32a37d57D022861b1a", key.getAddress()); - assertEquals(TypeOfUser.v2, key.getTypeOfUser()); + public void shouldFetchUserTypeAndPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-cyan", TORUS_TEST_EMAIL).get(); + TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-cyan", TORUS_TEST_EMAIL, null); + assertEquals("0x3507F0d192a44E436B8a6C32a37d57D022861b1a", key.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key.getMetadata().getTypeOfUser()); + assertThat(key).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xA3767911A84bE6907f26C572bc89426dDdDB2825", + "2853f323437da98ce021d06854f4b292db433c0ad03b204ef223ac2583609a6a", + "f026b4788e23523e0c8fcbf0bdcf1c1a62c9cde8f56170309607a7a52a19f7c1"), + new FinalPubKeyData("0x3507F0d192a44E436B8a6C32a37d57D022861b1a", + "8aaadab9530cb157d0b0dfb7b27d1a3aaca45274563c22c92c77ee2191779051", + "d57b89d9f62bb6609d8542c3057943805c8c72f6f27d39781b820f27d7210f12"), + new Metadata(key.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v2, false, key.getMetadata().getServerTimeOffset()), + new NodesData(key.getNodesData().getNodeIndexes()) + )); String v2Verifier = "tkey-google-cyan"; // 1/1 user String v2TestEmail = "somev2user@gmail.com"; - TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2TestEmail)).get(); - assertEquals("0x8EA83Ace86EB414747F2b23f03C38A34E0217814", key2.getAddress()); - assertEquals(TypeOfUser.v2, key2.getTypeOfUser()); + TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2TestEmail, null); + assertThat(key2).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x29446f428293a4E6470AEaEDa6EAfA0F842EF54e", + "8b6f2048aba8c7833e3b02c5b6522bb18c484ad0025156e428f17fb8d8c34021", + "cd9ba153ff89d665f655d1be4c6912f3ff93996e6fe580d89e78bf1476fef2aa"), + new FinalPubKeyData("0x8EA83Ace86EB414747F2b23f03C38A34E0217814", + "cbe7b0f0332e5583c410fcacb6d4ff685bec053cfd943ac75f5e4aa3278a6fbb", + "b525c463f438c7a3c4b018c8c5d16c9ef33b9ac6f319140a22b48b17bdf532dd"), + new Metadata(new PubNonce("da0039dd481e140090bed9e777ce16c0c4a16f30f47e8b08b73ac77737dd2d4", + "7fecffd2910fa47dbdbc989f5c119a668fc922937175974953cbb51c49268265" + ), BigInteger.ZERO, TypeOfUser.v2, false, key2.getMetadata().getServerTimeOffset()), + new NodesData(key2.getNodesData().getNodeIndexes()) + )); + assertEquals("0x8EA83Ace86EB414747F2b23f03C38A34E0217814", key2.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key2.getMetadata().getTypeOfUser()); // 2/n user String v2nTestEmail = "caspertorus@gmail.com"; - TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2nTestEmail)).get(); - assertEquals("0xCC1f953f6972a9e3d685d260399D6B85E2117561", key3.getAddress()); - assertEquals(TypeOfUser.v2, key3.getTypeOfUser()); + TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2nTestEmail, null); + assertThat(key3).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xe8a19482cbe5FaC896A5860Ca4156fb999DDc73b", + "c491ba39155594896b27cf71a804ccf493289d918f40e6ba4d590f1c76139e9e", + "d4649ed9e46461e1af00399a4c65fabb1dc219b3f4af501a7d635c17f57ab553"), + new FinalPubKeyData("0xCC1f953f6972a9e3d685d260399D6B85E2117561", + "8d784434becaad9b23d9293d1f29c4429447315c4cac824cbf2eb21d3f7d79c8", + "fe46a0ef5efe33d16f6cfa678a597be930fbec5432cbb7f3580189c18bd7e157"), + new Metadata(new PubNonce("50e250cc6ac1d50d32d2b0f85f11c6625a917a115ced4ef24f4eac183e1525c7", + "8067a52d02b8214bf82e91b66ce5009f674f4c3998b103059c46c386d0c17f90" + ), BigInteger.ZERO, TypeOfUser.v2, false, key3.getMetadata().getServerTimeOffset()), + new NodesData(key3.getNodesData().getNodeIndexes()) + )); + assertEquals("0xCC1f953f6972a9e3d685d260399D6B85E2117561", key3.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key3.getMetadata().getTypeOfUser()); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("tkey-google-cyan", email).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs("tkey-google-cyan", email)).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "tkey-google-cyan", email, null); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertFalse(publicAddress.getMetadata().isUpgraded()); } @DisplayName("Login test") @Test - public void shouldLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldLogin() throws Exception { NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey()); - BigInteger requiredPrivateKey = new BigInteger("42385046760370370667571434468449261359414980205144543049037030193411736357576", 10); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0xC615aA03Dd8C9b2dc6F7c43cBDfF2c34bBa47Ec9", retrieveSharesResponse.getEthAddress()); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("5db51619684b32a2ff2375b4c03459d936179dfba401cb1c176b621e8a2e4ac8")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0xC615aA03Dd8C9b2dc6F7c43cBDfF2c34bBa47Ec9", + "e2ed6033951af2851d1bea98799e62fb1ff24b952c1faea17922684678ba42d1", + "beef0efad88e81385952c0068ca48e8b9c2121be87cb0ddf18a68806db202359", + "5db51619684b32a2ff2375b4c03459d936179dfba401cb1c176b621e8a2e4ac8"), + new OAuthKeyData("0xC615aA03Dd8C9b2dc6F7c43cBDfF2c34bBa47Ec9", + "e2ed6033951af2851d1bea98799e62fb1ff24b952c1faea17922684678ba42d1", + "beef0efad88e81385952c0068ca48e8b9c2121be87cb0ddf18a68806db202359", + "5db51619684b32a2ff2375b4c03459d936179dfba401cb1c176b621e8a2e4ac8"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{new VerifyParams(idToken, TORUS_TEST_EMAIL)}); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, hashedIdToken).get(); - assertEquals("0x34117FDFEFBf1ad2DFA6d4c43804E6C710a6fB04", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x34117FDFEFBf1ad2DFA6d4c43804E6C710a6fB04", torusKey.getoAuthKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x34117FDFEFBf1ad2DFA6d4c43804E6C710a6fB04", + "afd12f2476006ef6aa8778190b29676a70039df8688f9dee69c779bdc8ff0223", + "e557a5ee879632727f5979d6b9cea69d87e3dab54a8c1b6685d86dfbfcd785dd", + "45a5b62c4ff5490baa75d33bf4f03ba6c5b0095678b0f4055312eef7b780b7bf"), + new OAuthKeyData("0x34117FDFEFBf1ad2DFA6d4c43804E6C710a6fB04", + "afd12f2476006ef6aa8778190b29676a70039df8688f9dee69c779bdc8ff0223", + "e557a5ee879632727f5979d6b9cea69d87e3dab54a8c1b6685d86dfbfcd785dd", + "45a5b62c4ff5490baa75d33bf4f03ba6c5b0095678b0f4055312eef7b780b7bf"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } } diff --git a/src/test/java/org/torusresearch/torusutilstest/MainnetTest.java b/src/test/java/org/torusresearch/torusutilstest/MainnetTest.java index e31ae80..d098029 100644 --- a/src/test/java/org/torusresearch/torusutilstest/MainnetTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/MainnetTest.java @@ -1,27 +1,37 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -32,8 +42,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class MainnetTest { @@ -47,12 +55,10 @@ public class MainnetTest { static String TORUS_TEST_EMAIL = "hello@tor.us"; - @BeforeAll - static void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Starting"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.MAINNET); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("mainnet"); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.MAINNET, null, 0, false); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); @@ -61,72 +67,136 @@ static void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpec @DisplayName("Gets Public Address") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("google", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A", publicAddress.getAddress()); + public void shouldGetPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google", TORUS_TEST_EMAIL).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "google", TORUS_TEST_EMAIL, null); + assertEquals("0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A", publicAddress.getFinalKeyData().getWalletAddress()); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A", + "3b5655d78978b6fd132562b5cb66b11bcd868bd2a9e16babe4a1ca50178e57d4", + "15338510798d6b55db28c121d86babcce19eb9f1882f05fae8ee9b52ed09e8f1"), + new FinalPubKeyData("0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A", + "3b5655d78978b6fd132562b5cb66b11bcd868bd2a9e16babe4a1ca50178e57d4", + "15338510798d6b55db28c121d86babcce19eb9f1882f05fae8ee9b52ed09e8f1"), + new Metadata(publicAddress.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); } - @DisplayName("Fetch User Type and Public Address") @Test - public void shouldFetchUserTypeAndPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("google", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0xb2e1c3119f8D8E73de7eaF7A535FB39A3Ae98C5E", key.getAddress());//0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A - assertEquals(TypeOfUser.v2, key.getTypeOfUser()); + public void shouldFetchUserTypeAndPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google", TORUS_TEST_EMAIL).get(); + TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), "google", TORUS_TEST_EMAIL, null); + assertEquals("0xb2e1c3119f8D8E73de7eaF7A535FB39A3Ae98C5E", key.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key.getMetadata().getTypeOfUser()); + assertThat(key).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x0C44AFBb5395a9e8d28DF18e1326aa0F16b9572A", + "3b5655d78978b6fd132562b5cb66b11bcd868bd2a9e16babe4a1ca50178e57d4", + "15338510798d6b55db28c121d86babcce19eb9f1882f05fae8ee9b52ed09e8f1"), + new FinalPubKeyData("0xb2e1c3119f8D8E73de7eaF7A535FB39A3Ae98C5E", + "072beda348a832aed06044a258cb6a8d428ec7c245c5da92db5da4f3ab433e55", + "54ace0d3df2504fa29f17d424a36a0f92703899fad0afee93d010f6d84b310e5"), + new Metadata(key.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v2, false, key.getMetadata().getServerTimeOffset()), + new NodesData(key.getNodesData().getNodeIndexes()) + )); String v2Verifier = "tkey-google"; // 1/1 user String v2TestEmail = "somev2user@gmail.com"; - TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2TestEmail)).get(); - assertEquals("0xFf669A15bFFcf32D3C5B40bE9E5d409d60D43526", key2.getAddress()); - assertEquals(TypeOfUser.v2, key2.getTypeOfUser()); + TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2TestEmail, null); + assertEquals("0xFf669A15bFFcf32D3C5B40bE9E5d409d60D43526", key2.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key2.getMetadata().getTypeOfUser()); + assertThat(key2).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xA9c6829e4899b6D630130ebf59D046CA868D7f83", + "5566cd940ea540ba1a3ba2ff0f5fd3d9a3a74350ac3baf47b811592ae6ea1c30", + "07a302e87e8d9eb5d143f570c248657288c13c09ecbe1e3a8720449daf9315b0"), + new FinalPubKeyData("0xFf669A15bFFcf32D3C5B40bE9E5d409d60D43526", + "bbfd26b1e61572c4e991a21b64f12b313cb6fce6b443be92d4d5fd8f311e8f33", + "df2c905356ec94faaa111a886be56ed6fa215b7facc1d1598486558355123c25"), + new Metadata(key2.getMetadata().getPubNonce(), + BigInteger.ZERO, TypeOfUser.v2, false, key2.getMetadata().getServerTimeOffset()), + new NodesData(key2.getNodesData().getNodeIndexes()) + )); // v1 user String v2nTestEmail = "caspertorus@gmail.com"; - TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2nTestEmail)).get(); - assertEquals("0x40A4A04fDa1f29a3667152C8830112FBd6A77BDD", key3.getAddress()); - assertEquals(TypeOfUser.v2, key3.getTypeOfUser()); + TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2nTestEmail, null); + assertEquals("0x40A4A04fDa1f29a3667152C8830112FBd6A77BDD", key3.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key3.getMetadata().getTypeOfUser()); + assertThat(key3).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x61E52B6e488EC3dD6FDc0F5ed04a62Bb9c6BeF53", + "c01282dd68d2341031a1cff06f70d821cad45140f425f1c25055a8aa64959df8", + "cb3937773bb819d60b780b6d4c2edcf27c0f7090ba1fc2ff42504a8138a8e2d7"), + new FinalPubKeyData("0x40A4A04fDa1f29a3667152C8830112FBd6A77BDD", + "6779af3031d9e9eec6b4133b0ae13e367c83a614f92d2008e10c7f3b8e6723bc", + "80edc4502abdfb220dd6e2fcfa2dbb058125dc95873e4bfa6877f9c26da7fdff"), + new Metadata(key3.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v2, false, key3.getMetadata().getServerTimeOffset()), + new NodesData(key3.getNodesData().getNodeIndexes()) + )); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google", email).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs("google", email)).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "google", email, ""); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertFalse(publicAddress.getMetadata().isUpgraded()); } @DisplayName("Login test") @Test - public void shouldLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldLogin() throws Exception { NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey()); - BigInteger requiredPrivateKey = new BigInteger("129494416ab5d5f674692b39fa49680e07d3aac01b9683ee7650e40805d4c44", 16); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0x90A926b698047b4A87265ba1E9D8b512E8489067", retrieveSharesResponse.getEthAddress()); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("0129494416ab5d5f674692b39fa49680e07d3aac01b9683ee7650e40805d4c44")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x90A926b698047b4A87265ba1E9D8b512E8489067", + "a92d8bf1f01ad62e189a5cb0f606b89aa6df1b867128438c38e3209f3b9fc34f", + "0ad1ffaecb2178b02a37c455975368be9b967ead1b281202cc8d48c77618bff1", + "0129494416ab5d5f674692b39fa49680e07d3aac01b9683ee7650e40805d4c44"), + new OAuthKeyData("0x90A926b698047b4A87265ba1E9D8b512E8489067", + "a92d8bf1f01ad62e189a5cb0f606b89aa6df1b867128438c38e3209f3b9fc34f", + "0ad1ffaecb2178b02a37c455975368be9b967ead1b281202cc8d48c77618bff1", + "0129494416ab5d5f674692b39fa49680e07d3aac01b9683ee7650e40805d4c44"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{new VerifyParams(idToken, TORUS_TEST_EMAIL)}); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, hashedIdToken).get(); - assertEquals("0x621a4d458cFd345dAE831D9E756F10cC40A50381", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x621a4d458cFd345dAE831D9E756F10cC40A50381", torusKey.getoAuthKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x621a4d458cFd345dAE831D9E756F10cC40A50381", + "52abc69ebec21deacd273dbdcb4d40066b701177bba906a187676e3292e1e236", + "5e57e251db2c95c874f7ec852439302a62ef9592c8c50024e3d48018a6f77c7e", + "f55d89088a0c491d797c00da5b2ed6dc9c269c960ff121e45f255d06a91c6534"), + new OAuthKeyData("0x621a4d458cFd345dAE831D9E756F10cC40A50381", + "52abc69ebec21deacd273dbdcb4d40066b701177bba906a187676e3292e1e236", + "5e57e251db2c95c874f7ec852439302a62ef9592c8c50024e3d48018a6f77c7e", + "f55d89088a0c491d797c00da5b2ed6dc9c269c960ff121e45f255d06a91c6534"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } } diff --git a/src/test/java/org/torusresearch/torusutilstest/OneKeyTest.java b/src/test/java/org/torusresearch/torusutilstest/OneKeyTest.java index 109306a..05d7df7 100644 --- a/src/test/java/org/torusresearch/torusutilstest/OneKeyTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/OneKeyTest.java @@ -1,27 +1,38 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -32,8 +43,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class OneKeyTest { @@ -47,79 +56,151 @@ public class OneKeyTest { static String TORUS_TEST_EMAIL = "hello@tor.us"; - @BeforeAll - static void setup() throws ExecutionException, InterruptedException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Starting"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.TESTNET); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("testnet"); - opts.setEnableOneKey(true); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.TESTNET, null, 0, true); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); algorithmRs = Algorithm.ECDSA256(publicKey, privateKey); } - @DisplayName("Gets Public Address") + @DisplayName("should still fetch v1 address correctly") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs(TORUS_TEST_VERIFIER, "Jonathan.Nolan@hotmail.com"); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args, true).get(); - assertEquals(TypeOfUser.v2, publicAddress.getTypeOfUser()); - assertEquals("0x2876820fd9536BD5dd874189A85d71eE8bDf64c2", publicAddress.getAddress()); + public void shouldStillFetchV1AddressCorrectly() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google-lrc", "himanshu@tor.us").get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "google-lrc", "himanshu@tor.us", null); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xf1e76fcDD28b5AA06De01de508fF21589aB9017E", + "b3f2b4d8b746353fe670e0c39ac9adb58056d4d7b718d06b623612d4ec49268b", + "ac9f79dff78add39cdba380dbbf517c20cf2c1e06b32842a90a84a31f6eb9a9a"), + new FinalPubKeyData("0x930abEDDCa6F9807EaE77A3aCc5c78f20B168Fd1", + "12f6b90d66bda29807cf9ff14b2e537c25080154fc4fafed446306e8356ff425", + "e7c92e164b83e1b53e41e5d87d478bb07d7b19d105143e426e1ef08f7b37f224"), + new Metadata(null, new BigInteger("186a20d9b00315855ff5622a083aca6b2d34ef66ef6e0a4de670f5b2fde37e0d", 16), TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); + assertEquals("0x930abEDDCa6F9807EaE77A3aCc5c78f20B168Fd1", publicAddress.getFinalKeyData().getWalletAddress()); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); + } + + @DisplayName("Login test v1") + @Test + public void shouldLoginV1() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x53010055542cCc0f2b6715a5c53838eC4aC96EF7", + "3fa78a0bfb9ec48810bf1ee332360def2600c4aef528ff8b1e49a0d304722c91", + "46aaca39fc00c0f88f63a79989697c70eeeeec6489300c493dd07a5608ded0d4", + "296045a5599afefda7afbdd1bf236358baff580a0fe2db62ae5c1bbe817fbae4"), + new OAuthKeyData("0xEfd7eDAebD0D99D1B7C8424b54835457dD005Dc4", + "18409385c38e9729eb6b7837dc8f234256233ffab1ed7eeb1c23b230333396b4", + "17d35ffc722d7a8dd88353815e9553cacf567c5f3b8d082adac9d653367ce47a", + "068ee4f97468ef1ae95d18554458d372e31968190ae38e377be59d8b3c9f7a25"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce( + "8e8c399d8ba00ff88e6c42eb40c10661f822868ba2ad8fe12a8830e996b1e25d", + "554b12253694bf9eb98485441bba7ba220b78cb78ee21664e96f934d10b1494d" + ), new BigInteger("22d160abe5320fe2be52a57c7aca8fe5d7e5eff104ff4d2b32767e3344e040bf", 16), TypeOfUser.v2, false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + assertEquals("296045a5599afefda7afbdd1bf236358baff580a0fe2db62ae5c1bbe817fbae4", torusKey.getFinalKeyData().getPrivKey()); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(TORUS_TEST_VERIFIER, email), true).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); - assertEquals(TypeOfUser.v2, publicAddress.getTypeOfUser()); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, email, null); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertEquals(publicAddress.getMetadata().getTypeOfUser(), TypeOfUser.v2); + assertFalse(publicAddress.getMetadata().isUpgraded()); } - @DisplayName("Login test v1") @Test - public void shouldLoginV1() throws ExecutionException, InterruptedException, TorusException { - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey().toString(16)); - BigInteger requiredPrivateKey = new BigInteger("296045a5599afefda7afbdd1bf236358baff580a0fe2db62ae5c1bbe817fbae4", 16); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0x53010055542cCc0f2b6715a5c53838eC4aC96EF7", retrieveSharesResponse.getEthAddress()); + public void testShouldBeAbleToKeyAssignViaLogin() throws Exception { + String fakeEmail = JwtUtils.getRandomEmail(); + String verifier = TORUS_TEST_VERIFIER; + String jwt = JwtUtils.generateIdToken(fakeEmail, algorithmRs); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, fakeEmail).get(); + VerifierParams verifierParams = new VerifierParams(fakeEmail, null, null, null); + TorusKey data = torusUtils.retrieveShares( + nodeDetails.getTorusNodeEndpoints(), + verifier, + verifierParams, + jwt, null + ); + assertEquals(data.getMetadata().getTypeOfUser(), TypeOfUser.v2); + assertNotEquals(data.getMetadata().getNonce(), BigInteger.ZERO); + assertFalse(data.getMetadata().isUpgraded()); + assertNotEquals("", data.getFinalKeyData().getWalletAddress()); + assertNotEquals("", data.getoAuthKeyData().getWalletAddress()); } @DisplayName("Login test v2") @Test - public void shouldLoginV2() throws ExecutionException, InterruptedException, TorusException { + public void shouldLoginV2() throws Exception { String email = "Jonathan.Nolan@hotmail.com"; + VerifierParams verifierParams = new VerifierParams(email, null, null, null); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", email); - }}, JwtUtils.generateIdToken(email, algorithmRs)).get(); - BigInteger requiredPrivateKey = new BigInteger("9ec5b0504e252e35218c7ce1e4660eac190a1505abfbec7102946f92ed750075", 16); - System.out.println(retrieveSharesResponse.getPrivKey().toString(16) + " priv key " + retrieveSharesResponse.getEthAddress() + " nonce " + retrieveSharesResponse.getNonce().toString(16)); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0x2876820fd9536BD5dd874189A85d71eE8bDf64c2", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, JwtUtils.generateIdToken(email, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals(torusKey.getFinalKeyData().getPrivKey(), "9ec5b0504e252e35218c7ce1e4660eac190a1505abfbec7102946f92ed750075"); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x2876820fd9536BD5dd874189A85d71eE8bDf64c2", + "ad4c223520aac9bc3ec72399869601fd59f29363471131914e2ed2bc4ba46e54", + "802c6e40b22b49b5ef73fa49b194c2037267215fa01683aa86746907aab37ae1", + "9ec5b0504e252e35218c7ce1e4660eac190a1505abfbec7102946f92ed750075"), + new OAuthKeyData("0x54de3Df0CA76AAe3e171FB410F0626Ab759f3c24", + "49d69b8550bb0eba77595c73bf57f0463ff96adf6b50d44f9e1bcf2b3fb7976e", + "d63bac65bdfc7484a28d4362347bbd098095db190c14a4ce9dbaafe74803eccc", + "f4b7e0fb1e6f6fbac539c55e22aff2900947de652d2d6254a9cd8709f505f83a"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce( + "f494a5bf06a2f0550aafb6aabeb495bd6ea3ef92eaa736819b5b0ad6bfbf1aab", + "35df3d3a14f88cbba0cfd092a1e5a0e4e725ba52a8d45719614555542d701f18" + ), new BigInteger("aa0dcf552fb5be7a5c52b783c1b61c1aca7113872e172a5818994715c8a5497c", 16), TypeOfUser.v2, false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{new VerifyParams(idToken, TORUS_TEST_EMAIL)}); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, hashedIdToken).get(); - assertEquals("0xE1155dB406dAD89DdeE9FB9EfC29C8EedC2A0C8B", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0xE1155dB406dAD89DdeE9FB9EfC29C8EedC2A0C8B", torusKey.getFinalKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0xE1155dB406dAD89DdeE9FB9EfC29C8EedC2A0C8B", + "78658b2671f1bd6a488baf2afb8ce6f8d8b9a1a70842130b3c8756a9d51d9723", + "2e5840f47d645afa4bfe93c3715e65974051080d7a1e474eef8d68752924f4fb", + "ad47959db4cb2e63e641bac285df1b944f54d1a1cecdaeea40042b60d53c35d2"), + new OAuthKeyData("0x5a165d2Ed4976BD104caDE1b2948a93B72FA91D2", + "aba2b085ae6390b3eb26802c3239bb7e3b9ed8ea6e1dcc28aeb67432571f20fc", + "f1a2163cba5620b7b40241a6112e7918e9445b0b9cfbbb9d77b2de6f61ed5c27", + "d9733fc1098151f3e3289673e7c69c4ed46cbbdbc13416560e14741524d2d51a"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce( + "376c0ac5e15686633061cf5833dd040365f91377686d7ab5338c5202bd963a2f", + "794d7edb6a5ec0307dd40789274b377f37f293b0410a6cbd303db309536099b7" + ), new BigInteger("d3d455dcab49dc700319244e9e187f443596f2acbce238cff1c215d8809fa1f9", 16), TypeOfUser.v2, false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } } diff --git a/src/test/java/org/torusresearch/torusutilstest/SapphireDevnetTest.java b/src/test/java/org/torusresearch/torusutilstest/SapphireDevnetTest.java new file mode 100644 index 0000000..be09475 --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/SapphireDevnetTest.java @@ -0,0 +1,399 @@ +package org.torusresearch.torusutilstest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.Header; + +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.torusresearch.fetchnodedetails.FetchNodeDetails; +import org.torusresearch.fetchnodedetails.types.NodeDetails; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; +import org.torusresearch.torusutils.TorusUtils; +import org.torusresearch.torusutils.helpers.Common; +import org.torusresearch.torusutils.helpers.KeyUtils; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.SessionToken; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; +import org.torusresearch.torusutilstest.utils.JwtUtils; +import org.torusresearch.torusutilstest.utils.PemUtils; +import org.web3j.crypto.Hash; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +public class SapphireDevnetTest { + + static FetchNodeDetails fetchNodeDetails; + + static TorusUtils torusUtils; + static Algorithm algorithmRs; + + static String TORUS_TEST_VERIFIER = "torus-test-health"; + static String TORUS_TEST_AGGREGATE_VERIFIER = "torus-test-health-aggregate"; + static String TORUS_TEST_EMAIL = "devnettestuser@tor.us"; + static String TORUS_EXTENDED_VERIFIER_EMAIL = "testextenderverifierid@example.com"; + static String HashEnabledVerifier = "torus-test-verifierid-hash"; + + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { + fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.SAPPHIRE_DEVNET); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.SAPPHIRE_DEVNET, null, 0, true); + torusUtils = new TorusUtils(opts); + ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); + ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); + algorithmRs = Algorithm.ECDSA256(publicKey, privateKey); + } + + @DisplayName("Gets Public Address") + @Test + public void shouldGetPublicAddress() throws Exception { + String verifier = TORUS_TEST_VERIFIER; + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, TORUS_TEST_EMAIL).get(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), verifier, TORUS_TEST_EMAIL, null); + assertTrue(torusPublicKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x137B3607958562D03Eb3C6086392D1eFa01aA6aa", torusPublicKey.getoAuthKeyData().getWalletAddress()); + assertEquals("0x462A8BF111A55C9354425F875F89B22678c0Bc44", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x137B3607958562D03Eb3C6086392D1eFa01aA6aa", + "118a674da0c68f16a1123de9611ba655f4db1e336fe1b2d746028d65d22a3c6b", + "8325432b3a3418d632b4fe93db094d6d83250eea60fe512897c0ad548737f8a5"), + new FinalPubKeyData("0x462A8BF111A55C9354425F875F89B22678c0Bc44", + "36e257717f746cdd52ba85f24f7c9040db8977d3b0354de70ed43689d24fa1b1", + "58ec9768c2fe871b3e2a83cdbcf37ba6a88ad19ec2f6e16a66231732713fd507"), + new Metadata(new PubNonce("5d03a0df9b3db067d3363733df134598d42873bb4730298a53ee100975d703cc", + "279434dcf0ff22f077877a70bcad1732412f853c96f02505547f7ca002b133ed"), + BigInteger.ZERO, TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @Test + public void testShouldBeAbleToImportKeyForANewUser() throws Exception { + String fakeEmail = JwtUtils.getRandomEmail(); + String jwt = JwtUtils.generateIdToken(fakeEmail, algorithmRs); + String key = Common.trimLeadingZeros(Hex.toHexString(KeyUtils.serializePrivateKey(KeyUtils.generateKeyPair().getPrivate()))); + String privateKey = Common.padLeft(key, '0', 64); + VerifierParams verifierParams = new VerifierParams(fakeEmail, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, fakeEmail).get(); + + TorusKey val = torusUtils.importPrivateKey( + nodeDetails.getTorusNodeSSSEndpoints(), + nodeDetails.getTorusIndexes(), + nodeDetails.getTorusNodePub(), + TORUS_TEST_VERIFIER, + verifierParams, + jwt, + privateKey, + null + ); + + assertEquals(val.getFinalKeyData().getPrivKey(), privateKey); + + jwt = JwtUtils.generateIdToken(fakeEmail, algorithmRs); + TorusKey shareRetrieval = torusUtils.retrieveShares( + nodeDetails.getTorusNodeSSSEndpoints(), + TORUS_TEST_VERIFIER, + verifierParams, + jwt, null + ); + assertEquals(shareRetrieval.getFinalKeyData().getPrivKey(), privateKey); + + TorusPublicKey addressRetrieval = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), + TORUS_TEST_VERIFIER, fakeEmail, null); + String publicAddress = KeyUtils.privateToPublic(new BigInteger(privateKey, 16)); + String retrievedAddress = KeyUtils.getPublicKeyFromCoords( + addressRetrieval.getFinalKeyData().getX(), + addressRetrieval.getFinalKeyData().getY(), true + ); + assertEquals(publicAddress, retrievedAddress); + + } + + @DisplayName("should be able to key assign") + @Test + public void shouldKeyAssign() throws Exception { + String email = JwtUtils.getRandomEmail(); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, null); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + } + + @DisplayName("should fetch public address of a legacy v1 user") + @Test + public void testFetchPublicAddressOfLegacyV1User() throws Exception { + fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.TESTNET); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.TESTNET, null, 0, true); + torusUtils = new TorusUtils(opts); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google-lrc", "himanshu@tor.us").get(); + TorusPublicKey publicKeyData = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), "google-lrc", "himanshu@tor.us", null); + assertEquals(TypeOfUser.v1, publicKeyData.getMetadata().getTypeOfUser()); + assertThat(publicKeyData).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xf1e76fcDD28b5AA06De01de508fF21589aB9017E", + "b3f2b4d8b746353fe670e0c39ac9adb58056d4d7b718d06b623612d4ec49268b", + "ac9f79dff78add39cdba380dbbf517c20cf2c1e06b32842a90a84a31f6eb9a9a"), + new FinalPubKeyData("0x930abEDDCa6F9807EaE77A3aCc5c78f20B168Fd1", + "12f6b90d66bda29807cf9ff14b2e537c25080154fc4fafed446306e8356ff425", + "e7c92e164b83e1b53e41e5d87d478bb07d7b19d105143e426e1ef08f7b37f224"), + new Metadata(null, new BigInteger("186a20d9b00315855ff5622a083aca6b2d34ef66ef6e0a4de670f5b2fde37e0d", 16), + TypeOfUser.v1, false, publicKeyData.getMetadata().getServerTimeOffset()), + new NodesData(publicKeyData.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should keep public address same") + @Test + public void shouldKeyPublicAddressSame() throws Exception { + String email = JwtUtils.getRandomEmail(); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); + TorusPublicKey result1 = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, null); + TorusPublicKey result2 = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, null); + assertThat(result1.getFinalKeyData()).isEqualToComparingFieldByFieldRecursively(result2.getFinalKeyData()); + assertThat(result1.getoAuthKeyData()).isEqualToComparingFieldByFieldRecursively(result2.getoAuthKeyData()); + } + + @DisplayName("should be able to key assign to tssVerifierId") + @Test + public void testShouldBeAbleToKeyAssignToTssVerifierId() throws Exception { + String fakeEmail = JwtUtils.getRandomEmail(); + int nonce = 0; + String tssTag = "default"; + String tssVerifierID = fakeEmail + "\u0015" + tssTag + "\u0016" + nonce; + String verifier = TORUS_TEST_VERIFIER; + + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, fakeEmail).get(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress( + nodeDetails.getTorusNodeSSSEndpoints(), + verifier, + fakeEmail, + tssVerifierID + ); + + assertNotNull(torusPublicKey.getFinalKeyData().getWalletAddress()); + assertNotEquals("", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertNotNull(torusPublicKey.getoAuthKeyData().getWalletAddress()); + assertNotEquals("", torusPublicKey.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, torusPublicKey.getMetadata().getTypeOfUser()); + assertFalse(torusPublicKey.getMetadata().isUpgraded()); + } + + @DisplayName("should fetch pubic address of tssVerifierId") + @Test + public void shouldFetchPubicAddressOfTssVerifierId() throws Exception { + String email = TORUS_EXTENDED_VERIFIER_EMAIL; + int nonce = 0; + String tssTag = "default"; + String tssVerifierId = email + "\u0015" + tssTag + "\u0016" + nonce; + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, tssVerifierId); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xBd6Bc8aDC5f2A0526078Fd2016C4335f64eD3a30", + "d45d4ad45ec643f9eccd9090c0a2c753b1c991e361388e769c0dfa90c210348c", + "fdc151b136aa7df94e97cc7d7007e2b45873c4b0656147ec70aad46e178bce1e"), + new FinalPubKeyData("0xBd6Bc8aDC5f2A0526078Fd2016C4335f64eD3a30", + "d45d4ad45ec643f9eccd9090c0a2c753b1c991e361388e769c0dfa90c210348c", + "fdc151b136aa7df94e97cc7d7007e2b45873c4b0656147ec70aad46e178bce1e"), + new Metadata(torusPublicKey.getMetadata().getPubNonce(), + new BigInteger("0"), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should allow test tssVerifier to fetch shares") + @Test + public void testShouldAllowTestTssVerifierToFetchShares() throws Exception { + String email = JwtUtils.getRandomEmail(); + int nonce = 0; + String tssTag = "default"; + String tssVerifierID = email + "\u0015" + tssTag + "\u0016" + nonce; + String verifier = TORUS_TEST_VERIFIER; + String token = JwtUtils.generateIdToken(email, algorithmRs); + + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, email).get(); + VerifierParams verifierParams = new VerifierParams(email, tssVerifierID, null, null); + TorusKey torusKey = torusUtils.retrieveShares( + nodeDetails.getTorusNodeSSSEndpoints(), + verifier, + verifierParams, + token, null + ); + assertNotNull(torusKey.getFinalKeyData().getPrivKey()); + assertNotNull(torusKey.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, torusKey.getMetadata().getTypeOfUser()); + assertEquals(BigInteger.ZERO, torusKey.getMetadata().getNonce()); + assertTrue(torusKey.getMetadata().isUpgraded()); + } + + + @DisplayName("should fetch public address when verifierID is hash enabled") + @Test + public void shouldFetchPubAddressWhenVerfierIdIsHashEnabled() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(torusNodeEndpoints, HashEnabledVerifier, TORUS_TEST_EMAIL, null); + assertEquals("0x8a7e297e20804786767B1918a5CFa11683e5a3BB", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xaEafa3Fc7349E897F8fCe981f55bbD249f12aC8C", + "72d9172d7edc623266d6c625db91505e6b64a5524e6d7c7c0184b1bbdea1e986", + "8c26d557a0a9cb22dc2a30d36bf67de93a0eb6d4ef503a849c7de2d14dcbdaaa"), + new FinalPubKeyData("0x8a7e297e20804786767B1918a5CFa11683e5a3BB", + "7927d5281aea24fd93f41696f79c91370ec0097ff65e83e95691fffbde6d733a", + "f22735f0e72ff225274cf499d50b240b7571063e0584471b2b4dab337ad5d8da"), + new Metadata(new PubNonce("5712d789f7ecf3435dd9bf1136c2daaa634f0222d64e289d2abe30a729a6a22b", + "2d2b4586fd5fd9d15c22f66b61bc475742754a8b96d1edb7b2590e4c4f97b3f0"), + new BigInteger("0"), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("Should fetch user type and public address when verifierID is hash enabled") + @Test + public void testFetchUserTypeAndPublicAddressWhenVerfierIdIsHashEnabled() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(torusNodeEndpoints, HashEnabledVerifier, TORUS_TEST_EMAIL, null); + assertTrue(torusPublicKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x8a7e297e20804786767B1918a5CFa11683e5a3BB", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xaEafa3Fc7349E897F8fCe981f55bbD249f12aC8C", + "72d9172d7edc623266d6c625db91505e6b64a5524e6d7c7c0184b1bbdea1e986", + "8c26d557a0a9cb22dc2a30d36bf67de93a0eb6d4ef503a849c7de2d14dcbdaaa"), + new FinalPubKeyData("0x8a7e297e20804786767B1918a5CFa11683e5a3BB", + "7927d5281aea24fd93f41696f79c91370ec0097ff65e83e95691fffbde6d733a", + "f22735f0e72ff225274cf499d50b240b7571063e0584471b2b4dab337ad5d8da"), + new Metadata(new PubNonce("5712d789f7ecf3435dd9bf1136c2daaa634f0222d64e289d2abe30a729a6a22b", + "2d2b4586fd5fd9d15c22f66b61bc475742754a8b96d1edb7b2590e4c4f97b3f0"), + new BigInteger("0"), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to login when verifierID is hash enabled") + @Test + public void testShouldBeAbleToLoginWhenVerifierIdIsHashEnabled() throws Exception { + String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusKey torusKey = torusUtils.retrieveShares(torusNodeEndpoints, HashEnabledVerifier, + verifierParams, idToken, null); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x8a7e297e20804786767B1918a5CFa11683e5a3BB", + "7927d5281aea24fd93f41696f79c91370ec0097ff65e83e95691fffbde6d733a", + "f22735f0e72ff225274cf499d50b240b7571063e0584471b2b4dab337ad5d8da", + "f161f63a84f1c935525ec0bda74bc5a15de6a9a7be28fad237ef6162df335fe6"), + new OAuthKeyData("0xaEafa3Fc7349E897F8fCe981f55bbD249f12aC8C", + "72d9172d7edc623266d6c625db91505e6b64a5524e6d7c7c0184b1bbdea1e986", + "8c26d557a0a9cb22dc2a30d36bf67de93a0eb6d4ef503a849c7de2d14dcbdaaa", + "62e110d9d698979c1966d14b2759006cf13be7dfc86a63ff30812e2032163f2f"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce("5712d789f7ecf3435dd9bf1136c2daaa634f0222d64e289d2abe30a729a6a22b", + "2d2b4586fd5fd9d15c22f66b61bc475742754a8b96d1edb7b2590e4c4f97b3f0"), + new BigInteger("8e80e560ae59319938f7ef727ff2c5346caac1c7f5be96d3076e3342ad1d20b7", 16), TypeOfUser.v2, + false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to login") + @Test + public void shouldLogin() throws Exception { + String token = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, token, null); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x462A8BF111A55C9354425F875F89B22678c0Bc44", + "36e257717f746cdd52ba85f24f7c9040db8977d3b0354de70ed43689d24fa1b1", + "58ec9768c2fe871b3e2a83cdbcf37ba6a88ad19ec2f6e16a66231732713fd507", + "230dad9f42039569e891e6b066ff5258b14e9764ef5176d74aeb594d1a744203"), + new OAuthKeyData("0x137B3607958562D03Eb3C6086392D1eFa01aA6aa", + "118a674da0c68f16a1123de9611ba655f4db1e336fe1b2d746028d65d22a3c6b", + "8325432b3a3418d632b4fe93db094d6d83250eea60fe512897c0ad548737f8a5", + "6b3c872a269aa8994a5acc8cdd70ea3d8d182d42f8af421c0c39ea124e9b66fa"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce("5d03a0df9b3db067d3363733df134598d42873bb4730298a53ee100975d703cc", + "279434dcf0ff22f077877a70bcad1732412f853c96f02505547f7ca002b133ed"), + new BigInteger("b7d126751b68ecd09e371a23898e6819dee54708a5ead4f6fe83cdc79c0f1c4a", 16), TypeOfUser.v2, + false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to aggregate login") + @Test + public void shouldAggregateLogin() throws Exception { + String email = JwtUtils.getRandomEmail(); + String idToken = JwtUtils.generateIdToken(email, algorithmRs); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(email, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(email, idToken)}); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, email).get(); + String[] endpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusKey result = torusUtils.retrieveShares(endpoints, TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertNotNull(result.getFinalKeyData().getWalletAddress()); + assertNotNull(result.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, result.getMetadata().getTypeOfUser()); + assertNotNull(result.getMetadata().getNonce()); + } + + @DisplayName("should be able to update the `sessionTime` of the token signature data") + @Test + public void shouldUpdateSessionTimeOfTokenSignatureData() throws Exception { + String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeEndpoints(); + + int customSessionTime = 3600; + torusUtils.setSessionTime(customSessionTime); + + TorusKey torusKey = torusUtils.retrieveShares(torusNodeEndpoints, TORUS_TEST_VERIFIER, + verifierParams, idToken, null); + + + for (SessionToken sessionToken : torusKey.getSessionData().getSessionTokenData()) { + String decodedToken = new String(Base64.getDecoder().decode(sessionToken.getToken()), StandardCharsets.UTF_8); + Header jwt = JwtUtils.parseTokenHeader(decodedToken); + Date expiry = jwt.getHeaderClaim("exp").asDate(); + Date now = new Date(); + long diffInMillis = Math.abs(expiry.getTime() - now.getTime()); + long diff = TimeUnit.SECONDS.convert(diffInMillis, TimeUnit.MILLISECONDS); + assert (diff > (customSessionTime - 30)); + assert (customSessionTime <= diff); + } + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/SapphireMainnetTest.java b/src/test/java/org/torusresearch/torusutilstest/SapphireMainnetTest.java new file mode 100644 index 0000000..93099ab --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/SapphireMainnetTest.java @@ -0,0 +1,340 @@ +package org.torusresearch.torusutilstest; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; +import static org.junit.jupiter.api.Assertions.assertNotEquals; +import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; + +import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.interfaces.Header; + +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.torusresearch.fetchnodedetails.FetchNodeDetails; +import org.torusresearch.fetchnodedetails.types.NodeDetails; +import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; +import org.torusresearch.torusutils.TorusUtils; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.SessionToken; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; +import org.torusresearch.torusutilstest.utils.JwtUtils; +import org.torusresearch.torusutilstest.utils.PemUtils; +import org.web3j.crypto.Hash; + +import java.io.IOException; +import java.math.BigInteger; +import java.nio.charset.StandardCharsets; +import java.security.KeyFactory; +import java.security.NoSuchAlgorithmException; +import java.security.interfaces.ECPrivateKey; +import java.security.interfaces.ECPublicKey; +import java.security.spec.ECPublicKeySpec; +import java.security.spec.InvalidKeySpecException; +import java.util.Base64; +import java.util.Date; +import java.util.concurrent.TimeUnit; + +public class SapphireMainnetTest { + + static FetchNodeDetails fetchNodeDetails; + + static TorusUtils torusUtils; + static Algorithm algorithmRs; + + static String TORUS_TEST_VERIFIER = "torus-test-health"; + static String TORUS_TEST_AGGREGATE_VERIFIER = "torus-aggregate-sapphire-mainnet"; + + static String TORUS_EXTENDED_VERIFIER_EMAIL = "testextenderverifierid@example.com"; + static String HashEnabledVerifier = "torus-test-verifierid-hash"; + + static String TORUS_TEST_EMAIL = "hello@tor.us"; + + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { + fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.SAPPHIRE_MAINNET); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.SAPPHIRE_MAINNET, null, 0, true); + torusUtils = new TorusUtils(opts); + ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); + ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); + algorithmRs = Algorithm.ECDSA256(publicKey, privateKey); + } + + @DisplayName("Gets Public Address") + @Test + public void shouldGetPublicAddress() throws Exception { + String verifier = "tkey-google-sapphire-mainnet"; + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, TORUS_TEST_EMAIL).get(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), verifier, TORUS_TEST_EMAIL, null); + assertEquals("0x327b2742768B436d09153429E762FADB54413Ded", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xb1a49C6E50a1fC961259a8c388EAf5953FA5152b", + "a9f5a463aefb16e90f4cbb9de4a5b6b7f6c6a3831cefa0f20cccb9e7c7b01c20", + "3376c6734da57ab3a67c7792eeea20707d16992dd2c827a59499f4c056b00d08"), + new FinalPubKeyData("0x327b2742768B436d09153429E762FADB54413Ded", + "1567e030ca76e520c180c50bc6baed07554ebc35c3132495451173e9310d8be5", + "123c0560757ffe6498bf2344165d0f295ea74eb8884683675e5f17ae7bb41cdb"), + new Metadata(new PubNonce("56e803db7710adbfe0ecca35bc6a3ad27e966df142e157e76e492773c88e8433", + "f4168594c1126ca731756dd480f992ee73b0834ba4b787dd892a9211165f50a3"), + new BigInteger("0", 16), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to key assign") + @Test + public void shouldKeyAssign() throws Exception { + String verifier = "tkey-google-sapphire-mainnet"; + String verifierId = JwtUtils.getRandomEmail(); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, verifierId).get(); + TorusPublicKey result = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), verifier, verifierId, null); + assertNotEquals("", result.getFinalKeyData().getWalletAddress()); + assertNotNull(result.getFinalKeyData().getWalletAddress()); + assertNotEquals("", result.getFinalKeyData().getWalletAddress()); + assertNotNull(result.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, result.getMetadata().getTypeOfUser()); + assertFalse(result.getMetadata().isUpgraded()); + } + + @DisplayName("should assign key to tss verifier id") + @Test + public void shouldAssignKeyToTssVerifierId() throws Exception { + String email = JwtUtils.getRandomEmail(); + int nonce = 0; + String tssTag = "default"; + String tssVerifierId = email + "\u0015" + tssTag + "\u0016" + nonce; + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); + TorusPublicKey result = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, tssVerifierId); + assertNotNull(result.getFinalKeyData().getWalletAddress()); + assertNotNull(result.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, result.getMetadata().getTypeOfUser()); + assertFalse(result.getMetadata().isUpgraded()); + } + + @DisplayName("should fetch pubic address of tssVerifierId") + @Test + public void shouldFetchPubicAddressOfTssVerifierId() throws Exception { + String email = TORUS_EXTENDED_VERIFIER_EMAIL; + int nonce = 0; + String tssTag = "default"; + String tssVerifierId = email + "\u0015" + tssTag + "\u0016" + nonce; + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, email).get(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(nodeDetails.getTorusNodeSSSEndpoints(), TORUS_TEST_VERIFIER, email, tssVerifierId); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x98EC5b049c5C0Dc818C69e95CF43534AEB80261A", + "a772c71ca6c650506f26a180456a6bdf462996781a10f1740f4e65314f360f29", + "776c2178ff4620c67197b2f26b1222503919ff26a7cbd0fdbc91a2c9764e56cb"), + new FinalPubKeyData("0x98EC5b049c5C0Dc818C69e95CF43534AEB80261A", + "a772c71ca6c650506f26a180456a6bdf462996781a10f1740f4e65314f360f29", + "776c2178ff4620c67197b2f26b1222503919ff26a7cbd0fdbc91a2c9764e56cb"), + new Metadata(torusPublicKey.getMetadata().getPubNonce(), + new BigInteger("0"), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should allow test tssVerifier to fetch shares") + @Test + public void testShouldAllowTestTssVerifierToFetchShares() throws Exception { + String email = JwtUtils.getRandomEmail(); + int nonce = 0; + String tssTag = "default"; + String tssVerifierID = email + "\u0015" + tssTag + "\u0016" + nonce; + String verifier = TORUS_TEST_VERIFIER; + String token = JwtUtils.generateIdToken(email, algorithmRs); + + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(verifier, email).get(); + VerifierParams verifierParams = new VerifierParams(email, tssVerifierID, null, null); + TorusKey torusKey = torusUtils.retrieveShares( + nodeDetails.getTorusNodeSSSEndpoints(), + verifier, + verifierParams, + token, null + ); + assertNotNull(torusKey.getFinalKeyData().getPrivKey()); + assertNotNull(torusKey.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, torusKey.getMetadata().getTypeOfUser()); + assertEquals(BigInteger.ZERO, torusKey.getMetadata().getNonce()); + assertTrue(torusKey.getMetadata().isUpgraded()); + } + + @DisplayName("should fetch public address when verifierID hash enabled") + @Test + public void shouldFetchPubAddressWhenVerfierIdHasEnabled() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(torusNodeEndpoints, HashEnabledVerifier, TORUS_TEST_EMAIL, null); + assertTrue(torusPublicKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xeBe48BE7693a36Ff562D18c4494AC4496A45EaaC", + "147d0a97d498ac17172dd92546617e06f2c32c405d414dfc06632b8fbcba93d8", + "cc6e57662c3866c4316c05b0fe902db9aaf5541fbf5fda854c3b4634eceeb43c"), + new FinalPubKeyData("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", + "b943bfdc29c515195270d3a219da6a57bcaf6e58e57d03e2accb8c716e6949c8", + "a0fe9ac87310d302a821f89a747d80c9b7dc5cbd0956571f84b09e58d11eee90"), + new Metadata(new PubNonce("498ed301af25a3b7136f478fa58677c79a6d6fe965bc13002a6f459b896313bd", + "d6feb9a1e0d6d0627fbb1ce75682bc09ab4cf0e2da4f0f7fcac0ba9d07596c8f"), + new BigInteger("0", 16), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should fetch user type and public address when verifierID hash enabled") + @Test + public void shouldFetchUserTypeAndPubAddressWhenVerfierIdHasEnabled() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(torusNodeEndpoints, HashEnabledVerifier, TORUS_TEST_EMAIL, null); + assertTrue(torusPublicKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xeBe48BE7693a36Ff562D18c4494AC4496A45EaaC", + "147d0a97d498ac17172dd92546617e06f2c32c405d414dfc06632b8fbcba93d8", + "cc6e57662c3866c4316c05b0fe902db9aaf5541fbf5fda854c3b4634eceeb43c"), + new FinalPubKeyData("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", + "b943bfdc29c515195270d3a219da6a57bcaf6e58e57d03e2accb8c716e6949c8", + "a0fe9ac87310d302a821f89a747d80c9b7dc5cbd0956571f84b09e58d11eee90"), + new Metadata(new PubNonce("498ed301af25a3b7136f478fa58677c79a6d6fe965bc13002a6f459b896313bd", + "d6feb9a1e0d6d0627fbb1ce75682bc09ab4cf0e2da4f0f7fcac0ba9d07596c8f"), + new BigInteger("0", 16), TypeOfUser.v2, false, + torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to login when verifierID hash enabled") + @Test + public void testShouldBeAbleToLoginWhenVerifierIdHashEnabled() throws Exception { + String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeSSSEndpoints(), HashEnabledVerifier, + verifierParams, idToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("13941ecd812b08d8a33a20bc975f0cd1c3f82de25b20c0c863ba5f21580b65f6")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", + "b943bfdc29c515195270d3a219da6a57bcaf6e58e57d03e2accb8c716e6949c8", + "a0fe9ac87310d302a821f89a747d80c9b7dc5cbd0956571f84b09e58d11eee90", + "13941ecd812b08d8a33a20bc975f0cd1c3f82de25b20c0c863ba5f21580b65f6"), + new OAuthKeyData("0xeBe48BE7693a36Ff562D18c4494AC4496A45EaaC", + "147d0a97d498ac17172dd92546617e06f2c32c405d414dfc06632b8fbcba93d8", + "cc6e57662c3866c4316c05b0fe902db9aaf5541fbf5fda854c3b4634eceeb43c", + "d768b327cbde681e5850a7d14f1c724bba2b8f8ab7fe2b1c4f1ee6979fc25478"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce("498ed301af25a3b7136f478fa58677c79a6d6fe965bc13002a6f459b896313bd", + "d6feb9a1e0d6d0627fbb1ce75682bc09ab4cf0e2da4f0f7fcac0ba9d07596c8f"), + new BigInteger("3c2b6ba5b54ca0ba4ae978eb48429a84c47b7b3e526b35e7d46dd716887f52bf", 16), TypeOfUser.v2, + false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to login") + @Test + public void shouldBeAbleToLogin() throws Exception { + String token = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, token, null); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("dfb39b84e0c64b8c44605151bf8670ae6eda232056265434729b6a8a50fa3419")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x70520A7F04868ACad901683699Fa32765C9F6871", + "adff099b5d3b1e238b43fba1643cfa486e8d9e8de22c1e6731d06a5303f9025b", + "21060328e7889afd303acb63201b6493e3061057d1d81279931ab4a6cabf94d4", + "dfb39b84e0c64b8c44605151bf8670ae6eda232056265434729b6a8a50fa3419"), + new OAuthKeyData("0x925c97404F1aBdf4A8085B93edC7B9F0CEB3C673", + "5cd8625fc01c7f7863a58c914a8c43b2833b3d0d5059350bab4acf6f4766a33d", + "198a4989615c5c2c7fa4d49c076ea7765743d09816bb998acb9ff54f5db4a391", + "90a219ac78273e82e36eaa57c15f9070195e436644319d6b9aea422bb4d31906"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(new PubNonce("ab4d287c263ab1bb83c37646d0279764e50fe4b0c34de4da113657866ddcf318", + "ad35db2679dfad4b62d77cf753d7b98f73c902e5d101cc2c3c1209ece6d94382"), + new BigInteger("4f1181d8689f0d0960f1a6f9fe26e03e557bdfba11f4b6c8d7b1285e9c271b13", 16), + TypeOfUser.v2, false, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to aggregate login") + @Test + public void shouldAggregateLogin() throws Exception { + String email = JwtUtils.getRandomEmail(); + String idToken = JwtUtils.generateIdToken(email, algorithmRs); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + + + VerifierParams verifierParams = new VerifierParams(email, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(email, idToken)}); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, email).get(); + String[] endpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusKey result = torusUtils.retrieveShares(endpoints, TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertNotNull(result.getFinalKeyData().getWalletAddress()); + assertNotNull(result.getoAuthKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, result.getMetadata().getTypeOfUser()); + assertNotNull(result.getMetadata().getNonce()); + } + + @DisplayName("Should fetch user type and public address when verifierID hash enabled") + @Test + public void testFetchUserTypeAndPublicAddressWhenVerfierIdHasEnabled() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(HashEnabledVerifier, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeSSSEndpoints(); + TorusPublicKey torusPublicKey = torusUtils.getPublicAddress(torusNodeEndpoints, HashEnabledVerifier, TORUS_TEST_EMAIL, null); + assertEquals("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", torusPublicKey.getFinalKeyData().getWalletAddress()); + assertThat(torusPublicKey).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xeBe48BE7693a36Ff562D18c4494AC4496A45EaaC", + "147d0a97d498ac17172dd92546617e06f2c32c405d414dfc06632b8fbcba93d8", + "cc6e57662c3866c4316c05b0fe902db9aaf5541fbf5fda854c3b4634eceeb43c"), + new FinalPubKeyData("0xCb76F4C8cbAe524997787B57efeeD99f6D3BD5AB", + "b943bfdc29c515195270d3a219da6a57bcaf6e58e57d03e2accb8c716e6949c8", + "a0fe9ac87310d302a821f89a747d80c9b7dc5cbd0956571f84b09e58d11eee90"), + new Metadata(new PubNonce("498ed301af25a3b7136f478fa58677c79a6d6fe965bc13002a6f459b896313bd", + "d6feb9a1e0d6d0627fbb1ce75682bc09ab4cf0e2da4f0f7fcac0ba9d07596c8f"), + new BigInteger("0", 16), TypeOfUser.v2, false, torusPublicKey.getMetadata().getServerTimeOffset()), + new NodesData(torusPublicKey.getNodesData().getNodeIndexes()) + )); + } + + @DisplayName("should be able to update the `sessionTime` of the token signature data") + @Test + public void shouldUpdateSessionTimeOfTokenSignatureData() throws Exception { + String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); + String[] torusNodeEndpoints = nodeDetails.getTorusNodeEndpoints(); + + int customSessionTime = 3600; + torusUtils.setSessionTime(customSessionTime); + TorusKey torusKey = torusUtils.retrieveShares(torusNodeEndpoints, TORUS_TEST_VERIFIER, + verifierParams, idToken, null); + + for (SessionToken sessionToken : torusKey.getSessionData().getSessionTokenData()) { + String decodedToken = new String(Base64.getDecoder().decode(sessionToken.getToken()), StandardCharsets.UTF_8); + Header jwt = JwtUtils.parseTokenHeader(decodedToken); + Date expiry = jwt.getHeaderClaim("exp").asDate(); + Date now = new Date(); + long diffInMillis = Math.abs(expiry.getTime() - now.getTime()); + long diff = TimeUnit.SECONDS.convert(diffInMillis, TimeUnit.MILLISECONDS); + assert (diff > (customSessionTime - 30)); + assert (customSessionTime <= diff); + } + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/TorusUtilsTest.java b/src/test/java/org/torusresearch/torusutilstest/TorusUtilsTest.java index e41b7b3..2c047f4 100644 --- a/src/test/java/org/torusresearch/torusutilstest/TorusUtilsTest.java +++ b/src/test/java/org/torusresearch/torusutilstest/TorusUtilsTest.java @@ -1,27 +1,38 @@ package org.torusresearch.torusutilstest; +import static org.assertj.core.api.Assertions.assertThat; import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertTrue; import com.auth0.jwt.algorithms.Algorithm; -import org.junit.jupiter.api.BeforeAll; +import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; import org.torusresearch.fetchnodedetails.FetchNodeDetails; import org.torusresearch.fetchnodedetails.types.NodeDetails; import org.torusresearch.fetchnodedetails.types.Web3AuthNetwork; import org.torusresearch.torusutils.TorusUtils; -import org.torusresearch.torusutils.types.RetrieveSharesResponse; -import org.torusresearch.torusutils.types.TorusCtorOptions; -import org.torusresearch.torusutils.types.TorusException; -import org.torusresearch.torusutils.types.TorusPublicKey; -import org.torusresearch.torusutils.types.TypeOfUser; -import org.torusresearch.torusutils.types.VerifierArgs; +import org.torusresearch.torusutils.helpers.TorusUtilError; +import org.torusresearch.torusutils.types.FinalKeyData; +import org.torusresearch.torusutils.types.FinalPubKeyData; +import org.torusresearch.torusutils.types.Metadata; +import org.torusresearch.torusutils.types.NodesData; +import org.torusresearch.torusutils.types.OAuthKeyData; +import org.torusresearch.torusutils.types.OAuthPubKeyData; +import org.torusresearch.torusutils.types.SessionData; +import org.torusresearch.torusutils.types.VerifierParams; +import org.torusresearch.torusutils.types.VerifyParams; +import org.torusresearch.torusutils.types.common.PubNonce; +import org.torusresearch.torusutils.types.common.TorusKey; +import org.torusresearch.torusutils.types.common.TorusOptions; +import org.torusresearch.torusutils.types.common.TorusPublicKey; +import org.torusresearch.torusutils.types.common.TypeOfUser; import org.torusresearch.torusutilstest.utils.JwtUtils; import org.torusresearch.torusutilstest.utils.PemUtils; -import org.torusresearch.torusutilstest.utils.VerifyParams; import org.web3j.crypto.Hash; import java.io.IOException; @@ -32,8 +43,6 @@ import java.security.interfaces.ECPublicKey; import java.security.spec.ECPublicKeySpec; import java.security.spec.InvalidKeySpecException; -import java.util.HashMap; -import java.util.concurrent.ExecutionException; public class TorusUtilsTest { @@ -45,14 +54,12 @@ public class TorusUtilsTest { static String TORUS_TEST_VERIFIER = "torus-test-health"; static String TORUS_TEST_AGGREGATE_VERIFIER = "torus-test-health-aggregate"; - static String TORUS_TEST_EMAIL = "hello@tor.us"; + static String TORUS_TEST_EMAIL = "archit1@tor.us"; - @BeforeAll - static void setup() throws ExecutionException, InterruptedException, IOException, NoSuchAlgorithmException, InvalidKeySpecException { - System.out.println("Setup Starting"); + @BeforeEach + void setup() throws IOException, NoSuchAlgorithmException, InvalidKeySpecException, TorusUtilError { fetchNodeDetails = new FetchNodeDetails(Web3AuthNetwork.TESTNET); - TorusCtorOptions opts = new TorusCtorOptions("Custom", "YOUR_CLIENT_ID"); - opts.setNetwork("testnet"); + TorusOptions opts = new TorusOptions("YOUR_CLIENT_ID", Web3AuthNetwork.TESTNET, null, 0, false); torusUtils = new TorusUtils(opts); ECPrivateKey privateKey = (ECPrivateKey) PemUtils.readPrivateKeyFromFile("src/test/java/org/torusresearch/torusutilstest/keys/key.pem", "EC"); ECPublicKey publicKey = (ECPublicKey) KeyFactory.getInstance("EC").generatePublic(new ECPublicKeySpec(privateKey.getParams().getGenerator(), privateKey.getParams())); @@ -61,72 +68,141 @@ static void setup() throws ExecutionException, InterruptedException, IOException @DisplayName("Gets Public Address") @Test - public void shouldGetPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("google-lrc", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0x872eEfa7495599A6983d396fE8dcf542457CF33f", publicAddress.getAddress()); + public void shouldGetPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google-lrc", TORUS_TEST_EMAIL).get(); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "google-lrc", TORUS_TEST_EMAIL, null); + assertThat(publicAddress).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x9bcBAde70546c0796c00323CD1b97fa0a425A506", + "894f633b3734ddbf08867816bc55da60803c1e7c2a38b148b7fb2a84160a1bb5", + "1cf2ea7ac63ee1a34da2330413692ba8538bf7cd6512327343d918e0102a1438"), + new FinalPubKeyData("0x9bcBAde70546c0796c00323CD1b97fa0a425A506", + "894f633b3734ddbf08867816bc55da60803c1e7c2a38b148b7fb2a84160a1bb5", + "1cf2ea7ac63ee1a34da2330413692ba8538bf7cd6512327343d918e0102a1438"), + new Metadata(publicAddress.getMetadata().getPubNonce(), BigInteger.ZERO, TypeOfUser.v1, false, publicAddress.getMetadata().getServerTimeOffset()), + new NodesData(publicAddress.getNodesData().getNodeIndexes()) + )); + assertTrue(publicAddress.getMetadata().getServerTimeOffset() < 20); } - @DisplayName("Fetch User Type and Public Address") @Test - public void shouldFetchUserTypeAndPublicAddress() throws ExecutionException, InterruptedException { - VerifierArgs args = new VerifierArgs("google-lrc", TORUS_TEST_EMAIL); - NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(args.getVerifier(), args.getVerifierId()).get(); - TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), args).get(); - assertEquals("0x872eEfa7495599A6983d396fE8dcf542457CF33f", key.getAddress()); - assertEquals(TypeOfUser.v1, key.getTypeOfUser()); + public void shouldFetchUserTypeAndPublicAddress() throws Exception { + NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google-lrc", TORUS_TEST_EMAIL).get(); + TorusPublicKey key = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), "google-lrc", TORUS_TEST_EMAIL, null); + assertThat(key).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x9bcBAde70546c0796c00323CD1b97fa0a425A506", + "894f633b3734ddbf08867816bc55da60803c1e7c2a38b148b7fb2a84160a1bb5", + "1cf2ea7ac63ee1a34da2330413692ba8538bf7cd6512327343d918e0102a1438"), + new FinalPubKeyData("0xf5804f608C233b9cdA5952E46EB86C9037fd6842", + "ed737569a557b50722a8b5c0e4e5ca30cef1ede2f5674a0616b78246bb93dfd0", + "d9e8e6c54c12c4da38c2f0d1047fcf6089036127738f4ef72a83431339586ca9"), + new Metadata(new PubNonce("f3f7caefd6540d923c9993113f34226371bd6714a5be6882dedc95a6a929a8", + "f28620603601ce54fa0d70fd691fb72ff52f5bf164bf1a91617922eaad8cc7a5"), + BigInteger.ZERO, TypeOfUser.v2, false, key.getMetadata().getServerTimeOffset()), + new NodesData(key.getNodesData().getNodeIndexes()) + )); + assertEquals("0xf5804f608C233b9cdA5952E46EB86C9037fd6842", key.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key.getMetadata().getTypeOfUser()); String v2Verifier = "tkey-google-lrc"; // 1/1 user String v2TestEmail = "somev2user@gmail.com"; - TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2TestEmail)).get(); - assertEquals("0xE91200d82029603d73d6E307DbCbd9A7D0129d8D", key2.getAddress()); - assertEquals(TypeOfUser.v2, key2.getTypeOfUser()); + TorusPublicKey key2 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2TestEmail, null); + assertThat(key2).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0x376597141d8d219553378313d18590F373B09795", + "86cd2db15b7a9937fa8ab7d0bf8e7f4113b64d1f4b2397aad35d6d4749d2fb6c", + "86ef47a3724144331c31a3a322d85b6fc1a5d113b41eaa0052053b6e3c74a3e2"), + new FinalPubKeyData("0xE91200d82029603d73d6E307DbCbd9A7D0129d8D", + "c350e338dde24df986915992fea6e0aef3560c245ca144ee7fe1498784c4ef4e", + "a605e52b65d3635f89654519dfa7e31f7b45f206ef4189866ad0c2240d40f97f"), + new Metadata(new PubNonce("ad121b67fa550da814bbbd54ec7070705d058c941e04c03e07967b07b2f90345", + "bfe2395b177a72ebb836aaf24cedff2f14cd9ed49047990f5cdb99e4981b5753"), + BigInteger.ZERO, TypeOfUser.v2, false, key2.getMetadata().getServerTimeOffset()), + new NodesData(key2.getNodesData().getNodeIndexes()) + )); + assertEquals("0xE91200d82029603d73d6E307DbCbd9A7D0129d8D", key2.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key2.getMetadata().getTypeOfUser()); // 2/n user String v2nTestEmail = "caspertorus@gmail.com"; - TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs(v2Verifier, v2nTestEmail)).get(); - assertEquals("0x1016DA7c47A04C76036637Ea02AcF1d29c64a456", key3.getAddress()); - assertEquals(TypeOfUser.v2, key3.getTypeOfUser()); + TorusPublicKey key3 = torusUtils.getUserTypeAndAddress(nodeDetails.getTorusNodeEndpoints(), v2Verifier, v2nTestEmail, null); + assertThat(key3).isEqualToComparingFieldByFieldRecursively(new TorusPublicKey( + new OAuthPubKeyData("0xd45383fbF04BccFa0450d7d8ee453ca86b7C6544", + "d25cc473fbb448d20b5551f3c9aa121e1924b3d197353347187c47ad13ecd5d8", + "3394000f43160a925e6c3017dde1354ecb2b61739571c6584f58edd6b923b0f5"), + new FinalPubKeyData("0x1016DA7c47A04C76036637Ea02AcF1d29c64a456", + "d3e222f6b23f0436b7c86e9cc4164eb5ea8448e4c0e7539c8b4f7fd00e8ec5c7", + "1c47f5faccec6cf57c36919f6f0941fe3d8d65033cf2cc78f209304386044222"), + new Metadata(new PubNonce("4f86b0e69992d1551f1b16ceb0909453dbe17b9422b030ee6c5471c2e16b65d0", + "640384f3d39debb04c4e9fe5a5ec6a1b494b0ad66d00ac9be6f166f21d116ca4"), + BigInteger.ZERO, TypeOfUser.v2, true, key3.getMetadata().getServerTimeOffset()), + new NodesData(key3.getNodesData().getNodeIndexes()) + )); + assertEquals("0x1016DA7c47A04C76036637Ea02AcF1d29c64a456", key3.getFinalKeyData().getWalletAddress()); + assertEquals(TypeOfUser.v2, key3.getMetadata().getTypeOfUser()); } @DisplayName("Key Assign test") @Test - public void shouldKeyAssign() throws ExecutionException, InterruptedException { + public void shouldKeyAssign() throws Exception { String email = JwtUtils.getRandomEmail(); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails("google-lrc", email).get(); - TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusNodePub(), new VerifierArgs("google-lrc", email)).get(); - System.out.println(email + " -> " + publicAddress.getAddress()); - assertNotNull(publicAddress.getAddress()); - assertNotEquals(publicAddress.getAddress(), ""); + TorusPublicKey publicAddress = torusUtils.getPublicAddress(nodeDetails.getTorusNodeEndpoints(), "google-lrc", email, ""); + assertNotNull(publicAddress.getFinalKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getFinalKeyData().getWalletAddress(), ""); + assertNotNull(publicAddress.getoAuthKeyData().getWalletAddress()); + assertNotEquals(publicAddress.getoAuthKeyData().getWalletAddress(), ""); + assertEquals(publicAddress.getMetadata().getTypeOfUser(), TypeOfUser.v2); + assertFalse(publicAddress.getMetadata().isUpgraded()); } @DisplayName("Login test") @Test - public void shouldLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldLogin() throws Exception { NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_VERIFIER, new HashMap() {{ - put("verifier_id", TORUS_TEST_EMAIL); - }}, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs)).get(); - System.out.println(retrieveSharesResponse.getPrivKey()); - BigInteger requiredPrivateKey = new BigInteger("68ee4f97468ef1ae95d18554458d372e31968190ae38e377be59d8b3c9f7a25", 16); - assert (requiredPrivateKey.equals(retrieveSharesResponse.getPrivKey())); - assertEquals("0xEfd7eDAebD0D99D1B7C8424b54835457dD005Dc4", retrieveSharesResponse.getEthAddress()); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, null, null); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_VERIFIER, + verifierParams, JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs), null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assert ((torusKey.getFinalKeyData().getPrivKey() != null) && torusKey.getFinalKeyData().getPrivKey().equals("9b0fb017db14a0a25ed51f78a258713c8ae88b5e58a43acb70b22f9e2ee138e3")); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0xF8d2d3cFC30949C1cb1499C9aAC8F9300535a8d6", + "6de2e34d488dd6a6b596524075b032a5d5eb945bcc33923ab5b88fd4fd04b5fd", + "d5fb7b51b846e05362461357ec6e8ca075ea62507e2d5d7253b72b0b960927e9", + "9b0fb017db14a0a25ed51f78a258713c8ae88b5e58a43acb70b22f9e2ee138e3"), + new OAuthKeyData("0xF8d2d3cFC30949C1cb1499C9aAC8F9300535a8d6", + "6de2e34d488dd6a6b596524075b032a5d5eb945bcc33923ab5b88fd4fd04b5fd", + "d5fb7b51b846e05362461357ec6e8ca075ea62507e2d5d7253b72b0b960927e9", + "9b0fb017db14a0a25ed51f78a258713c8ae88b5e58a43acb70b22f9e2ee138e3"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } @DisplayName("Aggregate Login test") @Test - public void shouldAggregateLogin() throws ExecutionException, InterruptedException, TorusException { + public void shouldAggregateLogin() throws Exception { String idToken = JwtUtils.generateIdToken(TORUS_TEST_EMAIL, algorithmRs); - String hashedIdToken = Hash.sha3String(idToken).substring(2); + String hashedIdToken = Hash.sha3String(idToken).replace("0x",""); + VerifierParams verifierParams = new VerifierParams(TORUS_TEST_EMAIL, null, new String[]{TORUS_TEST_VERIFIER}, new VerifyParams[]{new VerifyParams(TORUS_TEST_EMAIL, idToken)}); NodeDetails nodeDetails = fetchNodeDetails.getNodeDetails(TORUS_TEST_AGGREGATE_VERIFIER, TORUS_TEST_EMAIL).get(); - RetrieveSharesResponse retrieveSharesResponse = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), nodeDetails.getTorusIndexes(), TORUS_TEST_AGGREGATE_VERIFIER, new HashMap() {{ - put("verify_params", new VerifyParams[]{new VerifyParams(idToken, TORUS_TEST_EMAIL)}); - put("sub_verifier_ids", new String[]{TORUS_TEST_VERIFIER}); - put("verifier_id", TORUS_TEST_EMAIL); - }}, hashedIdToken).get(); - assertEquals("0x5a165d2Ed4976BD104caDE1b2948a93B72FA91D2", retrieveSharesResponse.getEthAddress()); + TorusKey torusKey = torusUtils.retrieveShares(nodeDetails.getTorusNodeEndpoints(), TORUS_TEST_AGGREGATE_VERIFIER, + verifierParams, hashedIdToken, null); + assertTrue(torusKey.getMetadata().getServerTimeOffset() < 20); + assertEquals("0x938a40E155d118BD31E439A9d92D67bd55317965", torusKey.getoAuthKeyData().getWalletAddress()); + assertThat(torusKey).isEqualToComparingFieldByFieldRecursively(new TorusKey( + new FinalKeyData("0x938a40E155d118BD31E439A9d92D67bd55317965", + "1c50e34ef5b7afcf5b0c6501a6ae00ec3a09a321dd885c5073dd122e2a251b95", + "2cc74beb28f2c4a7c4034f80836d51b2781b36fefbeafb4eb1cd055bdf73b1e6", + "3cbfa57d702327ec1af505adc88ad577804a1a7780bc013ed9e714c547fb5cb1"), + new OAuthKeyData("0x938a40E155d118BD31E439A9d92D67bd55317965", + "1c50e34ef5b7afcf5b0c6501a6ae00ec3a09a321dd885c5073dd122e2a251b95", + "2cc74beb28f2c4a7c4034f80836d51b2781b36fefbeafb4eb1cd055bdf73b1e6", + "3cbfa57d702327ec1af505adc88ad577804a1a7780bc013ed9e714c547fb5cb1"), + new SessionData(torusKey.getSessionData().getSessionTokenData(), torusKey.getSessionData().getSessionAuthKey()), + new Metadata(null, BigInteger.ZERO, TypeOfUser.v1, null, torusKey.getMetadata().getServerTimeOffset()), + new NodesData(torusKey.getNodesData().getNodeIndexes()) + )); } } diff --git a/src/test/java/org/torusresearch/torusutilstest/helpers/AES256CBC.java b/src/test/java/org/torusresearch/torusutilstest/helpers/AES256CBC.java deleted file mode 100644 index a17109e..0000000 --- a/src/test/java/org/torusresearch/torusutilstest/helpers/AES256CBC.java +++ /dev/null @@ -1,34 +0,0 @@ -package org.torusresearch.torusutilstest.helpers; - -import org.junit.jupiter.api.Test; - -import java.nio.charset.StandardCharsets; - -import static org.junit.jupiter.api.Assertions.*; - -public class AES256CBC { - - @Test - public void testLeadingZeroes() { - // This combination of private and public keys resuts in an SHA512 hash with a leading zero - String privateKey = "dec95a4ffa406daedd079956f1e43fb91baefdad00990699642474eeb09a5a90"; - String publicKey = "a6262a5650a9666195098c2e15e8eb451a755eb59ea2d1b437d11d9113f4d356bd479f01d29850b77fa6357628d2ed0fa0d8230620472b91f21db1c2c6e7def"; - - // Leading zeroes in IV - String iv = "0023456789ABCDEF0123456789ABCDEF"; - - byte[] payload = "Hello World".getBytes(StandardCharsets.UTF_8); - - try { - org.torusresearch.torusutils.helpers.AES256CBC aes256cbc = new org.torusresearch.torusutils.helpers.AES256CBC(privateKey, publicKey, iv); - String encrypted = aes256cbc.encrypt(payload); - byte[] decrypted = aes256cbc.decrypt(encrypted); - - assertArrayEquals(payload, decrypted); - } catch (Exception e) { - assertFalse(true); - } - } - - -} diff --git a/src/test/java/org/torusresearch/torusutilstest/helpers/TestAES256CBC.java b/src/test/java/org/torusresearch/torusutilstest/helpers/TestAES256CBC.java new file mode 100644 index 0000000..7b89686 --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/helpers/TestAES256CBC.java @@ -0,0 +1,38 @@ +package org.torusresearch.torusutilstest.helpers; + +import static org.junit.jupiter.api.Assertions.assertArrayEquals; +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.bouncycastle.util.encoders.Hex; +import org.junit.jupiter.api.Test; +import org.torusresearch.torusutils.helpers.KeyUtils; +import org.torusresearch.torusutils.helpers.MetadataUtils; +import org.torusresearch.torusutils.helpers.encryption.Encryption; +import org.torusresearch.torusutils.types.common.ecies.Ecies; + +import java.nio.charset.StandardCharsets; +import java.security.KeyPair; + +public class TestAES256CBC { + @Test + public void testECDH() throws Exception { + KeyPair secret = KeyUtils.generateKeyPair(); + KeyPair secret2 = KeyUtils.generateKeyPair(); + + byte[] shared = Encryption.ecdh(KeyUtils.serializePrivateKey(secret.getPrivate()), (KeyUtils.serializePublicKey(secret2.getPublic(), false))); + byte[] shared2 = Encryption.ecdh(KeyUtils.serializePrivateKey(secret2.getPrivate()), (KeyUtils.serializePublicKey(secret.getPublic(), false))); + assertArrayEquals(shared, shared2); +} + @Test + public void testEncryption() throws Exception { + KeyPair keypair = KeyUtils.generateKeyPair(); + String payload = "Hello World"; + String hexEncoded = Hex.toHexString(payload.getBytes()); + Ecies encrypted = Encryption.encrypt(KeyUtils.serializePublicKey(keypair.getPublic(), false), hexEncoded); + String decrypted = new String(Encryption.decrypt(Hex.toHexString(KeyUtils.serializePrivateKey(keypair.getPrivate())), encrypted), StandardCharsets.UTF_8); + String decryptedNodeData = MetadataUtils.decryptNodeData(encrypted.omitCipherText(), encrypted.ciphertext, Hex.toHexString(KeyUtils.serializePrivateKey(keypair.getPrivate()))); + + assertEquals(payload, decrypted); + assertEquals(hexEncoded, decryptedNodeData); + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/helpers/TestGenerateEthereumAddress.java b/src/test/java/org/torusresearch/torusutilstest/helpers/TestGenerateEthereumAddress.java new file mode 100644 index 0000000..5de9e7e --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/helpers/TestGenerateEthereumAddress.java @@ -0,0 +1,17 @@ +package org.torusresearch.torusutilstest.helpers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.torusresearch.torusutils.helpers.KeyUtils; + +public class TestGenerateEthereumAddress { + @Test + public void testGenerateAddressFromPublicKey() throws Exception { + String fullAddress = "04238569d5e12caf57d34fb5b2a0679c7775b5f61fd18cd69db9cc600a651749c3ec13a9367380b7a024a67f5e663f3afd40175c3223da63f6024b05d0bd9f292e"; + String[] coords = KeyUtils.getPublicKeyCoords(fullAddress); + String etherAddress = KeyUtils.generateAddressFromPubKey(coords[0], coords[1]); + String finalAddress = "0x048975d4997D7578A3419851639c10318db430b6"; + assertEquals(etherAddress, finalAddress); + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/helpers/TestKCombinations.java b/src/test/java/org/torusresearch/torusutilstest/helpers/TestKCombinations.java new file mode 100644 index 0000000..f7a97df --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/helpers/TestKCombinations.java @@ -0,0 +1,42 @@ +package org.torusresearch.torusutilstest.helpers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.torusresearch.torusutils.helpers.Common; + +import java.util.ArrayList; +import java.util.List; + +public class TestKCombinations { + @Test + public void testKCombinations() { + List set = new ArrayList<>(); + List> allCombis = Common.kCombinations(set, 0); + assertEquals(allCombis.size(), 0); + + set.add(0); + set.add(1); + set.add(2); + set.add(3); + set.add(4); + set.add(5); + + + allCombis = Common.kCombinations(set, 10); + assertEquals(allCombis.size(), 0); + + allCombis = Common.kCombinations(set, 6); + assertEquals(allCombis.size(), 1); + + allCombis = Common.kCombinations(set, 1); + assertEquals(allCombis.size(), 6); + + allCombis = Common.kCombinations(set, 2); + assertEquals(allCombis.size(), 15); + + set.remove(0); + allCombis = Common.kCombinations(set, 3); + assertEquals(allCombis.size(), 10); + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/helpers/TestLagrangeInterpolation.java b/src/test/java/org/torusresearch/torusutilstest/helpers/TestLagrangeInterpolation.java new file mode 100644 index 0000000..7bcdab8 --- /dev/null +++ b/src/test/java/org/torusresearch/torusutilstest/helpers/TestLagrangeInterpolation.java @@ -0,0 +1,40 @@ +package org.torusresearch.torusutilstest.helpers; + +import static org.junit.jupiter.api.Assertions.assertEquals; + +import org.junit.jupiter.api.Test; +import org.torusresearch.torusutils.helpers.Lagrange; +import org.torusresearch.torusutils.types.Point; +import org.torusresearch.torusutils.types.Polynomial; + +import java.math.BigInteger; +import java.util.ArrayList; + +public class TestLagrangeInterpolation { + @Test + public void testLagrangeInterpolation() { + ArrayList points = new ArrayList<>(); + points.add(new Point(BigInteger.ONE, new BigInteger(String.valueOf(2)))); + points.add(new Point(new BigInteger(String.valueOf(2)), new BigInteger(String.valueOf(5)))); + points.add(new Point(new BigInteger(String.valueOf(3)), new BigInteger(String.valueOf(10)))); + Polynomial poly = Lagrange.lagrangeInterpolatePolynomial(points.toArray(new Point[0])); + + ArrayList xValues = new ArrayList<>(); + xValues.add(BigInteger.ONE); + xValues.add(new BigInteger(String.valueOf(2))); + xValues.add(new BigInteger(String.valueOf(3))); + + ArrayList expectedYValues = new ArrayList<>(); + expectedYValues.add(new BigInteger(String.valueOf(2))); + expectedYValues.add(new BigInteger(String.valueOf(5))); + expectedYValues.add(new BigInteger(String.valueOf(10))); + + for (int i = 0; i < xValues.size(); i++) { + BigInteger x = xValues.get(i); + BigInteger expectedY = expectedYValues.get(i); + + BigInteger y = poly.polyEval(x); + assertEquals(expectedY, y); + } + } +} diff --git a/src/test/java/org/torusresearch/torusutilstest/utils/JwtUtils.java b/src/test/java/org/torusresearch/torusutilstest/utils/JwtUtils.java index 1531bf9..4f0b576 100644 --- a/src/test/java/org/torusresearch/torusutilstest/utils/JwtUtils.java +++ b/src/test/java/org/torusresearch/torusutilstest/utils/JwtUtils.java @@ -2,28 +2,44 @@ import com.auth0.jwt.JWT; import com.auth0.jwt.algorithms.Algorithm; +import com.auth0.jwt.impl.JWTParser; +import com.auth0.jwt.interfaces.Header; +import com.auth0.jwt.interfaces.JWTPartsParser; + import net.andreinc.mockneat.MockNeat; +import java.util.Calendar; import java.util.Date; public class JwtUtils { public static String generateIdToken(String email, Algorithm alg) { + + Date today = new Date(); + Calendar calendar = Calendar.getInstance(); + calendar.setTime(today); + calendar.add(Calendar.MINUTE, 2); + Date modifiedDate = calendar.getTime(); + return JWT.create() - .withSubject("email|" + email.split("@")[0]) - .withAudience("torus-key-test") - .withExpiresAt(new Date(System.currentTimeMillis() + 3600 * 1000)) - .withIssuedAt(new Date()) - .withIssuer("torus-key-test") - .withClaim("email", email) - .withClaim("nickname", email.split("@")[0]) + .withClaim("admin", false) .withClaim("name", email) - .withClaim("picture", "") + .withClaim("email", email) + .withSubject("email|" + email.split("@")[0]) // sub .withClaim("email_verified", true) + .withAudience("torus-key-test") // aud + .withExpiresAt(modifiedDate) // eat + .withIssuer("torus-key-test") // iss + .withIssuedAt(today) // iat .sign(alg); } + public static Header parseTokenHeader(String token) { + JWTPartsParser parts = new JWTParser(); + return parts.parseHeader(token); + } + public static String getRandomEmail() { MockNeat mock = MockNeat.threadLocal(); return mock.emails().val(); } -} +} \ No newline at end of file diff --git a/src/test/java/org/torusresearch/torusutilstest/utils/PemUtils.java b/src/test/java/org/torusresearch/torusutilstest/utils/PemUtils.java index e40490e..42c12cc 100644 --- a/src/test/java/org/torusresearch/torusutilstest/utils/PemUtils.java +++ b/src/test/java/org/torusresearch/torusutilstest/utils/PemUtils.java @@ -28,42 +28,25 @@ private static byte[] parsePEMFile(File pemFile) throws IOException { return content; } - private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) { + @SuppressWarnings("unused") + private static PublicKey getPublicKey(byte[] keyBytes, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException { PublicKey publicKey = null; - try { - KeyFactory kf = KeyFactory.getInstance(algorithm); - EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); - publicKey = kf.generatePublic(keySpec); - } catch (NoSuchAlgorithmException e) { - System.out.println("Could not reconstruct the public key, the given algorithm could not be found."); - } catch (InvalidKeySpecException e) { - System.out.println("Could not reconstruct the public key"); - } - + KeyFactory kf = KeyFactory.getInstance(algorithm); + EncodedKeySpec keySpec = new X509EncodedKeySpec(keyBytes); + publicKey = kf.generatePublic(keySpec); return publicKey; } - private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) { + private static PrivateKey getPrivateKey(byte[] keyBytes, String algorithm) throws NoSuchAlgorithmException, InvalidKeySpecException { PrivateKey privateKey = null; - try { - KeyFactory kf = KeyFactory.getInstance(algorithm); - EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); - privateKey = kf.generatePrivate(keySpec); - } catch (NoSuchAlgorithmException e) { - System.out.println("Could not reconstruct the private key, the given algorithm could not be found."); - } catch (InvalidKeySpecException e) { - System.out.println("Could not reconstruct the private key"); - } + KeyFactory kf = KeyFactory.getInstance(algorithm); + EncodedKeySpec keySpec = new PKCS8EncodedKeySpec(keyBytes); + privateKey = kf.generatePrivate(keySpec); return privateKey; } - public static PublicKey readPublicKeyFromFile(String filepath, String algorithm) throws IOException { - byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); - return PemUtils.getPublicKey(bytes, algorithm); - } - - public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException { + public static PrivateKey readPrivateKeyFromFile(String filepath, String algorithm) throws IOException, NoSuchAlgorithmException, InvalidKeySpecException { byte[] bytes = PemUtils.parsePEMFile(new File(filepath)); return PemUtils.getPrivateKey(bytes, algorithm); } diff --git a/src/test/java/org/torusresearch/torusutilstest/utils/VerifyParams.java b/src/test/java/org/torusresearch/torusutilstest/utils/VerifyParams.java deleted file mode 100644 index 46f4868..0000000 --- a/src/test/java/org/torusresearch/torusutilstest/utils/VerifyParams.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.torusresearch.torusutilstest.utils; - -public class VerifyParams { - private final String idtoken; - private final String verifier_id; - - public VerifyParams(String idtoken, String verifier_id) { - this.idtoken = idtoken; - this.verifier_id = verifier_id; - } - - public String getVerifier_id() { - return verifier_id; - } - - public String getIdtoken() { - return idtoken; - } -}