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
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. |