public static function LoadFromAsciiSafeString($savedKeyString)
{
try {
$bytes = Encoding::hexToBin($savedKeyString);
} catch (\RangeException $ex) {
throw new Ex\CannotPerformOperationException("Key has invalid hex encoding.");
}
/* Make sure we have enough bytes to get the version header. */
if (Core::ourStrlen($bytes) < self::KEY_HEADER_SIZE) {
throw new Ex\CannotPerformOperationException("Saved Key is shorter than the version header.");
}
/* Grab the version header. */
$version_header = Core::ourSubstr($bytes, 0, self::KEY_HEADER_SIZE);
/* Grab the config for that version. */
$config = self::GetKeyVersionConfigFromKeyHeader($version_header);
/* Now that we know the version, check the length is correct. */
if (Core::ourStrlen($bytes) !== self::KEY_HEADER_SIZE + $config->keyByteSize() + $config->checksumByteSize()) {
throw new Ex\CannotPerformOperationException("Saved Key is not the correct size.");
}
/* Grab the bytes that are part of the checksum. */
$checked_bytes = Core::ourSubstr($bytes, 0, self::KEY_HEADER_SIZE + $config->keyByteSize());
/* Grab the included checksum. */
$checksum_a = Core::ourSubstr($bytes, self::KEY_HEADER_SIZE + $config->keyByteSize(), $config->checksumByteSize());
/* Re-compute the checksum. */
$checksum_b = hash($config->checksumHashFunction(), $checked_bytes, true);
/* Validate it. It *is* important for this to be constant time. */
if (!Core::hashEquals($checksum_a, $checksum_b)) {
throw new Ex\CannotPerformOperationException("Saved key is corrupted -- checksums don't match.");
}
/* Everything checks out. Grab the key and create a Key object. */
$key_bytes = Core::ourSubstr($bytes, self::KEY_HEADER_SIZE, $config->keyByteSize());
return new Key($version_header, $key_bytes);
}