/** * 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()); }
/** * 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()); }
/** * @param string $message * @param string $signature * @param string $signer_public * @return bool * @throws SignatureException * @throws InvalidTypeException */ public static function verify($message, $signature, $signer_public) { # Test to make sure all the required variables are strings. Helpers::isString($message, 'PublicKeyEncryption', 'verify'); Helpers::isString($signature, 'PublicKeyEncryption', 'verify'); Helpers::isString($signer_public, 'PublicKeyEncryption', 'verify'); # Decode the signature from hex $signature = base64_decode(Helpers::hex2bin($signature)); # Decode the signer's public key from hex $signer_public = Helpers::hex2bin($signer_public); if (\Sodium\crypto_sign_verify_detached($signature, $message, $signer_public)) { return true; } else { throw new SignatureException('Signature for message invalid.'); } }
/** * 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()); }
/** * 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'}; }
/** * Verify a signed message with the correct public key * * @param string $message Message to verify * @param SignaturePublicKey $publicKey * @param string $signature * @param mixed $encoding Which encoding scheme to use? * @return bool * @throws InvalidSignature */ public static function verify(string $message, SignaturePublicKey $publicKey, string $signature, $encoding = Halite::ENCODE_BASE64URLSAFE) : bool { $decoder = Halite::chooseEncoder($encoding, true); if ($decoder) { // We were given hex data: $signature = $decoder($signature); } if (CryptoUtil::safeStrlen($signature) !== \Sodium\CRYPTO_SIGN_BYTES) { throw new InvalidSignature('Signature is not the correct length; is it encoded?'); } return \Sodium\crypto_sign_verify_detached($signature, $message, $publicKey->getRawKeyMaterial()); }
/** * Checks if the ticket has been authenticated. * Verifies user signature if libsodium is available. * * @return Returns an associative array containing user_id and aux data, or a WP_Error instance on error. */ static function verify_authentication($ticketid, $purpose, $nonce) { # Prepare parameters $params = "ticket-id=" . urlencode($ticketid); # Invoke API $cont = @file_get_contents("https://api.verifyne.me/v1/ticket-state?" . $params); 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"); } # Check result if ($resp->result->code !== 0) { return new WP_Error("verifyne", $resp->result->description); } # Extract content $ticket = $resp->content; # Sanity checks if ($ticket->id !== $ticketid || $ticket->purpose !== $purpose || $ticket->nonce !== $nonce) { return new WP_Error("verifyne", "Received invalid data from verifyne server"); } # We can verify the cryptographic signatures :) if (extension_loaded('libsodium')) { #<provider_signature>:<type>:<id>:<expires>:<nonce>:<purpose> $payload = $ticket->provider_signature . ":" . $ticket->type . ":" . $ticket->id . ":" . $ticket->expires . ":" . $ticket->nonce . ":" . $ticket->purpose; $signature = base64_decode($ticket->user_signature, $strict = true); if (FALSE === $signature) { return new WP_Error("verifyne", "Undecodable user signature"); } $user_key = base64_decode($ticket->user_id, $strict = true); if (FALSE === $user_key) { return new WP_Error("verifyne", "Undecodable user key"); } # Check user signature if (TRUE !== @\Sodium\crypto_sign_verify_detached($signature, $payload, $user_key)) { return new WP_Error("verifyne", "Invalid user signature"); } # User signature is legit :) } return array("userid" => $ticket->user_id, "aux" => $ticket->aux); }