Skip to content

Commit

Permalink
Added: encryption support
Browse files Browse the repository at this point in the history
  • Loading branch information
capcom6 committed Jan 13, 2024
1 parent c34345b commit 4f18d19
Show file tree
Hide file tree
Showing 7 changed files with 158 additions and 9 deletions.
24 changes: 18 additions & 6 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -26,33 +26,45 @@ Here is a simple example of how to send a message using the client:
require 'vendor/autoload.php';

use AndroidSmsGateway\Client;
use AndroidSmsGateway\EncryptedClient;
use AndroidSmsGateway\Domain\Message;

$login = 'your_login';
$password = 'your_password';

$client = new Client($login, $password);
// or
// $client = new EncryptedClient('your_passphrase', $login, $password);

$message = new Message('Your message text here.', ['+1234567890']);

try {
$messageState = $client->Send($message);
echo "Message sent with ID: " . $messageState->ID();
echo "Message sent with ID: " . $messageState->ID() . PHP_EOL;
} catch (Exception $e) {
echo "Error sending message: " . $e->getMessage();
echo "Error sending message: " . $e->getMessage() . PHP_EOL;
die(1);
}

try {
$messageState = $client->GetState($messageState->ID());
echo "Message state: " . $messageState->State();
echo "Message state: " . $messageState->State() . PHP_EOL;
} catch (Exception $e) {
echo "Error getting message state: " . $e->getMessage();
echo "Error getting message state: " . $e->getMessage() . PHP_EOL;
die(1);
}
```

## Methods
## Clients

The `Client` class provides the following methods:
There are two clients available:

- `Client` is used for sending SMS messages in plain text.
- `EncryptedClient` is used for sending AES encrypted SMS messages. You need to provide the same passphrase in Android app.

### Methods

Each client has the following methods:

* `Send(Message $message)`: Send a new SMS message.
* `GetState(string $id)`: Retrieve the state of a previously sent message by its ID.
Expand Down
30 changes: 29 additions & 1 deletion src/Domain/Message.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AndroidSmsGateway\Domain;

use AndroidSmsGateway\Encryptor;
use AndroidSmsGateway\Interfaces\SerializableInterface;

/**
Expand Down Expand Up @@ -30,6 +31,10 @@ class Message implements SerializableInterface {
* Request delivery report, `true` by default
*/
private bool $withDeliveryReport;
/**
* Is message and phones encrypted, `false` by default
*/
private bool $isEncrypted = false;
/**
* Phone numbers in E164 format
* @var array<string>
Expand All @@ -39,13 +44,35 @@ class Message implements SerializableInterface {
/**
* @param array<string> $phoneNumbers
*/
public function __construct(string $message, array $phoneNumbers, ?string $id = null, ?int $ttl = null, ?int $simNumber = null, bool $withDeliveryReport = true) {
public function __construct(
string $message,
array $phoneNumbers,
?string $id = null,
?int $ttl = null,
?int $simNumber = null,
bool $withDeliveryReport = true
) {
$this->id = $id;
$this->message = $message;
$this->ttl = $ttl;
$this->simNumber = $simNumber;
$this->withDeliveryReport = $withDeliveryReport;
$this->phoneNumbers = $phoneNumbers;
$this->isEncrypted = false;
}

public function Encrypt(Encryptor $encryptor): self {
if ($this->isEncrypted) {
return $this;
}

$this->isEncrypted = true;
$this->message = $encryptor->Encrypt($this->message);
$this->phoneNumbers = array_map(
fn(string $phoneNumber) => $encryptor->Encrypt($phoneNumber),
$this->phoneNumbers
);
return $this;
}

public function ToObject(): object {
Expand All @@ -55,6 +82,7 @@ public function ToObject(): object {
'ttl' => $this->ttl,
'simNumber' => $this->simNumber,
'withDeliveryReport' => $this->withDeliveryReport,
'isEncrypted' => $this->isEncrypted,
'phoneNumbers' => $this->phoneNumbers
];
}
Expand Down
31 changes: 29 additions & 2 deletions src/Domain/MessageState.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AndroidSmsGateway\Domain;

use AndroidSmsGateway\Encryptor;
use AndroidSmsGateway\Enums\ProcessState;

/**
Expand All @@ -24,13 +25,25 @@ class MessageState {
*/
private array $recipients;

/**
* Is message and phones encrypted
* @var bool
*/
private bool $isEncrypted;

/**
* @param array<RecipientState> $recipients
*/
public function __construct(string $id, ProcessState $state, array $recipients) {
public function __construct(
string $id,
ProcessState $state,
array $recipients,
bool $isEncrypted = false
) {
$this->id = $id;
$this->state = $state;
$this->recipients = $recipients;
$this->isEncrypted = $isEncrypted;
}

public function ID(): string {
Expand All @@ -48,14 +61,28 @@ public function Recipients(): array {
return $this->recipients;
}

public function Decrypt(Encryptor $encryptor): self {
if (!$this->isEncrypted) {
return $this;
}

$this->recipients = array_map(
static fn(RecipientState $recipient) => $recipient->Decrypt($encryptor),
$this->recipients
);

return $this;
}

public static function FromObject(object $obj): self {
return new self(
$obj->id,
ProcessState::FromValue($obj->state),
array_map(
static fn($obj) => RecipientState::FromObject($obj),
$obj->recipients
)
),
$obj->isEncrypted ?? false
);
}
}
7 changes: 7 additions & 0 deletions src/Domain/RecipientState.php
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

namespace AndroidSmsGateway\Domain;

use AndroidSmsGateway\Encryptor;
use AndroidSmsGateway\Enums\ProcessState;

/**
Expand Down Expand Up @@ -39,6 +40,12 @@ public function Error(): ?string {
return $this->error;
}

public function Decrypt(Encryptor $encryptor): self {
$this->phoneNumber = $encryptor->Decrypt($this->phoneNumber);

return $this;
}

public static function FromObject(object $obj): self {
return new self(
$obj->phoneNumber,
Expand Down
32 changes: 32 additions & 0 deletions src/EncryptedClient.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,32 @@
<?php

namespace AndroidSmsGateway;

use AndroidSmsGateway\Domain\Message;
use AndroidSmsGateway\Domain\MessageState;
use Http\Client\HttpClient;

class EncryptedClient extends Client {
protected Encryptor $encryptor;

public function __construct(
string $passphrase,
string $login,
string $password,
string $serverUrl = self::DEFAULT_URL,
?HttpClient $client = null
) {
parent::__construct($login, $password, $serverUrl, $client);

$this->encryptor = new Encryptor($passphrase);
}

public function Send(Message $message): MessageState {
$message = $message->Encrypt($this->encryptor);
return parent::Send($message)->Decrypt($this->encryptor);
}

public function GetState(string $id): MessageState {
return parent::GetState($id)->Decrypt($this->encryptor);
}
}
42 changes: 42 additions & 0 deletions src/Encryptor.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
<?php

namespace AndroidSmsGateway;

class Encryptor {
protected string $passphrase;

public function __construct(
string $passphrase
) {
$this->passphrase = $passphrase;
}

public function Encrypt(string $data): string {
$salt = $this->generateSalt();
$secretKey = $this->generateSecretKeyFromPassphrase($this->passphrase, $salt);

return base64_encode($salt) . '.' . openssl_encrypt($data, 'aes-256-cbc', $secretKey, 0, $salt);
}

public function Decrypt(string $data): string {
list($saltBase64, $encryptedBase64) = explode('.', $data, 2);

$salt = base64_decode($saltBase64);
$secretKey = $this->generateSecretKeyFromPassphrase($this->passphrase, $salt);

return openssl_decrypt($encryptedBase64, 'aes-256-cbc', $secretKey, 0, $salt);
}

protected function generateSalt(int $size = 16): string {
return random_bytes($size);
}

protected function generateSecretKeyFromPassphrase(
string $passphrase,
string $salt,
int $keyLength = 32,
int $iterationCount = 300000
): string {
return hash_pbkdf2('sha1', $passphrase, $salt, $iterationCount, $keyLength, true);
}
}
1 change: 1 addition & 0 deletions tests/Domain/MessageTest.php
Original file line number Diff line number Diff line change
Expand Up @@ -34,6 +34,7 @@ public function testCanSerializeToObject(): void {
'ttl' => $ttl,
'simNumber' => $simNumber,
'withDeliveryReport' => $withDeliveryReport,
'isEncrypted' => false,
'phoneNumbers' => $phoneNumbers
];

Expand Down

0 comments on commit 4f18d19

Please sign in to comment.