mirror of https://github.com/NekoX-Dev/NekoX.git
This commit is contained in:
parent
7d39be9cc0
commit
c19a079ca7
|
@ -9,13 +9,13 @@ jobs:
|
||||||
fossBuild:
|
fossBuild:
|
||||||
name: Foss Build
|
name: Foss Build
|
||||||
runs-on: ubuntu-latest
|
runs-on: ubuntu-latest
|
||||||
if: "contains(github.event.head_commit.message, '[RELEASE]') || contains(github.event.head_commit.message, '[N]')"
|
if: "contains(github.event.head_commit.message, '[RELEASE]')"
|
||||||
steps:
|
steps:
|
||||||
- uses: actions/checkout@v2
|
- uses: actions/checkout@v2
|
||||||
- uses: actions/cache@v1
|
- uses: actions/cache@v1
|
||||||
with:
|
with:
|
||||||
path: ~/.gradle
|
path: ~/.gradle
|
||||||
key: gradle-${{ hashFiles('**/*.gradle') }}
|
key: native-${{ hashFiles('**/*.gradle') }}
|
||||||
restore-keys: |
|
restore-keys: |
|
||||||
${{ runner.os }}-gradle-
|
${{ runner.os }}-gradle-
|
||||||
- run: git submodule update --init --recursive
|
- run: git submodule update --init --recursive
|
||||||
|
@ -73,7 +73,7 @@ jobs:
|
||||||
|
|
||||||
./patch_boringssl.sh
|
./patch_boringssl.sh
|
||||||
|
|
||||||
[ -d "patch_boringssl/build" ] || ./build_boringssl.sh
|
[ -d "boringssl/build" ] || ./build_boringssl.sh
|
||||||
|
|
||||||
- name: assemble
|
- name: assemble
|
||||||
run: |
|
run: |
|
||||||
|
|
|
@ -0,0 +1,75 @@
|
||||||
|
name: Native Build
|
||||||
|
|
||||||
|
on:
|
||||||
|
push:
|
||||||
|
branches:
|
||||||
|
- master
|
||||||
|
|
||||||
|
jobs:
|
||||||
|
nativeBuild:
|
||||||
|
name: Native Build
|
||||||
|
runs-on: ubuntu-latest
|
||||||
|
if: "contains(github.event.head_commit.message, '[N]')"
|
||||||
|
steps:
|
||||||
|
- uses: actions/checkout@v2
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: ~/.gradle
|
||||||
|
key: native-${{ hashFiles('**/*.gradle') }}
|
||||||
|
restore-keys: |
|
||||||
|
${{ runner.os }}-gradle-
|
||||||
|
- run: git submodule update --init --recursive
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: TMessagesProj/jni/boringssl/build
|
||||||
|
key: boringssl-${{ hashFiles('TMessagesProj/jni/boringssl/.git') }}
|
||||||
|
- uses: actions/cache@v1
|
||||||
|
with:
|
||||||
|
path: TMessagesProj/jni/ffmpeg/build
|
||||||
|
key: ffmpeg-${{ hashFiles('TMessagesProj/jni/ffmpeg/.git') }}
|
||||||
|
- uses: actions/setup-java@v1
|
||||||
|
with:
|
||||||
|
java-version: 1.8
|
||||||
|
- name: Build native libraries
|
||||||
|
run: |
|
||||||
|
|
||||||
|
cd TMessagesProj/jni
|
||||||
|
|
||||||
|
while :
|
||||||
|
do
|
||||||
|
sudo apt-get install -y ninja-build && break
|
||||||
|
sleep 5
|
||||||
|
done
|
||||||
|
|
||||||
|
export NDK=$ANDROID_HOME/ndk-bundle
|
||||||
|
export NINJA_PATH=/usr/bin/ninja
|
||||||
|
export PATH=`echo $ANDROID_HOME/cmake/*/bin`:$PATH
|
||||||
|
|
||||||
|
[ -d "ffmpeg/build" ] || ./build_ffmpeg_clang.sh
|
||||||
|
|
||||||
|
./patch_ffmpeg.sh
|
||||||
|
|
||||||
|
./patch_boringssl.sh
|
||||||
|
|
||||||
|
[ -d "boringssl/build" ] || ./build_boringssl.sh
|
||||||
|
|
||||||
|
- name: assemble
|
||||||
|
run: |
|
||||||
|
sudo bash <<EOF
|
||||||
|
export LOCAL_PROPERTIES="${{ secrets.LOCAL_PROPERTIES }}" &&
|
||||||
|
./gradlew assembleAfatFoss
|
||||||
|
EOF
|
||||||
|
ls TMessagesProj/build/outputs/apk
|
||||||
|
echo ::set-env name=APK_FILE::$(find TMessagesProj/build/outputs/apk -name "*universal*")
|
||||||
|
- uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: NekoX-Foss
|
||||||
|
path: ${{ env.APK_FILE }}
|
||||||
|
- uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: Boringssl Library
|
||||||
|
path: "TMessagesProj/jni/boringssl/build"
|
||||||
|
- uses: actions/upload-artifact@master
|
||||||
|
with:
|
||||||
|
name: Ffmpeg Library
|
||||||
|
path: "TMessagesProj/jni/ffmpeg/build"
|
|
@ -111,6 +111,7 @@ void Handshake::cleanupHandshake() {
|
||||||
void Handshake::clearServerPublicKey() {
|
void Handshake::clearServerPublicKey() {
|
||||||
|
|
||||||
serverPublicKeys.clear();
|
serverPublicKeys.clear();
|
||||||
|
clearServerPublicKey();
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -480,6 +481,8 @@ void Handshake::processHandshakeResponse(TLObject *message, int64_t messageId) {
|
||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (LOGS_ENABLED) DEBUG_D("publicKey: %s, fingerprint: %lld",key.c_str(),keyFingerprint);
|
||||||
|
|
||||||
authServerNonce = new ByteArray(result->server_nonce.get());
|
authServerNonce = new ByteArray(result->server_nonce.get());
|
||||||
|
|
||||||
uint64_t pq = ((uint64_t) (result->pq->bytes[0] & 0xff) << 56) |
|
uint64_t pq = ((uint64_t) (result->pq->bytes[0] & 0xff) << 56) |
|
||||||
|
|
|
@ -33,13 +33,17 @@ import org.telegram.tgnet.TLRPC;
|
||||||
import org.telegram.ui.Components.ForegroundDetector;
|
import org.telegram.ui.Components.ForegroundDetector;
|
||||||
|
|
||||||
import java.io.File;
|
import java.io.File;
|
||||||
|
import java.lang.reflect.Method;
|
||||||
|
|
||||||
import tw.nekomimi.nekogram.ExternalGcm;
|
import tw.nekomimi.nekogram.ExternalGcm;
|
||||||
import tw.nekomimi.nekogram.NekoConfig;
|
import tw.nekomimi.nekogram.NekoConfig;
|
||||||
|
import tw.nekomimi.nekogram.utils.EnvUtil;
|
||||||
import tw.nekomimi.nekogram.utils.FileUtil;
|
import tw.nekomimi.nekogram.utils.FileUtil;
|
||||||
import tw.nekomimi.nekogram.utils.ProxyUtil;
|
import tw.nekomimi.nekogram.utils.ProxyUtil;
|
||||||
import tw.nekomimi.nekogram.utils.UIUtil;
|
import tw.nekomimi.nekogram.utils.UIUtil;
|
||||||
|
|
||||||
|
import static android.os.Build.VERSION.SDK_INT;
|
||||||
|
|
||||||
public class ApplicationLoader extends Application {
|
public class ApplicationLoader extends Application {
|
||||||
|
|
||||||
@SuppressLint("StaticFieldLeak")
|
@SuppressLint("StaticFieldLeak")
|
||||||
|
@ -62,12 +66,103 @@ public class ApplicationLoader extends Application {
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void attachBaseContext(Context base) {
|
protected void attachBaseContext(Context base) {
|
||||||
|
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
Reflection.unseal(base);
|
||||||
|
}
|
||||||
super.attachBaseContext(base);
|
super.attachBaseContext(base);
|
||||||
if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
if (SDK_INT < Build.VERSION_CODES.LOLLIPOP) {
|
||||||
MultiDex.install(this);
|
MultiDex.install(this);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author weishu
|
||||||
|
* @date 2018/6/7.
|
||||||
|
*/
|
||||||
|
public static class Reflection {
|
||||||
|
private static final String TAG = "Reflection";
|
||||||
|
|
||||||
|
private static Object sVmRuntime;
|
||||||
|
private static Method setHiddenApiExemptions;
|
||||||
|
|
||||||
|
static {
|
||||||
|
if (SDK_INT >= Build.VERSION_CODES.P) {
|
||||||
|
try {
|
||||||
|
Method forName = Class.class.getDeclaredMethod("forName", String.class);
|
||||||
|
Method getDeclaredMethod = Class.class.getDeclaredMethod("getDeclaredMethod", String.class, Class[].class);
|
||||||
|
|
||||||
|
Class<?> vmRuntimeClass = (Class<?>) forName.invoke(null, "dalvik.system.VMRuntime");
|
||||||
|
Method getRuntime = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "getRuntime", null);
|
||||||
|
setHiddenApiExemptions = (Method) getDeclaredMethod.invoke(vmRuntimeClass, "setHiddenApiExemptions", new Class[]{String[].class});
|
||||||
|
sVmRuntime = getRuntime.invoke(null);
|
||||||
|
} catch (Throwable e) {
|
||||||
|
FileLog.e("reflect bootstrap failed:", e);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
private static int UNKNOWN = -9999;
|
||||||
|
|
||||||
|
private static final int ERROR_SET_APPLICATION_FAILED = -20;
|
||||||
|
|
||||||
|
private static final int ERROR_EXEMPT_FAILED = -21;
|
||||||
|
|
||||||
|
private static int unsealed = UNKNOWN;
|
||||||
|
|
||||||
|
public static int unseal(Context context) {
|
||||||
|
if (SDK_INT < 28) {
|
||||||
|
// Below Android P, ignore
|
||||||
|
return 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// try exempt API first.
|
||||||
|
if (exemptAll()) {
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return ERROR_EXEMPT_FAILED;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make the method exempted from hidden API check.
|
||||||
|
*
|
||||||
|
* @param method the method signature prefix.
|
||||||
|
* @return true if success.
|
||||||
|
*/
|
||||||
|
public static boolean exempt(String method) {
|
||||||
|
return exempt(new String[]{method});
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* make specific methods exempted from hidden API check.
|
||||||
|
*
|
||||||
|
* @param methods the method signature prefix, such as "Ldalvik/system", "Landroid" or even "L"
|
||||||
|
* @return true if success
|
||||||
|
*/
|
||||||
|
public static boolean exempt(String... methods) {
|
||||||
|
if (sVmRuntime == null || setHiddenApiExemptions == null) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
try {
|
||||||
|
setHiddenApiExemptions.invoke(sVmRuntime, new Object[]{methods});
|
||||||
|
return true;
|
||||||
|
} catch (Throwable e) {
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Make all hidden API exempted.
|
||||||
|
*
|
||||||
|
* @return true if success.
|
||||||
|
*/
|
||||||
|
public static boolean exemptAll() {
|
||||||
|
return exempt(new String[]{"L"});
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
@SuppressLint("SdCardPath")
|
@SuppressLint("SdCardPath")
|
||||||
public static File getDataDirFixed() {
|
public static File getDataDirFixed() {
|
||||||
try {
|
try {
|
||||||
|
@ -220,11 +315,22 @@ public class ApplicationLoader extends Application {
|
||||||
applicationHandler = new Handler(applicationContext.getMainLooper());
|
applicationHandler = new Handler(applicationContext.getMainLooper());
|
||||||
|
|
||||||
startPushService();
|
startPushService();
|
||||||
|
|
||||||
|
try {
|
||||||
|
|
||||||
|
EnvUtil.doTest();
|
||||||
|
|
||||||
|
}catch (Exception e) {
|
||||||
|
|
||||||
|
FileLog.e("EnvUtil test Failed",e);
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
public static void startPushService() {
|
public static void startPushService() {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
if (SDK_INT >= Build.VERSION_CODES.JELLY_BEAN_MR2) {
|
||||||
return; // USE NOTIF LISTENER
|
return; // USE NOTIF LISTENER
|
||||||
}
|
}
|
||||||
SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings();
|
SharedPreferences preferences = MessagesController.getGlobalNotificationsSettings();
|
||||||
|
@ -236,7 +342,7 @@ public class ApplicationLoader extends Application {
|
||||||
}
|
}
|
||||||
if (enabled) {
|
if (enabled) {
|
||||||
try {
|
try {
|
||||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.O && NekoConfig.residentNotification) {
|
if (SDK_INT >= Build.VERSION_CODES.O && NekoConfig.residentNotification) {
|
||||||
applicationContext.startForegroundService(new Intent(applicationContext, NotificationsService.class));
|
applicationContext.startForegroundService(new Intent(applicationContext, NotificationsService.class));
|
||||||
} else {
|
} else {
|
||||||
applicationContext.startService(new Intent(applicationContext, NotificationsService.class));
|
applicationContext.startService(new Intent(applicationContext, NotificationsService.class));
|
||||||
|
|
|
@ -112,10 +112,8 @@ import org.telegram.ui.Components.SlideView;
|
||||||
|
|
||||||
import java.io.BufferedReader;
|
import java.io.BufferedReader;
|
||||||
import java.io.InputStreamReader;
|
import java.io.InputStreamReader;
|
||||||
import java.security.KeyFactory;
|
import java.math.BigInteger;
|
||||||
import java.security.PublicKey;
|
import java.security.PublicKey;
|
||||||
import java.security.spec.PKCS8EncodedKeySpec;
|
|
||||||
import java.security.spec.X509EncodedKeySpec;
|
|
||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
|
@ -134,6 +132,7 @@ import tw.nekomimi.nekogram.BottomBuilder;
|
||||||
import tw.nekomimi.nekogram.DataCenter;
|
import tw.nekomimi.nekogram.DataCenter;
|
||||||
import tw.nekomimi.nekogram.EditTextAutoFill;
|
import tw.nekomimi.nekogram.EditTextAutoFill;
|
||||||
import tw.nekomimi.nekogram.NekoXConfig;
|
import tw.nekomimi.nekogram.NekoXConfig;
|
||||||
|
import tw.nekomimi.nekogram.parts.PKCS1Pub;
|
||||||
import tw.nekomimi.nekogram.utils.AlertUtil;
|
import tw.nekomimi.nekogram.utils.AlertUtil;
|
||||||
|
|
||||||
@SuppressLint("HardwareIds")
|
@SuppressLint("HardwareIds")
|
||||||
|
@ -410,7 +409,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addRadioItem(LocaleController.getString("CustomApiOffical", R.string.CustomApiOffical), NekoXConfig.customApi == 1, (cell) -> {
|
builder.addRadioItem(LocaleController.getString("CustomApiOfficial", R.string.CustomApiOfficial), NekoXConfig.customApi == 1, (cell) -> {
|
||||||
|
|
||||||
targetApi.set(1);
|
targetApi.set(1);
|
||||||
|
|
||||||
|
@ -543,9 +542,9 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
EditText[] inputs = new EditText[6];
|
EditText[] inputs = new EditText[6];
|
||||||
|
|
||||||
builder.addTitle("Custom Backend",
|
builder.addTitle(LocaleController.getString("CustomBackend",R.string.CustomBackend),
|
||||||
true,
|
true,
|
||||||
"~");
|
LocaleController.getString("CustomBackendNotice",R.string.CustomBackendNotice));
|
||||||
|
|
||||||
int dcType;
|
int dcType;
|
||||||
|
|
||||||
|
@ -557,7 +556,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
dcType = 0;
|
dcType = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
builder.addRadioItem("OFFICAL", dcType == 0, (cell) -> {
|
builder.addRadioItem(LocaleController.getString("CustomBackendProduction",R.string.CustomBackendProduction), dcType == 0, (cell) -> {
|
||||||
|
|
||||||
targetDc.set(0);
|
targetDc.set(0);
|
||||||
|
|
||||||
|
@ -569,7 +568,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
builder.addRadioItem("TEST DC", dcType == 1, (cell) -> {
|
builder.addRadioItem(LocaleController.getString("CustomBackendTestDC",R.string.CustomBackendTestDC), dcType == 1, (cell) -> {
|
||||||
|
|
||||||
targetDc.set(1);
|
targetDc.set(1);
|
||||||
|
|
||||||
|
@ -593,7 +592,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[0] = builder.addEditText("Ipv4 Address");
|
inputs[0] = builder.addEditText(LocaleController.getString("CustomBackendIpv4",R.string.CustomBackendIpv4));
|
||||||
inputs[0].setFilters(new InputFilter[]{new InputFilter.LengthFilter(15)});
|
inputs[0].setFilters(new InputFilter[]{new InputFilter.LengthFilter(15)});
|
||||||
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv4)) {
|
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv4)) {
|
||||||
inputs[0].setText(NekoXConfig.customDcIpv4);
|
inputs[0].setText(NekoXConfig.customDcIpv4);
|
||||||
|
@ -618,7 +617,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[1] = builder.addEditText("Ipv6 Address");
|
inputs[1] = builder.addEditText(LocaleController.getString("CustomBackendIpv6",R.string.CustomBackendIpv6));
|
||||||
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv6)) {
|
if (StrUtil.isNotBlank(NekoXConfig.customDcIpv6)) {
|
||||||
inputs[1].setText(NekoXConfig.customDcIpv6);
|
inputs[1].setText(NekoXConfig.customDcIpv6);
|
||||||
}
|
}
|
||||||
|
@ -642,7 +641,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[2] = builder.addEditText("Port");
|
inputs[2] = builder.addEditText(LocaleController.getString("UseProxyPort",R.string.UseProxyPort));
|
||||||
inputs[2].setInputType(InputType.TYPE_CLASS_NUMBER);
|
inputs[2].setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
if (NekoXConfig.customDcPort != 0) {
|
if (NekoXConfig.customDcPort != 0) {
|
||||||
inputs[2].setText(NekoXConfig.customDcPort + "");
|
inputs[2].setText(NekoXConfig.customDcPort + "");
|
||||||
|
@ -673,7 +672,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[3] = builder.addEditText("Layer");
|
inputs[3] = builder.addEditText(LocaleController.getString("CustomBackendLayer",R.string.CustomBackendLayer));
|
||||||
inputs[3].setInputType(InputType.TYPE_CLASS_NUMBER);
|
inputs[3].setInputType(InputType.TYPE_CLASS_NUMBER);
|
||||||
if (NekoXConfig.customDcLayer != 0) {
|
if (NekoXConfig.customDcLayer != 0) {
|
||||||
inputs[3].setText(NekoXConfig.customDcLayer + "");
|
inputs[3].setText(NekoXConfig.customDcLayer + "");
|
||||||
|
@ -704,7 +703,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[4] = builder.addEditText("Public Key");
|
inputs[4] = builder.addEditText(LocaleController.getString("CustomBackendPublicKey",R.string.CustomBackendPublicKey));
|
||||||
inputs[4].setInputType(InputType.TYPE_CLASS_TEXT);
|
inputs[4].setInputType(InputType.TYPE_CLASS_TEXT);
|
||||||
inputs[4].setGravity(Gravity.TOP | LocaleController.generateFlagStart());
|
inputs[4].setGravity(Gravity.TOP | LocaleController.generateFlagStart());
|
||||||
inputs[4].setSingleLine(false);
|
inputs[4].setSingleLine(false);
|
||||||
|
@ -721,32 +720,23 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
if (StrUtil.isBlank(s.toString())) {
|
if (StrUtil.isBlank(s.toString())) {
|
||||||
NekoXConfig.customDcPublicKey = "";
|
NekoXConfig.customDcPublicKey = "";
|
||||||
inputs[5].setText("PublicKey required");
|
inputs[5].setText("");
|
||||||
} else {
|
} else {
|
||||||
|
|
||||||
try {
|
try {
|
||||||
|
|
||||||
String publicKeyBase64 = s.toString()
|
String publicKeyBase64 = s.toString()
|
||||||
.replace("-----BEGIN RSA PUBLIC KEY-----", "")
|
.replace("-----BEGIN RSA PUBLIC KEY-----", "")
|
||||||
.replace("-----BEGIN PUBLIC KEY-----", "")
|
.replace("-----END RSA PUBLIC KEY-----", "");
|
||||||
.replace("-----END RSA PUBLIC KEY-----", "")
|
|
||||||
.replace("-----END PUBLIC KEY-----", "");
|
|
||||||
|
|
||||||
PublicKey publicKey = KeyFactory.getInstance("RSA").generatePublic(new X509EncodedKeySpec(Base64.decode(publicKeyBase64)));
|
PKCS1Pub.decodePKCS1PublicKey(Base64.decode(publicKeyBase64));
|
||||||
|
|
||||||
NekoXConfig.customDcPublicKey = s.toString();
|
NekoXConfig.customDcPublicKey = s.toString();
|
||||||
inputs[4].setError(null);
|
inputs[4].setError(null);
|
||||||
|
|
||||||
long fingerprint = DataCenter.calcAuthKeyId(publicKey.getEncoded());
|
|
||||||
inputs[5].setText(Long.toHexString(fingerprint));
|
|
||||||
NekoXConfig.customDcFingerprint = fingerprint;
|
|
||||||
|
|
||||||
} catch (Exception e) {
|
} catch (Exception e) {
|
||||||
|
|
||||||
FileLog.e("Invalid public key",e);
|
inputs[4].setError("Invalid PKCS1 Key");
|
||||||
|
|
||||||
inputs[4].setError("Invalid RSA Public Key");
|
|
||||||
inputs[5].setText("PublicKey required");
|
|
||||||
NekoXConfig.customDcPublicKey = "";
|
NekoXConfig.customDcPublicKey = "";
|
||||||
|
|
||||||
}
|
}
|
||||||
|
@ -759,12 +749,41 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
}
|
}
|
||||||
});
|
});
|
||||||
|
|
||||||
inputs[5] = builder.addEditText();
|
inputs[5] = builder.addEditText(LocaleController.getString("CustomBackendFingerprint",R.string.CustomBackendFingerprint));
|
||||||
inputs[5].setText("PublicKey required");
|
|
||||||
inputs[5].setEnabled(false);
|
|
||||||
if (NekoXConfig.customDcFingerprint != 0) {
|
if (NekoXConfig.customDcFingerprint != 0) {
|
||||||
inputs[5].setText(NekoXConfig.customDcFingerprint + "");
|
inputs[5].setText("0x" + Long.toString(NekoXConfig.customDcFingerprint,16));
|
||||||
}
|
}
|
||||||
|
inputs[5].addTextChangedListener(new TextWatcher() {
|
||||||
|
@Override
|
||||||
|
public void beforeTextChanged(CharSequence s, int start, int count, int after) {
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onTextChanged(CharSequence s, int start, int before, int count) {
|
||||||
|
if (StrUtil.isBlank(s.toString())) {
|
||||||
|
NekoXConfig.customDcFingerprint = 0;
|
||||||
|
inputs[5].setError(null);
|
||||||
|
} else {
|
||||||
|
String f = s.toString();
|
||||||
|
int r = 10;
|
||||||
|
if (f.startsWith("0x")) {
|
||||||
|
f = f.substring(2);
|
||||||
|
r = 16;
|
||||||
|
}
|
||||||
|
try {
|
||||||
|
NekoXConfig.customDcFingerprint = new BigInteger(f,r).longValue();
|
||||||
|
inputs[5].setError(null);
|
||||||
|
} catch (NumberFormatException e) {
|
||||||
|
NekoXConfig.customDcFingerprint = 0;
|
||||||
|
inputs[5].setError("Invalid Fingerprint");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void afterTextChanged(Editable s) {
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
if (dcType < 2) {
|
if (dcType < 2) {
|
||||||
|
|
||||||
|
@ -795,7 +814,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
|
||||||
} else if (StrUtil.isBlank(inputs[2].getText().toString())) {
|
} else if (NekoXConfig.customDcPort == 0) {
|
||||||
|
|
||||||
AlertUtil.showToast("Input Port");
|
AlertUtil.showToast("Input Port");
|
||||||
inputs[2].setError("Port required");
|
inputs[2].setError("Port required");
|
||||||
|
@ -804,7 +823,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
|
||||||
} else if (StrUtil.isBlank(inputs[3].getText().toString())) {
|
} else if (NekoXConfig.customDcLayer == 0) {
|
||||||
|
|
||||||
AlertUtil.showToast("Input Layer");
|
AlertUtil.showToast("Input Layer");
|
||||||
inputs[3].setError("Layer required");
|
inputs[3].setError("Layer required");
|
||||||
|
@ -813,7 +832,7 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
|
||||||
} else if (StrUtil.isBlank(inputs[4].getText().toString())) {
|
} else if (StrUtil.isBlank(NekoXConfig.customDcPublicKey)) {
|
||||||
|
|
||||||
AlertUtil.showToast("Input PublicKey");
|
AlertUtil.showToast("Input PublicKey");
|
||||||
inputs[4].setError("PublicKey required");
|
inputs[4].setError("PublicKey required");
|
||||||
|
@ -822,6 +841,15 @@ public class LoginActivity extends BaseFragment implements NotificationCenter.No
|
||||||
|
|
||||||
return Unit.INSTANCE;
|
return Unit.INSTANCE;
|
||||||
|
|
||||||
|
} else if (NekoXConfig.customDcFingerprint == 0L) {
|
||||||
|
|
||||||
|
AlertUtil.showToast("Input Fingerprint");
|
||||||
|
inputs[5].setError("Fingerprint required");
|
||||||
|
inputs[5].requestFocus();
|
||||||
|
AndroidUtilities.showKeyboard(inputs[5]);
|
||||||
|
|
||||||
|
return Unit.INSTANCE;
|
||||||
|
|
||||||
} else if (StrUtil.isBlank(NekoXConfig.customDcIpv4) && StrUtil.isBlank(NekoXConfig.customDcIpv6)) {
|
} else if (StrUtil.isBlank(NekoXConfig.customDcIpv4) && StrUtil.isBlank(NekoXConfig.customDcIpv6)) {
|
||||||
|
|
||||||
AlertUtil.showToast("Input Address");
|
AlertUtil.showToast("Input Address");
|
||||||
|
|
|
@ -1,24 +1,29 @@
|
||||||
package tw.nekomimi.nekogram
|
package tw.nekomimi.nekogram
|
||||||
|
|
||||||
import cn.hutool.core.util.ArrayUtil
|
|
||||||
import cn.hutool.crypto.digest.DigestUtil
|
import cn.hutool.crypto.digest.DigestUtil
|
||||||
import org.telegram.messenger.MessagesController
|
import org.telegram.messenger.MessagesController
|
||||||
import org.telegram.tgnet.ConnectionsManager
|
import org.telegram.tgnet.ConnectionsManager
|
||||||
|
import org.telegram.tgnet.SerializedData
|
||||||
|
import java.math.BigInteger
|
||||||
import java.nio.ByteBuffer
|
import java.nio.ByteBuffer
|
||||||
|
import java.security.interfaces.RSAPublicKey
|
||||||
|
|
||||||
object DataCenter {
|
object DataCenter {
|
||||||
|
|
||||||
// func calcAuthKeyId(keyData []byte) int64 {
|
// func calcAuthKeyId(keyData []byte) int64 {
|
||||||
// sha1 := Sha1Digest(keyData)
|
// sha1 := Sha1Digest(keyData)
|
||||||
// // Lower 64 bits = 8 bytes of 20 byte SHA1 hash.
|
// // Lower 64 bits = 8 bytes of 20 byte SHA1 hash.
|
||||||
// return int64(binary.LittleEndian.Uint64(sha1[12:]))
|
// return int64(binary.LittleEndian.Uint64(sha1[12:]))
|
||||||
// }
|
// }
|
||||||
@JvmStatic
|
@JvmStatic
|
||||||
fun calcAuthKeyId(publicKey: ByteArray): Long {
|
fun calcAuthKeyId(publicKey: RSAPublicKey): Long {
|
||||||
|
|
||||||
val sha1 = DigestUtil.sha1(publicKey)
|
val key = SerializedData()
|
||||||
|
|
||||||
return ByteBuffer.wrap(ArrayUtil.sub(sha1, 12, sha1.size)).long
|
key.writeByteArray(publicKey.modulus.toByteArray())
|
||||||
|
key.writeByteArray(publicKey.publicExponent.toByteArray())
|
||||||
|
|
||||||
|
return BigInteger(DigestUtil.sha1(key.toByteArray()).slice(12 until 20).toByteArray()).toLong()
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -80,7 +85,7 @@ object DataCenter {
|
||||||
}
|
}
|
||||||
|
|
||||||
ConnectionsManager.native_saveDatacenters(account)
|
ConnectionsManager.native_saveDatacenters(account)
|
||||||
ConnectionsManager.native_setLayer(account,layer)
|
ConnectionsManager.native_setLayer(account, layer)
|
||||||
|
|
||||||
repeat(5) {
|
repeat(5) {
|
||||||
|
|
||||||
|
|
|
@ -4,6 +4,7 @@ import android.annotation.SuppressLint;
|
||||||
import android.app.Activity;
|
import android.app.Activity;
|
||||||
import android.content.Intent;
|
import android.content.Intent;
|
||||||
import android.content.SharedPreferences;
|
import android.content.SharedPreferences;
|
||||||
|
import android.os.Environment;
|
||||||
|
|
||||||
import org.telegram.messenger.ApplicationLoader;
|
import org.telegram.messenger.ApplicationLoader;
|
||||||
import org.telegram.messenger.LocaleController;
|
import org.telegram.messenger.LocaleController;
|
||||||
|
|
|
@ -644,7 +644,7 @@ public class NekoSettingsActivity extends BaseFragment {
|
||||||
disablePhotoSideActionRow = rowCount++;
|
disablePhotoSideActionRow = rowCount++;
|
||||||
hideKeyboardOnChatScrollRow = rowCount++;
|
hideKeyboardOnChatScrollRow = rowCount++;
|
||||||
rearVideoMessagesRow = rowCount++;
|
rearVideoMessagesRow = rowCount++;
|
||||||
hideAllTabRow = NekoXConfig.developerMode ? rowCount++ : -1;
|
hideAllTabRow = rowCount ++;
|
||||||
mapPreviewRow = rowCount++;
|
mapPreviewRow = rowCount++;
|
||||||
stickerSizeRow = rowCount++;
|
stickerSizeRow = rowCount++;
|
||||||
messageMenuRow = rowCount++;
|
messageMenuRow = rowCount++;
|
||||||
|
|
|
@ -87,7 +87,7 @@ public class NekoXConfig {
|
||||||
|
|
||||||
public static String customDcIpv4 = preferences.getString("custom_dc_v4", "");
|
public static String customDcIpv4 = preferences.getString("custom_dc_v4", "");
|
||||||
public static String customDcIpv6 = preferences.getString("custom_dc_v6", "");
|
public static String customDcIpv6 = preferences.getString("custom_dc_v6", "");
|
||||||
public static int customDcPort = preferences.getInt("custom_dc_port", 12345);
|
public static int customDcPort = preferences.getInt("custom_dc_port", 0);
|
||||||
public static int customDcLayer = preferences.getInt("custom_dc_layer", 0);
|
public static int customDcLayer = preferences.getInt("custom_dc_layer", 0);
|
||||||
|
|
||||||
public static String customDcPublicKey = preferences.getString("custom_dc_public_key", "");
|
public static String customDcPublicKey = preferences.getString("custom_dc_public_key", "");
|
||||||
|
|
|
@ -0,0 +1,99 @@
|
||||||
|
package tw.nekomimi.nekogram.parts;
|
||||||
|
|
||||||
|
import java.security.KeyFactory;
|
||||||
|
import java.security.NoSuchAlgorithmException;
|
||||||
|
import java.security.interfaces.RSAPublicKey;
|
||||||
|
import java.security.spec.InvalidKeySpecException;
|
||||||
|
import java.security.spec.X509EncodedKeySpec;
|
||||||
|
|
||||||
|
public class PKCS1Pub {
|
||||||
|
|
||||||
|
private static final int SEQUENCE_TAG = 0x30;
|
||||||
|
private static final int BIT_STRING_TAG = 0x03;
|
||||||
|
private static final byte[] NO_UNUSED_BITS = new byte[] { 0x00 };
|
||||||
|
private static final byte[] RSA_ALGORITHM_IDENTIFIER_SEQUENCE =
|
||||||
|
{(byte) 0x30, (byte) 0x0d,
|
||||||
|
(byte) 0x06, (byte) 0x09, (byte) 0x2a, (byte) 0x86, (byte) 0x48, (byte) 0x86, (byte) 0xf7, (byte) 0x0d, (byte) 0x01, (byte) 0x01, (byte) 0x01,
|
||||||
|
(byte) 0x05, (byte) 0x00};
|
||||||
|
|
||||||
|
|
||||||
|
public static RSAPublicKey decodePKCS1PublicKey(byte[] pkcs1PublicKeyEncoding)
|
||||||
|
throws NoSuchAlgorithmException, InvalidKeySpecException
|
||||||
|
{
|
||||||
|
byte[] subjectPublicKeyInfo2 = createSubjectPublicKeyInfoEncoding(pkcs1PublicKeyEncoding);
|
||||||
|
KeyFactory rsaKeyFactory = KeyFactory.getInstance("RSA");
|
||||||
|
RSAPublicKey generatePublic = (RSAPublicKey) rsaKeyFactory.generatePublic(new X509EncodedKeySpec(subjectPublicKeyInfo2));
|
||||||
|
return generatePublic;
|
||||||
|
}
|
||||||
|
|
||||||
|
public static byte[] createSubjectPublicKeyInfoEncoding(byte[] pkcs1PublicKeyEncoding)
|
||||||
|
{
|
||||||
|
byte[] subjectPublicKeyBitString = createDEREncoding(BIT_STRING_TAG, concat(NO_UNUSED_BITS, pkcs1PublicKeyEncoding));
|
||||||
|
byte[] subjectPublicKeyInfoValue = concat(RSA_ALGORITHM_IDENTIFIER_SEQUENCE, subjectPublicKeyBitString);
|
||||||
|
byte[] subjectPublicKeyInfoSequence = createDEREncoding(SEQUENCE_TAG, subjectPublicKeyInfoValue);
|
||||||
|
|
||||||
|
return subjectPublicKeyInfoSequence;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] concat(byte[] ... bas)
|
||||||
|
{
|
||||||
|
int len = 0;
|
||||||
|
for (int i = 0; i < bas.length; i++)
|
||||||
|
{
|
||||||
|
len += bas[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] buf = new byte[len];
|
||||||
|
int off = 0;
|
||||||
|
for (int i = 0; i < bas.length; i++)
|
||||||
|
{
|
||||||
|
System.arraycopy(bas[i], 0, buf, off, bas[i].length);
|
||||||
|
off += bas[i].length;
|
||||||
|
}
|
||||||
|
|
||||||
|
return buf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createDEREncoding(int tag, byte[] value)
|
||||||
|
{
|
||||||
|
if (tag < 0 || tag >= 0xFF)
|
||||||
|
{
|
||||||
|
throw new IllegalArgumentException("Currently only single byte tags supported");
|
||||||
|
}
|
||||||
|
|
||||||
|
byte[] lengthEncoding = createDERLengthEncoding(value.length);
|
||||||
|
|
||||||
|
int size = 1 + lengthEncoding.length + value.length;
|
||||||
|
byte[] derEncodingBuf = new byte[size];
|
||||||
|
|
||||||
|
int off = 0;
|
||||||
|
derEncodingBuf[off++] = (byte) tag;
|
||||||
|
System.arraycopy(lengthEncoding, 0, derEncodingBuf, off, lengthEncoding.length);
|
||||||
|
off += lengthEncoding.length;
|
||||||
|
System.arraycopy(value, 0, derEncodingBuf, off, value.length);
|
||||||
|
|
||||||
|
return derEncodingBuf;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static byte[] createDERLengthEncoding(int size)
|
||||||
|
{
|
||||||
|
if (size <= 0x7F)
|
||||||
|
{
|
||||||
|
// single byte length encoding
|
||||||
|
return new byte[] { (byte) size };
|
||||||
|
}
|
||||||
|
else if (size <= 0xFF)
|
||||||
|
{
|
||||||
|
// double byte length encoding
|
||||||
|
return new byte[] { (byte) 0x81, (byte) size };
|
||||||
|
}
|
||||||
|
else if (size <= 0xFFFF)
|
||||||
|
{
|
||||||
|
// triple byte length encoding
|
||||||
|
return new byte[] { (byte) 0x82, (byte) (size >> Byte.SIZE), (byte) size };
|
||||||
|
}
|
||||||
|
|
||||||
|
throw new IllegalArgumentException("size too large, only up to 64KiB length encoding supported: " + size);
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
|
@ -0,0 +1,30 @@
|
||||||
|
package tw.nekomimi.nekogram.utils
|
||||||
|
|
||||||
|
import android.content.Context
|
||||||
|
import android.os.storage.StorageManager
|
||||||
|
import org.telegram.messenger.ApplicationLoader
|
||||||
|
import org.telegram.messenger.FileLog
|
||||||
|
import java.io.File
|
||||||
|
|
||||||
|
object EnvUtil {
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
@Suppress("UNCHECKED_CAST")
|
||||||
|
val rootDirectories by lazy {
|
||||||
|
|
||||||
|
val mStorageManager = ApplicationLoader.applicationContext.getSystemService(Context.STORAGE_SERVICE) as StorageManager
|
||||||
|
|
||||||
|
(mStorageManager.javaClass.getMethod("getVolumePaths").invoke(mStorageManager) as Array<String>).map { File(it) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
@JvmStatic
|
||||||
|
fun doTest() {
|
||||||
|
|
||||||
|
FileLog.d("rootDirectories: ${rootDirectories.size}")
|
||||||
|
|
||||||
|
rootDirectories.forEach { FileLog.d(it.path) }
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -6,10 +6,20 @@
|
||||||
<string name="CustomApi">Custom API</string>
|
<string name="CustomApi">Custom API</string>
|
||||||
<string name="UseCustomApiNotice">Log in using the custom api, if you are unable to register or log in, this may help.\n\nNote: fcm will not work if you are using the release version.</string>
|
<string name="UseCustomApiNotice">Log in using the custom api, if you are unable to register or log in, this may help.\n\nNote: fcm will not work if you are using the release version.</string>
|
||||||
<string name="CustomApiNo">Don\'t use custom API</string>
|
<string name="CustomApiNo">Don\'t use custom API</string>
|
||||||
<string name="CustomApiOffical">Telegram Android</string>
|
<string name="CustomApiOfficial">Telegram Android</string>
|
||||||
<string name="CustomApiTGX">Telegram Android X</string>
|
<string name="CustomApiTGX">Telegram Android X</string>
|
||||||
<string name="CustomApiInput">Manual input</string>
|
<string name="CustomApiInput">Manual input</string>
|
||||||
|
|
||||||
|
<string name="CustomBackend">Custom Backend</string>
|
||||||
|
<string name="CustomBackendNotice">This function is only provided for expert users, if you don\'t know what the following options represent, just ignore it.</string>
|
||||||
|
<string name="CustomBackendProduction">Official Production DataCenter</string>
|
||||||
|
<string name="CustomBackendTestDC">Official Test DataCenter</string>
|
||||||
|
<string name="CustomBackendIpv4">Ipv4 Address</string>
|
||||||
|
<string name="CustomBackendIpv6">Ipv6 Address</string>
|
||||||
|
<string name="CustomBackendLayer">Layer</string>
|
||||||
|
<string name="CustomBackendPublicKey">Public Key</string>
|
||||||
|
<string name="CustomBackendFingerprint">Key Fingerprint</string>
|
||||||
|
|
||||||
<string name="AllowFlashCall">Allow flash call</string>
|
<string name="AllowFlashCall">Allow flash call</string>
|
||||||
|
|
||||||
<string name="ChangeTranslateProvider">Change Provider</string>
|
<string name="ChangeTranslateProvider">Change Provider</string>
|
||||||
|
|
Loading…
Reference in New Issue