ppcl.php 4.2 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130
  1. <?php
  2. class Ppcl {
  3. public $identifier;
  4. public $serial;
  5. public $domains = array();
  6. public $members = array();
  7. public $last_modified;
  8. public $signature;
  9. public $shared_signature_key;
  10. public $shared_signature;
  11. public $publications = array();
  12. public function from_string($str) {
  13. $lines = explode("\n", $str);
  14. $header = explode(" ", $lines[0]);
  15. if($header[0] != "PPCL") {
  16. throw new Exception("Header does not start with PPCL magic number", 1);
  17. }
  18. $this->identifier = base64_decode($header[1]);
  19. $this->serial = (int)$header[2];
  20. foreach ($lines as $line) {
  21. $entry = explode(" ", $line);
  22. if($entry[0] == "PPCL") {
  23. continue;
  24. }
  25. if($entry[0] == "SSK") {
  26. $this->shared_signature_key = base64_decode($entry[1]);
  27. }
  28. else if($entry[0] == "DOM") {
  29. array_push($this->domains, $entry[1]);
  30. }
  31. else if($entry[0] == "MEM") {
  32. array_push($this->members, new CollectionMember($entry[1], base64_decode($entry[2]), base64_decode($entry[3]), base64_decode($entry[4])));
  33. }
  34. else if($entry[0] == "SIG") {
  35. $this->signature = base64_decode($entry[1]);
  36. }
  37. else if($entry[0] == "MOD") {
  38. $this->last_modified = new DateTime($entry[1]);
  39. }
  40. else if($entry[0] == "PUB") {
  41. array_push($this->publications, new CollectionPublication($line, $this->members));
  42. }
  43. else if($entry[0] == "SSG") {
  44. $this->shared_signature = base64_decode($entry[1]);
  45. }
  46. }
  47. // Verify authoritative signature
  48. $parts = explode("\nSIG ", $str, 2);
  49. $hash = hash("sha512", $parts[0], true);
  50. $sig_data = sodium_crypto_sign_open($this->signature, $this->identifier);
  51. if($hash != $sig_data) {
  52. throw new Exception("Invalid authoritative signature", 1);
  53. }
  54. // Verify shared signature
  55. $parts = explode("\nSSG ", $str, 2);
  56. $hash = hash("sha512", $parts[0], true);
  57. $sig_data = sodium_crypto_sign_open($this->shared_signature, $this->shared_signature_key);
  58. if($hash != $sig_data) {
  59. throw new Exception("Invalid shared signature", 1);
  60. }
  61. }
  62. }
  63. class CollectionMember {
  64. public $name;
  65. public $signing_public_key;
  66. public $sealing_public_key;
  67. public $collection_secret;
  68. public function __construct($name, $sign_key, $seal_key, $secret) {
  69. $this->name = $name;
  70. $this->signing_public_key = $sign_key;
  71. $this->sealing_public_key = $seal_key;
  72. $this->collection_secret = $secret;
  73. }
  74. }
  75. class CollectionPublication {
  76. public $name;
  77. public $member_name;
  78. public $timestamp;
  79. public $signature;
  80. public $checksum;
  81. public function __construct($line, $members) {
  82. $parts = explode(": ", $line, 2);
  83. $params = explode(" ", $parts[1]);
  84. $this->name = substr($parts[0], 4);
  85. $this->member_name = $params[0];
  86. $this->timestamp = new DateTime($params[1]);
  87. $this->signature = base64_decode($params[2]);
  88. $member = null;
  89. foreach($members as $m) {
  90. if($m->name == $this->member_name) {
  91. $member = $m;
  92. break;
  93. }
  94. }
  95. if($member == null) {
  96. throw new Exception("PUB entry references member (" . $this->member_name . ") that is not present in the collection", 1);
  97. }
  98. $hash_data = $this->name . ": " . $this->member_name . " " . $params[1];
  99. $hash = hash("sha512", $hash_data, true);
  100. $sig_data = sodium_crypto_sign_open($this->signature, $member->signing_public_key);
  101. if(substr($sig_data, 0, 64) != $hash) {
  102. throw new Exception("Invalid member signature on publication " . $this->name , 1);
  103. }
  104. $this->checksum = substr($sig_data, 64, 64);
  105. }
  106. public function verify_ppub() {
  107. $file_hash = hash_file("sha512", PUBLICATION_DIR . "/" . $this->name, true);
  108. return $file_hash == $this->checksum;
  109. }
  110. }
  111. ?>