Exemplo n.º 1
0
 /**
  * Get the root hash of this Merkle tree.
  *
  * @param bool $raw - Do we want a raw string instead of a hex string?
  * 
  * @return string
  */
 public function getRoot(bool $raw = false) : string
 {
     if (!$this->rootCalculated) {
         $this->root = $this->calculateRoot();
     }
     return $raw ? $this->root : \Sodium\bin2hex($this->root);
 }
Exemplo n.º 2
0
 public function getHash(bool $raw = false) : string
 {
     if ($raw) {
         return $this->lastHash;
     }
     return \Sodium\bin2hex($this->lastHash);
 }
Exemplo n.º 3
0
 /**
  * Calculate the Merkle root, taking care to distinguish between
  * leaves and branches (0x01 for the nodes, 0x00 for the branches)
  * to protect against second-preimage attacks
  *
  * @return string
  */
 protected function calculateRoot() : string
 {
     $size = \count($this->nodes);
     $order = self::getSizeRoundedUp($size);
     $hash = [];
     $debug = [];
     // Population (Use self::MERKLE_LEAF as a prefix)
     for ($i = 0; $i < $order; ++$i) {
         if ($i >= $size) {
             $hash[$i] = self::MERKLE_LEAF . $this->nodes[$size - 1]->getHash(true);
             $debug[$i] = \Sodium\bin2hex($hash[$i]);
         } else {
             $hash[$i] = self::MERKLE_LEAF . $this->nodes[$i]->getHash(true);
             $debug[$i] = \Sodium\bin2hex($hash[$i]);
         }
     }
     // Calculation (Use self::MERKLE_BRANCH as a prefix)
     do {
         $tmp = [];
         $j = 0;
         for ($i = 0; $i < $order; $i += 2) {
             if (empty($hash[$i + 1])) {
                 $tmp[$j] = \Sodium\crypto_generichash(self::MERKLE_BRANCH . $hash[$i] . $hash[$i]);
             } else {
                 $tmp[$j] = \Sodium\crypto_generichash(self::MERKLE_BRANCH . $hash[$i] . $hash[$i + 1]);
             }
             ++$j;
         }
         $hash = $tmp;
         $order >>= 1;
     } while ($order > 1);
     // We should only have one value left:t
     return \array_shift($hash);
 }
Exemplo n.º 4
0
 /**
  * Fetch the provider key from verifyne server.
  * Verifies the key if libsodium is available.
  * After successful return the key can be read from the global class variables PROVIDER_KEY and PROVIDER_KEY_HEX.
  *
  * @return Returns TRUE if successful, or a WP_Error instance otherwise.
  */
 static function get_provider_key()
 {
     if (NULL !== self::$PROVIDER_KEY) {
         return TRUE;
     }
     if (!extension_loaded('libsodium')) {
         return new WP_Error("verifyne", "libsodium not available");
     }
     # Decode CA key
     self::$VERIFYNE_CA_KEY = base64_decode("i7dbWvlFdHwviUXav7N1Lwoi+DOJDG9SuFHNwP/AUjU=", true);
     if (FALSE === self::$VERIFYNE_CA_KEY) {
         return new WP_Error("verifyne", "Wrong CA key");
     }
     # Invoke API
     $cont = @file_get_contents("https://api.verifyne.me/v1/provider-key");
     if (FALSE === $cont) {
         return new WP_Error("verifyne", "Failed to query verifyne API");
     }
     # Read JSON
     $resp = @json_decode($cont);
     if (NULL === $resp) {
         return new WP_Error("verifyne", "Invalid JSON received");
     }
     # Decode signature
     $sig = base64_decode($resp->content->psig, $strict = true);
     if (FALSE === $sig) {
         return new WP_Error("verifyne", "Decoding signature failed");
     }
     $pkey_b64 = $resp->content->pkey;
     # Verify signature
     if (TRUE !== @\Sodium\crypto_sign_verify_detached($sig, $pkey_b64, self::$VERIFYNE_CA_KEY)) {
         return new WP_Error("verifyne", "Failed to verify provider key");
     }
     # Decode key (dec == 'ed25519:xxxxxxx...')
     $dec = base64_decode($pkey_b64, $strict = true);
     if (FALSE === $dec) {
         return new WP_Error("verifyne", "Decoding provider key description failed");
     }
     # Decode raw provider key
     self::$PROVIDER_KEY = base64_decode(explode(":", $dec, 2)[1], $strict = true);
     if (FALSE === self::$PROVIDER_KEY) {
         return new WP_Error("verifyne", "Decoding provider key");
     }
     self::$PROVIDER_KEY_HEX = @\Sodium\bin2hex(self::$PROVIDER_KEY);
     self::$VERIFYNE_CA_KEY_HEX = @\Sodium\bin2hex(self::$VERIFYNE_CA_KEY);
     return TRUE;
 }
Exemplo n.º 5
0
 /**
  * Encrypt a message using the Halite encryption protocol
  * (Encrypt then MAC -- Xsalsa20 then HMAC-SHA-512/256)
  * 
  * @param string $plaintext
  * @param EncryptionKey $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt(string $plaintext, EncryptionKey $secretKey, bool $raw = false) : string
 {
     $config = SymmetricConfig::getConfig(Halite::HALITE_VERSION, 'encrypt');
     // Generate a nonce and HKDF salt:
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf($config->HKDF_SALT_LEN);
     // Split our keys according to the HKDF salt:
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt, $config);
     // Encrypt our message with the encryption key:
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     \Sodium\memzero($eKey);
     // Calculate an authentication tag:
     $auth = self::calculateMAC(Halite::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
Exemplo n.º 6
0
 /**
  * Sign a message with our private key
  * 
  * @param string $message Message to sign
  * @param Contract\CryptoKeyInterface $privatekey
  * @param boolean $raw Don't hex encode the output?
  * 
  * @return string Signature (detached)
  */
 public static function sign($message, Contract\CryptoKeyInterface $privatekey, $raw = false)
 {
     if (!$privatekey->isSigningKey()) {
         throw new CryptoAlert\InvalidKey('Expected a signing key');
     }
     if (!$privatekey->isSecretKey()) {
         throw new CryptoAlert\InvalidKey('Expected a secret key');
     }
     $signed = \Sodium\crypto_sign_detached($message, $privatekey->get());
     if ($raw) {
         return $signed;
     }
     return \Sodium\bin2hex($signed);
 }
Exemplo n.º 7
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;
 }
Exemplo n.º 8
0
 /**
  * Calculate the BLAKE2b checksum of an entire stream
  *
  * @param StreamInterface $fileStream
  * @param Key $key
  * @param bool $raw
  * @return string
  * @throws CryptoException\InvalidKey
  */
 protected static function checksumData(StreamInterface $fileStream, Key $key = null, bool $raw = false) : string
 {
     $config = self::getConfig(Halite::HALITE_VERSION_FILE, 'checksum');
     if ($key instanceof AuthenticationKey) {
         $state = \Sodium\crypto_generichash_init($key->getRawKeyMaterial(), $config->HASH_LEN);
     } elseif ($config->CHECKSUM_PUBKEY && $key instanceof SignaturePublicKey) {
         // In version 2, we use the public key as a hash key
         $state = \Sodium\crypto_generichash_init($key->getRawKeyMaterial(), $config->HASH_LEN);
     } elseif (isset($key)) {
         throw new CryptoException\InvalidKey('Argument 2: Expected an instance of AuthenticationKey');
     } else {
         $state = \Sodium\crypto_generichash_init('', $config->HASH_LEN);
     }
     $size = $fileStream->getSize();
     while ($fileStream->remainingBytes() > 0) {
         // Don't go past the file size even if $config->BUFFER is not an even multiple of it:
         if ($fileStream->getPos() + $config->BUFFER > $size) {
             $amount_to_read = $size - $fileStream->getPos();
         } else {
             $amount_to_read = $config->BUFFER;
         }
         $read = $fileStream->readBytes($amount_to_read);
         \Sodium\crypto_generichash_update($state, $read);
     }
     // Do we want a raw checksum?
     if ($raw) {
         return \Sodium\crypto_generichash_final($state, $config->HASH_LEN);
     }
     return \Sodium\bin2hex(\Sodium\crypto_generichash_final($state, $config->HASH_LEN));
 }
Exemplo n.º 9
0
 /**
  * Sign a message with our private key
  * 
  * @param string $message Message to sign
  * @param SignatureSecretKey $privateKey
  * @param boolean $raw Don't hex encode the output?
  * @return string Signature (detached)
  */
 public static function sign(string $message, SignatureSecretKey $privateKey, bool $raw = false) : string
 {
     $signed = \Sodium\crypto_sign_detached($message, $privateKey->getRawKeyMaterial());
     if ($raw) {
         return $signed;
     }
     return \Sodium\bin2hex($signed);
 }
Exemplo n.º 10
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;
 }
Exemplo n.º 11
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;
 }
Exemplo n.º 12
0
 /**
  * Save a key to a file
  * 
  * @param string $filePath
  * @param string $keyData
  * @return int|bool
  */
 protected static function saveKeyFile($filePath, $keyData)
 {
     return \file_put_contents($filePath, \Sodium\bin2hex(Halite::HALITE_VERSION_KEYS . $keyData . \Sodium\crypto_generichash(Halite::HALITE_VERSION_KEYS . $keyData, null, \Sodium\CRYPTO_GENERICHASH_BYTES_MAX)));
 }
Exemplo n.º 13
0
/**
 * Return the user's logout token. This is to prevent logout via CSRF.
 *
 * @return string
 */
function logout_token() : string
{
    if (\array_key_exists('logout_token', $_SESSION)) {
        return $_SESSION['logout_token'];
    }
    $_SESSION['logout_token'] = \Sodium\bin2hex(\random_bytes(16));
    return $_SESSION['logout_token'];
}
Exemplo n.º 14
0
 /**
  * Wrapper around \Sodium\crypto_generichash()
  *
  * Expects a key (binary string).
  * Returns hexadecimal characters.
  *
  * @param string $input
  * @param string $key
  * @param int $length
  * @return string
  */
 public static function keyed_hash(string $input, string $key, int $length = \Sodium\CRYPTO_GENERICHASH_BYTES) : string
 {
     return \Sodium\bin2hex(self::raw_keyed_hash($input, $key, $length));
 }
Exemplo n.º 15
0
 /**
  * Execute the keygen command
  *
  * @param array $args - CLI arguments
  * @echo
  * @return null
  * @throws \Error
  */
 public function fire(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 generate a key.', "\n";
         echo 'Run this command: ', $this->c['yellow'], 'barge login', $this->c[''], "\n";
         exit(255);
     }
     if (\count($this->config['suppliers'][$supplier]['signing_keys']) === 0) {
         // Your first key is a master key; always.
         $has_master = false;
         $key_type = 'master';
     } else {
         $has_master = true;
         echo 'Please enter the key type you would like to generate (master, signing).', "\n";
         do {
             $key_type = $this->prompt('Key type: ');
             switch ($key_type) {
                 case 'm':
                 case 'main':
                 case 'master':
                 case 'primary':
                     $key_type = 'master';
                     break;
                 case 's':
                 case 'secondary':
                 case 'sub':
                 case 'subkey':
                 case 'signing':
                     $key_type = 'signing';
                     break;
                 default:
                     echo 'Acceptable key types: master, signing', "\n";
                     $key_type = null;
             }
         } while (empty($key_type));
     }
     // Each key gets its own unique Argon2 salt
     echo 'Generating a unique salt...', "\n";
     $salt = \random_bytes(\Sodium\CRYPTO_PWHASH_SALTBYTES);
     $store_in_cloud = null;
     // This is optional and not recommended, but some people prefer convenience.
     // We really hope this is adequate information to make an informed choice
     // based on personal risk tolerance:
     echo 'Do you wish to store the salt for generating your signing key in the Skyport?', "\n";
     echo 'This is a security-convenience trade-off. The default is NO.', "\n\n";
     echo $this->c['green'], 'Pro:', $this->c[''], ' It\'s there if you need it, and the salt alone is not enough for us to', "\n", '     reproduce your signing key.', "\n";
     echo $this->c['red'], 'Con:', $this->c[''], ' If your salt is stored online, the security of your signing key depends', "\n", '     entirely on your password.', "\n\n";
     // Iterate until we get a valid response
     while ($store_in_cloud === null) {
         $choice = $this->prompt('Store salt in the Skyport? (y/N): ');
         switch ($choice) {
             case 'YES':
             case 'yes':
             case 'Y':
             case 'y':
                 $store_in_cloud = true;
                 break;
             case 'N':
             case 'NO':
             case 'n':
             case 'no':
             case '':
                 // Just pressing enter means "don't store it"!
                 $store_in_cloud = false;
                 break;
             default:
                 echo "\n", $this->c['yellow'], 'Invalid response. Please enter yes or no.', $this->c[''], "\n";
         }
     }
     $zxcvbn = new Zxcvbn();
     $userInput = $this->getZxcvbnKeywords($supplier);
     // If we're storing in the cloud, our standards should be much higher.
     $min_score = $store_in_cloud ? 3 : 2;
     do {
         // Next, let's get a password.
         echo 'Please enter a strong passphrase to use for your signing key.', "\n";
         $password = $this->silentPrompt("Passphrase:");
         $password2 = $this->silentPrompt("Confirm passphrase:");
         if (!\hash_equals($password, $password2)) {
             unset($password);
             echo $this->c['red'], 'Passwords did not match!', $this->c[''], "\n";
             continue;
         }
         // Use zxcvbn to assess password strength
         $strength = $zxcvbn->passwordStrength($password, $userInput);
         if ($strength['score'] < $min_score) {
             echo $this->c['yellow'], 'Sorry, that password is not strong enough. Try making ', 'your password longer and use a wider variety of characters.', $this->c[''], "\n";
             $password = false;
         }
     } while (empty($password));
     echo 'Generating signing key...';
     if ($key_type === 'master') {
         // Master keys are treated as sensitive.
         $sign_level = KeyFactory::SENSITIVE;
     } else {
         // Signing keys (day-to-day) are still moderately sensitive.
         // We're using a KDF locally so we don't have DDoS concerns
         // (which usually calls for INTERACTIVE).
         $sign_level = KeyFactory::MODERATE;
     }
     $keyPair = KeyFactory::deriveSignatureKeyPair($password, $salt, false, $sign_level);
     $sign_public = $keyPair->getPublicKey();
     echo 'DONE!', "\n";
     // Wipe the password from memory
     \Sodium\memzero($password);
     // Store this in the configuration
     $new_key = ['date_generated' => \date('Y-m-d\\TH:i:s'), 'store_in_cloud' => $store_in_cloud, 'salt' => \Sodium\bin2hex($salt), 'public_key' => \Sodium\bin2hex($sign_public->getRawKeyMaterial()), 'type' => $key_type];
     // This is the message we are signing.
     $message = \json_encode(['action' => 'CREATE', 'date_generated' => $new_key['date_generated'], 'public_key' => $new_key['public_key'], 'supplier' => $supplier, 'type' => $new_key['type']]);
     if ($has_master) {
         list($masterSig, $masterPubKey) = $this->signNewKeyWithMasterKey($supplier, $message);
     } else {
         // This is our first key, so we don't need it.
         $masterSig = '';
         $masterPubKey = '';
     }
     // Save the configuration
     $this->config['suppliers'][$supplier]['signing_keys'][] = $new_key;
     // Send the public kay (and, maybe, the salt) to the Skyport.
     $response = $this->sendToSkyport($supplier, $new_key, $message, $masterSig, $masterPubKey);
     if (!empty($response['status'])) {
         if ($response['status'] === 'ERROR') {
             echo "Error message returned!\n";
             throw new \Error($response['message']);
         }
         $pk = Base64UrlSafe::encode(\Sodium\hex2bin($new_key['public_key']));
         if ($new_key['type'] === 'master') {
             echo 'New master key: ', $this->c['red'], $pk, $this->c[''], "\n";
         } else {
             echo 'New signing key: ', $this->c['yellow'], $pk, $this->c[''], "\n";
         }
     }
 }
Exemplo n.º 16
0
 /**
  * This method stores the necessary bits of data in this object.
  *
  * @param Channel $chan
  * @param array $updateData
  * @return void
  * @throws CouldNotUpdate
  * @throws NoSupplier
  */
 protected function unpackMessageUpdate(Channel $chan, array $updateData)
 {
     // This is the JSON message from the tree node, stored as an array:
     $this->updateMessage = $updateData;
     if ($this->isPackageUpdate() || $this->isAirshipUpdate()) {
         // These aren't signed for updating the tree.
         return;
     }
     // We need a precise format:
     $dateGen = (new \DateTime($this->stored['date_generated']))->format(\AIRSHIP_DATE_FORMAT);
     $messageToSign = ['action' => $this->action, 'date_generated' => $dateGen, 'public_key' => $updateData['public_key'], 'supplier' => $updateData['supplier'], 'type' => $updateData['type']];
     try {
         $this->supplier = $this->loadSupplier($chan, $updateData);
     } catch (NoSupplier $ex) {
         if (!$this->isNewSupplier) {
             throw $ex;
         }
     }
     // If this isn't a new supplier, we need to verify the key
     if ($this->isNewSupplier) {
         return;
     }
     if ($updateData['master'] === null) {
         throw new CouldNotUpdate(\__('The master data is NULL, but the supplier exists.'));
     }
     $master = \json_decode($updateData['master'], true);
     foreach ($this->supplier->getSigningKeys() as $supKey) {
         // Yes, this is (in fact) a SignaturePublicKey:
         if (IDE_HACKS) {
             $supKey['key'] = new SignaturePublicKey();
         }
         if ($supKey['type'] !== 'master') {
             continue;
         }
         $pub = \Sodium\bin2hex($supKey['key']->getRawKeyMaterial());
         // Is this the key we're looking for?
         if (\hash_equals($pub, $master['public_key'])) {
             // Store the public key
             $this->supplierMasterKeyUsed = $supKey['key'];
             break;
         }
     }
     if (empty($this->supplierMasterKeyUsed)) {
         throw new CouldNotUpdate(\__('The provided public key does not match any known master key.'));
     }
     $encoded = \json_encode($messageToSign);
     if (!Asymmetric::verify($encoded, $this->supplierMasterKeyUsed, $master['signature'])) {
         throw new CouldNotUpdate(\__('Invalid signature for this master key.'));
     }
 }
Exemplo n.º 17
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'};
 }
Exemplo n.º 18
0
 /**
  * Calculate a BLAHE2b checksum of a file
  * 
  * @param string $fileHandle The file you'd like to checksum
  * @param string $key An optional BLAKE2b key
  * @param bool $raw Set to true if you don't want hex
  * 
  * @return string
  */
 public static function checksumResource($fileHandle, \ParagonIE\Halite\Contract\CryptoKeyInterface $key = null, $raw = false)
 {
     // Input validation
     if (!\is_resource($fileHandle)) {
         throw new \ParagonIE\Halite\Alerts\InvalidType('Expected input handle to be a resource');
     }
     $config = self::getConfig(Halite::HALITE_VERSION, 'checksum');
     if ($key) {
         $state = \Sodium\crypto_generichash_init($key->get(), $config['HASH_LEN']);
     } else {
         $state = \Sodium\crypto_generichash_init(null, $config['HASH_LEN']);
     }
     while (!\feof($fileHandle)) {
         $read = \fread($fileHandle, $config['BUFFER']);
         if ($read === false) {
             throw new CryptoException\FileAccessDenied('Could not read from the file');
         }
         \Sodium\crypto_generichash_update($state, $read);
     }
     if ($raw) {
         return \Sodium\crypto_generichash_final($state, $config['HASH_LEN']);
     }
     return \Sodium\bin2hex(\Sodium\crypto_generichash_final($state, $config['HASH_LEN']));
 }
Exemplo n.º 19
0
 /**
  * Sign a message with our private key
  * 
  * @param string $message Message to sign
  * @param SignatureSecretKey $privateKey
  * @param boolean $raw Don't hex encode the output?
  * 
  * @return string Signature (detached)
  * 
  * @throws CryptoException\InvalidKey
  */
 public static function sign($message, Contract\KeyInterface $privateKey, $raw = false)
 {
     if (!$privateKey instanceof SignatureSecretKey) {
         throw new CryptoException\InvalidKey('Argument 2: Expected an instance of SignatureSecretKey');
     }
     $signed = \Sodium\crypto_sign_detached($message, $privateKey->get());
     if ($raw) {
         return $signed;
     }
     return \Sodium\bin2hex($signed);
 }
Exemplo n.º 20
0
 /**
  * Converts binary data to hexadecimal data.
  *
  * @param string $string String containing binary data to convert to hexadecimal.
  * @return string
  */
 public static function bin2hex($string)
 {
     return \Sodium\bin2hex($string);
 }
Exemplo n.º 21
0
 /**
  * Save a key to a file
  * 
  * @param string $filePath
  * @param string $keyData
  * @return int|bool
  */
 protected static function saveKeyFile(string $filePath, string $keyData) : bool
 {
     return false !== \file_put_contents($filePath, \Sodium\bin2hex(Halite::HALITE_VERSION_KEYS . $keyData . \Sodium\crypto_generichash(Halite::HALITE_VERSION_KEYS . $keyData, '', \Sodium\CRYPTO_GENERICHASH_BYTES_MAX)));
 }
Exemplo n.º 22
0
 /**
  * This method is private to avoid it from being accessed outside of the
  * trusted methods (which handle validation). Don't change it.
  *
  * @param string $tmp_name
  * @return string "HH/HH/HHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHHH"
  * @throws UploadError
  */
 private function moveUploadedFile(string $tmp_name) : string
 {
     $dir1 = \Sodium\bin2hex(\random_bytes(1));
     $dir2 = \Sodium\bin2hex(\random_bytes(1));
     $base = AIRSHIP_UPLOADS . $dir1 . DIRECTORY_SEPARATOR . $dir2;
     if (!\file_exists($base)) {
         \mkdir($base, 0775, true);
     }
     do {
         $filename = \Sodium\bin2hex(\random_bytes(22));
     } while (\file_exists($base . DIRECTORY_SEPARATOR . $filename));
     if (!\move_uploaded_file($tmp_name, $base . DIRECTORY_SEPARATOR . $filename)) {
         throw new UploadError(\__('Could not move temporary file to its permanent home'));
     }
     return $dir1 . DIRECTORY_SEPARATOR . $dir2 . DIRECTORY_SEPARATOR . $filename;
 }
Exemplo n.º 23
0
 function crLoadEncKeys()
 {
     $this->webLog("Loading encryption keys.", __METHOD__);
     //  if the user is not logged in we will only load the anonymous key
     if ($this->config->profile->group === 'public') {
         $this->config->crCryptoKeys = (object) array('anonymous' => false, 'cookie' => false);
     }
     //  some internal pages allow unauthenticated users to run admin tools and only needs the system key
     if ($this->config->profile->internal === true) {
         $this->config->crCryptoKeys = (object) array('system' => false);
     }
     //  loop through each expected key
     foreach ($this->config->crCryptoKeys as $keyName => $value) {
         //  check if the key is loaded already or not
         if ($value === false) {
             $this->webLog("Found unloaded key with name '{$keyName}'", __METHOD__);
             //  check whether or not a key exists in the db
             $this->config->crCryptoKeys->{$keyName} = (object) array('loaded' => false, 'keyid' => false, 'eckey' => false);
             $userid = 'systemwide';
             if ($query = $this->mysql_query("SELECT `keyid`, `key` FROM `keys` WHERE `userid`='{$userid}' AND `name`='{$keyName}' ORDER BY `id` DESC LIMIT 1")) {
                 $result = $this->mysql_returnResource($query);
                 $data = $result->fetch_object();
                 if ($result->num_rows === 1) {
                     //  key is found, load it up
                     $this->webLog("Located existing key for '{$keyName}' with keyid '{$data->keyid}'", __METHOD__, 'info');
                     $this->config->crCryptoKeys->{$keyName}->keyid = $data->keyid;
                     $this->config->crCryptoKeys->{$keyName}->eckey = $data->key;
                     $this->config->crCryptoKeys->{$keyName}->loaded = true;
                 }
             }
             //  check if no key was found
             if ($this->config->crCryptoKeys->{$keyName}->loaded === false) {
                 $this->webLog("No key was located! Generating a new one.", __METHOD__);
                 $this->config->crCryptoKeys->{$keyName}->keyid = $this->crGenerateKeyId($userid, $keyName);
                 $this->config->crCryptoKeys->{$keyName}->eckey = \Sodium\bin2hex($this->crCryptoGenerateKey());
                 if ($this->mysql_query("INSERT INTO `keys` (`timestamp`, `userid`, `keyid`, `name`, `key`) VALUES \n\t\t\t\t\t\t\t(\n\t\t\t\t\t\t\t\t'" . time() . "',\n\t\t\t\t\t\t\t\t'{$userid}',\n\t\t\t\t\t\t\t\t'{$this->config->crCryptoKeys->{$keyName}->keyid}',\n\t\t\t\t\t\t\t\t'{$keyName}',\n\t\t\t\t\t\t\t\t'{$this->config->crCryptoKeys->{$keyName}->eckey}'\n\t\t\t\t\t\t\t)\n\t\t\t\t\t\t")) {
                     $this->webLog("Created new encryption key with keyid '{$this->config->crCryptoKeys->{$keyName}->keyid}'", __METHOD__);
                 } else {
                     $this->webLog("Failed to create new key with keyid '{$this->config->crCryptoKeys->{$keyName}->keyid}'", __METHOD__, "fatal");
                     $this->shutdown($this->crLanguage('generic', 'runtimeError'));
                 }
             }
             //  remove our tracking key
             unset($this->config->crCryptoKeys->{$keyName}->loaded);
         } else {
             //  if it's not a string then we have a problem
             if (!is_object($value)) {
                 $this->webLog($this->crLanguage('generic', 'invalidKey', array("config::crCryptoKeys::{$keyName}", "false or object")), __METHOD__, 'fatal');
                 $this->shutdown($this->crLanguage('generic', 'runtimeError'));
             } else {
                 $this->webLog("Doesn't actually yet support providing the keys via configuration; all DB managed!", __METHOD__, "fatal");
                 $this->shutdown($this->crLanguage('generic', 'runtimeError'));
             }
         }
     }
     //  pass the keys along to crCrypto
     $this->crCryptoSetup($this->config->crCryptoKeys);
 }
Exemplo n.º 24
0
 /**
  * Encrypt a message using the Halite encryption protocol
  * 
  * @param string $plaintext
  * @param EncryptionKey $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt($plaintext, Contract\KeyInterface $secretKey, $raw = false)
 {
     if (!$secretKey instanceof EncryptionKey) {
         throw new CryptoException\InvalidKey('Expected an instance of EncryptionKey');
     }
     $config = SymmetricConfig::getConfig(Halite::HALITE_VERSION, 'encrypt');
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf($config->HKDF_SALT_LEN);
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt, $config);
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     $auth = self::calculateMAC(Halite::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($eKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Halite::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
Exemplo n.º 25
0
 /**
  * 
  * @param \ParagonIE\Halite\Contract\StreamInterface $fileStream
  * @param AuthenticationKey $key
  * @param type $raw
  * @return type
  */
 public static function checksumStream(StreamInterface $fileStream, KeyInterface $key = null, $raw = false)
 {
     $config = self::getConfig(Halite::HALITE_VERSION_FILE, 'checksum');
     if ($key) {
         if (!$key instanceof AuthenticationKey) {
             throw new \ParagonIE\Halite\Alerts\InvalidKey('Argument 2: Expected an instance of AuthenticationKey');
         }
         $state = \Sodium\crypto_generichash_init($key->get(), $config->HASH_LEN);
     } else {
         $state = \Sodium\crypto_generichash_init(null, $config->HASH_LEN);
     }
     $size = $fileStream->getSize();
     while ($fileStream->remainingBytes() > 0) {
         $read = $fileStream->readBytes($fileStream->getPos() + $config->BUFFER > $size ? $size - $fileStream->getPos() : $config->BUFFER);
         \Sodium\crypto_generichash_update($state, $read);
     }
     if ($raw) {
         return \Sodium\crypto_generichash_final($state, $config->HASH_LEN);
     }
     return \Sodium\bin2hex(\Sodium\crypto_generichash_final($state, $config->HASH_LEN));
 }
Exemplo n.º 26
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;
 }
Exemplo n.º 27
0
 /**
  * Converts a binary string to an hexdecimal string.
  *
  * This is the same as PHP's bin2hex() implementation, but it is resistant to
  * timing attacks.
  *
  * @link https://paragonie.com/book/pecl-libsodium/read/03-utilities-helpers.md#bin2hex
  * @param  string $binaryString The binary string to convert
  * @return string
  */
 public function bin2hex($binaryString)
 {
     /** @noinspection PhpUndefinedNamespaceInspection @noinspection PhpUndefinedFunctionInspection */
     return \Sodium\bin2hex($binaryString);
 }
Exemplo n.º 28
0
 /**
  * Encrypt a message using the Halite encryption protocol
  * 
  * @param string $plaintext
  * @param Key $secretKey
  * @param boolean $raw Don't hex encode the output?
  * @return string
  */
 public static function encrypt($plaintext, Contract\CryptoKeyInterface $secretKey, $raw = false)
 {
     if ($secretKey->isAsymmetricKey()) {
         throw new CryptoAlert\InvalidKey('Expected a symmetric key, not an asymmetric key');
     }
     if (!$secretKey->isEncryptionKey()) {
         throw new CryptoAlert\InvalidKey('Encryption key expected');
     }
     $nonce = \Sodium\randombytes_buf(\Sodium\CRYPTO_SECRETBOX_NONCEBYTES);
     $salt = \Sodium\randombytes_buf(Config::HKDF_SALT_LEN);
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt);
     $xored = \Sodium\crypto_stream_xor($plaintext, $nonce, $eKey);
     $auth = self::calculateMAC(Config::HALITE_VERSION . $salt . $nonce . $xored, $aKey);
     \Sodium\memzero($eKey);
     \Sodium\memzero($aKey);
     if (!$raw) {
         return \Sodium\bin2hex(Config::HALITE_VERSION . $salt . $nonce . $xored . $auth);
     }
     return Config::HALITE_VERSION . $salt . $nonce . $xored . $auth;
 }
Exemplo n.º 29
0
 /**
  * Get a relative BLAKE2b hash of an input. Formatted as two lookup
  * directories followed by a cache entry. 'hh/hh/hhhhhhhh...'
  *
  * @param string $preHash The cache identifier (will be hashed)
  * @param bool $asString Return a string?
  * @return string|array
  * @throws InvalidType
  */
 public static function getRelativeHash(string $preHash, bool $asString = false)
 {
     $state = State::instance();
     $cacheKey = $state->keyring['cache.hash_key'];
     if (!$cacheKey instanceof Key) {
         throw new InvalidType(\trk('errors.type.wrong_class', '\\ParagonIE\\Halite\\Key'));
     }
     // We use a keyed hash, with a distinct key per Airship deployment to
     // make collisions unlikely,
     $hash = \Sodium\crypto_generichash($preHash, $cacheKey->getRawKeyMaterial(), self::HASH_SIZE);
     $relHash = [\Sodium\bin2hex($hash[0]), \Sodium\bin2hex($hash[1]), \Sodium\bin2hex(Util::subString($hash, 2))];
     if ($asString) {
         return \implode(DIRECTORY_SEPARATOR, $relHash);
     }
     return $relHash;
 }