Friendica Communications Platform (please note that this is a clone of the repository at github, issues are handled there) https://friendi.ca
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

153 lines
3.6 KiB

  1. <?php
  2. require_once('library/asn1.php');
  3. function salmon_key($pubkey) {
  4. $lines = explode("\n",$pubkey);
  5. unset($lines[0]);
  6. unset($lines[count($lines)]);
  7. $x = base64_decode(implode('',$lines));
  8. $r = ASN_BASE::parseASNString($x);
  9. $m = $r[0]->asnData[1]->asnData[0]->asnData[0]->asnData;
  10. $e = $r[0]->asnData[1]->asnData[0]->asnData[1]->asnData;
  11. return 'RSA' . '.' . $m . '.' . $e ;
  12. }
  13. function base64url_encode($s) {
  14. return strtr(base64_encode($s),'+/','-_');
  15. }
  16. function base64url_decode($s) {
  17. return base64_decode(strtr($s,'-_','+/'));
  18. }
  19. function get_salmon_key($uri,$keyhash) {
  20. $ret = array();
  21. $debugging = get_config('system','debugging');
  22. if($debugging)
  23. file_put_contents('salmon.out', "\n" . 'Fetch key' . "\n", FILE_APPEND);
  24. $arr = lrdd($uri);
  25. if(is_array($arr)) {
  26. foreach($arr as $a) {
  27. if($a['@attributes']['rel'] === 'magic-public-key') {
  28. $ret[] = $a['@attributes']['href'];
  29. }
  30. }
  31. }
  32. else {
  33. return '';
  34. }
  35. // We have found at least one key URL
  36. // If it's inline, parse it - otherwise get the key
  37. if(count($ret)) {
  38. for($x = 0; $x < count($ret); $x ++) {
  39. if(substr($ret[$x],0,5) === 'data:') {
  40. if(strstr($ret[$x],','))
  41. $ret[$x] = substr($ret[$x],strpos($ret[$x],',')+1);
  42. else
  43. $ret[$x] = substr($ret[$x],5);
  44. }
  45. else
  46. $ret[$x] = fetch_url($ret[$x]);
  47. }
  48. }
  49. if($debugging)
  50. file_put_contents('salmon.out', "\n" . 'Key located: ' . print_r($ret,true) . "\n", FILE_APPEND);
  51. if(count($ret) == 1) {
  52. // We only found one one key so we don't care if the hash matches.
  53. // If it's the wrong key we'll find out soon enough because
  54. // message verification will fail. This also covers some older
  55. // software which don't supply a keyhash. As long as they only
  56. // have one key we'll be right.
  57. return $ret[0];
  58. }
  59. else {
  60. foreach($ret as $a) {
  61. $hash = base64url_encode(hash('sha256',$a));
  62. if($hash == $keyhash)
  63. return $a;
  64. }
  65. }
  66. return '';
  67. }
  68. function slapper($owner,$contact,$slap) {
  69. // does contact have a salmon endpoint?
  70. if(! strlen($contact['notify']))
  71. return;
  72. // add all namespaces to item
  73. $namespaces = <<< EOT
  74. <entry xmlns="http://www.w3.org/2005/Atom"
  75. xmlns:thr="http://purl.org/syndication/thread/1.0"
  76. xmlns:at="http://purl.org/atompub/tombstones/1.0"
  77. xmlns:media="http://purl.org/syndication/atommedia"
  78. xmlns:dfrn="http://purl.org/macgirvin/dfrn/1.0"
  79. xmlns:as="http://activitystrea.ms/spec/1.0/"
  80. xmlns:georss="http://www.georss.org/georss"
  81. xmlns:poco="http://portablecontacts.net/spec/1.0" >
  82. EOT;
  83. $slap = str_replace('<entry>',$namespaces,$slap);
  84. // create a magic envelope
  85. $data = base64url_encode($slap);
  86. $data_type = 'application/atom+xml';
  87. $encoding = 'base64url';
  88. $algorithm = 'RSA-SHA256';
  89. $keyhash = base64url_encode(hash('sha256',salmon_key($owner['spubkey'])));
  90. // Setup RSA stuff to PKCS#1 sign the data
  91. set_include_path(get_include_path() . PATH_SEPARATOR . 'phpsec');
  92. require_once('phpsec/Crypt/RSA.php');
  93. $rsa = new CRYPT_RSA();
  94. $rsa->signatureMode = CRYPT_RSA_SIGNATURE_PKCS1;
  95. $rsa->setHash('sha256');
  96. $rsa->loadKey($owner['sprvkey']);
  97. $signature = base64url_encode($rsa->sign($data));
  98. $salmon_tpl = load_view_file('view/magicsig.tpl');
  99. $salmon = replace_macros($salmon_tpl,array(
  100. '$data' => $data,
  101. '$encoding' => $encoding,
  102. '$algorithm' => $algorithm,
  103. '$keyhash' => $keyhash,
  104. '$signature' => $signature
  105. ));
  106. // slap them
  107. post_url($contact['notify'],$salmon, array(
  108. 'Content-type: application/magic-envelope+xml',
  109. 'Content-length: ' . strlen($salmon)
  110. ));
  111. $a = get_app();
  112. echo "CURL returned: " . $a->get_curl_code() . "\n";
  113. return;
  114. }