mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-06 13:30:00 +00:00
Merge pull request #9571 from Felipebmaitan/feat/libressl-cmac
feat(libSceLibreSsl): implement CMAC with AES-CBC methods
This commit is contained in:
15 files changed
+2405
-184
No files matched your search
@@ -302,6 +302,8 @@ include(prx/libSceFreeType/BuildInfo.cmake)
|
||||
|
||||
add_custom_target(libs DEPENDS ${PATCHED_OUTPUTS})
|
||||
include(prx/libSceLibreSsl/ErrorQueue.cmake)
|
||||
include(prx/libSceLibreSsl/MemoryBio.cmake)
|
||||
include(prx/libSceLibreSsl/Cmac.cmake)
|
||||
|
||||
include(prx/libSceLibreSsl/ProtocolSelection.cmake)
|
||||
|
||||
|
||||
@@ -1,10 +1,21 @@
|
||||
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp)
|
||||
add_library(libSceLibreSsl SHARED EXCLUDE_FROM_ALL ${CMAKE_CURRENT_SOURCE_DIR}/Export.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Bignum.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Digest.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Runtime.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Aes.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Error.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ocsp.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Stack.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Ui.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Rsa.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/X509StoreContext.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Pkey.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/EvpCipherCtx.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/Evp.cpp)
|
||||
target_include_directories(libSceLibreSsl PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(libSceLibreSsl PRIVATE mbedcrypto)
|
||||
configure_windows_unwind(libSceLibreSsl)
|
||||
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Asn1.cpp ${CMAKE_CURRENT_SOURCE_DIR}/src/ExData.cpp)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_libressl_bn_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslBn.cpp)
|
||||
target_include_directories(guest_libressl_bn_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_libressl_bn_tests PRIVATE libSceLibreSsl libc)
|
||||
configure_windows_unwind(guest_libressl_bn_tests)
|
||||
add_test(NAME guest_libressl_bn COMMAND guest_libressl_bn_tests)
|
||||
set_tests_properties(guest_libressl_bn PROPERTIES TIMEOUT 30)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_libressl_bn PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
|
||||
endif()
|
||||
|
||||
add_test_executable(guest_libressl_x509_errors_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslX509Errors.cpp)
|
||||
target_include_directories(guest_libressl_x509_errors_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_libressl_x509_errors_tests PRIVATE libSceLibreSsl libc)
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/Cmac.cpp)
|
||||
target_link_libraries(libSceLibreSsl PRIVATE mbedcrypto)
|
||||
if(MINGW OR CMAKE_SYSTEM_NAME STREQUAL "Linux")
|
||||
target_link_options(libSceLibreSsl PRIVATE LINKER:--exclude-libs,ALL)
|
||||
endif()
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_libressl_cmac_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslCmac.cpp)
|
||||
target_include_directories(guest_libressl_cmac_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_libressl_cmac_tests PRIVATE libSceLibreSsl libc mbedcrypto)
|
||||
configure_windows_unwind(guest_libressl_cmac_tests)
|
||||
add_test(NAME guest_libressl_cmac COMMAND guest_libressl_cmac_tests)
|
||||
|
||||
add_test_executable(guest_libressl_cmac_patched_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslCmac.cpp)
|
||||
target_include_directories(guest_libressl_cmac_patched_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_compile_definitions(guest_libressl_cmac_patched_tests PRIVATE ANYPS5_CMAC_DYNAMIC)
|
||||
target_link_libraries(guest_libressl_cmac_patched_tests PRIVATE mbedcrypto ${CMAKE_DL_LIBS})
|
||||
configure_windows_unwind(guest_libressl_cmac_patched_tests)
|
||||
if(WIN32)
|
||||
target_link_libraries(guest_libressl_cmac_patched_tests PRIVATE libc)
|
||||
endif()
|
||||
add_custom_target(guest_libressl_cmac_patch DEPENDS ${PATCHED_DIR}/libSceLibreSsl.prx ${PATCHED_DIR}/libc.prx)
|
||||
add_dependencies(guest_libressl_cmac_patched_tests guest_libressl_cmac_patch)
|
||||
add_test(NAME guest_libressl_cmac_patched COMMAND guest_libressl_cmac_patched_tests ${PATCHED_DIR})
|
||||
set_tests_properties(guest_libressl_cmac guest_libressl_cmac_patched PROPERTIES TIMEOUT 30)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_libressl_cmac PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
|
||||
set_tests_properties(guest_libressl_cmac_patched PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:${PATCHED_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -8,6 +8,7 @@
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libSceLibreSsl/include/Bio.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include "mbedtls/des.h"
|
||||
|
||||
@@ -48,36 +49,16 @@ int APS5_VABI ASN1_TIME_print_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_ctrl_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_f_ssl_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_free_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_new_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_new_fp_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_new_mem_buf_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_new_socket_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
@@ -88,89 +69,13 @@ int APS5_VABI BIO_printf_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_puts_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_s_file_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_s_mem_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_write_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_CTX_end_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_CTX_free_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_CTX_new_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_CTX_start_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_add_word_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_bin2bn_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_bn2bin_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_free_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_hex2bn_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_mod_inverse_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_mul_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_new_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_num_bits_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
int APS5_VABI BIO_write_nid_postfix(LibreSsl::Bio* bio, const void* source, int size) {
|
||||
return LibreSslBioWrite_nid_no_patch(bio, source, size);
|
||||
}
|
||||
|
||||
int APS5_VABI BN_print_nid_postfix(void) {
|
||||
@@ -178,41 +83,6 @@ int APS5_VABI BN_print_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_set_word_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_sub_word_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_CTX_free_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_CTX_new_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Final_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Init_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Update_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI CONF_modules_free_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
@@ -570,10 +440,6 @@ int APS5_VABI EVP_PKEY_set1_RSA_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_128_cbc_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_128_cfb8_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
@@ -590,11 +456,6 @@ int APS5_VABI EVP_aes_128_gcm_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_192_cbc_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_192_cfb8_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
@@ -610,11 +471,6 @@ int APS5_VABI EVP_aes_192_gcm_nid_postfix(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_256_cbc_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI EVP_aes_256_cfb8_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
target_sources(libSceLibreSsl PRIVATE ${CMAKE_CURRENT_LIST_DIR}/src/Bio.cpp)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_libressl_bio_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslBio.cpp)
|
||||
target_include_directories(guest_libressl_bio_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_libressl_bio_tests PRIVATE libSceLibreSsl libc)
|
||||
configure_windows_unwind(guest_libressl_bio_tests)
|
||||
add_test(NAME guest_libressl_bio COMMAND guest_libressl_bio_tests)
|
||||
set_tests_properties(guest_libressl_bio PROPERTIES TIMEOUT 30)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_libressl_bio PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libSceLibreSsl>")
|
||||
endif()
|
||||
|
||||
add_test_executable(guest_libressl_bio_patched_tests ${LIBS_INCLUDE_DIR}/tests/GuestLibreSslBio.cpp)
|
||||
target_include_directories(guest_libressl_bio_patched_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_compile_definitions(guest_libressl_bio_patched_tests PRIVATE ANYPS5_BIO_DYNAMIC)
|
||||
target_link_libraries(guest_libressl_bio_patched_tests PRIVATE ${CMAKE_DL_LIBS})
|
||||
configure_windows_unwind(guest_libressl_bio_patched_tests)
|
||||
if(WIN32)
|
||||
target_link_libraries(guest_libressl_bio_patched_tests PRIVATE libc)
|
||||
endif()
|
||||
add_custom_target(libressl_bio_patched_libraries DEPENDS
|
||||
${PATCHED_DIR}/libSceLibreSsl.prx ${PATCHED_DIR}/libc.prx ${PATCHED_DIR}/libkernel.prx)
|
||||
add_dependencies(guest_libressl_bio_patched_tests libressl_bio_patched_libraries)
|
||||
add_test(NAME guest_libressl_bio_patched COMMAND guest_libressl_bio_patched_tests ${PATCHED_DIR})
|
||||
set_tests_properties(guest_libressl_bio_patched PROPERTIES TIMEOUT 30)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_libressl_bio_patched PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:${PATCHED_DIR}")
|
||||
endif()
|
||||
endif()
|
||||
@@ -1,6 +1,9 @@
|
||||
#pragma once
|
||||
|
||||
#include <vector>
|
||||
#include "SceTypes.hpp"
|
||||
#include "Bio.hpp"
|
||||
#include "Cmac.hpp"
|
||||
|
||||
struct Asn1String {
|
||||
std::int32_t length;
|
||||
@@ -131,6 +134,18 @@ struct Sha512Context {
|
||||
std::uint32_t num, mdLength;
|
||||
};
|
||||
|
||||
struct Bignum {
|
||||
std::uint64_t* d;
|
||||
int top;
|
||||
int dmax;
|
||||
int neg;
|
||||
int flags;
|
||||
};
|
||||
|
||||
struct BignumContext {
|
||||
std::vector<std::size_t> marks;
|
||||
};
|
||||
|
||||
using StackCompare = int (APS5_VABI*)(const void*, const void*);
|
||||
using StackElementFree = void (APS5_VABI*)(void*);
|
||||
|
||||
@@ -317,39 +332,39 @@ int APS5_VABI ASN1_STRING_print_nid_postfix(void);
|
||||
int APS5_VABI ASN1_STRING_to_UTF8_nid_postfix(unsigned char** output, Asn1String* value);
|
||||
int APS5_VABI ASN1_STRING_type_nid_postfix(Asn1String* value);
|
||||
int APS5_VABI ASN1_TIME_print_nid_postfix(void);
|
||||
int APS5_VABI BIO_ctrl_nid_postfix(void);
|
||||
std::int64_t APS5_VABI BIO_ctrl_nid_postfix(LibreSsl::Bio* bio, int command, std::int64_t argument, void* pointer);
|
||||
int APS5_VABI BIO_f_ssl_nid_postfix(void);
|
||||
int APS5_VABI BIO_free_nid_postfix(void);
|
||||
int APS5_VABI BIO_new_nid_postfix(void);
|
||||
int APS5_VABI BIO_free_nid_postfix(LibreSsl::Bio* bio);
|
||||
LibreSsl::Bio* APS5_VABI BIO_new_nid_postfix(const LibreSsl::BioMethod* method);
|
||||
int APS5_VABI BIO_new_fp_nid_postfix(void);
|
||||
int APS5_VABI BIO_new_mem_buf_nid_postfix(void);
|
||||
LibreSsl::Bio* APS5_VABI BIO_new_mem_buf_nid_postfix(const void* source, int size);
|
||||
int APS5_VABI BIO_new_socket_nid_postfix(void);
|
||||
int APS5_VABI BIO_printf_nid_postfix(void);
|
||||
int APS5_VABI BIO_puts_nid_postfix(void);
|
||||
int APS5_VABI BIO_puts_nid_postfix(LibreSsl::Bio* bio, const char* source);
|
||||
int APS5_VABI BIO_s_file_nid_postfix(void);
|
||||
int APS5_VABI BIO_s_mem_nid_postfix(void);
|
||||
int APS5_VABI BIO_write_nid_postfix(void);
|
||||
int APS5_VABI BN_CTX_end_nid_postfix(void);
|
||||
int APS5_VABI BN_CTX_free_nid_postfix(void);
|
||||
int APS5_VABI BN_CTX_new_nid_postfix(void);
|
||||
int APS5_VABI BN_CTX_start_nid_postfix(void);
|
||||
int APS5_VABI BN_add_word_nid_postfix(void);
|
||||
int APS5_VABI BN_bin2bn_nid_postfix(void);
|
||||
int APS5_VABI BN_bn2bin_nid_postfix(void);
|
||||
int APS5_VABI BN_free_nid_postfix(void);
|
||||
int APS5_VABI BN_hex2bn_nid_postfix(void);
|
||||
int APS5_VABI BN_mod_inverse_nid_postfix(void);
|
||||
int APS5_VABI BN_mul_nid_postfix(void);
|
||||
int APS5_VABI BN_new_nid_postfix(void);
|
||||
int APS5_VABI BN_num_bits_nid_postfix(void);
|
||||
const LibreSsl::BioMethod* APS5_VABI BIO_s_mem_nid_postfix();
|
||||
int APS5_VABI BIO_write_nid_postfix(LibreSsl::Bio* bio, const void* source, int size);
|
||||
void APS5_VABI BN_CTX_end_nid_postfix(BignumContext* ctx);
|
||||
void APS5_VABI BN_CTX_free_nid_postfix(BignumContext* ctx);
|
||||
BignumContext* APS5_VABI BN_CTX_new_nid_postfix(void);
|
||||
void APS5_VABI BN_CTX_start_nid_postfix(BignumContext* ctx);
|
||||
int APS5_VABI BN_add_word_nid_postfix(Bignum* a, std::uint64_t w);
|
||||
Bignum* APS5_VABI BN_bin2bn_nid_postfix(const unsigned char* s, int len, Bignum* ret);
|
||||
int APS5_VABI BN_bn2bin_nid_postfix(const Bignum* a, unsigned char* to);
|
||||
void APS5_VABI BN_free_nid_postfix(Bignum* a);
|
||||
int APS5_VABI BN_hex2bn_nid_postfix(Bignum** a, const char* str);
|
||||
Bignum* APS5_VABI BN_mod_inverse_nid_postfix(Bignum* r, const Bignum* a, const Bignum* n, BignumContext*);
|
||||
int APS5_VABI BN_mul_nid_postfix(Bignum* r, const Bignum* a, const Bignum* b, BignumContext*);
|
||||
Bignum* APS5_VABI BN_new_nid_postfix(void);
|
||||
int APS5_VABI BN_num_bits_nid_postfix(const Bignum* a);
|
||||
int APS5_VABI BN_print_nid_postfix(void);
|
||||
int APS5_VABI BN_set_word_nid_postfix(void);
|
||||
int APS5_VABI BN_sub_word_nid_postfix(void);
|
||||
int APS5_VABI CMAC_CTX_free_nid_postfix(void);
|
||||
int APS5_VABI CMAC_CTX_new_nid_postfix(void);
|
||||
int APS5_VABI CMAC_Final_nid_postfix(void);
|
||||
int APS5_VABI CMAC_Init_nid_postfix(void);
|
||||
int APS5_VABI CMAC_Update_nid_postfix(void);
|
||||
int APS5_VABI BN_set_word_nid_postfix(Bignum* a, std::uint64_t w);
|
||||
int APS5_VABI BN_sub_word_nid_postfix(Bignum* a, std::uint64_t w);
|
||||
void APS5_VABI CMAC_CTX_free_nid_postfix(LibreSslCmac* context);
|
||||
LibreSslCmac* APS5_VABI CMAC_CTX_new_nid_postfix();
|
||||
int APS5_VABI CMAC_Final_nid_postfix(LibreSslCmac* context, unsigned char* output, std::size_t* length);
|
||||
int APS5_VABI CMAC_Init_nid_postfix(LibreSslCmac* context, const void* key, std::size_t keyLength, const LibreSslCipher* cipher, void* engine);
|
||||
int APS5_VABI CMAC_Update_nid_postfix(LibreSslCmac* context, const void* data, std::size_t length);
|
||||
int APS5_VABI CONF_modules_free_nid_postfix(void);
|
||||
int APS5_VABI CONF_modules_load_file_nid_postfix(void);
|
||||
int APS5_VABI CRYPTO_THREADID_set_callback_nid_postfix(void* callback);
|
||||
@@ -399,13 +414,13 @@ int APS5_VABI ENGINE_init_nid_postfix(void);
|
||||
int APS5_VABI ENGINE_load_builtin_engines_nid_postfix(void);
|
||||
int APS5_VABI ENGINE_load_private_key_nid_postfix(void);
|
||||
int APS5_VABI ENGINE_set_default_nid_postfix(void);
|
||||
void APS5_VABI ERR_clear_error_nid_postfix(void);
|
||||
void APS5_VABI ERR_clear_error_nid_postfix();
|
||||
char* APS5_VABI ERR_error_string_nid_postfix(std::uint64_t error, char* buffer);
|
||||
void APS5_VABI ERR_error_string_n_nid_postfix(std::uint64_t error, char* buffer, std::size_t length);
|
||||
void APS5_VABI ERR_free_strings_nid_postfix();
|
||||
std::uint64_t APS5_VABI ERR_get_error_nid_postfix(void);
|
||||
std::uint64_t APS5_VABI ERR_peek_error_nid_postfix(void);
|
||||
void APS5_VABI ERR_remove_thread_state_nid_postfix(const void* threadId);
|
||||
std::uint64_t APS5_VABI ERR_get_error_nid_postfix();
|
||||
std::uint64_t APS5_VABI ERR_peek_error_nid_postfix();
|
||||
void APS5_VABI ERR_remove_thread_state_nid_postfix(const void* id);
|
||||
int APS5_VABI EVP_CIPHER_CTX_cleanup_nid_postfix(void);
|
||||
int APS5_VABI EVP_CIPHER_CTX_ctrl_nid_postfix(void);
|
||||
int APS5_VABI EVP_CIPHER_CTX_free_nid_postfix(void);
|
||||
@@ -441,15 +456,15 @@ int APS5_VABI EVP_PKEY_id_nid_postfix(void);
|
||||
int APS5_VABI EVP_PKEY_new_nid_postfix(void);
|
||||
int APS5_VABI EVP_PKEY_set1_RSA_nid_postfix(void);
|
||||
int APS5_VABI EVP_PKEY_type_nid_postfix(int type);
|
||||
int APS5_VABI EVP_aes_128_cbc_nid_postfix(void);
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_128_cbc_nid_postfix();
|
||||
int APS5_VABI EVP_aes_128_cfb8_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_128_ctr_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_128_gcm_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_192_cbc_nid_postfix(void);
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_192_cbc_nid_postfix();
|
||||
int APS5_VABI EVP_aes_192_cfb8_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_192_ctr_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_192_gcm_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_256_cbc_nid_postfix(void);
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_256_cbc_nid_postfix();
|
||||
int APS5_VABI EVP_aes_256_cfb8_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_256_ctr_nid_postfix(void);
|
||||
int APS5_VABI EVP_aes_256_gcm_nid_postfix(void);
|
||||
|
||||
@@ -0,0 +1,72 @@
|
||||
#ifndef CORE_LIBS_PRX_LIBSCELIBRESSL_INCLUDE_BIO_HPP
|
||||
#define CORE_LIBS_PRX_LIBSCELIBRESSL_INCLUDE_BIO_HPP
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
|
||||
namespace LibreSsl {
|
||||
|
||||
struct Bio;
|
||||
using BioCallback = std::int64_t (APS5_VABI *)(Bio*, int, const char*, int, std::int64_t, std::int64_t);
|
||||
using BioInfoCallback = void (APS5_VABI *)(Bio*, int, const char*, int, std::int64_t, std::int64_t);
|
||||
|
||||
struct BioMethod {
|
||||
int type;
|
||||
const char* name;
|
||||
int (APS5_VABI *write)(Bio*, const char*, int);
|
||||
int (APS5_VABI *read)(Bio*, char*, int);
|
||||
int (APS5_VABI *puts)(Bio*, const char*);
|
||||
int (APS5_VABI *gets)(Bio*, char*, int);
|
||||
std::int64_t (APS5_VABI *control)(Bio*, int, std::int64_t, void*);
|
||||
int (APS5_VABI *create)(Bio*);
|
||||
int (APS5_VABI *destroy)(Bio*);
|
||||
std::int64_t (APS5_VABI *callbackControl)(Bio*, int, BioInfoCallback);
|
||||
};
|
||||
|
||||
struct Bio {
|
||||
const BioMethod* method;
|
||||
BioCallback callback;
|
||||
char* callbackArgument;
|
||||
int initialized;
|
||||
int shutdown;
|
||||
int flags;
|
||||
int retryReason;
|
||||
int number;
|
||||
void* data;
|
||||
Bio* next;
|
||||
Bio* previous;
|
||||
int references;
|
||||
std::uint64_t bytesRead;
|
||||
std::uint64_t bytesWritten;
|
||||
void* extraData;
|
||||
};
|
||||
|
||||
struct BioBuffer {
|
||||
std::size_t length;
|
||||
char* data;
|
||||
std::size_t capacity;
|
||||
};
|
||||
|
||||
static_assert(sizeof(BioMethod) == 80);
|
||||
static_assert(offsetof(BioMethod, write) == 16);
|
||||
static_assert(offsetof(BioMethod, control) == 48);
|
||||
static_assert(offsetof(BioMethod, callbackControl) == 72);
|
||||
static_assert(sizeof(Bio) == 104);
|
||||
static_assert(offsetof(Bio, initialized) == 24);
|
||||
static_assert(offsetof(Bio, flags) == 32);
|
||||
static_assert(offsetof(Bio, number) == 40);
|
||||
static_assert(offsetof(Bio, data) == 48);
|
||||
static_assert(offsetof(Bio, references) == 72);
|
||||
static_assert(offsetof(Bio, bytesRead) == 80);
|
||||
static_assert(offsetof(Bio, bytesWritten) == 88);
|
||||
static_assert(offsetof(Bio, extraData) == 96);
|
||||
static_assert(sizeof(BioBuffer) == 24);
|
||||
static_assert(offsetof(BioBuffer, data) == 8);
|
||||
static_assert(offsetof(BioBuffer, capacity) == 16);
|
||||
|
||||
} // namespace LibreSsl
|
||||
|
||||
extern "C" int LibreSslBioWrite_nid_no_patch(LibreSsl::Bio* bio, const void* source, int size);
|
||||
|
||||
#endif
|
||||
@@ -0,0 +1,46 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
|
||||
struct LibreSslCipherContext;
|
||||
|
||||
struct LibreSslCipher {
|
||||
std::int32_t nid;
|
||||
std::int32_t blockSize;
|
||||
std::int32_t keyLength;
|
||||
std::int32_t ivLength;
|
||||
std::uint64_t flags;
|
||||
int (APS5_VABI* init)(LibreSslCipherContext*, const unsigned char*, const unsigned char*, int);
|
||||
int (APS5_VABI* cipher)(LibreSslCipherContext*, unsigned char*, const unsigned char*, std::size_t);
|
||||
int (APS5_VABI* cleanup)(LibreSslCipherContext*);
|
||||
std::int32_t contextSize;
|
||||
int (APS5_VABI* setAsn1Parameters)(LibreSslCipherContext*, void*);
|
||||
int (APS5_VABI* getAsn1Parameters)(LibreSslCipherContext*, void*);
|
||||
int (APS5_VABI* control)(LibreSslCipherContext*, int, int, void*);
|
||||
void* applicationData;
|
||||
};
|
||||
|
||||
struct LibreSslCipherContext {
|
||||
const LibreSslCipher* cipher;
|
||||
void* engine;
|
||||
std::int32_t encrypt;
|
||||
std::int32_t bufferLength;
|
||||
unsigned char originalIv[16];
|
||||
unsigned char iv[16];
|
||||
unsigned char buffer[32];
|
||||
std::int32_t position;
|
||||
void* applicationData;
|
||||
std::int32_t keyLength;
|
||||
std::uint64_t flags;
|
||||
void* cipherData;
|
||||
std::int32_t finalUsed;
|
||||
std::int32_t blockMask;
|
||||
unsigned char final[32];
|
||||
};
|
||||
|
||||
static_assert(sizeof(LibreSslCipher) == 88 && offsetof(LibreSslCipher, init) == 24 && offsetof(LibreSslCipher, contextSize) == 48);
|
||||
static_assert(sizeof(LibreSslCipherContext) == 168 && offsetof(LibreSslCipherContext, iv) == 40 && offsetof(LibreSslCipherContext, flags) == 112 && offsetof(LibreSslCipherContext, cipherData) == 120);
|
||||
|
||||
struct LibreSslCmac;
|
||||
@@ -0,0 +1,378 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include <bit>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <new>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include "prx/libc/include/General.hpp"
|
||||
|
||||
namespace {
|
||||
|
||||
static_assert(sizeof(Bignum) == 24);
|
||||
static_assert(offsetof(Bignum, d) == 0);
|
||||
static_assert(offsetof(Bignum, top) == 8);
|
||||
static_assert(offsetof(Bignum, dmax) == 12);
|
||||
static_assert(offsetof(Bignum, neg) == 16);
|
||||
static_assert(offsetof(Bignum, flags) == 20);
|
||||
|
||||
constexpr int FlagMalloced = 1;
|
||||
constexpr int FlagStaticData = 2;
|
||||
|
||||
using Mag = std::vector<std::uint64_t>;
|
||||
|
||||
struct Int {
|
||||
Mag mag;
|
||||
bool neg = false;
|
||||
};
|
||||
|
||||
void Trim(Mag& m) {
|
||||
while (!m.empty() && m.back() == 0) m.pop_back();
|
||||
}
|
||||
|
||||
int Compare(const Mag& a, const Mag& b) {
|
||||
if (a.size() != b.size()) return a.size() < b.size() ? -1 : 1;
|
||||
for (std::size_t i = a.size(); i-- > 0;) {
|
||||
if (a[i] != b[i]) return a[i] < b[i] ? -1 : 1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
Mag AddMag(const Mag& a, const Mag& b) {
|
||||
const std::size_t count = a.size() > b.size() ? a.size() : b.size();
|
||||
Mag r;
|
||||
r.reserve(count + 1);
|
||||
std::uint64_t carry = 0;
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
const unsigned __int128 sum = static_cast<unsigned __int128>(i < a.size() ? a[i] : 0) + (i < b.size() ? b[i] : 0) + carry;
|
||||
r.push_back(static_cast<std::uint64_t>(sum));
|
||||
carry = static_cast<std::uint64_t>(sum >> 64);
|
||||
}
|
||||
if (carry) r.push_back(carry);
|
||||
return r;
|
||||
}
|
||||
|
||||
Mag SubMag(const Mag& a, const Mag& b) {
|
||||
Mag r(a.size());
|
||||
std::uint64_t borrow = 0;
|
||||
for (std::size_t i = 0; i < a.size(); i++) {
|
||||
const std::uint64_t x = a[i];
|
||||
const std::uint64_t y = i < b.size() ? b[i] : 0;
|
||||
const std::uint64_t diff = x - y;
|
||||
const std::uint64_t first = x < y;
|
||||
r[i] = diff - borrow;
|
||||
borrow = first | (diff < borrow);
|
||||
}
|
||||
Trim(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
Mag MulMag(const Mag& a, const Mag& b) {
|
||||
if (a.empty() || b.empty()) return {};
|
||||
Mag r(a.size() + b.size(), 0);
|
||||
for (std::size_t i = 0; i < a.size(); i++) {
|
||||
std::uint64_t carry = 0;
|
||||
for (std::size_t j = 0; j < b.size(); j++) {
|
||||
const unsigned __int128 product = static_cast<unsigned __int128>(a[i]) * b[j] + r[i + j] + carry;
|
||||
r[i + j] = static_cast<std::uint64_t>(product);
|
||||
carry = static_cast<std::uint64_t>(product >> 64);
|
||||
}
|
||||
r[i + b.size()] = carry;
|
||||
}
|
||||
Trim(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
std::size_t BitLength(const Mag& m) {
|
||||
if (m.empty()) return 0;
|
||||
return (m.size() - 1) * 64 + std::bit_width(m.back());
|
||||
}
|
||||
|
||||
Mag ShiftLeft(const Mag& m, std::size_t bits) {
|
||||
if (m.empty()) return {};
|
||||
const std::size_t words = bits / 64;
|
||||
const unsigned rest = static_cast<unsigned>(bits % 64);
|
||||
Mag r(m.size() + words + 1, 0);
|
||||
for (std::size_t i = 0; i < m.size(); i++) {
|
||||
r[i + words] |= m[i] << rest;
|
||||
if (rest) r[i + words + 1] |= m[i] >> (64 - rest);
|
||||
}
|
||||
Trim(r);
|
||||
return r;
|
||||
}
|
||||
|
||||
void ShiftRightOne(Mag& m) {
|
||||
for (std::size_t i = 0; i < m.size(); i++) {
|
||||
m[i] >>= 1;
|
||||
if (i + 1 < m.size()) m[i] |= m[i + 1] << 63;
|
||||
}
|
||||
Trim(m);
|
||||
}
|
||||
|
||||
void DivMod(const Mag& a, const Mag& b, Mag& quotient, Mag& remainder) {
|
||||
remainder = a;
|
||||
quotient.clear();
|
||||
const std::size_t aBits = BitLength(a);
|
||||
const std::size_t bBits = BitLength(b);
|
||||
if (aBits < bBits) return;
|
||||
const std::size_t shift = aBits - bBits;
|
||||
Mag divisor = ShiftLeft(b, shift);
|
||||
quotient.assign(shift / 64 + 1, 0);
|
||||
for (std::size_t s = shift + 1; s-- > 0;) {
|
||||
if (Compare(remainder, divisor) >= 0) {
|
||||
remainder = SubMag(remainder, divisor);
|
||||
quotient[s / 64] |= std::uint64_t(1) << (s % 64);
|
||||
}
|
||||
ShiftRightOne(divisor);
|
||||
}
|
||||
Trim(quotient);
|
||||
}
|
||||
|
||||
Int AddSigned(const Int& x, const Int& y) {
|
||||
Int r;
|
||||
if (x.neg == y.neg) {
|
||||
r.mag = AddMag(x.mag, y.mag);
|
||||
r.neg = x.neg;
|
||||
} else {
|
||||
const int order = Compare(x.mag, y.mag);
|
||||
if (order > 0) {
|
||||
r.mag = SubMag(x.mag, y.mag);
|
||||
r.neg = x.neg;
|
||||
} else if (order < 0) {
|
||||
r.mag = SubMag(y.mag, x.mag);
|
||||
r.neg = y.neg;
|
||||
}
|
||||
}
|
||||
if (r.mag.empty()) r.neg = false;
|
||||
return r;
|
||||
}
|
||||
|
||||
bool InverseMod(const Int& a, const Mag& n, Mag& out) {
|
||||
if (n.size() == 1 && n[0] == 1) {
|
||||
out.clear();
|
||||
return true;
|
||||
}
|
||||
Mag quotient;
|
||||
Mag r1;
|
||||
DivMod(a.mag, n, quotient, r1);
|
||||
if (a.neg && !r1.empty()) r1 = SubMag(n, r1);
|
||||
Mag r0 = n;
|
||||
Int t0;
|
||||
Int t1;
|
||||
t1.mag = {1};
|
||||
while (!r1.empty()) {
|
||||
Mag remainder;
|
||||
DivMod(r0, r1, quotient, remainder);
|
||||
Int scaled;
|
||||
scaled.mag = MulMag(quotient, t1.mag);
|
||||
scaled.neg = !t1.neg && !scaled.mag.empty();
|
||||
Int t2 = AddSigned(t0, scaled);
|
||||
r0 = std::move(r1);
|
||||
r1 = std::move(remainder);
|
||||
t0 = std::move(t1);
|
||||
t1 = std::move(t2);
|
||||
}
|
||||
if (r0.size() != 1 || r0[0] != 1) return false;
|
||||
out = t0.neg ? SubMag(n, t0.mag) : t0.mag;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bignum* Checked(Bignum* value, const char* function) {
|
||||
if (!value) throw std::invalid_argument(std::string(function) + ": null BIGNUM");
|
||||
return value;
|
||||
}
|
||||
|
||||
const Bignum* Checked(const Bignum* value, const char* function) {
|
||||
if (!value) throw std::invalid_argument(std::string(function) + ": null BIGNUM");
|
||||
return value;
|
||||
}
|
||||
|
||||
Int Load(const Bignum* bn) {
|
||||
Int r;
|
||||
if (bn->top > 0) r.mag.assign(bn->d, bn->d + bn->top);
|
||||
Trim(r.mag);
|
||||
r.neg = bn->neg && !r.mag.empty();
|
||||
return r;
|
||||
}
|
||||
|
||||
bool Store(Bignum* bn, const Mag& m, bool neg) {
|
||||
if (m.empty()) {
|
||||
bn->top = 0;
|
||||
bn->neg = 0;
|
||||
return true;
|
||||
}
|
||||
const std::size_t count = m.size();
|
||||
if (!bn->d || static_cast<std::size_t>(bn->dmax) < count) {
|
||||
if (bn->flags & FlagStaticData) return false;
|
||||
if (count > 0x7FFFFFFF) return false;
|
||||
auto* grown = static_cast<std::uint64_t*>(calloc_nid_postfix(count, sizeof(std::uint64_t)));
|
||||
if (!grown) return false;
|
||||
if (bn->d) free_nid_postfix(bn->d);
|
||||
bn->d = grown;
|
||||
bn->dmax = static_cast<int>(count);
|
||||
}
|
||||
for (std::size_t i = 0; i < count; i++) bn->d[i] = m[i];
|
||||
for (std::size_t i = count; i < static_cast<std::size_t>(bn->dmax); i++) bn->d[i] = 0;
|
||||
bn->top = static_cast<int>(count);
|
||||
bn->neg = neg ? 1 : 0;
|
||||
return true;
|
||||
}
|
||||
|
||||
Bignum* NewBignum() {
|
||||
auto* bn = static_cast<Bignum*>(calloc_nid_postfix(1, sizeof(Bignum)));
|
||||
if (bn) bn->flags = FlagMalloced;
|
||||
return bn;
|
||||
}
|
||||
|
||||
void FreeBignum(Bignum* bn) {
|
||||
if (!bn) return;
|
||||
if (bn->d && !(bn->flags & FlagStaticData)) free_nid_postfix(bn->d);
|
||||
if (bn->flags & FlagMalloced) {
|
||||
free_nid_postfix(bn);
|
||||
} else {
|
||||
bn->d = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
int HexValue(char c) {
|
||||
if (c >= '0' && c <= '9') return c - '0';
|
||||
if (c >= 'a' && c <= 'f') return c - 'a' + 10;
|
||||
if (c >= 'A' && c <= 'F') return c - 'A' + 10;
|
||||
return -1;
|
||||
}
|
||||
|
||||
bool ApplyWord(Bignum* a, std::uint64_t w, bool subtract) {
|
||||
Int word;
|
||||
if (w) word.mag = {w};
|
||||
word.neg = subtract && w;
|
||||
const Int sum = AddSigned(Load(a), word);
|
||||
return Store(a, sum.mag, sum.neg);
|
||||
}
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
Bignum* APS5_VABI BN_new_nid_postfix(void) {
|
||||
return NewBignum();
|
||||
}
|
||||
|
||||
void APS5_VABI BN_free_nid_postfix(Bignum* a) {
|
||||
FreeBignum(a);
|
||||
}
|
||||
|
||||
BignumContext* APS5_VABI BN_CTX_new_nid_postfix(void) {
|
||||
return new (std::nothrow) BignumContext();
|
||||
}
|
||||
|
||||
void APS5_VABI BN_CTX_free_nid_postfix(BignumContext* ctx) {
|
||||
delete ctx;
|
||||
}
|
||||
|
||||
void APS5_VABI BN_CTX_start_nid_postfix(BignumContext* ctx) {
|
||||
if (!ctx) throw std::invalid_argument(std::string(__func__) + ": null context");
|
||||
ctx->marks.push_back(0);
|
||||
}
|
||||
|
||||
void APS5_VABI BN_CTX_end_nid_postfix(BignumContext* ctx) {
|
||||
if (!ctx) throw std::invalid_argument(std::string(__func__) + ": null context");
|
||||
if (!ctx->marks.empty()) ctx->marks.pop_back();
|
||||
}
|
||||
|
||||
Bignum* APS5_VABI BN_bin2bn_nid_postfix(const unsigned char* s, int len, Bignum* ret) {
|
||||
if (len < 0) return nullptr;
|
||||
if (len > 0 && !s) throw std::invalid_argument(std::string(__func__) + ": null input");
|
||||
Bignum* target = ret ? ret : NewBignum();
|
||||
if (!target) return nullptr;
|
||||
Mag m((static_cast<std::size_t>(len) + 7) / 8, 0);
|
||||
for (int i = 0; i < len; i++) {
|
||||
const std::size_t bit = static_cast<std::size_t>(len - 1 - i);
|
||||
m[bit / 8] |= static_cast<std::uint64_t>(s[i]) << (bit % 8 * 8);
|
||||
}
|
||||
Trim(m);
|
||||
if (!Store(target, m, false)) {
|
||||
if (!ret) FreeBignum(target);
|
||||
return nullptr;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_num_bits_nid_postfix(const Bignum* a) {
|
||||
return static_cast<int>(BitLength(Load(Checked(a, __func__)).mag));
|
||||
}
|
||||
|
||||
int APS5_VABI BN_bn2bin_nid_postfix(const Bignum* a, unsigned char* to) {
|
||||
const Mag m = Load(Checked(a, __func__)).mag;
|
||||
const std::size_t length = (BitLength(m) + 7) / 8;
|
||||
if (length && !to) throw std::invalid_argument(std::string(__func__) + ": null output");
|
||||
for (std::size_t i = 0; i < length; i++) {
|
||||
const std::size_t bit = length - 1 - i;
|
||||
to[i] = static_cast<unsigned char>(m[bit / 8] >> (bit % 8 * 8));
|
||||
}
|
||||
return static_cast<int>(length);
|
||||
}
|
||||
|
||||
int APS5_VABI BN_hex2bn_nid_postfix(Bignum** a, const char* str) {
|
||||
if (!str || !*str) return 0;
|
||||
const bool negative = *str == '-';
|
||||
const char* digits = str + negative;
|
||||
std::size_t count = 0;
|
||||
while (HexValue(digits[count]) >= 0) count++;
|
||||
if (count == 0 || count > 0x7FFFFFFF / 4) return 0;
|
||||
const int consumed = static_cast<int>(count + negative);
|
||||
if (!a) return consumed;
|
||||
Mag m((count + 15) / 16, 0);
|
||||
for (std::size_t i = 0; i < count; i++) {
|
||||
const std::size_t nibble = count - 1 - i;
|
||||
m[nibble / 16] |= static_cast<std::uint64_t>(HexValue(digits[i])) << (nibble % 16 * 4);
|
||||
}
|
||||
Trim(m);
|
||||
Bignum* target = *a ? *a : NewBignum();
|
||||
if (!target) return 0;
|
||||
if (!Store(target, m, negative)) {
|
||||
if (!*a) FreeBignum(target);
|
||||
return 0;
|
||||
}
|
||||
*a = target;
|
||||
return consumed;
|
||||
}
|
||||
|
||||
int APS5_VABI BN_set_word_nid_postfix(Bignum* a, std::uint64_t w) {
|
||||
Checked(a, __func__);
|
||||
Mag m;
|
||||
if (w) m = {w};
|
||||
return Store(a, m, false);
|
||||
}
|
||||
|
||||
int APS5_VABI BN_add_word_nid_postfix(Bignum* a, std::uint64_t w) {
|
||||
return ApplyWord(Checked(a, __func__), w, false);
|
||||
}
|
||||
|
||||
int APS5_VABI BN_sub_word_nid_postfix(Bignum* a, std::uint64_t w) {
|
||||
return ApplyWord(Checked(a, __func__), w, true);
|
||||
}
|
||||
|
||||
int APS5_VABI BN_mul_nid_postfix(Bignum* r, const Bignum* a, const Bignum* b, BignumContext*) {
|
||||
Checked(r, __func__);
|
||||
const Int x = Load(Checked(a, __func__));
|
||||
const Int y = Load(Checked(b, __func__));
|
||||
const Mag product = MulMag(x.mag, y.mag);
|
||||
return Store(r, product, x.neg != y.neg);
|
||||
}
|
||||
|
||||
Bignum* APS5_VABI BN_mod_inverse_nid_postfix(Bignum* r, const Bignum* a, const Bignum* n, BignumContext*) {
|
||||
const Int value = Load(Checked(a, __func__));
|
||||
const Int modulus = Load(Checked(n, __func__));
|
||||
if (modulus.mag.empty()) return nullptr;
|
||||
Mag inverse;
|
||||
if (!InverseMod(value, modulus.mag, inverse)) return nullptr;
|
||||
Bignum* target = r ? r : NewBignum();
|
||||
if (!target) return nullptr;
|
||||
if (!Store(target, inverse, false)) {
|
||||
if (!r) FreeBignum(target);
|
||||
return nullptr;
|
||||
}
|
||||
return target;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,284 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <algorithm>
|
||||
#include <atomic>
|
||||
#include <cstring>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
|
||||
|
||||
namespace {
|
||||
|
||||
using LibreSsl::Bio;
|
||||
using LibreSsl::BioBuffer;
|
||||
using LibreSsl::BioMethod;
|
||||
|
||||
constexpr int ReadOnly = 0x200;
|
||||
constexpr int RetryFlags = 0xf;
|
||||
constexpr int RetryRead = 0x9;
|
||||
constexpr std::size_t MaximumGrowth = 0x5ffffffc;
|
||||
constexpr int BioLibrary = 32;
|
||||
constexpr int BufferLibrary = 7;
|
||||
constexpr int AllocationFailure = 65;
|
||||
constexpr int NullParameter = 115;
|
||||
constexpr int Uninitialized = 120;
|
||||
constexpr int UnsupportedMethod = 121;
|
||||
constexpr int ReadOnlyWrite = 126;
|
||||
|
||||
int queueError(int library, int reason, int result) {
|
||||
LibreSslPutError_nid_no_patch(library, 0xfff, reason, __FILE__, __LINE__);
|
||||
return result;
|
||||
}
|
||||
|
||||
void clearBuffer(BioBuffer& buffer) {
|
||||
auto* bytes = reinterpret_cast<volatile unsigned char*>(buffer.data);
|
||||
for (std::size_t i = 0; i < buffer.capacity; ++i) bytes[i] = 0;
|
||||
}
|
||||
|
||||
BioBuffer& memoryBuffer(Bio* bio) {
|
||||
if (!bio || !bio->data) throw std::invalid_argument("BIO: missing memory buffer");
|
||||
return *static_cast<BioBuffer*>(bio->data);
|
||||
}
|
||||
|
||||
int APS5_VABI createMemory(Bio* bio) {
|
||||
auto* buffer = static_cast<BioBuffer*>(calloc_nid_postfix(1, sizeof(BioBuffer)));
|
||||
if (!buffer) return queueError(BufferLibrary, AllocationFailure, 0);
|
||||
bio->data = buffer;
|
||||
bio->initialized = 1;
|
||||
bio->shutdown = 1;
|
||||
bio->number = -1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI destroyMemory(Bio* bio) {
|
||||
if (!bio) return 0;
|
||||
if (bio->shutdown && bio->initialized && bio->data) {
|
||||
auto* buffer = static_cast<BioBuffer*>(bio->data);
|
||||
if (!(bio->flags & ReadOnly)) {
|
||||
clearBuffer(*buffer);
|
||||
free_nid_postfix(buffer->data);
|
||||
}
|
||||
free_nid_postfix(buffer);
|
||||
bio->data = nullptr;
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI readMemory(Bio* bio, char* destination, int size) {
|
||||
auto& buffer = memoryBuffer(bio);
|
||||
bio->flags &= ~RetryFlags;
|
||||
const int count = size >= 0 ? static_cast<int>(std::min<std::size_t>(size, buffer.length)) : size;
|
||||
if (destination && count > 0) {
|
||||
std::memcpy(destination, buffer.data, count);
|
||||
buffer.length -= count;
|
||||
if (bio->flags & ReadOnly) buffer.data += count;
|
||||
else std::memmove(buffer.data, buffer.data + count, buffer.length);
|
||||
return count;
|
||||
}
|
||||
if (buffer.length != 0) return count;
|
||||
if (bio->number != 0) bio->flags |= RetryRead;
|
||||
return bio->number;
|
||||
}
|
||||
|
||||
int APS5_VABI writeMemory(Bio* bio, const char* source, int size) {
|
||||
auto& buffer = memoryBuffer(bio);
|
||||
if (!source) return queueError(BioLibrary, NullParameter, -1);
|
||||
if (bio->flags & ReadOnly) return queueError(BioLibrary, ReadOnlyWrite, -1);
|
||||
if (size < 0) throw std::invalid_argument("BIO: negative memory write size");
|
||||
bio->flags &= ~RetryFlags;
|
||||
const auto count = static_cast<std::size_t>(size);
|
||||
if (buffer.length > std::numeric_limits<std::size_t>::max() - count) NotImplemented_nid_no_patch(__func__);
|
||||
const std::size_t length = buffer.length + count;
|
||||
if (length > buffer.capacity) {
|
||||
if (length > MaximumGrowth) return queueError(BufferLibrary, AllocationFailure, -1);
|
||||
const std::size_t capacity = (length + 3) / 3 * 4;
|
||||
auto* data = static_cast<char*>(calloc_nid_postfix(capacity, 1));
|
||||
if (!data) return queueError(BufferLibrary, AllocationFailure, -1);
|
||||
if (buffer.length != 0) std::memcpy(data, buffer.data, buffer.length);
|
||||
if (count != 0) std::memcpy(data + buffer.length, source, count);
|
||||
clearBuffer(buffer);
|
||||
free_nid_postfix(buffer.data);
|
||||
buffer.data = data;
|
||||
buffer.capacity = capacity;
|
||||
} else if (count != 0) {
|
||||
std::memmove(buffer.data + buffer.length, source, count);
|
||||
}
|
||||
buffer.length = length;
|
||||
return size;
|
||||
}
|
||||
|
||||
int APS5_VABI putsMemory(Bio* bio, const char* source) {
|
||||
if (!source) throw std::invalid_argument("BIO_puts: null string");
|
||||
const std::size_t size = std::strlen(source);
|
||||
if (size > static_cast<std::size_t>(std::numeric_limits<int>::max())) NotImplemented_nid_no_patch(__func__);
|
||||
return writeMemory(bio, source, static_cast<int>(size));
|
||||
}
|
||||
|
||||
int APS5_VABI getsMemory(Bio* bio, char* destination, int size) {
|
||||
auto& buffer = memoryBuffer(bio);
|
||||
if (!destination || size == std::numeric_limits<int>::min()) throw std::invalid_argument("BIO: invalid line buffer");
|
||||
bio->flags &= ~RetryFlags;
|
||||
if (size <= 1 || buffer.length == 0) {
|
||||
*destination = '\0';
|
||||
return 0;
|
||||
}
|
||||
const auto limit = std::min<std::size_t>(size - 1, buffer.length);
|
||||
const void* newline = std::memchr(buffer.data, '\n', limit);
|
||||
const std::size_t count = newline ? static_cast<const char*>(newline) - buffer.data + 1 : limit;
|
||||
const int result = readMemory(bio, destination, static_cast<int>(count));
|
||||
destination[result] = '\0';
|
||||
return result;
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI controlMemory(Bio* bio, int command, std::int64_t argument, void* pointer) {
|
||||
if (!bio) throw std::invalid_argument("BIO: null memory BIO");
|
||||
switch (command) {
|
||||
case 1: {
|
||||
auto& buffer = memoryBuffer(bio);
|
||||
if (buffer.data) {
|
||||
if (bio->flags & ReadOnly) {
|
||||
if (buffer.capacity < buffer.length) throw std::invalid_argument("BIO: invalid read-only buffer length");
|
||||
buffer.data -= buffer.capacity - buffer.length;
|
||||
buffer.length = buffer.capacity;
|
||||
} else {
|
||||
clearBuffer(buffer);
|
||||
buffer.length = 0;
|
||||
}
|
||||
}
|
||||
return 1;
|
||||
}
|
||||
case 2:
|
||||
return memoryBuffer(bio).length == 0;
|
||||
case 3:
|
||||
if (pointer) *static_cast<char**>(pointer) = memoryBuffer(bio).data;
|
||||
return static_cast<std::int64_t>(memoryBuffer(bio).length);
|
||||
case 8:
|
||||
return bio->shutdown;
|
||||
case 9:
|
||||
bio->shutdown = static_cast<int>(argument);
|
||||
return 1;
|
||||
case 10:
|
||||
return static_cast<std::int64_t>(memoryBuffer(bio).length);
|
||||
case 11:
|
||||
case 12:
|
||||
return 1;
|
||||
case 13:
|
||||
return 0;
|
||||
case 114:
|
||||
destroyMemory(bio);
|
||||
bio->data = pointer;
|
||||
bio->shutdown = static_cast<int>(argument);
|
||||
return 1;
|
||||
case 115:
|
||||
if (pointer) std::memcpy(pointer, &bio->data, sizeof(bio->data));
|
||||
return 1;
|
||||
case 130:
|
||||
bio->number = static_cast<int>(argument);
|
||||
return 1;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
const BioMethod MemoryMethod{0x401, "memory buffer", writeMemory, readMemory, putsMemory, getsMemory, controlMemory, createMemory, destroyMemory, nullptr};
|
||||
|
||||
} // namespace
|
||||
|
||||
extern "C" {
|
||||
|
||||
const LibreSsl::BioMethod* APS5_VABI BIO_s_mem_nid_postfix() {
|
||||
return &MemoryMethod;
|
||||
}
|
||||
|
||||
LibreSsl::Bio* APS5_VABI BIO_new_nid_postfix(const LibreSsl::BioMethod* method) {
|
||||
if (!method) throw std::invalid_argument("BIO_new: null method");
|
||||
auto* bio = static_cast<Bio*>(calloc_nid_postfix(1, sizeof(Bio)));
|
||||
if (!bio) {
|
||||
queueError(BioLibrary, AllocationFailure, 0);
|
||||
return nullptr;
|
||||
}
|
||||
bio->method = method;
|
||||
bio->shutdown = 1;
|
||||
bio->references = 1;
|
||||
try {
|
||||
if (!method->create || method->create(bio)) return bio;
|
||||
} catch (...) {
|
||||
free_nid_postfix(bio);
|
||||
throw;
|
||||
}
|
||||
free_nid_postfix(bio);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
LibreSsl::Bio* APS5_VABI BIO_new_mem_buf_nid_postfix(const void* source, int size) {
|
||||
if (!source) {
|
||||
queueError(BioLibrary, NullParameter, 0);
|
||||
return nullptr;
|
||||
}
|
||||
auto* bio = BIO_new_nid_postfix(&MemoryMethod);
|
||||
if (!bio) return nullptr;
|
||||
auto& buffer = memoryBuffer(bio);
|
||||
buffer.data = const_cast<char*>(static_cast<const char*>(source));
|
||||
buffer.length = size < 0 ? std::strlen(buffer.data) : static_cast<std::size_t>(size);
|
||||
buffer.capacity = buffer.length;
|
||||
bio->flags |= ReadOnly;
|
||||
bio->number = 0;
|
||||
return bio;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_free_nid_postfix(LibreSsl::Bio* bio) {
|
||||
if (!bio) return 0;
|
||||
if (bio->extraData) NotImplemented_nid_no_patch(__func__);
|
||||
if (std::atomic_ref(bio->references).fetch_sub(1) > 1) return 1;
|
||||
if (bio->callback) {
|
||||
const int result = static_cast<int>(bio->callback(bio, 1, nullptr, 0, 0, 1));
|
||||
if (result <= 0) return result;
|
||||
}
|
||||
if (bio->extraData) NotImplemented_nid_no_patch(__func__);
|
||||
if (bio->method && bio->method->destroy) bio->method->destroy(bio);
|
||||
free_nid_postfix(bio);
|
||||
return 1;
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI BIO_ctrl_nid_postfix(LibreSsl::Bio* bio, int command, std::int64_t argument, void* pointer) {
|
||||
if (!bio) return 0;
|
||||
if (!bio->method || !bio->method->control) return queueError(BioLibrary, UnsupportedMethod, -2);
|
||||
const auto callback = bio->callback;
|
||||
if (callback) {
|
||||
const auto result = callback(bio, 6, static_cast<const char*>(pointer), command, argument, 1);
|
||||
if (result <= 0) return result;
|
||||
}
|
||||
const auto result = bio->method->control(bio, command, argument, pointer);
|
||||
return callback ? callback(bio, 0x86, static_cast<const char*>(pointer), command, argument, result) : result;
|
||||
}
|
||||
|
||||
int LibreSslBioWrite_nid_no_patch(LibreSsl::Bio* bio, const void* source, int size) {
|
||||
if (!bio || size < 0) return 0;
|
||||
if (!bio->method || !bio->method->write) return queueError(BioLibrary, UnsupportedMethod, -2);
|
||||
const auto callback = bio->callback;
|
||||
if (callback) {
|
||||
const int result = static_cast<int>(callback(bio, 3, static_cast<const char*>(source), size, 0, 1));
|
||||
if (result <= 0) return result;
|
||||
}
|
||||
if (!bio->initialized) return queueError(BioLibrary, Uninitialized, -2);
|
||||
const int result = bio->method->write(bio, static_cast<const char*>(source), size);
|
||||
if (result > 0) bio->bytesWritten += static_cast<std::uint64_t>(result);
|
||||
return callback ? static_cast<int>(callback(bio, 0x83, static_cast<const char*>(source), size, 0, result)) : result;
|
||||
}
|
||||
|
||||
int APS5_VABI BIO_puts_nid_postfix(LibreSsl::Bio* bio, const char* source) {
|
||||
if (!bio || !bio->method || !bio->method->puts) return queueError(BioLibrary, UnsupportedMethod, -2);
|
||||
const auto callback = bio->callback;
|
||||
if (callback) {
|
||||
const int result = static_cast<int>(callback(bio, 4, source, 0, 0, 1));
|
||||
if (result <= 0) return result;
|
||||
}
|
||||
if (!bio->initialized) return queueError(BioLibrary, Uninitialized, -2);
|
||||
const int result = bio->method->puts(bio, source);
|
||||
if (result > 0) bio->bytesWritten += static_cast<std::uint64_t>(result);
|
||||
return callback ? static_cast<int>(callback(bio, 0x84, source, 0, 0, result)) : result;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,271 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include "prx/libSceLibreSsl/include/Cmac.hpp"
|
||||
#include "prx/libSceLibreSsl/include/Error.hpp"
|
||||
#include "prx/libc/include/General.hpp"
|
||||
#include <algorithm>
|
||||
#include <bit>
|
||||
#include <cstring>
|
||||
#include <stdexcept>
|
||||
#include <mbedtls/aes.h>
|
||||
|
||||
struct LibreSslCmac {
|
||||
LibreSslCipherContext cipherContext;
|
||||
unsigned char firstSubkey[32];
|
||||
unsigned char secondSubkey[32];
|
||||
unsigned char previousBlock[32];
|
||||
unsigned char lastBlock[32];
|
||||
std::int32_t lastLength;
|
||||
};
|
||||
|
||||
static_assert(sizeof(LibreSslCmac) == 304);
|
||||
|
||||
namespace {
|
||||
|
||||
void Cleanse(void* data, std::size_t length) {
|
||||
auto* bytes = static_cast<volatile unsigned char*>(data);
|
||||
while (length-- != 0) *bytes++ = 0;
|
||||
}
|
||||
|
||||
int Error(int reason) {
|
||||
LibreSslPutError_nid_no_patch(6, 0xfff, reason, __FILE__, __LINE__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
void RequireContext(const LibreSslCmac* context) {
|
||||
if (context == nullptr) throw std::invalid_argument("CMAC: null context");
|
||||
}
|
||||
|
||||
int Cleanup(LibreSslCipherContext& context) {
|
||||
if (context.engine != nullptr) NotImplemented_nid_no_patch(__func__);
|
||||
if (context.cipher != nullptr) {
|
||||
if (context.cipher->cleanup != nullptr && !context.cipher->cleanup(&context)) return 0;
|
||||
if (context.cipherData != nullptr) Cleanse(context.cipherData, context.cipher->contextSize);
|
||||
}
|
||||
free_nid_postfix(context.cipherData);
|
||||
Cleanse(&context, sizeof(context));
|
||||
return 1;
|
||||
}
|
||||
|
||||
int Control(LibreSslCipherContext& context, int command, int argument) {
|
||||
if (context.cipher == nullptr) return Error(131);
|
||||
if (context.cipher->control == nullptr) return Error(132);
|
||||
const int result = context.cipher->control(&context, command, argument, nullptr);
|
||||
return result == -1 ? Error(133) : result;
|
||||
}
|
||||
|
||||
int InitializeCipher(LibreSslCipherContext& context, const LibreSslCipher* cipher, const unsigned char* key, const unsigned char* iv, void* engine) {
|
||||
context.encrypt = 1;
|
||||
if (context.engine != nullptr || (cipher != nullptr && engine != nullptr)) NotImplemented_nid_no_patch(__func__);
|
||||
if (cipher != nullptr) {
|
||||
if (cipher->contextSize < 0 || cipher->ivLength < 0) throw std::invalid_argument("CMAC: invalid cipher dimensions");
|
||||
if (context.cipher != nullptr) {
|
||||
const auto flags = context.flags;
|
||||
Cleanup(context);
|
||||
context.encrypt = 1;
|
||||
context.flags = flags;
|
||||
}
|
||||
context.cipher = cipher;
|
||||
context.cipherData = nullptr;
|
||||
if (cipher->contextSize != 0) {
|
||||
context.cipherData = calloc_nid_postfix(1, cipher->contextSize);
|
||||
if (context.cipherData == nullptr) return Error(65);
|
||||
}
|
||||
context.keyLength = cipher->keyLength;
|
||||
context.flags &= 1;
|
||||
if ((cipher->flags & 0x40) != 0 && !Control(context, 0, 0)) return Error(134);
|
||||
}
|
||||
if (context.cipher == nullptr) return Error(131);
|
||||
const auto& method = *context.cipher;
|
||||
if (method.blockSize != 1 && method.blockSize != 8 && method.blockSize != 16) return Error(136);
|
||||
const auto mode = method.flags & 0xf0007;
|
||||
if ((context.flags & 1) == 0 && mode == 0x10002) return Error(170);
|
||||
if ((method.flags & 0x10) == 0) {
|
||||
if (mode == 2 || mode == 3 || mode == 4 || mode == 5) {
|
||||
if (method.ivLength > 16) return Error(102);
|
||||
if (mode != 2) context.position = 0;
|
||||
if (mode == 5) {
|
||||
if (iv != nullptr) std::memcpy(context.iv, iv, method.ivLength);
|
||||
} else {
|
||||
if (iv != nullptr) std::memcpy(context.originalIv, iv, method.ivLength);
|
||||
std::memcpy(context.iv, context.originalIv, method.ivLength);
|
||||
}
|
||||
} else if (mode != 0 && mode != 1) {
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
if (key != nullptr || (method.flags & 0x20) != 0) {
|
||||
if (method.init == nullptr) NotImplemented_nid_no_patch(__func__);
|
||||
if (!method.init(&context, key, iv, 1)) return 0;
|
||||
}
|
||||
context.bufferLength = 0;
|
||||
context.finalUsed = 0;
|
||||
context.blockMask = method.blockSize - 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int SetKeyLength(LibreSslCipherContext& context, std::size_t length) {
|
||||
const int narrowed = std::bit_cast<std::int32_t>(static_cast<std::uint32_t>(length));
|
||||
if ((context.cipher->flags & 0x80) != 0) return Control(context, 1, narrowed);
|
||||
if (context.keyLength == narrowed) return 1;
|
||||
if (narrowed > 0 && (context.cipher->flags & 8) != 0) {
|
||||
context.keyLength = narrowed;
|
||||
return 1;
|
||||
}
|
||||
return Error(130);
|
||||
}
|
||||
|
||||
int EncryptBlock(LibreSslCmac& context, unsigned char* output, const unsigned char* input) {
|
||||
if (context.cipherContext.cipher->cipher == nullptr) NotImplemented_nid_no_patch(__func__);
|
||||
return context.cipherContext.cipher->cipher(&context.cipherContext, output, input, context.cipherContext.cipher->blockSize);
|
||||
}
|
||||
|
||||
void DoubleBlock(unsigned char* output, const unsigned char* input, int length) {
|
||||
unsigned char carry = 0;
|
||||
for (int index = length; index-- > 0;) {
|
||||
const auto value = input[index];
|
||||
output[index] = static_cast<unsigned char>((value << 1) | carry);
|
||||
carry = value >> 7;
|
||||
}
|
||||
output[length - 1] ^= static_cast<unsigned char>((length == 16 ? 0x87 : 0x1b) & -static_cast<int>(carry));
|
||||
}
|
||||
|
||||
int APS5_VABI AesInit(LibreSslCipherContext* context, const unsigned char* key, const unsigned char*, int encrypt) {
|
||||
if (key == nullptr || context->cipherData == nullptr) throw std::invalid_argument("CMAC AES: missing key storage");
|
||||
if (context->keyLength != 16 && context->keyLength != 24 && context->keyLength != 32) return Error(143);
|
||||
auto* aes = static_cast<mbedtls_aes_context*>(context->cipherData);
|
||||
mbedtls_aes_init(aes);
|
||||
const int result = encrypt ? mbedtls_aes_setkey_enc(aes, key, context->keyLength * 8) : mbedtls_aes_setkey_dec(aes, key, context->keyLength * 8);
|
||||
if (result != 0) throw std::runtime_error("CMAC AES: backend key setup failed");
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI AesCipher(LibreSslCipherContext* context, unsigned char* output, const unsigned char* input, std::size_t length) {
|
||||
if (context->cipherData == nullptr || (length != 0 && (output == nullptr || input == nullptr))) throw std::invalid_argument("CMAC AES: null data");
|
||||
if (length == 0) return 1;
|
||||
auto* aes = static_cast<mbedtls_aes_context*>(context->cipherData);
|
||||
const int mode = context->encrypt ? MBEDTLS_AES_ENCRYPT : MBEDTLS_AES_DECRYPT;
|
||||
const auto complete = length - length % 16;
|
||||
if (complete != 0 && mbedtls_aes_crypt_cbc(aes, mode, complete, context->iv, input, output) != 0)
|
||||
throw std::runtime_error("CMAC AES: backend encryption failed");
|
||||
const auto remaining = length - complete;
|
||||
if (remaining == 0) return 1;
|
||||
input += complete;
|
||||
output += complete;
|
||||
unsigned char block[16];
|
||||
unsigned char nextIv[16];
|
||||
if (context->encrypt) {
|
||||
std::memcpy(block, context->iv, 16);
|
||||
for (std::size_t index = 0; index < remaining; ++index) block[index] ^= input[index];
|
||||
if (mbedtls_aes_crypt_ecb(aes, mode, block, output) != 0) throw std::runtime_error("CMAC AES: backend encryption failed");
|
||||
std::memcpy(context->iv, output, 16);
|
||||
} else {
|
||||
std::memcpy(nextIv, input, 16);
|
||||
if (mbedtls_aes_crypt_ecb(aes, mode, input, block) != 0) throw std::runtime_error("CMAC AES: backend decryption failed");
|
||||
for (std::size_t index = 0; index < remaining; ++index) output[index] = block[index] ^ context->iv[index];
|
||||
std::memcpy(context->iv, nextIv, 16);
|
||||
}
|
||||
Cleanse(block, sizeof(block));
|
||||
Cleanse(nextIv, sizeof(nextIv));
|
||||
return 1;
|
||||
}
|
||||
|
||||
const LibreSslCipher aes128{419, 16, 16, 16, 0x5002, AesInit, AesCipher, nullptr, sizeof(mbedtls_aes_context), nullptr, nullptr, nullptr, nullptr};
|
||||
const LibreSslCipher aes192{423, 16, 24, 16, 0x5002, AesInit, AesCipher, nullptr, sizeof(mbedtls_aes_context), nullptr, nullptr, nullptr, nullptr};
|
||||
const LibreSslCipher aes256{427, 16, 32, 16, 0x5002, AesInit, AesCipher, nullptr, sizeof(mbedtls_aes_context), nullptr, nullptr, nullptr, nullptr};
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
LibreSslCmac* APS5_VABI CMAC_CTX_new_nid_postfix() {
|
||||
auto* context = static_cast<LibreSslCmac*>(malloc_nid_postfix(sizeof(LibreSslCmac)));
|
||||
if (context != nullptr) {
|
||||
context->cipherContext = {};
|
||||
context->lastLength = -1;
|
||||
}
|
||||
return context;
|
||||
}
|
||||
|
||||
void APS5_VABI CMAC_CTX_free_nid_postfix(LibreSslCmac* context) {
|
||||
if (context == nullptr) return;
|
||||
Cleanup(context->cipherContext);
|
||||
Cleanse(context, sizeof(*context));
|
||||
free_nid_postfix(context);
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Init_nid_postfix(LibreSslCmac* context, const void* key, std::size_t keyLength, const LibreSslCipher* cipher, void* engine) {
|
||||
RequireContext(context);
|
||||
const unsigned char zero[32]{};
|
||||
if (key == nullptr && keyLength == 0 && cipher == nullptr && engine == nullptr) {
|
||||
if (context->lastLength == -1) return 0;
|
||||
if (!InitializeCipher(context->cipherContext, nullptr, nullptr, zero, nullptr)) return 0;
|
||||
std::memset(context->previousBlock, 0, context->cipherContext.cipher->blockSize);
|
||||
context->lastLength = 0;
|
||||
return 1;
|
||||
}
|
||||
if (cipher != nullptr && !InitializeCipher(context->cipherContext, cipher, nullptr, nullptr, engine)) return 0;
|
||||
if (key == nullptr) return 1;
|
||||
if (context->cipherContext.cipher == nullptr) return 0;
|
||||
if (!SetKeyLength(context->cipherContext, keyLength)) return 0;
|
||||
if (!InitializeCipher(context->cipherContext, nullptr, static_cast<const unsigned char*>(key), zero, nullptr)) return 0;
|
||||
if (!EncryptBlock(*context, context->previousBlock, zero)) return 0;
|
||||
const auto blockSize = context->cipherContext.cipher->blockSize;
|
||||
DoubleBlock(context->firstSubkey, context->previousBlock, blockSize);
|
||||
DoubleBlock(context->secondSubkey, context->firstSubkey, blockSize);
|
||||
Cleanse(context->previousBlock, blockSize);
|
||||
if (!InitializeCipher(context->cipherContext, nullptr, nullptr, zero, nullptr)) return 0;
|
||||
context->lastLength = 0;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Update_nid_postfix(LibreSslCmac* context, const void* data, std::size_t length) {
|
||||
RequireContext(context);
|
||||
if (context->lastLength == -1) return 0;
|
||||
if (length == 0) return 1;
|
||||
if (data == nullptr) throw std::invalid_argument("CMAC_Update: null input");
|
||||
const auto* bytes = static_cast<const unsigned char*>(data);
|
||||
const auto blockSize = context->cipherContext.cipher->blockSize;
|
||||
if (context->lastLength != 0) {
|
||||
const auto take = std::min(length, static_cast<std::size_t>(blockSize - context->lastLength));
|
||||
std::memcpy(context->lastBlock + context->lastLength, bytes, take);
|
||||
context->lastLength += static_cast<std::int32_t>(take);
|
||||
bytes += take;
|
||||
length -= take;
|
||||
if (length == 0) return 1;
|
||||
if (!EncryptBlock(*context, context->previousBlock, context->lastBlock)) return 0;
|
||||
}
|
||||
while (length > static_cast<std::size_t>(blockSize)) {
|
||||
if (!EncryptBlock(*context, context->previousBlock, bytes)) return 0;
|
||||
bytes += blockSize;
|
||||
length -= blockSize;
|
||||
}
|
||||
std::memcpy(context->lastBlock, bytes, length);
|
||||
context->lastLength = static_cast<std::int32_t>(length);
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI CMAC_Final_nid_postfix(LibreSslCmac* context, unsigned char* output, std::size_t* length) {
|
||||
RequireContext(context);
|
||||
if (context->lastLength == -1) return 0;
|
||||
if (length == nullptr) throw std::invalid_argument("CMAC_Final: null output length");
|
||||
const auto blockSize = context->cipherContext.cipher->blockSize;
|
||||
*length = blockSize;
|
||||
if (output == nullptr) return 1;
|
||||
const bool full = context->lastLength == blockSize;
|
||||
if (!full) {
|
||||
std::memset(context->lastBlock + context->lastLength, 0, blockSize - context->lastLength);
|
||||
context->lastBlock[context->lastLength] = 0x80;
|
||||
}
|
||||
const auto* subkey = full ? context->firstSubkey : context->secondSubkey;
|
||||
for (int index = 0; index < blockSize; ++index) output[index] = context->lastBlock[index] ^ subkey[index];
|
||||
if (EncryptBlock(*context, output, output)) return 1;
|
||||
Cleanse(output, blockSize);
|
||||
return 0;
|
||||
}
|
||||
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_128_cbc_nid_postfix() { return &aes128; }
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_192_cbc_nid_postfix() { return &aes192; }
|
||||
const LibreSslCipher* APS5_VABI EVP_aes_256_cbc_nid_postfix() { return &aes256; }
|
||||
|
||||
}
|
||||
@@ -0,0 +1,564 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libc/include/Api.hpp"
|
||||
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <atomic>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <filesystem>
|
||||
#include <limits>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <thread>
|
||||
#include <vector>
|
||||
|
||||
#ifdef ANYPS5_BIO_DYNAMIC
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
#endif
|
||||
|
||||
using LibreSsl::Bio;
|
||||
using LibreSsl::BioBuffer;
|
||||
using LibreSsl::BioMethod;
|
||||
|
||||
|
||||
#ifdef ANYPS5_BIO_DYNAMIC
|
||||
namespace {
|
||||
decltype(&BIO_s_mem_nid_postfix) bioMemoryMethod;
|
||||
decltype(&BIO_new_nid_postfix) bioNew;
|
||||
decltype(&BIO_new_mem_buf_nid_postfix) bioNewBuffer;
|
||||
decltype(&BIO_free_nid_postfix) bioFree;
|
||||
decltype(&BIO_ctrl_nid_postfix) bioControl;
|
||||
decltype(&BIO_write_nid_postfix) bioWrite;
|
||||
decltype(&BIO_puts_nid_postfix) bioPuts;
|
||||
decltype(&calloc_nid_postfix) guestCalloc;
|
||||
decltype(&free_nid_postfix) guestFree;
|
||||
decltype(&ApplicationHeapRegister_nid_no_patch) registerHeap;
|
||||
decltype(&ERR_clear_error_nid_postfix) clearError;
|
||||
decltype(&ERR_peek_error_nid_postfix) peekError;
|
||||
decltype(&ERR_get_error_nid_postfix) getError;
|
||||
|
||||
void* Load(const std::filesystem::path& path) {
|
||||
#ifdef _WIN32
|
||||
auto* module = LoadLibraryExW(path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
#else
|
||||
auto* module = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
||||
#endif
|
||||
if (!module) throw std::runtime_error("failed to load patched BIO dependency: " + path.string());
|
||||
return module;
|
||||
}
|
||||
|
||||
template<class TFunction>
|
||||
void Resolve(TFunction& function, void* module, const char* name) {
|
||||
#ifdef _WIN32
|
||||
function = reinterpret_cast<TFunction>(GetProcAddress(static_cast<HMODULE>(module), name));
|
||||
#else
|
||||
function = reinterpret_cast<TFunction>(dlsym(module, name));
|
||||
#endif
|
||||
if (!function) throw std::runtime_error(std::string("missing patched BIO dependency: ") + name);
|
||||
}
|
||||
|
||||
void InitializePatched(const std::filesystem::path& directory) {
|
||||
void* libc = Load(directory / "libc.prx");
|
||||
void* bio = Load(directory / "libSceLibreSsl.prx");
|
||||
Resolve(bioMemoryMethod, bio, "R86sXhW699g");
|
||||
Resolve(bioNew, bio, "ARlY6zhSacE");
|
||||
Resolve(bioNewBuffer, bio, "GMzjS9jvyDQ");
|
||||
Resolve(bioFree, bio, "lpJ3zhgftCg");
|
||||
Resolve(bioControl, bio, "RHpq9hQfTj8");
|
||||
Resolve(bioWrite, bio, "nH7XM+HhXWc");
|
||||
Resolve(bioPuts, bio, "eVcWmVlimPA");
|
||||
Resolve(guestCalloc, libc, "2X5agFjKxMc");
|
||||
Resolve(guestFree, libc, "tIhsqj0qsFE");
|
||||
Resolve(registerHeap, libc, "ApplicationHeapRegister_nid_no_patch");
|
||||
Resolve(clearError, bio, "sieQ--PINWg");
|
||||
Resolve(peekError, bio, "Aytjey6FPTU");
|
||||
Resolve(getError, bio, "DJg+qWX1rwY");
|
||||
}
|
||||
} // namespace
|
||||
|
||||
#define BIO_s_mem_nid_postfix bioMemoryMethod
|
||||
#define BIO_new_nid_postfix bioNew
|
||||
#define BIO_new_mem_buf_nid_postfix bioNewBuffer
|
||||
#define BIO_free_nid_postfix bioFree
|
||||
#define BIO_ctrl_nid_postfix bioControl
|
||||
#define BIO_write_nid_postfix bioWrite
|
||||
#define BIO_puts_nid_postfix bioPuts
|
||||
#define calloc_nid_postfix guestCalloc
|
||||
#define free_nid_postfix guestFree
|
||||
#define ApplicationHeapRegister_nid_no_patch registerHeap
|
||||
#define ERR_clear_error_nid_postfix clearError
|
||||
#define ERR_peek_error_nid_postfix peekError
|
||||
#define ERR_get_error_nid_postfix getError
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (!condition) {
|
||||
std::fprintf(stderr, "%s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
}
|
||||
|
||||
int allocationsUntilFailure = -1;
|
||||
std::atomic<int> allocations{0};
|
||||
void* wipePointer = nullptr;
|
||||
std::size_t wipeSize = 0;
|
||||
|
||||
void* APS5_VABI Allocate(std::size_t) { throw std::logic_error("unexpected nonzeroed BIO allocation"); }
|
||||
void* APS5_VABI Reallocate(void*, std::size_t) { throw std::logic_error("unexpected BIO reallocation"); }
|
||||
void* APS5_VABI Align(std::size_t, std::size_t) { throw std::logic_error("unexpected BIO aligned allocation"); }
|
||||
void* APS5_VABI Realign(void*, std::size_t, std::size_t) { throw std::logic_error("unexpected BIO realignment"); }
|
||||
int APS5_VABI PosixAlign(void**, std::size_t, std::size_t) { throw std::logic_error("unexpected BIO aligned allocation"); }
|
||||
|
||||
void* APS5_VABI Calloc(std::size_t count, std::size_t size) {
|
||||
if (allocationsUntilFailure == 0) return nullptr;
|
||||
if (allocationsUntilFailure > 0) --allocationsUntilFailure;
|
||||
void* result = std::calloc(count, size);
|
||||
if (result) ++allocations;
|
||||
return result;
|
||||
}
|
||||
|
||||
void APS5_VABI Free(void* pointer) {
|
||||
if (pointer && pointer == wipePointer) {
|
||||
for (std::size_t i = 0; i < wipeSize; ++i) Require(static_cast<unsigned char*>(pointer)[i] == 0, "freed buffer was not wiped");
|
||||
wipePointer = nullptr;
|
||||
}
|
||||
if (pointer) --allocations;
|
||||
std::free(pointer);
|
||||
}
|
||||
|
||||
void InitializeHeap() {
|
||||
const std::array<void*, 10> api{
|
||||
reinterpret_cast<void*>(Allocate), reinterpret_cast<void*>(Free), reinterpret_cast<void*>(Calloc),
|
||||
reinterpret_cast<void*>(Reallocate), reinterpret_cast<void*>(Align), reinterpret_cast<void*>(Realign),
|
||||
reinterpret_cast<void*>(PosixAlign)};
|
||||
ApplicationHeapRegister_nid_no_patch(api.data());
|
||||
}
|
||||
|
||||
template<class TFunction>
|
||||
void RequireUnsupported(TFunction function) {
|
||||
bool threw = false;
|
||||
try {
|
||||
function();
|
||||
} catch (const std::runtime_error&) {
|
||||
threw = true;
|
||||
}
|
||||
Require(threw, "unsupported BIO path did not throw");
|
||||
}
|
||||
|
||||
void RequireError(std::uint64_t expected) {
|
||||
Require(ERR_peek_error_nid_postfix() == expected, "BIO error code or ordering");
|
||||
Require(ERR_get_error_nid_postfix() == expected, "consume BIO error");
|
||||
Require(ERR_get_error_nid_postfix() == 0, "unexpected extra BIO error");
|
||||
}
|
||||
|
||||
BioBuffer* Buffer(Bio* bio) {
|
||||
BioBuffer* buffer = nullptr;
|
||||
Require(BIO_ctrl_nid_postfix(bio, 115, 0, &buffer) == 1 && buffer, "get buffer");
|
||||
return buffer;
|
||||
}
|
||||
|
||||
void RequireData(Bio* bio, const std::string& expected) {
|
||||
char* bytes = nullptr;
|
||||
Require(BIO_ctrl_nid_postfix(bio, 3, 0, &bytes) == static_cast<std::int64_t>(expected.size()), "buffer data length");
|
||||
Require(expected.empty() || (bytes && std::memcmp(bytes, expected.data(), expected.size()) == 0), "buffer data bytes");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 10, 0, nullptr) == static_cast<std::int64_t>(expected.size()), "pending bytes");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 2, 0, nullptr) == expected.empty(), "EOF flag");
|
||||
}
|
||||
|
||||
void TestWritable() {
|
||||
const auto* method = BIO_s_mem_nid_postfix();
|
||||
Require(method && method == BIO_s_mem_nid_postfix(), "stable method pointer");
|
||||
Require(method->type == 0x401 && std::strcmp(method->name, "memory buffer") == 0, "method identity");
|
||||
Require(method->write && method->read && method->puts && method->gets && method->control && method->create && method->destroy, "method callbacks");
|
||||
Require(method->callbackControl == nullptr, "no callback control method");
|
||||
Bio* bio = BIO_new_nid_postfix(method);
|
||||
Require(bio && bio->method == method && bio->initialized == 1 && bio->references == 1, "new BIO state");
|
||||
Require(!bio->callback && !bio->callbackArgument && !bio->flags && !bio->retryReason && !bio->next && !bio->previous && !bio->extraData, "new BIO zero fields");
|
||||
Require(bio->number == -1 && bio->bytesRead == 0 && bio->bytesWritten == 0, "new BIO counters");
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 7) == -1, "null write reaches memory method");
|
||||
RequireError(0x20fff073);
|
||||
Require(BIO_write_nid_postfix(bio, "x", 0) == 0, "zero-length owned write");
|
||||
RequireError(0);
|
||||
RequireData(bio, "");
|
||||
auto* buffer = Buffer(bio);
|
||||
Require(buffer->capacity == 0 && buffer->data == nullptr, "new buffer storage");
|
||||
char output[16]{};
|
||||
Require(method->read(bio, output, sizeof(output)) == -1 && (bio->flags & 0xf) == 9, "empty writable BIO retry");
|
||||
Require(BIO_write_nid_postfix(bio, "abc", 3) == 3 && (bio->flags & 0xf) == 0, "write clears retry flags");
|
||||
RequireData(bio, "abc");
|
||||
Require(buffer->capacity == 8 && buffer->data[3] == 0, "first buffer expansion");
|
||||
std::memset(buffer->data + buffer->length, 0xa5, buffer->capacity - buffer->length);
|
||||
wipePointer = buffer->data;
|
||||
wipeSize = buffer->capacity;
|
||||
Require(BIO_write_nid_postfix(bio, "0123456789", 10) == 10, "second write");
|
||||
Require(wipePointer == nullptr, "growth frees the wiped allocation");
|
||||
Require(buffer->capacity == 20 && buffer->data[13] == 0, "second buffer expansion");
|
||||
RequireData(bio, "abc0123456789");
|
||||
Require(method->read(bio, output, 4) == 4 && std::memcmp(output, "abc0", 4) == 0, "partial memory read");
|
||||
RequireData(bio, "123456789");
|
||||
Require(BIO_puts_nid_postfix(bio, "xy") == 2 && bio->bytesWritten == 15, "puts and byte accounting");
|
||||
RequireData(bio, "123456789xy");
|
||||
Require(method->read(bio, nullptr, 3) == 3, "null raw read reports available count");
|
||||
RequireData(bio, "123456789xy");
|
||||
Require(method->read(bio, output, -3) == -3, "negative raw read");
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 5) == -1, "reject null public source");
|
||||
RequireError(0x20fff073);
|
||||
RequireData(bio, "123456789xy");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 11, 0, nullptr) == 1 && BIO_ctrl_nid_postfix(bio, 12, 0, nullptr) == 1, "flush and duplicate controls");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 13, 0, nullptr) == 0, "no pending output");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 6, 0, nullptr) == 0 && BIO_ctrl_nid_postfix(bio, 7, 0, nullptr) == 0, "push and pop controls");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 0x7fffffff, 0, nullptr) == 0, "unknown memory control");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 1, 0, nullptr) == 1, "reset writable BIO");
|
||||
RequireData(bio, "");
|
||||
for (std::size_t i = 0; i < buffer->capacity; ++i) Require(buffer->data[i] == 0, "reset wipes full allocation");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 130, 7, nullptr) == 1, "set EOF return");
|
||||
Require(method->read(bio, output, 1) == 7 && (bio->flags & 0xf) == 9, "configured EOF return and retry");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 130, 0, nullptr) == 1, "set terminal EOF");
|
||||
Require(method->read(bio, output, 1) == 0 && (bio->flags & 0xf) == 0, "terminal EOF clears retry");
|
||||
Require(BIO_puts_nid_postfix(bio, "secret") == 6, "write before destruction");
|
||||
wipePointer = buffer->data;
|
||||
wipeSize = buffer->capacity;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free writable BIO");
|
||||
Require(wipePointer == nullptr, "destruction frees wiped storage");
|
||||
}
|
||||
|
||||
void TestReadOnlyAndLines() {
|
||||
const std::array<char, 8> source{'a', '\0', 'b', '\n', 'c', 'd', '\n', 'e'};
|
||||
Bio* bio = BIO_new_mem_buf_nid_postfix(source.data(), source.size());
|
||||
Require(bio && (bio->flags & 0x200) && bio->number == 0, "read-only BIO state");
|
||||
auto* buffer = Buffer(bio);
|
||||
Require(buffer->data == source.data() && buffer->capacity == source.size(), "read-only buffer is borrowed");
|
||||
char output[16];
|
||||
std::memset(output, 0x55, sizeof(output));
|
||||
Require(bio->method->gets(bio, output, sizeof(output)) == 4 && std::memcmp(output, source.data(), 4) == 0 && output[4] == 0, "line includes embedded NUL and newline");
|
||||
Require(buffer->data == source.data() + 4 && buffer->length == 4, "borrowed buffer advances");
|
||||
Require(bio->method->gets(bio, output, 3) == 2 && std::strcmp(output, "cd") == 0, "line length bound");
|
||||
Require(bio->method->gets(bio, output, 1) == 0 && output[0] == 0 && buffer->length == 2, "one-byte line buffer");
|
||||
Require(bio->method->gets(bio, output, sizeof(output)) == 1 && std::strcmp(output, "\n") == 0, "newline on next read");
|
||||
Require(bio->method->read(bio, output, sizeof(output)) == 1 && output[0] == 'e', "last read-only byte");
|
||||
Require(bio->method->read(bio, output, 1) == 0 && (bio->flags & 0xf) == 0, "read-only EOF");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 1, 0, nullptr) == 1, "read-only reset");
|
||||
Require(buffer->data == source.data() && buffer->length == source.size(), "read-only reset restores original range");
|
||||
Require(BIO_write_nid_postfix(bio, "x", 1) == -1, "reject read-only write");
|
||||
RequireError(0x20fff07e);
|
||||
Require(buffer->data == source.data() && buffer->length == source.size(), "rejected write preserves input");
|
||||
Require(BIO_free_nid_postfix(bio) == 1 && source[0] == 'a' && source[7] == 'e', "borrowed bytes survive free");
|
||||
for (const int size : {-1, -99}) {
|
||||
bio = BIO_new_mem_buf_nid_postfix("ab\0cd", size);
|
||||
RequireData(bio, "ab");
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free string BIO");
|
||||
}
|
||||
bio = BIO_new_mem_buf_nid_postfix("", 0);
|
||||
RequireData(bio, "");
|
||||
output[0] = 'x';
|
||||
Require(bio->method->gets(bio, output, 8) == 0 && output[0] == 0, "empty gets");
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free empty BIO");
|
||||
}
|
||||
|
||||
void TestBufferOwnership() {
|
||||
Bio* bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
Require(BIO_puts_nid_postfix(bio, "retained") == 8, "write retained buffer");
|
||||
auto* retained = Buffer(bio);
|
||||
Require(BIO_ctrl_nid_postfix(bio, 8, 0, nullptr) == 1, "default close flag");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 9, 0, nullptr) == 1 && BIO_ctrl_nid_postfix(bio, 8, 0, nullptr) == 0, "disable close");
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free BIO without its buffer");
|
||||
Require(retained->length == 8 && std::memcmp(retained->data, "retained", 8) == 0, "detached storage remains valid");
|
||||
free_nid_postfix(retained->data);
|
||||
free_nid_postfix(retained);
|
||||
|
||||
bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
Require(BIO_puts_nid_postfix(bio, "old") == 3, "write old buffer");
|
||||
auto* replacement = static_cast<BioBuffer*>(calloc_nid_postfix(1, sizeof(BioBuffer)));
|
||||
replacement->data = static_cast<char*>(calloc_nid_postfix(8, 1));
|
||||
replacement->length = 3;
|
||||
replacement->capacity = 8;
|
||||
std::memcpy(replacement->data, "new", 3);
|
||||
Require(BIO_ctrl_nid_postfix(bio, 114, 0, replacement) == 1 && Buffer(bio) == replacement, "replace buffer");
|
||||
RequireData(bio, "new");
|
||||
Require(BIO_write_nid_postfix(bio, "!", 1) == 1, "write replacement buffer");
|
||||
RequireData(bio, "new!");
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free BIO retaining replacement");
|
||||
Require(replacement->length == 4 && std::memcmp(replacement->data, "new!", 4) == 0, "caller owns replacement");
|
||||
free_nid_postfix(replacement->data);
|
||||
free_nid_postfix(replacement);
|
||||
|
||||
bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
Require(BIO_ctrl_nid_postfix(bio, 114, 1, nullptr) == 1, "detach without replacement");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 8, 0, nullptr) == 1, "close control without storage");
|
||||
Require(BIO_ctrl_nid_postfix(bio, 11, 0, nullptr) == 1, "flush without storage");
|
||||
void* missing = bio;
|
||||
Require(BIO_ctrl_nid_postfix(bio, 115, 0, &missing) == 1 && missing == nullptr, "query detached storage");
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free detached BIO");
|
||||
}
|
||||
|
||||
void TestInterleavedTransfers() {
|
||||
Bio* bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
std::string pending;
|
||||
std::uint32_t sequence = 0x346de821;
|
||||
std::uint64_t written = 0;
|
||||
for (int step = 0; step < 2000; ++step) {
|
||||
sequence = sequence * 1664525 + 1013904223;
|
||||
const int count = static_cast<int>((sequence >> 16) % 65);
|
||||
if ((sequence & 3) != 0) {
|
||||
std::array<char, 65> input{};
|
||||
for (int i = 0; i < count; ++i) input[i] = static_cast<char>(sequence + i * 37);
|
||||
Require(BIO_write_nid_postfix(bio, input.data(), count) == count, "interleaved write count");
|
||||
pending.append(input.data(), count);
|
||||
written += count;
|
||||
} else {
|
||||
std::array<char, 66> output;
|
||||
output.fill(static_cast<char>(0xa5));
|
||||
const int expected = static_cast<int>(std::min<std::size_t>(count, pending.size()));
|
||||
const int result = bio->method->read(bio, output.data(), count);
|
||||
Require(result == (pending.empty() ? -1 : expected), "interleaved read count");
|
||||
if (expected != 0) Require(std::memcmp(output.data(), pending.data(), expected) == 0, "interleaved read bytes");
|
||||
Require(output[expected] == static_cast<char>(0xa5), "read preserves destination tail");
|
||||
pending.erase(0, expected);
|
||||
}
|
||||
RequireData(bio, pending);
|
||||
Require(bio->bytesWritten == written && bio->bytesRead == 0, "interleaved transfer counters");
|
||||
if (step % 127 == 0) {
|
||||
Require(BIO_ctrl_nid_postfix(bio, 1, 0, nullptr) == 1, "interleaved reset");
|
||||
pending.clear();
|
||||
}
|
||||
}
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free interleaved BIO");
|
||||
}
|
||||
|
||||
struct WriteEvent {
|
||||
int operation;
|
||||
const char* source;
|
||||
int size;
|
||||
std::int64_t result;
|
||||
int flags;
|
||||
std::uint64_t bytesWritten;
|
||||
};
|
||||
|
||||
struct WriteTrace {
|
||||
std::vector<WriteEvent> events;
|
||||
int veto = 0;
|
||||
int postAdjustment = 0;
|
||||
};
|
||||
|
||||
std::int64_t APS5_VABI TraceWrite(Bio* bio, int operation, const char* source, int size, std::int64_t, std::int64_t result) {
|
||||
auto& trace = *reinterpret_cast<WriteTrace*>(bio->callbackArgument);
|
||||
trace.events.push_back({operation, source, size, result, bio->flags, bio->bytesWritten});
|
||||
if (operation == 3 && trace.veto) return trace.veto;
|
||||
return (operation & 0x80) ? result + trace.postAdjustment : result;
|
||||
}
|
||||
|
||||
void TestWriteBoundaries() {
|
||||
const char source[] = "abc";
|
||||
for (const bool readOnly : {false, true}) {
|
||||
Bio* bio = readOnly ? BIO_new_mem_buf_nid_postfix(source, 3) : BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
WriteTrace trace;
|
||||
bio->callback = TraceWrite;
|
||||
bio->callbackArgument = reinterpret_cast<char*>(&trace);
|
||||
for (const int size : {0, 1}) {
|
||||
bio->flags |= 0x4009;
|
||||
const int flags = bio->flags;
|
||||
trace.events.clear();
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, size) == -1, "null source dispatch result");
|
||||
RequireError(0x20fff073);
|
||||
Require(trace.events.size() == 2, "null source has pre/post callbacks");
|
||||
Require(trace.events[0].operation == 3 && trace.events[1].operation == 0x83, "null source callback operations");
|
||||
Require(trace.events[0].source == nullptr && trace.events[1].source == nullptr &&
|
||||
trace.events[0].size == size && trace.events[1].size == size, "null source callback arguments");
|
||||
Require(trace.events[0].result == 1 && trace.events[1].result == -1, "null source callback results");
|
||||
Require(trace.events[0].flags == flags && trace.events[1].flags == flags && bio->flags == flags, "null source preserves retry flags");
|
||||
Require(trace.events[0].bytesWritten == 0 && trace.events[1].bytesWritten == 0 && bio->bytesWritten == 0, "failed write does not count bytes");
|
||||
}
|
||||
bio->flags |= 0x4009;
|
||||
const int flags = bio->flags;
|
||||
trace.events.clear();
|
||||
Require(BIO_write_nid_postfix(bio, source, 0) == (readOnly ? -1 : 0), "zero-length method result");
|
||||
RequireError(readOnly ? 0x20fff07e : 0);
|
||||
Require(trace.events.size() == 2 && trace.events[0].operation == 3 && trace.events[1].operation == 0x83, "zero-length write callback operations");
|
||||
Require(trace.events[0].source == source && trace.events[1].source == source &&
|
||||
trace.events[0].size == 0 && trace.events[1].size == 0, "zero-length callback arguments");
|
||||
Require(trace.events[0].result == 1 && trace.events[1].result == (readOnly ? -1 : 0), "zero-length callback results");
|
||||
Require(trace.events[0].flags == flags && trace.events[1].flags == (readOnly ? flags : flags & ~0xf), "retry flags change during the method");
|
||||
Require(bio->flags == (readOnly ? flags : flags & ~0xf) && bio->bytesWritten == 0, "zero-length retry flags and byte count");
|
||||
RequireData(bio, readOnly ? "abc" : "");
|
||||
trace.events.clear();
|
||||
trace.veto = -7;
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 0) == -7 && trace.events.size() == 1, "null write callback veto prevents method");
|
||||
RequireError(0);
|
||||
trace.veto = 0;
|
||||
trace.postAdjustment = 5;
|
||||
trace.events.clear();
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 0) == 4 && bio->bytesWritten == 0, "post callback can override failure without counting bytes");
|
||||
RequireError(0x20fff073);
|
||||
bio->callback = nullptr;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free boundary BIO");
|
||||
}
|
||||
Bio* bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
WriteTrace trace;
|
||||
bio->callback = TraceWrite;
|
||||
bio->callbackArgument = reinterpret_cast<char*>(&trace);
|
||||
const auto* method = bio->method;
|
||||
bio->method = nullptr;
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 0) == -2 && trace.events.empty(), "method validation precedes callback");
|
||||
RequireError(0x20fff079);
|
||||
bio->method = method;
|
||||
bio->initialized = 0;
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 0) == -2 && trace.events.size() == 1 && trace.events[0].operation == 3, "initialization validation follows pre-callback");
|
||||
RequireError(0x20fff078);
|
||||
bio->initialized = 1;
|
||||
bio->callback = nullptr;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free dispatch-order BIO");
|
||||
}
|
||||
|
||||
struct CallbackState {
|
||||
std::vector<int> events;
|
||||
std::int64_t argument = 0;
|
||||
int veto = 0;
|
||||
};
|
||||
|
||||
std::int64_t APS5_VABI Callback(Bio* bio, int operation, const char*, int, std::int64_t argument, std::int64_t result) {
|
||||
auto& state = *reinterpret_cast<CallbackState*>(bio->callbackArgument);
|
||||
state.events.push_back(operation);
|
||||
state.argument = argument;
|
||||
if (state.veto && !(operation & 0x80)) return state.veto;
|
||||
return (operation & 0x80) ? result + 2 : 1;
|
||||
}
|
||||
|
||||
int APS5_VABI CreateCustom(Bio* bio) {
|
||||
bio->initialized = 1;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI WriteCustom(Bio*, const char*, int count) {
|
||||
return count - 1;
|
||||
}
|
||||
|
||||
std::int64_t APS5_VABI ControlCustom(Bio*, int command, std::int64_t argument, void*) {
|
||||
return argument + command;
|
||||
}
|
||||
|
||||
int APS5_VABI RejectCreate(Bio*) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
void TestDispatchAndReferences() {
|
||||
Bio* bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
CallbackState state;
|
||||
bio->callback = Callback;
|
||||
bio->callbackArgument = reinterpret_cast<char*>(&state);
|
||||
Require(BIO_write_nid_postfix(bio, "abc", 3) == 5 && bio->bytesWritten == 3, "callback changes result after accounting");
|
||||
Require(state.events == std::vector<int>{3, 0x83}, "write callback ordering");
|
||||
state.events.clear();
|
||||
Require(BIO_puts_nid_postfix(bio, "d") == 3 && state.events == std::vector<int>{4, 0x84}, "puts callbacks");
|
||||
state.events.clear();
|
||||
Require(BIO_ctrl_nid_postfix(bio, 10, 0x1234567887654321LL, nullptr) == 6, "control callback result");
|
||||
Require(state.argument == 0x1234567887654321LL && state.events == std::vector<int>{6, 0x86}, "64-bit control argument");
|
||||
state.events.clear();
|
||||
state.veto = -7;
|
||||
Require(BIO_write_nid_postfix(bio, "x", 1) == -7 && state.events == std::vector<int>{3}, "write callback veto");
|
||||
Require(bio->bytesWritten == 4 && static_cast<BioBuffer*>(bio->data)->length == 4, "veto prevents transfer");
|
||||
state.events.clear();
|
||||
Require(BIO_free_nid_postfix(bio) == -7 && state.events == std::vector<int>{1} && bio->references == 0, "free callback veto");
|
||||
bio->callback = nullptr;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free after veto");
|
||||
|
||||
BioMethod custom{0x480, "test", WriteCustom, nullptr, nullptr, nullptr, ControlCustom, CreateCustom, nullptr, nullptr};
|
||||
bio = BIO_new_nid_postfix(&custom);
|
||||
Require(bio && bio->method == &custom, "title-defined method");
|
||||
Require(BIO_write_nid_postfix(bio, "abc", 3) == 2 && bio->bytesWritten == 2, "title-defined writer");
|
||||
Require(BIO_write_nid_postfix(bio, nullptr, 3) == 2 && bio->bytesWritten == 4, "custom method receives null source");
|
||||
Require(BIO_write_nid_postfix(bio, "", 0) == -1 && bio->bytesWritten == 4, "custom method receives zero length");
|
||||
RequireError(0);
|
||||
Require(BIO_ctrl_nid_postfix(bio, 17, 0x1234567800000000LL, nullptr) == 0x1234567800000011LL, "64-bit method result");
|
||||
constexpr int referenceCount = 8;
|
||||
std::atomic_ref(bio->references).store(referenceCount + 1);
|
||||
std::array<std::thread, referenceCount> threads;
|
||||
for (auto& thread : threads) thread = std::thread([bio] { Require(BIO_free_nid_postfix(bio) == 1, "release shared reference"); });
|
||||
for (auto& thread : threads) thread.join();
|
||||
Require(bio->references == 1 && BIO_free_nid_postfix(bio) == 1, "last reference release");
|
||||
custom.create = RejectCreate;
|
||||
Require(BIO_new_nid_postfix(&custom) == nullptr, "custom create can reject allocation");
|
||||
}
|
||||
|
||||
void TestUnsupported() {
|
||||
Require(BIO_free_nid_postfix(nullptr) == 0, "free null");
|
||||
Require(BIO_ctrl_nid_postfix(nullptr, 1, 0, nullptr) == 0, "control null");
|
||||
Require(BIO_write_nid_postfix(nullptr, "a", 1) == 0, "write null BIO");
|
||||
Require(BIO_new_mem_buf_nid_postfix(nullptr, 0) == nullptr, "reject null borrowed input");
|
||||
RequireError(0x20fff073);
|
||||
Require(BIO_puts_nid_postfix(nullptr, "a") == -2, "reject puts without a method");
|
||||
RequireError(0x20fff079);
|
||||
Bio* bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
bio->initialized = 0;
|
||||
Require(BIO_write_nid_postfix(bio, "a", 1) == -2, "reject uninitialized write");
|
||||
RequireError(0x20fff078);
|
||||
bio->initialized = 1;
|
||||
const BioMethod* method = bio->method;
|
||||
bio->method = nullptr;
|
||||
Require(BIO_ctrl_nid_postfix(bio, 1, 0, nullptr) == -2, "reject control without a method");
|
||||
RequireError(0x20fff079);
|
||||
Require(BIO_write_nid_postfix(bio, "a", 1) == -2, "reject write without a method");
|
||||
RequireError(0x20fff079);
|
||||
bio->method = method;
|
||||
auto* buffer = Buffer(bio);
|
||||
buffer->length = 0x5ffffffd;
|
||||
Require(BIO_write_nid_postfix(bio, "a", 1) == -1, "reject oversized growth");
|
||||
RequireError(0x07fff041);
|
||||
buffer->length = 0;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free after unsupported cases");
|
||||
for (const int failAfter : {0, 1}) {
|
||||
allocationsUntilFailure = failAfter;
|
||||
Require(BIO_new_nid_postfix(BIO_s_mem_nid_postfix()) == nullptr, "failed BIO allocation");
|
||||
RequireError(failAfter == 0 ? 0x20fff041 : 0x07fff041);
|
||||
Require(allocations == 0, "failed creation leaked guest heap storage");
|
||||
}
|
||||
allocationsUntilFailure = -1;
|
||||
bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
Require(BIO_puts_nid_postfix(bio, "abc") == 3, "write before growth failure");
|
||||
allocationsUntilFailure = 0;
|
||||
Require(BIO_puts_nid_postfix(bio, "0123456789") == -1, "failed buffer expansion");
|
||||
RequireError(0x07fff041);
|
||||
RequireData(bio, "abc");
|
||||
allocationsUntilFailure = -1;
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free after growth failure");
|
||||
|
||||
bio = BIO_new_nid_postfix(BIO_s_mem_nid_postfix());
|
||||
bio->initialized = 0;
|
||||
Require(BIO_puts_nid_postfix(bio, "a") == -2, "reject uninitialized puts");
|
||||
RequireError(0x20fff078);
|
||||
bio->initialized = 1;
|
||||
Require(bio->method->write(bio, nullptr, 1) == -1, "reject null raw write source");
|
||||
RequireError(0x20fff073);
|
||||
Require(BIO_free_nid_postfix(bio) == 1, "free after method errors");
|
||||
}
|
||||
|
||||
} // namespace
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef ANYPS5_BIO_DYNAMIC
|
||||
Require(argc == 2, "patched BIO test needs the libraries directory");
|
||||
InitializePatched(std::filesystem::absolute(argv[1]));
|
||||
#else
|
||||
static_cast<void>(argc);
|
||||
static_cast<void>(argv);
|
||||
#endif
|
||||
InitializeHeap();
|
||||
ERR_clear_error_nid_postfix();
|
||||
TestWritable();
|
||||
TestReadOnlyAndLines();
|
||||
TestBufferOwnership();
|
||||
TestInterleavedTransfers();
|
||||
TestWriteBoundaries();
|
||||
TestDispatchAndReferences();
|
||||
TestUnsupported();
|
||||
Require(allocations == 0, "BIO tests leaked guest heap storage");
|
||||
Require(ERR_get_error_nid_postfix() == 0, "BIO tests left an unexpected error");
|
||||
}
|
||||
@@ -0,0 +1,204 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include "prx/libc/include/general/VabiMacros.hpp"
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <source_location>
|
||||
#include <string>
|
||||
|
||||
using Bn = Bignum;
|
||||
using BnCtx = BignumContext;
|
||||
|
||||
static_assert(sizeof(Bn) == 24);
|
||||
static_assert(offsetof(Bn, top) == 8);
|
||||
static_assert(offsetof(Bn, neg) == 16);
|
||||
|
||||
static void Require(bool value, int line = __builtin_LINE()) {
|
||||
if (value) return;
|
||||
std::fprintf(stderr, "check failed at line %d\n", line);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
static Bn* FromHex(const char* text) {
|
||||
Bn* value = nullptr;
|
||||
Require(BN_hex2bn_nid_postfix(&value, text) > 0 && value);
|
||||
return value;
|
||||
}
|
||||
|
||||
static std::string Hex(const Bn* value) {
|
||||
unsigned char bytes[64];
|
||||
const int length = BN_bn2bin_nid_postfix(value, bytes);
|
||||
Require(length >= 0 && length <= 64);
|
||||
static constexpr char digits[] = "0123456789ABCDEF";
|
||||
std::string text;
|
||||
for (int i = 0; i < length; i++) {
|
||||
text += digits[bytes[i] >> 4];
|
||||
text += digits[bytes[i] & 15];
|
||||
}
|
||||
while (text.size() > 1 && text[0] == '0') text.erase(0, 1);
|
||||
if (text.empty()) text = "0";
|
||||
return value->neg ? "-" + text : text;
|
||||
}
|
||||
|
||||
static void InitializeHeap() {
|
||||
void* const api[10] {};
|
||||
ApplicationHeapRegister_nid_no_patch(api);
|
||||
}
|
||||
|
||||
int main() {
|
||||
InitializeHeap();
|
||||
Bn* fresh = BN_new_nid_postfix();
|
||||
Require(fresh && fresh->top == 0 && fresh->neg == 0 && BN_num_bits_nid_postfix(fresh) == 0);
|
||||
BN_free_nid_postfix(fresh);
|
||||
BN_free_nid_postfix(nullptr);
|
||||
|
||||
BnCtx* ctx = BN_CTX_new_nid_postfix();
|
||||
Require(ctx);
|
||||
BN_CTX_start_nid_postfix(ctx);
|
||||
BN_CTX_end_nid_postfix(ctx);
|
||||
BN_CTX_free_nid_postfix(ctx);
|
||||
BN_CTX_free_nid_postfix(nullptr);
|
||||
ctx = BN_CTX_new_nid_postfix();
|
||||
Require(ctx);
|
||||
|
||||
{
|
||||
Bn* a = FromHex("FFFFFFFFFFFFFFFF");
|
||||
Bn* b = FromHex("FFFFFFFFFFFFFFFF");
|
||||
Bn* r = BN_new_nid_postfix();
|
||||
Require(BN_mul_nid_postfix(r, a, b, ctx) == 1);
|
||||
Require(Hex(r) == "FFFFFFFFFFFFFFFE0000000000000001");
|
||||
Require(r->top == 2 && r->neg == 0);
|
||||
BN_free_nid_postfix(a);
|
||||
BN_free_nid_postfix(b);
|
||||
BN_free_nid_postfix(r);
|
||||
}
|
||||
{
|
||||
Bn* a = FromHex("0123456789ABCDEFFEDCBA98765432100F1E2D3C4B5A6978");
|
||||
Bn* b = FromHex("00FF00FF00FF00FF00FF00FF00FF00FF00FF");
|
||||
Bn* r = BN_new_nid_postfix();
|
||||
Require(BN_mul_nid_postfix(r, a, b, ctx) == 1);
|
||||
Require(Hex(r) == "12223444566678876665444322210000F0F1CFC09E8F6D55CB34DD56FF79219B43BB52CA61D970E88");
|
||||
Require(BN_mul_nid_postfix(a, a, a, ctx) == 1);
|
||||
Require(Hex(a) == "14B66DC33F6ACDEE9D609EBE253A6424AD884E9A04269A9706601BB096443363E2CE44634913DF9C3B9FAA3D3A840");
|
||||
Bn* c = FromHex("-3");
|
||||
Bn* d = FromHex("5");
|
||||
Require(BN_mul_nid_postfix(d, c, d, ctx) == 1 && Hex(d) == "-F" && d->neg == 1);
|
||||
Require(BN_mul_nid_postfix(c, c, c, ctx) == 1 && Hex(c) == "9" && c->neg == 0);
|
||||
Bn* zero = FromHex("0");
|
||||
Require(BN_mul_nid_postfix(d, d, zero, ctx) == 1 && d->top == 0 && d->neg == 0);
|
||||
BN_free_nid_postfix(a);
|
||||
BN_free_nid_postfix(b);
|
||||
BN_free_nid_postfix(c);
|
||||
BN_free_nid_postfix(d);
|
||||
BN_free_nid_postfix(r);
|
||||
BN_free_nid_postfix(zero);
|
||||
}
|
||||
{
|
||||
const unsigned char bytes[] = {0x00, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09};
|
||||
Bn* value = BN_bin2bn_nid_postfix(bytes, sizeof(bytes), nullptr);
|
||||
Require(value && value->neg == 0 && value->top == 2);
|
||||
Require(Hex(value) == "10203040506070809");
|
||||
unsigned char out[16];
|
||||
std::memset(out, 0xEE, sizeof(out));
|
||||
Require(BN_bn2bin_nid_postfix(value, out) == 9);
|
||||
Require(std::memcmp(out, bytes + 2, 9) == 0 && out[9] == 0xEE);
|
||||
Require(BN_bin2bn_nid_postfix(bytes, 4, value) == value && Hex(value) == "102" && value->top == 1);
|
||||
const unsigned char zeros[] = {0, 0, 0};
|
||||
Require(BN_bin2bn_nid_postfix(zeros, 3, value) == value && value->top == 0);
|
||||
std::memset(out, 0xEE, sizeof(out));
|
||||
Require(BN_bn2bin_nid_postfix(value, out) == 0 && out[0] == 0xEE);
|
||||
Require(BN_bin2bn_nid_postfix(bytes, 0, value) == value && value->top == 0);
|
||||
Require(BN_bin2bn_nid_postfix(bytes, -1, value) == nullptr);
|
||||
BN_free_nid_postfix(value);
|
||||
}
|
||||
{
|
||||
Bn* value = BN_new_nid_postfix();
|
||||
Require(BN_num_bits_nid_postfix(value) == 0);
|
||||
Require(BN_set_word_nid_postfix(value, 1) == 1 && BN_num_bits_nid_postfix(value) == 1);
|
||||
Require(BN_set_word_nid_postfix(value, 0x100) == 1 && BN_num_bits_nid_postfix(value) == 9);
|
||||
Bn* big = FromHex("10000000000000000");
|
||||
Require(BN_num_bits_nid_postfix(big) == 65 && big->top == 2);
|
||||
Require(BN_set_word_nid_postfix(big, 0) == 1 && BN_num_bits_nid_postfix(big) == 0 && big->top == 0 && big->neg == 0);
|
||||
Require(BN_set_word_nid_postfix(value, 0xFFFFFFFFFFFFFFFFull) == 1 && BN_num_bits_nid_postfix(value) == 64);
|
||||
BN_free_nid_postfix(value);
|
||||
BN_free_nid_postfix(big);
|
||||
}
|
||||
{
|
||||
Bn* value = nullptr;
|
||||
Require(BN_hex2bn_nid_postfix(&value, "-1F") == 3 && value);
|
||||
Require(value->neg == 1 && value->top == 1 && value->d[0] == 0x1F);
|
||||
Bn* same = value;
|
||||
Require(BN_hex2bn_nid_postfix(&value, "ABC") == 3 && value == same);
|
||||
Require(value->neg == 0 && value->d[0] == 0xABC);
|
||||
Require(BN_hex2bn_nid_postfix(&value, "FFzz") == 2 && value->d[0] == 0xFF);
|
||||
Require(BN_hex2bn_nid_postfix(&value, "") == 0 && value->d[0] == 0xFF);
|
||||
Require(BN_hex2bn_nid_postfix(&value, "zz") == 0 && BN_hex2bn_nid_postfix(&value, "-") == 0);
|
||||
Require(BN_hex2bn_nid_postfix(&value, "-0") == 2 && value->top == 0 && value->neg == 0);
|
||||
Require(BN_hex2bn_nid_postfix(&value, "deadBEEF0123456789abcdef") == 24);
|
||||
Require(Hex(value) == "DEADBEEF0123456789ABCDEF" && value->top == 2);
|
||||
Require(BN_hex2bn_nid_postfix(nullptr, "12G") == 2);
|
||||
BN_free_nid_postfix(value);
|
||||
}
|
||||
{
|
||||
Bn* value = FromHex("FFFFFFFFFFFFFFFF");
|
||||
Require(BN_add_word_nid_postfix(value, 1) == 1);
|
||||
Require(value->top == 2 && value->d[0] == 0 && value->d[1] == 1 && Hex(value) == "10000000000000000");
|
||||
Require(BN_sub_word_nid_postfix(value, 1) == 1);
|
||||
Require(value->top == 1 && value->d[0] == 0xFFFFFFFFFFFFFFFFull && value->neg == 0);
|
||||
Require(BN_set_word_nid_postfix(value, 3) == 1);
|
||||
Require(BN_sub_word_nid_postfix(value, 5) == 1 && value->neg == 1 && value->top == 1 && value->d[0] == 2);
|
||||
Require(BN_add_word_nid_postfix(value, 2) == 1 && value->top == 0 && value->neg == 0);
|
||||
Require(BN_sub_word_nid_postfix(value, 7) == 1 && value->neg == 1 && Hex(value) == "-7");
|
||||
Require(BN_add_word_nid_postfix(value, 10) == 1 && value->neg == 0 && Hex(value) == "3");
|
||||
Require(BN_sub_word_nid_postfix(value, 0) == 1 && Hex(value) == "3");
|
||||
Bn* wide = FromHex("-10000000000000000");
|
||||
Require(BN_sub_word_nid_postfix(wide, 1) == 1 && wide->neg == 1 && Hex(wide) == "-10000000000000001");
|
||||
Require(BN_add_word_nid_postfix(wide, 2) == 1 && wide->neg == 1 && Hex(wide) == "-FFFFFFFFFFFFFFFF" && wide->top == 1);
|
||||
Bn* chain = FromHex("FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
Require(BN_add_word_nid_postfix(chain, 1) == 1 && chain->top == 3 && Hex(chain) == "100000000000000000000000000000000");
|
||||
Require(BN_sub_word_nid_postfix(chain, 1) == 1 && chain->top == 2 && Hex(chain) == "FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
BN_free_nid_postfix(value);
|
||||
BN_free_nid_postfix(wide);
|
||||
BN_free_nid_postfix(chain);
|
||||
}
|
||||
{
|
||||
Bn* r = BN_new_nid_postfix();
|
||||
Bn* three = FromHex("3");
|
||||
Bn* eleven = FromHex("B");
|
||||
Require(BN_mod_inverse_nid_postfix(r, three, eleven, ctx) == r && Hex(r) == "4");
|
||||
Bn* seven = FromHex("7");
|
||||
Bn* forty = FromHex("28");
|
||||
Require(BN_mod_inverse_nid_postfix(r, seven, forty, ctx) == r && Hex(r) == "17");
|
||||
Bn* four = FromHex("4");
|
||||
Bn* eight = FromHex("8");
|
||||
Require(BN_mod_inverse_nid_postfix(r, four, eight, ctx) == nullptr);
|
||||
Bn* allocated = BN_mod_inverse_nid_postfix(nullptr, three, eleven, ctx);
|
||||
Require(allocated && Hex(allocated) == "4");
|
||||
Bn* minusThree = FromHex("-3");
|
||||
Require(BN_mod_inverse_nid_postfix(r, minusThree, eleven, ctx) == r && Hex(r) == "7");
|
||||
Bn* fifteen = FromHex("F");
|
||||
Require(BN_mod_inverse_nid_postfix(r, fifteen, eleven, ctx) == r && Hex(r) == "3");
|
||||
Bn* inPlace = FromHex("3");
|
||||
Require(BN_mod_inverse_nid_postfix(inPlace, inPlace, eleven, ctx) == inPlace && Hex(inPlace) == "4");
|
||||
|
||||
Bn* prime = FromHex("7FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF");
|
||||
Bn* a = FromHex("123456789ABCDEF0FEDCBA9876543210");
|
||||
Bn* inverse = BN_mod_inverse_nid_postfix(nullptr, a, prime, ctx);
|
||||
Require(inverse && Hex(inverse) == "62CFEFE6EB41053969967BD711C5169F");
|
||||
Bn* product = BN_new_nid_postfix();
|
||||
Require(BN_mul_nid_postfix(product, a, inverse, ctx) == 1);
|
||||
Bn* quotient = FromHex("E0DA4F6BE78BFD74B8C9F95AF178811");
|
||||
Bn* check = BN_new_nid_postfix();
|
||||
Require(BN_mul_nid_postfix(check, quotient, prime, ctx) == 1);
|
||||
Require(BN_add_word_nid_postfix(check, 1) == 1);
|
||||
Require(Hex(check) == Hex(product));
|
||||
|
||||
Bn* all[] = {r, three, eleven, seven, forty, four, eight, allocated, minusThree, fifteen, inPlace, prime, a, inverse, product, quotient, check};
|
||||
for (Bn* value : all) BN_free_nid_postfix(value);
|
||||
}
|
||||
BN_CTX_free_nid_postfix(ctx);
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,444 @@
|
||||
#include "prx/libSceLibreSsl/libSceLibreSsl.hpp"
|
||||
#include "prx/libSceLibreSsl/include/Cmac.hpp"
|
||||
#include "prx/libSceLibreSsl/ErrorQueue.hpp"
|
||||
#include "prx/libc/include/ApplicationHeap.hpp"
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <cstring>
|
||||
#include <memory>
|
||||
#include <stdexcept>
|
||||
#include <string>
|
||||
#include <string_view>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <mbedtls/cipher.h>
|
||||
#include <mbedtls/cmac.h>
|
||||
#include <mbedtls/aes.h>
|
||||
#include <mbedtls/des.h>
|
||||
#ifdef _WIN32
|
||||
#include <windows.h>
|
||||
#else
|
||||
#include <dlfcn.h>
|
||||
#endif
|
||||
|
||||
#ifdef ANYPS5_CMAC_DYNAMIC
|
||||
namespace {
|
||||
decltype(&CMAC_CTX_new_nid_postfix) cmacNew;
|
||||
decltype(&CMAC_CTX_free_nid_postfix) cmacFree;
|
||||
decltype(&CMAC_Init_nid_postfix) cmacInit;
|
||||
decltype(&CMAC_Update_nid_postfix) cmacUpdate;
|
||||
decltype(&CMAC_Final_nid_postfix) cmacFinal;
|
||||
decltype(&EVP_aes_128_cbc_nid_postfix) aes128Method;
|
||||
decltype(&EVP_aes_192_cbc_nid_postfix) aes192Method;
|
||||
decltype(&EVP_aes_256_cbc_nid_postfix) aes256Method;
|
||||
decltype(&ERR_clear_error_nid_postfix) clearError;
|
||||
decltype(&ERR_get_error_nid_postfix) getError;
|
||||
decltype(&ApplicationHeapRegister_nid_no_patch) registerHeap;
|
||||
|
||||
void* Load(const std::string& path) {
|
||||
#ifdef _WIN32
|
||||
auto* library = LoadLibraryExA(path.c_str(), nullptr, LOAD_WITH_ALTERED_SEARCH_PATH);
|
||||
#else
|
||||
auto* library = dlopen(path.c_str(), RTLD_NOW | RTLD_LOCAL);
|
||||
#endif
|
||||
if (library == nullptr) throw std::runtime_error("failed to load patched CMAC dependency: " + path);
|
||||
return library;
|
||||
}
|
||||
|
||||
template<typename TFunction> void Resolve(TFunction& destination, void* library, const char* name) {
|
||||
#ifdef _WIN32
|
||||
destination = reinterpret_cast<TFunction>(GetProcAddress(static_cast<HMODULE>(library), name));
|
||||
#else
|
||||
destination = reinterpret_cast<TFunction>(dlsym(library, name));
|
||||
#endif
|
||||
if (destination == nullptr) throw std::runtime_error(std::string("missing patched CMAC dependency: ") + name);
|
||||
}
|
||||
|
||||
void InitializePatched(const char* directory) {
|
||||
const auto root = std::string(directory) + "/";
|
||||
auto* libc = Load(root + "libc.prx");
|
||||
auto* library = Load(root + "libSceLibreSsl.prx");
|
||||
Resolve(cmacNew, library, "mwJYST4Bb3U");
|
||||
Resolve(cmacFree, library, "cBNGAG1DkXc");
|
||||
Resolve(cmacInit, library, "LATrF+dIZYI");
|
||||
Resolve(cmacUpdate, library, "XpAJ1Ckg-q4");
|
||||
Resolve(cmacFinal, library, "6MM7ng9P+18");
|
||||
Resolve(aes128Method, library, "rV07jlO-+6Q");
|
||||
Resolve(aes192Method, library, "0X5DnA3olsk");
|
||||
Resolve(aes256Method, library, "J44dsZUcK4I");
|
||||
Resolve(clearError, library, "sieQ--PINWg");
|
||||
Resolve(getError, library, "DJg+qWX1rwY");
|
||||
Resolve(registerHeap, libc, "ApplicationHeapRegister_nid_no_patch");
|
||||
}
|
||||
}
|
||||
|
||||
#define CMAC_CTX_new_nid_postfix cmacNew
|
||||
#define CMAC_CTX_free_nid_postfix cmacFree
|
||||
#define CMAC_Init_nid_postfix cmacInit
|
||||
#define CMAC_Update_nid_postfix cmacUpdate
|
||||
#define CMAC_Final_nid_postfix cmacFinal
|
||||
#define EVP_aes_128_cbc_nid_postfix aes128Method
|
||||
#define EVP_aes_192_cbc_nid_postfix aes192Method
|
||||
#define EVP_aes_256_cbc_nid_postfix aes256Method
|
||||
#define ERR_clear_error_nid_postfix clearError
|
||||
#define ERR_get_error_nid_postfix getError
|
||||
#define ApplicationHeapRegister_nid_no_patch registerHeap
|
||||
#endif
|
||||
|
||||
namespace {
|
||||
|
||||
void Require(bool condition, const char* message) {
|
||||
if (condition) return;
|
||||
std::fprintf(stderr, "failed: %s\n", message);
|
||||
std::abort();
|
||||
}
|
||||
|
||||
int allocationsUntilFailure = -1;
|
||||
std::vector<std::pair<void*, std::size_t>> allocations;
|
||||
|
||||
void* APS5_VABI Allocate(std::size_t size) {
|
||||
if (allocationsUntilFailure == 0) return nullptr;
|
||||
if (allocationsUntilFailure > 0) --allocationsUntilFailure;
|
||||
auto* pointer = std::malloc(size);
|
||||
if (pointer != nullptr) allocations.emplace_back(pointer, size);
|
||||
return pointer;
|
||||
}
|
||||
void* APS5_VABI Reallocate(void*, std::size_t) { throw std::logic_error("unexpected CMAC reallocation"); }
|
||||
void* APS5_VABI Align(std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC aligned allocation"); }
|
||||
void* APS5_VABI Realign(void*, std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC realignment"); }
|
||||
int APS5_VABI PosixAlign(void**, std::size_t, std::size_t) { throw std::logic_error("unexpected CMAC aligned allocation"); }
|
||||
|
||||
void* APS5_VABI Calloc(std::size_t count, std::size_t size) {
|
||||
if (allocationsUntilFailure == 0) return nullptr;
|
||||
if (allocationsUntilFailure > 0) --allocationsUntilFailure;
|
||||
auto* pointer = std::calloc(count, size);
|
||||
if (pointer != nullptr) allocations.emplace_back(pointer, count * size);
|
||||
return pointer;
|
||||
}
|
||||
|
||||
void APS5_VABI Free(void* pointer) {
|
||||
if (pointer == nullptr) return;
|
||||
const auto found = std::find_if(allocations.begin(), allocations.end(), [&](const auto& entry) { return entry.first == pointer; });
|
||||
Require(found != allocations.end(), "free a guest-owned allocation");
|
||||
const auto* bytes = static_cast<const unsigned char*>(pointer);
|
||||
Require(std::all_of(bytes, bytes + found->second, [](auto value) { return value == 0; }), "wipe contexts and key storage before freeing");
|
||||
allocations.erase(found);
|
||||
std::free(pointer);
|
||||
}
|
||||
|
||||
void InitializeHeap() {
|
||||
const std::array<void*, 10> api{
|
||||
reinterpret_cast<void*>(Allocate), reinterpret_cast<void*>(Free), reinterpret_cast<void*>(Calloc),
|
||||
reinterpret_cast<void*>(Reallocate), reinterpret_cast<void*>(Align), reinterpret_cast<void*>(Realign),
|
||||
reinterpret_cast<void*>(PosixAlign)};
|
||||
ApplicationHeapRegister_nid_no_patch(api.data());
|
||||
}
|
||||
|
||||
template<typename TAction> void RequireThrows(TAction action) {
|
||||
try { action(); } catch (const std::exception&) { return; }
|
||||
Require(false, "unsupported input must throw");
|
||||
}
|
||||
|
||||
std::vector<unsigned char> Hex(std::string_view text) {
|
||||
Require(text.size() % 2 == 0, "hex fixture has complete bytes");
|
||||
std::vector<unsigned char> bytes;
|
||||
for (std::size_t index = 0; index < text.size(); index += 2) {
|
||||
const auto nibble = [](char value) { return value <= '9' ? value - '0' : value - 'a' + 10; };
|
||||
bytes.push_back(static_cast<unsigned char>((nibble(text[index]) << 4) | nibble(text[index + 1])));
|
||||
}
|
||||
return bytes;
|
||||
}
|
||||
|
||||
using CmacFree = void (APS5_VABI*)(LibreSslCmac*);
|
||||
using Context = std::unique_ptr<LibreSslCmac, CmacFree>;
|
||||
|
||||
Context NewContext() {
|
||||
Context context(CMAC_CTX_new_nid_postfix(), CMAC_CTX_free_nid_postfix);
|
||||
Require(context != nullptr, "allocate CMAC context");
|
||||
return context;
|
||||
}
|
||||
|
||||
std::array<unsigned char, 16> Final(LibreSslCmac* context) {
|
||||
std::array<unsigned char, 18> output;
|
||||
output.fill(0xa5);
|
||||
std::size_t length = 99;
|
||||
Require(CMAC_Final_nid_postfix(context, output.data() + 1, &length) == 1, "finalize CMAC");
|
||||
Require(length == 16 && output.front() == 0xa5 && output.back() == 0xa5, "tag size and output bounds");
|
||||
std::array<unsigned char, 16> result;
|
||||
std::copy_n(output.data() + 1, 16, result.data());
|
||||
return result;
|
||||
}
|
||||
|
||||
const LibreSslCipher* Methods[3];
|
||||
|
||||
void RfcVectors() {
|
||||
const auto key = Hex("2b7e151628aed2a6abf7158809cf4f3c");
|
||||
const auto message = Hex("6bc1bee22e409f96e93d7e117393172aae2d8a571e03ac9c9eb76fac45af8e5130c81c46a35ce411e5fbc1191a0a52eff69f2445df4f9b17ad2b417be66c3710");
|
||||
const std::size_t lengths[] = {0, 16, 40, 64};
|
||||
const char* tags[] = {"bb1d6929e95937287fa37d129b756746", "070a16b46b4d4144f79bdd9dd04a287c", "dfa66747de9ae63030ca32611497c827", "51f0bebf7e3b9d92fc49741779363cfe"};
|
||||
auto context = NewContext();
|
||||
for (std::size_t vector = 0; vector < 4; ++vector) {
|
||||
const auto expected = Hex(tags[vector]);
|
||||
for (std::size_t split = 0; split <= lengths[vector]; ++split) {
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 1, "RFC key initialization");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), split) == 1, "RFC first update");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), nullptr, 0) == 1, "empty update");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data() + split, lengths[vector] - split) == 1, "RFC second update");
|
||||
std::size_t length = 99;
|
||||
Require(CMAC_Final_nid_postfix(context.get(), nullptr, &length) == 1 && length == 16, "length-only final");
|
||||
Require(CMAC_Final_nid_postfix(context.get(), nullptr, &length) == 1 && length == 16, "repeat length query");
|
||||
const auto actual = Final(context.get());
|
||||
if (!std::equal(actual.begin(), actual.end(), expected.begin())) {
|
||||
std::fprintf(stderr, "RFC vector %zu split %zu: ", vector, split);
|
||||
for (auto byte : actual) std::fprintf(stderr, "%02x", byte);
|
||||
std::fputc('\n', stderr);
|
||||
}
|
||||
Require(std::equal(actual.begin(), actual.end(), expected.begin()), "RFC 4493 tag");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Differential() {
|
||||
const mbedtls_cipher_type_t types[] = {MBEDTLS_CIPHER_AES_128_ECB, MBEDTLS_CIPHER_AES_192_ECB, MBEDTLS_CIPHER_AES_256_ECB};
|
||||
std::array<unsigned char, 32> key;
|
||||
std::array<unsigned char, 257> message;
|
||||
for (std::size_t index = 0; index < key.size(); ++index) key[index] = static_cast<unsigned char>(index * 73 + 41);
|
||||
for (std::size_t index = 0; index < message.size(); ++index) message[index] = static_cast<unsigned char>(index * 197 + 17);
|
||||
auto context = NewContext();
|
||||
for (std::size_t method = 0; method < 3; ++method) {
|
||||
const auto* cipher = Methods[method];
|
||||
Require(cipher->nid == 419 + static_cast<int>(method) * 4 && cipher->blockSize == 16 && cipher->keyLength == 16 + static_cast<int>(method) * 8 && cipher->ivLength == 16 && cipher->flags == 0x5002, "AES descriptor metadata");
|
||||
const auto* reference = mbedtls_cipher_info_from_type(types[method]);
|
||||
Require(reference != nullptr, "reference cipher available");
|
||||
for (std::size_t length = 0; length <= message.size(); ++length) {
|
||||
std::array<unsigned char, 16> expected;
|
||||
Require(mbedtls_cipher_cmac(reference, key.data(), cipher->keyLength * 8, message.data(), length, expected.data()) == 0, "reference CMAC");
|
||||
for (const auto chunk : {1u, 7u, 15u, 16u, 17u, 64u, 257u}) {
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), cipher->keyLength, cipher, nullptr) == 1, "differential initialization");
|
||||
for (std::size_t offset = 0; offset < length;) {
|
||||
const auto take = std::min(static_cast<std::size_t>(chunk), length - offset);
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data() + offset, take) == 1, "differential update");
|
||||
offset += take;
|
||||
}
|
||||
Require(Final(context.get()) == expected, "CMAC agrees with independent backend");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void Lifecycle() {
|
||||
auto context = NewContext();
|
||||
const std::array<unsigned char, 32> key{};
|
||||
const std::array<unsigned char, 19> message{};
|
||||
std::array<unsigned char, 16> output;
|
||||
output.fill(0xa5);
|
||||
std::size_t length = 99;
|
||||
Require(CMAC_Update_nid_postfix(context.get(), nullptr, 1) == 0, "uninitialized update");
|
||||
Require(CMAC_Final_nid_postfix(context.get(), output.data(), &length) == 0 && length == 99 && output[0] == 0xa5, "uninitialized final preserves outputs");
|
||||
Require(CMAC_Final_nid_postfix(context.get(), nullptr, nullptr) == 0, "uninitialized final precedes null output length");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 0, "uninitialized restart");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 16, nullptr, nullptr) == 0, "key without cipher");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 1, nullptr, nullptr) == 1, "nonzero key length without a key is not restart");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, Methods[0], nullptr) == 1, "stage cipher selection");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), 1) == 0, "cipher-only context has no key");
|
||||
ERR_clear_error_nid_postfix();
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 15, nullptr, nullptr) == 0, "reject invalid key length");
|
||||
Require(ERR_get_error_nid_postfix() == 0x06fff082, "invalid key length publishes EVP error");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 16, nullptr, nullptr) == 1, "finish staged initialization");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1, "first message");
|
||||
const auto expected = Final(context.get());
|
||||
const auto repeated = Final(context.get());
|
||||
Require(repeated != expected, "final advances the CBC state");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 1, "restart reuses the key");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == expected, "restart resets state");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), (std::size_t{1} << 32) + 16, nullptr, nullptr) == 1, "upstream key length narrows to int");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == expected, "narrowed key length tag");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 24, Methods[1], nullptr) == 1, "switch AES key size");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), 32, Methods[2], nullptr) == 1, "switch AES key size again");
|
||||
RequireThrows([&] { CMAC_Init_nid_postfix(nullptr, key.data(), 16, Methods[0], nullptr); });
|
||||
RequireThrows([&] { CMAC_Update_nid_postfix(nullptr, nullptr, 0); });
|
||||
RequireThrows([&] { CMAC_Final_nid_postfix(nullptr, output.data(), &length); });
|
||||
RequireThrows([&] { CMAC_Update_nid_postfix(context.get(), nullptr, 1); });
|
||||
RequireThrows([&] { CMAC_Final_nid_postfix(context.get(), output.data(), nullptr); });
|
||||
CMAC_CTX_free_nid_postfix(nullptr);
|
||||
}
|
||||
|
||||
void AllocationFailures() {
|
||||
allocationsUntilFailure = 0;
|
||||
Require(CMAC_CTX_new_nid_postfix() == nullptr, "context allocation failure");
|
||||
allocationsUntilFailure = 1;
|
||||
auto context = NewContext();
|
||||
const std::array<unsigned char, 16> key{};
|
||||
ERR_clear_error_nid_postfix();
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 0, "cipher allocation failure");
|
||||
Require(ERR_get_error_nid_postfix() == 0x06fff041, "cipher allocation failure publishes EVP error");
|
||||
allocationsUntilFailure = -1;
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), Methods[0], nullptr) == 1, "recover from failed allocation");
|
||||
}
|
||||
|
||||
void CipherCallbacks() {
|
||||
std::array<unsigned char, 32> key{};
|
||||
std::array<unsigned char, 16> iv;
|
||||
for (std::size_t index = 0; index < iv.size(); ++index) iv[index] = static_cast<unsigned char>(index + 1);
|
||||
for (const auto* method : Methods) {
|
||||
for (std::size_t length = 0; length <= 48; ++length) {
|
||||
const auto padded = (length + 15) / 16 * 16;
|
||||
std::vector<unsigned char> input(padded);
|
||||
for (std::size_t index = 0; index < length; ++index) input[index] = static_cast<unsigned char>(index * 31 + 17);
|
||||
std::vector<unsigned char> expected(padded);
|
||||
auto referenceIv = iv;
|
||||
mbedtls_aes_context reference;
|
||||
mbedtls_aes_init(&reference);
|
||||
Require(mbedtls_aes_setkey_enc(&reference, key.data(), method->keyLength * 8) == 0, "reference AES key");
|
||||
Require(mbedtls_aes_crypt_cbc(&reference, MBEDTLS_AES_ENCRYPT, padded, referenceIv.data(), input.data(), expected.data()) == 0, "reference CBC");
|
||||
mbedtls_aes_free(&reference);
|
||||
std::vector<unsigned char> storage(method->contextSize);
|
||||
LibreSslCipherContext context{};
|
||||
context.cipher = method;
|
||||
context.cipherData = storage.data();
|
||||
context.keyLength = method->keyLength;
|
||||
for (const bool inPlace : {false, true}) {
|
||||
context.encrypt = 1;
|
||||
std::copy(iv.begin(), iv.end(), context.iv);
|
||||
Require(method->init(&context, key.data(), nullptr, 1) == 1, "AES descriptor encryption setup");
|
||||
auto output = input;
|
||||
output.push_back(0xa5);
|
||||
Require(method->cipher(&context, output.data(), inPlace ? output.data() : input.data(), length) == 1, "AES descriptor partial encryption");
|
||||
Require(output.back() == 0xa5 && std::equal(expected.begin(), expected.end(), output.begin()), "CBC partial encryption and bounds");
|
||||
Require(std::equal(referenceIv.begin(), referenceIv.end(), context.iv), "CBC encryption IV");
|
||||
context.encrypt = 0;
|
||||
std::copy(iv.begin(), iv.end(), context.iv);
|
||||
Require(method->init(&context, key.data(), nullptr, 0) == 1, "AES descriptor decryption setup");
|
||||
if (!inPlace) output.assign(padded + 1, 0xa5);
|
||||
const auto before = output;
|
||||
Require(method->cipher(&context, output.data(), inPlace ? output.data() : expected.data(), length) == 1, "AES descriptor partial decryption");
|
||||
Require(std::equal(input.begin(), input.begin() + length, output.begin()), "CBC decrypted prefix");
|
||||
Require(std::equal(before.begin() + length, before.end(), output.begin() + length), "partial decryption preserves trailing bytes");
|
||||
Require(std::equal(referenceIv.begin(), referenceIv.end(), context.iv), "CBC decryption IV");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
int APS5_VABI DesInit(LibreSslCipherContext* context, const unsigned char* key, const unsigned char*, int) {
|
||||
auto* des = static_cast<mbedtls_des3_context*>(context->cipherData);
|
||||
mbedtls_des3_init(des);
|
||||
return mbedtls_des3_set3key_enc(des, key) == 0;
|
||||
}
|
||||
|
||||
int APS5_VABI DesCipher(LibreSslCipherContext* context, unsigned char* output, const unsigned char* input, std::size_t length) {
|
||||
return mbedtls_des3_crypt_cbc(static_cast<mbedtls_des3_context*>(context->cipherData), MBEDTLS_DES_ENCRYPT, length, context->iv, input, output) == 0;
|
||||
}
|
||||
|
||||
void GuestDes() {
|
||||
const LibreSslCipher method{44, 8, 24, 8, 2, DesInit, DesCipher, nullptr, sizeof(mbedtls_des3_context), nullptr, nullptr, nullptr, nullptr};
|
||||
std::array<unsigned char, 24> key;
|
||||
std::array<unsigned char, 33> message;
|
||||
for (std::size_t index = 0; index < key.size(); ++index) key[index] = static_cast<unsigned char>(index * 31 + 7);
|
||||
for (std::size_t index = 0; index < message.size(); ++index) message[index] = static_cast<unsigned char>(index * 71 + 3);
|
||||
const auto* reference = mbedtls_cipher_info_from_type(MBEDTLS_CIPHER_DES_EDE3_ECB);
|
||||
Require(reference != nullptr, "reference 64-bit cipher");
|
||||
auto context = NewContext();
|
||||
for (std::size_t length = 0; length <= message.size(); ++length) {
|
||||
std::array<unsigned char, 8> expected;
|
||||
Require(mbedtls_cipher_cmac(reference, key.data(), 192, message.data(), length, expected.data()) == 0, "reference 64-bit CMAC");
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 1, "guest 64-bit cipher initialization");
|
||||
for (std::size_t index = 0; index < length; ++index) Require(CMAC_Update_nid_postfix(context.get(), message.data() + index, 1) == 1, "guest 64-bit cipher update");
|
||||
std::array<unsigned char, 10> actual;
|
||||
actual.fill(0xa5);
|
||||
std::size_t outputLength = 99;
|
||||
Require(CMAC_Final_nid_postfix(context.get(), actual.data() + 1, &outputLength) == 1, "guest 64-bit cipher final");
|
||||
Require(outputLength == 8 && actual.front() == 0xa5 && actual.back() == 0xa5 && std::equal(expected.begin(), expected.end(), actual.begin() + 1), "64-bit CMAC tag and output bounds");
|
||||
}
|
||||
}
|
||||
|
||||
const LibreSslCipher* delegated;
|
||||
int initCalls;
|
||||
int cipherCalls;
|
||||
int cleanupCalls;
|
||||
int failAt;
|
||||
|
||||
int APS5_VABI GuestInit(LibreSslCipherContext* context, const unsigned char* key, const unsigned char* iv, int encrypt) {
|
||||
++initCalls;
|
||||
Require(context->encrypt == 1 && context->keyLength == 16 && context->cipherData != nullptr, "guest callback context");
|
||||
return delegated->init(context, key, iv, encrypt);
|
||||
}
|
||||
|
||||
int APS5_VABI GuestCipher(LibreSslCipherContext* context, unsigned char* output, const unsigned char* input, std::size_t length) {
|
||||
++cipherCalls;
|
||||
if (failAt == cipherCalls) {
|
||||
std::memset(output, 0x5a, length);
|
||||
return 0;
|
||||
}
|
||||
return delegated->cipher(context, output, input, length);
|
||||
}
|
||||
|
||||
int APS5_VABI GuestCleanup(LibreSslCipherContext*) {
|
||||
++cleanupCalls;
|
||||
return 1;
|
||||
}
|
||||
|
||||
int APS5_VABI GuestControl(LibreSslCipherContext* context, int command, int argument, void*) {
|
||||
if (command == 0) return 1;
|
||||
if (command == 1 && argument == 16) {
|
||||
context->keyLength = argument;
|
||||
return 1;
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
void Callbacks() {
|
||||
delegated = Methods[0];
|
||||
auto method = *delegated;
|
||||
method.init = GuestInit;
|
||||
method.cipher = GuestCipher;
|
||||
method.cleanup = GuestCleanup;
|
||||
method.control = GuestControl;
|
||||
method.flags |= 0xc0;
|
||||
std::array<unsigned char, 16> key{};
|
||||
std::array<unsigned char, 33> message{};
|
||||
{
|
||||
auto context = NewContext();
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 1 && initCalls == 1, "guest initialization callback");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && cipherCalls == 3, "guest cipher callbacks");
|
||||
const auto tag = Final(context.get());
|
||||
Require(CMAC_Init_nid_postfix(context.get(), nullptr, 0, nullptr, nullptr) == 1, "guest callback restart");
|
||||
Require(CMAC_Update_nid_postfix(context.get(), message.data(), message.size()) == 1 && Final(context.get()) == tag, "guest-defined cipher matches builtin");
|
||||
failAt = cipherCalls + 1;
|
||||
std::array<unsigned char, 16> output;
|
||||
output.fill(0xa5);
|
||||
std::size_t length = 99;
|
||||
Require(CMAC_Final_nid_postfix(context.get(), output.data(), &length) == 0 && length == 16, "cipher failure propagates");
|
||||
Require(std::all_of(output.begin(), output.end(), [](auto value) { return value == 0; }), "failed final clears output");
|
||||
failAt = 0;
|
||||
}
|
||||
Require(cleanupCalls == 1, "guest cleanup callback");
|
||||
auto context = NewContext();
|
||||
method.blockSize = 32;
|
||||
ERR_clear_error_nid_postfix();
|
||||
Require(CMAC_Init_nid_postfix(context.get(), key.data(), key.size(), &method, nullptr) == 0, "reject unsupported block size");
|
||||
Require(ERR_get_error_nid_postfix() == 0x06fff088, "bad block length publishes EVP error");
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv) {
|
||||
#ifdef ANYPS5_CMAC_DYNAMIC
|
||||
Require(argc == 2, "patched test needs the library directory");
|
||||
InitializePatched(argv[1]);
|
||||
#endif
|
||||
InitializeHeap();
|
||||
Methods[0] = EVP_aes_128_cbc_nid_postfix();
|
||||
Methods[1] = EVP_aes_192_cbc_nid_postfix();
|
||||
Methods[2] = EVP_aes_256_cbc_nid_postfix();
|
||||
RfcVectors();
|
||||
Differential();
|
||||
Lifecycle();
|
||||
Callbacks();
|
||||
AllocationFailures();
|
||||
CipherCallbacks();
|
||||
GuestDes();
|
||||
Require(allocations.empty(), "all guest allocations released");
|
||||
std::puts("CMAC vectors, streaming, lifecycle and callback checks passed");
|
||||
}
|
||||
@@ -7,14 +7,14 @@
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 261 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.
|
||||
- [libSceLibreSsl pending exports](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - 231 named callable exports are listed in the [PS5 3.20 export table](https://github.com/DNNDHH/PS5-3.20_Libs/blob/b2a77c09cd71c01f34c790cfbe98c751a13262ad/libSceLibreSsl.c) and were cross-checked against the same module in PS5 firmware 13.60. Their signatures and behaviour are unverified; the declarations use the no-parameter unknown-signature convention and throw through `NotImplemented_nid_no_patch`.
|
||||
- [MD4, MD5, SHA1, SHA224, SHA256, SHA384 and SHA512 Init/Update/Final and SHA256](../../../core/libs/prx/libSceLibreSsl/src/Digest.cpp) (libSceLibreSsl) - signatures and context layouts follow the [LibreSSL headers](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha/sha.h), and buffering and `Final` follow its [sources](https://github.com/libressl/openbsd/tree/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/sha); nothing is checked on the console. Null contexts, null input with a nonzero length, null digest buffers in the 32-bit `Final` functions and a buffered byte count that is not below the block size, which LibreSSL does not check, throw. SHA-384 and SHA-512 initialization retains the block buffer, following [LibreSSL 2.5.4 `sha512.c`](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/sha/sha512.c)
|
||||
- [DES_set_key, DES_ecb_encrypt, DES_set_odd_parity](../../../core/libs/prx/libSceLibreSsl/Export.cpp) (libSceLibreSsl) - the 128-byte `DES_key_schedule` holds the 8-byte key followed by zeros instead of LibreSSL's precomputed subkeys, so a title reading its contents sees different bytes; `DES_ecb_encrypt` derives the subkeys through mbedtls on each call. Not compared with the console. Results follow LibreSSL [des_key.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des_key.c) and [des.c](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/des/des.c): `DES_set_key` skips the parity and weak-key checks and returns 0, as it does while `DES_check_key` is 0 (that variable is not in the PS5 export inventory), a nonzero `enc` encrypts, and `DES_set_odd_parity` sets each byte's low bit for odd parity. Null pointers throw where LibreSSL faults
|
||||
- [X509_verify_cert_error_string](../../../core/libs/prx/libSceLibreSsl/Export.cpp) - no console call is verified. The 64-bit argument, low-32-bit lookup, messages and shared numeric fallback follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/x509/x509_txt.c), the version identified in [published firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214). Console modifications and other firmware versions remain unverified. Unknown-code results share an overwritten static buffer and are not safe for concurrent use, as in that release; code 1 and codes above 54 use the numeric fallback.
|
||||
- [CRYPTO_num_locks, CRYPTO_set_locking_callback, CRYPTO_set_add_lock_callback, CRYPTO_THREADID_set_callback, CRYPTO_THREADID_set_numeric](../../../core/libs/prx/libSceLibreSsl/Export.cpp) (libSceLibreSsl) - the locking, add-lock and thread id callbacks are stored but never called: LibreSSL calls them from `CRYPTO_lock`, `CRYPTO_add_lock` and `CRYPTO_THREADID_current`, which the module does not export, around its own shared state. Nothing is checked on the console. Results follow [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/cryptlib.c), the version in the [firmware 13.40 measurements](https://github.com/boykopovar/AnyPS5/discussions/8981#discussioncomment-19111214): 41 locks, the first non-null thread id callback is kept and later ones return 0, and `CRYPTO_THREADID_set_numeric` clears the 16-byte `{ptr, val}` structure; a null structure throws, as LibreSSL writes through it unchecked
|
||||
- [RAND_add, RAND_bytes, RAND_status, RAND_load_file, RAND_file_name, CRYPTO_malloc, CRYPTO_free, CRYPTO_set_mem_functions](../../../core/libs/prx/libSceLibreSsl/src/Runtime.cpp) (libSceLibreSsl) - follow LibreSSL's [rand_lib.c, randfile.c](https://github.com/libressl/openbsd/tree/0f0b16dc977f6ca499f0f4540078d992c96da597/src/lib/libcrypto/rand) and [malloc-wrapper.c](https://github.com/libressl/openbsd/blob/0f0b16dc977f6ca499f0f4540078d992c96da597/src/lib/libcrypto/malloc-wrapper.c), which are the same in 2.6.5 (3.4.3 linked); nothing is checked on the console. `RAND_bytes` reads host entropy and `CRYPTO_malloc` and `CRYPTO_free` use the guest heap. `CRYPTO_malloc` returns null for a size of zero or less, as LibreSSL 2.5.4 to 3.4 do; later versions take a `size_t` size and call `malloc`. A null buffer with a positive length throws
|
||||
- [AES_set_encrypt_key, AES_set_decrypt_key, AES_encrypt, AES_decrypt, AES_ecb_encrypt, AES_cbc_encrypt, AES_cfb128_encrypt, AES_ctr128_encrypt](../../../core/libs/prx/libSceLibreSsl/src/Aes.cpp) (libSceLibreSsl) - follow LibreSSL's [aes.h](https://github.com/libressl/openbsd/blob/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/aes/aes.h) and [mode code](https://github.com/libressl/openbsd/tree/bc9483725c5815efb64b6d9acc52fc5fba139212/src/lib/libcrypto/modes). Nothing is checked on the console. Key setup follows [LibreSSL 2.5.4's aes_core.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/aes/aes_core.c), the version the `X509_verify_cert_error_string` entry names: a null pointer returns -1 and a key length other than 128, 192 or 256 bits returns -2 before the key is written, and only the 44, 52 or 60 words the key length needs are written. The `AES_KEY` schedule uses that file's generic C layout (big-endian words, `rounds` 10, 12 or 14). The 2.5.4 amd64 build uses [aes-x86_64](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/aes/asm/aes-x86_64.pl) instead, which copies the key bytes without swapping, so each word is byte-reversed compared with the C layout (OpenSSL 3.0.13's x86-64 code gives the same schedules byte-reversed); the console's build options are unknown, so a schedule the title reads or fills is unverified. CTR leaves the last keystream block in `ecount_buf` even at a block boundary. Null pointers in the cipher and mode functions, other `rounds` values and a CFB or CTR position outside 0-15 throw
|
||||
- [ERR_get_error, ERR_peek_error, ERR_clear_error, ERR_remove_thread_state](../../../core/libs/prx/libSceLibreSsl/src/Error.cpp) have no console execution verification or production error producers. The 16-slot calling-thread queue retains 15 codes following LibreSSL err.c and err.h. [Firmware 13.42 module comparisons on a PC](https://github.com/boykopovar/AnyPS5/pull/9535#issuecomment-6407060914) matched libSceLibreSsl3 queue ordering and overflow for 0-20 publications; whether the two firmware libraries share a queue was not tested. Explicit thread IDs throw because guest thread identity mapping is unverified; null clears the calling thread. File/line metadata is stored by the native producer helper but no guest export exposes it.
|
||||
- [ERR_get_error, ERR_peek_error, ERR_clear_error, ERR_remove_thread_state](../../../core/libs/prx/libSceLibreSsl/src/Error.cpp) have no console execution verification. Memory BIO and CMAC failures publish to this queue. The 16-slot calling-thread queue retains 15 codes following LibreSSL err.c and err.h. [Firmware 13.42 module comparisons on a PC](https://github.com/boykopovar/AnyPS5/pull/9535#issuecomment-6407060914) matched libSceLibreSsl3 queue ordering and overflow for 0-20 publications; whether the two firmware libraries share a queue was not tested. Explicit thread IDs throw because guest thread identity mapping is unverified; null clears the calling thread. File/line metadata is stored by the native producer helper but no guest export exposes it.
|
||||
- [OCSP_response_status_str, OCSP_cert_status_str and OCSP_crl_reason_str](../../../core/libs/prx/libSceLibreSsl/src/Ocsp.cpp) (libSceLibreSsl) - the module's strings are not measured on the console. The tables follow LibreSSL [ocsp_prn.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ocsp/ocsp_prn.c), unchanged from revision 1.8 (2015) to 1.12 (2025), with codes from `ocsp.h`; the argument is a 64-bit `long` and every value not in a table returns `(UNKNOWN)`
|
||||
- [SSL_get_ex_new_index, SSL_CTX_get_ex_new_index, SSL_get_ex_data_X509_STORE_CTX_idx and CRYPTO_cleanup_all_ex_data](../../../core/libs/prx/libSceLibreSsl/src/ExData.cpp) (libSceLibreSsl) - unverified on a PS5. The stored `argl`, `argp` and callbacks are never called, because no export creates an `SSL` or `SSL_CTX` and `SSL_get_ex_data`/`SSL_set_ex_data` still throw. Index numbering follows LibreSSL 2.5.4's [ex_data.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/ex_data.c#L310-L406), [ssl_lib.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl_lib.c#L2828-L2854) and [ssl_cert.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libssl/ssl_cert.c#L133-L160): per class from 0, the verify-callback index allocated once and kept across `CRYPTO_cleanup_all_ex_data`, which restarts every class at 0.
|
||||
- [EVP digest and HMAC functions](../../../core/libs/prx/libSceLibreSsl/src/Evp.cpp) (libSceLibreSsl) - follow LibreSSL 2.5.4's [digest.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/evp/digest.c) and [hmac.c](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/hmac/hmac.c). They and the `EVP_MD`, `EVP_MD_CTX` and `HMAC_CTX` layouts behave the same up to 3.4.1, so this holds for 2.5.4 (the firmware 13.40 measurements) and for 2.7 or later (its `SSL_get0_param` export). Nothing is checked on the console. `sign` and `verify` in the `EVP_MD` tables are null as `RSA_sign` and `RSA_verify` still throw, failures add nothing to the error queue, and a failed allocation leaves the context without a digest, where LibreSSL keeps it. An `ENGINE`, an `EVP_PKEY_CTX`, a null context, `EVP_DigestInit_ex` without a type on a context that has a digest, and an update or final without a digest throw
|
||||
@@ -31,6 +31,16 @@
|
||||
|
||||
### Functional
|
||||
|
||||
- [Error publication](../../../core/libs/prx/libSceLibreSsl/ErrorQueue.hpp) is integrated with memory BIO and CMAC cipher errors. The digest, low-level AES, allocator/RAND, DES, BIGNUM and EVP/HMAC implementations require separate error-publication integration. `LibreSslPutError_nid_no_patch(int library, int function, int reason, const char* file, int line)` has C linkage and the native host calling convention, accepts upstream numeric fields and publishes to the calling thread's queue. Three-argument source calls default file/line to null/zero. It stores file/line without taking ownership; producers must supply a filename with static lifetime. Guest ERR entry points use `APS5_VABI`. Unsupported producer exports continue throwing; the local tests publish through this helper.
|
||||
|
||||
- [CMAC and AES-CBC descriptors](../../../core/libs/prx/libSceLibreSsl/src/Cmac.cpp) have no PS5 equivalence verification. Signatures, the 88-byte cipher method and 168-byte cipher context follow [LibreSSL 3.4.1](https://github.com/libressl/openbsd/blob/078b3d1df8ca2011c1605cd40418c58fa0f5b143/src/lib/libcrypto/cmac/cmac.c); the console version remains unknown. AES cipher data uses Mbed TLS storage instead of LibreSSL's private key schedule. Guest-defined cipher methods run through guest-ABI callbacks. Other built-in cipher selectors and the general EVP cipher exports still throw. ENGINE-backed methods throw. Null contexts, null nonempty update buffers, null initialized-final length pointers, negative cipher dimensions and missing required callbacks throw where upstream dereferences unchecked pointers. Error metadata names host source locations.
|
||||
|
||||
- [BN_new, BN_free, BN_CTX_new, BN_CTX_free, BN_CTX_start, BN_CTX_end, BN_bin2bn, BN_bn2bin, BN_hex2bn, BN_num_bits, BN_set_word, BN_add_word, BN_sub_word, BN_mul and BN_mod_inverse](../../../core/libs/prx/libSceLibreSsl/src/Bignum.cpp) (libSceLibreSsl) - the `BIGNUM` layout (24 bytes, 64-bit limbs, `top`, `dmax`, `neg`, `flags`) and the documented semantics follow the public LibreSSL 2.x API; the module's LibreSSL version is unknown and nothing is checked on the console. `BN_CTX` is opaque and only counts `BN_CTX_start` and `BN_CTX_end`, since `BN_CTX_get` is not implemented, and `BN_mul` and `BN_mod_inverse` ignore it. `BN_mod_inverse` treats a negative modulus by its magnitude and returns 0 for a modulus of 1, which is not compared with LibreSSL. `BN_hex2bn` rejects a string without a hex digit, such as `-`, which older LibreSSL versions accept. Null `BIGNUM` pointers, which LibreSSL does not check, throw. `BN_print` still throws because it needs a `BIO`.
|
||||
|
||||
- [Memory BIOs](../../../core/libs/prx/libSceLibreSsl/src/Bio.cpp) have no console execution verification. [Firmware 13.42 module comparisons on a PC](https://github.com/boykopovar/AnyPS5/pull/9536#issuecomment-6407192928) cover 1,048 sequences with stand-in allocators, including null/zero writes, callbacks and growth wiping. They support dispatching nonnegative writes to the method even with null source or zero length; LibreSSL 3.4.1 instead guards those calls. Negative public lengths retain that source's early return because the module report did not measure them. The 104-byte BIO, 80-byte method and 24-byte buffer use LP64 layouts. Allocator padding is zeroed rather than retaining the module allocator's fill. Other firmware and console imports remain unverified. CRYPTO ex-data, file, socket and SSL BIOs remain unsupported.
|
||||
|
||||
- [Memory BIO unchecked inputs](../../../core/libs/prx/libSceLibreSsl/src/Bio.cpp) throw for null method or raw line-buffer pointers, negative raw method write sizes, an overflowing raw line-buffer size, inconsistent read-only buffer lengths, and memory write lengths that overflow size_t. Upstream does not validate these inputs. An unrepresentable string length throws instead of narrowing into an unchecked write.
|
||||
|
||||
- [ERR string formatting](../../../core/libs/prx/libSceLibreSsl/ErrorQueue.cpp) is not console-verified. [Firmware 13.42 modules run natively on a PC](https://github.com/boykopovar/AnyPS5/pull/9535#issuecomment-6407060914) produced numeric-only strings before and after their loaders. Formatting, truncation and caller/static buffers matched with a standard-C snprintf stand-in; other firmware, console imports and concurrent static-buffer use remain unverified. Loading and freeing do not enable named strings or alter queued errors. Historical tables are generated only as regression-test inputs.
|
||||
|
||||
- [ASN1_INTEGER_get](../../../core/libs/prx/libSceLibreSsl/src/Asn1.cpp) has no console verification for nonpositive lengths. It follows [LibreSSL 2.5.4](https://github.com/libressl/openbsd/blob/2e2c612066aa993717528a0249c15c51128d61cb/src/lib/libcrypto/asn1/a_int.c#L363-L389): valid integer types return zero without reading data when length is zero or negative; invalid types return -1 first. Later LibreSSL versions reject negative lengths through `ASN1_INTEGER_get_int64`; that later contract is not used for this library.
|
||||
|
||||
Reference in new issue
Block a user