Skip to content

Commit

Permalink
Merge pull request #95 from smswithoutborders/dev
Browse files Browse the repository at this point in the history
Dev
  • Loading branch information
sherlockwisdom authored Jun 11, 2024
2 parents 47a098c + 17287e7 commit 56ee19b
Show file tree
Hide file tree
Showing 16 changed files with 285 additions and 59 deletions.
3 changes: 1 addition & 2 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,6 @@ KEYSTORE_PASSWD = ks.passwd
DOCKERFILE_FILENAME = Dockerfile
TRACK_FILENAME = track.py

# github_url=https://api.github.com/repos/deku-messaging/Deku-SMS-Android/releases
github_url=https://api.github.com/repos/smswithoutborders/SMSWithoutBorders-App-Android/releases

docker_apk_image=swob_app_apk_image
Expand Down Expand Up @@ -215,7 +214,7 @@ release-cd: requirements.txt bump_version info docker-build-aab
--track ${track} \
--app_bundle_file apk-outputs/${aab_output} \
--app_apk_file apk-outputs/${apk_output} \
--status $(status) \
--status "completed" \
--platforms "all" \
--github_url "${github_url}" \
)
64 changes: 26 additions & 38 deletions README.md
Original file line number Diff line number Diff line change
@@ -1,48 +1,36 @@
# SMSWithoutBorders - Android

## Implementations
### Publishing Payload
```python3
pl = b'g'
encrypted_content=b'...'
device_id=b'...'

### DeviceID
payload = len(encrypted_content) + pl + encrypted_content + device_id
return base64.encode(payload)

**Requirements**

- _secret_key_:- Derived from a DH handshake with Vault. Not used for any form of encryption for messaging sending.
- _phone_number_:- Phone number used in creating Vault account.
- _public_key_:- The Public key used in the DH handshake above.

```python
import hmac
import hashlib

def generate_device_id(secret_key, phone_number, public_key) -> bytes:
# Combine the phone number and public key
combined_input = phone_number + public_key

# Create an HMAC object using the secret key and SHA-256 hash function
hmac_object = hmac.new(secret_key.encode(), combined_input.encode(), hashlib.sha256)

# Generate the device ID as a hexadecimal string
return hmac_object
#unpacking in Python
import struct
import base64

device_id = generate_device_id(...)
payload = base64.decode(incoming_payload)
len_enc_content = struct.unpack("<i", payload[0])
pl = payload[1]
encrypted_content = payload[2:len_enc_content]
device_id = payload[2+len_enc_content:]
```

**Implementation considerations**

- Proof of number ownership is required to avoid spoofing as another user. OTP via SMS when creating account can be used here.

**Payload structure**
```python
import struct
### Platform specific publications (encrypted content)
```python3
""" Email (Gmail etc)
"""
# to:cc:bcc:subject:body

# Encoding
...
ctLen = len(encrypted_content_bytes)
payload = base64.encode(concat_bytes(struct.pack("<i", ctLen), encrypted_content_bytes, device_id))
""" Messages (Telegram etc)
"""
# to:body

# Decoding
...
ctLen = int.from_bytes(payload[0:4])
cipher_text = payload[ctLen:-64]
device_id = payload[-64:]
""" Text (X; Twitter etc)
"""
# body
```
72 changes: 59 additions & 13 deletions app/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,14 @@ plugins {
id 'org.jetbrains.kotlin.android'
// id 'org.jetbrains.kotlin.jvm' version '1.9.23'
id 'org.jetbrains.kotlin.plugin.serialization' version '1.9.23'
id "com.google.protobuf"
}


def versionsPropertiesFile = rootProject.file("version.properties")
def versionProperties = new Properties()
versionProperties.load(new FileInputStream(versionsPropertiesFile))


android {
compileSdkVersion 34

Expand Down Expand Up @@ -40,6 +41,7 @@ android {

manifestPlaceholders =
['appAuthRedirectScheme': 'com.afkanerd.sw0b']

}

buildTypes {
Expand All @@ -52,9 +54,9 @@ android {
debug {
buildConfigField("Boolean", "IS_RECAPTCHA", "false")
buildConfigField("Boolean", "IS_ONBOARDING", "true")
// buildConfigField "boolean", "IS_RECAPTCHA" false
}
}

compileOptions {
sourceCompatibility JavaVersion.VERSION_1_8
targetCompatibility JavaVersion.VERSION_1_8
Expand Down Expand Up @@ -86,31 +88,63 @@ repositories {
}


dependencies {
import org.apache.tools.ant.taskdefs.condition.Os

// Compatible with macOS on Apple Silicon
def archSuffix = Os.isFamily(Os.FAMILY_MAC) ? ':osx-x86_64' : ''
protobuf {
protoc {
artifact = "com.google.protobuf:protoc:4.27.0"
}

plugins {
grpc {
artifact = 'io.grpc:protoc-gen-grpc-java:1.64.0'
}
}

implementation 'androidx.appcompat:appcompat:1.7.0-alpha03'
implementation 'com.google.android.material:material:1.11.0'
generateProtoTasks {
all().each { task ->
task.plugins {
// javalite {}
grpc { option 'lite' }
}
task.builtins {
java { option 'lite' }
}
}
}
}

configurations {
implementation.exclude module:'protobuf-javalite'
}


dependencies {
implementation 'androidx.appcompat:appcompat:1.7.0'
implementation 'com.google.android.material:material:1.12.0'
implementation 'androidx.constraintlayout:constraintlayout:2.1.4'
implementation 'androidx.annotation:annotation:1.7.1'
implementation 'androidx.annotation:annotation:1.8.0'
implementation group: 'androidx.lifecycle', name: 'lifecycle-extensions', version: '2.2.0'
// implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.4.1.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-viewmodel-ktx:2.8.1'
implementation 'androidx.legacy:legacy-support-v4:1.0.0'
implementation 'androidx.preference:preference:1.2.1'
implementation "androidx.preference:preference-ktx:1.2.1"
implementation 'androidx.databinding:baseLibrary:3.2.0-alpha11'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.7.0'
implementation 'androidx.lifecycle:lifecycle-livedata-ktx:2.8.1'
implementation 'androidx.activity:activity:1.9.0'
implementation 'com.google.android.gms:play-services-auth:21.1.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:18.0.2'
implementation 'com.google.android.gms:play-services-safetynet:18.0.1'
implementation 'com.google.android.gms:play-services-auth:21.2.0'
implementation 'com.google.android.gms:play-services-auth-api-phone:18.1.0'
implementation 'com.google.android.gms:play-services-safetynet:18.1.0'
// implementation 'com.android.support:appcompat-v7:28.0.0'
testImplementation 'junit:junit:4.13.2'
androidTestImplementation 'androidx.test.ext:junit:1.1.5'
androidTestImplementation 'androidx.test.espresso:espresso-core:3.5.1'

// Java language implementation
implementation "androidx.fragment:fragment:1.6.2"
implementation "androidx.fragment:fragment:1.7.1"

// implementation 'com.android.support:design:28.0.0'
implementation 'com.android.volley:volley:1.2.1'
Expand All @@ -122,7 +156,7 @@ dependencies {

// (Java only)
implementation "androidx.work:work-runtime:2.9.0"
implementation 'com.budiyev.android:code-scanner:2.1.0'
// implementation 'com.budiyev.android:code-scanner:2.1.0'

// implementation 'androidx.security:security-crypto:1.1.0-alpha06'

Expand All @@ -146,5 +180,17 @@ dependencies {
implementation 'com.afkanerd.smswithoutborders:libsignal_doubleratchet:0.0.3'
implementation "net.openid:appauth:0.11.1"
implementation 'com.hbb20:ccp:2.7.0'

implementation 'com.squareup.okhttp:okhttp:2.7.5'
implementation 'javax.annotation:javax.annotation-api:1.3.2'
implementation 'io.grpc:grpc-core:1.64.0'
implementation 'io.grpc:grpc-stub:1.64.0'
implementation 'io.grpc:grpc-okhttp:1.64.0'
implementation('io.grpc:grpc-protobuf-lite:1.64.0') {
exclude module: "protobuf-lite"
}

// Add protobuf-java dependency
implementation "com.google.protobuf:protobuf-java:3.25.1"
}

84 changes: 84 additions & 0 deletions app/src/androidTest/java/com/example/sw0b_001/gRPCTest.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.example.sw0b_001

import io.grpc.ManagedChannel
import io.grpc.ManagedChannelBuilder
import io.grpc.StatusRuntimeException
import org.junit.Before
import org.junit.Test
import vault.v1.EntityGrpc
import vault.v1.EntityGrpc.EntityBlockingStub
import vault.v1.EntityGrpc.EntityFutureStub
import vault.v1.EntityGrpc.EntityStub
import vault.v1.Vault1.CreateEntityRequest
import java.net.Inet6Address

class gRPCTest {
lateinit var channel: ManagedChannel
lateinit var entityStub: EntityBlockingStub

val globalPhoneNumber = "+237123456789"

@Before
fun init() {
channel = ManagedChannelBuilder
.forAddress("staging.smswithoutborders.com", 9050)
.useTransportSecurity()
.build()

entityStub = EntityGrpc.newBlockingStub(channel)
}

@Test
fun vaultTestCreateEntity() {
val createEntityRequest1 = CreateEntityRequest.newBuilder().apply {
setPhoneNumber(globalPhoneNumber)
}.build()

try {
val createResponse = entityStub.createEntity(createEntityRequest1)
assert(createResponse.requiresOwnershipProof)
println(createResponse.message)
} catch(e: Exception) {
if(e is StatusRuntimeException) {
when(e.status.code.toString()) {
"INVALID_ARGUMENT" -> {
println(e.message)
throw e
}
"ALREADY_EXISTS" -> {
println(e.message)
}
}
}
}
}

@Test
fun vaultTestCreateEntity2() {
vaultTestCreateEntity()
val createEntityRequest2 = CreateEntityRequest.newBuilder().apply {
setCountryCode("CM")
setPhoneNumber(globalPhoneNumber)
setPassword("dMd2Kmo9")
setClientPublishPubKey("dMd2Kmo9")
setClientDeviceIdPubKey("dMd2Kmo9")
setOwnershipProofResponse("123456")
}.build()

try {
val createResponse = entityStub.createEntity(createEntityRequest2)
assert(createResponse.requiresOwnershipProof)
println(createResponse.message)
} catch(e: Exception) {
if(e is StatusRuntimeException) {
when(e.status.code.toString()) {
"UNAUTHENTICATED" -> {
println(e.message)
}
}
}
throw e
}
}

}
10 changes: 10 additions & 0 deletions app/src/main/java/com/example/sw0b_001/EmailViewActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -69,6 +69,16 @@ class EmailViewActivity : AppCompactActivityCustomized() {
}
return true
}
R.id.compose_view_edit_menu_delete -> {
ThreadExecutorPool.executorService.execute {
Datastore.getDatastore(applicationContext).encryptedContentDAO()
.delete(intent.getLongExtra("message_id", -1))
runOnUiThread {
finish()
}
}
return true
}
}
return false
}
Expand Down
2 changes: 2 additions & 0 deletions app/src/main/java/com/example/sw0b_001/HomepageActivity.kt
Original file line number Diff line number Diff line change
Expand Up @@ -111,12 +111,14 @@ class HomepageActivity : AppCompactActivityCustomized() {
Platforms.TYPE_TEXT -> {
startActivity(Intent(this, TextViewActivity::class.java).apply {
putExtra("platform_id", it.second.id)
putExtra("platform_name", it.second.name)
putExtra("message_id", it.first.id)
})
}
Platforms.TYPE_EMAIL -> {
startActivity(Intent(this, EmailViewActivity::class.java).apply {
putExtra("platform_id", it.second.id)
putExtra("platform_name", it.second.name)
putExtra("message_id", it.first.id)
})
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -19,6 +19,9 @@ public interface EncryptedContentDAO {
@Query("DELETE FROM EncryptedContent")
void deleteAll();

@Query("DELETE FROM EncryptedContent WHERE id = :id")
void delete(long id);

@Query("SELECT * FROM EncryptedContent WHERE id=:encryptedContentId")
EncryptedContent get(long encryptedContentId);

Expand Down
4 changes: 4 additions & 0 deletions app/src/main/java/com/example/sw0b_001/Models/v2/Vault_V2.kt
Original file line number Diff line number Diff line change
Expand Up @@ -272,6 +272,10 @@ class Vault_V2(val uid: String) {
loginViaUID(context, _uid, password)
}

if(networkResponseResults.response.statusCode != 200) {
throw Exception(String(networkResponseResults.response.data))
}

fragment?.let {
it.activity?.runOnUiThread {
Toast.makeText(context, context.getString(R.string.login_successful),
Expand Down
Loading

0 comments on commit 56ee19b

Please sign in to comment.