Пример #1
0
 /**
  * Execute the start command, which will start a new hangar session.
  *
  * @param array $args
  * @return bool
  * @throws \Error
  */
 public function fire(array $args = []) : bool
 {
     $file = $this->selectFile($args[0] ?? '');
     if (!isset($this->config['salt']) && \count($args) < 2) {
         throw new \Error('No salt configured or passed');
     }
     if (\count($args) > 2) {
         switch (\strtolower($args[2])) {
             case 'fast':
             case 'i':
             case 'interactive':
             case 'weak':
                 $level = KeyFactory::INTERACTIVE;
                 break;
             case 'm':
             case 'signing':
             case 'moderate':
                 $level = KeyFactory::MODERATE;
                 break;
             default:
                 $level = KeyFactory::SENSITIVE;
                 break;
         }
     } elseif (isset($this->config['keytype'])) {
         switch ($this->config['keytype']) {
             case 'fast':
             case 'i':
             case 'interactive':
             case 'weak':
                 $level = KeyFactory::INTERACTIVE;
                 break;
             case 'm':
             case 'signing':
             case 'moderate':
                 $level = KeyFactory::MODERATE;
                 break;
             default:
                 $level = KeyFactory::SENSITIVE;
                 break;
         }
     } else {
         $level = KeyFactory::SENSITIVE;
     }
     $salt = \Sodium\hex2bin($args[1] ?? $this->config['salt']);
     echo 'Generating a signature for: ', $file, "\n";
     $password = $this->silentPrompt('Enter password: '******'false' in version 2.0.0 (with Halite 3)
     $sign_kp = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $level);
     if (!$sign_kp instanceof SignatureKeyPair) {
         throw new \Error('Error during key derivation');
     }
     $signature = File::sign($file, $sign_kp->getSecretKey());
     if (isset($this->history)) {
         $this->config['build_history']['signed'] = true;
     }
     \file_put_contents($file . '.sig', $signature);
     echo 'File signed: ' . $file . '.sig', "\n";
     echo 'Public key: ' . \Sodium\bin2hex($sign_kp->getPublicKey()->getRawKeyMaterial()), "\n";
     return true;
 }
Пример #2
0
 public function testLegacyDerive()
 {
     $key = KeyFactory::deriveEncryptionKey('apple', "\t\n\v\f\r" . "", true);
     $this->assertEquals($key->getRawKeyMaterial(), "6�¹je\r��~^X��" . "63�u��7�B�TX-", true);
     $salt = \Sodium\hex2bin('762ce4cabd543065172236de1027536ad52ec4c9133ced3766ff319f10301888');
     // Issue #10
     $enc_secret = KeyFactory::deriveEncryptionKey('correct horse battery staple', $salt, Key::ENCRYPTION | Key::SECRET_KEY);
     $this->assertTrue($enc_secret->isEncryptionKey());
 }
Пример #3
0
 /**
  * Get the configuration for this version of halite
  *
  * @param string $stored   A stored password hash
  * @return SymmetricConfig
  * @throws InvalidMessage
  */
 protected static function getConfig(string $stored) : SymmetricConfig
 {
     $length = Util::safeStrlen($stored);
     // This doesn't even have a header.
     if ($length < 8) {
         throw new InvalidMessage('Encrypted password hash is way too short.');
     }
     if (\hash_equals(Util::safeSubstr($stored, 0, 5), Halite::VERSION_PREFIX)) {
         return SymmetricConfig::getConfig(Base64UrlSafe::decode($stored), 'encrypt');
     }
     $v = \Sodium\hex2bin(Util::safeSubstr($stored, 0, 8));
     return SymmetricConfig::getConfig($v, 'encrypt');
 }
Пример #4
0
 /**
  * Reload the signing keys
  *
  * @param array $data
  * @return Supplier
  */
 public function reloadSigningKeys(array $data = []) : self
 {
     if (empty($data)) {
         $data = \Airship\loadJSON(ROOT . '/config/supplier_keys/' . $this->name . '.json');
     }
     if (isset($data['signing_keys'])) {
         $keys = [];
         foreach ($data['signing_keys'] as $sk) {
             $keys[] = ['type' => $sk['type'], 'key' => new SignaturePublicKey(\Sodium\hex2bin($sk['public_key']))];
         }
         $this->signing_keys = $keys;
     }
     return $this;
 }
 /**
  * $secret should each be a 128-character hexademical value.
  *
  * This will be broken into 2 64-character parts: crypto secret and auth secret.
  *
  * While in memory these are stored as OpaqueProperty, to obscure from debug code or stacktraces.
  *
  * @param string $secret
  *
  * @throws CryptoException
  */
 public function __construct($secret)
 {
     if (!extension_loaded(self::LIBSODIUM_EXT)) {
         throw new CryptoException(self::ERR_LIBSODIUM);
     }
     if (!function_exists(self::FQDN_LIBSODIUM_VERSION)) {
         throw new CryptoException(self::ERR_LIBSODIUM);
     }
     if (!function_exists(self::FQDN_RANDOMBYTES)) {
         throw new CryptoException(self::ERR_CSPRNG);
     }
     if (1 !== preg_match(sprintf('#%s#', self::REGEX_FULL_SECRET), $secret)) {
         throw new CryptoException(self::ERR_INVALID_SECRET);
     }
     $this->cryptoSecret = new OpaqueProperty(\Sodium\hex2bin(ByteString::substr($secret, 0, 64)));
     $this->authSecret = new OpaqueProperty(\Sodium\hex2bin(ByteString::substr($secret, 64)));
 }
Пример #6
0
 /**
  * Channel constructor.
  *
  * @param object $parent (Continuum or Keyggdrasil)
  * @param string $name
  * @param array $config
  * @throws \TypeError
  */
 public function __construct($parent, string $name, array $config = [])
 {
     if ($parent instanceof Keyggdrasil || $parent instanceof Continuum) {
         $this->parent = $parent;
     }
     if (!\is1DArray($config['urls'])) {
         throw new \TypeError(\trk('errors.type.expected_1d_array'));
     }
     // The channel should be signing responses at the application layer:
     $this->publicKey = new SignaturePublicKey(\Sodium\hex2bin($config['publickey']));
     $this->name = $name;
     foreach (\array_values($config['urls']) as $index => $url) {
         if (!\is_string($url)) {
             throw new \TypeError(\trk('errors.type.expected_string_array', \gettype($url), $index));
         }
         $this->urls[] = $url;
     }
 }
Пример #7
0
 /**
  * Converts hexadecimal data to binary data.
  *
  * @param string $string String containing hexadecimal data to convert to binary.
  * @param string $ignore String characters to ignore in binary conversion.
  * @return string
  */
 public static function hex2bin($string, $ignore = '')
 {
     return \Sodium\hex2bin($string, $ignore);
 }
Пример #8
0
    $db->beginTransaction();
    foreach ($updates as $update) {
        $db->update('airship_package_cache', ['skyport_metadata' => \json_encode($update['metadata'])], ['packagetype' => $update['package']['type'], 'supplier' => $update['package']['supplier'], 'name' => $update['package']['name']]);
    }
    return $db->commit();
}
$channels = \Airship\loadJSON(ROOT . '/config/channels.json');
$state = State::instance();
$lastScan = \file_get_contents(ROOT . '/tmp/last_metadata_update.txt');
if ($lastScan === false) {
    $lastScan = '1970-01-01\\T00:00:00';
}
$db = \Airship\get_database();
if ($state->hail instanceof Hail) {
    foreach ($channels as $identifier => $channel) {
        $publicKey = new SignaturePublicKey(\Sodium\hex2bin($channel['public_key']));
        foreach ($channel['urls'] as $url) {
            try {
                $updates = $state->hail->postSignedJSON($url . '/packageMeta', $publicKey, ['since' => $lastScan]);
                if ($updates['status'] === 'success') {
                    if (\processUpdates($db, ['packageMetadata'])) {
                        file_put_contents(ROOT . '/tmp/last_metadata_update.txt', (new \DateTime())->format(\AIRSHIP_DATE_FORMAT));
                        exit(0);
                    }
                }
            } catch (NoAPIResponse $ex) {
            }
        }
    }
}
exit(255);
Пример #9
0
 /**
  * Get the new public key as a SignaturePublicKey object
  *
  * @return SignaturePublicKey
  */
 public function getPublicKeyObject() : SignaturePublicKey
 {
     return new SignaturePublicKey(\Sodium\hex2bin($this->newPublicKey));
 }
Пример #10
0
 /**
  * Sign the new key with our current master key
  *
  * @param string $supplier
  * @param string $messageToSign
  * @return string[]
  * @throws \Exception
  */
 protected function signNewKeyWithMasterKey(string $supplier, string $messageToSign) : array
 {
     $master_keys = [];
     foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $key) {
         if ($key['type'] === 'master' && !empty($key['salt'])) {
             $master_keys[] = $key;
         }
     }
     // This shouldn't happen, but just in case:
     if (empty($master_keys)) {
         throw new \Exception('You cannot generate another key unless you already have a master key with the salt loaded locally.');
     }
     // Select the correct master key.
     if (\count($master_keys) === 1) {
         $signingKey = $master_keys[0];
     } else {
         echo 'Select which master key to use:';
         do {
             foreach ($master_keys as $index => $key) {
                 $pk = Base64UrlSafe::encode(\Sodium\hex2bin($key['public_key']));
                 echo $index + 1, "\t", $pk, "\n";
             }
             $keyIndex = $this->prompt('Enter a number: ');
             if (empty($keyIndex)) {
                 // Okay, let's cancel.
                 throw new \Exception('Aborted.');
             }
             if ($keyIndex < 1 || $keyIndex > \count($master_keys)) {
                 $keyIndex = 0;
                 echo 'Please enter a number between 1 and ', \count($master_keys), ".\n";
             }
         } while ($keyIndex < 1);
         $signingKey = $master_keys[--$keyIndex];
     }
     $signature = '';
     $masterSalt = \Sodium\hex2bin($signingKey['salt']);
     do {
         $password = $this->silentPrompt('Enter the password for your master key: ');
         if (empty($password)) {
             // Okay, let's cancel.
             throw new \Exception('Aborted.');
         }
         $masterKeyPair = KeyFactory::deriveSignatureKeyPair($password, $masterSalt, false, KeyFactory::SENSITIVE);
         // We must verify the public key matches:
         $masterPublicKey = $masterKeyPair->getPublicKey();
         if (\hash_equals($masterPublicKey->getRawKeyMaterial(), \Sodium\hex2bin($signingKey['public_key']))) {
             $masterSecretKey = $masterKeyPair->getSecretKey();
             // Setting $signature exits the loop
             $signature = Asymmetric::sign($messageToSign, $masterSecretKey);
         } else {
             echo 'Incorrect master key passphrase!', "\n";
         }
     } while (!$signature);
     // We are returning two strings:
     return [$signature, $signingKey['public_key']];
 }
Пример #11
0
 /**
  * Check if a password is known by the knownpassword.org API.
  *
  * @param string $password      	The password to check.
  * @param string $passwordFormat    The format of the given password (Blake2b, Sha512, Cleartext) [Default: Blake2b].
  * @return mixed               		Exception on error, true if the password is known and false if the password is unknown.
  * @access public
  */
 public function checkPassword($password, $passwordFormat = "Blake2b")
 {
     $apiData = array();
     switch ($passwordFormat) {
         case "Blake2b":
             $apiData = array("Blake2b" => $password);
             break;
         case "Sha512":
             $apiData = array("Sha512" => $password);
             break;
         case "Cleartext":
             $apiData = array("Cleartext" => $password);
             break;
         default:
             throw new \Exception("Unknown passwordFormat.");
     }
     $nonce = \Sodium\randombytes_buf(24);
     $signature = \Sodium\crypto_sign_detached($nonce, $this->_privatekey);
     $clearJson = json_encode($apiData);
     $encryptionNonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_BOX_NONCEBYTES);
     $encryptionKeyPair = \Sodium\crypto_box_keypair();
     $encryptionSecretkey = \Sodium\crypto_box_secretkey($encryptionKeyPair);
     $encryptionPublickey = \Sodium\crypto_box_publickey($encryptionKeyPair);
     $encryptionKeyPair = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($encryptionSecretkey, $this->_serverEncryptionPublicKey);
     $ciphertext = \Sodium\crypto_box($clearJson, $encryptionNonce, $encryptionKeyPair);
     $encryptedApiData = array("PublicKey" => \Sodium\bin2hex($encryptionPublickey), "Nonce" => \Sodium\bin2hex($encryptionNonce), "Ciphertext" => \Sodium\bin2hex($ciphertext));
     $data_string = json_encode($encryptedApiData);
     $ch = curl_init($this->_apiurl . "/checkpassword");
     curl_setopt($ch, CURLOPT_CUSTOMREQUEST, "POST");
     curl_setopt($ch, CURLOPT_RETURNTRANSFER, TRUE);
     curl_setopt($ch, CURLOPT_SSL_VERIFYPEER, TRUE);
     curl_setopt($ch, CURLOPT_SSL_VERIFYHOST, 2);
     curl_setopt($ch, CURLOPT_HEADER, 1);
     curl_setopt($ch, CURLOPT_POSTFIELDS, $data_string);
     curl_setopt($ch, CURLOPT_HTTPHEADER, array('Content-Type: application/json', 'Content-Length: ' . strlen($data_string), 'User-Agent: ' . 'Laravel 5', 'X-Public: ' . \Sodium\bin2hex($this->_publickey), 'X-Nonce: ' . \Sodium\bin2hex($nonce), 'X-Signature: ' . \Sodium\bin2hex($signature)));
     if (!($result = curl_exec($ch))) {
         throw new \Exception("Request failed");
     }
     $header_size = curl_getinfo($ch, CURLINFO_HEADER_SIZE);
     $header = substr($result, 0, $header_size);
     $headers = $this->get_headers_from_curl_response($header);
     if (array_key_exists("http_code", $headers[0]) && array_key_exists("X-Powered-By", $headers[0]) && array_key_exists("X-Signature", $headers[0])) {
         $httpCode = $headers[0]["http_code"];
         $responsePowered = $headers[0]["X-Powered-By"];
         $responseSignature = $headers[0]["X-Signature"];
         $responseNonce = $headers[0]["X-Nonce"];
         if ($httpCode === "HTTP/1.1 200 OK" || $httpCode === "HTTP/2.0 200 OK") {
             if ($responsePowered === "bitbeans") {
                 // validate the response signature
                 if (!\Sodium\crypto_sign_verify_detached(\Sodium\hex2bin($responseSignature), \Sodium\crypto_generichash(\Sodium\hex2bin($responseNonce), null, 64), $this->_serverSignaturePublicKey)) {
                     throw new \Exception("Invalid signature");
                 }
             } else {
                 throw new \Exception("Invalid server");
             }
         } else {
             throw new \Exception("Invalid response code");
         }
     } else {
         throw new \Exception("Invalid header");
     }
     $result = substr($result, $header_size);
     curl_close($ch);
     $resultJson = json_decode($result);
     $decryptionKeyPair = \Sodium\crypto_box_keypair_from_secretkey_and_publickey($encryptionSecretkey, \Sodium\hex2bin($resultJson->{'publicKey'}));
     $plaintext = \Sodium\crypto_box_open(\Sodium\hex2bin($resultJson->{'ciphertext'}), \Sodium\hex2bin($resultJson->{'nonce'}), $decryptionKeyPair);
     if ($plaintext === FALSE) {
         throw new \Exception("Malformed message or invalid MAC");
     }
     $plaintextJson = json_decode($plaintext);
     return !$plaintextJson->{'FoundPassword'};
 }
Пример #12
0
 /**
  * Verify a MAC, given a MAC key
  * 
  * @param string $message
  * @param AuthenticationKey $secretKey
  * @param string $mac
  * @param boolean $raw
  * @return boolean
  */
 public static function verify($message, Contract\KeyInterface $secretKey, $mac, $raw = false)
 {
     if (!$secretKey instanceof AuthenticationKey) {
         throw new CryptoException\InvalidKey('Expected an instance of AuthenticationKey');
     }
     if (!$raw) {
         $mac = \Sodium\hex2bin($mac);
     }
     return self::verifyMAC($mac, $message, $secretKey->get());
 }
Пример #13
0
 /**
  * Verify that this key update was signed by the master key for this supplier.
  *
  * @param array $supplierData
  * @param array $keyData
  * @param array $nodeData
  * @return bool
  */
 protected function verifyMasterSignature(array $supplierData, array $keyData, array $nodeData) : bool
 {
     $masterData = \json_decode($nodeData['master'], true);
     if ($masterData === false) {
         return false;
     }
     foreach ($supplierData['signing_keys'] as $key) {
         if ($key['type'] !== 'master') {
             continue;
         }
         if (\hash_equals($keyData['public_key'], $masterData['public_key'])) {
             $publicKey = new SignaturePublicKey(\Sodium\hex2bin($masterData['public_key']));
             $message = \json_encode($keyData);
             // If the signature is valid, we return true.
             return AsymmetricCrypto::verify($message, $publicKey, $masterData['signature']);
         }
     }
     // Fail closed.
     return false;
 }
Пример #14
0
 /**
  * Read a key from a file, verify its checksum
  * 
  * @param string $filePath
  * @return string
  * @throws Alerts\CannotPerformOperation
  */
 protected static function loadKeyFile(string $filePath) : string
 {
     $filedata = \file_get_contents($filePath);
     if ($filedata === false) {
         throw new Alerts\CannotPerformOperation('Cannot load key from file: ' . $filePath);
     }
     $data = \Sodium\hex2bin($filedata);
     \Sodium\memzero($filedata);
     return self::getKeyDataFromString($data);
 }
Пример #15
0
 /**
  * Verify a signed message with the correct public key
  * 
  * @param string $message Message to verify
  * @param SignaturePublicKey $publicKey
  * @param string $signature
  * @param boolean $raw Don't hex decode the input?
  * 
  * @return boolean
  * 
  * @throws CryptoException\InvalidKey
  * @throws CryptoException\CannotPerformOperation
  */
 public static function verify($message, Contract\KeyInterface $publicKey, $signature, $raw = false)
 {
     if (!$publicKey instanceof SignaturePublicKey) {
         throw new CryptoException\InvalidKey('Argument 2: Expected an instance of SignaturePublicKey');
     }
     if (!$raw) {
         $signature = \Sodium\hex2bin($signature);
     }
     return \Sodium\crypto_sign_verify_detached($signature, $message, $publicKey->get());
 }
Пример #16
0
 /**
  * Get the signature
  *
  * @param bool $hex
  * @return string
  */
 public function getSignature(bool $hex = false) : string
 {
     $signature = $this->releaseInfo['signature'];
     if (!$hex) {
         return \Sodium\hex2bin($signature);
     }
     return $signature;
 }
Пример #17
0
 /**
  * Verify a MAC, given a MAC key
  * 
  * @param string $message
  * @param AuthenticationKey $secretKey
  * @param string $mac
  * @param boolean $raw
  * @return boolean
  */
 public static function verify(string $message, AuthenticationKey $secretKey, string $mac, bool $raw = false) : bool
 {
     if (!$raw) {
         $mac = \Sodium\hex2bin($mac);
     }
     return self::verifyMAC($mac, $message, $secretKey->getRawKeyMaterial());
 }
Пример #18
0
 /**
  * @return array
  * @throws \Exception
  */
 protected final function getSkyport() : array
 {
     $sp = $this->config['skyports'];
     if (empty($sp)) {
         throw new \Exception("No skyports configured");
     }
     if (\count($sp) === 1) {
         $ret = \array_shift($sp);
         return [$ret['url'], new SignaturePublicKey(\Sodium\hex2bin($ret['public_key']))];
     }
     $k = \array_keys($sp);
     $i = $k[\random_int(0, \count($sp) - 1)];
     $ret = $sp[$i];
     return [$ret['url'], new SignaturePublicKey(\Sodium\hex2bin($ret['public_key']))];
 }
Пример #19
0
 function crCryptoProcess($dir, $input, $eckey, $cipher)
 {
     //  sanity checks
     $allowedDir = array('encrypt', 'decrypt');
     $inputTypes = array('string', 'ad');
     $cipherList = array('aes256gcm', 'chacha');
     if (!in_array($dir, $allowedDir)) {
         $this->webLog($this->crLanguage('generic', 'invalidKey', array('dir', implode('|', $allowedDir))), __METHOD__, 'error');
         return false;
     }
     if (is_string($input)) {
         $input = (object) array('string' => $input, 'ad' => '');
     }
     if (is_object($input)) {
         if (count((array) $input) === 2) {
             foreach ($input as $key => $value) {
                 if (!in_array($key, $inputTypes)) {
                     $this->webLog($this->crLanguage('generic', 'invalidKey', array('input', implode('|', $inputTypes))), __METHOD__, 'error');
                     return false;
                 }
             }
         } else {
             $this->webLog($this->crLanguage('generic', 'tooManyKeys', array(count($inputTypes), count($input), "input::(" . implode('|', $inputTypes) . ")")), __METHOD__, 'error');
             return false;
         }
     }
     /*if ( !is_string($eckey) ) {
     		
     			$this->webLog($this->crLanguage('generic', 'missingKeyType', 'eckey'), __METHOD__, 'error');
     			return false;
     			
     		}*/
     if (!in_array($cipher, $cipherList)) {
         $this->webLog($this->crLanguage('generic', 'invalidKey', array('cipher', implode('|', $cipherList))), __METHOD__, 'error');
         return false;
     }
     //  if the eckey matches the name of a stored key we'll map it to the proper eckey
     $keyid = '00-';
     if (isset($this->crCryptoKeys->{$eckey})) {
         //  set our keyid
         $keyid = $this->crCryptoKeys->{$eckey}->keyid;
         //  create our index
         $this->crCryptoKeys->index->{$keyid} = $eckey;
         //  set our eckey to the hex value
         $eckey = $this->crCryptoKeys->{$eckey}->eckey;
     }
     //  process the request
     $start = microtime(true);
     //  if we're decrypting then the input should be nonce.ciphertext
     if ($dir === 'decrypt') {
         //  grab our nonce bytes param
         if ($cipher === 'aes256gcm') {
             $bytes = \Sodium\CRYPTO_AEAD_AES256GCM_NPUBBYTES;
         }
         if ($cipher === 'chacha') {
             $bytes = \Sodium\CRYPTO_AEAD_CHACHA20POLY1305_NPUBBYTES;
         }
         //  grab our message data
         //  accepts two input types: <keyid>$<ciphertext> or just <ciphertext>
         $data = explode('$', $input->string);
         if (count($data) === 2) {
             //  located a keyid and ciphertext
             $keyid = $data[0];
             $message = $data[1];
             //  no keyid was located, so treat it as pure ciphertext
         } else {
             $data = $input->string;
         }
         //  parse the message contents
         $message = \Sodium\hex2bin($message);
         $nonce = mb_substr($message, 0, $bytes, '8bit');
         $ciphertext = mb_substr($message, $bytes, null, '8bit');
         //  on decrypt only: check if a key has been provided yet
         if ($eckey === false) {
             //  check if the keyid is indexed
             if (isset($this->crCryptoKeys->index->{$keyid})) {
                 //  found a match; set our eckey
                 $eckey = $this->crCryptoKeys->{$this->crCryptoKeys->index->{$keyid}}->eckey;
             } else {
                 //  no match and this far along means we can't decrypt
                 $this->webLog("Cannot decrypt ciphertext because no suitable eckey was located", __METHOD__, 'error');
                 return false;
             }
         }
     }
     //  if eckey is false, we cannot proceed
     if ($eckey === false) {
         $this->webLog();
         return false;
     }
     //  set our eckey to the actual encryption key
     $eckey = \Sodium\hex2bin($eckey);
     //  process AES-256-GCM methods
     if ($cipher === 'aes256gcm') {
         //$eckey = ( $eckey === false ) ? \Sodium\randombytes_buf(\Sodium\CRYPTO_AEAD_AES256GCM_KEYBYTES) : \Sodium\hex2bin($eckey);
         if ($dir === 'encrypt') {
             //  create the ciphertext
             $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_AEAD_AES256GCM_NPUBBYTES);
             $resultString = \Sodium\crypto_aead_aes256gcm_encrypt($input->string, $input->ad, $nonce, $eckey);
         } else {
             //  decrypt the ciphertext
             $resultString = \Sodium\crypto_aead_aes256gcm_decrypt($ciphertext, $input->ad, $nonce, $eckey);
         }
         //  process CHACHA20-POLY1305 methods
     } else {
         if ($cipher === 'chacha') {
             //$eckey = ( $eckey === false ) ? \Sodium\randombytes_buf(\Sodium\CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES) : \Sodium\hex2bin($eckey);
             if ($dir === 'encrypt') {
                 //  create the ciphertext
                 $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_AEAD_CHACHA20POLY1305_KEYBYTES);
                 $resultString = \Sodium\crypto_aead_chacha20poly1305_encrypt($input->string, $input->ad, $nonce, $eckey);
             } else {
                 //  decrypt the ciphertext
                 $resultString = \Sodium\crypto_aead_chacha20poly1305_decrypt($ciphertext, $input->ad, $nonce, $eckey);
             }
         }
     }
     //  finishing up
     $totalTime = number_format(microtime(true) - $start, $this->config->precison + 10);
     $this->webLog("Performed '{$dir}' on a string with {$cipher} in {$totalTime} seconds", __METHOD__);
     $this->crCryptoAnalytics($eckey, $nonce, $dir, $cipher, strlen($input->string), $totalTime);
     \Sodium\memzero($eckey);
     //  if decrypt, just send the string
     if ($dir === 'decrypt') {
         \Sodium\memzero($nonce);
         if ($resultString === false) {
             $this->webLog("Decryption failed!", __METHOD__, 'warn');
         }
         return $resultString;
     }
     //  if encrypt, send back the data in case we generated it
     $ciphertext = \Sodium\bin2hex($nonce . $resultString);
     \Sodium\memzero($nonce);
     return $keyid . '$' . $ciphertext;
 }
Пример #20
0
 /**
  * Verify a signed message with the correct public key
  * 
  * @param string $message Message to verify
  * @param SignaturePublicKey $publicKey
  * @param string $signature
  * @param boolean $raw Don't hex decode the input?
  * @return bool
  * @throws CryptoException\InvalidSignature
  */
 public static function verify(string $message, SignaturePublicKey $publicKey, string $signature, bool $raw = false) : bool
 {
     if (!$raw) {
         $signature = \Sodium\hex2bin($signature);
     }
     if (CryptoUtil::safeStrlen($signature) !== \Sodium\CRYPTO_SIGN_BYTES) {
         throw new CryptoException\InvalidSignature('Signature is not the correct length; is it encoded?');
     }
     return \Sodium\crypto_sign_verify_detached($signature, $message, $publicKey->getRawKeyMaterial());
 }
Пример #21
0
 /**
  * Verify a signed message with the correct public key
  * 
  * @param string $message Message to verify
  * @param Key $publickey
  * @param string $signature
  * @param boolean $raw Don't hex decode the input?
  * 
  * @return boolean
  */
 public static function verify($message, Contract\CryptoKeyInterface $publickey, $signature, $raw = false)
 {
     if (!$publickey->isSigningKey()) {
         throw new CryptoAlert\InvalidKey('Expected a signing key');
     }
     if (!$publickey->isPublicKey()) {
         throw new CryptoAlert\InvalidKey('Expected a public key');
     }
     if (!$raw) {
         $signature = \Sodium\hex2bin($signature);
     }
     return \Sodium\crypto_sign_verify_detached($signature, $message, $publickey->get());
 }
Пример #22
0
 /**
  * We are revoking a key.
  *
  * @param array $args
  * @throws \Exception
  * @return mixed
  */
 protected function handleKeyRevoke(array $args)
 {
     if (count($this->config['suppliers']) === 1) {
         $supplier = \count($args) > 0 ? $args[0] : \array_keys($this->config['suppliers'])[0];
     } else {
         $supplier = \count($args) > 0 ? $args[0] : $this->prompt("Please enter the name of the supplier: ");
     }
     if (!\array_key_exists($supplier, $this->config['suppliers'])) {
         echo 'Please authenticate before attempting to revoke keys.', "\n";
         echo 'Run this command: ', $this->c['yellow'], 'barge login', $this->c[''], "\n";
         exit(255);
     }
     $masterKeys = [];
     $keyList = [];
     foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $key) {
         if ($key['type'] === 'master') {
             if (!empty($key['salt'])) {
                 $masterKeys[] = $key;
             } else {
                 $keyList[] = $key;
             }
         } else {
             $keyList[] = $key;
         }
     }
     if (empty($masterKeys)) {
         echo 'No usable master keys found. Make sure the salt is loaded locally.', "\n";
         exit(255);
     }
     if (empty($keyList)) {
         // If and only if you have nothing more to revoke, allow revoking the master key:
         $keyList = $masterKeys;
     }
     if (\count($masterKeys) === 1) {
         $masterKey = $masterKeys[0];
     } else {
         $masterKey = $this->selectKeyFromList('Select your master key: ', $masterKeys);
         // Add other master keys to the list
         foreach ($masterKeys as $key) {
             if ($key['public_key'] !== $masterKey['public_key']) {
                 $keyList[] = $key;
             }
         }
     }
     if (\count($keyList) === 1) {
         $revokingKey = $keyList[0];
     } else {
         $revokingKey = $this->selectKeyFromList('Select which key to revoke: ', $keyList);
     }
     $confirm_revoke = null;
     while ($confirm_revoke === null) {
         $choice = $this->prompt('Are you sure you wish to revoke this key? (y/N): ');
         switch ($choice) {
             case 'YES':
             case 'yes':
             case 'Y':
             case 'y':
                 $confirm_revoke = true;
                 break;
             case 'N':
             case 'NO':
             case 'n':
             case 'no':
             case '':
                 // Just pressing enter means "don't store it"!
                 $confirm_revoke = false;
                 break;
             default:
                 echo "\n", $this->c['yellow'], 'Invalid response. Please enter yes or no.', $this->c[''], "\n";
         }
     }
     // This is what get signed by our master key:
     $message = ['action' => 'REVOKE', 'date_revoked' => \date('Y-m-d\\TH:i:s'), 'public_key' => $revokingKey['public_key'], 'supplier' => $supplier];
     $messageToSign = \json_encode($message);
     $iter = false;
     do {
         if ($iter) {
             echo 'Incorrect password.', "\n";
         }
         $password = $this->silentPrompt('Enter the password for your master key: ');
         if (empty($password)) {
             // Okay, let's cancel.
             throw new \Exception('Aborted.');
         }
         $masterKeyPair = KeyFactory::deriveSignatureKeyPair($password, \Sodium\hex2bin($masterKey['salt']), false, KeyFactory::SENSITIVE);
         \Sodium\memzero($password);
         $masterPublicKeyString = \Sodium\bin2hex($masterKeyPair->getPublicKey()->getRawKeyMaterial());
         $iter = true;
     } while (!\hash_equals($masterKey['public_key'], $masterPublicKeyString));
     $signature = Asymmetric::sign($messageToSign, $masterKeyPair->getSecretKey());
     $response = $this->sendRevocation($supplier, $message, $signature, $masterPublicKeyString);
     if ($response['status'] === 'OK') {
         foreach ($this->config['suppliers'][$supplier]['signing_keys'] as $i => $key) {
             if ($key['public_key'] === $message['public_key']) {
                 unset($this->config['suppliers'][$supplier]['signing_keys'][$i]);
             }
         }
     }
     return $response;
 }
Пример #23
0
 /**
  * Authenticate a user by a long-term authentication token (e.g. a cookie).
  * 
  * @param string $token
  * @return mixed int
  * @throws LongTermAuthAlert
  */
 public function loginByToken(string $token = '') : int
 {
     $table = $this->db->escapeIdentifier($this->tableConfig['table']['longterm']);
     $f = ['selector' => $this->db->escapeIdentifier($this->tableConfig['fields']['longterm']['selector']), 'userid' => $this->tableConfig['fields']['longterm']['userid'], 'validator' => $this->tableConfig['fields']['longterm']['validator']];
     try {
         $decoded = Base64::decode($token);
     } catch (\RangeException $ex) {
         throw new LongTermAuthAlert(\trk('errors.security.invalid_persistent_token'));
     }
     if ($decoded === false) {
         throw new LongTermAuthAlert(\trk('errors.security.invalid_persistent_token'));
     } elseif (Binary::safeStrlen($decoded) !== self::LONG_TERM_AUTH_BYTES) {
         throw new LongTermAuthAlert(\trk('errors.security.invalid_persistent_token'));
     }
     \Sodium\memzero($token);
     $sel = Binary::safeSubstr($decoded, 0, self::SELECTOR_BYTES);
     $val = CryptoUtil::raw_hash(Binary::safeSubstr($decoded, self::SELECTOR_BYTES));
     \Sodium\memzero($decoded);
     $record = $this->db->row('SELECT * FROM ' . $table . ' WHERE ' . $f['selector'] . ' = ?', Base64::encode($sel));
     if (empty($record)) {
         \Sodium\memzero($val);
         throw new LongTermAuthAlert(\trk('errors.security.invalid_persistent_token'));
     }
     $stored = \Sodium\hex2bin($record[$f['validator']]);
     \Sodium\memzero($record[$f['validator']]);
     if (!\hash_equals($stored, $val)) {
         \Sodium\memzero($val);
         \Sodium\memzero($stored);
         throw new LongTermAuthAlert(\trk('errors.security.invalid_persistent_token'));
     }
     \Sodium\memzero($stored);
     \Sodium\memzero($val);
     $userID = (int) $record[$f['userid']];
     $_SESSION['session_canary'] = $this->db->cell('SELECT session_canary FROM airship_users WHERE userid = ?', $userID);
     return $userID;
 }
Пример #24
0
 /**
  * Check that the signature is valid for a given Phar
  *
  * @param string $path
  * @param array $manifest
  * @return bool
  */
 protected function zipSignatureCheck(string $path, array $manifest = []) : bool
 {
     $supplier_name = $manifest['supplier'];
     $zipName = $supplier_name . '.' . $manifest['name'] . '.zip';
     $signature = \file_get_contents($path . '/dist/' . $zipName . '.ed25519.sig');
     $supplier =& $this->config['suppliers'][$supplier_name];
     $numKeys = \count($supplier['signing_keys']);
     $verified = false;
     for ($i = 0; $i < $numKeys; ++$i) {
         // signing key
         $publicKey = new SignaturePublicKey(\Sodium\hex2bin($supplier['signing_keys'][$i]['public_key']), true);
         if (File::verify($path . '/dist/' . $zipName, $publicKey, $signature)) {
             $verified = true;
         }
     }
     return $verified;
 }
Пример #25
0
 /**
  * Common signing process. User selects key, provides password.
  *
  * @param array $manifest
  * @return SignatureSecretKey
  * @throws \Exception
  */
 protected function signPreamble(array $manifest) : SignatureSecretKey
 {
     $HTAB = \str_repeat(' ', \intdiv(self::TAB_SIZE, 2));
     $supplier_name = $manifest['supplier'];
     // Sanity checks:
     if (!\array_key_exists('suppliers', $this->config)) {
         echo 'You are not authenticated for any suppliers.', "\n";
         exit(255);
     }
     if (!\array_key_exists($supplier_name, $this->config['suppliers'])) {
         echo 'Check the supplier in the JSON file (', $supplier_name, ') for correctness.', 'Otherwise, you might need to log in.', "\n";
         exit(255);
     }
     $supplier = $this->config['suppliers'][$supplier_name];
     $numKeys = 0;
     if ($this->signWithMasterKeys) {
         $good_keys = [];
         // This should really not be used:
         $numKeys = \count($supplier['signing_keys']);
         foreach ($supplier['signing_keys'] as $k) {
             if (!empty($k['salt'])) {
                 $good_keys[] = $k;
                 ++$numKeys;
             }
         }
     } else {
         // This should be used instead:
         $good_keys = [];
         foreach ($supplier['signing_keys'] as $k) {
             if ($k['type'] === 'signing' && !empty($k['salt'])) {
                 $good_keys[] = $k;
                 ++$numKeys;
             }
         }
     }
     if ($numKeys > 1) {
         echo 'You have more than one signing key available.', "\n";
         $n = 1;
         $size = (int) \floor(\log($numKeys, 10));
         $key_associations = $HTAB . "ID\t Public Key " . \str_repeat(' ', 33) . "\t Type\n";
         foreach ($supplier['signing_keys'] as $sign_key) {
             if (!$this->signWithMasterKeys && $sign_key['type'] === 'master') {
                 continue;
             }
             $_n = \str_pad($n, $size, ' ', STR_PAD_LEFT);
             // Short format:
             $pk = Base64UrlSafe::encode(\Sodium\hex2bin($sign_key['public_key']));
             $key_associations .= $HTAB . $_n . $HTAB . $pk . $HTAB . $sign_key['type'] . "\n";
             ++$n;
         }
         // Let's ascertain the user's key selection
         do {
             echo $key_associations;
             $choice = (int) $this->prompt('Enter the ID for the key you wish to use: ');
             if ($choice < 1 || $choice > $numKeys) {
                 $choice = null;
             }
         } while (empty($choice));
         $supplierKey = $good_keys[$choice - 1];
         echo "\n";
     } else {
         $supplierKey = $good_keys[0];
     }
     // The above !empty($k['salt']) check should have rendered this check redundant:
     if (empty($supplierKey['salt'])) {
         echo 'Salt not found for this key. It is not possible to reproduce it.', "\n";
         exit(255);
     }
     // Short format:
     $pk = Base64UrlSafe::encode(\Sodium\hex2bin($supplierKey['public_key']));
     // Color coded: Master keys are red, since they take longer.
     // We don't support signing packages with a master key, but
     // this decision could be undone in the future.
     $c = $supplierKey['type'] === 'master' ? $this->c['red'] : $this->c['yellow'];
     echo 'Selected ', $supplierKey['type'], ' key: ', $c, $pk, $this->c[''], "\n";
     $password = $this->silentPrompt('Enter Password for Signing Key:');
     // Derive and split the SignatureKeyPair from your password and salt
     $salt = \Sodium\hex2bin($supplierKey['salt']);
     switch ($supplierKey['type']) {
         case 'signing':
             $type = KeyFactory::MODERATE;
             echo 'Verifying (this may take a second or two)...';
             break;
         case 'master':
             $type = KeyFactory::SENSITIVE;
             echo 'Verifying (this may take a few seconds)...';
             break;
         default:
             $type = KeyFactory::INTERACTIVE;
             echo 'Verifying...';
     }
     $keyPair = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $type);
     $sign_secret = $keyPair->getSecretKey();
     $sign_public = $keyPair->getPublicKey();
     echo ' Done.', "\n";
     // We don't need this anymore.
     \Sodium\memzero($password);
     // Check that the public key we derived from the password matches the one on file
     $pubKey = \Sodium\bin2hex($sign_public->getRawKeyMaterial());
     if (!\hash_equals($supplierKey['public_key'], $pubKey)) {
         // Zero the memory ASAP
         unset($sign_secret);
         unset($sign_public);
         echo 'Invalid password for selected key', "\n";
         exit(255);
     }
     // Zero the memory ASAP
     unset($sign_public);
     return $sign_secret;
 }
Пример #26
0
 /**
  * Get the updated metadata for a particular package.
  *
  * @param string $type
  * @param string $supplier
  * @param string $pkg
  * @return array
  */
 protected function getPackageMetadata(string $type, string $supplier, string $pkg) : array
 {
     $state = State::instance();
     if (IDE_HACKS) {
         $state->hail = new Hail(new Client());
     }
     $channels = \Airship\loadJSON(ROOT . "/config/channels.json");
     $ch = $state->universal['airship']['trusted-supplier'] ?? 'paragonie';
     if (empty($channels[$ch])) {
         return [];
     }
     $publicKey = new SignaturePublicKey(\Sodium\hex2bin($channels[$ch]['publickey']));
     foreach ($channels[$ch]['urls'] as $url) {
         try {
             $response = $state->hail->postSignedJSON($url, $publicKey, ['type' => $type, 'supplier' => $supplier, 'name' => $pkg]);
         } catch (NoAPIResponse $ex) {
             // Continue
         }
     }
     if (empty($response)) {
         return [];
     }
     if ($response['status'] !== 'success') {
         return [];
     }
     return $response['packageMetadata'];
 }
 /**
  * Converts an hexdecimal string to a binary string.
  *
  * This is the same as PHP's hex2bin() implementation, but it is resistant to
  * timing attacks.
  *
  * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#hex2bin
  * @param  string $hexString The hex string to convert
  * @param  string|null $ignore	(optional) Characters to ignore
  * @throws \Threema\Core\Exception
  * @return string
  */
 public function hex2bin($hexString, $ignore = null)
 {
     /** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
     return \Sodium\hex2bin($hexString, $ignore);
 }
Пример #28
0
 /**
  * Verify a MAC, given a MAC key
  * 
  * @param string $message
  * @param \ParagonIE\Halite\Contract\CryptoKeyInterface $secretKey
  * @param string $mac
  * @param boolean $raw
  * @return boolean
  */
 public static function verify($message, Contract\CryptoKeyInterface $secretKey, $mac, $raw = false)
 {
     if (!$raw) {
         $mac = \Sodium\hex2bin($mac);
     }
     return self::verifyMAC($mac, $message, $secretKey->get());
 }
Пример #29
0
 /**
  * Get the EncryptionPublicKey used for encrypting password guesses
  * to give admins insight into the type of attack being launched.
  *
  * @param string $publicKey    Hex-encoded public key
  * @return EncryptionPublicKey
  * @throws SecurityAlert
  */
 public function getLogPublicKey(string $publicKey = '') : EncryptionPublicKey
 {
     if (!$publicKey) {
         $publicKey = $this->config['log-public-key'] ?? null;
         if (!$publicKey) {
             throw new SecurityAlert(\__('Encryption public key not configured.'));
         }
     }
     return new EncryptionPublicKey(\Sodium\hex2bin($publicKey));
 }