]> git.mxchange.org Git - friendica.git/blob - mod/receive.php
get_diaspora_key()
[friendica.git] / mod / receive.php
1 <?php
2
3 /**
4  * Diaspora endpoint
5  */
6
7
8 require_once('include/salmon.php');
9 require_once('include/certfns.php');
10
11 function receive_return($val) {
12
13         if($val >= 400)
14                 $err = 'Error';
15         if($val >= 200 && $val < 300)
16                 $err = 'OK';
17
18         logger('mod-diaspora returns ' . $val); 
19         header($_SERVER["SERVER_PROTOCOL"] . ' ' . $val . ' ' . $err);
20         killme();
21
22 }
23
24
25 function get_diaspora_key($uri) {
26         $key = '';
27
28         logger('Fetching diaspora key for: ' . $uri);
29
30         $arr = lrdd($uri);
31
32         if(is_array($arr)) {
33                 foreach($arr as $a) {
34                         if($a['@attributes']['rel'] === 'diaspora-public-key') {
35                                 $key = base64_decode($a['@attributes']['href']);
36                         }
37                 }
38         }
39         else {
40                 return '';
41         }
42
43         if($key)
44                 return rsatopem($key);
45         return '';
46 }
47         
48 function receive_post(&$a) {
49
50         if($a->argc != 3 || $a->argv[1] !== 'users')
51                 receive_return(500);
52
53         $guid = $a->argv[2];
54
55         $r = q("SELECT * FROM `user` WHERE `guid` = '%s' LIMIT 1",
56                 dbesc($guid)
57         );
58         if(! count($r))
59                 salmon_return(500);
60
61         $importer = $r[0];
62
63         $xml = $_POST['xml'];
64
65         logger('mod-diaspora: new salmon ' . $xml, LOGGER_DATA);
66
67         if(! $xml)
68                 receive_return(500);
69
70
71         $basedom = parse_xml_string($xml);
72
73         if($basedom)
74                 logger('parsed dom');
75
76         $atom = $basedom->children(NAMESPACE_ATOM1);
77
78         logger('atom: ' . count($atom));
79         $encrypted_header = json_decode(base64_decode($atom->encrypted_header));
80
81         print_r($encrypted_header);
82         
83         $encrypted_aes_key_bundle = base64_decode($encrypted_header->aes_key);
84         $ciphertext = base64_decode($encrypted_header->ciphertext);
85
86         logger('encrypted_aes: ' . print_r($encrypted_aes_key_bundle,true));
87         logger('ciphertext: ' . print_r($ciphertext,true));
88
89         $outer_key_bundle = '';
90         openssl_private_decrypt($encrypted_aes_key_bundle,$outer_key_bundle,$localprvkey);
91
92         logger('outer_bundle: ' . print_r($outer_key_bundle,true));
93
94         $j_outer_key_bundle = json_decode($outer_key_bundle);
95
96         $outer_iv = base64_decode($j_outer_key_bundle->iv);
97         $outer_key = base64_decode($j_outer_key_bundle->key);
98
99         $decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $outer_key, $ciphertext, MCRYPT_MODE_CBC, $outer_iv);
100
101         $decrypted = pkcs5_unpad($decrypted);
102
103         logger('decrypted: ' . print_r($decrypted,true));
104
105         /**
106          * $decrypted now contains something like
107          *
108          *  <decrypted_header>
109          *     <iv>8e+G2+ET8l5BPuW0sVTnQw==</iv>
110          *     <aes_key>UvSMb4puPeB14STkcDWq+4QE302Edu15oaprAQSkLKU=</aes_key>
111          *     <author>
112          *       <name>Ryan Hughes</name>
113          *       <uri>acct:galaxor@diaspora.pirateship.org</uri>
114          *     </author>
115          *  </decrypted_header>
116          */
117
118         $idom = parse_xml_string($decrypted,false);
119
120         print_r($idom);
121         $inner_iv = base64_decode($idom->iv);
122         $inner_aes_key = base64_decode($idom->aes_key);
123
124         logger('inner_iv: ' . $inner_iv);
125
126         $dom = $basedom->children(NAMESPACE_SALMON_ME);
127
128         if($dom)
129                 logger('have dom');
130
131         logger('dom: ' . count($dom));
132         // figure out where in the DOM tree our data is hiding
133
134         if($dom->provenance->data)
135                 $base = $dom->provenance;
136         elseif($dom->env->data)
137                 $base = $dom->env;
138         elseif($dom->data)
139                 $base = $dom;
140         
141         if(! $base) {
142                 logger('mod-diaspora: unable to locate salmon data in xml ');
143                 dt_return(400);
144         }
145
146
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);
149
150         logger('signature: ' . bin2hex($signature));
151
152 //      openssl_public_encrypt('test',$rrr,$rpubkey);
153 //      logger('rrr: ' . $rrr);
154
155 //      $pubdecsig = '';
156 //      openssl_public_decrypt($signature,$pubdecsig,$rpubkey);
157 //      logger('decsig: ' . bin2hex($pubdecsig));
158
159         // unpack the  data
160
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);
163
164         // stash away some other stuff for later
165
166         $type = $base->data[0]->attributes()->type[0];
167         $keyhash = $base->sig[0]->attributes()->keyhash[0];
168         $encoding = $base->encoding;
169         $alg = $base->alg;
170
171         $signed_data = $data  . "\n" . '.' . base64url_encode($type) . "\n" . '.' . base64url_encode($encoding) . "\n" . '.' . base64url_encode($alg) . "\n";
172
173         logger('signed data: ' . $signed_data);
174
175         // decode the data
176         $data = base64url_decode($data);
177
178         // Now pull out the inner encrypted blob
179
180
181
182
183         $inner_encrypted = base64_decode($data);
184
185         $inner_decrypted = 
186         $inner_decrypted = mcrypt_decrypt(MCRYPT_RIJNDAEL_128, $inner_aes_key, $inner_encrypted, MCRYPT_MODE_CBC, $inner_iv);
187
188         $inner_decrypted = pkcs5_unpad($inner_decrypted);
189
190         logger('inner_decrypted: ' . $inner_decrypted);
191
192
193
194         if(! $author_link) {
195                 logger('mod-diaspora: Could not retrieve author URI.');
196                 receive_return(400);
197         }
198
199         // Once we have the author URI, go to the web and try to find their public key
200         // *** or look it up locally ***
201
202         logger('mod-diaspora: Fetching key for ' . $author_link );
203
204 // Get diaspora public key (pkcs#1) and convert to pkcs#8
205         $key = get_diaspora_key($author_link);
206
207         if(! $key) {
208                 logger('mod-salmon: Could not retrieve author key.');
209                 receive_return(400);
210         }
211
212 // FIXME
213 // Use non salmon compliant signature
214
215 /*
216
217         // Setup RSA stuff to verify the signature
218
219         set_include_path(get_include_path() . PATH_SEPARATOR . 'library' . PATH_SEPARATOR . 'phpsec');
220
221         require_once('library/phpsec/Crypt/RSA.php');
222
223         $key_info = explode('.',$key);
224
225         $m = base64url_decode($key_info[1]);
226         $e = base64url_decode($key_info[2]);
227
228         logger('mod-salmon: key details: ' . print_r($key_info,true));
229
230     $rsa = new CRYPT_RSA();
231     $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
232     $rsa->setHash('sha256');
233
234     $rsa->modulus = new Math_BigInteger($m, 256);
235     $rsa->k = strlen($rsa->modulus->toBytes());
236     $rsa->exponent = new Math_BigInteger($e, 256);
237
238     $verify = $rsa->verify($signed_data,$signature);
239
240         if(! $verify) {
241                 logger('mod-diaspora: Message did not verify. Discarding.');
242                 receive_return(400);
243         }
244 */
245
246         logger('mod-diaspora: Message verified.');
247
248         /* decrypt the sucker */
249         /*
250                 // TODO
251         */
252
253         /*
254         *
255         * If we reached this point, the message is good. Now let's figure out if the author is allowed to send us stuff.
256         *
257         */
258
259         $r = q("SELECT * FROM `contact` WHERE `network` = 'dspr' AND ( `url` = '%s' OR `alias` = '%s') 
260                 AND `uid` = %d LIMIT 1",
261                 dbesc($author_link),
262                 dbesc($author_link),
263                 intval($importer['uid'])
264         );
265         if(! count($r)) {
266                 logger('mod-diaspora: Author unknown to us.');
267         }       
268
269         // is this a follower? Or have we ignored the person?
270         // If so we can not accept this post.
271
272         if((count($r)) && (($r[0]['readonly']) || ($r[0]['rel'] == REL_VIP) || ($r[0]['blocked']))) {
273                 logger('mod-diaspora: Ignoring this author.');
274                 receive_return(202);
275                 // NOTREACHED
276         }
277
278         require_once('include/items.php');
279
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.
282
283         $hub = '';
284
285         /**
286          *
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.
289          *
290          */
291
292         $contact_rec = ((count($r)) ? $r[0] : null);
293
294
295
296
297 // figure out what kind of diaspora message we have, and process accordingly.
298
299
300
301
302
303         receive_return(200);
304 }
305
306
307
308