8 require_once('include/salmon.php');
9 require_once('include/certfns.php');
11 function receive_return($val) {
15 if($val >= 200 && $val < 300)
18 logger('mod-diaspora returns ' . $val);
19 header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err);
25 function get_diaspora_key($uri) {
28 logger('Fetching diaspora key for: ' . $uri);
34 if($a['@attributes']['rel'] === 'diaspora-public-key') {
35 $key = base64_decode($a['@attributes']['href']);
44 return rsatopem($key);
48 function receive_post(&$a) {
50 if($a->argc != 3 || $a->argv[1] !== 'users')
55 $r = q("SELECT * FROM `user` WHERE `guid` = '%s' LIMIT 1",
65 logger('mod-diaspora: new salmon ' . $xml, LOGGER_DATA);
71 $basedom = parse_xml_string($xml);
76 $atom = $basedom->children(NAMESPACE_ATOM1);
78 logger('atom: ' . count($atom));
79 $encrypted_header = json_decode(base64_decode($atom->encrypted_header));
81 print_r($encrypted_header);
83 $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
84 $ciphertext = base64_decode($encrypted_header->ciphertext);
86 logger('encrypted_aes: ' . print_r($encrypted_aes_key_bundle,true));
87 logger('ciphertext: ' . print_r($ciphertext,true));
89 $outer_key_bundle = '';
90 openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$localprvkey);
92 logger('outer_bundle: ' . print_r($outer_key_bundle,true));
94 $j_outer_key_bundle = json_decode($outer_key_bundle);
96 $outer_iv = base64_decode($j_outer_key_bundle->iv);
97 $outer_key = base64_decode($j_outer_key_bundle->key);
99 $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
101 $decrypted = pkcs5_unpad($decrypted);
103 logger('decrypted: ' . print_r($decrypted,true));
106 * $decrypted now contains something like
109 * <iv>8e+G2+ET8l5BPuW0sVTnQw==</iv>
110 * <aes_key>UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU=</aes_key>
112 * <name>Ryan Hughes</name>
113 * <uri>acct:galaxor@diaspora.pirateship.org</uri>
115 * </decrypted_header>
118 $idom = parse_xml_string($decrypted,false);
121 $inner_iv = base64_decode($idom->iv);
122 $inner_aes_key = base64_decode($idom->aes_key);
124 logger('inner_iv: ' . $inner_iv);
126 $dom = $basedom->children(NAMESPACE_SALMON_ME);
131 logger('dom: ' . count($dom));
132 // figure out where in the DOM tree our data is hiding
134 if($dom->provenance->data)
135 $base = $dom->provenance;
136 elseif($dom->env->data)
142 logger('mod-diaspora: unable to locate salmon data in xml ');
147 // Stash the signature away for now. We have to find their key or it won't be good for anything.
148 $signature = base64url_decode($base->sig);
150 logger('signature: ' . bin2hex($signature));
152 // openssl_public_encrypt('test',$rrr,$rpubkey);
153 // logger('rrr: ' . $rrr);
156 // openssl_public_decrypt($signature,$pubdecsig,$rpubkey);
157 // logger('decsig: ' . bin2hex($pubdecsig));
161 // strip whitespace so our data element will return to one big base64 blob
162 $data = str_replace(array(" ","\t","\r","\n"),array("","","",""),$base->data);
164 // stash away some other stuff for later
166 $type = $base->data[0]->attributes()->type[0];
167 $keyhash = $base->sig[0]->attributes()->keyhash[0];
168 $encoding = $base->encoding;
171 $signed_data = $data . "\n" . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n";
173 logger('signed data: ' . $signed_data);
176 $data = base64url_decode($data);
178 // Now pull out the inner encrypted blob
183 $inner_encrypted = base64_decode($data);
186 $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
188 $inner_decrypted = pkcs5_unpad($inner_decrypted);
190 logger('inner_decrypted: ' . $inner_decrypted);
195 logger('mod-diaspora: Could not retrieve author URI.');
199 // Once we have the author URI, go to the web and try to find their public key
200 // *** or look it up locally ***
202 logger('mod-diaspora: Fetching key for ' . $author_link );
204 // Get diaspora public key (pkcs#1) and convert to pkcs#8
205 $key = get_diaspora_key($author_link);
208 logger('mod-salmon: Could not retrieve author key.');
213 // Use non salmon compliant signature
217 // Setup RSA stuff to verify the signature
219 set_include_path(get_include_path() . PATH_SEPARATOR . 'library' . PATH_SEPARATOR . 'phpsec');
221 require_once('library/phpsec/Crypt/RSA.php');
223 $key_info = explode('.',$key);
225 $m = base64url_decode($key_info[1]);
226 $e = base64url_decode($key_info[2]);
228 logger('mod-salmon: key details: ' . print_r($key_info,true));
230 $rsa = new CRYPT_RSA();
231 $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
232 $rsa->setHash('sha256');
234 $rsa->modulus = new Math_BigInteger($m, 256);
235 $rsa->k = strlen($rsa->modulus->toBytes());
236 $rsa->exponent = new Math_BigInteger($e, 256);
238 $verify = $rsa->verify($signed_data,$signature);
241 logger('mod-diaspora: Message did not verify. Discarding.');
246 logger('mod-diaspora: Message verified.');
248 /* decrypt the sucker */
255 * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
259 $r = q("SELECT * FROM `contact` WHERE `network` = 'dspr' AND ( `url` = '%s' OR `alias` = '%s')
260 AND `uid` = %d LIMIT 1",
263 intval($importer['uid'])
266 logger('mod-diaspora: Author unknown to us.');
269 // is this a follower? Or have we ignored the person?
270 // If so we can not accept this post.
272 if((count($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == REL_VIP) || ($r[0]['blocked']))) {
273 logger('mod-diaspora: Ignoring this author.');
278 require_once('include/items.php');
280 // Placeholder for hub discovery. We shouldn't find any hubs
281 // since we supplied the fake feed header - and it doesn't have any.
287 * anti-spam measure: consume_feed will accept a follow activity from
288 * this person (and nothing else) if there is no existing contact record.
292 $contact_rec = ((count($r)) ? $r[0] : null);
297 // figure out what kind of diaspora message we have, and process accordingly.