-
Notifications
You must be signed in to change notification settings - Fork 254
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
Showing
219 changed files
with
5,436 additions
and
4,237 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package co.edgesecure.app; | ||
|
||
class Base58 { | ||
public static String encode(byte data[]) { | ||
// Data iterator: | ||
int i = 0; | ||
|
||
// Count leading zeroes: | ||
int zeroes = 0; | ||
while (i < data.length && data[i] == 0) { | ||
zeroes++; | ||
i++; | ||
} | ||
|
||
// ln 256 / ln 58 = 1.3657: | ||
int maxDigits = (data.length + 1 - zeroes) * 137 / 100; | ||
|
||
// The base58 digits, stored in little endian: | ||
int digits[] = new int[maxDigits]; | ||
int digitsUsed = 0; | ||
|
||
// For each input byte, we want to multiply the digits array by 256, | ||
// then add the byte. | ||
while (i < data.length) { | ||
int carry = 0xff & data[i]; | ||
int j = 0; | ||
while (j < digitsUsed || carry != 0) { | ||
carry += digits[j] << 8; | ||
digits[j] = carry % 58; | ||
carry /= 58; | ||
j++; | ||
} | ||
if (j > digitsUsed) digitsUsed = j; | ||
i++; | ||
} | ||
|
||
// Now we have the digits in base58, but we need to stringify them: | ||
char[] out = new char[zeroes + digitsUsed]; | ||
int j = 0; | ||
while (j < zeroes) { | ||
out[j] = BASE58[0]; | ||
j++; | ||
} | ||
while (j < out.length) { | ||
digitsUsed--; | ||
out[j] = BASE58[digits[digitsUsed]]; | ||
j++; | ||
} | ||
return new String(out); | ||
} | ||
|
||
private static final char[] BASE58 = { | ||
'1', '2', '3', '4', '5', '6', '7', '8', '9', 'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H', 'J', 'K', | ||
'L', 'M', 'N', 'P', 'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z', 'a', 'b', 'c', 'd', 'e', | ||
'f', 'g', 'h', 'i', 'j', 'k', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', | ||
'z' | ||
}; | ||
} |
202 changes: 202 additions & 0 deletions
202
android/app/src/main/java/co/edgesecure/app/EdgeCore.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,202 @@ | ||
package co.edgesecure.app; | ||
|
||
import android.content.Context; | ||
import android.util.Base64; | ||
import android.util.Log; | ||
import androidx.annotation.NonNull; | ||
import java.io.ByteArrayOutputStream; | ||
import java.io.File; | ||
import java.io.FileInputStream; | ||
import java.io.IOException; | ||
import java.io.InputStream; | ||
import java.io.OutputStream; | ||
import java.net.URL; | ||
import java.util.ArrayList; | ||
import java.util.HashMap; | ||
import javax.net.ssl.HttpsURLConnection; | ||
import javax.net.ssl.SSLContext; | ||
import org.json.JSONArray; | ||
import org.json.JSONException; | ||
import org.json.JSONObject; | ||
|
||
public class EdgeCore { | ||
Context context; | ||
String logId; | ||
|
||
public EdgeCore(String logId, Context context) { | ||
this.context = context; | ||
this.logId = logId; | ||
} | ||
|
||
/** | ||
* Fetches messages from the auth server, and returns an array of usernames with pending logins. | ||
*/ | ||
public @NonNull Iterable<String> fetchPendingLogins() throws JSONException, IOException { | ||
File basePath = context.getFilesDir(); | ||
File loginsPath = new File(basePath, "logins"); | ||
File[] files = loginsPath.listFiles(); | ||
|
||
// Load the files: | ||
HashMap<String, String> loginIds = new HashMap(); | ||
for (File file : files) { | ||
try { | ||
JSONObject json = new JSONObject(readFile(file)); | ||
if (!json.has("loginAuthBox")) continue; | ||
String loginId = json.getString("loginId"); | ||
String username = json.getString("username"); | ||
loginIds.put(loginId, username); | ||
} catch (Exception e) { | ||
continue; | ||
} | ||
} | ||
|
||
// Bail out if there are no logged-in users: | ||
if (loginIds.size() == 0) return new ArrayList(); | ||
|
||
// Prepare our payload: | ||
JSONObject payloadJson = new JSONObject(); | ||
payloadJson.put("loginIds", new JSONArray(loginIds.keySet())); | ||
|
||
// Do the request: | ||
String uri = "https://auth.airbitz.co/api/v2/messages"; | ||
JSONObject replyJson = new JSONObject(authFetch(uri, payloadJson.toString())); | ||
|
||
// Validate the response: | ||
int statusCode = replyJson.getInt("status_code"); | ||
if (statusCode != 0) throw new JSONException("Incorrect status code"); | ||
JSONArray messages = replyJson.getJSONArray("results"); | ||
|
||
// Find messages with problems: | ||
ArrayList<String> problemUsers = new ArrayList(); | ||
int messagesLength = messages.length(); | ||
for (int i = 0; i < messagesLength; ++i) { | ||
JSONObject message = messages.getJSONObject(i); | ||
String loginId = message.getString("loginId"); | ||
String username = loginIds.get(loginId); | ||
if (username == null) continue; | ||
|
||
JSONArray pendingVouchers = message.optJSONArray("pendingVouchers"); | ||
boolean hasVoucher = pendingVouchers != null && pendingVouchers.length() > 0; | ||
boolean hasReset = message.optBoolean("otpResetPending", false); | ||
|
||
if (hasVoucher || hasReset) { | ||
problemUsers.add(username); | ||
} | ||
} | ||
|
||
return problemUsers; | ||
} | ||
|
||
public void updatePushToken(String token) throws JSONException, IOException { | ||
// Read the clientId: | ||
File basePath = context.getFilesDir(); | ||
File file = new File(basePath, "client.json"); | ||
JSONObject json = new JSONObject(readFile(file)); | ||
String clientId64 = json.getString("clientId"); | ||
String clientId = Base58.encode(Base64.decode(clientId64, Base64.DEFAULT)); | ||
|
||
// Prepare our payload: | ||
JSONObject payloadJson = new JSONObject(); | ||
payloadJson.put("apiKey", EdgeApiKey.apiKey); | ||
payloadJson.put("deviceId", clientId); | ||
payloadJson.put("deviceToken", token); | ||
|
||
// Do the request: | ||
String uri = EdgeApiKey.pushServer.concat("/v2/device/"); | ||
pushFetch(uri, payloadJson.toString()); | ||
} | ||
|
||
/** Does a request / reply with the auth server. */ | ||
private String authFetch(String uri, String body) throws IOException { | ||
HttpsURLConnection connection = null; | ||
try { | ||
// Set up the HTTPS connection: | ||
connection = (HttpsURLConnection) new URL(uri).openConnection(); | ||
SSLContext context = SSLContext.getInstance("TLS"); | ||
context.init(null, null, null); | ||
connection.setSSLSocketFactory(context.getSocketFactory()); | ||
|
||
// Add the auth server headers: | ||
byte[] bodyData = body.getBytes("UTF-8"); | ||
connection.setRequestProperty("Accept", "application/json"); | ||
connection.setRequestProperty("Content-Type", "application/json"); | ||
connection.setRequestProperty("Authorization", "Token " + EdgeApiKey.apiKey); | ||
connection.setRequestProperty("Content-Length", Integer.toString(bodyData.length)); | ||
connection.setRequestMethod("POST"); | ||
connection.setDoInput(true); | ||
connection.setDoOutput(true); | ||
connection.setUseCaches(false); | ||
|
||
// Send the body: | ||
OutputStream wr = connection.getOutputStream(); | ||
wr.write(bodyData); | ||
wr.flush(); | ||
wr.close(); | ||
connection.connect(); | ||
|
||
Log.i(logId, String.format("%s %d", uri, connection.getResponseCode())); | ||
|
||
// Read the reply body: | ||
return readInputStream(connection.getInputStream()); | ||
} catch (Exception e) { | ||
throw new IOException("Could not reach auth server " + uri, e); | ||
} finally { | ||
if (connection != null) connection.disconnect(); | ||
} | ||
} | ||
|
||
/** Does a request / reply with the push server. */ | ||
private void pushFetch(String uri, String body) throws IOException { | ||
HttpsURLConnection connection = null; | ||
try { | ||
// Set up the HTTPS connection: | ||
connection = (HttpsURLConnection) new URL(uri).openConnection(); | ||
SSLContext context = SSLContext.getInstance("TLS"); | ||
context.init(null, null, null); | ||
connection.setSSLSocketFactory(context.getSocketFactory()); | ||
|
||
// Add the auth server headers: | ||
byte[] bodyData = body.getBytes("UTF-8"); | ||
connection.setRequestProperty("Accept", "application/json"); | ||
connection.setRequestProperty("Content-Type", "application/json"); | ||
connection.setRequestProperty("Content-Length", Integer.toString(bodyData.length)); | ||
connection.setRequestMethod("POST"); | ||
connection.setDoInput(true); | ||
connection.setDoOutput(true); | ||
connection.setUseCaches(false); | ||
|
||
// Send the body: | ||
OutputStream wr = connection.getOutputStream(); | ||
wr.write(bodyData); | ||
wr.flush(); | ||
wr.close(); | ||
connection.connect(); | ||
|
||
Log.i(logId, String.format("%s %d", uri, connection.getResponseCode())); | ||
} catch (Exception e) { | ||
throw new IOException("Could not reach push server " + uri, e); | ||
} finally { | ||
if (connection != null) connection.disconnect(); | ||
} | ||
} | ||
|
||
/** Reads a file from disk. */ | ||
private static @NonNull String readFile(File file) throws IOException { | ||
try (FileInputStream stream = new FileInputStream(file)) { | ||
return readInputStream(stream); | ||
} | ||
} | ||
|
||
/** Reads an input stream into a string, as utf-8. */ | ||
private static @NonNull String readInputStream(@NonNull InputStream stream) throws IOException { | ||
ByteArrayOutputStream out = new ByteArrayOutputStream(); | ||
|
||
int size; | ||
byte[] data = new byte[4096]; | ||
while ((size = stream.read(data)) > 0) { | ||
out.write(data, 0, size); | ||
} | ||
|
||
return out.toString("UTF-8"); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.