函数文档

verify_file_signature()

💡 云策文档标注

概述

verify_file_signature() 是 WordPress 核心函数,用于验证文件内容与其 ED25519 签名是否匹配,确保文件完整性和真实性。该函数处理签名验证、系统兼容性检查,并返回布尔值或 WP_Error 对象。

关键要点

  • 函数参数:$filename(必需,文件路径)、$signatures(必需,签名字符串或数组)、$filename_for_errors(可选,错误显示用文件名,默认 false)。
  • 返回值:成功返回 true,未尝试验证返回 false,错误时返回 WP_Error 对象。
  • 验证流程:检查系统支持(sodium_crypto_sign_verify_detached 和 sha384 哈希算法)、Sodium_Compat 运行时速度、签名存在性,然后遍历签名和信任密钥进行验证。
  • 错误处理:返回 WP_Error 对象,包含错误代码(如 signature_verification_unsupported)和调试数据(如文件名、密钥、签名等)。
  • 相关函数:依赖 wp_trusted_keys() 获取信任密钥,使用 mbstring_binary_safe_encoding() 和 reset_mbstring_encoding() 处理编码。
  • 引入版本:WordPress 5.2.0。

代码示例

// 示例调用 verify_file_signature()
$result = verify_file_signature( '/path/to/file.zip', $signatures, 'file.zip' );
if ( is_wp_error( $result ) ) {
    // 处理错误
    echo $result->get_error_message();
} elseif ( $result === true ) {
    // 验证成功
    echo 'File verified.';
} else {
    // 验证未尝试
    echo 'Verification not attempted.';
}

注意事项

  • 系统要求:需支持 sodium_crypto_sign_verify_detached 函数和 sha384 哈希算法,否则返回 WP_Error。
  • 性能考虑:如果未加载 sodium 扩展且 Sodium_Compat 速度慢,验证可能失败,建议优化环境。
  • 签名格式:签名必须为 base64 编码,长度符合 SODIUM_CRYPTO_SIGN_BYTES;密钥长度需符合 SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES。
  • 错误调试:WP_Error 对象包含详细数据(如 skipped_key、skipped_sig),便于排查问题。

📄 原文内容

Verifies the contents of a file against its ED25519 signature.

Parameters

$filenamestringrequired
The file to validate.
$signaturesstring|arrayrequired
A Signature provided for the file.
$filename_for_errorsstring|falseoptional
A friendly filename for errors.

Default:false

Return

bool|WP_Error True on success, false if verification not attempted, or WP_Error describing an error condition.

Source

function verify_file_signature( $filename, $signatures, $filename_for_errors = false ) {
	if ( ! $filename_for_errors ) {
		$filename_for_errors = wp_basename( $filename );
	}

	// Check we can process signatures.
	if ( ! function_exists( 'sodium_crypto_sign_verify_detached' ) || ! in_array( 'sha384', array_map( 'strtolower', hash_algos() ), true ) ) {
		return new WP_Error(
			'signature_verification_unsupported',
			sprintf(
				/* translators: %s: The filename of the package. */
				__( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ),
				'<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
			),
			( ! function_exists( 'sodium_crypto_sign_verify_detached' ) ? 'sodium_crypto_sign_verify_detached' : 'sha384' )
		);
	}

	// Verify runtime speed of Sodium_Compat is acceptable.
	if ( ! extension_loaded( 'sodium' ) && ! ParagonIE_Sodium_Compat::polyfill_is_fast() ) {
		$sodium_compat_is_fast = false;

		// Allow for an old version of Sodium_Compat being loaded before the bundled WordPress one.
		if ( method_exists( 'ParagonIE_Sodium_Compat', 'runtime_speed_test' ) ) {
			/*
			 * Run `ParagonIE_Sodium_Compat::runtime_speed_test()` in optimized integer mode,
			 * as that's what WordPress utilizes during signing verifications.
			 */
			// phpcs:disable WordPress.NamingConventions.ValidVariableName
			$old_fastMult                      = ParagonIE_Sodium_Compat::$fastMult;
			ParagonIE_Sodium_Compat::$fastMult = true;
			$sodium_compat_is_fast             = ParagonIE_Sodium_Compat::runtime_speed_test( 100, 10 );
			ParagonIE_Sodium_Compat::$fastMult = $old_fastMult;
			// phpcs:enable
		}

		/*
		 * This cannot be performed in a reasonable amount of time.
		 * https://github.com/paragonie/sodium_compat#help-sodium_compat-is-slow-how-can-i-make-it-fast
		 */
		if ( ! $sodium_compat_is_fast ) {
			return new WP_Error(
				'signature_verification_unsupported',
				sprintf(
					/* translators: %s: The filename of the package. */
					__( 'The authenticity of %s could not be verified as signature verification is unavailable on this system.' ),
					'<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
				),
				array(
					'php'                => PHP_VERSION,
					'sodium'             => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ),
					'polyfill_is_fast'   => false,
					'max_execution_time' => ini_get( 'max_execution_time' ),
				)
			);
		}
	}

	if ( ! $signatures ) {
		return new WP_Error(
			'signature_verification_no_signature',
			sprintf(
				/* translators: %s: The filename of the package. */
				__( 'The authenticity of %s could not be verified as no signature was found.' ),
				'<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
			),
			array(
				'filename' => $filename_for_errors,
			)
		);
	}

	$trusted_keys = wp_trusted_keys();
	$file_hash    = hash_file( 'sha384', $filename, true );

	mbstring_binary_safe_encoding();

	$skipped_key       = 0;
	$skipped_signature = 0;

	foreach ( (array) $signatures as $signature ) {
		$signature_raw = base64_decode( $signature );

		// Ensure only valid-length signatures are considered.
		if ( SODIUM_CRYPTO_SIGN_BYTES !== strlen( $signature_raw ) ) {
			++$skipped_signature;
			continue;
		}

		foreach ( (array) $trusted_keys as $key ) {
			$key_raw = base64_decode( $key );

			// Only pass valid public keys through.
			if ( SODIUM_CRYPTO_SIGN_PUBLICKEYBYTES !== strlen( $key_raw ) ) {
				++$skipped_key;
				continue;
			}

			if ( sodium_crypto_sign_verify_detached( $signature_raw, $file_hash, $key_raw ) ) {
				reset_mbstring_encoding();
				return true;
			}
		}
	}

	reset_mbstring_encoding();

	return new WP_Error(
		'signature_verification_failed',
		sprintf(
			/* translators: %s: The filename of the package. */
			__( 'The authenticity of %s could not be verified.' ),
			'<span class="code">' . esc_html( $filename_for_errors ) . '</span>'
		),
		// Error data helpful for debugging:
		array(
			'filename'    => $filename_for_errors,
			'keys'        => $trusted_keys,
			'signatures'  => $signatures,
			'hash'        => bin2hex( $file_hash ),
			'skipped_key' => $skipped_key,
			'skipped_sig' => $skipped_signature,
			'php'         => PHP_VERSION,
			'sodium'      => defined( 'SODIUM_LIBRARY_VERSION' ) ? SODIUM_LIBRARY_VERSION : ( defined( 'ParagonIE_Sodium_Compat::VERSION_STRING' ) ? ParagonIE_Sodium_Compat::VERSION_STRING : false ),
		)
	);
}

Changelog

Version Description
5.2.0 Introduced.