/** * @return string */ public static function createId() { return 'cxn:' . BinHex::bin2hex(crypt_random_string(Constants::CXN_ID_CHARS)); }
/** * Validate the signature and date of the message, then * decrypt it. * * @param string $secret * @param string $body * @param string $signature * @return string * Plain text. * @throws InvalidMessageException */ public static function authenticateThenDecrypt($secret, $body, $signature) { $keys = self::deriveAesKeys($secret); $localHmac = hash_hmac('sha256', $body, $keys['auth']); if (!self::hash_compare($signature, $localHmac)) { throw new InvalidMessageException("Incorrect hash"); } list($jsonEnvelope, $jsonEncrypted) = explode(Constants::PROTOCOL_DELIM, $body, 2); if (strlen($jsonEnvelope) > Constants::MAX_ENVELOPE_BYTES) { throw new InvalidMessageException("Oversized envelope"); } $envelope = json_decode($jsonEnvelope, TRUE); if (!$envelope) { throw new InvalidMessageException("Malformed envelope"); } if (!is_numeric($envelope['ttl']) || Time::getTime() > $envelope['ttl']) { throw new InvalidMessageException("Invalid TTL"); } if (!is_string($envelope['iv']) || strlen($envelope['iv']) !== Constants::AES_BYTES * 2 || !preg_match('/^[a-f0-9]+$/', $envelope['iv'])) { // AES_BYTES (32) ==> bin2hex ==> 2 hex digits (4-bit) per byte (8-bit) throw new InvalidMessageException("Malformed initialization vector"); } $jsonPlaintext = UserError::adapt('Civi\\Cxn\\Rpc\\Exception\\InvalidMessageException', function () use($jsonEncrypted, $envelope, $keys) { $cipher = new \Crypt_AES(CRYPT_AES_MODE_CBC); $cipher->setKeyLength(Constants::AES_BYTES); $cipher->setKey($keys['enc']); $cipher->setIV(BinHex::hex2bin($envelope['iv'])); return $cipher->decrypt($jsonEncrypted); }); return $jsonPlaintext; }