/** * @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); }
/** * @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)); }
/** * 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')))); }