Browse Source

genesis commit - open source

bug_fix
ZhangJun 5 years ago
parent
commit
976cec5490
  1. 74
      .gitignore
  2. 3
      .gitmodules
  3. 61
      README.md
  4. 1
      app/.gitignore
  5. 296
      app/build.gradle
  6. 100
      app/keywords.txt
  7. BIN
      app/libs/cvos.jar
  8. 29
      app/lint.xml
  9. 183
      app/proguard-rules.pro
  10. 389
      app/schemas/com.cobo.cold.db.AppDatabase/1.json
  11. 360
      app/schemas/com.cobo.cold.db.AppDatabase/2.json
  12. 121
      app/src/main/AndroidManifest.xml
  13. 14
      app/src/main/assets/bundleMap.json
  14. 228
      app/src/main/assets/en_license.html
  15. 152
      app/src/main/assets/en_privacy_policy.html
  16. 45
      app/src/main/assets/en_safety_instruction.html
  17. 59
      app/src/main/assets/script/BCH.bundle.js
  18. 2
      app/src/main/assets/script/BTC.bundle.js
  19. 30
      app/src/main/assets/script/DASH.bundle.js
  20. 2
      app/src/main/assets/script/DCR.bundle.js
  21. 2
      app/src/main/assets/script/EOS.bundle_d0f384b2cc63ac78f25e.js
  22. 2
      app/src/main/assets/script/ETC.bundle_b0d7ccb0dc5138935dc7.js
  23. 2
      app/src/main/assets/script/ETH.bundle_b0d7ccb0dc5138935dc7.js
  24. 2
      app/src/main/assets/script/IOST.bundle_010b6f92f916b495faad.js
  25. 30
      app/src/main/assets/script/LTC.bundle.js
  26. 2
      app/src/main/assets/script/TRON.bundle.js
  27. 38
      app/src/main/assets/script/XRP.bundle.js
  28. 2
      app/src/main/assets/script/XZC.bundle.js
  29. 30
      app/src/main/assets/utils.bundle.js
  30. 229
      app/src/main/assets/zh_rCN_license.html
  31. 138
      app/src/main/assets/zh_rCN_privacy_policy.html
  32. 43
      app/src/main/assets/zh_rCN_safety_instruction.html
  33. 85
      app/src/main/java/com/cobo/cold/AppExecutors.java
  34. 202
      app/src/main/java/com/cobo/cold/DataRepository.java
  35. 146
      app/src/main/java/com/cobo/cold/MainApplication.java
  36. 229
      app/src/main/java/com/cobo/cold/Utilities.java
  37. 72
      app/src/main/java/com/cobo/cold/callables/BlockingCallable.java
  38. 53
      app/src/main/java/com/cobo/cold/callables/ChangePasswordCallable.java
  39. 44
      app/src/main/java/com/cobo/cold/callables/CheckBootModeCallable.java
  40. 53
      app/src/main/java/com/cobo/cold/callables/CheckUpdateFirmwareCallable.java
  41. 39
      app/src/main/java/com/cobo/cold/callables/ClearTokenCallable.java
  42. 49
      app/src/main/java/com/cobo/cold/callables/FirmwareParameterCallable.java
  43. 78
      app/src/main/java/com/cobo/cold/callables/GetExtendedPublicKeyCallable.java
  44. 46
      app/src/main/java/com/cobo/cold/callables/GetMessageCallable.java
  45. 54
      app/src/main/java/com/cobo/cold/callables/GetPasswordTokenCallable.java
  46. 46
      app/src/main/java/com/cobo/cold/callables/GetRandomEntropyCallable.java
  47. 47
      app/src/main/java/com/cobo/cold/callables/GetUpdateKeyCallable.java
  48. 49
      app/src/main/java/com/cobo/cold/callables/GetUuidCallable.java
  49. 56
      app/src/main/java/com/cobo/cold/callables/GetVaultIdCallable.java
  50. 54
      app/src/main/java/com/cobo/cold/callables/RegisterPublicKeyCallable.java
  51. 139
      app/src/main/java/com/cobo/cold/callables/RequestUpdateCallable.java
  52. 39
      app/src/main/java/com/cobo/cold/callables/ResetCallable.java
  53. 58
      app/src/main/java/com/cobo/cold/callables/ResetPasswordCallable.java
  54. 131
      app/src/main/java/com/cobo/cold/callables/SignTxCallable.java
  55. 210
      app/src/main/java/com/cobo/cold/callables/UpdateCallable.java
  56. 50
      app/src/main/java/com/cobo/cold/callables/UpdatePassphraseCallable.java
  57. 54
      app/src/main/java/com/cobo/cold/callables/VerifyFingerprintCallable.java
  58. 48
      app/src/main/java/com/cobo/cold/callables/VerifyMnemonicCallable.java
  59. 48
      app/src/main/java/com/cobo/cold/callables/VerifyPasswordCallable.java
  60. 69
      app/src/main/java/com/cobo/cold/callables/WebAuthCallable.java
  61. 51
      app/src/main/java/com/cobo/cold/callables/WriteMnemonicCallable.java
  62. 28
      app/src/main/java/com/cobo/cold/config/BaseFeatureFlags.java
  63. 105
      app/src/main/java/com/cobo/cold/db/AppDatabase.java
  64. 72
      app/src/main/java/com/cobo/cold/db/PresetData.java
  65. 40
      app/src/main/java/com/cobo/cold/db/dao/AccountDao.java
  66. 52
      app/src/main/java/com/cobo/cold/db/dao/AddressDao.java
  67. 56
      app/src/main/java/com/cobo/cold/db/dao/CoinDao.java
  68. 47
      app/src/main/java/com/cobo/cold/db/dao/TxDao.java
  69. 49
      app/src/main/java/com/cobo/cold/db/dao/WhiteListDao.java
  70. 97
      app/src/main/java/com/cobo/cold/db/entity/AccountEntity.java
  71. 142
      app/src/main/java/com/cobo/cold/db/entity/AddressEntity.java
  72. 190
      app/src/main/java/com/cobo/cold/db/entity/CoinEntity.java
  73. 22
      app/src/main/java/com/cobo/cold/db/entity/FilterableItem.java
  74. 188
      app/src/main/java/com/cobo/cold/db/entity/TxEntity.java
  75. 95
      app/src/main/java/com/cobo/cold/db/entity/WhiteListEntity.java
  76. 47
      app/src/main/java/com/cobo/cold/encryption/ChipSigner.java
  77. 121
      app/src/main/java/com/cobo/cold/encryption/EncryptionCoreProvider.java
  78. 105
      app/src/main/java/com/cobo/cold/encryption/exception/EncryptionCoreException.java
  79. 28
      app/src/main/java/com/cobo/cold/encryption/interception/Intercept.java
  80. 27
      app/src/main/java/com/cobo/cold/encryption/interception/InterceptManager.java
  81. 39
      app/src/main/java/com/cobo/cold/encryption/interception/InterceptManagerGroup.java
  82. 89
      app/src/main/java/com/cobo/cold/encryption/interception/Secp256k1SignIntercept.java
  83. 97
      app/src/main/java/com/cobo/cold/encryption/interfaces/BASECONSTANTS.java
  84. 171
      app/src/main/java/com/cobo/cold/encryption/signature/Signature.java
  85. 26
      app/src/main/java/com/cobo/cold/fingerprint/EnrollListener.java
  86. 243
      app/src/main/java/com/cobo/cold/fingerprint/FingerprintKit.java
  87. 24
      app/src/main/java/com/cobo/cold/fingerprint/RemovalListener.java
  88. 26
      app/src/main/java/com/cobo/cold/fingerprint/VerifyListener.java
  89. 192
      app/src/main/java/com/cobo/cold/logging/FileLogger.java
  90. 202
      app/src/main/java/com/cobo/cold/mnemonic/AutoCompleteInput.java
  91. 147
      app/src/main/java/com/cobo/cold/mnemonic/MnemonicInputTable.java
  92. 114
      app/src/main/java/com/cobo/cold/mnemonic/TableItemDecoration.java
  93. 36
      app/src/main/java/com/cobo/cold/model/Address.java
  94. 42
      app/src/main/java/com/cobo/cold/model/Coin.java
  95. 44
      app/src/main/java/com/cobo/cold/model/Tx.java
  96. 47
      app/src/main/java/com/cobo/cold/protocol/EncodeConfig.java
  97. 59
      app/src/main/java/com/cobo/cold/protocol/ZipUtil.java
  98. 138
      app/src/main/java/com/cobo/cold/protocol/builder/BaseBuilder.java
  99. 53
      app/src/main/java/com/cobo/cold/protocol/builder/SignTxResultBuilder.java
  100. 110
      app/src/main/java/com/cobo/cold/protocol/builder/SyncBuilder.java

74
.gitignore

@ -0,0 +1,74 @@
# Built application files
*.apk
*.ap_
*.aab
# Files for the ART/Dalvik VM
*.dex
# Java class files
*.class
# Generated files
bin/
gen/
out/
release/
releases/
# Gradle files
.gradle/
build/
# Local configuration file (sdk path, etc)
local.properties
# Proguard folder generated by Eclipse
proguard/
# Log Files
*.log
# Android Studio Navigation editor temp files
.navigation/
# Android Studio captures folder
captures/
# Keystore files
# Uncomment the following lines if you do not want to check your keystore files in.
#*.jks
#*.keystore
# External native build folder generated in Android Studio 2.2 and later
.externalNativeBuild
# Google Services (e.g. APIs or Firebase)
# google-services.json
# Freeline
freeline.py
freeline/
freeline_project_description.json
# fastlane
fastlane/report.xml
fastlane/Preview.html
fastlane/screenshots
fastlane/test_output
fastlane/readme.md
# Version control
vcs.xml
# lint
lint/intermediates/
lint/generated/
lint/outputs/
lint/tmp/
# lint/reports/
.idea
*.iml
.DS_Store

3
.gitmodules

@ -0,0 +1,3 @@
[submodule "proto"]
path = app/src/main/proto
url = git@github.com:CoboVault/crypto-coin-message-protocol.git

61
README.md

@ -0,0 +1,61 @@
# Cobo Vault
Cobo Vault is an air-gapped & open source hardware wallet that uses completely transparent QR code data transmissions.Visit [Cobo Vault official website]( https://cobo.com/hardware-wallet/cobo-vault) to know more information about Cobo Vault.
Follow [@Cobo Vault](https://twitter.com/CoboVault) on Twitter.
<div align=center><img src="https://cobo.com/_next/static/images/intro-2b5b0b44cc64639df4fcdd9ccc46fd4b.png"/></div>
## Contents
- [Introduction](#introduction)
- [Clone](#clone)
- [Build](#build)
- [Test](#test)
- [Code Structure](#code-structure)
- [Core Dependencies](#core-dependencies)
- [Issues and PRS](#issues-and-prs)
- [License](#license)
## Introduction
Cobo Vault runs as a standalone application on customized hardware and Android 8.1 Oreo (Go Edition). This app performs:
1. Interaction with user.
2. Interaction with mobile application [Cobo Vault Mobile](https://cobo.com/hardware-wallet/cobo-vault-app) via QR code.
3. Interaction with Secure Element (SE) via serial port, the firmware of SE is opensourced at [cobo-vault-se-firmware](https://github.com/CoboVault/cobo-vault-se-firmware). The transaction data will be signed by this SE and the generated signature will be send back to this application. This signature and other necessary message will be displayed to user via QR code. Users use their mobile or desktop application to acquire signed transaction and broadcast it.
The application of this hardware wallet is programmed with Java language. The transaction related work is done by Typescript opensourced at [crypto-coin-kit](https://github.com/CoboVault/crypto-coin-kit). The framework, J2V8 is used as the bridge between Java and Typescript.
## Clone
git clone git@github.com:CoboVault/cobo-vault-cold.git --recursive
## Build
cd cobo-vault-cold
./gradlew assembleVault_v2Release
or you can build with IDEs, such as `Android Studio`,`intelliJ`
## Test
./gradlew test
## Code Structure
Modules:
`app` the main application module
`coinlib` the module for supported blockchains, currently included 12 blockchains
`encryption-core` module for Secure Element, include commands, protocol, serialize/deserialize, serial port communication
## Core Dependencies
1. [crypto-coin-message-protocol](https://github.com/CoboVault/crypto-coin-message-protocol) - protocol buffer of communication with mobile application
2. [crypto-coin-kit](https://github.com/CoboVault/crypto-coin-kit) - crypto-coin libraries
3. [cobo-vault-se-firmware](https://github.com/CoboVault/cobo-vault-se-firmware) - the firmware of SE
## Issues and PRS
any issues please submit at [issues](https://github.com/CoboVault/cobo-vault-cold/issues). and PRS are welcome!
## License
[![GPLv3 License](https://img.shields.io/badge/License-GPL%20v3-green.svg)](https://opensource.org/licenses/)
This project is licensed under the GPL License - see the [LICENSE](LICENSE) file for details

1
app/.gitignore

@ -0,0 +1 @@
/build

296
app/build.gradle

@ -0,0 +1,296 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
import groovy.xml.XmlUtil
import java.security.MessageDigest
apply plugin: 'com.android.application'
apply plugin: 'com.google.protobuf'
/**
* Set this to true to create two separate APKs instead of one:
* - An APK that only works on ARM devices
* - An APK that only works on x86 devices
* The advantage is the size of the APK is reduced by about 4MB.
* Upload all the APKs to the Play Store and people will download
* the correct one based on the CPU architecture of their device.
*/
def enableSeparateBuildPerCPUArchitecture = false
/**
* Run Proguard to shrink the Java bytecode in release builds.
*/
def enableProguardInReleaseBuilds = true
android {
compileSdkVersion rootProject.compileSdkVersion
buildToolsVersion rootProject.buildToolsVersion
def (mIsVaultRelease, mVersionNumber, mVersionName) = getVersionProperties()
println("start to build ${mVersionName}")
println("start to build ${getGitHash()}")
defaultConfig {
applicationId "com.cobo.cold"
minSdkVersion rootProject.minSdkVersion
targetSdkVersion rootProject.targetSdkVersion
versionCode mVersionNumber
versionName mVersionName
ndk {
abiFilters "armeabi-v7a"
}
testInstrumentationRunner "androidx.test.runner.AndroidJUnitRunner"
javaCompileOptions {
annotationProcessorOptions {
arguments = ["room.schemaLocation": "$projectDir/schemas".toString()]
}
}
}
splits {
abi {
reset()
enable enableSeparateBuildPerCPUArchitecture
universalApk false // If true, also generate a universal APK
include "armeabi-v7a", "x86"
}
}
dataBinding {
enabled = true
}
signingConfigs {
vault_v2 {
def key = getReleaseKeystore()
storeFile key.store
storePassword key.storePassword
keyAlias key.alias
keyPassword key.keyPassword
}
}
buildTypes {
release {
debuggable false
minifyEnabled enableProguardInReleaseBuilds
shrinkResources enableProguardInReleaseBuilds
proguardFiles getDefaultProguardFile('proguard-android.txt')
proguardFiles 'proguard-rules.pro'
}
}
compileOptions {
sourceCompatibility = '1.8'
targetCompatibility = '1.8'
}
flavorDimensions 'machine'
productFlavors {
vault_v2 {
dimension 'machine'
ndk {
abiFilters "armeabi-v7a"
}
manifestPlaceholders = ["sharedUserId": 'android.uid.system']
buildConfigField "String", "GIT_HASH", "\"${getGitHash()}\""
signingConfig signingConfigs.vault_v2
}
}
if (mIsVaultRelease) {
afterEvaluate {
task copyRelease(type: Copy) {
from "${buildDir}/outputs/apk/vault_v2/release"
into "${rootDir}/releases/${mVersionNumber}"
include '*.apk'
}
task archiveMapping(type: Zip) {
from "${buildDir}/outputs/mapping/vault_v2/release"
destinationDir file("${rootDir}/releases/${mVersionNumber}")
include '*'
archiveName "mapping_${mVersionNumber}.zip"
}
assembleVault_v2Release.finalizedBy(copyRelease, archiveMapping)
}
this.gradle.buildFinished {
def apkPath = "${rootDir}/releases/${mVersionNumber}/app-vault_v2-release.apk"
def apk = file(apkPath)
exec {
commandLine 'mv', apkPath,
"${rootDir}/releases/${mVersionNumber}/app_${mVersionNumber}_V${mVersionName}_${getGitHash()}_${calcSha1(apk)}.apk"
}
}
}
sourceSets {
main {
proto {
srcDir 'src/main/protos'
}
}
}
}
dependencies {
compileOnly files('libs/cvos.jar')
implementation 'androidx.appcompat:appcompat:1.1.0'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.navigation:navigation-fragment:2.2.0-rc04'
implementation 'androidx.navigation:navigation-ui:2.2.0-rc04'
implementation 'androidx.lifecycle:lifecycle-extensions:2.1.0'
implementation 'androidx.room:room-runtime:2.1.0'
implementation "androidx.preference:preference:1.1.0"
implementation 'com.google.android.material:material:1.0.0'
implementation 'com.google.protobuf:protobuf-java:3.7.1'
implementation 'com.google.zxing:core:3.3.3'
implementation 'com.googlecode.protobuf-java-format:protobuf-java-format:1.4'
implementation 'com.madgag.spongycastle:core:1.58.0.0@jar'
implementation 'com.yanzhenjie:permission:2.0.0-rc4'
implementation 'cn.carbswang.android:NumberPickerView:1.2.0'
implementation 'com.andrognito.patternlockview:patternlockview:1.0.0'
implementation 'com.allenliu.badgeview:library:1.1.1'
implementation 'net.lingala.zip4j:zip4j:1.3.2@jar'
implementation 'com.wei.android.lib:fingerprintidentify:1.2.6'
annotationProcessor 'androidx.room:room-compiler:2.1.0'
implementation project(':encryption-core')
implementation project(path: ':coinlib')
testImplementation 'junit:junit:4.12'
testImplementation 'org.json:json:20140107'
androidTestImplementation 'androidx.test:runner:1.2.0'
androidTestImplementation 'junit:junit:4.12'
}
preBuild {
doLast {
def imlFile = file(project.name + ".iml")
println 'Change ' + project.name + '.iml order'
try {
def parsedXml = (new XmlParser()).parse(imlFile)
def jdkNode = parsedXml.component[1].orderEntry.find { it.'@type' == 'jdk' }
parsedXml.component[1].remove(jdkNode)
def sdkString = "Android API " + android.compileSdkVersion.substring("android-".length()) + " Platform"
//noinspection GroovyResultOfObjectAllocationIgnored
new Node(parsedXml.component[1], 'orderEntry', ['type': 'jdk', 'jdkName': sdkString, 'jdkType': 'Android SDK'])
XmlUtil.serialize(parsedXml, new FileOutputStream(imlFile))
} catch (FileNotFoundException ignored) {
// nop, iml not found
}
}
}
task copyDownloadableDepsToLibs(type: Copy) {
from configurations.compile
into 'libs'
}
def getVersionProperties() {
def versionPropsFile = file('version.properties')
def versionProps = new Properties()
if (versionPropsFile.exists()) {
if (versionPropsFile.canRead()) {
versionProps.load(new FileInputStream(versionPropsFile))
} else {
throw new GradleException("could not read version.properties!")
}
}
def versionMajor = 1
def versionMinor = 0
def versionPatch = versionProps.getProperty('patch', '0').toInteger()
def isVaultRelease = false
gradle.startParameter.taskNames.each {
if (it.contains("assembleVaultRelease") || it.contains("assembleVault_v2Release")) {
isVaultRelease = true
return
}
}
def versionNumber = versionMajor * 10000 + versionMinor * 100 + versionPatch
def versionName = "${versionMajor}.${versionMinor}.${versionPatch}"
return [isVaultRelease, versionNumber, versionName]
}
def getReleaseKeystore() {
def keystoreDir = new File(rootDir, "keystores")
if (!keystoreDir.exists()) {
throw new FileNotFoundException("could not find ${keystoreDir}")
}
def keystorePropsFile = new File(keystoreDir, "test.properties")
if (!keystorePropsFile.exists()) {
throw new FileNotFoundException("could not find ${keystorePropsFile}")
}
def keystoreProps = new Properties()
keystoreProps.load(new FileInputStream(keystorePropsFile))
def keystoreFile = new File(keystoreDir, keystoreProps['key.store'])
if (!keystoreFile.exists()) {
throw new FileNotFoundException("could not find ${keystoreFile}")
}
return [
store : keystoreFile,
alias : keystoreProps['key.alias'],
storePassword: keystoreProps['key.store.password'],
keyPassword : keystoreProps['key.alias.password']
].asImmutable()
}
def getGitHash() {
def stdout = new ByteArrayOutputStream()
exec {
commandLine 'git', 'rev-parse', '--short=40', 'HEAD'
standardOutput = stdout
}
return stdout.toString().trim()
}
static def calcSha1(file) {
MessageDigest md = MessageDigest.getInstance("SHA-1")
file.eachByte 4096, { bytes, size ->
md.update(bytes, 0, size)
}
return md.digest().collect { String.format "%02x", it }.join()
}
protobuf {
protoc {
artifact = 'com.google.protobuf:protoc:3.7.1'
}
generateProtoTasks {
all().each { task ->
task.builtins {
remove java
}
task.builtins {
java {}
}
}
}
}

100
app/keywords.txt

@ -0,0 +1,100 @@
fIFInfZpDFQUphQYNyPV
ApNyHrpxzojOijkbxzrk
wlPdDEaaYglFnrUWQqvg
VZsTfraVAGFyJbaotOvk
GYRzEJtezwuULpOJKmoP
zGENmbfSdMuEOLUOUybO
iqRcFbpQUtKTjKGMeXAj
KLDvEYLohLJXzWTujjhn
zCZgtaxFiWGJbLOQLxZb
jLouzvlcarFXTyfrdEoE
nguEwdSTZhZnwfjnqpZg
cjucengcVUCLUGXrRmzC
EWlriTPSivBTQPKnxzal
UnWGKaqLCSDnVTrjCvRU
LwwNpNQZXmuOzTMgDNZc
fTabUkWbLziARXPaFOLs
WrTaggQduwmtOvyBTdtl
GNMeFSDGdWVIHVjUsHqe
CQZyJEvoHeLcfNWnZUSw
czBMxNfWVsoeNIfmPTer
zVIejMlYdCIYzkTTNDOr
AwgYkTQGKwCnaTpfawPI
DvncMLpRefXyNfKPxPaT
DpAKYFngQOaRaOSzcXIB
rewohphbUPHABBLUurFj
jQuYNuXNIJSDEEMqkEJn
wUlyCZIrGoVPXxCmQFdB
xZFKZQptlIfynnxQcDZg
xRbZEOKGygakOUpvWmCM
gKkjxcRVXZkTXuGYIWdz
McaaAGcdYiJKnRVuTYfT
eZkXmOKVOxsFvHTxbLAF
VASWWOvqnPyVstqjaSfV
uJdsdjmtxIrjXkggZEFf
XitgPRxjLMSbympWHCVk
qusqanrhRlBoSsooAzxN
mRPLKnsrrivnlWAahlIt
iZOBMcgFQKHyJTgtszWl
dlDcSJfmVIfLgvblKOKH
PtlggfTFONyCehyQlMph
AGdtjFoQcVEDYBPBAfsB
ZUGxaDBiqOvALDXWsXWC
wzRcrllBFKsuBHJcNySb
JDTZMZMNCQwwKMkQyoLs
FxSfxDGUYTZHvitBDtEw
VTxHyvBidyagjDtCrLKR
erJRRcflSPgWUztjsSWt
QyuUaQQpnXHgnABvsVLQ
STOCmzaWNPiHvUaeveGo
SsVEWCakNlLpMINOUVym
wnNliiTczRCayeODCCdl
XQbDDNTJWMZrlNouhWMu
zPRbBZaLuYLLUMjKuadS
qQwlyhEZpimUPnvjWHVg
rGVPsoFskDWzHwPygdsK
ycyoyWSOTuVSeYyXqSFN
VXzMqmkFrgNMPjmGiGfr
IBBTdOrrWjAhZAAkpbwQ
pkdgBbJdhKMKFBrsMwWC
lhckqSQEcLNcSLIeKord
HEYVEXKpwHbcdYPRKpKV
IfUIlYDRlnwdrwHxsiqB
BFKlucrvcBLhNwRyOwwy
xxomGpvMEBmikCnfSckF
vZEpRloQpItEFzaMOrQG
GGzlaqnSytxprIxDzEwW
ofXqWJjhwYnvMVoKHgqN
wCBbLzUKzVCaKwOlpECU
qOsyneWUaWBJyvQbVpma
JMFQaigLBJNmdPbODNWN
dXTTYOoAlbAXhFQRalEZ
VpULAidfEQzBfaVGlwWQ
RvHWdEQuHanuNvkyvbGh
VxWGOtYtMHeyVJVKerFa
dBeDoBPuwarZWXzhAhew
IXuzJmNkhdoUJWoJlKdA
AVRsMeiGCNcmfCOFFtmi
oHymyKlVvEBNPYoTumAE
zmRSsEXrzMknyoIJfyAE
auBcYziKMdLAalpreTqL
aHYXloiEokwrWLHmzfYl
mIMForKRkmyLqHXJLELH
yvWzoowUWXvYjvtHwznV
UbvmAYutiAdCnOswsSek
EoHkpiTftWJefvcScduM
ZeDZJqVmvzfqexkJojRD
DyYcLQlMNOrBUEDVVVwd
CXSkQjJtKCZVJRMwOGrh
uUfETCrFfaEnZHcRyjfU
BbvrNjbOGkYpRUESiFnH
LUtuGbVVYsDjmilkomAb
dGCEGksYmgzbQKdATdCx
twsNmqhEPQvbdFPeMgVS
ImQRmmpkitnornxJgQrH
MrovCJrsnWsIETMDUONk
lMzDMuENeVoqeRWGPeCU
ldWIUqUTesuzutpPXbBK
djzxzScKAGcIvmCIRDOg
qYNDRGXuOvrpgJozPKdS
hpDcSRfQgRtujpdsFdDT

BIN
app/libs/cvos.jar

Binary file not shown.

29
app/lint.xml

@ -0,0 +1,29 @@
<?xml version="1.0" encoding="UTF-8" ?>
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<lint>
<issue id="InvalidPackage" severity="ignore" />
<issue id="DefaultLocale" severity="ignore" />
<issue id="SmallSp" severity="ignore" />
<issue id="AlwaysShowAction" severity="ignore" />
<issue id="RelativeOverlap" severity="ignore" />
<issue id="HardcodedText" severity="ignore" />
<issue id="SetTextI18n" severity="ignore" />
<issue id="UnusedResources" severity="ignore" />
<issue id="UselessParent" severity="ignore"/>
</lint>

183
app/proguard-rules.pro

@ -0,0 +1,183 @@
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in /usr/local/Cellar/android-sdk/24.3.3/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Disabling obfuscation is useful if you collect stack traces from production crashes
# (unless you are using a system that supports de-obfuscate the stack traces).
-dontobfuscate
-dontoptimize
-dontwarn java.lang.invoke.**
-dontwarn org.bitcoinj.**
-dontwarn org.slf4j.**
-dontwarn com.google.**
-dontwarn org.bouncycastle.**
-dontwarn java8.util.**
-dontwarn jnr.posix.**
-dontwarn com.fasterxml.**
-dontwarn com.kenai.**
-dontwarn com.samsung.android.**
-keep,includedescriptorclasses class org.bitcoinj.crypto.HDKeyDerivation { *; }
# TextLayoutBuilder uses a non-public Android constructor within StaticLayout.
-dontwarn android.text.StaticLayout
# okhttp
-keepattributes Signature
-keepattributes *Annotation*
-keep class okhttp3.** { *; }
-keep interface okhttp3.** { *; }
-dontwarn okhttp3.**
# okio
-keep class sun.misc.Unsafe { *; }
-dontwarn java.nio.file.*
-dontwarn org.codehaus.mojo.animal_sniffer.IgnoreJRERequirement
-dontwarn okio.**
-dontwarn com.googlecode.protobuf.format.*
-dontwarn com.google.android.gms.**
######### initialize by facebook #########
######### general #########
# 代码混淆压缩比,在0~7之间,默认为5,一般不做修改
-optimizationpasses 5
# 混合时不使用大小写混合,混合后的类名为小写
-dontusemixedcaseclassnames
# 指定不去忽略非公共库的类
-dontskipnonpubliclibraryclasses
# 这句话能够使我们的项目混淆后产生映射文件
# 包含有类名->混淆后类名的映射关系
-verbose
# 指定不去忽略非公共库的类成员
-dontskipnonpubliclibraryclassmembers
# 不做预校验,preverify proguard 的四个步骤之一,Android 不需要 preverify,去掉这一步能够加快混淆速度
-dontpreverify
# 保留 Annotation 不混淆
-keepattributes *Annotation*,InnerClasses
# 避免混淆泛型
-keepattributes Signature
# 抛出异常时保留代码行号
-keepattributes SourceFile,LineNumberTable
# 指定混淆是采用的算法,后面的参数是一个过滤器
# 这个过滤器是谷歌推荐的算法,一般不做更改
-optimizations !code/simplification/cast,!field/*,!class/merging/*
#############################################
#
# Android开发中一些需要保留的公共部分
#
#############################################
# 保留我们使用的四大组件,自定义的 Application 等等这些类不被混淆
# 因为这些子类都有可能被外部调用
-keep public class * extends android.app.Activity
-keep public class * extends android.app.Appliction
-keep public class * extends android.app.Service
-keep public class * extends android.content.BroadcastReceiver
-keep public class * extends android.content.ContentProvider
-keep public class * extends android.app.backup.BackupAgentHelper
-keep public class * extends android.preference.Preference
-keep public class * extends android.view.View
-keep public class com.android.vending.licensing.ILicensingService
# 保留 support 下的所有类及其内部类
-keep class android.support.** {*;}
-keep class androidx.** {*;}
# 保留继承的
-keep public class * extends android.support.v4.**
-keep public class * extends android.support.v7.**
-keep public class * extends android.support.annotation.**
-keep public class * extends androidx.**
# 保留本地 native 方法不被混淆
-keepclasseswithmembernames class * {
native <methods>;
}
# 保留在 Activity 中的方法参数是view的方法,
# 这样以来我们在 layout 中写的 onClick 就不会被影响
-keepclassmembers class * extends android.app.Activity{
public void *(android.view.View);
}
# 保留枚举类不被混淆
-keepclassmembers enum * {
public static **[] values();
public static ** valueOf(java.lang.String);
}
# 保留我们自定义控件(继承自 View)不被混淆
-keep public class * extends android.view.View{
*** get*();
void set*(***);
public <init>(android.content.Context);
public <init>(android.content.Context, android.util.AttributeSet);
public <init>(android.content.Context, android.util.AttributeSet, int);
}
# 保留 Parcelable 序列化类不被混淆
-keep class * implements android.os.Parcelable {
public static final android.os.Parcelable$Creator *;
}
# 保留 Serializable 序列化的类不被混淆
-keepclassmembers class * implements java.io.Serializable {
static final long serialVersionUID;
private static final java.io.ObjectStreamField[] serialPersistentFields;
!static !transient <fields>;
!private <fields>;
!private <methods>;
private void writeObject(java.io.ObjectOutputStream);
private void readObject(java.io.ObjectInputStream);
java.lang.Object writeReplace();
java.lang.Object readResolve();
}
######### general #########
######### custom #########
-ignorewarnings
-keep class com.facebook.react.** { *; }
-keep class com.eclipsesource.v8.** { *; }
-keep class com.cobo.cold.protobuf.** { *; }
-keep class com.cobo.coinlib.coins.** { *; }
-useuniqueclassmembernames
-obfuscationdictionary keywords.txt
-classobfuscationdictionary keywords.txt
-packageobfuscationdictionary keywords.txt
######### custom #########

389
app/schemas/com.cobo.cold.db.AppDatabase/1.json

@ -0,0 +1,389 @@
{
"formatVersion": 1,
"database": {
"version": 1,
"identityHash": "e40b0124ef809ff4998dbf0d3a0675b9",
"entities": [
{
"tableName": "coins",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `coinId` TEXT, `name` TEXT, `iconResId` INTEGER NOT NULL, `show` INTEGER NOT NULL, `addressCount` INTEGER NOT NULL, `coinCode` TEXT, `exPub` TEXT, `passphraseHash` TEXT, `index` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "iconResId",
"columnName": "iconResId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "show",
"columnName": "show",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addressCount",
"columnName": "addressCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "exPub",
"columnName": "exPub",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "passphraseHash",
"columnName": "passphraseHash",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_coins_id",
"unique": false,
"columnNames": [
"id"
],
"createSql": "CREATE INDEX `index_coins_id` ON `${TABLE_NAME}` (`id`)"
}
],
"foreignKeys": []
},
{
"tableName": "addresses",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `path` TEXT, `coinId` TEXT, `addressString` TEXT, `name` TEXT, `index` INTEGER NOT NULL, `passphraseHash` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addressString",
"columnName": "addressString",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "passphraseHash",
"columnName": "passphraseHash",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "txs",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`txId` TEXT NOT NULL, `coinId` TEXT, `coinCode` TEXT, `amount` TEXT, `from` TEXT, `to` TEXT, `fee` TEXT, `signedHex` TEXT, `timeStamp` INTEGER NOT NULL, `memo` TEXT, `signId` TEXT, `passphraseHash` TEXT, PRIMARY KEY(`txId`))",
"fields": [
{
"fieldPath": "txId",
"columnName": "txId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "amount",
"columnName": "amount",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "from",
"columnName": "from",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "to",
"columnName": "to",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fee",
"columnName": "fee",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "signedHex",
"columnName": "signedHex",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "timeStamp",
"columnName": "timeStamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "memo",
"columnName": "memo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "signId",
"columnName": "signId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "passphraseHash",
"columnName": "passphraseHash",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"txId"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_txs_txId",
"unique": false,
"columnNames": [
"txId"
],
"createSql": "CREATE INDEX `index_txs_txId` ON `${TABLE_NAME}` (`txId`)"
}
],
"foreignKeys": []
},
{
"tableName": "passphrase",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`passHash` TEXT NOT NULL, PRIMARY KEY(`passHash`))",
"fields": [
{
"fieldPath": "passHash",
"columnName": "passHash",
"affinity": "TEXT",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"passHash"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_passphrase_passHash",
"unique": false,
"columnNames": [
"passHash"
],
"createSql": "CREATE INDEX `index_passphrase_passHash` ON `${TABLE_NAME}` (`passHash`)"
}
],
"foreignKeys": []
},
{
"tableName": "white_list",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addr` TEXT, `addrName` TEXT, `coinCode` TEXT, `memo` TEXT, `passphraseHash` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addr",
"columnName": "addr",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addrName",
"columnName": "addrName",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "memo",
"columnName": "memo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "passphraseHash",
"columnName": "passphraseHash",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `hdPath` TEXT, `exPub` TEXT, `addressLength` INTEGER NOT NULL, `isMultiSign` INTEGER NOT NULL, `coinId` INTEGER NOT NULL, FOREIGN KEY(`coinId`) REFERENCES `coins`(`id`) ON UPDATE NO ACTION ON DELETE NO ACTION )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hdPath",
"columnName": "hdPath",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "exPub",
"columnName": "exPub",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addressLength",
"columnName": "addressLength",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isMultiSign",
"columnName": "isMultiSign",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": [
{
"table": "coins",
"onDelete": "NO ACTION",
"onUpdate": "NO ACTION",
"columns": [
"coinId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, 'e40b0124ef809ff4998dbf0d3a0675b9')"
]
}
}

360
app/schemas/com.cobo.cold.db.AppDatabase/2.json

@ -0,0 +1,360 @@
{
"formatVersion": 1,
"database": {
"version": 2,
"identityHash": "227b9fc6314e81dc9cb31e45d4d443b1",
"entities": [
{
"tableName": "coins",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `coinId` TEXT, `name` TEXT, `iconResId` INTEGER NOT NULL, `show` INTEGER NOT NULL, `addressCount` INTEGER NOT NULL, `coinCode` TEXT, `exPub` TEXT, `belongTo` TEXT, `index` INTEGER NOT NULL)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "iconResId",
"columnName": "iconResId",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "show",
"columnName": "show",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addressCount",
"columnName": "addressCount",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "exPub",
"columnName": "exPub",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "belongTo",
"columnName": "belongTo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [
{
"name": "index_coins_id",
"unique": false,
"columnNames": [
"id"
],
"createSql": "CREATE INDEX `index_coins_id` ON `${TABLE_NAME}` (`id`)"
}
],
"foreignKeys": []
},
{
"tableName": "addresses",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `path` TEXT, `coinId` TEXT, `addressString` TEXT, `name` TEXT, `index` INTEGER NOT NULL, `belongTo` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "path",
"columnName": "path",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addressString",
"columnName": "addressString",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "name",
"columnName": "name",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "index",
"columnName": "index",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "belongTo",
"columnName": "belongTo",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "txs",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`txId` TEXT NOT NULL, `coinId` TEXT, `coinCode` TEXT, `amount` TEXT, `from` TEXT, `to` TEXT, `fee` TEXT, `signedHex` TEXT, `timeStamp` INTEGER NOT NULL, `memo` TEXT, `signId` TEXT, `belongTo` TEXT, PRIMARY KEY(`txId`))",
"fields": [
{
"fieldPath": "txId",
"columnName": "txId",
"affinity": "TEXT",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "amount",
"columnName": "amount",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "from",
"columnName": "from",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "to",
"columnName": "to",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "fee",
"columnName": "fee",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "signedHex",
"columnName": "signedHex",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "timeStamp",
"columnName": "timeStamp",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "memo",
"columnName": "memo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "signId",
"columnName": "signId",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "belongTo",
"columnName": "belongTo",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"txId"
],
"autoGenerate": false
},
"indices": [
{
"name": "index_txs_txId",
"unique": false,
"columnNames": [
"txId"
],
"createSql": "CREATE INDEX `index_txs_txId` ON `${TABLE_NAME}` (`txId`)"
}
],
"foreignKeys": []
},
{
"tableName": "white_list",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `addr` TEXT, `addrName` TEXT, `coinCode` TEXT, `memo` TEXT, `belongTo` TEXT)",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "addr",
"columnName": "addr",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addrName",
"columnName": "addrName",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "coinCode",
"columnName": "coinCode",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "memo",
"columnName": "memo",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "belongTo",
"columnName": "belongTo",
"affinity": "TEXT",
"notNull": false
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": []
},
{
"tableName": "accounts",
"createSql": "CREATE TABLE IF NOT EXISTS `${TABLE_NAME}` (`id` INTEGER PRIMARY KEY AUTOINCREMENT NOT NULL, `hdPath` TEXT, `exPub` TEXT, `addressLength` INTEGER NOT NULL, `isMultiSign` INTEGER NOT NULL, `coinId` INTEGER NOT NULL, FOREIGN KEY(`coinId`) REFERENCES `coins`(`id`) ON UPDATE NO ACTION ON DELETE CASCADE )",
"fields": [
{
"fieldPath": "id",
"columnName": "id",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "hdPath",
"columnName": "hdPath",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "exPub",
"columnName": "exPub",
"affinity": "TEXT",
"notNull": false
},
{
"fieldPath": "addressLength",
"columnName": "addressLength",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "isMultiSign",
"columnName": "isMultiSign",
"affinity": "INTEGER",
"notNull": true
},
{
"fieldPath": "coinId",
"columnName": "coinId",
"affinity": "INTEGER",
"notNull": true
}
],
"primaryKey": {
"columnNames": [
"id"
],
"autoGenerate": true
},
"indices": [],
"foreignKeys": [
{
"table": "coins",
"onDelete": "CASCADE",
"onUpdate": "NO ACTION",
"columns": [
"coinId"
],
"referencedColumns": [
"id"
]
}
]
}
],
"views": [],
"setupQueries": [
"CREATE TABLE IF NOT EXISTS room_master_table (id INTEGER PRIMARY KEY,identity_hash TEXT)",
"INSERT OR REPLACE INTO room_master_table (id,identity_hash) VALUES(42, '227b9fc6314e81dc9cb31e45d4d443b1')"
]
}
}

121
app/src/main/AndroidManifest.xml

@ -0,0 +1,121 @@
<?xml version="1.0" encoding="utf-8"?>
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
xmlns:tools="http://schemas.android.com/tools"
package="com.cobo.cold"
android:sharedUserId="${sharedUserId}">
<uses-permission android:name="android.permission.CAMERA" />
<uses-feature android:name="android.hardware.camera" />
<uses-permission
android:name="android.permission.RECOVERY"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.SERIAL_PORT"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.USE_FINGERPRINT" />
<uses-permission android:name="android.permission.PREVENT_POWER_KEY" />
<uses-permission android:name="android.permission.SYSTEM_ALERT_WINDOW" />
<uses-permission android:name="android.permission.WRITE_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.READ_EXTERNAL_STORAGE" />
<uses-permission android:name="android.permission.REQUEST_INSTALL_PACKAGES" />
<uses-permission
android:name="android.permission.INSTALL_PACKAGES"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.WRITE_SETTINGS"
tools:ignore="ProtectedPermissions" />
<uses-permission android:name="android.permission.VIBRATE" />
<uses-permission
android:name="android.permission.SHUTDOWN"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.DEVICE_POWER"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.READ_LOGS"
tools:ignore="ProtectedPermissions" />
<uses-permission
android:name="android.permission.WAKE_LOCK"
tools:ignore="ProtectedPermissions" />
<application
android:name=".MainApplication"
android:allowBackup="true"
android:icon="@mipmap/ic_launcher"
android:label="@string/app_name"
android:largeHeap="true"
android:supportsRtl="true"
android:theme="@style/AppTheme.NoActionBar"
tools:ignore="AllowBackup,GoogleAppIndexingWarning">
<activity
android:name=".ui.SetupVaultActivity"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar"
android:exported="false"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name=".ui.SplashActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:screenOrientation="portrait"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
<category android:name="android.intent.category.DEFAULT" />
<category android:name="android.intent.category.HOME" />
<category android:name="android.intent.category.LAUNCHER" />
</intent-filter>
</activity>
<activity
android:name=".ui.UnlockActivity"
android:configChanges="orientation|keyboardHidden|screenSize"
android:launchMode="singleTask"
android:screenOrientation="portrait"
android:exported="false"
android:theme="@style/AppTheme.NoActionBar"
android:windowSoftInputMode="adjustResize">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity
android:name=".ui.MainActivity"
android:label="@string/app_name"
android:screenOrientation="portrait"
android:exported="false"
android:theme="@style/AppTheme.NoActionBar">
<intent-filter>
<action android:name="android.intent.action.MAIN" />
</intent-filter>
</activity>
<activity android:name=".ui.AttackWarningActivity" />
<service android:name=".service.AttackCheckingService" />
</application>
</manifest>

14
app/src/main/assets/bundleMap.json

@ -0,0 +1,14 @@
{
"BTC": "BTC.bundle.js",
"BCH": "BCH.bundle.js",
"ETH": "ETH.bundle_b0d7ccb0dc5138935dc7.js",
"ETC": "ETC.bundle_b0d7ccb0dc5138935dc7.js",
"DASH": "DASH.bundle.js",
"LTC": "LTC.bundle.js",
"XRP": "XRP.bundle.js",
"XZC": "XZC.bundle.js",
"DCR": "DCR.bundle.js",
"EOS": "EOS.bundle_d0f384b2cc63ac78f25e.js",
"IOST": "IOST.bundle_010b6f92f916b495faad.js",
"TRON": "TRON.bundle.js"
}

228
app/src/main/assets/en_license.html

@ -0,0 +1,228 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>Hardware and App Terms of Service</strong>
<strong>Last Updated: [Oct. 18, 2018]</strong>
These Terms govern your use of our App and Hardware. Please read these Terms of Service (the "<strong>Terms</strong>") and our Privacy Policy (<u>https://webview.cobo.com/internal/vault/pp</u>) ("<strong>Privacy Policy</strong>") carefully because they govern your use of our mobile device application ("<strong>App</strong>"), our Wallet (as defined below), and any equipment supplied by Cobo for use with the Wallet and the App ("<strong>Hardware</strong>"). These Terms and the documents that are referred to herein constitute a binding obligation between you and Cobo Global HK Limited, the seller of Cobo. To make these Terms easier to read, App, Wallet, Hardware and our associated services are collectively called the "<strong>Services</strong>." A "<strong>Virtual Currency</strong>" is any blockchain-based currency or token such as Bitcoin. The use of our website located at <u>https://cobo.com</u> (the "Site") is governed by our general website terms of use <u>https://webview.cobo.com/internal/des/toc</u>.
<strong>1. Agreement to Terms.</strong> By using our Services, you agree to be bound by these Terms. If you don't agree to be bound by these Terms, do not use the Services. If you are accessing and using the Services on behalf of a company (such as your employer) or other legal entity, you represent and warrant that you have the authority to bind that company or other legal entity to these Terms. In that case, "you" and "your" will refer to that company or other legal entity. These Terms constitute the entire agreement between Cobo and you with respect to this Services and it supersedes all prior or contemporaneous communications, agreements and understandings between Cobo and you with respect to the subject matter hereof.
<strong>2. Privacy Policy.</strong> Please refer to our Privacy Policy for information on how we collect, use and disclose information from our users. You acknowledge and agree that your use of the Services is subject to our Privacy Policy.
<strong>IMPORTANT NOTICE REGARDING ARBITRATION: WHEN YOU AGREE TO THESE TERMS YOU ARE AGREEING (WITH LIMITED EXCEPTION) TO RESOLVE ANY DISPUTE BETWEEN YOU AND COBO THROUGH BINDING, INDIVIDUAL ARBITRATION RATHER THAN IN COURT. PLEASE CAREFULLY REVIEW THE "DISPUTE RESOLUTION" SECTION BELOW FOR DETAILS REGARDING ARBITRATION (INCLUDING THE PROCEDURE TO OPT OUT OF ARBITRATION).</strong>
<strong>3. Changes to Terms or Services.</strong> We may update the Terms at any time, in our sole discretion. If we do so, we'll let you know either by posting the updated Terms on the Site or through other communications. It's important that you review the Terms whenever we update them or you use the Services. If you continue to use the Services after we have posted updated Terms, you are agreeing to be bound by the updated Terms. If you don't agree to be bound by the updated Terms, then, except as otherwise provided the "Dispute Resolution" Section you may not use the Services anymore. Because our Services are evolving over time we may change or discontinue all or any part of the Services, at any time and without notice, at our sole discretion.
<strong>4. Who May Use the Services?</strong>
<strong>(a)</strong> Eligibility. You may use the Services only if you are 18 years or older, capable of forming a binding contract with Cobo) and are not barred from using the Services under applicable law.
<strong>(b)</strong> Registration and Your Information. If you want to use certain features of the Services you'll have to create an account ("<strong>Account</strong>"). You can do this via the Site or through your account with certain third-party social networking services such as Facebook or Twitter (each, an "<strong>SNS Account</strong>"). If you choose the SNS Account option we'll create your Account by extracting from your SNS Account certain personal information such as your name and email address and other personal information that your privacy settings on the SNS Account permit us to access.
<strong>(c)</strong> Accuracy of Account Information. It's important that you provide us with accurate, complete and up-to-date information for your Account and you agree to update such information to keep it accurate, complete and up-to-date. If you don't, we might have to suspend or terminate your Account. You agree that you won't disclose your Account password to anyone and you'll notify us immediately of any unauthorized use of your Account. You're responsible for all activities that occur under your Account, whether or not you know about them.
<strong>5. Hardware, Wallet and Description of Services.</strong>
<strong>(a)</strong> General. Cobo offers a hierarchical deterministic Virtual Currency wallet that implements the BIP44 blockchain protocol ("<strong>Wallet</strong>"). It is powered by Hardware in which a tablet-like device containing a security chip randomly generates a 24-word mnemonic phrase ("<strong>Mnemonic Phrase</strong>") and master seed to derive pairs of private and public keys to manage Virtual Currencies.
<strong>(b)</strong> Use of the Services with Virtual Currency. Please note that neither the Wallet nor do any of the Services "hold" Virtual Currency, but rather facilitates the interaction between the holder of the Virtual Currency and the underlying blockchain network that processes the transactions. Public keys allow you to receive and deposit Virtual Currency, whereas private keys allow you to send Virtual Currency outside of your account. To complete a Virtual Currency transaction, a user downloads the App on a mobile device, begins a transaction, and generates a QR code that is scanned by the Wallet, which will then complete the transaction by providing the private key for signature authority (provided however, that the user must control the Mnemonic Phrase and any other necessary passwords and details). After the transaction is signed by the Wallet, it generates a QR code that is scanned by the App on the mobile device, which then broadcasts the transaction to the blockchain via the internet. The App can only be used in conjunction with the Wallet and for no other purpose.
<strong>(c)</strong> Passwords. The Mnemonic Phrase and any password that you select in connection with your use of the Wallet is not known or otherwise stored by Cobo. Using the Mnemonic Phrase, your private key can be recovered on any BIP44-compatible wallet. IF YOU LOSE OR OTHERWISE FORGET YOUR MNEMONIC PHRASE, THEN NEITHER YOU NOR COBO WILL BE UNABLE TO RETRIEVE YOUR PRIVATE KEY, as the Wallet hardware is designed to resist tampering. The Wallet is designed to be separated and unconnected from the internet to provide further security against hacking, however no solution is theft-proof, including the Wallet.
<strong>(d)</strong> Firmware. In order to support certain functionality, such as new forks of Virtual Currency, firmware upgrades will be needed to be installed onto the Wallet per any instructions by Cobo.
<strong>(e)</strong> Sole Responsibility. YOU ARE SOLELY RESPONSIBLE FOR MAINTAINING THE SECURITY AND AVAILABILITY OF YOUR MNEMONIC PHRASE, PRIVATE KEY AND ANY OTHER PASSWORDS ASSOCIATED WITH YOUR WALLET. IT IS RECOMMENDED THAT YOU TAKE APPROPRIATE SAFEGUARDS TO ENSURE SUCH SECURITY, WHICH MAY INCLUDE MAINTAINING A SAFETY DEPOSIT BOX AT A REPUTABLE FINANCIAL INSTITUTION. FAILURE TO DO SO MAY RESULT IN THE LOSS OF CONTROL OF VIRTUAL CURRENCY ASSOCIATED WITH THE WALLET.
<strong>6. Virtual Currency Transactions.</strong>
<strong>(a)</strong> Transactions. In order to be completed, all proposed Virtual Currency transactions must be confirmed and recorded in the Virtual Currency public ledger associated with the relevant Virtual Currency network. Such networks are decentralized, peer-to-peer networks supported by independent third-parties, which are not owned, controlled or operated by Cobo. Virtual Currency networks are operated by decentralized networks of independent third parties. Cobo has no control over any Virtual Currency network and therefore cannot and does not ensure that any transaction details you submit via the Services will be confirmed via the relevant Virtual Currency network. You acknowledge and agree that the transaction details you submit via the Services may not be completed, or may be substantially delayed, by the Virtual Currency network used to process the transaction.
<strong>(b)</strong> No Storage or Transfer of Title of Virtual Currency. A Virtual Currency is an intangible, digital asset. They exist only by virtue of the ownership record maintained in the underlying Virtual Currency network. The Services do not store, send or receive Virtual Currency. Any transfer of title that might occur in any Virtual Currency occurs on the decentralized ledger within the Virtual Currency network and not within the Services. We do not guarantee that the Service can affect the transfer of title or right in any Virtual Currency.
<strong>(c)</strong> No Cancellations or Modifications. Once transaction details have been submitted to the Virtual Currency network via the Services, the Services cannot assist you to cancel or otherwise modify your transaction details. Cobo has no control over any Virtual Currency network and does not have the ability to facilitate any cancellation or modification requests.
<strong>7. Disclaimers.</strong>
<strong>(a)</strong> No Password Retrieval. Cobo does not receive or store your Wallet Password, Mnemonic Phrase, or the unencrypted keys and addresses. Therefore, we cannot assist you with Wallet Password retrieval. Our Services cannot generate a new Password for your Wallet. You are solely responsible for remembering your Wallet Password and Mnemonic Phrase. If you have not safely stored a backup of any Wallet Addresses and Mnemonic Phrase maintained in your Wallet, you accept and acknowledge that any Virtual Currency you have associated with such Wallet Addresses will become inaccessible if you do not have your Wallet Password and/or Mnemonic Phrase.
<strong>(b)</strong> Discontinuation of the Services. We may, in our sole discretion and without cost to you, with or without prior notice and at any time, modify or discontinue, temporarily or permanently, any portion of our Services. You are solely responsible for storing, outside of the Services, a backup of any Mnemonic Phrase, private key or other associated passwords, as you may not be able to access Virtual Currency maintained in your Wallet in the event that we discontinue or deprecate the Services.
<strong>(c)</strong> Relationship. Nothing in these Terms is intended to nor shall create any partnership, joint venture, agency, consultancy or trusteeship, you and Cobo being with respect to one another independent contractors.
<strong>(d)</strong> Accuracy of Information; Responsibility for Activities. You represent and warrant that any information you provide via the Services is accurate and complete. You accept and acknowledge that Cobo is not responsible for any errors or omissions that you make in connection with any Virtual Currency transaction initiated via the Services, for instance, if you mistype a public key or otherwise provide incorrect information. We strongly encourage you to review your transaction details carefully before completing them via the Services. You hereby accept and acknowledge that you take responsibility for all activities that occur under your Wallet and accept all risks of any authorized or unauthorized access to your Wallet, to the maximum extent permitted by law.
<strong>(e)</strong> Taxes. It is your responsibility to determine what, if any, taxes apply to the transactions you for which you have submitted transaction details via the Services, and it is your responsibility to report and remit the correct tax to the appropriate tax authority. You agree that Cobo is not responsible for determining whether taxes apply to your Virtual Currency transactions or for collecting, reporting, withholding or remitting any taxes arising from any Virtual Currency transactions.
<strong>(f)</strong> Disclaimer. THE SERVICES, HARDWARE AND CONTENT ARE PROVIDED "AS IS," WITHOUT WARRANTY OF ANY KIND. WITHOUT LIMITING THE FOREGOING, TO THE FULLEST EXTENT OF THE LAW, WE EXPLICITLY DISCLAIM ANY IMPLIED WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE, QUIET ENJOYMENT AND NON-INFRINGEMENT, AND ANY WARRANTIES ARISING OUT OF COURSE OF DEALING OR USAGE OF TRADE. We make no warranty that the Services will meet your requirements or be available on an uninterrupted, secure, or error-free basis. We make no warranty regarding the quality, accuracy, timeliness, truthfulness, completeness or reliability of any Content.
<strong>8. Feedback.</strong> We welcome feedback, comments and suggestions for improvements to the Services ("<strong>Feedback</strong>"). You can submit Feedback by emailing us at <u>support@cobo.com</u>. You grant to us a non-exclusive, transferable, worldwide, perpetual, irrevocable, fully-paid, royalty-free license, with the right to sublicense, under any and all intellectual property rights that you own or control to use, copy, modify, create derivative works based upon and otherwise exploit the Feedback for any purpose.
<strong>9. Content Ownership, Responsibility and Removal.</strong>
<strong>(a)</strong> Definitions. For purposes of these Terms: (i) "<strong>Content</strong>" means text, logos, service marks, trade names, domain names, URLs and icons graphics, images, music, software, audio, video, works of authorship of any kind, and information or other materials that are posted, generated, provided or otherwise made available through the Services; and (ii) "<strong>User Content</strong>" means any Content that Account holders (including you) provide to be made available through the Services. Content includes without limitation User Content.
<strong>(b)</strong> Our Content Ownership <strong>.</strong> Cobo does not claim any ownership rights in any User Content and nothing in these Terms will be deemed to restrict any rights that you may have to use and exploit your User Content. Subject to the foregoing, Cobo and its licensors exclusively own all right, title and interest in and to the Services and Content, including all associated intellectual property rights. You acknowledge that the Services and Content are protected by copyright, trademark, and other laws of the United States and foreign countries. You agree not to remove, alter or obscure any copyright, trademark, service mark or other proprietary rights notices incorporated in or accompanying the Services or Content.
<strong>(c)</strong> Rights in User Content Granted by You. By making any User Content available through the Services you hereby grant to Cobo a non-exclusive, perpetual, transferable, worldwide, royalty-free license, with the right to sublicense, to use, copy, modify, distribute, publicly display, publicly perform, distribute, and if applicable, create derivative works based upon your User Content in connection with operating and providing the Services and Content to you and to other Account holders. You agree that we can use any User Content to help us to analyze and improve the Services, and that we may aggregate and anonymize User Content, including pricing information, and own such resulting information and use it for any lawful purpose, including disclosing it to third parties.
<strong>(d)</strong> Your Responsibility for User Content. You are solely responsible for all your User Content. You represent and warrant that you own all your User Content or you have all rights that are necessary to grant us the license rights in your User Content under these Terms. You also represent and warrant that neither your User Content, nor your use and provision of your User Content to be made available through the Services, nor any use of your User Content by Cobo on or through the Services will infringe, misappropriate or violate a third party's intellectual property rights, or rights of publicity or privacy, or result in the violation of any applicable law or regulation.
<strong>(e)</strong> Removal of User Content. You can remove your User Content by specifically deleting it. However, in certain instances, some of your User Content (such as posts or comments you make) may not be completely removed and copies of your User Content may continue to exist on the Services. We are not responsible or liable for the removal or deletion of (or the failure to remove or delete) any of your User Content.
<strong>(f)</strong> Rights in Content Granted by Cobo. Subject to your compliance with these Terms, Cobo grants to you, a limited, non-exclusive, non-transferable license, with no right to sublicense, to download, view, copy, display and print the Content solely in connection with your permitted use of the Services and solely for your personal and non-commercial purposes. For clarity, this license will immediately terminate upon any termination or expiration of these Terms.
<strong>10. Claims of Infringement</strong>
<strong>(a)</strong> Cobo respects the intellectual property of others and requires that you do the same. In accordance with the Digital Millennium Copyright Act ("DMCA"), the text of which may be found on the United States Copyright Office website located at <u>http://www.copyright.gov/legislation/dmca.pdf</u>, Cobo will respond expeditiously to notices of alleged copyright infringement that are duly reported to its Designated Copyright Agent identified in the notice below. If you believe your content has been copied in a way that constitutes copyright infringement, or your intellectual property rights have been otherwise violated, please provide Cobo's Copyright Agent the following information:
<strong>(i)</strong> Identify the copyrighted work that you claim has been infringed, or—if multiple works are covered by this Notice—you may provide a representative list of the copyrighted works that you claim have been infringed.
<strong>(ii)</strong> Identify the material or link you claim is infringing and provide a description of where the infringing work is located on this Website.
<strong>(iii)</strong> Provide your mailing address, telephone number and, if available, email address.
<strong>(iv)</strong> Include both of the following statements in the body of the Notice:
<strong>(1)</strong> I hereby state that I have a good-faith belief that the disputed use of the copyrighted material is not authorized by the copyright owner, its agent, or the law (e.g., fair use).
<strong>(2)</strong> I hereby state that the information in this Notice is accurate and, under penalty of perjury, that I am the owner or authorized to act on behalf of the owner, of the copyright or of an exclusive right under the copyright that is allegedly infringed.
<strong>(v)</strong> Provide your full legal name and your electronic or physical signature.
<strong>(b)</strong> Deliver this Notice, with all items completed, to Cobo to:
COBO GLOBAL HK LTD.
ATTN: COPYRIGHT AGENT
FLAT/AM A 20/F KIU FU COMMERCIAL BLDG,
300 LOCKHART ROAD,
WAN CHAI,
HONG KONG
<strong>(c)</strong> While Cobo considers all such notices seriously, you may be liable for damages (including costs and attorneys' fees) if you materially misrepresent that content or activity is infringing. If you are uncertain whether material infringes your copyrights (including whether use of copyrighted material may constitute fair use) you may wish to seek the advice of an attorney.
<strong>11. Rights and Terms for Apps.</strong>
<strong>(a)</strong> Rights in App Granted by Cobo. Subject to your compliance with these Terms, Cobo grants to you a limited non-exclusive, non-transferable license, with no right to sublicense, to download and install a copy of the App on a mobile device or computer that you own or control and to run such copy of the App solely in connection for use with the Wallet. You may not copy the App, except for making a reasonable number of copies for backup or archival purposes. Except as expressly permitted in these Terms, you may not: (i) copy, modify or create derivative works based on the App; (ii) distribute, transfer, sublicense, lease, lend or rent the App to any third party; (iii) reverse engineer, decompile or disassemble the App; or (iv) make the functionality of the App available to multiple users through any means. Cobo reserves all rights in and to the App not expressly granted to you under these Terms.
<strong>(b)</strong> Accessing App from App Store. The following terms apply to any App accessed through or downloaded from any app store or distribution platform (like the Apple App Store or Google Play) where the App may now or in the future be made available (each an "<strong>App Provider</strong>"). You acknowledge and agree that:
<strong>(i)</strong> These Terms are concluded between you and Cobo, and not with the App Provider, and Cobo (not the App Provider), is solely responsible for the App.
<strong>(ii)</strong> The App Provider has no obligation to furnish any maintenance and support services with respect to the App.
<strong>(iii)</strong> In the event of any failure of the App to conform to any applicable warranty, you may notify the App Provider, and the App Provider will refund the purchase price for the App to you (if applicable) and, to the maximum extent permitted by applicable law, the App Provider will have no other warranty obligation whatsoever with respect to the App. Any other claims, losses, liabilities, damages, costs or expenses attributable to any failure to conform to any warranty will be the sole responsibility of Cobo.
<strong>(iv)</strong> The App Provider is not responsible for addressing any claims you have or any claims of any third party relating to the App or your possession and use of the App, including, but not limited to: (i) product liability claims; (ii) any claim that the App fails to conform to any applicable legal or regulatory requirement; and (iii) claims arising under consumer protection or similar legislation.
<strong>(v)</strong> In the event of any third party claim that the App or your possession and use of that App infringes that third party's intellectual property rights, Cobo will be solely responsible for the investigation, defense, settlement and discharge of any such intellectual property infringement claim to the extent required by these Terms.
<strong>(vi)</strong> The App Provider, and its subsidiaries, are third-party beneficiaries of these Terms as related to your license to the App, and that, upon your acceptance of the Terms, the App Provider will have the right (and will be deemed to have accepted the right) to enforce these Terms as related to your license of the App against you as a third-party beneficiary thereof.
<strong>(vii)</strong> You represent and warrant that (i) you are not located in a country that is subject to a U.S. Government embargo, or that has been designated by the U.S. Government as a terrorist-supporting country; and (ii) you are not listed on any U.S. Government list of prohibited or restricted parties.
<strong>(viii)</strong> You must also comply with all applicable third party terms of service when using the App.
<strong>12. General Prohibitions and Cobo's Enforcement Rights</strong>. You agree not to do any of the following:
<strong>(a)</strong> Post, upload, publish, submit or transmit any Content that: (i) infringes, misappropriates or violates a third party's patent, copyright, trademark, trade secret, moral rights or other intellectual property rights, or rights of publicity or privacy; (ii) violates, or encourages any conduct that would violate, any applicable law or regulation or would give rise to civil liability; (iii) is fraudulent, false, misleading or deceptive; (iv) is defamatory, obscene, pornographic, vulgar or offensive; (v) promotes discrimination, bigotry, racism, hatred, harassment or harm against any individual or group; (vi) is violent or threatening or promotes violence or actions that are threatening to any person or entity; or (vii) promotes illegal or harmful activities or substances;
<strong>(b)</strong> Use, display, mirror or frame the Services or any individual element within the Services, Cobo's name, any Cobo trademark, logo or other proprietary information, or the layout and design of any page or form contained on a page, without Cobo's express written consent;
<strong>(c)</strong> Access, tamper with, or use non-public areas of the Services, Cobo's computer systems, or the technical delivery systems of Cobo's providers;
<strong>(d)</strong> Attempt to probe, scan or test the vulnerability of any Cobo system or network or breach any security or authentication measures;
<strong>(e)</strong> Avoid, bypass, remove, deactivate, impair, descramble or otherwise circumvent any technological measure implemented by Cobo or any of Cobo's providers or any other third party (including another user) to protect the Services or Content;
<strong>(f)</strong> Attempt to access or search the Services or Content or download Content from the Services through the use of any engine, software, tool, agent, device or mechanism (including spiders, robots, crawlers, data mining tools or the like) other than the software and/or search agents provided by Cobo or other generally available third-party web browsers;
<strong>(g)</strong> Send any unsolicited or unauthorized advertising, promotional materials, email, junk mail, spam, chain letters or other form of solicitation;
<strong>(h)</strong> Use any meta tags or other hidden text or metadata utilizing a Cobo trademark, logo URL or product name without Cobo's express written consent;
<strong>(i)</strong> Use the Services or Content, or any portion thereof, for any commercial purpose or for the benefit of any third party or in any manner not permitted by these Terms;
<strong>(j)</strong> Forge any TCP/IP packet header or any part of the header information in any email or newsgroup posting, or in any way use the Services or Content to send altered, deceptive or false source-identifying information;
<strong>(k)</strong> Attempt to decipher, decompile, disassemble or reverse engineer any of the software used to provide the Services or Content;
<strong>(l)</strong> Interfere with, or attempt to interfere with, the access of any user, host or network, including, without limitation, sending a virus, overloading, flooding, spamming, or mail-bombing the Services;
<strong>(m)</strong> Collect or store any personally identifiable information from the Services from other users of the Services without their express permission;
<strong>(n)</strong> Impersonate or misrepresent your affiliation with any person or entity;
<strong>(o)</strong> Violate any applicable law or regulation; or
<strong>(p)</strong> Encourage or enable any other individual to do any of the foregoing.
Although we're not obligated to monitor access to or use of the Services or Content or to review or edit any Content, we have the right to do so for the purpose of operating the Services, to ensure compliance with these Terms and to comply with applicable law or other legal requirements. We reserve the right, but are not obligated, to remove or disable access to any Content, at any time and without notice, including, but not limited to, if we, at our sole discretion, consider any Content to be objectionable or in violation of these Terms. We have the right to investigate violations of these Terms or conduct that affects the Services. We may also consult and cooperate with law enforcement authorities to prosecute users who violate the law.
<strong>13. Links to Third Party Websites or Resources.</strong> The Services (including the App) may contain links to third-party websites or resources. We provide these links only as a convenience and are not responsible for the content, products or services on or available from those websites or resources or links displayed on such websites. You acknowledge sole responsibility for and assume all risk arising from, your use of any third-party websites or resources.
<strong>14. Termination.</strong> We may terminate your access to and use of the Services, at our sole discretion, at any time and without notice to you. You may cancel your Account at any time by following the instructions noted in the Services.
<strong>15. Indemnity.</strong> You will indemnify and hold harmless Cobo and its officers, directors, employees and agents, from and against any claims, disputes, demands, liabilities, damages, losses, and costs and expenses, including, without limitation, reasonable legal and accounting fees arising out of or in any way connected with (i) your access to or use of the Services, Hardware or Content, (ii) your User Content, or (iii) your violation of these Terms.
<strong>16. Limitation of Liability.</strong>
<strong>(a)</strong> NEITHER COBO NOR ANY OTHER PARTY INVOLVED IN CREATING, PRODUCING, OR DELIVERING THE SERVICES, HARDWARE OR CONTENT WILL BE LIABLE FOR ANY INCIDENTAL, SPECIAL, EXEMPLARY OR CONSEQUENTIAL DAMAGES, OR DAMAGES FOR LOST PROFITS, LOST REVENUES, LOST SAVINGS, LOST BUSINESS OPPORTUNITY, LOSS OF DATA OR GOODWILL, SERVICE INTERRUPTION, COMPUTER DAMAGE OR SYSTEM FAILURE OR THE COST OF SUBSTITUTE SERVICES OF ANY KIND ARISING OUT OF OR IN CONNECTION WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE THE SERVICES OR CONTENT, WHETHER BASED ON WARRANTY, CONTRACT, TORT (INCLUDING NEGLIGENCE), PRODUCT LIABILITY OR ANY OTHER LEGAL THEORY, AND WHETHER OR NOT COBO OR ANY OTHER PARTY HAS BEEN INFORMED OF THE POSSIBILITY OF SUCH DAMAGE, EVEN IF A LIMITED REMEDY SET FORTH HEREIN IS FOUND TO HAVE FAILED OF ITS ESSENTIAL PURPOSE. SOME JURISDICTIONS DO NOT ALLOW THE EXCLUSION OR LIMITATION OF LIABILITY FOR CONSEQUENTIAL OR INCIDENTAL DAMAGES, SO THE ABOVE LIMITATION MAY NOT APPLY TO YOU.
<strong>(b)</strong> IN NO EVENT WILL COBO'S TOTAL LIABILITY ARISING OUT OF OR IN CONNECTION WITH THESE TERMS OR FROM THE USE OF OR INABILITY TO USE THE SERVICES OR CONTENT EXCEED THE AMOUNTS YOU HAVE PAID TO COBO FOR USE OF THE SERVICES OR CONTENT OR ONE HUNDRED DOLLARS (100), IF YOU HAVE NOT HAD ANY PAYMENT OBLIGATIONS TO COBO, AS APPLICABLE.
<strong>(c)</strong> THE EXCLUSIONS AND LIMITATIONS OF DAMAGES SET FORTH ABOVE ARE FUNDAMENTAL ELEMENTS OF THE BASIS OF THE BARGAIN BETWEEN COBO AND YOU.
<strong>17. Governing Law and Forum Choice.</strong> These Terms and any action related thereto will be governed by the Federal Arbitration Act, federal arbitration law, and the laws of the Hong Kong Special Administrative Region of the People's Republic of China, without regard to its conflict of laws provisions. Except as otherwise expressly set forth in the "Dispute Resolution" section below, the exclusive jurisdiction for all Disputes (defined below) that you and Cobo are not required to arbitrate will be a court of competent jurisdiction located in Hong Kong, and you and Cobo each waive any objection to jurisdiction and venue in such courts. Notwithstanding the above, if you are a citizen of the People's Republic of China and are located outside the United States, you consent that any dispute arising from or in connection with these Terms and Conditions shall be submitted to China International Economic and Trade Arbitration Commission (CIETAC) for arbitration, which shall be conducted in Beijing in accordance with the CIETAC's arbitration rules in effect at the time of applying for arbitration.
<strong>18. Dispute Resolution.</strong> The following terms of this "Dispute Resolution" section only apply if you are an individual who is using the Services and Content for your own personal use and are not representing a legal entity.
<strong>(a)</strong> Mandatory Arbitration of Disputes. We each agree that any dispute, claim or controversy arising out of or relating to these Terms or the breach, termination, enforcement, interpretation or validity thereof or the use of the Services or Content (collectively, "<strong>Disputes</strong>") will be resolved <strong>solely by binding, individual arbitration and not in a class, representative or consolidated action or proceeding</strong>. However, if for any reason a Dispute proceeds in court rather than in arbitration, <strong>you and we each waive any right to a jury trial.</strong>
<strong>(b)</strong> Exceptions and Opt-out. As limited exceptions to Section 19(a) above: (i) you may seek to resolve a Dispute in small claims court if it qualifies; and (ii) we each retain the right to seek injunctive or other equitable relief from a court to prevent (or enjoin) the infringement or misappropriation of our intellectual property rights. In addition, <strong>you will retain the right to opt out of arbitration entirely and litigate any Dispute,</strong> if you provide us with written notice of your desire to do soby email at <u>support@cobo.com</u> or by regular mail at [FLAT/AM A 20/F KIU FU COMMERCIAL BLDG, 300 LOCKHART ROAD, WAN CHAI, HONG KONG] within thirty (30) days following the date you first agree to these Terms.
<strong>(c)</strong> Starting Arbitration. If you want to begin arbitrating a Dispute, you must send a letter to us at the following address [FLAT/AM A 20/F KIU FU COMMERCIAL BLDG, 300 LOCKHART ROAD, WAN CHAI, HONG KONG] requesting arbitration and describing the Dispute. If we want to begin arbitrating a Dispute, we'll send such a letter to you at the email address or street address that you provided.
<strong>(d)</strong> Conducting Arbitration and Arbitration Rules. The arbitration will be conducted by the American Arbitration Association ("<strong>AAA</strong>") under its Consumer Arbitration Rules (the "<strong>AAA Rules</strong>") or a comparable arbitral body (e.g., JAMS), in the event the AAA is unable to conduct the arbitration). The AAA Rules are available at <u>www.adr.org</u> or by calling 1-800-778-7879. The arbitration may be conducted in writing, remotely (e.g., by videoconference) or in-person in the county where you live (or at some other location that we both agree to).
<strong>(e)</strong> Arbitration Costs. Payment of all filing, administration and arbitrator fees will be governed by the AAA Rules. We'll pay for all filing, administration and arbitrator fees and expenses if your Dispute is for less than $10,000 USD, unless the arbitrator finds your Dispute frivolous. If we prevail in arbitration we'll pay all of our attorneys' fees and costs and won't seek to recover them from you. If you prevail in arbitration you will be entitled to an award of attorneys' fees and expenses to the extent provided under applicable law.
<strong>(f)</strong> Class Action Waiver. <strong>YOU AND COBO AGREE THAT EACH MAY BRING CLAIMS AGAINST THE OTHER ONLY IN YOUR OR ITS INDIVIDUAL CAPACITY, AND NOT AS A PLAINTIFF OR CLASS MEMBER IN ANY PURPORTED CLASS OR REPRESENTATIVE PROCEEDING</strong>. Further, if the parties' dispute is resolved through arbitration, the arbitrator may not consolidate another person's claims with your claims, and may not otherwise preside over any form of a representative or class proceeding. If this specific provision is found to be unenforceable, then the entirety of this Dispute Resolution section shall be null and void.
<strong>(g)</strong> Effect of Changes on Arbitration. Notwithstanding the provisions of "Changes to Terms or Services" section above, if Cobo changes any of the terms of this "Dispute Resolution" section after the date you first accepted these Terms (or accepted any subsequent changes to these Terms), you may reject any such change by sending us written notice (including by email to <u>support@cobo.com</u>) within 30 days of the date such change became effective, as indicated in the "Last Updated" date above or in the date of Cobo s email to you notifying you of such change. By rejecting any change, you are agreeing that you will arbitrate any Dispute between you and Cobo in accordance with the terms of this "Dispute Resolution" section as of the date you first accepted these Terms (or accepted any subsequent changes to these Terms).
<strong>19. General Terms.</strong>
<strong>(a)</strong> Entire Agreement. These Terms constitute the entire and exclusive understanding and agreement between Cobo and you regarding the Services and Content, and these Terms supersede and replace any and all prior oral or written understandings or agreements between Cobo and you regarding the Services and Content. With the exception of any provisions in the "Class Action Waiver" section of these Terms, if any provision of these Terms is held invalid or unenforceable by an arbitrator or a court of competent jurisdiction, that provision will be enforced to the maximum extent permissible and the other provisions of these Terms will remain in full force and effect. You may not assign or transfer these Terms, by operation of law or otherwise, without Cobo's prior written consent. Any attempt by you to assign or transfer these Terms, without such consent, will be null. Cobo may freely assign or transfer these Terms without restriction. Subject to the foregoing, these Terms will bind and inure to the benefit of the parties, their successors and permitted assigns.
<strong>(b)</strong> Survival. Upon any termination, discontinuation or cancellation of the Services or your Account, the following Sections will survive: "Hardware, Wallet and Description of Services" (as to the "Passwords" and "Sole Responsibility" subsections); "Content Ownership, Responsibility and Removal (as to the "Our Content Ownership", "Rights in User Content Granted by You", "Your Responsibility for User Content", and "Removal of User Content" subsections) "Disclaimers", "Feedback", "Indemnity", "Limitation of Liability", "Governing Law and Forum Choice", "Dispute Resolution", and "General Terms".
<strong>(c)</strong> Notices. Any notices or other communications provided by Cobo under these Terms, including those regarding modifications to these Terms, will be given: (i) via email; or (ii) by posting to the Services. For notices made by e-mail, the date of receipt will be deemed the date on which such notice is transmitted.
<strong>(d)</strong> Waiver of Rights. Cobo's failure to enforce any right or provision of these Terms will not be considered a waiver of such right or provision. The waiver of any such right or provision will be effective only if in writing and signed by a duly authorized representative of Cobo. Except as expressly set forth in these Terms, the exercise by either party of any of its remedies under these Terms will be without prejudice to its other remedies under these Terms or otherwise.
<strong>20. Contact Information.</strong> If you have any questions about these Terms or the Services, please contact Cobo at <u>support@cobo.com</u>.
</html>

152
app/src/main/assets/en_privacy_policy.html

@ -0,0 +1,152 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>Cobo Vault Privacy Policy</strong>
<strong>Effective Date of Privacy Policy: [September 15, 2018]</strong>
<strong>1. PURPOSE</strong>
This Privacy Policy is incorporated by reference into the Cobo Vault Terms of Service (the "<strong>Terms</strong>"). The terms "Cobo Vault," "Cobo," "we," and "us" include Cobo Global HK Limited and our affiliates and subsidiaries. This Privacy Policy explains our information practices, the kinds of Personal Information we may collect, how we intend to use and share that information, and how you can opt-out of a use or correct or change such information. All other terms not defined in Section 12 or otherwise herein will have the meanings set forth in the Terms.
<strong>2. SCOPE</strong>
This Privacy Policy applies to Personal Information that is Processed by Cobo Vault in the course of operating our Cobo Vault mobile application (the "<strong>App</strong>") and the Cobo Vault hardware (collectively, the "<strong>Services</strong>"). This Privacy Policy does not apply to any information collected on Cobo's website or when you purchase the Cobo Vault hardware from a Cobo or third party website. That information is governed by the Privacy Policy of such website when you initially provide the information.
<strong>3. TRANSPARENCY/NOTICE—TYPES OF PERSONAL INFORMATION WE COLLECT AND HOW WE USE IT</strong>
<strong>3.1 TYPES OF PERSONAL INFORMATION WE COLLECT</strong>
Cobo Vault collects limited amounts of information in the following circumstances:
<ul>
<li><strong>Automatic Data Collection</strong>. We may collect certain information automatically through our Services, such as the destination address where you send cryptocurrency, the amount of cryptocurrency you send, your Internet protocol (IP) address, mobile carrier, mobile advertising or other mobile device identifiers, the serial number of a hardware device you use via the Services, MAC address, IMEI, language, geo-location information, hardware type, operating system, Internet service provider, pages that you visit or events you engage in using the Services, the date and time of your use of the Services, the amount of time you spend in different areas of the Services, information about the links you click and pages you view within the Services, and other actions taken through use of the Services.</li>
<li><strong>Communications with Us</strong>. We may collect Personal Information from you such as email address, phone number or mailing address when you choose to request information about our Services, request to receive customer or technical support, or otherwise proactively communicate with us.</li>
</ul>
<strong>3.2 HOW COBO VAULT USES YOUR INFORMATION</strong>
We Process Personal Information about Individuals for a variety of business purposes, including:
<ul>
<li><strong>To Provide the Services</strong>. Cobo Vault may use the information it collects to provide the Services.</li>
<li><strong>Administrative Purposes</strong>. Cobo Vault may use Personal Information about you for its administrative purposes, including to:
<ul>
<li>Measure interest in Cobo Vault's Services;</li>
<li>Develop new products and Services;</li>
<li>Ensure internal quality control;</li>
<li>Verify Individual identity;</li>
<li>Process transactions;</li>
<li>Prevent potentially prohibited or illegal activities;</li>
<li>Enforce our Terms.</li>
</ul>
</li>
<li><strong>Research and Development</strong>. Cobo Vault may use the information it collects to create non-identifiable information that we may use alone or in the aggregate with information obtained from other sources, in order to help us to optimally deliver our existing products and Services or develop new products and Services. We may share de-identified Individual and aggregate data for research and analysis purposes.</li>
<li><strong>De-identified and Aggregated Information Use</strong>. Cobo Vault may use information it collects from your use of the Services to create de-identified and aggregated information, such as de-identified demographic information, de-identified location information, information about the device from which you access Cobo Vault's Services, or other analyses we create. De-identified and aggregated information is used for a variety of functions, including the measurement of visitors' interest in and use of various portions or features of the Services. De-identified or aggregated information is not Personal Information, and Cobo Vault may use such information in a number of ways, including research, internal analysis, analytics, and any other legally permissible purposes. We may share this information within Cobo Vault and with Third Parties for our or their purposes in de-identified or aggregated form that is designed to prevent anyone from identifying you.</li>
<li><strong>Other Uses</strong>. Cobo Vault may use Personal Information for which we have a legitimate interest, such as direct marketing, research (including marketing research), network and information security, fraud prevention, disclosure to affiliated organizations for administrative purposes, or any other purpose disclosed to you at the time you provide Personal Information or with your consent.</li>
</ul>
<strong>4. ONWARD TRANSFER—COBO VAULT MAY DISCLOSE YOUR INFORMATION</strong>
<strong>4.1 INFORMATION WE SHARE</strong>
We may share your information as described in this Privacy Policy (e.g., with our Third-Party service providers; to comply with legal obligations; to protect and defend our rights and property) or with your permission.
<ul>
<li><strong>We Use Vendors and Service Providers</strong>. We may share any information we receive with vendors and service providers. The types of service providers (processors) to whom we entrust Personal Information include service providers for: (i) provision of IT and related services; (ii) provision of information you have requested and/or the Services; (iii) payment processing; and (iv) customer service activities.</li>
<li><strong>Business Partners</strong>. Cobo Vault may share the information it collects with our business partners, and affiliates for our and our affiliates' internal business purposes or to provide you with a product or service that you have requested.</li>
<li><strong>Information Displayed on the Blockchain</strong>. When you transfer or receive cryptocurrency through the Services, transactional details such as the destination address and the amount of cryptocurrency may be publicly displayed on the blockchain. This information cannot be deleted and we are not responsible for the privacy practices of others who will view and use the posted information.</li>
<li><strong>Disclosures to Protect Us or Others (e.g., as Required by Law and Similar Disclosures)</strong>. We may access, preserve, and disclose the information we collect if we believe doing so is required or appropriate to: (i) comply with law enforcement or national security requests and legal process, such as a court order or subpoena; (ii) respond to your requests; (iii) protect yours', ours' or others' rights, property, or safety; (iv) to enforce Cobo Vault policies or contracts; (v) to collect amounts owed to Cobo Vault; (vi) when we believe disclosure is necessary or appropriate to prevent physical harm or financial loss or in connection with an investigation or prosecution of suspected or actual illegal activity; or (vii) if we, in good faith, believe that disclosure is otherwise necessary or advisable.</li>
<li><strong>Merger, Sale, or Other Asset Transfers</strong>. If we are involved in a merger, acquisition, financing due diligence, reorganization, bankruptcy, receivership, sale of company assets, or transition of service to another provider, then your information may be sold or transferred as part of such a transaction as permitted by law and/or contract. In such event, Cobo Vault will endeavor to direct the transferee to use the information we collect in a manner that is consistent with the Privacy Policy in effect at the time such information was collected.</li>
</ul>
<strong>4.2 INTERNATIONAL DATA TRANSFERS</strong>
You agree that all information collected via or by Cobo Vault may be transferred, processed, and stored anywhere in the world, including but not limited to, Hong Kong, the People's Republic of China, the United States, the European Union, in the cloud, on our servers, on the servers of our affiliates, or the servers of our service providers, in order to provide the Services.
<strong>5. OPT-OUT (RIGHT TO OBJECT TO PROCESSING)</strong>
<strong>5.1 GENERAL</strong>
You have the right to opt out of and object to certain uses and disclosures of your Personal Information. Where you have consented to Cobo Vault's Processing of your Personal Information or, you may withdraw that consent at any time and opt-out to further Processing by contacting us as described below.
<strong>5.2 MOBILE DEVICES</strong>
Cobo Vault may occasionally send you push notifications through our mobile applications with alerts and other notices that may be of interest to you. You may at any time opt-out from receiving these types of communications by changing the settings on your mobile device. Cobo Vault may also collect location-based information if you use our mobile applications. You may opt-out of this collection by changing the settings on your mobile device.
<strong>6. RIGHTS OF ACCESS, RECTIFICATION, ERASURE, AND RESTRICTION</strong>
We value your rights to your Personal Information. In accordance with applicable law, you may have the right to: (i) request confirmation of whether we are Processing your Personal Information; (ii) obtain access to or a copy of your Personal Information; (iii) receive an electronic copy of Personal Information that you have provided to us, or ask us to send that information to another company (the "right of data portability"); (iv) restrict our uses of your Personal Information or; (v) seek correction of inaccurate, untrue or incomplete Personal Information; or (vi) request erasure of Personal Information held about you by Cobo Vault, subject to certain exceptions prescribed by law. If you would like to exercise any of these rights, please contact us as set forth below.
We will process such requests in accordance with local laws. To protect your privacy, Cobo will take steps to verify your identity before fulfilling your request. Note that due to the nature of the Services, we may be unable to authenticate or link you to information we have, and therefore may not be able to fulfill your request.
<strong>7. DATA RETENTION</strong>
Cobo Vault retains the information we receive as described in this Privacy Policy for as long as you use our Services or as necessary to fulfill the purpose(s) for which it was collected, provide our Services, resolve disputes, establish legal defenses, conduct audits, pursue legitimate business purposes, enforce our agreements, and comply with applicable laws.
<strong>8. SECURITY OF YOUR INFORMATION</strong>
We take steps to ensure that your information is treated securely and in accordance with this Privacy Policy. Unfortunately, the Internet cannot be guaranteed to be 100% secure, and we cannot ensure or warrant the security of any information you provide to us. We do not accept liability for unintentional disclosure.
By using the Services or providing information to us, you agree that we may communicate with you electronically regarding security, privacy, and administrative issues relating to your use of the Services.
<strong>9. CHILDREN’S PRIVACY</strong>
The Services are not directed to children under 13 (and in certain jurisdictions under the age of 16) years of age, and Cobo Vault does not knowingly collect Personal Information from children under 13 (and in certain jurisdictions under the age of 16) years of age. If we learn that we have collected any Personal Information from children under 13 (and in certain jurisdictions under the age of 16), we will promptly take steps to delete such information and terminate that Individual's account.
<strong>10. CONTACT US</strong>
If you have any questions about our privacy practices or this Privacy Policy, please contact Cobo Vault by email at [support@cobo.com]. We will address your concerns and attempt to resolve any privacy issues in a timely manner.
<strong>11. OTHER RIGHTS AND IMPORTANT INFORMATION</strong>
<strong>11.1 CHANGES TO OUR PRIVACY POLICY AND PRACTICES</strong>
We may revise this Privacy Policy from time to time in our sole discretion. If there are any material changes to this Privacy Policy, Cobo Vault will notify you within the Services or as otherwise required by applicable law. You understand and agree that you will be deemed to have accepted the updated Privacy Policy if you use the Services after the updated Privacy Policy is posted on the Services. If at any point you do not agree to any portion of the Privacy Policy in effect, you must immediately stop using the Services.
<strong>11.2 CALIFORNIA PRIVACY RIGHTS</strong>
California law permits users who are California residents to request and obtain from us once a year, free of charge, a list of the Third Parties to whom we have disclosed their Personal Information (if any) for their direct marketing purposes in the prior calendar year, as well as the type of Personal Information disclosed to those parties. Cobo Vault does not share Personal Information with Third Parties for their own marketing purposes.
<strong>11.3 SUPERVISORY AUTHORITY</strong>
For users of our Services located in the European Economic Area, you have the right to lodge a complaint with a supervisory authority if you believe our Processing of your Personal Information violates applicable law.
<strong>12. DEFINITIONS</strong>
The following capitalized terms shall have the meanings herein as set forth below.
<ul>
<li>"<strong>Agent</strong>" means any Third Party that Processes Personal Information pursuant to the instructions of, and solely for, Cobo Vault or to which Cobo Vault discloses Personal Information for use on its behalf.</li>
<li>"<strong>Personal Information</strong>" is any information relating to an identified or identifiable natural person ("<strong>Individual</strong>").</li>
<li>"<strong>Process</strong>" or "<strong>Processing</strong>" means any operation which is performed upon Personal Information, whether or not by automatic means, such as collection, recording, organization, structuring, storage, adaptation or alteration, retrieval, consultation, use, disclosure by transmission, dissemination or otherwise making available, alignment or combination, restriction, erasure, or destruction.</li>
<li>"<strong>Third Party</strong>" is any company, natural or legal person, public authority, agency, or body other than the Individual, Cobo Vault or Cobo Vault's Agents.</li>
</ul>
<strong>13. REVISION HISTORY</strong>
<ul>
<li>Title | Effective Date</li>
<li>Cobo Vault Privacy Policy | Effective: 09/15/2018</li>
</ul>
</html>

45
app/src/main/assets/en_safety_instruction.html

@ -0,0 +1,45 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>CAUTION:</strong>
<strong>Read this section before using your device.</strong>
<strong>1.</strong>Cobo Vault is a hierarchical deterministic (HD) wallet complaint with BIP 32/39/44. It uses a Secure Element for true random number generation (TRNG), from which a master private key is derived. The master private key can be used to manage virtually unlimited pairs of private and public keys for different cryptocurrencies. Users can also import an existing recovery phrase into the Cobo Vault.
<strong>2.</strong> The master private key is encrypted and securely stored within the Secure Element. You will need to carefully write down the recovery phrase when it is generated during Cobo Vault initialization.
<strong>3.</strong>The recovery phrase enables final ownership of all digital assets under the master private key in the Cobo Vault. If it is lost or forgotten, all assets will be irretrievable.
<strong>4.</strong>After recording your recovery phrase upon initialization of the device, store it where it in a secure location and consider using Cobo Tablet to give it a high degree of permanence.
<strong>5.</strong>If the device is lost or broken, all digital assets under the master private key can be recovered with the recovery phrase on any device or software that supports the BIP 39 protocol.
<strong>6.</strong>The Hidden Vaults feature allows you to create separate, concealed Vaults that can be accessed with Passphrases. Once a Hidden Vault is created, its Passphrase cannot be changed. If the Passphrase (and/or recovery phrase) is forgotten or lost, the Hidden Vault and all of its assets will be irretrievable.
<strong>7.</strong>In addition to the recovery phrase and Passphrase, there is a system password, pattern unlock, and fingerprint sensor (Cobo Vault Pro only):
<strong>a.</strong>System password unlocks screen and signs transactions.
<strong>b.</strong>Pattern unlock unlocks screen and may be used to reset the system password.
<strong>c.</strong>Fingerprint sensor unlocks screen and signs transactions.
<strong>8.</strong>After 12 incorrect pattern unlock attempts, users will be required to unlock with the system password. After 5 incorrect system password attempts, the device will be wiped. If your device is wiped, your Cobo Vault can only be restored with the recovery phrase. The system password also can only be reset by entering the recovery phrase. If both the system password and recovery phrase are lost, all assets will be irretrievable. This is a preventative measure against theft.
<strong>9.</strong>Do not use the device if Web Authentication on the official website fails or if the system warns that the device may have been tampered with. Cobo Vault implements these measures to protect you from supply chain attacks.
<strong>10.</strong> The Cobo Vault Pro is equipped with an anti-tampering self-destruct mechanism. If the device is disassembled, the recovery phrase and master seed will automatically be wiped from the Secure Element. Due to hardware constraints, it is not guaranteed this mechanism will function as intended beyond 5 years of operation. Cobo Vault Pro will remain operational, but this security mechanism may not. We recommend replacing your device after 5 years of usage to ensure this mechanism is working properly.
<strong>Note:</strong> Cobo Vault Essential does not have this function.
</html>

59
app/src/main/assets/script/BCH.bundle.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/BTC.bundle.js

File diff suppressed because one or more lines are too long

30
app/src/main/assets/script/DASH.bundle.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/DCR.bundle.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/EOS.bundle_d0f384b2cc63ac78f25e.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/ETC.bundle_b0d7ccb0dc5138935dc7.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/ETH.bundle_b0d7ccb0dc5138935dc7.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/IOST.bundle_010b6f92f916b495faad.js

File diff suppressed because one or more lines are too long

30
app/src/main/assets/script/LTC.bundle.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/TRON.bundle.js

File diff suppressed because one or more lines are too long

38
app/src/main/assets/script/XRP.bundle.js

File diff suppressed because one or more lines are too long

2
app/src/main/assets/script/XZC.bundle.js

File diff suppressed because one or more lines are too long

30
app/src/main/assets/utils.bundle.js

File diff suppressed because one or more lines are too long

229
app/src/main/assets/zh_rCN_license.html

@ -0,0 +1,229 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>服务条款</strong>
<strong>更新日期:【2018年10月18日】</strong>
本条款涵盖您对Cobo Global HK Ltd.(以下称为“Cobo”)的应用和硬件的使用。请您通过Cobo网站 <u>https://cobo.com</u>(以下简称"<strong>本网站</strong>")仔细阅读以下服务条款(以下简称"<strong>本条款</strong>")及<strong>隐私政策</strong><u>https://webview.cobo.com/internal/vault/pp</u>)。您使用手机应用("应用")、钱包(定义见下文)及Cobo提供的与钱包及应用有关的任何设备(以下简称"<strong>硬件</strong>")时,适用于本条款及隐私政策。本条款及本条款中提及的其他协议对您和 Cobo 均具有约束力。为本条款之目的,本网站、应用、钱包、硬件及我们的其他相关服务合称为"<strong>本服务</strong>"。"<strong>虚拟货币</strong>"指基于区块链的货币,例如比特币。
<strong>1. 接受本条款。</strong> 当您使用我们的服务时,即视为您接受了本条款并受本条款约束。如果您不同意本条款,请勿使用本服务。如果您代表公司(如您的雇主)或其他实体访问及使用本服务,您承诺您有权代表该公司或实体受本条款的约束。在该种情形下,"您"应指您代表的公司或实体。本条款构成您与Cobo之间关于本服务的所有约定,并取代之前Cobo与您之间关于本服务的所有交流、协定及理解。
<strong>2. 隐私政策:</strong> 请浏览我们的隐私政策,了解我们怎样收集、使用及披露用户信息。您理解并同意您使用本服务受制于隐私政策的规定。
<strong>特别提请您注意:当您同意本条款时,即视为您同意通过仲裁而非诉讼解决您与 Cobo 之间的争议(例外情况除外)。请您仔细阅读下文 "争议解决" 条款了解仲裁的相关规定(包括拒绝仲裁的程序)。</strong>
<strong>3. 本条款或本服务的修改。</strong> 我们可能会不时修改本条款。如果我们修改了本条款,我们将通过在网站公布修改后的条款或其他方式使您知悉。请您注意频繁浏览本条款。如果您在本条款修改后仍继续使用本服务,即视为您接受了修改后的条款并受其约束。如果您不同意本条款的修改,除"争议解决"条款规定的以外,您应停止使用本服务。我们可能会在未通知您的情况下不时更改或停止某部分服务。
<strong>4. 哪些用户能使用本服务?</strong>
<strong>(a)</strong> 行为能力。您使用本服务时年龄应不小于18周岁并未被适用法律禁止使用本服务。
<strong>(b)</strong> 注册及信息。如果您使用服务的特定功能,您需要先创建一个账户("<strong>账户</strong>")。您可以通过本网站或第三方社交网站例如facebook或twitter(单独称为"<strong>SNS 账户</strong>")来创建账户。如果您选择通过SNS账户来创造账户,我们可能会从您的SNS账户中提取您的名字、邮箱地址以及您在SNS账户的隐私设置中允许被访问的其他个人信息。
<strong>(c)</strong> 账户信息准确性。您应在账户中向我们提供准确、完整、最新的信息,并应及时更新相应信息。如果您未按照上述要求提供信息,我们可能会暂停或终止您对账户的使用。您同意您不会向任何人泄露您的账户密码,并就他人未授权使用您账户的情形及时通知我们。无论您是否明知,您都应当对您账户下发生的所有活动负责。
<strong>5. 硬件、钱包及本服务描述。</strong>
<strong>(a)</strong> 一般规定。Cobo按照BIP44区块链协议的规定提供分级的、确定的虚拟货币钱包(以下简称"<strong>钱包</strong>")。该钱包由包含安全芯片的平板设备进行技术支持,该安全芯片可任意创造由24个字节组成的助记词(以下简称为"<strong>助记词</strong>")及主种子株,用以获得管理虚拟货币的私钥及公钥。
<strong>(b)</strong> 虚拟货币服务。请您注意钱包及任何其他服务是为了便于虚拟货币持有者与处理交易的潜在区块链网络进行联系,但钱包及任何其他服务本身不持有虚拟货币。您可以使用公钥接收及存储虚拟货币,使用私钥在您的账户以外发送虚拟货币。为完成虚拟货币交易,用户需在移动设备上下载应用、开启交易并生成供钱包扫描的二维码,钱包将通过提供私钥获得签名权限来完成交易(前提是用户必须掌控助记词及其他必要密码)。钱包在交易中签名后,会生成一个由移动端应用扫描的二维码,并将该交易通过网络在区块链进行公布。应用只能用于与钱包有关的用途。
<strong>(c)</strong> 密码。Cobo不会获取也不储存助记词及您使用钱包时创建的密码。您可以使用助记词在任何BIP44兼容的钱包上找回私钥。如果您丢失或忘记了您的助记词,您和Cobo都无法找回您的私钥。为采取反黑客措施,钱包与网络分离设计,但钱包本身无法防盗。
<strong>(d)</strong> 固件。为支持特定功能,如支持新的币种,您可以根据Cobo指示,在钱包上进行固件升级。
<strong>(e)</strong> 个人责任。您应自行为您的助记码、私钥和其他与钱包有关的密码安全负责。我们建议您采取适当的安全措施确保助记码、私钥及其他密码的安全,包括存放在金融机构的保管箱中。如您未采取安全措施,可能会导致您丢失虚拟货币。
<strong>6. 虚拟货币交易。</strong>
<strong>(a)</strong> 交易。为完成交易,所有拟进行的虚拟货币交易都必须在与虚拟货币网络有关的公共账上进行确认和记录。该等网络是由独立第三方支持的去中心化的、点对点的网络,非由Cobo所有、控制或运营。虚拟货币网络由独立第三方的去中心化网络运营。Cobo无法控制虚拟货币网络,因此无法确保您通过本服务提交的交易将会通过相关的虚拟货币网络被确认。您理解并同意您通过本服务提交的交易可能由于虚拟货币网络的原因为无法完成或延迟。
<strong>(b)</strong> 不储存或转让虚拟货币。虚拟货币是无形的数字资产,并于潜在的虚拟货币网络中记录其所有权。本服务本身不存储、发送或接收虚拟货币。虚拟货币的转让发生在去中心化的分类账中而非本服务中。我们无法保证本服务能够影响虚拟货币所有权或其他权利的转让。
<strong>(c)</strong> 无取消或更改。一旦您通过本服务向虚拟货币网络提交交易,该交易无法通过本服务取消或更改。Cobo对虚拟货币网络没有控制权,也无法提交取消或更改交易的请求。
<strong>7. 免责声明。</strong>
<strong>(a)</strong> 无密码找回服务。Cobo不接收或存储您的钱包密码、助记词或密钥及地址。因此,我们无法协助您找回钱包密码。我们的服务无法为您的钱包创造新密码。您应牢记您的钱包密码及助记词。如果您未能为您的钱包地址及助记词备份,您理解并同意,在未能提供钱包密码及/或助记词的情况下,您将无法访问与该等钱包地址相关联的虚拟货币。
<strong>(b)</strong> 服务中断。我们可能会在未通知您的情况下,自行决定修改、中断或终止我们的全部或部分服务。您应在服务器外部妥善存储、备份您的助记词、密钥或其他关联密码,以防在我们中断本服务时发生您未能通过钱包访问虚拟货币的情形。
<strong>(c)</strong> 关系。本条款的任何规定均不应被视为Cobo与您之间建立了任何合伙、合资、代理、咨询或信托关系,您和Cobo分别为独立的合同相对方。
<strong>(d)</strong> 信息准确性;自行担责。您承诺及保证,您通过本服务提供给我们的信息均为准确、完整的信息。您接受并理解,Cobo不对您在虚拟货币交易中产生的任何错误或疏忽负责,例如,您输入了错误的公钥或提供了不准确的信息。我们强烈建议您在通过本服务完成交易前,仔细确认交易信息。您在此接受并理解,您应对您的钱包账户下发生的任何活动负责,并应在法律允许的最大程度下,承担任何授权或未授权访问您钱包而产生的风险。
<strong>(e)</strong> 税费。您有责任判定您通过本服务提交的交易是否需要缴税并向税务机关缴纳应缴税费。您同意,Cobo没有责任判定您的虚拟货币交易是否需要缴税,也无需向税务机关代扣代缴由于虚拟货币交易而产生的税费。
<strong>(f)</strong> 免责。本服务、硬件及内容均以其原始状态向您提供,Cobo不对本服务、硬件及内容作出任何形式的承诺。在法律允许的最大程度下,我们不对由于在商业惯例或商业习惯中产生的任何默示的适销性承诺、符合特定目的承诺、稳定权承诺、不侵权承诺及其他承诺承担责任。我们不保证本服务能完全满足您的需求,也不对本服务的无干扰性、安全性或零失误性作出承诺。我们不对内容的质量、精确性、时间、真实性、完整性或可靠性作出任何承诺。
<strong>8. 反馈。</strong> 我们欢迎您对我们的服务提出任何意见或建议("<strong>反馈</strong>")。您可以通过发送邮件至<u>support@cobo.com</u> 来提交您的反馈。您在此不可撤销地无偿授予我们对于您提出的反馈进行转授权、复制、修改、制造衍生产品或其他的非排他的、可转让的、全球性的、永久性的知识产权相关权利。
<strong>9. 内容所有权、责任及移除权。</strong>
<strong>(a)</strong> 定义。为本条款之目的:(i)"<strong>内容</strong>"应指通过本服务显示、提供或可以访问的任何文本、商标、服务标识、商号、域名、网站链接、图标及图形、图像、音乐、软件、音频、视频、作品或其他信息、资料。(ii)"<strong>用户内容</strong>"应指账户持有人(包括您)通过本服务上传的内容。内容包括但不限于用户内容。
<strong>(b)</strong> 内容所有权。Cobo不拥有对任何用户内容的所有权,本条款的任何规定也不应被视为限制您使用和开发用户内容的权利。在不违反前述规定的前提下,Cobo及其许可方对于本服务及内容拥有所有权,包括与本服务及内容相关的知识产权。您同意本服务及内容受美国法及其余国家版权法、商标法及其他法律的保护。您不得对本服务或内容上载有的任何版权、商标、本服务标志或其他专有权利标记进行移除、更改或模糊处理。
<strong>(c)</strong> 用户内容授予。关于在使用本服务过程中您所提供的用户内容,您在此授予Cobo 拥有非独家、永久的、可转让的、全球范围内的免版税许可,同时Cobo拥有发布从属证书,使用、复制、修改、分发、公开展示、公开演播用户内容的权利。在Cobo运营及提供本服务时可基于用户内容衍生新的成果以为您及其他账户持有人所用(如适用)。您同意Cobo可以使用用户内容以帮助我们提高本服务水平,我们可能会聚合或隐匿部分用户内容,包括报价信息。我们可能会为任何合法用途使用结果信息,包括向第三方披露相关信息。
<strong>(d)</strong> 您对用户内容的责任。您只对您的用户内容负责。您承诺及保证:您拥有对用户内容的所有权,或您拥有为在本条款下向我们授予用户内容权利所必要的一切权利。您同时承诺及保证:您的用户内容,您通过本服务使用或提供用户内容、Cobo使用您的用户内容均不会侵犯第三方的知识产权、公开发表权、隐私权或违反其他适用的法律法规。
<strong>(e)</strong> 删除用户内容。您可以删除用户内容。但是,在某些情况下,您的某些用户内容(例如您的评论)可能不会被完全删除,您的用户内容副本也可能会继续存在于本服务中。我们不对任何用户内容的删除(或删除失败)承担责任。
<strong>(f)</strong> Cobo授予的内容权利。在您遵守本条款的前提下,Cobo将向您授予为使用本服务及非商业目的而下载、查看、复制、展示以及打印内容的非独占的、不可转让的许可权。为明确起见,本许可将在本条款终止或到期后立即终止。
<strong>10. 损害索赔。</strong>
<strong>(a)</strong> Cobo尊重他人的知识产权,并要求您也尊重他人的知识产权。根据《数学千年版权法案》("DMCA",其文本可通过登陆美国版权局网站 <u>http://www.copyright.gov/legislation/dmca.pdf</u> 查看), 对于报告给Cobo指定版权管理人的侵犯知识产权的指控,Cobo将会迅速做出回应。如果您认为您的内容上的知识产权被第三方侵犯,或您的其他知识产权权利受到了侵犯,请向Cobo指定版权管理人提供下列信息:
<strong>(i)</strong> 您声称被侵犯知识产权的作品,或——若通知中包含多个作品,您可以提供该等作品的代表清单。
<strong>(ii)</strong> 您声称侵权的材料或链接,并提供该侵权内容在网站的位置信息。
<strong>(iii)</strong> 您的邮箱地址、电话号码及收件地址(如有)。
<strong>(iv)</strong> 您的通知中应包含如下声明:
<strong>(1)</strong> 本人在此声明:本着诚实信用的原则,本人确信版权作品的使用未经版权所有人或其授权代表的授权或法律允许(例如合理使用)。
<strong>(2)</strong> 本人在此声明:本通知中的所有信息均为准确信息,本人为声称被侵权的版权所有人或版权所有人的授权代表人,并受伪证罪的约束。
<strong>(v)</strong> 您的全名及您的电子或手写签名。
<strong>(b)</strong> 您应当将含有上述完整信息的通知发至Cobo的下列地址:
COBO GLOBAL HK LTD.
收件人: COPYRIGHT AGENT
FLAT/AM A 20/F KIU FU COMMERCIAL BLDG,
300 LOCKHART ROAD,
WAN CHAI ,
HONG KONG
<strong>(c)</strong> 尽管Cobo将认真处理您发来的上述通知,如果您对侵权内容或活动有重大不实陈述,您可能会对因此造成的损害承担责任(包括由此产生的花费及律师费)。如果您不确定您的版权是否遭受侵害(包括对您版权的使用是否构成合理使用),您可以寻求律师的建议。
<strong>11. 应用权利及条款。</strong>
<strong>(a)</strong> Cobo在应用中授予您的权利。在您遵守本条款的前提下,Cobo授予您在移动设备或电脑上下载、安装及为使用钱包之目的运行应用副本的非独占、不可转让的许可权,但您无权转授权该等许可。除非为备份或存档目的制作合理数量的副本外,您不得复制应用。除非本条款明确允许,否则您不得:(i)基于应用复制、修改或创建衍生作品; (ii)将应用分发、转让、再许可、租赁、出借或出租给任何第三方; (iii)对应用程序进行逆向工程、反编译或反汇编; 或(iv)通过任何方式使应用的功能可供多个用户使用。 Cobo保留在本条款下未明确授予您的所有权利。
<strong>(b)</strong> 通过应用商店访问应用。以下条款适用于通过任何应用商店或分发平台(如Apple App Store或Google Play)(单独称为"<strong>应用程序提供商</strong>")访问或下载的应用程序。您理解并同意:
<strong>(i)</strong> 本条款由您和Cobo而非应用程序提供商之间签订,Cobo(而非应用程序提供商)为应用程序承担责任。
<strong>(ii)</strong> 应用程序提供商无义务提供与应用相关的任何维护和支持服务。
<strong>(iii)</strong> 如果应用未能达到任何适用的承诺保证的要求,您可以通知应用程序提供商,应用程序提供商将把您购买应用的费用退还给您,在法律允许的最大程度下,应用程序提供商将不作出其他任何有关应用的陈述保证。其他任何由于违反陈述保证的索赔、损失、法律责任、损害、花费及开支将由Cobo独自承担。
<strong>(iv)</strong> 应用程序提供商不对任何您或第三方提出的有关应用的索赔承担责任,包括但不限于:(i)产品责任索赔;(ii)应用违反任何适用法律法规要求的索赔;及(iii)基于消费者保护法或其他类似法律法规的索赔。
<strong>(v)</strong> 在发生针对应用或您占有、使用应用侵犯第三方知识产权的第三方侵权索赔时,在本条款允许的情况下,Cobo将对由此产生的调查、辩护、解决争议及处理该等知识产权侵权诉讼等事宜独自承担责任。
<strong>(vi)</strong> 应用程序提供商及其分支机构为本条款下与您许可应用使用的权利有关的第三方受益人,您接受本条款后,应用程序提供商将有权(及将被视为接受了该权利)以第三方受益人的身份,执行与您许可应用使用的权利有关的条款。
<strong>(vii)</strong> 您在此承诺及保证:(i)您不位于美国禁止贸易清单中的国家,也未位于被美国政府指定为支持恐怖活动的国家;及(ii)您不位于任何处于美国政府禁止或限制清单中的国家。
<strong>(viii)</strong> 您在使用应用时也应当遵守所有适用的第三方服务条款。
<strong>12. 禁止条款及 Cobo 的执行权。</strong> 您在此同意不得从事下列行为:
<strong>(a)</strong> 发布、上传、公布、提交或传输任何含有内容的信息:(i)侵犯第三方的专利权、版权、商标权、商业秘密、精神权利或其他知识产权或公开发表权、隐私权;(ii)侵犯任何适用法律法规或引起任何民事责任;(iii)具有欺诈性、误导性或伪造信息;(iv)包含诽谤、淫秽、色情或粗俗信息;(v)煽动对任何个人或团体的歧视、偏见、种族主义、仇视、骚扰或伤害;(vi)含有对任何个人或组织的暴力、威胁信息;或(vii)煽动非法或其他有害活动或包含非法信息;
<strong>(b)</strong>未经Cobo事先书面同意,使用、展示、复制本服务中的任何单个元素、Cobo的名称、商标、标志或其他专有信息,或页面上包含的任何布局、设计;
<strong>(c)</strong>访问、篡改或使用本服务内的非公有信息、Cobo 的计算机系统或Cobo提供商的技术支持系统;
<strong>(d)</strong>试图探测、扫描或测试任何Cobo系统或网络的漏洞或违反任何安全或身份验证要求;
<strong>(e)</strong>规避、绕过、删除、停用、损害、解除或以其他方式规避 Cobo、Cobo 供应商或其他第三方(包括其他用户)为保护本服务或内容实施的任何技术措施;
<strong>(f)</strong>尝试通过除 Cobo 提供的软件和/或搜索商或其他可用的第三方网站浏览器之外的任何引擎、软件、工具、代理、设备或机制(包括网络蜘蛛、机器人、网络爬虫、数据挖掘工具或其他类似程序)来访问或搜索本服务或内容或从本服务中下载内容。
<strong>(g)</strong>发送任何未授权的广告、宣传材料、邮件、垃圾邮件、连环信或其他形式的招揽信息;
<strong>(h)</strong>未经 Cobo 书面同意,使用任何元标记或其他隐藏文本,或使用Cobo商标、标志或产品名称的元数据。
<strong>(i)</strong>出于任何商业目的或为任何第三方的利益,以本条款不允许的任何方式使用本服务或内容;
<strong>(j)</strong>在任何邮件或新闻组中伪造任何TCP/IP数据包报头,或使用本服务或内容发送更改的、欺骗性的或错误的源标识信息;
<strong>(k)</strong>尝试破译、反编译、反汇编或反向工程任何用于提供本服务或内容的软件;
<strong>(l)</strong>干扰或试图干扰任何用户,主机或网络的访问,包括但不限于发送病毒、重载、垃圾邮件或对本服务进行邮件轰炸;
<strong>(m)</strong>未经其他用户明确许可,收集或存储其他用户的个人身份信息;
<strong>(n)</strong>冒充或歪曲您与任何个人或实体的关系;
<strong>(o)</strong>违反任何适用的法律法规,或
<strong>(p)</strong>鼓励或允许任何其他个人从事上述任何行为。
尽管我们没有义务监控您访问或使用本服务或内容,或者审查或编辑任何内容,但我们有权为运营本服务行使上述行为,以使得本条款、适用法律或其他法律要求得到遵守。我们保留在任何时候删除或禁止您访问任何内容的权利,恕不另行通知,包括但不限于我们认为任何内容是禁止性的或涉嫌违反本条款的情况下。我们有权调查违反本条款或影响本服务的行为。我们可能会与执法机关协商和合作,以起诉违法用户。
<strong>13. 第三方网站或资源链接。</strong> 本服务(包括应用)可能会载有第三方网站或资源。我们仅为您使用方便而提供该等链接,并不对该等网站、资源或链接展示或提供的内容、产品或服务承担任何责任。您同意自行承担因使用第三方网站或链接引起的一切风险。
<strong>14. 终止。</strong> 我们可能会在未通知您的情况下随时终止您访问或使用本服务。您可以根据本服务中的指示随时取消您的帐户。
<strong>15. 赔偿。</strong> 对于Cobo由于下列事项引起的或与下列事项相关而实际遭受、蒙受或发生的或针对Cobo的管理人员、董事、雇员及代理提起的任何索赔、争议、要求、法律责任、损害、损失、费用和开支(包括但不限于合理的律师费及会计费),您应当向承担赔偿责任,为上述人员提供辩护并使其免受损害:(i)您访问或使用本服务、硬件或内容;(ii)您的用户内容,或(iii)您违反了本条款。
<strong>16. 责任限制。</strong>
<strong>(a) Cobo 及提供本服务、硬件或内容的其他方均不对任何由于本服务或内容引起的或与本服务或内容有关的任何附带损失、特殊损害、间接损害、利润损失、收入损失、储蓄损失、商业机会损失、数据损失、商业名誉受损、本服务中断、计算机损害、系统故障或与替代本服务相关的费用承担任何责任,而无论该等损失是基于承诺、合同、侵权(包括过失侵权)、产品责任或其他法律基础,也无论 Cobo 或其他第三方是否知悉该等损害发生的可能性。一些法域不允许对于间接损害或附带损害赔偿的豁免,因此上述责任限制条款可能不适用于您。</strong>
<strong>(b) 在任何情况下, Cobo 应向您承担的由于本条款、本服务或内容产生的或与本条款、本服务或内容有关的责任总计不应超过您由于使用本服务或内容而向 Cobo 支付的总价款,若您未向 Cobo 支付任何价款,则上述责任总计不应超过 100 美元。</strong>
<strong>(c)上述责任限制条款是您和 Cobo 订立本条款的基础。</strong>
<strong>17. 适用法律及管辖法院。</strong>本条款应适用于美国联邦仲裁法及香港特别行政区法律(不包括冲突法)。除本条款"争议解决"条款另有规定外,Cobo与您之间无法通过仲裁解决的争议均应由位于香港的有管辖权的法院管辖,您和Cobo在此放弃对于上述管辖法庭及法庭地的异议。尽管存在上述规定,若您为中华人民共和国的公民且并未在美国居住,您同意任何由本条款引起的或与本条款有关的争议均应提交中国国际经济贸易仲裁委员会,按其届时有效的仲裁规则在北京仲裁解决。
<strong>18. 争议解决。</strong> 下列争议解决条款仅适用于您为自身目的而使用本服务及内容的情形,不适用于您作为公司或其他法律实体的代表而使用本服务及内容的情形。
<strong>(a)</strong> 强制仲裁。我们和您在此一致同意,任何由于本条款产生的或与本条款有关的争议、索赔或冲突、与本条款违约、终止、执行、解释及有效性相关的争议或与使用本服务或内容有关的争议(合称为"<strong>争议</strong>"), <strong>均应通过单个仲裁员仲裁的方式而非集体诉讼、代表诉讼或合并诉讼等诉讼程序解决</strong> 。仲裁裁决为终局的,且对双方均具有约束力。若由于任何原因,上述争议通过诉讼而非仲裁解决, <strong>则我们和您在此明示放弃陪审团审判的权利</strong>
<strong>例外情形。</strong> 尽管存在上述第18(a)条的规定, (i)您可以在条件允许的情况下选择在小额诉讼法院解决争议;(ii)我们和您均保留向法庭寻求禁令或其他临时性救济的权利。除此之外,您可以决定拒绝以仲裁的方式解决争议,如果您选择拒绝以仲裁的方式解决争议,请于您接受本条款之日起三十(30)日内发送邮件至<u>support@cobo.com</u>通知我们,或发送快递至下列地址:FLAT/AM A 20/F KIU FU COMMERCIAL BLDG, 300 LOCKHART ROAD, WAN CHAI, HONG KONG。
<strong>(b)</strong> 启动仲裁。如您希望开启仲裁程序,请提前书面通知我们请求仲裁并列明争议事项,并将上述书面通知邮寄至下列地址:FLAT/AM A 20/F KIU FU COMMERCIAL BLDG, 300 LOCKHART ROAD, WAN CHAI, HONG KONG。如果我们希望开启仲裁程序,我们会向您提供给我们的邮箱或地址发出书面通知。
<strong>(c)</strong> 仲裁程序及仲裁规则。争议将提交美国仲裁协会,根据其消费仲裁规则("AAA规则")进行仲裁,或在美国仲裁协会无法进行仲裁的情况下,提交其他类似仲裁协会(例如JAMS)进行仲裁。您可登陆<u>www.adr.org</u> 或致电 1-800-778-7879 查看AAA规则。仲裁可以以书面形式、远程形式(例如,视频会议)或在您居住的城市或我们一致同意的其他地点以面对面形式进行。
<strong>(d)</strong> 仲裁费。AAA规则具体对仲裁费用作出了规定。若争议标的少于10,000美元,我们将承担所有的仲裁费用,但仲裁员判定争议无意义的情况除外。如果仲裁庭支持我们的仲裁请求,我们将会自行支付律师费并不向您进行追偿。如果仲裁庭支持您的仲裁请求,则在适用法律允许的情况下,您有权获得律师费及其他花费的补偿。
<strong>(e)</strong> 集体诉讼弃权。 <strong>您和 Cobo 在此同意,您仅以个人身份而非以集体诉讼代表身份提起诉讼。</strong> 除此之外,若通过仲裁解决争议,则仲裁员不会将您的仲裁请求与第三人的仲裁请求合并审理,也不会开启任何形式的代表人仲裁或集体仲裁。若本特别规定被认定为不可执行,则本条款的争议解决条款应被视为完全无效。
<strong>(f)</strong> 仲裁条款的修改。尽管存在"条款或服务更改"条款的规定,若Cobo在您接受了本条款(或接受了本条款的后续更新版本)后修改本"争议解决"条款,您可以在该修改生效之日(修改生效之日将于文首"更新日期"处显示,或以Cobo邮件通知您更改"争议解决"章节的日期为准)起30天内向我们发送书面通知(包括发送邮件至[support@cobo.com](support@cobo.com))拒绝该等修改。拒绝该等修改后,您同意您与Cobo之间的争议将通过您初次接受本条款(或接受本条款的后续更新版本)时"争议解决"章节规定的争议解决方式进行解决。
<strong>19. 一般条款</strong>
<strong>(a)</strong> 完整协议。本条款构成Cobo与您关于本服务及内容的所有合意,并取代先前所有与之相关的口头或书面沟通、协议。除本条款的"集体诉讼弃权"条款外,若本条款的任何其他条款被仲裁员或有管辖权的法庭认定为无效或不可执行,则该等条款应在法律允许的最大程度下进行执行,且其余条款应继续有效。未经Cobo事先书面同意,您不得转让您在本条款项下的权利或义务,否则任何转让权利或义务的行为均应视为无效。Cobo有权转让本条款项下的权利或义务。受限于上述规定,本条款应对双方及其继受人、受让人具有约束力。
<strong>(b)</strong> 继续有效。当本服务或您的账户终止、停止或取消时,下列条款应继续有效:"硬件、钱包及服务描述"(针对"密码"及"独立责任"条款);"内容所有权、责任及移除权"(针对"内容所有权"、"用户内容移除"条款);"免责条款"、"反馈"、"赔偿条款"、"责任限制"、"适用法律及管辖法院"、"争议解决"及"一般条款"。
<strong>(c)</strong> 通知。Cobo将通过下列方式发布有关本条款的通知:(i)发送邮件;(ii)在本服务中进行公告。对于通过邮件发送的通知,收到通知的时间应为通知发出的时间。
<strong>(d)</strong> 弃权。Cobo不执行本条款的任何权利或条款不应被视为Cobo对该等权利或条款的弃权。该等权利或条款的弃权仅应在Cobo授权代表书面签署弃权声明时方为有效。除本条款另有规定外,一方根据本条款对于某种救济权利的行使不影响其对其他救济权利的行使。
<strong>20. 联系信息。</strong> 如果您对本条款或本服务有任何疑问,请通过邮箱 <u>support@cobo.com</u> 联系我们。
</html>

138
app/src/main/assets/zh_rCN_privacy_policy.html

@ -0,0 +1,138 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>Cobo Vault隐私政策</strong>
<strong>隐私政策生效日:2018年9月15日</strong>
<strong>1. 目的</strong>
本隐私政策为Cobo Vault服务条款("<strong>服务条款</strong>")的一部分。"Cobo Vault"、 "Cobo"、"我们"指Cobo Global HK Limited及其关联方和分支机构。本隐私政策解释了我们使用信息的原则、我们可能收集的个人信息类型、我们如何使用和分享个人信息以及您如何选择拒绝我们收集个人信息或要求我们更正您的个人信息。本隐私政策第12条未定义的术语应与服务条款中的相同术语具有相同含义。
<strong>2. 范围</strong>
本隐私政策适用于Cobo Vault在您操作我们的Cobo Vault移动应用("<strong>应用</strong>")及Cobo Vault硬件(与"应用"合称为"<strong>服务</strong>")时处理的个人信息。本隐私政策不适用于在Cobo网站上收集的或在您从Cobo或其它第三方网站购买Cobo Vault硬件过程中收集到的任何信息,该等信息应由前述网站相应的隐私政策加以规定。
<strong>3. 透明化/通知——我们收集的个人信息类型及我们如何使用该等个人信息</strong>
<strong>3.1 我们收集的个人信息类型</strong>
Cobo Vault仅在下列情况收集下述个人信息:
<ul>
<li><strong>自动收集</strong> 。我们可能通过服务自动收集您的特定信息。例如您发送加密货币的目的地址、您发送加密货币的数额、您的IP地址、移动网络运营商、手机广告或其它移动设备标识信息、您通过服务使用的硬件设备序列号、MAC地址、国际移动设备识别码、语言、地理位置信息、硬件类型、操作系统、网络服务提供商、您使用服务时访问的网页或参与的事件、您使用服务的日期和时间、您使用不同服务的时间、您在服务中点击的链接信息及浏览的网页,以及您在使用服务中采取的其它行动。</li>
<li><strong>交流信息</strong> 。我们可能会在您向我们寻求服务信息、寻求用户支持或技术支持或者你主动与我们进行其它交流时收集您的个人信息,例如您的邮箱地址、电话号码或邮寄地址。</li>
</ul>
<strong>3.2 Cobo Vault如何使用您的个人信息</strong>
我们收集用户的个人信息用于商业目的,包括:
<ul>
<li><strong>提供服务</strong> 。Cobo Vault可能会为提供服务之目的,使用您的个人信息。
<li><strong>管理目的</strong> 。Cobo Vault可能会为便于管理之目的而使用个人信息,包括:
<ul>
<li>衡量用户对Cobo Vault服务的兴趣;</li>
<li>开发新产品和新服务;</li>
<li>确保内部质量控制;</li>
<li>验证个人身份;</li>
<li>处理交易;</li>
<li>防范禁止性或非法活动;</li>
<li>执行服务条款。</li>
</ul>
<li><strong>研究及开发</strong> 。Cobo Vault会将收集的个人信息转化为不可识别的信息,Cobo Vault可能会将这些不可识别的信息单独或与其它来源的信息一起,用于帮助我们提升现有产品及服务或开发新产品及新服务。我们可能会将不可识别的个人信息单独或集中用于调研及研究目的。</li>
<li><strong>不可识别信息及集中信息的使用</strong> 。Cobo Vault可能会将收集的信息转化为不可识别信息及集中信息,例如不可识别的人口统计信息、不可识别的位置信息、您访问Cobo Vault服务时的设备信息或我们转化的其它分解信息。不可识别信息及集中信息可为多种目的而使用,包括衡量访问者对于服务的兴趣及使用。不可识别信息及集中信息不属于个人信息,Cobo Vault可能会将该等信息用于不同用途,包括调研、内部分析、解析及法律允许的其它用途。我们可能会在Cobo Vault内部分享,或与第三人分享经过不可识别或集中化处理的信息,以防止任何人识别您的身份。</li>
<li><strong>其它用途。</strong> Cobo Vault可能会将个人信息用于其它合法用途,例如直接推广、调研(包括市场调研)、网络及信息安全、防欺诈、为便于管理之目的将信息披露给关联机构,或在您提供个人信息时披露给您的或经过您允许的其它用途。</li>
</ul>
<strong>4. 向外传送信息——Cobo Vault可能会披露您的信息</strong>
<strong>4.1 我们分享的信息</strong>
我们可能会根据本隐私政策的规定(例如,与我们的第三方服务提供商分享信息;为遵守法律规定分享信息;为保护我们的权利及财产而分享信息)或经您的允许而分享您的信息。
<ul>
<li><strong>供应商及服务提供商</strong> 。我们可能会与供应商及服务提供商分享信息。我们授权的分享个人信息的服务商类型有:(i)提供IT及关联服务的服务商;(ii)您请求提供的信息或服务的提供商;(iii)支付处理系统;及(iv)客户服务活动。</li>
<li><strong>商业伙伴</strong> 。Cobo Vault可能会为我们或关联方的商业目的,或为您提供产品或服务的目的,而与我们的商业伙伴及关联方分享信息。</li>
<li><strong>区块链展示的信息</strong> 。当您通过服务转让或接收加密货币时,您的交易信息例如目的地址及加密货币数额可能会在区块链公开展示。该等信息无法删除,我们不对他人查看及使用该等信息承担责任。</li>
<li><strong>为保护我们或他人权利进行披露</strong> (例如,依据法律要求进行披露)。我们会在认为必要或适当时,为下列情况之目的而访问、保存及披露我们收集的个人信息:(i)为执行法律的规定、国家安全需要或法律程序要求,例如法庭命令或传票;(ii)为响应您的要求;(iii)为保护您的、我们的或他人的权利、财产或安全;(iv)为执行Cobo Vault政策或合同;(v)为追回属于Cobo Vault的欠款;(vi) 我们认为必要或适当时,为防止物理损害或经济损失,或为调查或起诉;或者(vii)我们合理认为的披露信息的其它必要情况。</li>
<li><strong>合并、出售或其它资产转让</strong> 。如果我们涉及合并、并购、财务尽职调查、重组、破产、破产管理、出售公司资产或向其它服务提供商转让服务,您的信息可能会在法律和/或合同允许的情况下被出售或转让。在该等情况下,Cobo Vault将促使受让方按照本隐私政策的规定使用收集的信息。</li>
</ul>
<strong>4.2 跨国数据转让</strong>
您同意,为提供服务的目的,通过Cobo Vault收集的信息可能会在全球任何地方通过云端、我们或我们关联机构的服务器、我们服务提供商的服务器而被转让、处理或储存,包括但不限于香港、中国、美国、欧盟。
<strong>5. 拒绝权(拒绝处理信息的权利)</strong>
<strong>5.1 一般规定</strong>
您有权选择拒绝我们使用及披露您的个人信息。若您已经同意Cobo Vault处理您的个人信息,您可以在任何时候通过我们在后文提供的联系方式撤销该等同意并选择拒绝我们对您的信息进行进一步处理。
<strong>5.2 移动设备</strong>
Cobo Vault可能会通过您的移动设备向您不时发送推送通知及您感兴趣的其它通知。您可以通过更改移动设备的设置而选择拒绝接收该等信息。若您使用移动设备,Cobo Vault可能会收集您的地理位置信息。您也可以通过更改移动设备的设置而选择拒绝该等地理位置信息的收集。
<strong>6. 访问、更改、消除及限制权利</strong>
我们非常重视您的个人信息权利。根据适用法律,您享有以下权利:(i)要求确认我们是否在处理您的个人信息;(ii)获得您的个人信息的访问权及复制权;(iii)获得您向我们提供的个人信息的电子复印件,或要求我们向第三方发送该等信息("数据移植权");(iv)限制我们使用您的个人信息;(v)更正不准确、不真实或不完整的个人信息;或(vi)除法律另有规定外,要求Cobo Vault消除所持有的您的个人信息。若您希望行使上述权利,请通过后文所示联系方式联系我们。
我们将根据当地法律的规定处理上述请求。为保护您的隐私,Cobo将在执行您的请求前验证您的身份。请您注意,由于服务的性质,我们可能无法鉴定您与我们持有的信息的关联性,因此可能无法完成您的请求。
<strong>7. 信息保留</strong>
Cobo Vault将在您使用服务期间保留您的个人信息,为实现收集该等信息的目的之必要而保留您的个人信息,或为提供服务、解决争议、建立合法抗辩、开展审计、追求合法商业目的、执行我们与您之间的协议及遵守适用法律之目的而保留您的个人信息。
<strong>8. 您的信息安全</strong>
我们确保将按照本隐私政策的规定保护您的信息安全。然而,我们无法100%保证网络安全,我们也无法确保或承诺您提供给我们的信息安全。我们不对非故意的信息泄露承担责任。
通过使用服务或向我们提供信息,您同意我们可通过电子形式就您使用服务的安全、隐私及管理问题与您进行交流。
<strong>9. 儿童的隐私</strong>
服务不针对13岁以下的儿童(在特定管辖区内为16岁以下)开放。Cobo Vault不会在明知的情况下收集13岁以下的儿童(在特定管辖区内为16岁以下)的个人信息。若我们得知收集了任何13岁以下的儿童(在特定管辖区内为16岁以下)的个人信息,我们将立即删除该等信息并注销相应的个人账户。
<strong>10. 联系我们</strong>
若您对我们的隐私政策有任何问题,请通过<u>support@cobo.com</u>联系我们。我们将及时处理您的问题。
<strong>11. 其它权利及重要信息</strong>
<strong>11.1 隐私政策的更改</strong>
我们可能会不时更改本隐私政策。如果我们对本隐私政策做了任何实质性修改,Cobo Vault将在服务中或通过法律要求的其它途径通知您。您理解并同意,若您在隐私政策更新后选择继续使用服务,即视为您接受了更新的隐私政策。如您不同意届时生效的隐私政策,您必须立即停止使用服务。
<strong>11.2 加利福尼亚隐私权</strong>
加利福尼亚州法律允许加利福尼亚居民要求我们每年无偿提供一次我们为第三方的直接营销目的,而披露其个人信息的该等第三方名单(如有),以及我们披露的个人信息类型。Cobo Vault不会为第三方的营销目的而与其分享个人信息。
<strong>11.3 监管机构</strong>
若欧洲经济区的用户认为我们处理您的个人信息违反了适用法律,您有权向监管机构提出控告。
<strong>12. 定义</strong>
下列术语应具有如下含义:
<ul>
<li>"<strong>代理</strong>"应指根据Cobo Vault的指示,仅为Cobo Vault的利益而处理个人信息的第三方。</li>
<li>"<strong>个人信息</strong>"应指与可识别的自然人("<strong>个人</strong>")有关的任何信息。</li>
<li>"<strong>处理</strong>"应指对于个人信息的任何操作,无论是否为自动化处理,例如收集、记录、组织、构造、储存、改编、更改、恢复、咨询、使用、通过传送、传播或其它可行方式披露、校准或组合、限制、消除或毁坏。</li>
<li>"<strong>第三方</strong>"应指任何公司、自然人或法人、公共机构、代理或除个人、Cobo Vault或Cobo Vault的代理之外的其它机构。</li>
</ul>
<strong>13. 修订历史</strong>
<ul>
<li>标题 | 生效日</li>
<li>Cobo Vault隐私政策 | 生效日:09/15/2018</li>
</ul>
</html>

43
app/src/main/assets/zh_rCN_safety_instruction.html

@ -0,0 +1,43 @@
<!--
~ Copyright (c) 2020 Cobo
~
~ This program is free software: you can redistribute it and/or modify
~ it under the terms of the GNU General Public License as published by
~ the Free Software Foundation, either version 3 of the License, or
~ (at your option) any later version.
~
~ This program is distributed in the hope that it will be useful,
~ but WITHOUT ANY WARRANTY; without even the implied warranty of
~ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
~ GNU General Public License for more details.
~
~ You should have received a copy of the GNU General Public License
~ in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
-->
<html>
<strong>安全须知</strong>
<strong>1.</strong>Cobo金库采用分层确定性协议的解决方案,创建金库将通过加密芯片生成单一随机助记词及主私钥,并由此主私钥生成树状结构储存多组私钥和公钥,管理不同币种。用户也可以导入已有的遵循BIP39协议的助记词钱包到本金库使用。
<strong>2.</strong>用户的助记词及主私钥经过加密后,保存于Cobo金库安全芯片内,金库生成助记词后,请仔细抄写备份。
<strong>3.</strong>主私钥对Cobo金库内的数字资产拥有绝对控制权,一旦丢失,用户将永远无法恢复其中的资产。
<strong>4.</strong>初次创建金库时生成的助记词,为用户找回主私钥的唯一途径,请务必牢记并妥善保存。建议使用Cobo助记板备份,增加安全性。
<strong>5.</strong>如果Cobo金库遗失或损坏,可通过助记词在任何支持BIP39协议的设备或软件上恢复原Cobo金库内的全部数字资产。
<strong>6.</strong>Cobo金库支持创建基于密语的隐藏金库。用户在创建隐藏金库后,其对应的密语不可修改。如果密语(或原助记词)遗忘,隐藏金库将无法恢复,其中存储的资产也将无法取回。
<strong>7.</strong>除助记词和Cobo密语外,用户在使用Cobo金库过程中还会接触其他三类密码:
<strong>a.</strong>系统密码:用于解除屏幕锁定和签名。
<strong>b.</strong>手势密码:仅用于解除屏幕锁定,可以通过系统密码修改手势密码。
<strong>c.</strong>指纹密码(仅限专业版):用于解除屏幕锁定和签名
<strong>8.</strong>如手势密码连续12次输入错误,可通过系统密码解锁屏幕;如系统密码连续5次输入错误,金库将恢复出厂设置。一旦恢复出厂设置,用户只能通过导入助记词恢复。如果同时遗忘助记词和系统密码,资产将无法找回。
<strong>9.</strong>为保护您的数字资产安全,Cobo金库设计有多重安全验证及防护措施,当官网校验失败(详细使用请参见快速向导)或系统提示Cobo金库被攻击时,请勿继续使用该设备,以防供应链攻击。
<strong>10.</strong>为保护您的数字资产安全,Cobo金库专业版设计有拆机自毁机制,一旦拆机,安全芯片内的助记词及主私钥即被清空,以此确保您的资产安全。防拆电路有效工作年限为5年,防拆电路失效后不影响用户正常使用设备,但会降低设备安全性,建议用户适时更换新设备。
<strong>注明:</strong>Cobo金库标准版无此功能。
</html>

85
app/src/main/java/com/cobo/cold/AppExecutors.java

@ -0,0 +1,85 @@
/*
* Copyright (C) 2017 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package com.cobo.cold;
import android.os.Handler;
import android.os.Looper;
import androidx.annotation.NonNull;
import java.util.concurrent.Executor;
import java.util.concurrent.Executors;
/**
* Global executor pools for the whole application.
* <p>
* Grouping tasks like this avoids the effects of task starvation (e.g. disk reads don't wait behind
* webservice requests).
*/
public class AppExecutors {
private final Executor mDiskIO;
private final Executor mNetworkIO;
private final Executor mMainThread;
private volatile static AppExecutors appExecutors;
public static AppExecutors getInstance() {
if (appExecutors == null) {
synchronized (AppExecutors.class) {
if (appExecutors == null) {
appExecutors = new AppExecutors();
}
}
}
return appExecutors;
}
private AppExecutors(Executor diskIO, Executor networkIO, Executor mainThread) {
this.mDiskIO = diskIO;
this.mNetworkIO = networkIO;
this.mMainThread = mainThread;
}
private AppExecutors() {
this(Executors.newSingleThreadExecutor(), Executors.newFixedThreadPool(3),
new MainThreadExecutor());
}
public Executor diskIO() {
return mDiskIO;
}
public Executor networkIO() {
return mNetworkIO;
}
public Executor mainThread() {
return mMainThread;
}
private static class MainThreadExecutor implements Executor {
private final Handler mainThreadHandler = new Handler(Looper.getMainLooper());
@Override
public void execute(@NonNull Runnable command) {
mainThreadHandler.post(command);
}
}
}

202
app/src/main/java/com/cobo/cold/DataRepository.java

@ -0,0 +1,202 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold;
import android.content.Context;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MediatorLiveData;
import com.cobo.coinlib.utils.Coins;
import com.cobo.cold.db.AppDatabase;
import com.cobo.cold.db.entity.AccountEntity;
import com.cobo.cold.db.entity.AddressEntity;
import com.cobo.cold.db.entity.CoinEntity;
import com.cobo.cold.db.entity.TxEntity;
import com.cobo.cold.db.entity.WhiteListEntity;
import com.cobo.cold.model.Coin;
import java.util.Collections;
import java.util.List;
import java.util.stream.Collectors;
public class DataRepository {
private static DataRepository sInstance;
private final AppDatabase mDb;
private final MediatorLiveData<List<CoinEntity>> mObservableCoins;
private final Context context;
private DataRepository(Context context, final AppDatabase database) {
mDb = database;
this.context = context;
mObservableCoins = new MediatorLiveData<>();
mObservableCoins.addSource(mDb.coinDao().loadAllCoins(), coins -> {
if (mDb.getDatabaseCreated().getValue() != null) {
mObservableCoins.postValue(filterByBelongTo(coins));
}
});
}
public String getBelongTo() {
return Utilities.getCurrentBelongTo(context);
}
public static DataRepository getInstance(Context context, final AppDatabase database) {
if (sInstance == null) {
synchronized (DataRepository.class) {
if (sInstance == null) {
sInstance = new DataRepository(context, database);
}
}
}
return sInstance;
}
public LiveData<List<CoinEntity>> loadCoins() {
return mObservableCoins;
}
public LiveData<List<CoinEntity>> reloadCoins() {
MediatorLiveData<List<CoinEntity>> coins = new MediatorLiveData<>();
coins.addSource(mDb.coinDao().loadAllCoins(), coinEntities -> {
if (mDb.getDatabaseCreated().getValue() != null) {
coins.postValue(filterByBelongTo(coinEntities));
}
});
return coins;
}
public List<CoinEntity> loadCoinsSync() {
return filterByBelongTo(mDb.coinDao().loadAllCoinsSync());
}
public void updateCoin(Coin coin) {
mDb.coinDao().update(new CoinEntity(coin));
}
public void insertAddress(List<AddressEntity> addrs) {
mDb.addressDao().insertAll(addrs);
}
public void insertAddress(AddressEntity addrs) {
mDb.addressDao().insertAddress(addrs);
}
public LiveData<CoinEntity> loadCoin(final long id) {
return mDb.coinDao().loadCoin(id);
}
public CoinEntity loadCoinSync(final String coinId) {
return mDb.coinDao().loadCoinSync(coinId, getBelongTo());
}
public LiveData<List<AddressEntity>> loadAddress(String coinId) {
return mDb.addressDao().loadAddressForCoin(coinId, getBelongTo());
}
public List<AddressEntity> loadAddressSync(String coinId) {
return mDb.addressDao().loadAddressSync(coinId, getBelongTo());
}
public AddressEntity loadAddressBypath(String path) {
return mDb.addressDao().loadAddress(path.toUpperCase(), getBelongTo());
}
public void updateAddress(AddressEntity addressEntity) {
mDb.addressDao().update(addressEntity);
}
public LiveData<List<TxEntity>> loadTxs(String coinId) {
return mDb.txDao().loadTxs(coinId);
}
public LiveData<TxEntity> loadTx(String txId) {
return mDb.txDao().load(txId);
}
public TxEntity loadTxSync(String txId) {
return mDb.txDao().loadSync(txId);
}
public void insertTx(TxEntity tx) {
mDb.txDao().insert(tx);
}
public void insertCoins(List<CoinEntity> coins) {
mDb.runInTransaction(() -> mDb.coinDao().insertAll(coins));
}
public long insertCoin(CoinEntity coin) {
return mDb.coinDao().insert(coin);
}
public void clearDb() {
mDb.clearAllTables();
}
private List<CoinEntity> filterByBelongTo(List<CoinEntity> coins) {
String belongTo = Utilities.getCurrentBelongTo(context);
return coins.isEmpty() ? Collections.emptyList()
:
coins.stream()
.filter(coin -> belongTo.equals(coin.getBelongTo()))
.collect(Collectors.toList());
}
public LiveData<List<WhiteListEntity>> loadWhiteList() {
return mDb.whiteListDao().load();
}
public void insertWhiteList(WhiteListEntity entity) {
mDb.whiteListDao().insert(entity);
}
public void deleteWhiteList(WhiteListEntity entity) {
mDb.whiteListDao().delete(entity);
}
public WhiteListEntity queryWhiteList(String address) {
return mDb.whiteListDao().queryAddress(address, Utilities.getCurrentBelongTo(context));
}
public void insertAccount(AccountEntity account) {
mDb.accountDao().add(account);
}
public void updateAccount(AccountEntity account) {
mDb.accountDao().update(account);
}
public List<AccountEntity> loadAccountsForCoin(CoinEntity coin) {
return mDb.accountDao().loadForCoin(coin.getId());
}
public CoinEntity loadCoinEntityByCoinCode(String coinCode) {
String coinId = Coins.coinIdFromCoinCode(coinCode);
return loadCoinSync(coinId);
}
public void deleteHiddenVaultData() {
mDb.coinDao().deleteHidden();
mDb.txDao().deleteHidden();
mDb.addressDao().deleteHidden();
mDb.whiteListDao().deleteHidden();
}
}

146
app/src/main/java/com/cobo/cold/MainApplication.java

@ -0,0 +1,146 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold;
import android.app.Activity;
import android.app.Application;
import android.content.BroadcastReceiver;
import android.content.Context;
import android.content.Intent;
import android.content.IntentFilter;
import android.os.Bundle;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import com.cobo.coinlib.v8.ScriptLoader;
import com.cobo.cold.db.AppDatabase;
import com.cobo.cold.encryption.EncryptionCoreProvider;
import com.cobo.cold.logging.FileLogger;
import com.cobo.cold.service.AttackCheckingService;
import com.cobo.cold.ui.MainActivity;
import com.cobo.cold.ui.UnlockActivity;
import com.cobo.cold.util.HashUtil;
import org.spongycastle.util.encoders.Hex;
import java.lang.ref.SoftReference;
public class MainApplication extends Application {
private static MainApplication sApplication;
private AppExecutors mAppExecutors;
private SoftReference<Activity> topActivity;
private boolean shouldLock;
public MainApplication() {
sApplication = this;
}
@NonNull
public static MainApplication getApplication() {
return sApplication;
}
@Override
public void onCreate() {
super.onCreate();
mAppExecutors = AppExecutors.getInstance();
EncryptionCoreProvider.getInstance().initialize(this);
mAppExecutors.diskIO().execute(() -> {
FileLogger.init(this);
FileLogger.purgeLogs(this);
});
initBackgroundCallBack();
ScriptLoader.init(this);
if (TextUtils.isEmpty(Utilities.getRandomSalt(this))) {
Utilities.setRandomSalt(this, Hex.toHexString(HashUtil.getNextSalt()));
}
IntentFilter mScreenOffFilter = new IntentFilter(Intent.ACTION_SCREEN_OFF);
registerReceiver(mScreenOReceiver, mScreenOffFilter);
shouldLock = Utilities.hasVaultCreated(this);
startAttackCheckingService();
}
private void startAttackCheckingService() {
Intent intent = new Intent(this, AttackCheckingService.class);
startService(intent);
}
public AppDatabase getDatabase() {
return AppDatabase.getInstance(this, mAppExecutors);
}
public DataRepository getRepository() {
return DataRepository.getInstance(this, getDatabase());
}
private void initBackgroundCallBack() {
registerActivityLifecycleCallbacks(new ActivityLifecycleCallbacks() {
@Override
public void onActivityCreated(@NonNull Activity activity, Bundle savedInstanceState) {
if (activity instanceof MainActivity && savedInstanceState == null && shouldLock) {
Intent intent = new Intent(activity, UnlockActivity.class);
activity.startActivity(intent);
shouldLock = false;
}
}
@Override
public void onActivityStarted(@NonNull Activity activity) {
}
@Override
public void onActivityResumed(@NonNull Activity activity) {
topActivity = new SoftReference<>(activity);
}
@Override
public void onActivityPaused(@NonNull Activity activity) {
}
@Override
public void onActivityStopped(@NonNull Activity activity) {
}
@Override
public void onActivitySaveInstanceState(@NonNull Activity activity, @NonNull Bundle outState) {
}
@Override
public void onActivityDestroyed(@NonNull Activity activity) {
}
});
}
private final BroadcastReceiver mScreenOReceiver = new BroadcastReceiver() {
@Override
public void onReceive(Context context, Intent intent) {
String action = intent.getAction();
if (Intent.ACTION_SCREEN_OFF.equals(action)) {
Activity activity = topActivity.get();
if (!(activity instanceof UnlockActivity)
&& Utilities.hasVaultCreated(activity)
&& !Utilities.isAttackDetected(activity)) {
startActivity(new Intent(activity, UnlockActivity.class));
}
}
}
};
}

229
app/src/main/java/com/cobo/cold/Utilities.java

@ -0,0 +1,229 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold;
import android.app.Activity;
import android.content.Context;
import android.content.SharedPreferences;
import android.provider.Settings;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.app.AppCompatActivity;
import androidx.fragment.app.FragmentActivity;
import com.cobo.cold.ui.modal.ModalDialog;
import static android.content.Context.MODE_PRIVATE;
import static com.cobo.cold.ui.fragment.setting.FingerprintPreferenceFragment.FINGERPRINT_SIGN;
import static com.cobo.cold.ui.fragment.setting.FingerprintPreferenceFragment.FINGERPRINT_UNLOCK;
public class Utilities {
public static final String PREFERENCE_SECRET = "secret";
public static final String PREFERENCE_KEY_PASSWORD = "password";
public static final String PREFERENCE_KEY_PATTERN = "pattern";
public static final String PREFERENCE_KEY_VAULT_CREATED = "vault_created";
public static final String PREFERENCE_KEY_LANGUAGE_SET = "language_set";
public static final String PREFERENCE_KEY_VAULT_ID = "vault_id";
public static final String SHARED_PREFERENCES_KEY = "com.cobo.cold.prefs";
public static final String IS_SETUP_VAULT = "is_setup_vault";
public static final String IS_SET_PASSPHRASE = "is_set_passphrase";
public static final String PREFERENCE_KEY_BELONG_TO = "belong_to";
public static final String PREFERENCE_KEY_PWD_RETRY = "pwd_retry_times";
public static final String PREFERENCE_KEY_PATTERN_RETRY = "pattern_retry_times";
public static final String PREFERENCE_KEY_MNEMONIC_COUNT = "mnemonic_count";
public static final String FINGERPRINT_CLICKED = "fingerprint_clicked";
public static final String PATTERN_LOCK_CLICKED = "pattern_lock_clicked";
public static final String PREFERENCE_KEY_RANDOM_SALT = "random_salt";
public static final String FINGERPRINT_PASSWORD = "fingerprint_password";
public static final String ATTACK_DETECTED = "attack_detected";
public static void alert(AppCompatActivity activity,
@Nullable String title, @NonNull String message,
String buttonText, Runnable action) {
ModalDialog.showCommonModal(activity,
title,
message,
buttonText,
action);
}
public static boolean hasVaultCreated(Context activity) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(PREFERENCE_KEY_VAULT_CREATED, false);
}
public static void setVaultCreated(Activity activity) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(PREFERENCE_KEY_VAULT_CREATED, true).apply();
}
public static void setLanguageSet(Activity activity) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(PREFERENCE_KEY_LANGUAGE_SET, true).apply();
}
public static boolean hasLanguageSet(Activity activity) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(PREFERENCE_KEY_LANGUAGE_SET, false);
}
public static boolean isPatternUnlock(Activity activity) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return !TextUtils.isEmpty(sp.getString(PREFERENCE_KEY_PATTERN, ""));
}
public static boolean verifyPatternUnlock(Activity activity, String patternSha1) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return patternSha1.equals(sp.getString(PREFERENCE_KEY_PATTERN, ""));
}
public static void setPattern(Activity activity, String s) {
SharedPreferences sp = activity.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putString(PREFERENCE_KEY_PATTERN, s).apply();
}
public static void clearPatternUnlock(FragmentActivity activity) {
setPattern(activity, "");
}
public static SharedPreferences getPrefs(Context context) {
return context.getSharedPreferences(SHARED_PREFERENCES_KEY, MODE_PRIVATE);
}
public static String getVaultId(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getString(PREFERENCE_KEY_VAULT_ID, "");
}
public static void setVaultId(Context context, String id) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putString(PREFERENCE_KEY_VAULT_ID, id).apply();
}
public static void setRandomSalt(Context context, String salt) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putString(PREFERENCE_KEY_RANDOM_SALT, salt).apply();
}
public static String getRandomSalt(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getString(PREFERENCE_KEY_RANDOM_SALT, "");
}
public static void setCurrentBelongTo(Context context, String s) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putString(PREFERENCE_KEY_BELONG_TO, s).apply();
}
public static String getCurrentBelongTo(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getString(PREFERENCE_KEY_BELONG_TO, "");
}
public static void setPasswordRetryTimes(Context context, int times) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putInt(PREFERENCE_KEY_PWD_RETRY, times).apply();
}
public static int getPasswordRetryTimes(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getInt(PREFERENCE_KEY_PWD_RETRY, 0);
}
public static void setPatternRetryTimes(Context context, int times) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putInt(PREFERENCE_KEY_PATTERN_RETRY, times).apply();
}
public static int getPatternRetryTimes(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getInt(PREFERENCE_KEY_PATTERN_RETRY, 0);
}
public static int getMnemonicCount(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getInt(PREFERENCE_KEY_MNEMONIC_COUNT, 24);
}
public static void setMnemonicCount(Context context, int count) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putInt(PREFERENCE_KEY_MNEMONIC_COUNT, count).apply();
}
public static boolean isFingerprintUnlockEnable(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(FINGERPRINT_UNLOCK, false);
}
public static boolean isFingerprintSignEnable(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(FINGERPRINT_SIGN, false);
}
public static void setFingerprintUnlockEnable(Context context, boolean enable) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(FINGERPRINT_UNLOCK, enable).apply();
}
public static void setFingerprintSignEnable(Context context, boolean enable) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(FINGERPRINT_SIGN, enable).apply();
}
public static boolean hasUserClickFingerprint(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(FINGERPRINT_CLICKED, false);
}
public static void setUserClickFingerprint(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(FINGERPRINT_CLICKED, true).apply();
}
public static boolean hasUserClickPatternLock(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(PATTERN_LOCK_CLICKED, false);
}
public static void setUserClickPatternLock(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(PATTERN_LOCK_CLICKED, true).apply();
}
public static String getFingerprintPassword(Context context) {
return Settings.System.getString(context.getContentResolver(), FINGERPRINT_PASSWORD);
}
public static void setFingerprintPassword(Context context, String pwd) {
Settings.System.putString(context.getContentResolver(), FINGERPRINT_PASSWORD, pwd);
}
public static void setAttackDetected(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
sp.edit().putBoolean(ATTACK_DETECTED,true).apply();
}
public static boolean isAttackDetected(Context context) {
SharedPreferences sp = context.getSharedPreferences(PREFERENCE_SECRET, MODE_PRIVATE);
return sp.getBoolean(ATTACK_DETECTED,false);
}
}

72
app/src/main/java/com/cobo/cold/callables/BlockingCallable.java

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.EncryptionCoreProvider;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.interfaces.Callback;
import com.cobo.cold.encryptioncore.interfaces.JobScheduler;
import java.util.concurrent.Callable;
import java.util.concurrent.CountDownLatch;
import java.util.concurrent.atomic.AtomicReference;
public class BlockingCallable implements Callable<Packet> {
private final Packet mPacket;
public BlockingCallable(@NonNull Packet packet) {
mPacket = packet;
}
@NonNull
@Override
public Packet call() throws Exception {
final JobScheduler jobScheduler = EncryptionCoreProvider.getInstance().getImpl();
final CountDownLatch mLatch = new CountDownLatch(1);
final AtomicReference<Object> reference = new AtomicReference<>();
jobScheduler.offer(mPacket, new Callback() {
@Override
public void onSuccess(@NonNull Packet packet) {
reference.set(packet);
mLatch.countDown();
}
@Override
public void onFail(@NonNull Exception exception) {
reference.set(exception);
mLatch.countDown();
}
});
mLatch.await();
final Object result = reference.get();
if (result instanceof Packet) {
return (Packet) result;
} else if (result instanceof Exception) {
throw (Exception) result;
} else {
throw new IllegalStateException("zero result");
}
}
}

53
app/src/main/java/com/cobo/cold/callables/ChangePasswordCallable.java

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class ChangePasswordCallable implements Callable<Boolean> {
private final String newPassword;
private final String currentPassword;
public ChangePasswordCallable(@NonNull String newPassword, @NonNull String currentPassword) {
this.newPassword = newPassword;
this.currentPassword = currentPassword;
}
@Override
public Boolean call() {
Packet.Builder builder = new Packet.Builder(CONSTANTS.METHODS.CHANGE_USER_PASSWORD)
.addHexPayload(CONSTANTS.TAGS.NEW_PASSWORD, newPassword)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, currentPassword);
final Callable<Packet> callable = new BlockingCallable(builder.build());
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

44
app/src/main/java/com/cobo/cold/callables/CheckBootModeCallable.java

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class CheckBootModeCallable implements Callable<Boolean> {
@Override
@NonNull
public Boolean call() {
try {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_FIRMWARE_STATUS).build());
final Packet result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.BOOT_VERSION);
return payload != null;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

53
app/src/main/java/com/cobo/cold/callables/CheckUpdateFirmwareCallable.java

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
import static com.cobo.cold.encryption.interfaces.CONSTANTS.TAGS.REQUEST_UPDATE_META_DATA;
public class CheckUpdateFirmwareCallable implements Callable<Boolean> {
private final byte[] mMetaData;
public CheckUpdateFirmwareCallable(byte[] metaData) {
mMetaData = metaData;
}
/**
* tag 0x0117 0001--checkOnly
* 0000--enter boot mode
*/
@Override
public Boolean call() {
try {
final Callable callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.CHECK_UPDATE)
.addBytesPayload(REQUEST_UPDATE_META_DATA, mMetaData)
.build()
);
callable.call();
return true;
} catch (Exception e) {
e.printStackTrace();
return false;
}
}
}

39
app/src/main/java/com/cobo/cold/callables/ClearTokenCallable.java

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class ClearTokenCallable implements Callable {
@Override
public Void call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.CLEAR_TOKEN)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

49
app/src/main/java/com/cobo/cold/callables/FirmwareParameterCallable.java

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class FirmwareParameterCallable implements Callable<String[]> {
@Override
public String[] call() {
try {
String[] res = new String[2];
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_FIRMWARE_PARAMETER).build());
final Packet result = callable.call();
Payload payload = result.getPayload(CONSTANTS.TAGS.FIRMWARE_SN);
if (payload != null) {
res[0] = payload.toUtf8();
}
payload = result.getPayload(CONSTANTS.TAGS.FIRMWARE_APP_VERSION);
if (payload != null) {
res[1] = payload.toUtf8();
}
return res;
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

78
app/src/main/java/com/cobo/cold/callables/GetExtendedPublicKeyCallable.java

@ -0,0 +1,78 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.coinlib.utils.Coins;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
import static com.cobo.cold.db.PresetData.getCurveByPath;
public class GetExtendedPublicKeyCallable implements Callable<String> {
private final String pubKeyPath;
private final Coins.CURVE curve;
public GetExtendedPublicKeyCallable(String pubKeyPath) {
this.pubKeyPath = pubKeyPath;
this.curve = getCurveByPath(pubKeyPath);
}
public GetExtendedPublicKeyCallable(String pubKeyPath, Coins.CURVE curve) {
this.pubKeyPath = pubKeyPath;
this.curve = curve;
}
@Override
public String call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_EXTENDED_PUBLICKEY)
.addBytePayload(CONSTANTS.TAGS.CURVE, getCurveTag())
.addTextPayload(CONSTANTS.TAGS.PATH, pubKeyPath).build());
final Packet result;
try {
result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.EXTEND_PUB_KEY);
if (payload != null) {
return payload.toUtf8();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private int getCurveTag() {
int value = 0;
switch (curve) {
case SECP256K1:
break;
case SECP256R1:
value = 1;
break;
case ED25519:
value = 2;
break;
}
return value;
}
}

46
app/src/main/java/com/cobo/cold/callables/GetMessageCallable.java

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetMessageCallable implements Callable<String> {
@Override
public String call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.GET_MESSAGE)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
final Packet result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.MESSAGE);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

54
app/src/main/java/com/cobo/cold/callables/GetPasswordTokenCallable.java

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetPasswordTokenCallable implements Callable<String> {
private final String password;
public GetPasswordTokenCallable(@NonNull String password) {
this.password = password;
}
@Override
public String call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.VERIFY_USER_PASSWORD)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.addBytePayload(CONSTANTS.TAGS.NEED_TOKEN, 1)
.build());
try {
Packet packet = callable.call();
final Payload payload = packet.getPayload(CONSTANTS.TAGS.AUTH_TOKEN);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

46
app/src/main/java/com/cobo/cold/callables/GetRandomEntropyCallable.java

@ -0,0 +1,46 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetRandomEntropyCallable implements Callable<String> {
@Override
public String call() {
try {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_RANDOM_ENTROPY)
.addShortPayload(CONSTANTS.TAGS.ENTROPY_TYPE, 256)
.addBytePayload(CONSTANTS.TAGS.ENTROPY_CHECKSUM, 0).build());
final Packet result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.ENTROPY);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return "";
}
}

47
app/src/main/java/com/cobo/cold/callables/GetUpdateKeyCallable.java

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetUpdateKeyCallable implements Callable<String> {
@Override
public String call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.GET_UPDATE_KEY)
.addBytePayload(CONSTANTS.TAGS.UPDATE_KEY_OPERATION, CONSTANTS.VALS.READ_UPDATE_KEY)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
final Packet result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.UPDATE_KEY);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

49
app/src/main/java/com/cobo/cold/callables/GetUuidCallable.java

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.coinlib.Util;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetUuidCallable implements Callable<String> {
private static final String pubKeyPath = "M/44'/1131373167'/0'";
@Override
public String call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_EXTENDED_PUBLICKEY)
.addTextPayload(CONSTANTS.TAGS.PATH, pubKeyPath).build());
final Packet result;
try {
result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.EXTEND_PUB_KEY);
if (payload != null) {
return Util.pubKeyFromExtentPubKey(payload.toUtf8()).substring(2);
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

56
app/src/main/java/com/cobo/cold/callables/GetVaultIdCallable.java

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import android.text.TextUtils;
import com.cobo.coinlib.Util;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class GetVaultIdCallable implements Callable<String> {
private static final String pubKeyPath = "M/44'/1131373167'/0'";
@Override
public String call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.GET_EXTENDED_PUBLICKEY)
.addTextPayload(CONSTANTS.TAGS.PATH, pubKeyPath).build());
final Packet result;
String xPubKey = null;
try {
result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.EXTEND_PUB_KEY);
if (payload != null) {
xPubKey = payload.toUtf8();
}
String pubKey;
if (!TextUtils.isEmpty(xPubKey)) {
pubKey = Util.pubKeyFromExtentPubKey(xPubKey);
return pubKey.substring(2, 8).toUpperCase();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

54
app/src/main/java/com/cobo/cold/callables/RegisterPublicKeyCallable.java

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
import static com.cobo.cold.encryption.interfaces.BASECONSTANTS.TAGS.CURRENT_PASSWORD;
import static com.cobo.cold.encryption.interfaces.BASECONSTANTS.TAGS.PUBLIC_KEY;
public class RegisterPublicKeyCallable implements Callable<Boolean> {
private final byte[] publicKey;
private final String password;
public RegisterPublicKeyCallable(byte[] publicKey, String password) {
this.publicKey = publicKey;
this.password = password;
}
@Override
public Boolean call() {
try {
final Callable callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.REGISTER_PUBLIC_KEY)
.addBytesPayload(PUBLIC_KEY, publicKey)
.addHexPayload(CURRENT_PASSWORD, password)
.build()
);
callable.call();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

139
app/src/main/java/com/cobo/cold/callables/RequestUpdateCallable.java

@ -0,0 +1,139 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import android.util.Log;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.io.ByteArrayInputStream;
import java.io.InputStream;
import java.util.concurrent.Callable;
import static com.cobo.cold.callables.UpdateCallable.TYPE_PACKAGE_END;
import static com.cobo.cold.callables.UpdateCallable.TYPE_PACKAGE_HEADER;
import static com.cobo.cold.callables.UpdateCallable.calculateChecksum;
import static com.cobo.cold.callables.UpdateCallable.calculatePackageType;
import static com.cobo.cold.callables.UpdateCallable.fitBuffer;
public class RequestUpdateCallable implements Callable<Boolean> {
private final byte[] updateData;
private final String password;
private static final int UPDATE_RETRY_TIMES = 3;
private static final int PACKET_RETRY_TIMES = 6;
private static final int METADATA_SIZE = 128;
private String TAG = "RequestUpdateCallableNew";
public RequestUpdateCallable(byte[] updateData, String password) {
this.updateData = updateData;
this.password = password;
}
@Override
public Boolean call() {
for (int i = 1; ; ++i) {
boolean updateSuccess = true;
try {
updating();
} catch (Exception e) {
e.printStackTrace();
updateSuccess = false;
}
if (updateSuccess) {
break;
} else if (i == UPDATE_RETRY_TIMES) {
return false;
}
}
return true;
}
private boolean updating() throws Exception {
final byte[] checksum = calculateChecksum(updateData);
final byte[] readBuffer = new byte[CONSTANTS.CONFIG.PAGE_SIZE];
try (InputStream inputStream = new ByteArrayInputStream(updateData)) {
// skip metadata
byte[] metaData = new byte[METADATA_SIZE];
inputStream.read(metaData);
int read;
final int packageSize = updateData.length - METADATA_SIZE;
int packageIndex = 0;
int readPosition = 0;
while ((read = inputStream.read(readBuffer)) > 0) {
final byte[] availableBuffer = fitBuffer(readBuffer, read);
final int type = calculatePackageType(packageSize, readPosition, read);
for (int i = 1; i <= PACKET_RETRY_TIMES; ++i) {
boolean writeSuccess = true;
final Packet.Builder builder = new Packet.Builder(CONSTANTS.METHODS.REQUEST_UPDATE)
.addBytePayload(CONSTANTS.TAGS.UPDATING_PACKAGE_TYPE, type)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.addBytesPayload(CONSTANTS.TAGS.UPDATING_PACKAGE, availableBuffer);
if (type == TYPE_PACKAGE_END) {
builder.addBytesPayload(CONSTANTS.TAGS.UPDATING_CHECKSUM, checksum);
}
final Callable callable = new BlockingCallable(builder.build());
try {
Log.w(TAG, String.format("write the %sth package", packageIndex));
callable.call();
} catch (Exception e) {
Log.e(TAG, "updating: ", e);
writeSuccess = false;
}
if (writeSuccess) {
break;
} else if (i == PACKET_RETRY_TIMES) {
throw new RuntimeException("write updating packet out of retry times");
}
}
readPosition += read;
++packageIndex;
}
for (int i = 1; i <= PACKET_RETRY_TIMES; ++i) {
boolean writeSuccess = true;
final Packet.Builder builder = new Packet.Builder(0x0203)
.addBytePayload(CONSTANTS.TAGS.UPDATING_PACKAGE_TYPE, TYPE_PACKAGE_HEADER)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.addBytesPayload(CONSTANTS.TAGS.UPDATING_PACKAGE, metaData);
final Callable callable = new BlockingCallable(builder.build());
try {
Log.w(TAG, String.format("write the header package", packageIndex));
callable.call();
} catch (Exception e) {
Log.e(TAG, "updating: ", e);
writeSuccess = false;
}
if (writeSuccess) {
break;
} else if (i == PACKET_RETRY_TIMES) {
throw new RuntimeException("write updating packet out of retry times");
}
}
}
return true;
}
}

39
app/src/main/java/com/cobo/cold/callables/ResetCallable.java

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class ResetCallable implements Callable {
@Override
public Void call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.RESET)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

58
app/src/main/java/com/cobo/cold/callables/ResetPasswordCallable.java

@ -0,0 +1,58 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class ResetPasswordCallable implements Callable<Boolean> {
private final String newPassword;
private final String mnemonic;
public ResetPasswordCallable(@NonNull String newPassword, @Nullable String mnemonic) {
this.newPassword = newPassword;
this.mnemonic = mnemonic;
}
@Override
public Boolean call() {
Packet.Builder builder = new Packet.Builder(CONSTANTS.METHODS.RESET_USER_PASSWORD)
.addHexPayload(CONSTANTS.TAGS.NEW_PASSWORD, newPassword);
if (!TextUtils.isEmpty(mnemonic)) {
builder.addTextPayload(CONSTANTS.TAGS.MNEMONIC, mnemonic);
}
final Callable<Packet> callable = new BlockingCallable(builder.build());
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

131
app/src/main/java/com/cobo/cold/callables/SignTxCallable.java

@ -0,0 +1,131 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.coinlib.utils.Coins;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryption.signature.Signature;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import com.cobo.cold.encryptioncore.utils.ByteFormatter;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Objects;
import java.util.concurrent.Callable;
public class SignTxCallable implements Callable<String> {
private final String hdPath;
private final String hash;
private final Coins.CURVE curve;
private final String authToken;
public SignTxCallable(String path, String hash, String authToken) {
this.hdPath = path;
this.hash = hash;
this.curve = getCurveByPath(path);
this.authToken = authToken;
}
private static boolean isCanonical(byte[] sigs) {
return (sigs[0] & 0x80) == 0
&& !(sigs[0] == 0 && ((sigs[1] & 0x80) == 0))
&& (sigs[32] & 0x80) == 0
&& !(sigs[32] == 0 && ((sigs[33] & 0x80) == 0));
}
private void postIntercept(@NonNull Packet packet) {
final String publicKey = Objects.requireNonNull(packet.getPayload(CONSTANTS.TAGS.PUBLIC_KEY_HASH)).toHex();
final String signed = Objects.requireNonNull(packet.getPayload(CONSTANTS.TAGS.SIGNED)).toHex();
final byte[] signBytes = ByteFormatter.hex2bytes(signed);
if (!isCanonical(signBytes)) {
throw new RuntimeException("couldn't find a canonical signature");
}
final byte[] signR = Arrays.copyOf(signBytes, 32);
final byte[] signS = Arrays.copyOfRange(signBytes, 32, 64);
final BigInteger publicKeyInt = new BigInteger(publicKey, 16);
final int recId = Signature.getRecoverIdFromSignature(publicKeyInt, new BigInteger(1, signR), new BigInteger(1, signS), Hex.decode(hash));
final byte[] signBytesWithRecId = new byte[signBytes.length + 1];
System.arraycopy(signBytes, 0, signBytesWithRecId, 0, signBytes.length);
signBytesWithRecId[signBytesWithRecId.length - 1] = (byte) recId;
packet.getPayloads().put(CONSTANTS.TAGS.SIGNED, new Payload(signBytesWithRecId));
}
@Override
public String call() {
try {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.SIGN)
.addTextPayload(CONSTANTS.TAGS.PATH, hdPath)
.addBytePayload(CONSTANTS.TAGS.CURVE, getCurveTag())
.addHexPayload(CONSTANTS.TAGS.AUTH_TOKEN, authToken)
.addHexPayload(CONSTANTS.TAGS.TX_HASH, hash).build());
final Packet result = callable.call();
// secp256k1 should calculate recovery id
if (curve == Coins.CURVE.SECP256K1) {
postIntercept(result);
}
final Payload payload = result.getPayload(CONSTANTS.TAGS.SIGNED);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
private Coins.CURVE getCurveByPath(String pubKeyPath) {
String[] strs = pubKeyPath.split("/");
int coinIndex;
if (strs[2].endsWith("'")) {
coinIndex = Integer.parseInt(strs[2].substring(0, strs[2].length() - 1));
} else {
coinIndex = Integer.parseInt(strs[2]);
}
return Coins.curveFromCoinCode(Coins.coinCodeOfIndex(coinIndex));
}
private int getCurveTag() {
int value = 0;
switch (curve) {
case SECP256K1:
break;
case SECP256R1:
value = 1;
break;
case ED25519:
value = 2;
break;
}
return value;
}
}

210
app/src/main/java/com/cobo/cold/callables/UpdateCallable.java

@ -0,0 +1,210 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import android.util.Log;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.utils.Preconditions;
import java.io.ByteArrayInputStream;
import java.io.IOException;
import java.io.InputStream;
import java.util.concurrent.Callable;
public class UpdateCallable implements Callable<Void> {
private static final String TAG = "Vault.UpdateCallable";
private static final int UPDATE_RETRY_TIMES = 3;
private static final int PACKET_RETRY_TIMES = 6;
private static final int TYPE_PACKAGE_START = 0x00;
private static final int TYPE_PACKAGE_MIDDLE = 0x01;
static final int TYPE_PACKAGE_END = 0x02;
static final int TYPE_PACKAGE_HEADER = 0x03;
private static final int APP_BOOT_SWITCH_TIME = 5000; //ms
private static final int METADATA_SIZE = 128; // 64byte meta data
private final byte[] mUpdateData;
private String password;
public UpdateCallable(@NonNull byte[] updateData, String password) {
mUpdateData = updateData;
this.password = password;
}
@NonNull
static byte[] calculateChecksum(@NonNull byte[] data) throws IOException {
try (InputStream inputStream = new ByteArrayInputStream(data)) {
return Preconditions.checkNotNull(CONSTANTS.CONFIG.DIGEST.checksum(inputStream));
}
}
static byte[] fitBuffer(@NonNull byte[] data, int size) {
if (data.length == size) {
return data;
}
final byte[] result = new byte[size];
System.arraycopy(data, 0, result, 0, size);
return result;
}
static int calculatePackageType(int dataLength, int currentPosition, int read) {
if (currentPosition == 0) {
return TYPE_PACKAGE_START;
} else if (currentPosition + read == dataLength) {
return TYPE_PACKAGE_END;
} else {
return TYPE_PACKAGE_MIDDLE;
}
}
@Override
public Void call() throws Exception {
if (!prepareUpdate()) return null;
for (int i = 1; ; ++i) {
boolean updateSuccess = true;
try {
updating();
} catch (Exception e) {
e.printStackTrace();
updateSuccess = false;
}
if (updateSuccess) {
break;
} else if (i == UPDATE_RETRY_TIMES) {
throw new RuntimeException("update serial out of retry times");
}
}
return null;
}
private void updating() throws Exception {
final byte[] checksum = calculateChecksum(mUpdateData);
final byte[] readBuffer = new byte[CONSTANTS.CONFIG.PAGE_SIZE];
try (InputStream inputStream = new ByteArrayInputStream(mUpdateData)) {
byte[] header = new byte[METADATA_SIZE];
// skip metadata
inputStream.read(header);
int read;
final int packageSize = mUpdateData.length - METADATA_SIZE;
int packageIndex = 0;
int readPosition = 0;
while ((read = inputStream.read(readBuffer)) > 0) {
final byte[] availableBuffer = fitBuffer(readBuffer, read);
final int type = calculatePackageType(packageSize, readPosition, read);
for (int i = 1; i <= PACKET_RETRY_TIMES; ++i) {
if (writeUpdateBytes(checksum, packageIndex, availableBuffer, type, i)) break;
}
readPosition += read;
++packageIndex;
}
for (int i = 1; i <= PACKET_RETRY_TIMES; ++i) {
if (writeHeaderBytes(header)) break;
}
}
Thread.sleep(APP_BOOT_SWITCH_TIME);
Preconditions.checkState(!new CheckBootModeCallable().call(),
"update fail, se has still stayed in boot mode");
}
private boolean writeHeaderBytes(byte[] header) {
final Packet.Builder builder = new Packet.Builder(CONSTANTS.METHODS.WRITE_UPDATE_BYTES)
.addBytePayload(CONSTANTS.TAGS.UPDATING_PACKAGE_TYPE, TYPE_PACKAGE_HEADER)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.addBytesPayload(CONSTANTS.TAGS.UPDATING_PACKAGE, header);
final Callable callable = new BlockingCallable(builder.build());
try {
callable.call();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
private boolean writeUpdateBytes(byte[] checksum, int packageIndex, byte[] availableBuffer, int type, int i) {
boolean writeSuccess = true;
final Packet.Builder builder = new Packet.Builder(CONSTANTS.METHODS.WRITE_UPDATE_BYTES)
.addBytePayload(CONSTANTS.TAGS.UPDATING_PACKAGE_TYPE, type)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.addBytesPayload(CONSTANTS.TAGS.UPDATING_PACKAGE, availableBuffer);
if (type == TYPE_PACKAGE_END) {
builder.addBytesPayload(CONSTANTS.TAGS.UPDATING_CHECKSUM, checksum);
}
final Callable callable = new BlockingCallable(builder.build());
try {
Log.w(TAG, String.format("write the %sth package", packageIndex));
callable.call();
} catch (Exception e) {
Log.e(TAG, "updating: ", e);
writeSuccess = false;
}
if (writeSuccess) {
return true;
} else if (i == PACKET_RETRY_TIMES) {
throw new RuntimeException("write updating packet out of retry times");
}
return false;
}
private boolean prepareUpdate() throws Exception {
int retry = 0;
while (!checkBootMode()) {
if (retry >= 10) {
return false;
}
if (!requestUpdate()) {
return false;
}
Thread.sleep(APP_BOOT_SWITCH_TIME);
retry++;
}
return true;
}
private boolean checkBootMode() {
return new CheckBootModeCallable().call();
}
private boolean requestUpdate() {
return new RequestUpdateCallable(mUpdateData, password).call();
}
}

50
app/src/main/java/com/cobo/cold/callables/UpdatePassphraseCallable.java

@ -0,0 +1,50 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class UpdatePassphraseCallable implements Callable<String> {
private final String passphrase;
private final String password;
public UpdatePassphraseCallable(String passphrase, String password) {
this.passphrase = passphrase;
this.password = password;
}
@Override
public String call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.UPDATE_PASSPHRASE)
.addTextPayload(CONSTANTS.TAGS.PASSPHRASE, passphrase)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

54
app/src/main/java/com/cobo/cold/callables/VerifyFingerprintCallable.java

@ -0,0 +1,54 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import java.util.concurrent.Callable;
public class VerifyFingerprintCallable implements Callable<String> {
private final String signature;
public VerifyFingerprintCallable(@NonNull String signature) {
this.signature = signature;
}
@Override
public String call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.VERIFY_FINGERPRINT)
.addHexPayload(CONSTANTS.TAGS.MESSAGE_SIGNATURE, signature)
.addBytePayload(CONSTANTS.TAGS.NEED_TOKEN, 1).build());
try {
Packet packet = callable.call();
Payload payload = packet.getPayload(CONSTANTS.TAGS.AUTH_TOKEN);
if (payload != null) {
return payload.toHex();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

48
app/src/main/java/com/cobo/cold/callables/VerifyMnemonicCallable.java

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class VerifyMnemonicCallable implements Callable<Boolean> {
private final String mnemonic;
public VerifyMnemonicCallable(String mnemonic) {
this.mnemonic = mnemonic;
}
@Override
public Boolean call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.VERIFY_MNEMONIC)
.addTextPayload(CONSTANTS.TAGS.MNEMONIC, mnemonic)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
callable.call();
return true;
} catch (Exception e) {
e.printStackTrace();
}
return false;
}
}

48
app/src/main/java/com/cobo/cold/callables/VerifyPasswordCallable.java

@ -0,0 +1,48 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class VerifyPasswordCallable implements Callable<Boolean> {
private final String passwordHash;
public VerifyPasswordCallable(@NonNull String passwordHash) {
this.passwordHash = passwordHash;
}
@Override
public Boolean call() {
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.VERIFY_USER_PASSWORD)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, passwordHash).build());
try {
callable.call();
} catch (Exception e) {
e.printStackTrace();
return false;
}
return true;
}
}

69
app/src/main/java/com/cobo/cold/callables/WebAuthCallable.java

@ -0,0 +1,69 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import org.spongycastle.util.encoders.Base64;
import org.spongycastle.util.encoders.Hex;
import java.util.Arrays;
import java.util.concurrent.Callable;
public class WebAuthCallable implements Callable<String> {
private byte[] encrypted;
private byte[] signature;
public WebAuthCallable(String data) {
try {
byte[] bytes = Base64.decode(data);
this.encrypted = Arrays.copyOfRange(bytes, 0, bytes.length - 64);
this.signature = Arrays.copyOfRange(bytes, bytes.length - 64, bytes.length);
} catch (Exception e) {
this.encrypted = null;
this.signature = null;
}
}
@Override
public String call() {
if (encrypted == null || signature == null) {
return null;
}
final Callable<Packet> callable = new BlockingCallable(
new Packet.Builder(CONSTANTS.METHODS.WEB_AUTH)
.addHexPayload(CONSTANTS.TAGS.ENCRYPTED, Hex.toHexString(encrypted))
.addHexPayload(CONSTANTS.TAGS.SIGNATURE, Hex.toHexString(signature)).build());
final Packet result;
try {
result = callable.call();
final Payload payload = result.getPayload(CONSTANTS.TAGS.DECRYPTED);
if (payload != null) {
return payload.toUtf8();
}
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

51
app/src/main/java/com/cobo/cold/callables/WriteMnemonicCallable.java

@ -0,0 +1,51 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.callables;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import java.util.concurrent.Callable;
public class WriteMnemonicCallable implements Callable<String> {
private final String mnemonic;
private final String password;
public WriteMnemonicCallable(@NonNull String mnemonic, @NonNull String password) {
this.mnemonic = mnemonic;
this.password = password;
}
@Override
public String call() {
try {
final Packet packet = new Packet.Builder(CONSTANTS.METHODS.WRITE_MNEMONIC)
.addTextPayload(CONSTANTS.TAGS.MNEMONIC, mnemonic)
.addHexPayload(CONSTANTS.TAGS.CURRENT_PASSWORD, password)
.build();
final Callable<Packet> callable = new BlockingCallable(packet);
callable.call();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}

28
app/src/main/java/com/cobo/cold/config/BaseFeatureFlags.java

@ -0,0 +1,28 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.config;
public abstract class BaseFeatureFlags {
BaseFeatureFlags() {
}
public static final boolean ENABLE_WHITE_LIST = true;
public static final boolean ENABLE_SHARDING_MNEMONIC = true;
public static final boolean ENABLE_SYSTEM_UPDATE = true;
public static final boolean ENABLE_SERIAL_UPDATE = true;
}

105
app/src/main/java/com/cobo/cold/db/AppDatabase.java

@ -0,0 +1,105 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db;
import android.content.Context;
import androidx.annotation.NonNull;
import androidx.lifecycle.LiveData;
import androidx.lifecycle.MutableLiveData;
import androidx.room.Database;
import androidx.room.Room;
import androidx.room.RoomDatabase;
import androidx.sqlite.db.SupportSQLiteDatabase;
import com.cobo.cold.AppExecutors;
import com.cobo.cold.db.dao.AccountDao;
import com.cobo.cold.db.dao.AddressDao;
import com.cobo.cold.db.dao.CoinDao;
import com.cobo.cold.db.dao.TxDao;
import com.cobo.cold.db.dao.WhiteListDao;
import com.cobo.cold.db.entity.AccountEntity;
import com.cobo.cold.db.entity.AddressEntity;
import com.cobo.cold.db.entity.CoinEntity;
import com.cobo.cold.db.entity.TxEntity;
import com.cobo.cold.db.entity.WhiteListEntity;
@Database(entities = {CoinEntity.class, AddressEntity.class,
TxEntity.class, WhiteListEntity.class, AccountEntity.class}, version = 2)
public abstract class AppDatabase extends RoomDatabase {
private static final String DATABASE_NAME = "cobo-vault-db";
private static AppDatabase sInstance;
public abstract CoinDao coinDao();
public abstract AddressDao addressDao();
public abstract TxDao txDao();
public abstract WhiteListDao whiteListDao();
public abstract AccountDao accountDao();
private final MutableLiveData<Boolean> mIsDatabaseCreated = new MutableLiveData<>();
public static AppDatabase getInstance(final Context context, final AppExecutors executors) {
if (sInstance == null) {
synchronized (AppDatabase.class) {
if (sInstance == null) {
sInstance = buildDatabase(context.getApplicationContext(), executors);
sInstance.updateDatabaseCreated(context.getApplicationContext());
}
}
}
return sInstance;
}
private static AppDatabase buildDatabase(final Context appContext,
final AppExecutors executors) {
return Room.databaseBuilder(appContext, AppDatabase.class, DATABASE_NAME)
.addCallback(new Callback() {
@Override
public void onCreate(@NonNull SupportSQLiteDatabase db) {
super.onCreate(db);
executors.diskIO().execute(() -> {
// Generate the data for pre-population
AppDatabase database = AppDatabase.getInstance(appContext, executors);
// notify that the database was created and it's ready to be used
database.setDatabaseCreated();
});
}
})
.fallbackToDestructiveMigration()
.build();
}
private void updateDatabaseCreated(final Context context) {
if (context.getDatabasePath(DATABASE_NAME).exists()) {
setDatabaseCreated();
}
}
private void setDatabaseCreated() {
mIsDatabaseCreated.postValue(true);
}
public LiveData<Boolean> getDatabaseCreated() {
return mIsDatabaseCreated;
}
}

72
app/src/main/java/com/cobo/cold/db/PresetData.java

@ -0,0 +1,72 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db;
import android.content.Context;
import com.cobo.coinlib.path.CoinPath;
import com.cobo.coinlib.utils.Coins;
import com.cobo.cold.Utilities;
import com.cobo.cold.db.entity.AccountEntity;
import com.cobo.cold.db.entity.CoinEntity;
import java.util.List;
import java.util.stream.Collectors;
public class PresetData {
public static List<CoinEntity> generateCoins(Context context) {
return Coins.SUPPORTED_COINS.stream()
.map(coin -> mapToCoinEntity(context, coin))
.collect(Collectors.toList());
}
private static CoinEntity mapToCoinEntity(Context context, Coins.Coin coin) {
CoinEntity entity = new CoinEntity();
entity.setCoinId(coin.coinId());
entity.setName(coin.coinName());
entity.setCoinCode(coin.coinCode());
entity.setIndex(coin.coinIndex());
entity.setBelongTo(Utilities.getCurrentBelongTo(context));
entity.setAddressCount(0);
AccountEntity account = new AccountEntity();
String defaultHdPath = CoinPath.M()
.purpose(Coins.purposeNumber(entity.getCoinCode()))
.coinType(entity.getIndex())
.account(0)
.toString();
if (Coins.CURVE.ED25519 == getCurveByPath(defaultHdPath)) {
defaultHdPath += "/0'/0'";
}
account.setHdPath(defaultHdPath);
entity.addAccount(account);
return entity;
}
public static Coins.CURVE getCurveByPath(String pubKeyPath) {
String[] strs = pubKeyPath.split("/");
int coinIndex;
if (strs[2].endsWith("'")) {
coinIndex = Integer.parseInt(strs[2].substring(0, strs[2].length() - 1));
} else {
coinIndex = Integer.parseInt(strs[2]);
}
return Coins.curveFromCoinCode(Coins.coinCodeOfIndex(coinIndex));
}
}

40
app/src/main/java/com/cobo/cold/db/dao/AccountDao.java

@ -0,0 +1,40 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.dao;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;
import com.cobo.cold.db.entity.AccountEntity;
import java.util.List;
@Dao
public interface AccountDao {
@Insert(onConflict = OnConflictStrategy.REPLACE)
long add(AccountEntity account);
@Query("SELECT * FROM accounts WHERE coinId=:id")
List<AccountEntity> loadForCoin(long id);
@Update
void update(AccountEntity account);
}

52
app/src/main/java/com/cobo/cold/db/dao/AddressDao.java

@ -0,0 +1,52 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.Query;
import androidx.room.Update;
import com.cobo.cold.db.entity.AddressEntity;
import java.util.List;
@Dao
public interface AddressDao {
@Query("SELECT * FROM addresses WHERE coinId = :coinId AND belongTo =:belongTo")
LiveData<List<AddressEntity>> loadAddressForCoin(String coinId, String belongTo);
@Query("SELECT * FROM addresses WHERE coinId = :coinId AND belongTo =:belongTo")
List<AddressEntity> loadAddressSync(String coinId, String belongTo);
@Query("SELECT * FROM addresses WHERE path = :path AND belongTo = :belongTo")
AddressEntity loadAddress(String path, String belongTo);
@Insert
void insertAddress(AddressEntity address);
@Insert
void insertAll(List<AddressEntity> addresses);
@Update
int update(AddressEntity addr);
@Query("DELETE FROM addresses WHERE belongTo = 'hidden'")
int deleteHidden();
}

56
app/src/main/java/com/cobo/cold/db/dao/CoinDao.java

@ -0,0 +1,56 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import androidx.room.Update;
import com.cobo.cold.db.entity.CoinEntity;
import java.util.List;
@Dao
public interface CoinDao {
@Query("SELECT * FROM coins")
LiveData<List<CoinEntity>> loadAllCoins();
@Query("SELECT * FROM coins")
List<CoinEntity> loadAllCoinsSync();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insertAll(List<CoinEntity> coins);
@Insert(onConflict = OnConflictStrategy.REPLACE)
long insert(CoinEntity coin);
@Query("SELECT * FROM coins WHERE id = :id")
LiveData<CoinEntity> loadCoin(long id);
@Query("SELECT * FROM coins WHERE coinId = :coinId AND belongTo = :belongTo")
CoinEntity loadCoinSync(String coinId, String belongTo);
@Update
int update(CoinEntity coinEntity);
@Query("DELETE FROM coins WHERE belongTo = 'hidden'")
int deleteHidden();
}

47
app/src/main/java/com/cobo/cold/db/dao/TxDao.java

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.cobo.cold.db.entity.TxEntity;
import java.util.List;
@Dao
public interface TxDao {
@Query("SELECT * FROM txs where coinId = :coinId ORDER BY timeStamp DESC")
LiveData<List<TxEntity>> loadTxs(String coinId);
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(TxEntity tx);
@Query("SELECT * FROM txs WHERE txId = :id")
LiveData<TxEntity> load(String id);
@Query("SELECT * FROM txs WHERE txId = :id")
TxEntity loadSync(String id);
@Query("DELETE FROM txs WHERE belongTo = 'hidden'")
int deleteHidden();
}

49
app/src/main/java/com/cobo/cold/db/dao/WhiteListDao.java

@ -0,0 +1,49 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.dao;
import androidx.lifecycle.LiveData;
import androidx.room.Dao;
import androidx.room.Delete;
import androidx.room.Insert;
import androidx.room.OnConflictStrategy;
import androidx.room.Query;
import com.cobo.cold.db.entity.WhiteListEntity;
import java.util.List;
@Dao
public interface WhiteListDao {
@Query("select * from white_list")
LiveData<List<WhiteListEntity>> load();
@Insert(onConflict = OnConflictStrategy.REPLACE)
void insert(WhiteListEntity entity);
@Delete
void delete(WhiteListEntity entity);
@Query("select * from white_list where addr = :addr and belongTo = :belongTo")
WhiteListEntity queryAddress(String addr, String belongTo);
@Query("DELETE FROM white_list WHERE belongTo = 'hidden'")
int deleteHidden();
}

97
app/src/main/java/com/cobo/cold/db/entity/AccountEntity.java

@ -0,0 +1,97 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.ForeignKey;
import androidx.room.Ignore;
import androidx.room.PrimaryKey;
import static androidx.room.ForeignKey.CASCADE;
@Entity(tableName = "accounts",
foreignKeys = @ForeignKey(entity = CoinEntity.class,
parentColumns = "id",
childColumns = "coinId", onDelete = CASCADE))
public class AccountEntity {
@PrimaryKey(autoGenerate = true)
public long id;
private String hdPath;
private String exPub;
private int addressLength;
private boolean isMultiSign;
private long coinId;
public AccountEntity(String hdPath, String exPub, int addressLength, boolean isMultiSign, long coinId) {
this.hdPath = hdPath;
this.exPub = exPub;
this.addressLength = addressLength;
this.isMultiSign = isMultiSign;
this.coinId = coinId;
}
@Ignore
public AccountEntity() {
}
@NonNull
public String getHdPath() {
return hdPath;
}
public void setHdPath(@NonNull String hdPath) {
this.hdPath = hdPath;
}
public String getExPub() {
return exPub;
}
public void setExPub(String exPub) {
this.exPub = exPub;
}
public int getAddressLength() {
return addressLength;
}
public void setAddressLength(int addressLength) {
this.addressLength = addressLength;
}
public boolean isMultiSign() {
return isMultiSign;
}
public void setMultiSign(boolean multiSign) {
isMultiSign = multiSign;
}
public Long getCoinId() {
return coinId;
}
public void setCoinId(Long coinId) {
this.coinId = coinId;
}
}

142
app/src/main/java/com/cobo/cold/db/entity/AddressEntity.java

@ -0,0 +1,142 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
import com.cobo.cold.model.Address;
@Entity(tableName = "addresses")
public class AddressEntity implements Address, FilterableItem {
@PrimaryKey(autoGenerate = true)
private long id;
private String path;
private String coinId;
private String addressString;
private String name;
private int index;
private String belongTo;
public AddressEntity() {
}
public AddressEntity(Address address) {
id = address.getId();
belongTo = address.getBelongTo();
path = address.getPath();
coinId = address.getCoinId();
addressString = address.getAddressString();
name = address.getName();
index = address.getIndex();
}
public void setPath(String path) {
this.path = path;
}
public void setCoinId(String coinId) {
this.coinId = coinId;
}
public void setAddressString(String addressString) {
this.addressString = addressString;
}
@Override
public void setName(String name) {
this.name = name;
}
public void setIndex(int index) {
this.index = index;
}
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public void setBelongTo(String belongTo) {
this.belongTo = belongTo;
}
@Override
public String getBelongTo() {
return belongTo;
}
@Override
public int getIndex() {
return index;
}
@Override
public String getAddressString() {
return addressString;
}
@Override
public String getName() {
return name;
}
@Override
public String getPath() {
return path;
}
@Override
public String getCoinId() {
return coinId;
}
@NonNull
@Override
public String toString() {
return "AddressEntity{" +
"id=" + id +
", path='" + path + '\'' +
", coinId='" + coinId + '\'' +
", addressString='" + addressString + '\'' +
", name='" + name + '\'' +
", index=" + index +
", belongTo='" + belongTo + '\'' +
'}';
}
@Override
public boolean filter(String s) {
if (TextUtils.isEmpty(s)) {
return true;
}
s = s.toLowerCase();
return name.toLowerCase().contains(s)
|| addressString.toLowerCase().contains(s);
}
}

190
app/src/main/java/com/cobo/cold/db/entity/CoinEntity.java

@ -0,0 +1,190 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.Ignore;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.cobo.cold.model.Coin;
import java.util.ArrayList;
import java.util.List;
@Entity(tableName = "coins", indices = {@Index("id")})
public class CoinEntity implements Coin, FilterableItem {
@PrimaryKey(autoGenerate = true)
private long id;
private String coinId;
private String name;
private int iconResId;
private boolean show = false;
private int addressCount;
private String coinCode;
private String exPub;
private String belongTo;
private int index;
@Ignore
private final List<AccountEntity> accounts = new ArrayList<>();
public CoinEntity() {
}
public CoinEntity(@NonNull Coin coin) {
id = coin.getId();
coinId = coin.getCoinId();
name = coin.getName();
iconResId = getIconResId();
show = coin.isShow();
addressCount = coin.getAddressCount();
coinCode = coin.getCoinCode();
belongTo = coin.getBelongTo();
exPub = coin.getExPub();
index = coin.getIndex();
}
@Override
public String getName() {
return name;
}
public void setName(String name) {
this.name = name;
}
@Override
public String getCoinCode() {
return coinCode;
}
public void setCoinCode(String coinCode) {
this.coinCode = coinCode;
}
@Override
public int getIconResId() {
return iconResId;
}
public void setIconResId(int iconResId) {
this.iconResId = iconResId;
}
@Override
public int getAddressCount() {
return addressCount;
}
public void setAddressCount(int addressCount) {
this.addressCount = addressCount;
}
@Override
public boolean isShow() {
return show;
}
public void setShow(boolean show) {
this.show = show;
}
@Override
public String getExPub() {
return exPub;
}
@Override
public int getIndex() {
return index;
}
@Override
public void setIndex(int index) {
this.index = index;
}
public void setBelongTo(String belongTo) {
this.belongTo = belongTo;
}
@Override
public String getBelongTo() {
return belongTo;
}
public void setExPub(String exPub) {
this.exPub = exPub;
}
@Override
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
@Override
public String getCoinId() {
return coinId;
}
public void setCoinId(String coinId) {
this.coinId = coinId;
}
public void addAccount(AccountEntity account) {
accounts.add(account);
}
public List<AccountEntity> getAccounts() {
return accounts;
}
@NonNull
@Override
public String toString() {
return "CoinEntity{" +
"id=" + id +
", coinId='" + coinId + '\'' +
", name='" + name + '\'' +
", iconResId=" + iconResId +
", show=" + show +
", addressCount=" + addressCount +
", coinCode='" + coinCode + '\'' +
", exPub='" + exPub + '\'' +
", belongTo='" + belongTo + '\'' +
'}';
}
@Override
public boolean filter(String s) {
if (TextUtils.isEmpty(s)) {
return true;
}
s = s.toLowerCase();
return name.toLowerCase().contains(s) || coinCode.toLowerCase().contains(s);
}
}

22
app/src/main/java/com/cobo/cold/db/entity/FilterableItem.java

@ -0,0 +1,22 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
public interface FilterableItem {
boolean filter(String s);
}

188
app/src/main/java/com/cobo/cold/db/entity/TxEntity.java

@ -0,0 +1,188 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.room.Entity;
import androidx.room.Index;
import androidx.room.PrimaryKey;
import com.cobo.cold.model.Tx;
@Entity(tableName = "txs", indices = {@Index("txId")})
public class TxEntity implements Tx, FilterableItem {
@PrimaryKey
@NonNull
private String txId;
private String coinId;
private String coinCode;
private String amount;
private String from;
private String to;
private String fee;
private String signedHex;
private long timeStamp;
private String memo;
private String signId;
private String belongTo;
@Override
public String getBelongTo() {
return belongTo;
}
public void setBelongTo(String belongTo) {
this.belongTo = belongTo;
}
@NonNull
@Override
public String getTxId() {
return txId;
}
public void setTxId(@NonNull String txId) {
this.txId = txId;
}
@Override
public String getCoinId() {
return coinId;
}
public void setCoinCode(String coinCode) {
this.coinCode = coinCode;
}
@Override
public String getCoinCode() {
return coinCode;
}
public void setCoinId(String coinId) {
this.coinId = coinId;
}
@Override
public String getAmount() {
return amount;
}
public void setAmount(String amount) {
this.amount = amount;
}
@Override
public String getFrom() {
return from;
}
public void setFrom(String from) {
this.from = from;
}
@Override
public String getTo() {
return to;
}
public void setTo(String to) {
this.to = to;
}
@Override
public String getFee() {
return fee;
}
public void setFee(String fee) {
this.fee = fee;
}
@Override
public String getSignedHex() {
return signedHex;
}
public void setSignedHex(String signedHex) {
this.signedHex = signedHex;
}
@Override
public long getTimeStamp() {
return timeStamp;
}
public void setMemo(String memo) {
this.memo = memo;
}
@Override
public String getMemo() {
return memo;
}
public void setSignId(String signId) {
this.signId = signId;
}
@Override
public String getSignId() {
return signId;
}
public void setTimeStamp(long timeStamp) {
this.timeStamp = timeStamp;
}
@NonNull
@Override
public String toString() {
return "TxEntity{" +
"txId='" + txId + '\'' +
", coinId='" + coinId + '\'' +
", coinCode='" + coinCode + '\'' +
", amount='" + amount + '\'' +
", from='" + from + '\'' +
", to='" + to + '\'' +
", fee='" + fee + '\'' +
", signedHex='" + signedHex + '\'' +
", timeStamp=" + timeStamp +
", memo='" + memo + '\'' +
", signId='" + signId + '\'' +
", belongTo='" + belongTo + '\'' +
'}';
}
@Override
public boolean filter(String s) {
if (TextUtils.isEmpty(s)) {
return true;
}
s = s.toLowerCase();
return from.toLowerCase().contains(s)
|| to.toLowerCase().contains(s)
|| txId.toLowerCase().contains(s)
|| memo.toLowerCase().contains(s);
}
}

95
app/src/main/java/com/cobo/cold/db/entity/WhiteListEntity.java

@ -0,0 +1,95 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.db.entity;
import androidx.room.Entity;
import androidx.room.PrimaryKey;
@Entity(tableName = "white_list")
public class WhiteListEntity {
@PrimaryKey(autoGenerate = true)
private long id;
private String addr;
private String addrName;
private String coinCode;
private String memo;
private String belongTo;
public WhiteListEntity(String addr, String addrName, String coinCode, String memo) {
this.addr = addr;
this.addrName = addrName;
this.coinCode = coinCode;
this.memo = memo;
}
public long getId() {
return id;
}
public void setId(long id) {
this.id = id;
}
public String getAddr() {
return addr;
}
public void setAddr(String addr) {
this.addr = addr;
}
public String getAddrName() {
return addrName;
}
public void setAddrName(String addrName) {
this.addrName = addrName;
}
public String getCoinCode() {
return coinCode;
}
public void setCoinCode(String coinCode) {
this.coinCode = coinCode;
}
public String getMemo() {
return memo;
}
public void setMemo(String memo) {
this.memo = memo;
}
public String getBelongTo() {
return belongTo;
}
public void setBelongTo(String belongTo) {
this.belongTo = belongTo;
}
}

47
app/src/main/java/com/cobo/cold/encryption/ChipSigner.java

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption;
import androidx.annotation.Nullable;
import com.cobo.coinlib.interfaces.Signer;
import com.cobo.cold.callables.SignTxCallable;
import java.util.Objects;
public class ChipSigner extends Signer {
private final String privKeyPath;
private final String authToken;
public ChipSigner(String path, String authToken) {
this(path, authToken, null);
}
public ChipSigner(String path, String authToken, @Nullable String publicKey) {
super(publicKey);
this.privKeyPath = Objects.requireNonNull(path);
this.authToken = authToken;
}
@Override
public String sign(String data) {
SignTxCallable callable = new SignTxCallable(privKeyPath, data, authToken);
return callable.call();
}
}

121
app/src/main/java/com/cobo/cold/encryption/EncryptionCoreProvider.java

@ -0,0 +1,121 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption;
import android.content.Context;
import android.os.Looper;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.exception.EncryptionCoreException;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import com.cobo.cold.encryptioncore.interfaces.Callback;
import com.cobo.cold.encryptioncore.interfaces.JobScheduler;
import com.cobo.cold.encryptioncore.utils.ByteFormatter;
import com.cobo.cold.encryptioncore.utils.Preconditions;
public class EncryptionCoreProvider {
private static final EncryptionCoreProvider sInstance = new EncryptionCoreProvider();
private JobScheduler mImpl;
@NonNull
public static EncryptionCoreProvider getInstance() {
return sInstance;
}
static void checkRawResponse(int target, @NonNull Packet result) {
Preconditions.checkNotNull(result);
Preconditions.checkArgument(result.getId() == target,
String.format("id is not the same, expected %s but actual is %s",
ByteFormatter.addHexPrefix(target),
ByteFormatter.addHexPrefix(result.getId())));
final Payload responseCodePayload = result.getPayload(CONSTANTS.TAGS.RESPONSE_CODE);
Preconditions.checkNotNull(responseCodePayload,
"can not find response code");
final int responseCode = responseCodePayload.toInt();
if (responseCode != CONSTANTS.VALS.SUCCESS_RESPONSE) {
final Payload errorMessagePayload = result.getPayload(CONSTANTS.TAGS.ERROR_MESSAGE);
final String errorMessage;
if (errorMessagePayload != null) {
errorMessage = errorMessagePayload.toUtf8();
} else {
errorMessage = null;
}
throw new EncryptionCoreException(responseCode, errorMessage);
}
}
public void initialize(@NonNull Context context) {
Preconditions.checkState(Looper.getMainLooper() == Looper.myLooper(), "should initialize in main loop");
Preconditions.checkState(mImpl == null, "should not initialize again");
mImpl = new JobSchedulerWrapper(new ImplementProvider().getImplement(context));
}
@NonNull
public JobScheduler getImpl() {
return Preconditions.checkNotNull(mImpl, "should initialize first");
}
private static class JobSchedulerWrapper implements JobScheduler {
private final JobScheduler mBase;
private JobSchedulerWrapper(@NonNull JobScheduler base) {
mBase = Preconditions.checkNotNull(base);
}
@Override
public void offer(@NonNull Packet packet, @NonNull Callback callback) {
mBase.offer(packet, new CallbackWrapper(packet.getId(), callback));
}
}
private static class CallbackWrapper implements Callback {
private final int mId;
private final Callback mBase;
private CallbackWrapper(int id, @NonNull Callback base) {
mId = id;
mBase = Preconditions.checkNotNull(base);
}
@Override
public void onSuccess(@NonNull Packet packet) {
try {
checkRawResponse(mId, packet);
} catch (Exception e) {
onFail(e);
return;
}
mBase.onSuccess(packet);
}
@Override
public void onFail(@NonNull Exception exception) {
mBase.onFail(exception);
}
}
}

105
app/src/main/java/com/cobo/cold/encryption/exception/EncryptionCoreException.java

@ -0,0 +1,105 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.exception;
import android.text.TextUtils;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
import org.json.JSONException;
import org.json.JSONObject;
public class EncryptionCoreException extends RuntimeException {
private static final SparseArrayCompat<String> mDefineMap;
static {
mDefineMap = new SparseArrayCompat<>();
mDefineMap.put(0xFFAA, "ERT_UnderAttack [attacked mode]");
mDefineMap.put(0xFFFF, "ERT_Unauthorized [authorize with 0102 first]");
mDefineMap.put(0x0001, "ERT_InitRngFail [random generator initialization failed]");
mDefineMap.put(0x0002, "ERT_InitFlashFail [flash initialization failed]");
mDefineMap.put(0x0003, "ERT_InitUartFail [uart initialization failed]");
mDefineMap.put(0x0004, "ERT_InitTimerFail [timer initialization failed]");
mDefineMap.put(0x0005, "ERT_InvalidKey [invalid key]");
mDefineMap.put(0x0006, "ERT_RngFail [failed to get random number]");
mDefineMap.put(0x0007, "ERT_SFlashFail [secure Flash operation failed]");
mDefineMap.put(0x0008, "ERT_MallocFail: [heap application failed]");
mDefineMap.put(0x0009, "ERT_GenKeyFail: [key generation failed]");
mDefineMap.put(0x000A, "ERT_ECDSASignFail: [ecdsa signature failed]");
mDefineMap.put(0x000B, "ERT_ECDSAVerifyFail: [ecdsa verification failed]");
mDefineMap.put(0x000C, "ERT_SecpEncryptFail: [secp encryption failed]");
mDefineMap.put(0x000D, "ERT_SecpDecryptFail: [secp decryption failed]");
mDefineMap.put(0x000E, "ERT_CheckSumFail: [checksum verification failed]");
mDefineMap.put(0x000F, "ERT_CheckMD5Fail: [md5 verification failed]");
mDefineMap.put(0x0010, "ERT_FuncParamInvalid: [function parameter error]");
mDefineMap.put(0x0011, "ERT_CommTimeOut: [serial communication timeout]");
mDefineMap.put(0x0012, "ERT_CommInvalidCMD: [serial communication command is not recognized]");
mDefineMap.put(0x0013, "ERT_CommFailEncrypt: [serial communication encryption flag byte error]");
mDefineMap.put(0x0014, "ERT_CommFailLen: [serial communication length byte error]");
mDefineMap.put(0x0015, "ERT_CommFailEtx: [serial communication ETX byte error]");
mDefineMap.put(0x0016, "ERT_CommFailLrc: [serial communication LRC verification failed]");
mDefineMap.put(0x0017, "ERT_CommFailTLV: [serial communication TLV internal error]");
mDefineMap.put(0x0018, "ERT_CommFailParam: [serial communication parameter error]");
mDefineMap.put(0x0019, "ERT_3DESFail: [serial communication 3DES encryption and decryption error]");
mDefineMap.put(0x001A, "ERT_tlvArray_to_buf: [tlv array to buf error]");
mDefineMap.put(0x001B, "ERT_StorageFail: [flash storage error]");
mDefineMap.put(0x001C, "ERT_CKD_Fail: [ckd part error]");
mDefineMap.put(0x001D, "ERT_VerConflict: [firmware version conflict]");
mDefineMap.put(0x001E, "ERT_GetStatsFail: [get firmware status error]");
mDefineMap.put(0x001F, "ERT_HDPathIllegal: [hdpath illegal]");
mDefineMap.put(0x0020, "ERT_WithoutPermission: [permission no match]");
mDefineMap.put(0x0021, "ERT_RecIDFail: [recoveryParam error]");
mDefineMap.put(0x0022, "ERT_NeedPreCMD: [Need pre-command]");
mDefineMap.put(0x0023, "ERT_MnemonicNotMatch: [mnemonic verification does not match]");
mDefineMap.put(0x0024, "ERT_UnexpectedFail: [should not occur]");
}
private int errorCode;
private String errorMessage;
public EncryptionCoreException(int code, @Nullable String error) {
super(toJsonString(code, error));
this.errorCode = code;
this.errorMessage = error;
}
public int getErrorCode() {
return errorCode;
}
public String getErrorMessage() {
return errorMessage;
}
@NonNull
private static String toJsonString(int code, @Nullable String error) {
final JSONObject jsonObject = new JSONObject();
try {
jsonObject.put("code", code);
jsonObject.put("define", mDefineMap.get(code, "Not Found"));
jsonObject.put("error", TextUtils.isEmpty(error) ? "empty error message" : error);
} catch (JSONException e) {
e.printStackTrace();
}
return jsonObject.toString();
}
}

28
app/src/main/java/com/cobo/cold/encryption/interception/Intercept.java

@ -0,0 +1,28 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.interception;
import androidx.annotation.NonNull;
import com.cobo.cold.encryptioncore.base.Packet;
public interface Intercept {
void preIntercept(@NonNull Packet packet);
void postIntercept(@NonNull Packet packet);
}

27
app/src/main/java/com/cobo/cold/encryption/interception/InterceptManager.java

@ -0,0 +1,27 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.interception;
import androidx.annotation.NonNull;
public interface InterceptManager {
int getId();
@NonNull
Intercept getIntercept();
}

39
app/src/main/java/com/cobo/cold/encryption/interception/InterceptManagerGroup.java

@ -0,0 +1,39 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.interception;
import androidx.annotation.Nullable;
import androidx.collection.SparseArrayCompat;
public class InterceptManagerGroup {
private static final SparseArrayCompat<InterceptManager> sMap;
static {
sMap = new SparseArrayCompat<>();
register(new Secp256k1SignIntercept());
}
@Nullable
public static InterceptManager get(int id) {
return sMap.get(id);
}
private static void register(InterceptManager interceptManager) {
sMap.put(interceptManager.getId(), interceptManager);
}
}

89
app/src/main/java/com/cobo/cold/encryption/interception/Secp256k1SignIntercept.java

@ -0,0 +1,89 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.interception;
import androidx.annotation.NonNull;
import com.cobo.cold.encryption.interfaces.CONSTANTS;
import com.cobo.cold.encryption.signature.Signature;
import com.cobo.cold.encryptioncore.base.Packet;
import com.cobo.cold.encryptioncore.base.Payload;
import com.cobo.cold.encryptioncore.utils.ByteFormatter;
import org.spongycastle.util.encoders.Hex;
import java.math.BigInteger;
import java.util.Arrays;
import java.util.Objects;
class Secp256k1SignIntercept implements InterceptManager {
private static boolean isCanonical(byte[] sigs) {
return (sigs[0] & 0x80) == 0
&& !(sigs[0] == 0 && ((sigs[1] & 0x80) == 0))
&& (sigs[32] & 0x80) == 0
&& !(sigs[32] == 0 && ((sigs[33] & 0x80) == 0));
}
@Override
public int getId() {
return CONSTANTS.METHODS.SIGN;
}
@NonNull
@Override
public Intercept getIntercept() {
return new InterceptImpl();
}
private static final class InterceptImpl implements Intercept {
private String txHash;
@Override
public void preIntercept(@NonNull Packet packet) {
txHash = Objects.requireNonNull(
Objects.requireNonNull(packet.getPayload(CONSTANTS.TAGS.TX_HASH))
.toHex());
}
@Override
public void postIntercept(@NonNull Packet packet) {
final String publicKey = Objects.requireNonNull(packet.getPayload(CONSTANTS.TAGS.PUBLIC_KEY_HASH)).toHex();
final String signed = Objects.requireNonNull(packet.getPayload(CONSTANTS.TAGS.SIGNED)).toHex();
final byte[] signBytes = ByteFormatter.hex2bytes(signed);
if (!isCanonical(signBytes)) {
throw new RuntimeException("couldn't find a canonical signature");
}
final byte[] signR = Arrays.copyOf(signBytes, 32);
final byte[] signS = Arrays.copyOfRange(signBytes, 32, 64);
final BigInteger publicKeyInt = new BigInteger(publicKey, 16);
final int recId = Signature.getRecoverIdFromSignature(publicKeyInt, new BigInteger(1, signR), new BigInteger(1, signS), Hex.decode(txHash));
final byte[] signBytesWithRecId = new byte[signBytes.length + 1];
System.arraycopy(signBytes, 0, signBytesWithRecId, 0, signBytes.length);
signBytesWithRecId[signBytesWithRecId.length - 1] = (byte) recId;
packet.getPayloads().put(CONSTANTS.TAGS.SIGNED, new Payload(signBytesWithRecId));
}
}
}

97
app/src/main/java/com/cobo/cold/encryption/interfaces/BASECONSTANTS.java

@ -0,0 +1,97 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.interfaces;
import com.cobo.cold.update.utils.Digest;
public abstract class BASECONSTANTS {
public interface CONFIG {
Digest DIGEST = Digest.MD5;
int PAGE_SIZE = 1024;
int SPEED = 57600;
}
public interface METHODS {
int TEST = 0x0101;
int GET_FIRMWARE_STATUS = 0x0103;
int GET_FIRMWARE_PARAMETER = 0x0104;
int RESET = 0x0306;
int GET_RANDOM_ENTROPY = 0x0301;
int WRITE_MNEMONIC = 0x0302;
int VERIFY_MNEMONIC = 0x0502;
int UPDATE_PASSPHRASE = 0x0303;
int GET_EXTENDED_PUBLICKEY = 0x0305;
int WEB_AUTH = 0x0701;
int GET_UPDATE_KEY = 0x0702;
int REQUEST_UPDATE = 0x0108;
int WRITE_UPDATE_BYTES = 0x0106;
int SIGN = 0x0307;
int CHANGE_USER_PASSWORD = 0x0901;
int RESET_USER_PASSWORD = 0x0902;
int VERIFY_USER_PASSWORD = 0x0903;
int GET_MESSAGE = 0x0905;
int REGISTER_PUBLIC_KEY = 0x0906;
int VERIFY_FINGERPRINT = 0x0907;
int CLEAR_TOKEN = 0x0908;
}
public interface TAGS {
int RESPONSE_CODE = 0x0002;
int ERROR_MESSAGE = 0x0003;
int FIRMWARE_STATUS = 0x0102;
int FIRMWARE_SN = 0x0111;
int FIRMWARE_APP_VERSION = 0x0106;
int UPDATE_KEY_OPERATION = 0x0701;
int UPDATE_KEY = 0x0702;
int UPDATING_FLASH_POSITION = 0x0107;
int UPDATING_PACKAGE_TYPE = 0x010A;
int UPDATING_PACKAGE = 0x0109;
int UPDATING_CHECKSUM = 0x010C;
int UPDATING_SHA256 = 0x010B;
int TX_HASH = 0x0307;
int PUBLIC_KEY_HASH = 0x0303;
int SIGNED = 0x0308;
int ENTROPY = 0x0202;
int ENTROPY_TYPE = 0x0201;
int ENTROPY_CHECKSUM = 0x030b;
int PATH = 0x0207;
int EXTEND_PUB_KEY = 0x020A;
int MNEMONIC = 0x0203;
int ENCRYPTED = 0x0306;
int SIGNATURE = 0x0308;
int DECRYPTED = 0x0305;
int PASSPHRASE = 0x0204;
int APP_VERSION = 0x010f;
int BOOT_VERSION = 0x0110;
int CURVE = 0x030D;
int NEW_PASSWORD = 0x0401;
int CURRENT_PASSWORD = 0x0402;
int PUBLIC_KEY = 0x0403;
int AUTH_TOKEN = 0x0404;
int NEED_TOKEN = 0x0405;
int MESSAGE = 0x0406;
int MESSAGE_SIGNATURE = 0x0407;
}
public interface VALS {
int SUCCESS_RESPONSE = 0x0000;
int READ_UPDATE_KEY = 0x01;
int UPDATE_FLASH_START_POSITION = 0x10000000;
}
}

171
app/src/main/java/com/cobo/cold/encryption/signature/Signature.java

@ -0,0 +1,171 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.encryption.signature;
import androidx.annotation.NonNull;
import com.cobo.cold.encryptioncore.utils.Preconditions;
import org.spongycastle.asn1.x9.X9ECParameters;
import org.spongycastle.asn1.x9.X9IntegerConverter;
import org.spongycastle.crypto.ec.CustomNamedCurves;
import org.spongycastle.crypto.params.ECDomainParameters;
import org.spongycastle.math.ec.ECAlgorithms;
import org.spongycastle.math.ec.ECPoint;
import org.spongycastle.math.ec.custom.sec.SecP256K1Curve;
import java.math.BigInteger;
import java.util.Arrays;
/**
* Migrate from web3j source code
*/
public class Signature {
private static final X9ECParameters CURVE_PARAMS = CustomNamedCurves.getByName("secp256k1");
private static final ECDomainParameters CURVE = new ECDomainParameters(
CURVE_PARAMS.getCurve(), CURVE_PARAMS.getG(), CURVE_PARAMS.getN(), CURVE_PARAMS.getH());
public static int getRecoverIdFromSignature(final BigInteger publicKey, @NonNull BigInteger signR, @NonNull BigInteger signS, @NonNull byte[] message) {
Preconditions.checkNotNull(publicKey, "public key cannot be null");
Preconditions.checkArgument(signR.signum() >= 0, "r must be positive");
Preconditions.checkArgument(signS.signum() >= 0, "s must be positive");
Preconditions.checkNotNull(message, "message cannot be null");
int recId = -1;
for (int i = 0; i < 4; ++i) {
final BigInteger k = recoverFromSignature(i, signR, signS, message);
if (k != null && k.equals(publicKey)) {
recId = i;
break;
}
}
if (recId == -1) {
throw new RuntimeException(
"Could not construct a recoverable key. This should never happen.");
}
return recId;
}
public static byte[] decompress(@NonNull final byte[] compressedBytes) {
Preconditions.checkNotNull(compressedBytes, "key array should not be null");
Preconditions.checkArgument(compressedBytes.length == 33, String.format("Illegal key bytes length, need 33 but get %s", compressedBytes.length));
Preconditions.checkArgument(compressedBytes[0] == 2 || compressedBytes[0] == 3, "Illegal compress public key, the first byte of array should be 2 or 3");
final ECPoint point = CURVE.getCurve().decodePoint(compressedBytes);
final byte[] unCompressedBytes = point.getEncoded(false);
return Arrays.copyOfRange(unCompressedBytes, 1, unCompressedBytes.length);
}
/**
* <p>Given the components of a signature and a selector value, recover and return the public
* key that generated the signature according to the algorithm in SEC1v2 section 4.1.6.</p>
* <p>
* <p>The recId is an index from 0 to 3 which indicates which of the 4 possible keys is the
* correct one. Because the key recovery operation yields multiple potential keys, the correct
* key must either be stored alongside the
* signature, or you must be willing to try each recId in turn until you find one that outputs
* the key you are expecting.</p>
* <p>
* <p>If this method returns null it means recovery was not possible and recId should be
* iterated.</p>
* <p>
* <p>Given the above two points, a correct usage of this method is inside a for loop from
* 0 to 3, and if the output is null OR a key that is not the one you expect, you try again
* with the next recId.</p>
*
* @param recId Which possible key to recover.
* @param signR the R components of the signature.
* @param signS the S components of the signature.
* @param message Hash of the data that was signed.
* @return An ECKey containing only the public part, or null if recovery wasn't possible.
*/
private static BigInteger recoverFromSignature(final int recId, @NonNull BigInteger signR, @NonNull BigInteger signS, @NonNull byte[] message) {
// 1.0 For j from 0 to h (h == recId here and the loop is outside this function)
// 1.1 Let x = r + jn
final BigInteger n = CURVE.getN(); // Curve order.
final BigInteger i = BigInteger.valueOf((long) recId / 2);
final BigInteger x = signR.add(i.multiply(n));
// 1.2. Convert the integer x to an octet string X of length mlen using the conversion
// routine specified in Section 2.3.7, where mlen = ⌈(log2 p)/8⌉ or mlen = ⌈m/8⌉.
// 1.3. Convert the octet string (16 set binary digits)||X to an elliptic curve point R
// using the conversion routine specified in Section 2.3.4. If this conversion
// routine outputs "invalid", then do another iteration of Step 1.
//
// More concisely, what these points mean is to use X as a compressed public key.
final BigInteger prime = SecP256K1Curve.q;
if (x.compareTo(prime) >= 0) {
// Cannot have point co-ordinates larger than this as everything takes place modulo Q.
return null;
}
// Compressed keys require you to know an extra bit of data about the y-coord as there are
// two possibilities. So it's encoded in the recId.
final ECPoint R = decompressKey(x, (recId & 1) == 1);
// 1.4. If nR != point at infinity, then do another iteration of Step 1 (callers
// responsibility).
if (!R.multiply(n).isInfinity()) {
return null;
}
// 1.5. Compute e from M using Steps 2 and 3 of ECDSA signature verification.
final BigInteger e = new BigInteger(1, message);
// 1.6. For k from 1 to 2 do the following. (loop is outside this function via
// iterating recId)
// 1.6.1. Compute a candidate public key as:
// Q = mi(r) * (sR - eG)
//
// Where mi(x) is the modular multiplicative inverse. We transform this into the following:
// Q = (mi(r) * s ** R) + (mi(r) * -e ** G)
// Where -e is the modular additive inverse of e, that is z such that z + e = 0 (mod n).
// In the above equation ** is point multiplication and + is point addition (the EC group
// operator).
//
// We can find the additive inverse by subtracting e from zero then taking the mod. For
// example the additive inverse of 3 modulo 11 is 8 because 3 + 8 mod 11 = 0, and
// -3 mod 11 = 8.
final BigInteger eInv = BigInteger.ZERO.subtract(e).mod(n);
final BigInteger rInv = signR.modInverse(n);
final BigInteger srInv = rInv.multiply(signS).mod(n);
final BigInteger eInvrInv = rInv.multiply(eInv).mod(n);
final ECPoint q = ECAlgorithms.sumOfTwoMultiplies(CURVE.getG(), eInvrInv, R, srInv);
final byte[] qBytes = q.getEncoded(false);
// We remove the prefix
return new BigInteger(1, Arrays.copyOfRange(qBytes, 1, qBytes.length));
}
/**
* Decompress a compressed public key (x co-ord and low-bit of y-coord).
*/
private static ECPoint decompressKey(BigInteger xBN, boolean yBit) {
final X9IntegerConverter x9 = new X9IntegerConverter();
final byte[] compEnc = x9.integerToBytes(xBN, 1 + x9.getByteLength(CURVE.getCurve()));
compEnc[0] = (byte) (yBit ? 0x03 : 0x02);
return CURVE.getCurve().decodePoint(compEnc);
}
}

26
app/src/main/java/com/cobo/cold/fingerprint/EnrollListener.java

@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.fingerprint;
public interface EnrollListener {
void onEnrollmentError(int errMsgId, CharSequence errString);
void onEnrollmentHelp(int helpMsgId, CharSequence helpString);
void onEnrollmentProgress(int remaining);
}

243
app/src/main/java/com/cobo/cold/fingerprint/FingerprintKit.java

@ -0,0 +1,243 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.fingerprint;
import android.annotation.UserIdInt;
import android.app.admin.DevicePolicyManager;
import android.content.Context;
import android.hardware.fingerprint.Fingerprint;
import android.hardware.fingerprint.FingerprintManager;
import android.os.CancellationSignal;
import android.os.UserHandle;
import android.text.TextUtils;
import android.util.Log;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import com.android.internal.widget.LockPatternUtils;
import com.cobo.cold.AppExecutors;
import com.cobo.cold.Utilities;
import com.cobo.cold.util.HashUtil;
import java.util.List;
import java.util.Objects;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import static android.content.Context.FINGERPRINT_SERVICE;
public class FingerprintKit {
private static final String TAG = "Vault.FingerprintKit";
private FingerprintManager fp;
private final Context mContext;
private final LockPatternUtils lockPatternUtils;
@UserIdInt
private final int mUserId;
private final ExecutorService sExecutor = Executors.newSingleThreadExecutor();
private CancellationSignal mCancellationSignal;
private boolean isEnrolling;
private boolean isVerifying;
public FingerprintKit(Context context) {
mContext = context;
lockPatternUtils = new LockPatternUtils(mContext);
mUserId = UserHandle.myUserId();
fp = (FingerprintManager) mContext.getSystemService(FINGERPRINT_SERVICE);
if (!lockPatternUtils.isLockPasswordEnabled(mUserId)) {
String password = HashUtil.generateRandomPassword(20);
lockPatternUtils.saveLockPassword(password, null,
DevicePolicyManager.PASSWORD_QUALITY_NUMERIC, mUserId);
Utilities.setFingerprintPassword(mContext, password);
}
}
public static boolean isHardwareDetected(Context context) {
FingerprintManager fm = (FingerprintManager) context.getSystemService(FINGERPRINT_SERVICE);
return fm.isHardwareDetected();
}
public static void verifyPassword(Context context) {
AppExecutors.getInstance().diskIO().execute(() -> {
try {
String password = Utilities.getFingerprintPassword(context);
if (!TextUtils.isEmpty(password)) {
new LockPatternUtils(context).verifyPassword(password,
0L, UserHandle.myUserId());
}
} catch (LockPatternUtils.RequestThrottledException e) {
e.printStackTrace();
}
});
}
public List<Fingerprint> getEnrolledFingerprints() {
Objects.requireNonNull(fp, "fp is null");
return fp.getEnrolledFingerprints();
}
public boolean hasEnrolledFingerprint() {
Objects.requireNonNull(fp, "fp is null");
return fp.hasEnrolledFingerprints();
}
public void renameFingerprint(@NonNull Fingerprint fingerprint, @NonNull String newName) {
fp.rename(fingerprint.getFingerId(), UserHandle.myUserId(), newName);
}
public void removeFingerprint(@NonNull Fingerprint fingerprint, RemovalListener listener) {
fp.remove(fingerprint, UserHandle.myUserId(), new FingerprintManager.RemovalCallback() {
@Override
public void onRemovalError(Fingerprint fp, int errMsgId, CharSequence errString) {
super.onRemovalError(fp, errMsgId, errString);
if (listener != null) {
listener.onError(errMsgId, errString.toString());
}
}
@Override
public void onRemovalSucceeded(Fingerprint fp, int remaining) {
super.onRemovalSucceeded(fp, remaining);
if (listener != null) {
listener.onSuccess();
}
}
});
}
public void cancelEnroll() {
if (isEnrolling
&& mCancellationSignal != null
&& !mCancellationSignal.isCanceled()) {
mCancellationSignal.cancel();
isEnrolling = false;
}
}
public void startEnroll(@Nullable EnrollListener listener) {
long challenge = fp.preEnroll();
final Future<byte[]> future = sExecutor.submit(()
-> lockPatternUtils.verifyPassword(Utilities.getFingerprintPassword(mContext),
challenge, mUserId));
byte[] token;
try {
token = future.get();
} catch (ExecutionException | InterruptedException e) {
e.printStackTrace();
return;
}
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
mCancellationSignal = new CancellationSignal();
fp.setActiveUser(mUserId);
isEnrolling = true;
fp.enroll(token, mCancellationSignal, 0, mUserId, new FingerprintManager.EnrollmentCallback() {
@Override
public void onEnrollmentError(int errMsgId, CharSequence errString) {
super.onEnrollmentError(errMsgId, errString);
if (listener != null) {
listener.onEnrollmentError(errMsgId, errString);
}
mCancellationSignal.cancel();
isEnrolling = false;
}
@Override
public void onEnrollmentHelp(int helpMsgId, CharSequence helpString) {
super.onEnrollmentHelp(helpMsgId, helpString);
if (listener != null) {
listener.onEnrollmentHelp(helpMsgId, helpString);
}
}
@Override
public void onEnrollmentProgress(int remaining) {
super.onEnrollmentProgress(remaining);
if (listener != null) {
listener.onEnrollmentProgress(remaining);
}
if (remaining == 0) {
fp.postEnroll();
isEnrolling = false;
}
}
});
}
public void cancelVerify() {
if (isVerifying
&& mCancellationSignal != null
&& !mCancellationSignal.isCanceled()) {
mCancellationSignal.cancel();
isVerifying = false;
}
}
public void startVerify(@NonNull VerifyListener listener, FingerprintManager.CryptoObject object) {
if (mCancellationSignal != null) {
mCancellationSignal.cancel();
}
mCancellationSignal = new CancellationSignal();
isVerifying = true;
Log.w(TAG, "fp kit startVerify");
fp.authenticate(object, mCancellationSignal, 0,
new FingerprintManager.AuthenticationCallback() {
int failCount = 0;
@Override
public void onAuthenticationError(int errorCode, CharSequence errString) {
listener.onAuthenticationError(errorCode, errString);
isVerifying = false;
mCancellationSignal.cancel();
}
@Override
public void onAuthenticationHelp(int helpCode, CharSequence helpString) {
}
@Override
public void onAuthenticationSucceeded(FingerprintManager.AuthenticationResult result) {
listener.onAuthenticationSucceeded(result.getCryptoObject());
isVerifying = false;
mCancellationSignal.cancel();
}
@Override
public void onAuthenticationFailed() {
failCount++;
if (failCount == 5) {
onAuthenticationError(0, "");
}
}
@Override
public void onAuthenticationAcquired(int acquireInfo) {
}
}, null, mUserId);
}
}

24
app/src/main/java/com/cobo/cold/fingerprint/RemovalListener.java

@ -0,0 +1,24 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.fingerprint;
public interface RemovalListener {
void onSuccess();
void onError(int errMsgId, String errString);
}

26
app/src/main/java/com/cobo/cold/fingerprint/VerifyListener.java

@ -0,0 +1,26 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.fingerprint;
import android.hardware.fingerprint.FingerprintManager;
public interface VerifyListener {
void onAuthenticationError(int errorCode, CharSequence errString);
void onAuthenticationSucceeded(FingerprintManager.CryptoObject cryptoObject);
}

192
app/src/main/java/com/cobo/cold/logging/FileLogger.java

@ -0,0 +1,192 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.logging;
import android.content.Context;
import android.os.Environment;
import android.util.Log;
import com.cobo.cold.update.utils.Storage;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.text.DateFormat;
import java.text.SimpleDateFormat;
import java.util.Date;
import java.util.Locale;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.zip.ZipEntry;
import java.util.zip.ZipOutputStream;
public class FileLogger {
private static final String TAG = "Vault.FileLogger";
private static final String DATE_FORMAT = "yyyyMMddHHmmssSSS";
private static final long LOG_PERSIST_LIMIT = 20;
private static final String LOG_PATTERN = "^(main|system|crash)\\.(\\d+)\\.log$";
public static void init(Context context) {
File logDir = getLogDir(context);
long logSequence = getLogSequence(context);
File coboLog = new File(logDir, "main." + logSequence + ".log");
File sysLog = new File(logDir, "system." + logSequence + ".log");
File crashLog = new File(logDir, "crash." + logSequence + ".log");
try {
Runtime.getRuntime().exec("logcat -c");
Runtime.getRuntime().exec("logcat -f " + coboLog + " -b main *:I");
Runtime.getRuntime().exec("logcat -f " + sysLog + " -b system *:E");
Runtime.getRuntime().exec("logcat -f " + crashLog + " -b crash");
int pid = android.os.Process.myPid();
Log.i(TAG, "Package: " + context.getPackageName() + " Pid: " + pid);
} catch (IOException e) {
e.printStackTrace();
}
}
public static boolean exportLogfiles(Context context) {
Storage storage = Storage.createByEnvironment(context);
if (storage == null) {
Log.e(TAG, "failed to export log files, removable storage not found");
return false;
}
File removableStorage = storage.getExternalDir();
if (removableStorage == null) {
Log.e(TAG, "failed to export log files, removable storage not found");
return false;
}
Log.i(TAG, "export log files: " + removableStorage);
File exportFile = new File(removableStorage, "logs-" + getTimestamp() + ".zip");
return compressLogs(getLogDir(context), exportFile);
}
public static void purgeLogs(Context context) {
long logSequence = getLogSequence(context);
File logDir = getLogDir(context);
File[] files = logDir.listFiles();
if (files != null) {
Pattern pattern = Pattern.compile(LOG_PATTERN);
for (File f : files) {
Matcher matcher = pattern.matcher(f.getName());
if (matcher.matches()) {
try {
long seq = Long.parseLong(matcher.group(2));
if (seq < logSequence - LOG_PERSIST_LIMIT) {
f.delete();
}
} catch (SecurityException | NumberFormatException e) {
Log.e(TAG, "failed to delete log file: " + e.toString());
}
}
}
}
}
public static void purgeOldLogs(Context context) {
File logDir = getLogDir(context);
File[] files = logDir.listFiles();
if (files != null) {
Pattern pattern = Pattern.compile("^(\\d{17})_(main|system|crash)\\.log$");
for (File f : files) {
Matcher matcher = pattern.matcher(f.getName());
if (matcher.matches()) {
try {
f.delete();
} catch (SecurityException e) {
Log.e(TAG, "failed to delete log file: " + e.toString());
}
}
}
}
}
private static File getLogDir(Context context) {
File storage = context.getExternalCacheDir();
File logDir = new File(storage + "/logs");
if (!logDir.exists()) {
logDir.mkdir();
}
Log.d(TAG, logDir.getAbsolutePath());
return logDir;
}
private static long getLogSequence(Context context) {
long maxSequence = -1;
File logDir = getLogDir(context);
File[] files = logDir.listFiles();
if (files != null) {
Pattern pattern = Pattern.compile("^(main|system|crash)\\.(\\d+)\\.log$");
for (File f : files) {
Matcher matcher = pattern.matcher(f.getName());
if (matcher.matches()) {
try {
long seq = Long.parseLong(matcher.group(2));
maxSequence = Math.max(seq, maxSequence);
} catch (NumberFormatException ignored) {
}
}
}
}
return maxSequence + 1;
}
private static String getTimestamp() {
DateFormat formatter = new SimpleDateFormat(DATE_FORMAT, Locale.getDefault());
return formatter.format(new Date());
}
private static File getRemovableExternalStorage(Context context) {
File[] dirs = context.getExternalFilesDirs(null);
for (File dir : dirs) {
if (dir != null && dir.isDirectory() && Environment.isExternalStorageRemovable(dir)) {
return dir;
}
}
return null;
}
private static boolean compressLogs(File dir, File outputFile) {
Log.d(TAG, dir.getAbsolutePath());
Log.d(TAG, outputFile.getAbsolutePath());
try {
FileOutputStream fos = new FileOutputStream(outputFile);
ZipOutputStream zos = new ZipOutputStream(fos);
File[] files = dir.listFiles();
if (files != null) {
for (File f : files) {
byte[] buffer = new byte[1024];
FileInputStream fis = new FileInputStream(f);
zos.putNextEntry(new ZipEntry(f.getName()));
int length;
while ((length = fis.read(buffer)) > 0) {
zos.write(buffer, 0, length);
}
zos.closeEntry();
fis.close();
}
}
zos.close();
return true;
} catch (IOException e) {
e.printStackTrace();
return false;
}
}
}

202
app/src/main/java/com/cobo/cold/mnemonic/AutoCompleteInput.java

@ -0,0 +1,202 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.mnemonic;
import android.annotation.SuppressLint;
import android.content.Context;
import android.graphics.Rect;
import android.text.InputType;
import android.util.AttributeSet;
import android.view.View;
import android.view.inputmethod.CompletionInfo;
import android.view.inputmethod.InputMethodManager;
import android.widget.AdapterView;
import android.widget.ArrayAdapter;
import android.widget.Button;
import android.widget.ScrollView;
import androidx.annotation.ColorInt;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.appcompat.widget.AppCompatAutoCompleteTextView;
import androidx.collection.ArraySet;
import com.cobo.coinlib.WordList;
import com.cobo.cold.R;
import java.util.Arrays;
import java.util.Set;
public class AutoCompleteInput extends AppCompatAutoCompleteTextView {
private static final Set<String> sWordsSet = new ArraySet<>(Arrays.asList(WordList.words));
@ColorInt
private static int sNormalTextColor;
@ColorInt
private static int sWrongTextColor;
@Nullable
private FilterCompleteCallback mCallback;
private ScrollView scrollView;
public AutoCompleteInput(Context context, AttributeSet attrs) {
super(context, attrs);
sNormalTextColor = context.getColor(R.color.text);
sWrongTextColor = context.getColor(R.color.text_wrong);
InputServant mInputServant = new InputServant(this);
setAdapter(new ArrayAdapter<>(context, R.layout.mnemonic_dropdowm_item, WordList.words));
setFilterCompleteCallback(mInputServant);
setOnFocusChangeListener(mInputServant);
setOnItemClickListener(mInputServant);
setInputType(InputType.TYPE_TEXT_VARIATION_PASSWORD);
setTransformationMethod(null);
}
@Override
public void showDropDown() {
super.showDropDown();
}
private void adjustScroll() {
Rect displayFrame = new Rect();
getWindowVisibleDisplayFrame(displayFrame);
int[] locationOnScreen = new int[2];
getLocationOnScreen(locationOnScreen);
int bottom = locationOnScreen[1] + getHeight();
int availableHeightBelow = displayFrame.bottom - bottom;
if (availableHeightBelow < 200) {
if (scrollView != null) {
scrollView.smoothScrollBy(0, 200 - availableHeightBelow);
}
}
}
@Override
protected void onSizeChanged(int w, int h, int oldw, int oldh) {
super.onSizeChanged(w, h, oldw, oldh);
scrollView = (ScrollView) getParent().getParent().getParent().getParent();
}
@Override
public void onFilterComplete(int count) {
super.onFilterComplete(count);
if (mCallback != null) {
mCallback.onFilterComplete(this, count);
}
}
public void setFilterCompleteCallback(FilterCompleteCallback callback) {
mCallback = callback;
}
@Nullable
public FilterCompleteCallback getFilterCompleteCallback() {
return mCallback;
}
public interface FilterCompleteCallback {
void onFilterComplete(AutoCompleteInput view, int count);
}
@Override
public View focusSearch(int direction) {
return super.focusSearch(direction);
}
public void onInputComplete() {
if (scrollView != null) {
Button button = scrollView.findViewById(R.id.import_mnemonic);
if (button != null) {
button.requestFocus();
} else {
button = scrollView.findViewById(R.id.verify_mnemonic);
if (button != null) {
button.requestFocus();
}
}
scrollView.smoothScrollTo(0, 0);
}
}
private static class InputServant implements AdapterView.OnItemClickListener,
AutoCompleteInput.FilterCompleteCallback, View.OnFocusChangeListener {
private final AutoCompleteInput mHost;
final boolean focusNextOnClick = true;
final boolean autoCompleteOnMatch = true;
private InputServant(@NonNull AutoCompleteInput host) {
mHost = host;
}
@Override
public void onItemClick(AdapterView<?> parent, View view, int position, long id) {
if (focusNextOnClick) {
@SuppressLint("WrongConstant") final View v = mHost.focusSearch(View.FOCUS_FORWARD);
if (isNextFocusableView(v)) {
v.requestFocus();
} else {
mHost.onInputComplete();
final InputMethodManager imm = (InputMethodManager) mHost.getContext().getSystemService(Context.INPUT_METHOD_SERVICE);
if (imm != null) {
imm.hideSoftInputFromWindow(mHost.getWindowToken(), 0);
}
}
}
}
private boolean isNextFocusableView(View view) {
if (view == null || view.getTag() == null) {
return false;
}
int oldIndex = (int) mHost.getTag();
int newIndex = (int) view.getTag();
return view.isFocusableInTouchMode() && oldIndex < newIndex;
}
@Override
public void onFilterComplete(AutoCompleteInput view, int count) {
mHost.setTextColor(count > 0 ? sNormalTextColor : sWrongTextColor);
if (autoCompleteOnMatch && count == 1) {
mHost.onCommitCompletion(new CompletionInfo(0, 0, null));
mHost.performCompletion();
}
}
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (!hasFocus) {
final String text = mHost.getText().toString();
final boolean valid = sWordsSet.contains(text);
mHost.setTextColor(valid ? sNormalTextColor : sWrongTextColor);
} else {
mHost.adjustScroll();
}
}
}
}

147
app/src/main/java/com/cobo/cold/mnemonic/MnemonicInputTable.java

@ -0,0 +1,147 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.mnemonic;
import android.content.Context;
import android.util.AttributeSet;
import android.view.LayoutInflater;
import android.view.ViewGroup;
import android.view.inputmethod.EditorInfo;
import androidx.annotation.IntDef;
import androidx.annotation.NonNull;
import androidx.annotation.Nullable;
import androidx.databinding.DataBindingUtil;
import androidx.databinding.ObservableField;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.cobo.cold.R;
import com.cobo.cold.databinding.MnemonicInputItemBinding;
import java.util.ArrayList;
import java.util.List;
public class MnemonicInputTable extends RecyclerView {
public static final int TWELVE = 12;
public static final int EIGHTEEN = 18;
public static final int TWEENTYFOUR = 24;
private final List<ObservableField<String>> wordsList = new ArrayList<>();
private int mMnemonicCount = TWEENTYFOUR;
private MnemonicAdapter mAdapter;
private boolean editable = true;
public MnemonicInputTable(@NonNull Context context) {
this(context, null);
}
public MnemonicInputTable(@NonNull Context context, @Nullable AttributeSet attrs) {
this(context, attrs, -1);
}
public MnemonicInputTable(@NonNull Context context, @Nullable AttributeSet attrs, int defStyle) {
super(context, attrs, defStyle);
initTable();
fillWordlist();
}
private void fillWordlist() {
for (int i = 0; i < mMnemonicCount; i++) {
wordsList.add(new ObservableField<>());
}
}
public void setEditable(boolean editable) {
this.editable = editable;
}
private void initTable() {
setLayoutManager(new GridLayoutManager(getContext(), 3));
addItemDecoration(new TableItemDecoration(getContext()));
mAdapter = new MnemonicAdapter();
setAdapter(mAdapter);
}
public List<ObservableField<String>> getWordsList() {
return wordsList;
}
public void setMnemonicNumber(@MnemonicCount int mnemonicType) {
mMnemonicCount = mnemonicType;
wordsList.clear();
fillWordlist();
mAdapter.setMnemonicCount(mMnemonicCount);
mAdapter.notifyDataSetChanged();
}
@IntDef({TWELVE, EIGHTEEN, TWEENTYFOUR})
public @interface MnemonicCount {
}
class MnemonicAdapter extends RecyclerView.Adapter<MnemonicHolder> {
private int count;
public void setMnemonicCount(@MnemonicCount int count) {
this.count = count;
}
@NonNull
@Override
public MnemonicHolder onCreateViewHolder(@NonNull ViewGroup parent, int viewType) {
MnemonicInputItemBinding binding =
DataBindingUtil.inflate(LayoutInflater.from(parent.getContext()),
R.layout.mnemonic_input_item, parent, false);
return new MnemonicHolder(binding);
}
@Override
public void onBindViewHolder(@NonNull MnemonicHolder holder, int position) {
holder.binding.setIndex(position);
holder.binding.setWord(wordsList.get(position));
if (!editable) {
holder.binding.input.setFocusable(false);
holder.binding.input.setFocusableInTouchMode(false);
}
if (position < mMnemonicCount - 1) {
holder.binding.input.setImeOptions(EditorInfo.IME_ACTION_NEXT);
} else {
holder.binding.input.setImeOptions(EditorInfo.IME_ACTION_DONE);
}
}
@Override
public int getItemCount() {
return count;
}
}
private class MnemonicHolder extends ViewHolder {
private final MnemonicInputItemBinding binding;
public MnemonicHolder(@NonNull MnemonicInputItemBinding binding) {
super(binding.getRoot());
this.binding = binding;
}
}
}

114
app/src/main/java/com/cobo/cold/mnemonic/TableItemDecoration.java

@ -0,0 +1,114 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.mnemonic;
import android.content.Context;
import android.graphics.Canvas;
import android.graphics.Paint;
import android.util.TypedValue;
import android.view.View;
import androidx.annotation.NonNull;
import androidx.recyclerview.widget.GridLayoutManager;
import androidx.recyclerview.widget.RecyclerView;
import com.cobo.cold.R;
class TableItemDecoration extends RecyclerView.ItemDecoration {
private final Paint mPaint;
private final int dividerWidth;
public TableItemDecoration(Context context) {
mPaint = new Paint();
mPaint.setColor(context.getColor(R.color.white40));
dividerWidth = dp2px(context, 1);
mPaint.setStrokeWidth(dividerWidth);
}
@Override
public void onDrawOver(@NonNull Canvas c, @NonNull RecyclerView parent, @NonNull RecyclerView.State state) {
drawHorizontal(c, parent);
drawVertical(c, parent);
}
private int getSpanCount(RecyclerView parent) {
int spanCount = -1;
RecyclerView.LayoutManager layoutManager = parent.getLayoutManager();
if (layoutManager instanceof GridLayoutManager) {
spanCount = ((GridLayoutManager) layoutManager).getSpanCount();
}
return spanCount;
}
public void drawHorizontal(Canvas c, RecyclerView parent) {
int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final int left = child.getLeft();
final int right = child.getRight();
final int top = child.getBottom();
if (i + getSpanCount(parent) >= childCount) {
c.drawLine(left, top - dividerWidth, right, top - dividerWidth, mPaint);
} else {
c.drawLine(left, top, right, top, mPaint);
}
//first row
if (i < getSpanCount(parent)) {
c.drawLine(left, child.getTop() + dividerWidth / 2, right,
child.getTop() + dividerWidth / 2, mPaint);
}
}
}
public void drawVertical(Canvas c, RecyclerView parent) {
final int childCount = parent.getChildCount();
for (int i = 0; i < childCount; i++) {
final View child = parent.getChildAt(i);
final int top = child.getTop();
final int bottom = child.getBottom();
final int left = child.getRight();
//last column
if ((i + 1) % getSpanCount(parent) == 0) {
c.drawLine(left - dividerWidth, top,
left - dividerWidth, bottom, mPaint);
} else {
c.drawLine(left, top, left, bottom, mPaint);
}
//first column
if (i % getSpanCount(parent) == 0) {
c.drawLine(child.getLeft() + dividerWidth / 2, top,
child.getLeft() + dividerWidth / 2, bottom, mPaint);
}
}
}
private int dp2px(Context context, int dp) {
return (int) TypedValue.applyDimension(TypedValue.COMPLEX_UNIT_DIP,
dp, context.getResources().getDisplayMetrics());
}
}

36
app/src/main/java/com/cobo/cold/model/Address.java

@ -0,0 +1,36 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.model;
public interface Address {
long getId();
String getBelongTo();
int getIndex();
String getAddressString();
String getName();
void setName(String name);
String getPath();
String getCoinId();
}

42
app/src/main/java/com/cobo/cold/model/Coin.java

@ -0,0 +1,42 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.model;
public interface Coin {
long getId();
String getBelongTo();
String getCoinId();
String getName();
String getCoinCode();
int getIconResId();
int getAddressCount();
boolean isShow();
String getExPub();
int getIndex();
void setIndex(int index);
}

44
app/src/main/java/com/cobo/cold/model/Tx.java

@ -0,0 +1,44 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.model;
public interface Tx {
String getTxId();
String getCoinId();
String getCoinCode();
String getAmount();
String getFrom();
String getTo();
String getFee();
String getSignedHex();
long getTimeStamp();
String getMemo();
String getSignId();
String getBelongTo();
}

47
app/src/main/java/com/cobo/cold/protocol/EncodeConfig.java

@ -0,0 +1,47 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.protocol;
public class EncodeConfig {
public boolean compress = true;
public Encoding encoding = Encoding.BASE64;
public Format format = Format.PROTOBUF;
public EncodeConfig() {
}
public EncodeConfig(boolean compress, Encoding encoding, Format format) {
this.compress = compress;
this.encoding = encoding;
this.format = format;
}
public static final EncodeConfig DEFAULT
= new EncodeConfig(true, Encoding.BASE64, Format.PROTOBUF);
public enum Encoding {
BASE64,
Hex,
}
public enum Format {
JSON,
PROTOBUF
}
}

59
app/src/main/java/com/cobo/cold/protocol/ZipUtil.java

@ -0,0 +1,59 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.protocol;
import java.io.ByteArrayInputStream;
import java.io.ByteArrayOutputStream;
import java.io.IOException;
import java.util.zip.GZIPInputStream;
import java.util.zip.GZIPOutputStream;
public class ZipUtil {
public static byte[] zip(byte[] data) {
ByteArrayOutputStream out = new ByteArrayOutputStream();
GZIPOutputStream gzip;
try {
gzip = new GZIPOutputStream(out);
gzip.write(data);
gzip.close();
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
public static byte[] unzip(byte[] bytes) {
if (bytes == null || bytes.length == 0) {
return null;
}
ByteArrayOutputStream out = new ByteArrayOutputStream();
ByteArrayInputStream in = new ByteArrayInputStream(bytes);
try {
GZIPInputStream ungzip = new GZIPInputStream(in);
byte[] buffer = new byte[256];
int n;
while ((n = ungzip.read(buffer)) >= 0) {
out.write(buffer, 0, n);
}
} catch (IOException e) {
e.printStackTrace();
}
return out.toByteArray();
}
}

138
app/src/main/java/com/cobo/cold/protocol/builder/BaseBuilder.java

@ -0,0 +1,138 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.protocol.builder;
import android.os.SystemProperties;
import android.text.TextUtils;
import android.util.Log;
import com.cobo.cold.BuildConfig;
import com.cobo.cold.callables.GetUuidCallable;
import com.cobo.cold.encryptioncore.utils.ByteFormatter;
import com.cobo.cold.protobuf.BaseProtoc;
import com.cobo.cold.protobuf.PayloadProtoc;
import com.cobo.cold.protobuf.SignTransactionResultProtoc;
import com.cobo.cold.protobuf.SyncProtoc;
import com.cobo.cold.protocol.EncodeConfig;
import com.cobo.cold.protocol.ZipUtil;
import com.googlecode.protobuf.format.JsonFormat;
import org.spongycastle.util.encoders.Base64;
public class BaseBuilder {
protected final BaseProtoc.Base.Builder base;
protected PayloadProtoc.Payload.Builder payload;
protected SyncProtoc.Sync.Builder sync;
protected SignTransactionResultProtoc.SignTransactionResult.Builder signTxResult;
private final EncodeConfig config;
BaseBuilder(PayloadProtoc.Payload.Type type, EncodeConfig config) {
Header header = new Header();
base = BaseProtoc.Base.newBuilder()
.setVersion(header.version)
.setDescription(header.description)
.setDeviceType(header.deviceType)
.setColdVersion(header.coldVersion);
initPayload(type, header);
this.config = config;
}
private void initPayload(PayloadProtoc.Payload.Type type, Header header) {
payload = PayloadProtoc.Payload.newBuilder()
.setUuid(header.uuid);
switch (type) {
case TYPE_SYNC:
sync = SyncProtoc.Sync.newBuilder();
break;
case TYPE_SIGN_TX_RESULT:
signTxResult = SignTransactionResultProtoc.SignTransactionResult.newBuilder();
break;
}
payload.setType(type);
}
public String build() {
base.setData(payload);
if (BuildConfig.DEBUG) {
String json = new JsonFormat().printToString(base.build());
String TAG = "Vault.QrCode";
Log.d(TAG, "json = " + json);
}
byte[] data = getBytes();
return getEncodedString(data);
}
private String getEncodedString(byte[] data) {
String res;
switch (config.encoding) {
case Hex:
res = ByteFormatter.bytes2hex(data);
break;
case BASE64:
default:
res = Base64.toBase64String(data);
}
return res;
}
private byte[] getBytes() {
byte[] data;
switch (config.format) {
case JSON:
data = new JsonFormat().printToString(base.build()).getBytes();
break;
case PROTOBUF:
default:
data = base.build().toByteArray();
}
data = config.compress ? ZipUtil.zip(data) : data;
return data;
}
class Header {
private final int version = 1;
private final String uuid;
private final String description;
private final int coldVersion;
private final String deviceType;
Header() {
String uuid = getUuid();
this.uuid = TextUtils.isEmpty(uuid) ? " " : uuid;
description = "cobo vault qrcode";
coldVersion = BuildConfig.VERSION_CODE;
deviceType = getDeviceType();
}
private String getDeviceType() {
String boardType = SystemProperties.get("boardtype");
if ("B".equals(boardType)) {
return "Cobo Vault Essential";
} else {
return "Cobo Vault Pro";
}
}
private String getUuid() {
return new GetUuidCallable().call();
}
}
}

53
app/src/main/java/com/cobo/cold/protocol/builder/SignTxResultBuilder.java

@ -0,0 +1,53 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.protocol.builder;
import com.cobo.cold.protobuf.PayloadProtoc;
import com.cobo.cold.protocol.EncodeConfig;
public class SignTxResultBuilder extends BaseBuilder {
public SignTxResultBuilder() {
this(EncodeConfig.DEFAULT);
}
public SignTxResultBuilder(EncodeConfig config) {
super(PayloadProtoc.Payload.Type.TYPE_SIGN_TX_RESULT, config);
}
@Override
public String build() {
payload.setSignTxResult(signTxResult);
return super.build();
}
public SignTxResultBuilder setSignId(String signId) {
signTxResult.setSignId(signId);
return this;
}
public SignTxResultBuilder setTxId(String txId) {
signTxResult.setTxId(txId);
return this;
}
public SignTxResultBuilder setRawTx(String rawTx) {
signTxResult.setRawTx(rawTx);
return this;
}
}

110
app/src/main/java/com/cobo/cold/protocol/builder/SyncBuilder.java

@ -0,0 +1,110 @@
/*
* Copyright (c) 2020 Cobo
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* in the file COPYING. If not, see <http://www.gnu.org/licenses/>.
*/
package com.cobo.cold.protocol.builder;
import android.text.TextUtils;
import com.cobo.cold.encryptioncore.utils.Preconditions;
import com.cobo.cold.protobuf.PayloadProtoc;
import com.cobo.cold.protobuf.SyncProtoc;
import com.cobo.cold.protocol.EncodeConfig;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
public class SyncBuilder extends BaseBuilder {
public SyncBuilder(EncodeConfig config) {
super(PayloadProtoc.Payload.Type.TYPE_SYNC, config);
}
@Override
public String build() {
payload.setSync(sync);
return super.build();
}
public void addCoin(Coin coin) {
sync.addCoins(coin.toProto());
}
public static class Sync {
public List<Coin> coins = new ArrayList<>();
}
public static class Coin {
public String coinCode;
public boolean active;
public final List<Account> accounts = new ArrayList<>();
public Coin() {
}
public Coin setCoinCode(String coinCode) {
this.coinCode = coinCode;
return this;
}
public Coin setActive(boolean active) {
this.active = active;
return this;
}
public Coin setAccounts(List<Account> accounts) {
this.accounts.clear();
this.accounts.addAll(accounts);
return this;
}
public Coin addAccount(Account account) {
this.accounts.add(account);
return this;
}
SyncProtoc.Coin.Builder toProto() {
Preconditions.checkArgument(!TextUtils.isEmpty(coinCode), "coinCode is null");
Preconditions.checkArgument(accounts.size() > 0, "accounts is empty");
List<SyncProtoc.Account> accounts = this.accounts.stream()
.map(a -> a.toProto().build())
.collect(Collectors.toList());
return SyncProtoc.Coin.newBuilder()
.setCoinCode(coinCode)
.setActive(active)
.addAllAccounts(accounts);
}
}
public static class Account {
public String hdPath;
public String xPub;
public int addressLength;
public boolean isMultiSign;
SyncProtoc.Account.Builder toProto() {
Preconditions.checkArgument(!TextUtils.isEmpty(hdPath), "hdPath is null");
Preconditions.checkArgument(!TextUtils.isEmpty(xPub), "xpub is null");
return SyncProtoc.Account.newBuilder()
.setHdPath(hdPath)
.setXPub(xPub)
.setAddressLength(addressLength)
.setIsMultiSign(isMultiSign);
}
}
}

Some files were not shown because too many files changed in this diff

Loading…
Cancel
Save