Пример #1
0
 public function testSjclValidatorValidatesCorrectly()
 {
     $this->assertTrue(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'valid sjcl');
     $this->assertFalse(sjcl::isValid('{"iv":"$","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of iv');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"$","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid base64 encoding of salt');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","salt":"Gx1vA2/gQ3U","ct":"$"}'), 'invalid base64 encoding of ct');
     $this->assertFalse(sjcl::isValid('{"iv":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'iv to long');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA=","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'salt to long');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA","foo":"MTIzNDU2Nzg5MDEyMzQ1Njc4OTA="}'), 'invalid additional key');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":0.9,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'unsupported version');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":100,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'not enough iterations');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":127,"ts":64,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid key size');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":63,"mode":"ccm","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid tag length');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"!#@","adata":"","cipher":"aes","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid mode');
     $this->assertFalse(sjcl::isValid('{"iv":"83Ax/OdUav3SanDW9dcQPg","v":1,"iter":1000,"ks":128,"ts":64,"mode":"ccm","adata":"","cipher":"!#@","salt":"Gx1vA2/gQ3U","ct":"j7ImByuE5xCqD2YXm6aSyA"}'), 'invalid cipher');
     // @note adata is not validated, except as part of the total message length
 }
Пример #2
0
 /**
  * Store new paste or comment
  *
  * POST contains:
  * data (mandatory) = json encoded SJCL encrypted text (containing keys: iv,salt,ct)
  *
  * All optional data will go to meta information:
  * expire (optional) = expiration delay (never,5min,10min,1hour,1day,1week,1month,1year,burn) (default:never)
  * opendiscusssion (optional) = is the discussion allowed on this paste ? (0/1) (default:0)
  * nickname (optional) = in discussion, encoded SJCL encrypted text nickname of author of comment (containing keys: iv,salt,ct)
  * parentid (optional) = in discussion, which comment this comment replies to.
  * pasteid (optional) = in discussion, which paste this comment belongs to.
  *
  * @access private
  * @param  string $data
  * @return void
  */
 private function _create($data)
 {
     header('Content-type: application/json');
     $error = false;
     // Make sure last paste from the IP address was more than X seconds ago.
     trafficlimiter::setLimit($this->_conf['traffic']['limit']);
     trafficlimiter::setPath($this->_conf['traffic']['dir']);
     if (!trafficlimiter::canPass($_SERVER['REMOTE_ADDR'])) {
         $this->_return_message(1, 'Please wait ' . $this->_conf['traffic']['limit'] . ' seconds between each post.');
     }
     // Make sure content is not too big.
     $sizelimit = (int) $this->_getMainConfig('sizelimit', 2097152);
     if (strlen($data) > $sizelimit) {
         $this->_return_message(1, 'Paste is limited to ' . filter::size_humanreadable($sizelimit) . ' of encrypted data.');
     }
     // Make sure format is correct.
     if (!sjcl::isValid($data)) {
         $this->_return_message(1, 'Invalid data.');
     }
     // Read additional meta-information.
     $meta = array();
     // Read expiration date
     if (!empty($_POST['expire'])) {
         $selected_expire = (string) $_POST['expire'];
         if (array_key_exists($selected_expire, $this->_conf['expire_options'])) {
             $expire = $this->_conf['expire_options'][$selected_expire];
         } else {
             $expire = $this->_conf['expire_options'][$this->_conf['expire']['default']];
         }
         if ($expire > 0) {
             $meta['expire_date'] = time() + $expire;
         }
     }
     // Destroy the paste when it is read.
     if (!empty($_POST['burnafterreading'])) {
         $burnafterreading = $_POST['burnafterreading'];
         if ($burnafterreading !== '0') {
             if ($burnafterreading !== '1') {
                 $error = true;
             }
             $meta['burnafterreading'] = true;
         }
     }
     // Read open discussion flag.
     if ($this->_conf['main']['opendiscussion'] && !empty($_POST['opendiscussion'])) {
         $opendiscussion = $_POST['opendiscussion'];
         if ($opendiscussion !== '0') {
             if ($opendiscussion !== '1') {
                 $error = true;
             }
             $meta['opendiscussion'] = true;
         }
     }
     // You can't have an open discussion on a "Burn after reading" paste:
     if (isset($meta['burnafterreading'])) {
         unset($meta['opendiscussion']);
     }
     // Optional nickname for comments
     if (!empty($_POST['nickname'])) {
         // Generation of the anonymous avatar (Vizhash):
         // If a nickname is provided, we generate a Vizhash.
         // (We assume that if the user did not enter a nickname, he/she wants
         // to be anonymous and we will not generate the vizhash.)
         $nick = $_POST['nickname'];
         if (!sjcl::isValid($nick)) {
             $error = true;
         } else {
             $meta['nickname'] = $nick;
             $vz = new vizhash16x16();
             $pngdata = $vz->generate($_SERVER['REMOTE_ADDR']);
             if ($pngdata != '') {
                 $meta['vizhash'] = 'data:image/png;base64,' . base64_encode($pngdata);
             }
             // Once the avatar is generated, we do not keep the IP address, nor its hash.
         }
     }
     if ($error) {
         $this->_return_message(1, 'Invalid data.');
     }
     // Add post date to meta.
     $meta['postdate'] = time();
     // We just want a small hash to avoid collisions:
     // Half-MD5 (64 bits) will do the trick
     $dataid = substr(hash('md5', $data), 0, 16);
     $storage = array('data' => $data);
     // Add meta-information only if necessary.
     if (count($meta)) {
         $storage['meta'] = $meta;
     }
     // The user posts a comment.
     if (!empty($_POST['parentid']) && !empty($_POST['pasteid'])) {
         $pasteid = (string) $_POST['pasteid'];
         $parentid = (string) $_POST['parentid'];
         if (!filter::is_valid_paste_id($pasteid) || !filter::is_valid_paste_id($parentid)) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Comments do not expire (it's the paste that expires)
         unset($storage['expire_date']);
         unset($storage['opendiscussion']);
         // Make sure paste exists.
         if (!$this->_model()->exists($pasteid)) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Make sure the discussion is opened in this paste.
         $paste = $this->_model()->read($pasteid);
         if (!$paste->meta->opendiscussion) {
             $this->_return_message(1, 'Invalid data.');
         }
         // Check for improbable collision.
         if ($this->_model()->existsComment($pasteid, $parentid, $dataid)) {
             $this->_return_message(1, 'You are unlucky. Try again.');
         }
         // New comment
         if ($this->_model()->createComment($pasteid, $parentid, $dataid, $storage) === false) {
             $this->_return_message(1, 'Error saving comment. Sorry.');
         }
         // 0 = no error
         $this->_return_message(0, $dataid);
     } else {
         // Check for improbable collision.
         if ($this->_model()->exists($dataid)) {
             $this->_return_message(1, 'You are unlucky. Try again.');
         }
         // New paste
         if ($this->_model()->create($dataid, $storage) === false) {
             $this->_return_message(1, 'Error saving paste. Sorry.');
         }
         // Generate the "delete" token.
         // The token is the hmac of the pasteid signed with the server salt.
         // The paste can be delete by calling http://myserver.com/zerobin/?pasteid=<pasteid>&deletetoken=<deletetoken>
         $deletetoken = hash_hmac('sha1', $dataid, serversalt::get());
         // 0 = no error
         $this->_return_message(0, $dataid, array('deletetoken' => $deletetoken));
     }
     $this->_return_message(1, 'Server error.');
 }
Пример #3
0
 /**
  * Set data and recalculate ID.
  *
  * @access public
  * @param string $data
  * @throws Exception
  * @return void
  */
 public function setData($data)
 {
     if (!sjcl::isValid($data)) {
         throw new Exception('Invalid data.', 61);
     }
     $this->_data->data = $data;
     // We just want a small hash to avoid collisions:
     // Half-MD5 (64 bits) will do the trick
     $this->setId(substr(hash('md5', $data), 0, 16));
 }
Пример #4
0
 /**
  * Set nickname.
  *
  * @access public
  * @param string $nickname
  * @throws Exception
  * @return void
  */
 public function setNickname($nickname)
 {
     if (!sjcl::isValid($nickname)) {
         throw new Exception('Invalid data.', 66);
     }
     $this->_data->meta->nickname = $nickname;
     // Generation of the anonymous avatar (Vizhash):
     // If a nickname is provided, we generate a Vizhash.
     // (We assume that if the user did not enter a nickname, he/she wants
     // to be anonymous and we will not generate the vizhash.)
     $vh = new vizhash16x16();
     $pngdata = $vh->generate(trafficlimiter::getIp());
     if ($pngdata != '') {
         $this->_data->meta->vizhash = 'data:image/png;base64,' . base64_encode($pngdata);
     }
     // Once the avatar is generated, we do not keep the IP address, nor its hash.
 }
Пример #5
0
 /**
  * Set paste's attachment name.
  *
  * @access public
  * @param string $attachmentname
  * @throws Exception
  * @return void
  */
 public function setAttachmentName($attachmentname)
 {
     if (!$this->_conf->getKey('fileupload') || !sjcl::isValid($attachmentname)) {
         throw new Exception('Invalid attachment.', 72);
     }
     $this->_data->meta->attachmentname = $attachmentname;
 }