/**
  * @param AppStoreInterface $appStore
  * @param string $blob
  * @return array
  *   Decoded data.
  */
 public static function decode($appStore, $blob)
 {
     $parts = explode(Constants::PROTOCOL_DELIM, $blob, 5);
     if (count($parts) != 5) {
         throw new InvalidMessageException('Invalid message: insufficient parameters');
     }
     list($wireProt, $wireAppId, $rsaCiphertextB64, $signature, $body) = $parts;
     if ($wireProt !== self::NAME) {
         throw new InvalidMessageException('Invalid message: wrong protocol name');
     }
     $appPrivKey = $appStore->getPrivateKey($wireAppId);
     if (!$appPrivKey) {
         throw new InvalidMessageException('Received message intended for unknown app.');
     }
     $rsaCiphertext = base64_decode($rsaCiphertextB64);
     if (strlen($rsaCiphertext) !== Constants::RSA_MSG_BYTES) {
         throw new InvalidMessageException("RSA ciphertext has incorrect length");
     }
     $secret = UserError::adapt('Civi\\Cxn\\Rpc\\Exception\\InvalidMessageException', function () use($rsaCiphertext, $appPrivKey) {
         return RegistrationMessage::getRsa($appPrivKey, 'private')->decrypt($rsaCiphertext);
     });
     if (empty($secret)) {
         throw new InvalidMessageException("Invalid message: decryption produced empty secret");
     }
     $plaintext = AesHelper::authenticateThenDecrypt($secret, $body, $signature);
     return json_decode($plaintext, TRUE);
 }
Exemple #2
0
 /**
  * @param CxnStoreInterface $cxnStore
  *   A repository that contains shared secrets.
  * @param string $message
  *   Ciphertext.
  * @return static
  * @throws InvalidMessageException
  */
 public static function decode($cxnStore, $message)
 {
     list($parsedProt, $parsedCxnId, $parsedHmac, $parsedBody) = explode(Constants::PROTOCOL_DELIM, $message, 4);
     if ($parsedProt != self::NAME) {
         throw new InvalidMessageException('Incorrect coding. Expected: ' . self::NAME);
     }
     $cxn = $cxnStore->getByCxnId($parsedCxnId);
     if (empty($cxn)) {
         throw new InvalidMessageException('Unknown connection ID');
     }
     $jsonPlaintext = AesHelper::authenticateThenDecrypt($cxn['secret'], $parsedBody, $parsedHmac);
     return new StdMessage($parsedCxnId, $cxn['secret'], json_decode($jsonPlaintext, TRUE));
 }
Exemple #3
0
 /**
  * Encrypt $plaintext with $secret, then date and sign the message.
  *
  * @param string $secret
  * @param string $plaintext
  * @return array
  *   Array(string $body, string $signature).
  *   Note that $body begins with an unencrypted envelope (ttl, iv).
  * @throws InvalidMessageException
  */
 public static function encryptThenSign($secret, $plaintext)
 {
     $iv = crypt_random_string(Constants::AES_BYTES);
     $keys = AesHelper::deriveAesKeys($secret);
     $cipher = new \Crypt_AES(CRYPT_AES_MODE_CBC);
     $cipher->setKeyLength(Constants::AES_BYTES);
     $cipher->setKey($keys['enc']);
     $cipher->setIV($iv);
     // JSON string; this will be signed but not encrypted
     $jsonEnvelope = json_encode(array('ttl' => Time::getTime() + Constants::REQUEST_TTL, 'iv' => BinHex::bin2hex($iv)));
     // JSON string; this will be signed and encrypted
     $jsonEncrypted = $cipher->encrypt($plaintext);
     $body = $jsonEnvelope . Constants::PROTOCOL_DELIM . $jsonEncrypted;
     $signature = hash_hmac('sha256', $body, $keys['auth']);
     return array($body, $signature);
 }
 /**
  * @param array $appMeta
  * @return array
  *   Array($cxnId, $isOk).
  */
 public function register($appMeta)
 {
     AppMeta::validate($appMeta);
     if ($this->certValidator) {
         $this->certValidator->validateCert($appMeta['appCert']);
     }
     $cxn = $this->cxnStore->getByAppId($appMeta['appId']);
     if (!$cxn) {
         $cxn = array('cxnId' => Cxn::createId(), 'secret' => AesHelper::createSecret(), 'appId' => $appMeta['appId']);
     }
     $cxn['appUrl'] = $appMeta['appUrl'];
     $cxn['siteUrl'] = $this->siteUrl;
     $cxn['perm'] = $appMeta['perm'];
     Cxn::validate($cxn);
     $this->cxnStore->add($cxn);
     list($respCode, $respData) = $this->doCall($appMeta, 'Cxn', 'register', array(), $cxn);
     $success = $respCode == 200 && $respData['is_error'] == 0;
     $this->log->info($success ? 'Registered cxnId={cxnId} ({appId}, {appUrl})' : 'Failed to register cxnId={cxnId} ({appId}, {appUrl})', array('cxnId' => $cxn['cxnId'], 'appId' => $cxn['appId'], 'appUrl' => $cxn['appUrl']));
     return array($cxn['cxnId'], $respData);
 }
 public function invalidInputExamples()
 {
     $appKeyPair = KeyPair::create();
     $otherKeyPair = KeyPair::create();
     return array(array($appKeyPair, new InsecureMessage(array('sldjkfasdf'))), array($appKeyPair, new InsecureMessage(array('cxn' => array('abcd')))), array($appKeyPair, new StdMessage(Cxn::createId(), AesHelper::createSecret(), array('whatever'))), array($appKeyPair, new RegistrationMessage('app:org.civicrm.other', $appKeyPair['publickey'], array('whatever'))), array($appKeyPair, new RegistrationMessage(self::APP_ID, $otherKeyPair['publickey'], array('whatever'))));
 }