Пример #1
0
/**
 * Create a unique ID (e.g. for permalinks)
 *
 * @param int $length
 * @return string
 */
function uniqueId(int $length = 24) : string
{
    if ($length < 1) {
        return '';
    }
    $n = (int) ceil($length * 0.75);
    $str = \random_bytes($n);
    return Binary::safeSubstr(Base64UrlSafe::encode($str), 0, $length);
}
Пример #2
0
 /**
  * @route notary
  */
 public function index()
 {
     \Airship\json_response(['status' => 'OK', 'channel' => $this->channel, 'message' => '', 'public_key' => Base64UrlSafe::encode($this->pk->getRawKeyMaterial())]);
 }
Пример #3
0
 /**
  * Create a random workspace directory
  *
  * @return string
  * @throws \Error
  */
 protected function createWorkspace() : string
 {
     do {
         $dirname = Base64UrlSafe::encode(\random_bytes(18));
     } while (\is_dir(AIRSHIP_LOCAL_CONFIG . DIRECTORY_SEPARATOR . $dirname));
     if (!\mkdir(AIRSHIP_LOCAL_CONFIG . DIRECTORY_SEPARATOR . $dirname, 0700)) {
         throw new \Error('Could not create workspace directory: ' . AIRSHIP_LOCAL_CONFIG . DIRECTORY_SEPARATOR . $dirname);
     }
     return AIRSHIP_LOCAL_CONFIG . DIRECTORY_SEPARATOR . $dirname;
 }
Пример #4
0
 /**
  * Get/verify/parse a JSON response
  *
  * The _server_ is the one that signs the message.
  * We're just verifying the Ed25519 signature.
  *
  * @param string $url
  * @param SignaturePublicKey $publicKey
  * @param array $args
  * @param array $options
  * @return array
  * @throws \Exception
  */
 public static function postSignedJSON(string $url, SignaturePublicKey $publicKey, array $args = [], array $options = []) : array
 {
     $body = self::post($url, $args, $options);
     if (empty($body)) {
         throw new \Exception('Empty response from ' . $url);
     }
     if (self::$debug) {
         \var_dump($body);
     }
     $firstNewLine = \strpos($body, "\n");
     // There should be a newline immediately after the base64urlsafe-encoded signature
     if ($firstNewLine !== self::ENCODED_SIGNATURE_LENGTH) {
         throw new \Exception('Invalid Signature');
     }
     $sig = Base64UrlSafe::decode(Binary::safeSubstr($body, 0, self::ENCODED_SIGNATURE_LENGTH));
     $msg = Binary::safeSubstr($body, self::ENCODED_SIGNATURE_LENGTH + 1);
     if (!Asymmetric::verify($msg, $publicKey, $sig, true)) {
         throw new \Exception('Invalid Signature');
     }
     return \json_decode($msg, true);
 }
Пример #5
0
 /**
  * Parse the HTTP response and get the useful information out of it.
  *
  * @param array $data
  * @param \DateTime $originated
  * @return array
  * @throws CouldNotUpdate
  */
 protected function parseChannelUpdateResponse(array $data, \DateTime $originated) : array
 {
     if ($data['status'] !== 'success') {
         throw new CouldNotUpdate($data['message'] ?? \__('An update error has occurred'));
     }
     $valid = [];
     if (!empty($data['no_updates'])) {
         // Verify signature of the "no updates" timestamp.
         $sig = Base64UrlSafe::decode($data['signature']);
         if (!AsymmetricCrypto::verify($data['no_updates'], $this->channelPublicKey, $sig, true)) {
             throw new CouldNotUpdate(\__('Invalid signature from channel'));
         }
         $time = (new \DateTime($data['no_updates']))->add(new \DateInterval('P01D'));
         if ($time < $originated) {
             throw new CouldNotUpdate(\__('Channel is reporting a stale "no update" status'));
         }
         // No updates.
         return [];
     }
     // Verify the signature of each update.
     foreach ($data['updates'] as $update) {
         $data = Base64UrlSafe::decode($update['data']);
         $sig = Base64UrlSafe::decode($update['signature']);
         if (AsymmetricCrypto::verify($data, $this->channelPublicKey, $sig, true)) {
             $dataInternal = \json_decode($data, true);
             $valid[] = ['id' => (int) $update['id'], 'stored' => $dataInternal['stored'], 'master_signature' => $dataInternal['master_signature'], 'root' => $dataInternal['root'], 'data' => $dataInternal['data']];
         }
     }
     // Sort by ID
     \uasort($valid, function (array $a, array $b) : int {
         return (int) ($a['id'] <=> $b['id']);
     });
     return $valid;
 }
Пример #6
0
/**
 * Display the notary <meta> tag.
 *
 * @param SignaturePublicKey $pk
 */
function display_notary_tag(SignaturePublicKey $pk = null)
{
    $state = State::instance();
    $notary = $state->universal['notary'];
    if (!empty($notary['enabled'])) {
        if (!$pk) {
            $sk = $state->keyring['notary.online_signing_key'];
            if (!$sk instanceof SignatureSecretKey) {
                return;
            }
            $pk = $sk->derivePublicKey()->getRawKeyMaterial();
        }
        echo '<meta name="airship-notary" content="' . Base64UrlSafe::encode($pk) . '; channel=' . Util::noHTML($notary['channel']) . '; url=' . cabin_url('Bridge') . 'notary' . '" />';
    }
}
Пример #7
0
 /**
  * Parse a signed JSON response
  *
  * @param Response $response
  * @param SignaturePublicKey $publicKey
  * @return mixed
  * @throws SignatureFailed
  * @throws TransferException
  */
 public function parseSignedJSON(Response $response, SignaturePublicKey $publicKey)
 {
     $code = $response->getStatusCode();
     if ($code >= 200 && $code < 300) {
         $body = (string) $response->getBody();
         $firstNewLine = \strpos($body, "\n");
         // There should be a newline immediately after the base64urlsafe-encoded signature
         if ($firstNewLine !== self::ENCODED_SIGNATURE_LENGTH) {
             throw new SignatureFailed(\sprintf("First newline found at position %s, expected %d.\n%s", \print_r($firstNewLine, true), \print_r(self::ENCODED_SIGNATURE_LENGTH, true), Base64::encode($body)));
         }
         $sig = Base64UrlSafe::decode(Binary::safeSubstr($body, 0, 88));
         $msg = Binary::safeSubstr($body, 89);
         if (!Asymmetric::verify($msg, $publicKey, $sig, true)) {
             throw new SignatureFailed();
         }
         return \Airship\parseJSON($msg, true);
     }
     throw new TransferException();
 }
Пример #8
0
 /**
  * RFC 4648 Base64 (URL Safe) decoding
  *
  * "Zm9v" -> "foo"
  *
  * @param string $str
  * @return string
  */
 public function base64UrlSafeDecode(string $str) : string
 {
     return Base64UrlSafe::decode($str, true);
 }
Пример #9
-1
 public function testEncoding()
 {
     $random_bytes = \random_bytes(31);
     // Backwards compatibility:
     $encoder = Halite::chooseEncoder(false);
     $this->assertSame(Hex::encode($random_bytes), $encoder($random_bytes));
     $encoder = Halite::chooseEncoder(true);
     $this->assertSame(null, $encoder);
     // New encoding in version 3:
     $encoder = Halite::chooseEncoder(Halite::ENCODE_HEX);
     $this->assertSame(Hex::encode($random_bytes), $encoder($random_bytes));
     $encoder = Halite::chooseEncoder(Halite::ENCODE_BASE32);
     $this->assertSame(Base32::encode($random_bytes), $encoder($random_bytes));
     $encoder = Halite::chooseEncoder(Halite::ENCODE_BASE32HEX);
     $this->assertSame(Base32Hex::encode($random_bytes), $encoder($random_bytes));
     $encoder = Halite::chooseEncoder(Halite::ENCODE_BASE64);
     $this->assertSame(Base64::encode($random_bytes), $encoder($random_bytes));
     $encoder = Halite::chooseEncoder(Halite::ENCODE_BASE64URLSAFE);
     $this->assertSame(Base64UrlSafe::encode($random_bytes), $encoder($random_bytes));
 }