package org.ripple.power;
import org.json.JSONObject;
import org.ripple.power.collection.LongArray;
import org.ripple.power.config.LSystem;
import org.ripple.power.password.PasswordEasy;
import org.ripple.power.utils.BigNumber;
import org.ripple.power.utils.BitArray;
import org.ripple.power.utils.HMAC;
import org.ripple.power.utils.HttpRequest;
import org.ripple.power.utils.StringUtils;
public class RippleBlobObj {
public final static int LOGIN = 0;
public final static int UNLOCK = 1;
public static String keyHash(LongArray key, String token) throws Exception {
return BigNumber.hex_fromBits(BitArray.bitSlice(
new HMAC(key).encrypt(token), 0, 256));
}
public static class Missing {
public String region;
public String phone_verified;
public String phone;
public String auth_id_2fa;
public String country_code_2fa;
public String enabled_2fa;
public String phone_2fa;
public String city;
public String country;
}
public static class BlobInfoRes {
public String id;
public String key;
public String encrypted_secret;
public String blob;
public int revision;
public boolean success;
public int quota;
public String email;
public String identity_id;
public Missing missing_fields;
public AuthInfoRes auth;
}
public static class AuthInfoRes {
public int version;
public String blobvault;
public boolean exists;
public String username;
public String address;
public boolean emailVerified;
public boolean reserved;
public boolean profile_verified;
public boolean identity_verified;
public boolean success;
public String signreq;
public String[] result = new String[0];
}
public static class UnlockInfoRes {
public String auth_secret;
public String ripple_secret;
public String account_id;
public String email;
public String created;
public BlobInfoRes blob;
public String toString() {
StringBuilder sbr = new StringBuilder();
if (blob != null && blob.auth != null) {
sbr.append("version:");
sbr.append(blob.auth.version);
sbr.append(' ');
sbr.append("exists:");
sbr.append(blob.auth.exists);
sbr.append('\n');
sbr.append("username:");
sbr.append(blob.auth.username);
sbr.append('\n');
sbr.append("address:");
sbr.append(blob.auth.address);
sbr.append('\n');
sbr.append("email:");
sbr.append(blob.email);
sbr.append('\n');
sbr.append("identity");
sbr.append(blob.identity_id);
sbr.append('\n');
sbr.append("created:");
sbr.append(created);
}
return sbr.toString();
}
}
public static String def_pakdf_name = "PAKDF_1_0_0";
public static String def_authinfo_url = "https://id.ripple.com/";
public static String def_domain = "rippletrade.com";
private final static String authinfo = "v1/authinfo";
private String baseUrl;
public RippleBlobObj(String url) {
this.baseUrl = url;
if (!baseUrl.endsWith("/")) {
baseUrl += "/";
}
}
public RippleBlobObj() {
this(def_authinfo_url);
}
public BlobInfoRes load(final String username, final String secret)
throws Exception {
return load(username, secret, def_domain, def_pakdf_name);
}
public BlobInfoRes load(final String username, final String secret,
final String domain, final String pakdfName) throws Exception {
AuthInfoRes info = derive(username, secret, domain, pakdfName, LOGIN);
if (info != null && info.success) {
String[] result = info.result;
if (result != null && result.length == 2) {
String url = baseUrl + "v1/blob/" + result[0];
url += "?device_id=" + createSecret(32);
BlobInfoRes res = new BlobInfoRes();
res.auth = info;
res.id = result[0];
res.key = result[1];
HttpRequest request = HttpRequest.get(url);
if (request.ok()) {
JSONObject json = new JSONObject(request.body());
res.success = "success".equalsIgnoreCase(json
.getString("result"));
if (res.success) {
res.blob = json.getString("blob");
res.revision = json.getInt("revision");
res.quota = json.getInt("quota");
res.email = json.getString("email");
res.identity_id = json.getString("identity_id");
res.encrypted_secret = json
.getString("encrypted_secret");
if (json.has("missing_fields")) {
JSONObject miss = json
.getJSONObject("missing_fields");
Missing missing = new Missing();
missing.phone_2fa = miss.getString("2fa_phone");
missing.region = miss.getString("region");
missing.phone_verified = miss
.getString("phone_verified");
missing.phone = miss.getString("phone");
missing.auth_id_2fa = miss.getString("2fa_auth_id");
missing.country_code_2fa = miss
.getString("2fa_country_code");
missing.enabled_2fa = miss.getString("2fa_enabled");
missing.city = miss.getString("city");
missing.country = miss.getString("country");
}
}
return res;
}
}
}
return null;
}
public AuthInfoRes derive(final String username, final String secret,
final int mode) throws Exception {
return derive(username, secret, def_domain, def_pakdf_name, mode);
}
public AuthInfoRes derive(final String username, final String secret,
final String domain, final String pakdfName, final int mode)
throws Exception {
String purpose = null;
if (mode == 0) {
purpose = "login";
} else {
purpose = "unlock";
}
String page = baseUrl + authinfo + "?domain=" + domain + "&username="
+ username;
HttpRequest request = HttpRequest.get(page);
if (request.ok()) {
AuthInfoRes res = new AuthInfoRes();
String data = request.body();
JSONObject obj = new JSONObject(data);
if (obj.has("version")) {
res.version = obj.getInt("version");
}
if (obj.has("blobvault")) {
res.blobvault = obj.getString("blobvault");
}
if (obj.has("exists")) {
res.exists = obj.getBoolean("exists");
}
if (obj.has("username")) {
res.username = obj.getString("username");
}
if (obj.has("address")) {
res.address = obj.getString("address");
}
if (obj.has("emailVerified")) {
res.emailVerified = obj.getBoolean("emailVerified");
}
if (obj.has("reserved")) {
res.reserved = obj.getBoolean("reserved");
}
if (obj.has("profile_verified")) {
res.profile_verified = obj.getBoolean("profile_verified");
}
if (obj.has("identity_verified")) {
res.identity_verified = obj.getBoolean("identity_verified");
}
if (obj.has("pakdf")) {
JSONObject pakdf = obj.getJSONObject("pakdf");
String host = pakdf.getString("host");
String exponent = pakdf.getString("exponent");
String alpha = pakdf.getString("alpha");
String url = pakdf.getString("url");
String modulus = pakdf.getString("modulus");
BigNumber iExponent = BigNumber.bn(exponent), iModulus = BigNumber
.bn(modulus), iAlpha = BigNumber.bn(alpha);
String publicInfo = StringUtils.join(":", pakdfName,
host.length(), host, username.length(), username,
purpose.length(), purpose);
long publicSize = (long) Math.ceil(Math.min(
(7 + iModulus.bitLength()) >>> 3, 256) / 8);
LongArray publicHash = BigNumber.fdh(publicInfo, publicSize);
String publicHex = BigNumber.hex_fromBits(publicHash);
BigNumber iPublic = BigNumber.bn(publicHex).setBitM(0);
String secretInfo = StringUtils.join(":", publicInfo,
secret.length(), secret);
long secretSize = (7 + iModulus.bitLength()) >>> 3;
LongArray secretHash = BigNumber.fdh(secretInfo, secretSize);
String secretHex = BigNumber.hex_fromBits(secretHash);
BigNumber iSecret = BigNumber.bn(secretHex).mod(iModulus);
if (iSecret.jacobi(iModulus) != 1) {
iSecret = iSecret.mul(iAlpha).mod(iModulus);
}
BigNumber iRandom;
for (;;) {
iRandom = BigNumber.random(iModulus, 0);
if (iRandom.jacobi(iModulus) == 1) {
break;
}
}
BigNumber iBlind = iRandom.powermodMontgomery(
iPublic.mul(iExponent), iModulus), iSignreq = iSecret
.mulmod(iBlind, iModulus);
String signreq = BigNumber.hex_fromBits(iSignreq.toBits());
HttpRequest post = HttpRequest.post(url);
post.send("info=" + publicInfo + "&signreq=" + signreq);
if (post.ok()) {
JSONObject jsonObject = new JSONObject(post.body());
res.success = "success".equalsIgnoreCase(jsonObject
.getString("result"));
if (!res.success) {
return res;
}
res.signreq = jsonObject.getString("signres");
BigNumber iSignres = BigNumber.bn(res.signreq);
BigNumber iRandomInv = iRandom.inverseMod(iModulus);
BigNumber iSigned = iSignres.mulmod(iRandomInv, iModulus);
LongArray key = iSigned.toBits();
String[] result = null;
if (mode == 0) {
result = new String[] { keyHash(key, "id"),
keyHash(key, "crypt") };
} else {
result = new String[] { keyHash(key, "unlock") };
}
res.result = result;
return res;
}
}
}
return null;
}
public String createSecret(int size) {
PasswordEasy pass = new PasswordEasy();
pass.setPassMatrix(LSystem.hex16);
return pass.pass(size);
}
public String unescapeToken(String str) {
return str.replace("~0", "~").replace("~1", "/");
}
}