safeStrlen() public static method

Safe string length
public static safeStrlen ( string $str ) : integer
$str string
return integer
 /**
  * @param string $keyMaterial - The actual key data
  * @param bool $signing - Is this a signing key?
  */
 public function __construct(string $keyMaterial = '', ...$args)
 {
     if (CryptoUtil::safeStrlen($keyMaterial) !== \Sodium\CRYPTO_BOX_PUBLICKEYBYTES) {
         throw new InvalidKey('Encryption public key must be CRYPTO_BOX_PUBLICKEYBYTES bytes long');
     }
     parent::__construct($keyMaterial, false);
 }
Example #2
0
 /**
  * Write to a stream; prevent partial writes
  * 
  * @param resource $stream
  * @param string $buf
  * @param int $num (number of bytes)
  * @throws FileAlert\AccessDenied
  */
 public function writeBytes($buf, $num = null)
 {
     $bufSize = Util::safeStrlen($buf);
     if ($num === null || $num > $bufSize) {
         $num = $bufSize;
     }
     if ($num < 0) {
         throw new \Exception('num < 0');
     }
     $remaining = $num;
     do {
         if ($remaining <= 0) {
             break;
         }
         $written = \fwrite($this->fp, $buf, $remaining);
         if ($written === false) {
             throw new CryptoException\FileAccessDenied('Could not write to the file');
         }
         $buf = Util::safeSubstr($buf, $written, null);
         $this->pos += $written;
         $this->stat = \fstat($this->fp);
         $remaining -= $written;
     } while ($remaining > 0);
     return $num;
 }
 /**
  * @param string $keyMaterial - The actual key data
  * @param bool $signing - Is this a signing key?
  */
 public function __construct(string $keyMaterial = '', ...$args)
 {
     if (CryptoUtil::safeStrlen($keyMaterial) !== \Sodium\CRYPTO_SIGN_SECRETKEYBYTES) {
         throw new InvalidKey('Signature secret key must be CRYPTO_SIGN_SECRETKEYBYTES bytes long');
     }
     parent::__construct($keyMaterial, true);
 }
 /**
  * @param string $keyMaterial - The actual key data
  */
 public function __construct(string $keyMaterial = '', ...$args)
 {
     if (CryptoUtil::safeStrlen($keyMaterial) !== \Sodium\CRYPTO_AUTH_KEYBYTES) {
         throw new InvalidKey('Authentication key must be CRYPTO_AUTH_KEYBYTES bytes long');
     }
     parent::__construct($keyMaterial, true);
 }
Example #5
0
 /**
  * @param HiddenString $keyMaterial - The actual key data
  * @throws InvalidKey
  */
 public function __construct(HiddenString $keyMaterial)
 {
     if (CryptoUtil::safeStrlen($keyMaterial->getString()) !== \Sodium\CRYPTO_BOX_SECRETKEYBYTES) {
         throw new InvalidKey('Encryption secret key must be CRYPTO_BOX_SECRETKEYBYTES bytes long');
     }
     parent::__construct($keyMaterial);
 }
Example #6
0
 /**
  * Read from a stream; prevent partial reads (also uses run-time testing to
  * prevent partial reads -- you can turn this off if you need performance
  * and aren't concerned about race condition attacks, but this isn't a
  * decision to make lightly!)
  * 
  * @param int $num
  * @param boolean $skipTests Only set this to TRUE if you're absolutely sure
  *                           that you don't want to defend against TOCTOU /
  *                           race condition attacks on the filesystem!
  * @return string
  * @throws FileAlert\AccessDenied
  */
 public function readBytes($num, $skipTests = false)
 {
     if ($num <= 0) {
         throw new \Exception('num < 0');
     }
     if ($this->pos + $num > $this->stat['size']) {
         throw new \Exception('Out-of-bounds read');
     }
     $buf = '';
     $remaining = $num;
     if (!$skipTests) {
         $this->toctouTest();
     }
     do {
         if ($remaining <= 0) {
             break;
         }
         $read = \fread($this->fp, $remaining);
         if ($read === false) {
             throw new CryptoException\FileAccessDenied('Could not read from the file');
         }
         $buf .= $read;
         $readSize = Util::safeStrlen($read);
         $this->pos += $readSize;
         $remaining -= $readSize;
     } while ($remaining > 0);
     return $buf;
 }
Example #7
0
 /**
  * @param HiddenString $keyMaterial - The actual key data
  * @throws InvalidKey
  */
 public function __construct(HiddenString $keyMaterial)
 {
     if (CryptoUtil::safeStrlen($keyMaterial->getString()) !== \Sodium\CRYPTO_AUTH_KEYBYTES) {
         throw new InvalidKey('Authentication key must be CRYPTO_AUTH_KEYBYTES bytes long');
     }
     parent::__construct($keyMaterial);
     $this->isSigningKey = true;
 }
Example #8
0
 /**
  * test safeStrLen() with illegal parameter. We expect to see an exception
  * @return void
  * @throws CannotPerformOperation
  */
 public function testSafeStrlen()
 {
     $this->setExpectedException('\\ParagonIE\\Halite\\Alerts\\HaliteAlert');
     $teststring = [];
     // is not a string, will provoke a warning
     //suppress php warning
     Util::safeStrlen($teststring);
 }
Example #9
0
 /**
  * @param HiddenString $keyMaterial - The actual key data
  * @throws InvalidKey
  */
 public function __construct(HiddenString $keyMaterial)
 {
     if (CryptoUtil::safeStrlen($keyMaterial->getString()) !== \Sodium\CRYPTO_SIGN_SECRETKEYBYTES) {
         throw new InvalidKey('Signature secret key must be CRYPTO_SIGN_SECRETKEYBYTES bytes long');
     }
     parent::__construct($keyMaterial);
     $this->isSigningKey = true;
 }
Example #10
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');
 }
Example #11
0
 /**
  * Decrypt a message using the Halite encryption protocol
  * 
  * @param string $ciphertext
  * @param Key $secretKey
  * @param boolean $raw Don't hex decode the input?
  */
 public static function decrypt($ciphertext, 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');
     }
     if (!$raw) {
         // We were given hex data:
         $ciphertext = \Sodium\hex2bin($ciphertext);
     }
     $length = CryptoUtil::safeStrlen($ciphertext);
     // The first 4 bytes are reserved for the version size
     $version = CryptoUtil::safeSubstr($ciphertext, 0, Config::VERSION_TAG_LEN);
     // The HKDF is used for key splitting
     $salt = CryptoUtil::safeSubstr($ciphertext, Config::VERSION_TAG_LEN, Config::HKDF_SALT_LEN);
     // This is the nonce (we authenticated it):
     $nonce = CryptoUtil::safeSubstr($ciphertext, Config::VERSION_TAG_LEN + Config::HKDF_SALT_LEN, \Sodium\CRYPTO_STREAM_NONCEBYTES);
     // This is the crypto_stream_xor()ed ciphertext
     $xored = CryptoUtil::safeSubstr($ciphertext, Config::VERSION_TAG_LEN + Config::HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES, $length - (Config::VERSION_TAG_LEN + Config::HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES + \Sodium\CRYPTO_AUTH_BYTES));
     // $auth is the last 32 bytes
     $auth = CryptoUtil::safeSubstr($ciphertext, $length - \Sodium\CRYPTO_AUTH_BYTES);
     // Split our keys
     list($eKey, $aKey) = self::splitKeys($secretKey, $salt);
     // Check the MAC first
     if (!self::verifyMAC($auth, $version . $salt . $nonce . $xored, $aKey)) {
         throw new CryptoAlert\InvalidMessage('Invalid message authenticaiton code');
     }
     // Down the road, do whatever logic around $version here, in case we
     // need to upgrade our protocol.
     // Add version logic above
     $plaintext = \Sodium\crypto_stream_xor($xored, $nonce, $eKey);
     if ($plaintext === false) {
         throw new CryptoAlert\InvalidMessage('Invalid message authenticaiton code');
     }
     return $plaintext;
 }
Example #12
0
 /**
  * Read from a stream; prevent partial reads
  * 
  * @param resource $stream
  * @param int $num
  * @throws FileAlert\AccessDenied
  */
 private static final function readBytes($stream, $num)
 {
     if ($num <= 0) {
         throw new \Exception('num < 0');
     }
     $buf = '';
     $remaining = $num;
     do {
         if ($remaining <= 0) {
             break;
         }
         $read = \fread($stream, $remaining);
         if ($read === false) {
             throw new FileAlert\AccessDenied('Could not read from the file');
         }
         $buf .= $read;
         $remaining = $num - CryptoUtil::safeStrlen($buf);
     } while ($remaining > 0);
     return $buf;
 }
Example #13
0
 /**
  * Decrypt then verify a password
  *
  * @param HiddenString $password    The user's password
  * @param string $stored            The encrypted password hash
  * @param EncryptionKey $secretKey  The master key for all passwords
  * @return bool                     Is this password valid?
  * @throws InvalidMessage
  */
 public static function verify(HiddenString $password, string $stored, EncryptionKey $secretKey) : bool
 {
     $config = self::getConfig($stored);
     // Base64-urlsafe encoded, so 4/3 the size of raw binary
     if (Util::safeStrlen($stored) < $config->SHORTEST_CIPHERTEXT_LENGTH * 4 / 3) {
         throw new InvalidMessage('Encrypted password hash is too short.');
     }
     // First let's decrypt the hash
     $hash_str = Crypto::decrypt($stored, $secretKey, $config->ENCODING);
     // Upon successful decryption, verify the password is correct
     return \Sodium\crypto_pwhash_str_verify($hash_str->getString(), $password->getString());
 }
Example #14
0
 /**
  * @covers Symmetric::unpackMessageForDecryption()
  */
 public function testUnpack()
 {
     $key = new EncryptionKey(new HiddenString(\str_repeat('A', 32)));
     // Randomly sized plaintext
     $size = \Sodium\randombytes_uniform(1023) + 1;
     $plaintext = \Sodium\randombytes_buf($size);
     $message = Symmetric::encrypt(new HiddenString($plaintext), $key, true);
     // Let's unpack our message
     $unpacked = Symmetric::unpackMessageForDecryption($message);
     // Now to test our expected results!
     $this->assertSame(Util::safeStrlen($unpacked[0]), Halite::VERSION_TAG_LEN);
     $this->assertTrue($unpacked[1] instanceof \ParagonIE\Halite\Symmetric\Config);
     $config = $unpacked[1];
     if ($config instanceof \ParagonIE\Halite\Symmetric\Config) {
         $this->assertSame(Util::safeStrlen($unpacked[2]), $config->HKDF_SALT_LEN);
         $this->assertSame(Util::safeStrlen($unpacked[3]), \Sodium\CRYPTO_STREAM_NONCEBYTES);
         $this->assertSame(Util::safeStrlen($unpacked[4]), Util::safeStrlen($message) - (Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES + $config->MAC_SIZE));
         $this->assertSame(Util::safeStrlen($unpacked[5]), $config->MAC_SIZE);
     } else {
         $this->fail('Cannot continue');
     }
 }
Example #15
0
 /**
  * Unpack a message string into an array.
  * 
  * @param string $ciphertext
  * @return array
  */
 public static function unpackMessageForDecryption($ciphertext)
 {
     $length = CryptoUtil::safeStrlen($ciphertext);
     // The first 4 bytes are reserved for the version size
     $version = CryptoUtil::safeSubstr($ciphertext, 0, Halite::VERSION_TAG_LEN);
     $config = SymmetricConfig::getConfig($version, 'encrypt');
     // The HKDF is used for key splitting
     $salt = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN, $config->HKDF_SALT_LEN);
     // This is the nonce (we authenticated it):
     $nonce = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN, \Sodium\CRYPTO_STREAM_NONCEBYTES);
     // This is the crypto_stream_xor()ed ciphertext
     $xored = CryptoUtil::safeSubstr($ciphertext, Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES, $length - (Halite::VERSION_TAG_LEN + $config->HKDF_SALT_LEN + \Sodium\CRYPTO_STREAM_NONCEBYTES + \Sodium\CRYPTO_AUTH_BYTES));
     // $auth is the last 32 bytes
     $auth = CryptoUtil::safeSubstr($ciphertext, $length - \Sodium\CRYPTO_AUTH_BYTES);
     // We don't need this anymore.
     \Sodium\memzero($ciphertext);
     return [$version, $config, $salt, $nonce, $xored, $auth];
 }
Example #16
0
 /**
  * Derive a key pair for public key signatures from a password and salt
  * 
  * @param HiddenString $password
  * @param string $salt
  * @param string $level Security level for KDF
  *
  * @return SignatureKeyPair
  * @throws CryptoException\InvalidSalt
  */
 public static function deriveSignatureKeyPair(HiddenString $password, string $salt, string $level = self::INTERACTIVE) : SignatureKeyPair
 {
     $kdfLimits = self::getSecurityLevels($level);
     // VERSION 2+ (argon2)
     if (Util::safeStrlen($salt) !== \Sodium\CRYPTO_PWHASH_SALTBYTES) {
         throw new CryptoException\InvalidSalt('Expected ' . \Sodium\CRYPTO_PWHASH_SALTBYTES . ' bytes, got ' . Util::safeStrlen($salt));
     }
     // Digital signature keypair
     $seed = \Sodium\crypto_pwhash(\Sodium\CRYPTO_SIGN_SEEDBYTES, $password->getString(), $salt, $kdfLimits[0], $kdfLimits[1]);
     $keyPair = \Sodium\crypto_sign_seed_keypair($seed);
     $secretKey = \Sodium\crypto_sign_secretkey($keyPair);
     // Let's wipe our $kp variable
     \Sodium\memzero($keyPair);
     return new SignatureKeyPair(new SignatureSecretKey(new HiddenString($secretKey)));
 }