public function KeyExchangeMessage($messageVersion = null, $sequence = null, $flags = null, $baseKey = null, $baseKeySignature = null, $ratchetKey = null, $identityKey = null, $serialized = null) { /* :type messageVersion: int :type sequence: int :type flags:int :type baseKey: ECPublicKey :type baseKeySignature: bytearray :type ratchetKey: ECPublicKey :type identityKey: IdentityKey :type serialized: bytearray */ if ($serialized == null) { $this->supportedVersion = CiphertextMessage::CURRENT_VERSION; $this->version = $messageVersion; $this->sequence = $sequence; $this->flags = $flags; $this->baseKey = $baseKey; $this->baseKeySignature = $baseKeySignature; $this->ratchetKey = $ratchetKey; $this->identityKey = $identityKey; $version = ByteUtil::intsToByteHighAndLow($this->version, $this->supportedVersion); $keyExchangeMessage = new Textsecure_KeyExchangeMessage(); $keyExchangeMessage->setId($this->sequence << 5 | $this->flags); $keyExchangeMessage->setBaseKey($baseKey->serialize()); $keyExchangeMessage->setRatchetKey($ratchetKey->serialize()); $keyExchangeMessage->setIdentityKey($identityKey->serialize()); if ($messageVersion >= 3) { $keyExchangeMessage->setBaseKeySignature($baseKeySignature); } $this->serialized = ByteUtil::combine([chr((int) $version), $keyExchangeMessage->serializeToString()]); } else { try { $parts = ByteUtil::split($serialized, 1, strlen($serialized) - 1); $this->version = ByteUtil::highBitsToInt(ord($parts[0][0])); $this->supportedVersion = ByteUtil::lowBitsToInt(ord($parts[0][0])); if ($this->version <= CiphertextMessage::UNSUPPORTED_VERSION) { throw new LegacyMessageException('Unsupported legacy version: ' . $this->version); } if ($this->version > CiphertextMessage::CURRENT_VERSION) { throw new InvalidVersionException('Unkown version: ' . $this->version); } $message = new Textsecure_KeyExchangeMessage(); $message->parseFromString($parts[1]); if ($message->getId() == null || $message->getBaseKey() == null || $message->getRatchetKey() == null || $message->getIdentityKey() == null || $this->version >= 3 && $message->getBaseKeySignature() == null) { throw new InvalidMessageException('Some required fields are missing!'); } $this->sequence = $message->getId() >> 5; $this->flags = $message->getId() & 0x1f; $this->serialized = $serialized; $this->baseKey = Curve::decodePoint($message->getBaseKey(), 0); $this->baseKeySignature = $message->getBaseKeySignature(); $this->ratchetKey = Curve::decodePoint($message->getRatchetKey(), 0); $this->identityKey = new IdentityKey($message->getIdentityKey(), 0); } catch (InvalidKeyException $ex) { throw new InvalidMessageException($ex->getMessage()); } } }
public function DerivedMessageSecrets($okm) { $keys = ByteUtil::split($okm, self::CIPHER_KEY_LENGTH, self::MAC_KEY_LENGTH, self::IV_LENGTH); $this->cipherKey = $keys[0]; //AES $this->macKey = $keys[1]; //sha256 $this->iv = $keys[2]; }
public function SenderMessageKey($iteration, $seed) { $hkdf = new HKDFv3(); $derivative = $hkdf->deriveSecrets($seed, "WhisperGroup", 48); /* match: 21c8b6ca */ $parts = ByteUtil::split($derivative, 16, 32); $this->iteration = $iteration; $this->seed = $seed; $this->iv = $parts[0]; $this->cipherKey = $parts[1]; }
public function verifySignature($signatureKey) { try { $parts = ByteUtil::split($this->serialized, strlen($this->serialized) - self::SIGNATURE_LENGTH, self::SIGNATURE_LENGTH); if (!Curve::verifySignature($signatureKey, $parts[0], $parts[1])) { throw new InvalidMessageException("Invalid signature!"); } } catch (InvalidKeyException $ex) { throw new InvalidMessageException($ex->getMessage()); } }
public function PreKeyWhisperMessage($messageVersion = null, $registrationId = null, $preKeyId = null, $signedPreKeyId = null, $ecPublicBaseKey = null, $identityKey = null, $whisperMessage = null, $serialized = null) { if ($serialized != null) { $this->version = ByteUtil::highBitsToInt($serialized[0]); if ($this->version > self::CURRENT_VERSION) { throw new InvalidVersionException("Unknown version " . $this->version); } $preKeyWhisperMessage = new Textsecure_PreKeyWhisperMessage(); $preKeyWhisperMessage->parseFromString(substr($serialized, 1)); if ($this->version == 2 && $preKeyWhisperMessage->getPreKeyId() == null || $this->version == 3 && $preKeyWhisperMessage->getSignedPreKeyId() == null || $preKeyWhisperMessage->getBaseKey() == null || $preKeyWhisperMessage->getIdentityKey() == null || $preKeyWhisperMessage->getMessage() == null) { throw new InvalidMessageException("Incomplete message"); } $this->serialized = $serialized; $this->registrationId = $preKeyWhisperMessage->getRegistrationId(); $this->preKeyId = $preKeyWhisperMessage->getPreKeyId(); $this->signedPreKeyId = $preKeyWhisperMessage->getSignedPreKeyId(); $this->baseKey = Curve::decodePoint((string) $preKeyWhisperMessage->getBaseKey(), 0); $this->identityKey = new IdentityKey(Curve::decodePoint((string) $preKeyWhisperMessage->getIdentityKey(), 0)); $this->message = new WhisperMessage(null, null, null, null, null, null, null, null, $preKeyWhisperMessage->getMessage()); } else { try { $this->version = $messageVersion; $this->registrationId = $registrationId; $this->preKeyId = $preKeyId; $this->signedPreKeyId = $signedPreKeyId; $this->baseKey = $ecPublicBaseKey; $this->identityKey = $identityKey; $this->message = $whisperMessage; $builder = new Textsecure_PreKeyWhisperMessage(); $builder->setSignedPreKeyId($this->signedPreKeyId); $builder->setBaseKey($this->baseKey->serialize()); $builder->setIdentityKey($this->identityKey->serialize()); $builder->setMessage($whisperMessage->serialize()); $builder->setRegistrationId($this->registrationId); if ($preKeyId != null) { $builder->setPreKeyId($preKeyId); } $versionBytes = ByteUtil::intsToByteHighAndLow($this->version, self::CURRENT_VERSION); $messageBytes = $builder->serializeToString(); $this->serialized = ByteUtil::combine([chr($versionBytes), $messageBytes]); } catch (Exception $ex) { throw new InvalidMessageException($ex->getMessage() . " - " . $ex->getLine() . " - " . $ex->getFile()); } } }
public function SenderKeyDistributionMessage($id = null, $iteration = null, $chainKey = null, $signatureKey = null, $serialized = null) { if ($serialized == null) { $version = ByteUtil::intsToByteHighAndLow(self::CURRENT_VERSION, self::CURRENT_VERSION); $this->id = $id; $this->iteration = $iteration; $this->chainKey = $chainKey; $this->signatureKey = $signatureKey; $proto_skdm = new Textsecure_SenderKeyDistributionMessage(); $proto_skdm->setId($id); $proto_skdm->setIteration($iteration); $proto_skdm->setChainKey((string) $chainKey); $proto_skdm->setSigningKey((string) $signatureKey->serialize()); $this->serialized = chr($version) . $proto_skdm->serializeToString(); } else { $parts = ByteUtil::split($serialized, 1, strlen($serialized) - 1); $version = ord($parts[0][0]); $message = $parts[1]; if (ByteUtil::highBitsToInt($version) < self::CURRENT_VERSION) { throw new LegacyMessageException("Legacy message: " + ByteUtil::highBitsToInt($version)); } if (ByteUtil::highBitsToInt($version) > self::CURRENT_VERSION) { throw new InvalidMessageException("Unknown version: " + ByteUtil::highBitsToInt($version)); } $proto_skdm = new Textsecure_SenderKeyDistributionMessage(); try { $proto_skdm->parseFromString($message); } catch (Exception $ex) { throw new InvalidMessageException("Incomplete message."); } if ($proto_skdm->getId() === null || $proto_skdm->getIteration() === null || $proto_skdm->getChainKey() === null || $proto_skdm->getSigningKey() === null) { throw new InvalidMessageException("Incomplete message."); } $this->serialized = $serialized; $this->id = $proto_skdm->getId(); $this->iteration = $proto_skdm->getIteration(); $this->chainKey = $proto_skdm->getChainKey(); $this->signatureKey = Curve::decodePoint($proto_skdm->getSigningKey(), 0); } }
public function testSplit() { $okm = pack('H*', '02a9aa6c7dbd64f9d3aa92f92a277bf54609dadf0b00828acfc61e3c724b84a7bfbe5efb603030526742e3ee89c7024e884e' . '440f1ff376bb2317b2d64deb7c8322f4c5015d9d895849411ba1d793a827'); $data = ''; for ($i = 0; $i < 80; $i++) { $data .= chr($i); } $a_data = ''; for ($i = 0; $i < 32; $i++) { $a_data .= chr($i); } $b_data = ''; for ($i = 32; $i < 64; $i++) { $b_data .= chr($i); } $c_data = ''; for ($i = 64; $i < 80; $i++) { $c_data .= chr($i); } $result = ByteUtil::split($data, 32, 32, 16); $this->assertEquals($result[0], $a_data); $this->assertEquals($result[1], $b_data); $this->assertEquals($result[2], $c_data); }
public function DerivedRootSecrets($okm) { $keys = ByteUtil::split($okm, 32, 32); $this->rootKey = $keys[0]; $this->chainKey = $keys[1]; }
private function getMac($messageVersion, $senderIdentityKey, $receiverIdentityKey, $macKey, $serialized) { $mac = hash_init("sha256", HASH_HMAC, $macKey); if ($messageVersion >= 3) { hash_update($mac, $senderIdentityKey->getPublicKey()->serialize()); hash_update($mac, $receiverIdentityKey->getPublicKey()->serialize()); } hash_update($mac, $serialized); $result = hash_final($mac, true); return ByteUtil::trim($result, self::MAC_LENGTH); }