protected function prepare(array $args = array()) { GNUsocial::setApi(true); // Send smaller error pages parent::prepare($args); if (!isset($_SERVER['CONTENT_TYPE'])) { // TRANS: Client error. Do not translate "Content-type" $this->clientError(_m('Salmon requires a Content-type header.')); } $envxml = null; switch ($_SERVER['CONTENT_TYPE']) { case 'application/magic-envelope+xml': $envxml = file_get_contents('php://input'); break; case 'application/x-www-form-urlencoded': $envxml = Magicsig::base64_url_decode($this->trimmed('xml')); break; default: // TRANS: Client error. Do not translate the quoted "application/[type]" strings. $this->clientError(_m('Salmon requires "application/magic-envelope+xml". For Diaspora we also accept "application/x-www-form-urlencoded" with an "xml" parameter.', 415)); } try { if (empty($envxml)) { throw new ClientException('No magic envelope supplied in POST.'); } $magic_env = new MagicEnvelope($envxml); // parse incoming XML as a MagicEnvelope $entry = $magic_env->getPayload(); // Not cryptographically verified yet! $this->activity = new Activity($entry->documentElement); if (empty($this->activity->actor->id)) { common_log(LOG_ERR, "broken actor: " . var_export($this->activity->actor->id, true)); common_log(LOG_ERR, "activity with no actor: " . var_export($this->activity, true)); // TRANS: Exception. throw new Exception(_m('Received a salmon slap from unidentified actor.')); } // ensureProfiles sets $this->actor and $this->oprofile $this->ensureProfiles(); } catch (Exception $e) { common_debug('Salmon envelope parsing failed with: ' . $e->getMessage()); $this->clientError($e->getMessage()); } // Cryptographic verification test if (!$magic_env->verify($this->actor)) { common_log(LOG_DEBUG, "Salmon signature verification failed."); // TRANS: Client error. $this->clientError(_m('Salmon signature verification failed.')); } return true; }
/** * * @param string $signed_bytes as raw byte string * @param string $signature as base64 * @return boolean */ public function verify($signed_bytes, $signature) { $signature = Magicsig::base64_url_decode($signature); return $this->publicKey->verify($signed_bytes, $signature); }
public function verify($env) { if ($env['alg'] != 'RSA-SHA256') { common_log(LOG_DEBUG, "Salmon error: bad algorithm"); return false; } if ($env['encoding'] != MagicEnvelope::ENCODING) { common_log(LOG_DEBUG, "Salmon error: bad encoding"); return false; } $text = Magicsig::base64_url_decode($env['data']); $signer_uri = $this->getAuthor($text); try { $keypair = $this->getKeyPair($signer_uri); } catch (Exception $e) { common_log(LOG_DEBUG, "Salmon error: " . $e->getMessage()); return false; } $verifier = Magicsig::fromString($keypair); if (!$verifier) { common_log(LOG_DEBUG, "Salmon error: unable to parse keypair"); return false; } return $verifier->verify($env['data'], $env['sig']); }
public function getPayload() { $dom = new DOMDocument(); if (!$dom->loadXML(Magicsig::base64_url_decode($this->data))) { throw new ServerException('Malformed XML in Salmon payload'); } switch ($this->data_type) { case 'application/atom+xml': if ($dom->documentElement->namespaceURI !== Activity::ATOM || $dom->documentElement->tagName !== 'entry') { throw new ServerException(_m('Salmon post must be an Atom entry.')); } $prov = $dom->createElementNS(self::NS, 'me:provenance'); $prov->setAttribute('xmlns:me', self::NS); $data = $dom->createElementNS(self::NS, 'me:data', $this->data); $data->setAttribute('type', $this->data_type); $prov->appendChild($data); $enc = $dom->createElementNS(self::NS, 'me:encoding', $this->encoding); $prov->appendChild($enc); $alg = $dom->createElementNS(self::NS, 'me:alg', $this->alg); $prov->appendChild($alg); $sig = $dom->createElementNS(self::NS, 'me:sig', $this->getSignature()); $prov->appendChild($sig); $dom->documentElement->appendChild($prov); break; default: throw new ServerException('Unknown Salmon payload data type'); } return $dom; }
/** * Fill out $this->privateKey or $this->publicKey with a Crypt_RSA object * representing the give key (as mod/exponent pair). * * @param string $mod base64-encoded * @param string $exp base64-encoded exponent * @param string $type one of 'public' or 'private' */ public function loadKey($mod, $exp, $type = 'public') { $rsa = new Crypt_RSA(); $rsa->setSignatureMode(CRYPT_RSA_SIGNATURE_PKCS1); $rsa->setHash($this->getHash()); $rsa->modulus = new Math_BigInteger(Magicsig::base64_url_decode($mod), 256); $rsa->k = strlen($rsa->modulus->toBytes()); $rsa->exponent = new Math_BigInteger(Magicsig::base64_url_decode($exp), 256); if ($type == 'private') { $this->privateKey = $rsa; } else { $this->publicKey = $rsa; } }