Пример #1
1
 public function test_basicEncryptDecrypt()
 {
     $aliceStore = new InMemorySenderKeyStore();
     $bobStore = new InMemorySenderKeyStore();
     $charlieStore = new InMemorySenderKeyStore();
     $aliceSessionBuilder = new GroupSessionBuilder($aliceStore);
     $bobSessionBuilder = new GroupSessionBuilder($bobStore);
     $charlieSessionBuilder = new GroupSessionBuilder($charlieStore);
     $aliceGroupCipher = new GroupCipher($aliceStore, "groupWithBobInIt");
     $bobGroupCipher = new GroupCipher($bobStore, "groupWithBobInIt::aliceUserName");
     $charlieGroupCipher = new GroupCipher($charlieStore, "groupWithBobInIt::aliceUserName");
     $aliceSenderKey = KeyHelper::generateSenderKey();
     $aliceSenderSigningKey = KeyHelper::generateSenderSigningKey();
     $aliceSenderKeyId = KeyHelper::generateSenderKeyId();
     $aliceDistributionMessage = $aliceSessionBuilder->process("groupWithBobInIt", $aliceSenderKeyId, 0, $aliceSenderKey, $aliceSenderSigningKey);
     echo niceVarDump($aliceDistributionMessage);
     echo niceVarDump($aliceDistributionMessage->serialize());
     echo $aliceDistributionMessage->serialize();
     $bobSessionBuilder->processSender("groupWithBobInIt::aliceUserName", $aliceDistributionMessage);
     $ciphertextFromAlice = $aliceGroupCipher->encrypt("smert ze smert");
     $plaintextFromAlice_Bob = $bobGroupCipher->decrypt($ciphertextFromAlice);
     $ciphertextFromAlice_2 = $aliceGroupCipher->encrypt("smert ze smert");
     echo niceVarDump($aliceDistributionMessage);
     $charlieSessionBuilder->processSender("groupWithBobInIt::aliceUserName", $aliceDistributionMessage);
     $plaintextFromAlice_Charlie = $charlieGroupCipher->decrypt($ciphertextFromAlice_2);
     $this->assertEquals($plaintextFromAlice_Bob, "smert ze smert");
     $this->assertEquals($plaintextFromAlice_Charlie, "smert ze smert");
 }
Пример #2
0
 protected function processEncryptedNode(ProtocolNode $node)
 {
     if ($this->parent->getAxolotlStore() == null) {
         return;
     }
     //is a chat encrypted message
     $from = $node->getAttribute('from');
     if (strpos($from, Constants::WHATSAPP_SERVER) !== false) {
         $author = ExtractNumber($node->getAttribute('from'));
         $version = $node->getChild(0)->getAttribute('v');
         $encType = $node->getChild(0)->getAttribute('type');
         $encMsg = $node->getChild('enc')->getData();
         if (!$this->parent->getAxolotlStore()->containsSession($author, 1)) {
             //we don't have the session to decrypt, save it in pending and process it later
             $this->parent->addPendingNode($node);
             $this->parent->logFile('info', 'Requesting cipher keys from {from}', ['from' => $author]);
             $this->parent->sendGetCipherKeysFromUser($author);
         } else {
             //decrypt the message with the session
             if ($node->getChild('enc')->getAttribute('count') == '') {
                 $this->parent->setRetryCounter($node->getAttribute('id'), 1);
             }
             if ($version == '2') {
                 if (!in_array($author, $this->parent->getv2Jids())) {
                     $this->parent->setv2Jids($author);
                 }
             }
             $plaintext = $this->decryptMessage($from, $encMsg, $encType, $node->getAttribute('id'), $node->getAttribute('t'));
             //$plaintext ="A";
             if ($plaintext === false) {
                 $this->parent->sendRetry($this->node, $from, $node->getAttribute('id'), $node->getAttribute('t'));
                 $this->parent->logFile('info', 'Couldn\'t decrypt message with {id} id from {from}. Retrying...', ['id' => $node->getAttribute('id'), 'from' => ExtractNumber($from)]);
                 return $node;
                 // could not decrypt
             }
             if (isset($this->parent->retryNodes[$node->getAttribute('id')])) {
                 unset($this->parent->retryNodes[$node->getAttribute('id')]);
             }
             if (isset($this->parent->retryCounters[$node->getAttribute('id')])) {
                 unset($this->parent->retryCounters[$node->getAttribute('id')]);
             }
             switch ($node->getAttribute('type')) {
                 case 'text':
                     $node->addChild(new ProtocolNode('body', null, null, $plaintext));
                     break;
                 case 'media':
                     switch ($node->getChild('enc')->getAttribute('mediatype')) {
                         case 'image':
                             $image = new ImageMessage();
                             $image->parseFromString($plaintext);
                             $keys = (new HKDFv3())->deriveSecrets($image->getRefKey(), hex2bin('576861747341707020496d616765204b657973'), 112);
                             $iv = substr($keys, 0, 16);
                             $keys = substr($keys, 16);
                             $parts = str_split($keys, 32);
                             $key = $parts[0];
                             $macKey = $parts[1];
                             $refKey = $parts[2];
                             //should be changed to nice curl, no extra headers :D
                             $file_enc = file_get_contents($image->getUrl());
                             //requires mac check , last 10 chars
                             $mac = substr($file_enc, -10);
                             $cipherImage = substr($file_enc, 0, strlen($file_enc) - 10);
                             $decrypted_image = pkcs5_unpad(mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $key, $cipherImage, MCRYPT_MODE_CBC, $iv));
                             //$save_file = tempnam(sys_get_temp_dir(),"WAIMG_");
                             //file_put_contents($save_file,$decrypted_image);
                             $child = new ProtocolNode('media', ['size' => $image->getLength(), 'caption' => $image->getCaption(), 'url' => $image->getUrl(), 'mimetype' => $image->getMimeType(), 'filehash' => bin2hex($image->getSha256()), 'width' => 0, 'height' => 0, 'file' => $decrypted_image, 'type' => 'image'], null, $image->getThumbnail());
                             $node->addChild($child);
                             break;
                     }
                     break;
             }
             $this->parent->logFile('info', 'Decrypted message with {id} from {from}', ['id' => $node->getAttribute('id'), 'from' => ExtractNumber($from)]);
             return $node;
         }
     } else {
         $author = ExtractNumber($node->getAttribute('participant'));
         $group_number = ExtractNumber($node->getAttribute('from'));
         $childs = $node->getChildren();
         foreach ($childs as $child) {
             if ($child->getAttribute('type') == 'pkmsg' || $child->getAttribute('type') == 'msg') {
                 if (!$this->parent->getAxolotlStore()->containsSession($author, 1)) {
                     $this->parent->addPendingNode($node);
                     $this->parent->sendGetCipherKeysFromUser($author);
                     break;
                 } else {
                     //decrypt senderKey and save it
                     $encType = $child->getAttribute('type');
                     $encMsg = $child->getData();
                     $from = $node->getAttribute('participant');
                     $version = $child->getAttribute('v');
                     if ($node->getChild('enc')->getAttribute('count') == '') {
                         $this->parent->setRetryCounter($node->getAttribute('id'), 1);
                     }
                     if ($version == '2') {
                         if (!in_array($author, $this->parent->getv2Jids())) {
                             $this->parent->setv2Jids($author);
                         }
                     }
                     $skip_unpad = $node->getChild('enc', ['type' => 'skmsg']) == null;
                     $senderKeyBytes = $this->decryptMessage($from, $encMsg, $encType, $node->getAttribute('id'), $node->getAttribute('t'), $node->getAttribute('from'), $skip_unpad);
                     if ($senderKeyBytes) {
                         if (!$skip_unpad) {
                             $senderKeyGroupMessage = new SenderKeyGroupMessage();
                             $senderKeyGroupMessage->parseFromString($senderKeyBytes);
                         } else {
                             $senderKeyGroupMessage = new SenderKeyGroupData();
                             try {
                                 $senderKeyGroupMessage->parseFromString($senderKeyBytes);
                             } catch (Exception $ex) {
                                 try {
                                     $senderKeyGroupMessage->parseFromString(substr($senderKeyBytes, 0, -1));
                                 } catch (Exception $ex) {
                                     return $node;
                                 }
                             }
                             $message = $senderKeyGroupMessage->getMessage();
                             $senderKeyGroupMessage = $senderKeyGroupMessage->getSenderKey();
                         }
                         $senderKey = new SenderKeyDistributionMessage(null, null, null, null, $senderKeyGroupMessage->getSenderKey());
                         $groupSessionBuilder = new GroupSessionBuilder($this->parent->axolotlStore);
                         $groupSessionBuilder->processSender($group_number . ':' . $author, $senderKey);
                         if (isset($message)) {
                             $this->parent->sendReceipt($node, 'receipt', $this->parent->getJID($this->phoneNumber));
                             $node->addChild(new ProtocolNode('body', null, null, $message));
                         }
                     }
                 }
             } elseif ($child->getAttribute('type') == 'skmsg') {
                 $version = $child->getAttribute('v');
                 if ($version == '2') {
                     if (!in_array($author, $this->parent->v2Jids)) {
                         $this->parent->setv2Jids($author);
                     }
                 }
                 $plaintext = $this->decryptMessage([$group_number, $author], $child->getData(), $child->getAttribute('type'), $node->getAttribute('id'), $node->getAttribute('t'));
                 if (!$plaintext) {
                     $this->parent->sendRetry($this->node, $from, $node->getAttribute('id'), $node->getAttribute('t'), $node->getAttribute('participant'));
                     $this->parent->logFile('info', 'Couldn\'t decrypt group message with {id} id from {from}. Retrying...', ['id' => $node->getAttribute('id'), 'from' => $from]);
                     return $node;
                     // could not decrypt
                 } else {
                     if (isset($this->parent->retryNodes[$node->getAttribute('id')])) {
                         unset($this->parent->retryNodes[$node->getAttribute('id')]);
                     }
                     if (isset($this->parent->retryCounters[$node->getAttribute('id')])) {
                         unset($this->parent->retryCounters[$node->getAttribute('id')]);
                     }
                     $this->parent->logFile('info', 'Decrypted group message with {id} from {from}', ['id' => $node->getAttribute('id'), 'from' => $from]);
                     $this->parent->sendReceipt($node, 'receipt', $this->parent->getJID($this->phoneNumber));
                     $node->addChild(new ProtocolNode('body', null, null, $plaintext));
                 }
             }
         }
     }
 }
Пример #3
0
 protected function processEncryptedNode(ProtocolNode $node)
 {
     if ($this->parent->getAxolotlStore() == null) {
         return null;
     }
     //is a chat encrypted message
     $from = $node->getAttribute('from');
     if (strpos($from, Constants::WHATSAPP_SERVER) !== false) {
         $author = ExtractNumber($node->getAttribute("from"));
         $version = $node->getChild(0)->getAttribute("v");
         $encType = $node->getChild(0)->getAttribute("type");
         $encMsg = $node->getChild("enc")->getData();
         if (!$this->parent->getAxolotlStore()->containsSession($author, 1)) {
             //we don't have the session to decrypt, save it in pending and process it later
             $this->parent->addPendingNode($node);
             $this->parent->logFile('info', 'Requesting cipher keys from {from}', array('from' => $author));
             $this->parent->sendGetCipherKeysFromUser($author);
         } else {
             //decrypt the message with the session
             if ($node->getChild("enc")->getAttribute('count') == "") {
                 $this->parent->setRetryCounter = 1;
             }
             if ($version == "2") {
                 if (!in_array($author, $this->parent->getv2Jids())) {
                     $this->parent->setv2Jids($author);
                 }
             }
             $plaintext = $this->decryptMessage($from, $encMsg, $encType, $node->getAttribute('id'), $node->getAttribute('t'));
             if (!$plaintext) {
                 $this->parent->sendRetry($from, $node->getAttribute('id'), $node->getAttribute('t'));
                 $this->parent->logFile('info', 'Couldn\'t decrypt message with {id} id from {from}. Retrying...', array('id' => $node->getAttribute('id'), 'from' => ExtractNumber($from)));
                 return $node;
                 // could not decrypt
             }
             switch ($node->getAttribute("type")) {
                 case "text":
                     $node->addChild(new ProtocolNode("body", null, null, $plaintext));
                     break;
                 case "media":
                     switch ($node->getChild("enc")->getAttribute("mediatype")) {
                         case "image":
                             $image = new ImageMessage();
                             $image->parseFromString($plaintext);
                             break;
                     }
                     break;
             }
             $this->parent->logFile('info', 'Decrypted message with {id} from {from}', array('id' => $node->getAttribute('id'), 'from' => ExtractNumber($from)));
             return $node;
         }
     } else {
         $author = ExtractNumber($node->getAttribute("participant"));
         $group_number = ExtractNumber($node->getAttribute("from"));
         $childs = $node->getChildren();
         foreach ($childs as $child) {
             if ($child->getAttribute("type") == "pkmsg" || $child->getAttribute("type") == "msg") {
                 if (!$this->parent->getAxolotlStore()->containsSession($author, 1)) {
                     $this->parent->addPendingNode($node);
                     $this->parent->sendGetCipherKeysFromUser($author);
                     break;
                 } else {
                     //decrypt senderKey and save it
                     $encType = $child->getAttribute("type");
                     $encMsg = $child->getData();
                     $from = $node->getAttribute("participant");
                     $version = $child->getAttribute("v");
                     if ($node->getChild("enc")->getAttribute('count') == "") {
                         $this->parent->retryCounter = 1;
                     }
                     if ($version == "2") {
                         if (!in_array($author, $this->parent->getv2Jids())) {
                             $this->parent->setv2Jids($author);
                         }
                     }
                     $skip_unpad = $node->getChild("enc", ["type" => "skmsg"]) == null;
                     $senderKeyBytes = $this->decryptMessage($from, $encMsg, $encType, $node->getAttribute('id'), $node->getAttribute('t'), $node->getAttribute("from"), $skip_unpad);
                     if ($senderKeyBytes) {
                         if (!$skip_unpad) {
                             $senderKeyGroupMessage = new SenderKeyGroupMessage();
                             $senderKeyGroupMessage->parseFromString($senderKeyBytes);
                         } else {
                             $senderKeyGroupMessage = new SenderKeyGroupData();
                             try {
                                 $senderKeyGroupMessage->parseFromString($senderKeyBytes);
                             } catch (Exception $ex) {
                                 try {
                                     $senderKeyGroupMessage->parseFromString(substr($senderKeyBytes, 0, -1));
                                 } catch (Exception $ex) {
                                     return $node;
                                 }
                             }
                             $message = $senderKeyGroupMessage->getMessage();
                             $senderKeyGroupMessage = $senderKeyGroupMessage->getSenderKey();
                         }
                         $senderKey = new SenderKeyDistributionMessage(null, null, null, null, $senderKeyGroupMessage->getSenderKey());
                         $groupSessionBuilder = new GroupSessionBuilder($this->axolotlStore);
                         $groupSessionBuilder->processSender($group_number . ":" . $author, $senderKey);
                         if (isset($message)) {
                             $this->parent->sendReceipt($node, 'receipt', $this->parent->getJID($this->phoneNumber));
                             $node->addChild(new ProtocolNode("body", null, null, $message));
                         }
                     }
                 }
             } else {
                 if ($child->getAttribute("type") == "skmsg") {
                     $version = $child->getAttribute("v");
                     if ($version == "2") {
                         if (!in_array($author, $this->v2Jids)) {
                             $this->v2Jids[] = $author;
                         }
                     }
                     $plaintext = $this->decryptMessage([$group_number, $author], $child->getData(), $child->getAttribute("type"), $node->getAttribute('id'), $node->getAttribute('t'));
                     if (!$plaintext) {
                         $this->parent->sendRetry($from, $node->getAttribute('id'), $node->getAttribute('t'));
                         $this->parent->logFile('info', 'Couldn\'t decrypt group message with {id} id from {from}. Retrying...', array('id' => $node->getAttribute('id'), 'from' => $from));
                         return $node;
                         // could not decrypt
                     } else {
                         $this->parent->logFile('info', 'Decrypted group message with {id} from {from}', array('id' => $node->getAttribute('id'), 'from' => $from));
                         $this->parent->sendReceipt($node, 'receipt', $this->parent->getJID($this->phoneNumber));
                         $node->addChild(new ProtocolNode("body", null, null, $plaintext));
                     }
                 }
             }
         }
     }
 }