PHP Classes

File: src/Util.php

Recommend this page to a friend!
  Classes of Scott Arciszewski   Halite   src/Util.php   Download  
File: src/Util.php
Role: Class source
Content type: text/plain
Description: Class source
Class: Halite
Perform cryptography operations with libsodium
Author: By
Last change: For version 2, let's use strict types!
Date: 8 years ago
Size: 4,940 bytes
 

Contents

Class file image Download
<?php
declare(strict_types=1);
namespace
ParagonIE\Halite;

abstract class
Util
{
   
/**
     * Use a derivative of HKDF to derive multiple keys from one.
     * http://tools.ietf.org/html/rfc5869
     *
     * This is a variant from hash_hkdf() and instead uses BLAKE2b provided by
     * libsodium.
     *
     * Important: instead of a true HKDF (from HMAC) construct, this uses the
     * \Sodium\crypto_generichash() key parameter. This is *probably* okay.
     *
     * @param string $ikm Initial Keying Material
     * @param int $length How many bytes?
     * @param string $info What sort of key are we deriving?
     * @param string $salt
     * @return string
     * @throws \ParagonIE\Halite\Alerts\InvalidDigestLength
     * @throws \ParagonIE\Halite\Alerts\CannotPerformOperation
     */
   
public static function hkdfBlake2b(
       
string $ikm,
       
int $length,
       
string $info = '',
       
string $salt = ''
   
): string {
       
// Sanity-check the desired output length.
       
if (empty($length)
            || !\
is_int($length)
            ||
$length < 0
           
|| $length > 255 * \Sodium\CRYPTO_GENERICHASH_KEYBYTES
       
) {
            throw new \
ParagonIE\Halite\Alerts\InvalidDigestLength(
               
'Bad HKDF Digest Length'
           
);
        }
       
// "If [salt] not provided, is set to a string of HashLen zeroes."
       
if (empty($salt)) {
           
$salt = \str_repeat("\x00", \Sodium\CRYPTO_GENERICHASH_KEYBYTES);
        }

       
// HKDF-Extract:
        // PRK = HMAC-Hash(salt, IKM)
        // The salt is the HMAC key.
       
$prk = \Sodium\crypto_generichash($ikm, $salt);

       
// HKDF-Expand:
        // This check is useless, but it serves as a reminder to the spec.
       
if (self::safeStrlen($prk) < \Sodium\CRYPTO_GENERICHASH_KEYBYTES) {
            throw new \
ParagonIE\Halite\Alerts\CannotPerformOperation(
               
'An unknown error has occurred'
           
);
        }
       
// T(0) = ''
       
$t = '';
       
$last_block = '';
        for (
$block_index = 1; self::safeStrlen($t) < $length; ++$block_index) {
           
// T(i) = HMAC-Hash(PRK, T(i-1) | info | 0x??)
           
$last_block = \Sodium\crypto_generichash(
               
$last_block . $info . \chr($block_index),
               
$prk
           
);
           
// T = T(1) | T(2) | T(3) | ... | T(N)
           
$t .= $last_block;
        }
       
// ORM = first L octets of T
       
$orm = self::safeSubstr($t, 0, $length);
        if (
$orm === false) {
            throw new \
ParagonIE\Halite\Alerts\CannotPerformOperation(
               
'An unknown error has occurred'
           
);
        }
        return
$orm;
    }
   
   
/**
     * Safe string length
     *
     * @ref mbstring.func_overload
     *
     * @staticvar boolean $exists
     * @param string $str
     * @return int
     * @throws \ParagonIE\Halite\Alerts\CannotPerformOperation
     */
   
public static function safeStrlen($str)
    {
        static
$exists = null;
        if (
$exists === null) {
           
$exists = \function_exists('mb_strlen');
        }
        if (!\
is_string($str)) {
            throw new
Alerts\InvalidType(
               
'A string was expected.'
           
);
        }
        if (
$exists) {
           
$length = \mb_strlen($str, '8bit');
            if (
$length === false) {
                throw new
Alerts\CannotPerformOperation(
                   
'mb_strlen() failed unexpectedly'
               
);
            }
        } else {
           
// If we reached here, we can rely on strlen to count bytes:
           
$length = \strlen($str);
            if (
$length === false) {
                throw new
Alerts\CannotPerformOperation(
                   
'strlen() failed unexpectedly'
               
);
            }
        }
        return
$length;
    }
   
   
/**
     * Safe substring
     *
     * @staticvar boolean $exists
     * @param string $str
     * @param int $start
     * @param int $length
     * @return string
     */
   
public static function safeSubstr($str, $start, $length = null)
    {
        static
$exists = null;
        if (
$exists === null) {
           
$exists = \function_exists('mb_substr');
        }
        if (
$exists) {
           
// mb_substr($str, 0, NULL, '8bit') returns an empty string on PHP
            // 5.3, so we have to find the length ourselves.
           
if ($length === null) {
                if (
$start >= 0) {
                   
$length = self::safeStrlen($str) - $start;
                } else {
                   
$length = -$start;
                }
            }
            return \
mb_substr($str, $start, $length, '8bit');
        }
       
// Unlike mb_substr(), substr() doesn't accept NULL for length
       
if ($length !== null) {
            return \
substr($str, $start, $length);
        } else {
            return \
substr($str, $start);
        }
    }
}