Skip to content

Commit

Permalink
Rework signature/cert/tik interfaces.
Browse files Browse the repository at this point in the history
* signature: add comments to SignatureType enum entries about the exact signing algorithms and padding schemes used.
* signature: rename signatureGetSigType() -> signatureGetTypeFromSignedBlob().
* signature: rename signatureIsValidSigType() -> signatureIsValidType().
* signature: rename signatureGetSigSize() -> signatureGetSigSizeByType().
* signature: rename signatureGetBlockSize() -> signatureGetBlockSizeByType().
* signature: rename signatureGetSig() -> signatureGetSigFromSignedBlob().
* signature: rename signatureGetPayload() -> signatureGetPayloadFromSignedBlob().
* signature: add signatureGetBlockSizeFromSignedBlob().

* cert: add more comments to the code.
* cert: update code to match signature interface changes.
* cert: add CERT_RSA_PUB_EXP_SIZE macro.
* cert: change public_exponent field in CertPublicKeyBlockRsa* structs from u32 to u8 array.
* cert: add size field to CertificateChain struct.
* cert: rename certGetCommonBlock() -> certGetCommonBlockFromSignedCertBlob.
* cert: rename certGetPublicKeySize() -> certGetPublicKeySizeByType().
* cert: rename certGetPublicKeyBlockSize() -> certGetPublicKeyBlockSizeByType().
* cert: rename certIsValidCertificate() -> certIsValidSignedCertBlob().
* cert: rename certGetSignedCertificateSize() -> certGetSignedCertBlobSize().
* cert: rename certGetSignedCertificateHashAreaSize() -> certGetSignedCertBlobHashAreaSize().
* cert: remove certGetPublicKey(), certGetPublicExponent() and certCalculateRawCertificateChainSize().
* cert: add certGetPublicKeyTypeFromCommonBlock(), certGetPublicKeyTypeFromSignedCertBlob(), certGetPublicKeySizeFromSignedCertBlob(), certGetPublicKeyBlockSizeFromSignedCertBlob(), certGetPublicKeyFromSignedCertBlob(), certGetPublicExponentFromSignedCertBlob(), certIsValidCertificate() (w/diff func sig), certGetCommonBlockFromCertificate(), certGetPublicKeyTypeFromCertificate(), certGetPublicKeySizeFromCertificate(), certGetPublicKeyBlockSizeFromCertificate(), certGetPublicKeyFromCertificate(), certGetPublicExponentFromCertificate() and certGetHashAreaSizeFromCertificate() functions.
* cert: avoid byteswapping the public key type value in multiple places -- it is now only being done in certGetPublicKeyTypeFromCommonBlock().
* cert: call certFreeCertificateChain() in _certRetrieveCertificateChainBySignatureIssuer() before attempting to retrieve the certificate chain.
* cert: other minor changes and corrections.

* tik: update code to match signature interface changes.
* tik: add missing comments to TikPropertyMask enum entries.
* tik: add key_generation, enc_titlekey_str and dec_titlekey_str fields to Ticket struct.
* tik: update tikRetrieveTicketByRightsId() to also take in a key_generation argument, instead of getting it from the rights ID (which could fail if it's using a key generation lower than HOS 3.0.1) or the key_generation field from the common ticket block (which could fail if the ticket has been tampered by certain tools).
* tik: rename tikGetCommonBlock() -> tikGetCommonBlockFromSignedTicketBlob().
* tik: change function signature for tikGetTicketSectionRecordsBlockSize().
* tik: rename tikIsValidTicket() -> tikIsValidSignedTicketBlob().
* tik: rename tikGetSignedTicketSize() -> tikGetSignedTicketBlobSize().
* tik: rename tikGetSignedTicketHashAreaSize() -> tikGetSignedTicketBlobHashAreaSize().
* tik: rename tikGetEncryptedTitleKeyFromTicket() -> tikGetEncryptedTitleKey().
* tik: add tikIsValidTicket() (w/diff func sig), tikGetCommonBlockFromTicket(), tikGetHashAreaSizeFromTicket(), tikFixTamperedCommonTicket(), tikVerifyRsa2048Sha256Signature() and tikDecryptVolatileTicket() functions. Ticket signature verification is only carried out for common tickets in tikFixTamperedCommonTicket().
* tik: change argument order in tikGetTicketEntryOffsetFromTicketList() and tikRetrieveTicketEntryFromTicketBin().
* tik: add TIK_COMMON_CERT_NAME and TIK_DEV_CERT_ISSUER macros.
* tik: use a scoped lock when calling tikRetrieveTicketFromEsSaveDataByRightsId().
* tik: simplify certificate chain retrieval steps in tikConvertPersonalizedTicketToCommonTicket() by always using the XS00000020 certificate.
* tik: wipe license_type and property_mask fields in tikConvertPersonalizedTicketToCommonTicket().
* tik: other minor changes and corrections.

Other changes include:

* keys: fix key generation checks in keysGetNcaKeyAreaKeyEncryptionKey() and keysGetTicketCommonKey().

* rsa: move core logic from rsa2048VerifySha256BasedPssSignature() into a new function: rsa2048VerifySha256BasedSignature().
* rsa: add rsa2048VerifySha256BasedPkcs1v15Signature() function.
  • Loading branch information
DarkMatterCore committed Oct 15, 2023
1 parent ecaeddf commit c1b76fb
Show file tree
Hide file tree
Showing 12 changed files with 629 additions and 353 deletions.
20 changes: 5 additions & 15 deletions code_templates/nxdt_rw_poc.c
Original file line number Diff line number Diff line change
Expand Up @@ -2869,8 +2869,6 @@ static bool saveTicket(void *userdata)
NcaContext *nca_ctx = NULL;

Ticket tik = {0};
TikCommonBlock *tik_common_block = NULL;
char enc_titlekey_str[33] = {0};

u32 crc = 0;
char *filename = NULL;
Expand Down Expand Up @@ -2916,21 +2914,14 @@ static bool saveTicket(void *userdata)
goto end;
}

if (!tik.size)
if (!nca_ctx->titlekey_retrieved)
{
consolePrint("failed to retrieve ticket (unavailable?)\ntry launching nxdumptool while overriding the title you wish to dump a ticket from\n");
goto end;
}

/* Retrieve ticket common block. */
if (!(tik_common_block = tikGetCommonBlock(tik.data)))
{
consolePrint("failed to get tik common block\n");
goto end;
}

/* Remove console-specific data, if needed. */
if (remove_console_data && tik_common_block->titlekey_type == TikTitleKeyType_Personalized && !tikConvertPersonalizedTicketToCommonTicket(&tik, NULL, NULL))
if (remove_console_data && tikIsPersonalizedTicket(&tik) && !tikConvertPersonalizedTicketToCommonTicket(&tik, NULL, NULL))
{
consolePrint("failed to convert personalized ticket to common ticket\n");
goto end;
Expand All @@ -2945,10 +2936,9 @@ static bool saveTicket(void *userdata)

if (!saveFileData(filename, tik.data, tik.size)) goto end;

utilsGenerateHexStringFromData(enc_titlekey_str, MAX_ELEMENTS(enc_titlekey_str), tik.enc_titlekey, sizeof(tik.enc_titlekey), false);

consolePrint("rights id: %s\n", tik.rights_id_str);
consolePrint("titlekey: %s\n\n", enc_titlekey_str);
consolePrint("encrypted titlekey: %s\n", tik.enc_titlekey_str);
consolePrint("decrypted titlekey: %s\n\n", tik.dec_titlekey_str);

consolePrint("successfully saved ticket as \"%s\"\n", filename);
success = true;
Expand Down Expand Up @@ -5003,7 +4993,7 @@ static void nspThreadFunc(void *arg)
bool retrieve_tik_cert = (!remove_titlekey_crypto && tik.size > 0);
if (retrieve_tik_cert)
{
if (!(tik_common_block = tikGetCommonBlock(tik.data)))
if (!(tik_common_block = tikGetCommonBlockFromTicket(&tik)))
{
consolePrint("tik common block failed");
goto end;
Expand Down
158 changes: 112 additions & 46 deletions include/core/cert.h
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,17 @@
extern "C" {
#endif

#define SIGNED_CERT_MAX_SIZE sizeof(CertSigRsa4096PubKeyRsa4096)
#define SIGNED_CERT_MIN_SIZE sizeof(CertSigHmac160PubKeyEcc480)
#define SIGNED_CERT_MAX_SIZE sizeof(CertSigRsa4096PubKeyRsa4096)

#define CERT_RSA_PUB_EXP_SIZE 4

#define GENERATE_CERT_STRUCT(sigtype, pubkeytype, certsize) \
\
typedef struct { \
SignatureBlock##sigtype sig_block; \
CertCommonBlock cert_common_block; \
CertPublicKeyBlock##pubkeytype pub_key_block; \
} CertSig##sigtype##PubKey##pubkeytype; \
\
NXDT_ASSERT(CertSig##sigtype##PubKey##pubkeytype, certsize);

typedef enum {
Expand All @@ -63,7 +63,7 @@ NXDT_ASSERT(CertCommonBlock, 0x88);
/// RSA-4096 public key block. Placed after the certificate common block.
typedef struct {
u8 public_key[0x200];
u32 public_exponent;
u8 public_exponent[CERT_RSA_PUB_EXP_SIZE];
u8 padding[0x34];
} CertPublicKeyBlockRsa4096;

Expand All @@ -72,7 +72,7 @@ NXDT_ASSERT(CertPublicKeyBlockRsa4096, 0x238);
/// RSA-2048 public key block. Placed after the certificate common block.
typedef struct {
u8 public_key[0x100];
u32 public_exponent;
u8 public_exponent[CERT_RSA_PUB_EXP_SIZE];
u8 padding[0x34];
} CertPublicKeyBlockRsa2048;

Expand Down Expand Up @@ -133,10 +133,11 @@ typedef struct {
u8 data[SIGNED_CERT_MAX_SIZE]; ///< Raw certificate data.
} Certificate;

/// Used to store two or more certificates.
/// Used to store multiple certificates.
typedef struct {
u32 count;
Certificate *certs;
u32 count; ///< Number of certificates in this chain.
u64 size; ///< Raw certificate chain size (when concatenated).
Certificate *certs; ///< Certificate array.
} CertificateChain;

/// Retrieves a certificate by its name (e.g. "CA00000003", "XS00000020", etc.).
Expand All @@ -145,77 +146,142 @@ bool certRetrieveCertificateByName(Certificate *dst, const char *name);
/// Retrieves a certificate chain by a full signature issuer string (e.g. "Root-CA00000003-XS00000020").
bool certRetrieveCertificateChainBySignatureIssuer(CertificateChain *dst, const char *issuer);

/// Returns a pointer to a dynamically allocated buffer that contains the raw contents from the certificate chain matching the input signature issuer. It must be freed by the user.
/// NULL is returned if an error occurs.
/// Returns a pointer to a dynamically allocated buffer that holds the concatenated raw contents from the certificate chain matching the input signature issuer.
/// The returned buffer must be freed by the user.
/// Returns NULL if an error occurs.
u8 *certGenerateRawCertificateChainBySignatureIssuer(const char *issuer, u64 *out_size);

/// Returns a pointer to a dynamically allocated buffer that contains the raw contents from the certificate chain matching the input Rights ID (stored in the inserted gamecard).
/// It must be freed by the user.
/// NULL is returned if an error occurs.
/// Returns a pointer to a dynamically allocated buffer that holds the concatenated raw contents from the certificate chain matching the input rights ID in the inserted gamecard.
/// The returned buffer must be freed by the user.
/// Returns NULL if an error occurs.
u8 *certRetrieveRawCertificateChainFromGameCardByRightsId(const FsRightsId *id, u64 *out_size);

/// Helper inline functions.
/// General purpose helper inline functions.

NX_INLINE void certFreeCertificateChain(CertificateChain *chain)
NX_INLINE bool certIsValidPublicKeyType(u32 type)
{
if (!chain) return;
if (chain->certs) free(chain->certs);
memset(chain, 0, sizeof(CertificateChain));
return (type < CertPubKeyType_Count);
}

NX_INLINE CertCommonBlock *certGetCommonBlock(void *buf)
NX_INLINE u64 certGetPublicKeySizeByType(u32 type)
{
return (CertCommonBlock*)signatureGetPayload(buf, true);
return (u64)(type == CertPubKeyType_Rsa4096 ? MEMBER_SIZE(CertPublicKeyBlockRsa4096, public_key) : \
(type == CertPubKeyType_Rsa2048 ? MEMBER_SIZE(CertPublicKeyBlockRsa2048, public_key) : \
(type == CertPubKeyType_Ecc480 ? MEMBER_SIZE(CertPublicKeyBlockEcc480, public_key) : 0)));
}

NX_INLINE bool certIsValidPublicKeyType(u32 type)
NX_INLINE u64 certGetPublicKeyBlockSizeByType(u32 type)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? sizeof(CertPublicKeyBlockRsa4096) : \
(type == CertPubKeyType_Rsa2048 ? sizeof(CertPublicKeyBlockRsa2048) : \
(type == CertPubKeyType_Ecc480 ? sizeof(CertPublicKeyBlockEcc480) : 0)));
}

NX_INLINE u32 certGetPublicKeyTypeFromCommonBlock(CertCommonBlock *cert_common_block)
{
return (type == CertPubKeyType_Rsa4096 || type == CertPubKeyType_Rsa2048 || type == CertPubKeyType_Ecc480);
return (cert_common_block ? __builtin_bswap32(cert_common_block->pub_key_type) : CertPubKeyType_Count);
}

NX_INLINE u8 *certGetPublicKey(CertCommonBlock *cert_common_block)
/// Helper inline functions for signed certificate blobs.

NX_INLINE CertCommonBlock *certGetCommonBlockFromSignedCertBlob(void *buf)
{
return ((cert_common_block != NULL && certIsValidPublicKeyType(__builtin_bswap32(cert_common_block->pub_key_type))) ? ((u8*)cert_common_block + sizeof(CertCommonBlock)) : NULL);
return (CertCommonBlock*)signatureGetPayloadFromSignedBlob(buf, true);
}

NX_INLINE u64 certGetPublicKeySize(u32 type)
NX_INLINE bool certIsValidSignedCertBlob(void *buf)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? MEMBER_SIZE(CertPublicKeyBlockRsa4096, public_key) : \
(type == CertPubKeyType_Rsa2048 ? MEMBER_SIZE(CertPublicKeyBlockRsa2048, public_key) : \
(type == CertPubKeyType_Ecc480 ? MEMBER_SIZE(CertPublicKeyBlockEcc480, public_key) : 0)));
CertCommonBlock *cert_common_block = certGetCommonBlockFromSignedCertBlob(buf);
return (cert_common_block && certIsValidPublicKeyType(certGetPublicKeyTypeFromCommonBlock(cert_common_block)));
}

NX_INLINE u64 certGetPublicKeyBlockSize(u32 type)
NX_INLINE u32 certGetPublicKeyTypeFromSignedCertBlob(void *buf)
{
return (u64)(type == CertPubKeyType_Rsa4096 ? sizeof(CertPublicKeyBlockRsa4096) : \
(type == CertPubKeyType_Rsa2048 ? sizeof(CertPublicKeyBlockRsa2048) : \
(type == CertPubKeyType_Ecc480 ? sizeof(CertPublicKeyBlockEcc480) : 0)));
return (certIsValidSignedCertBlob(buf) ? certGetPublicKeyTypeFromCommonBlock(certGetCommonBlockFromSignedCertBlob(buf)) : CertPubKeyType_Count);
}

NX_INLINE u64 certGetPublicKeySizeFromSignedCertBlob(void *buf)
{
return certGetPublicKeySizeByType(certGetPublicKeyTypeFromSignedCertBlob(buf));
}

NX_INLINE u64 certGetPublicKeyBlockSizeFromSignedCertBlob(void *buf)
{
return certGetPublicKeyBlockSizeByType(certGetPublicKeyTypeFromSignedCertBlob(buf));
}

NX_INLINE u8 *certGetPublicKeyFromSignedCertBlob(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? ((u8*)certGetCommonBlockFromSignedCertBlob(buf) + sizeof(CertCommonBlock)) : NULL);
}

NX_INLINE u8 *certGetPublicExponentFromSignedCertBlob(void *buf)
{
u32 pub_key_type = certGetPublicKeyTypeFromSignedCertBlob(buf);
u8 *public_key = certGetPublicKeyFromSignedCertBlob(buf);
return (pub_key_type < CertPubKeyType_Ecc480 ? (public_key + certGetPublicKeySizeByType(pub_key_type)) : NULL); // Only allow RSA public key types.
}

NX_INLINE u64 certGetSignedCertBlobSize(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? (signatureGetBlockSizeFromSignedBlob(buf, true) + sizeof(CertCommonBlock) + certGetPublicKeyBlockSizeFromSignedCertBlob(buf)) : 0);
}

NX_INLINE u64 certGetSignedCertBlobHashAreaSize(void *buf)
{
return (certIsValidSignedCertBlob(buf) ? (sizeof(CertCommonBlock) + certGetPublicKeyBlockSizeFromSignedCertBlob(buf)) : 0);
}

/// Helper inline functions for Certificate elements.

NX_INLINE bool certIsValidCertificate(Certificate *cert)
{
return (cert && cert->type > CertType_None && cert->type < CertType_Count && cert->size >= SIGNED_CERT_MIN_SIZE && cert->size <= SIGNED_CERT_MAX_SIZE && \
certIsValidSignedCertBlob(cert->data));
}

NX_INLINE CertCommonBlock *certGetCommonBlockFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetCommonBlockFromSignedCertBlob(cert->data) : NULL);
}

NX_INLINE u32 certGetPublicKeyTypeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeyTypeFromSignedCertBlob(cert->data) : CertPubKeyType_Count);
}

NX_INLINE u64 certGetPublicKeySizeFromCertificate(Certificate *cert)
{
return (certIsValidCertificate(cert) ? certGetPublicKeySizeFromSignedCertBlob(cert->data) : 0);
}

NX_INLINE u32 certGetPublicExponent(CertCommonBlock *cert_common_block)
NX_INLINE u64 certGetPublicKeyBlockSizeFromCertificate(Certificate *cert)
{
u8 *public_key = certGetPublicKey(cert_common_block);
return ((cert_common_block != NULL && public_key != NULL) ? __builtin_bswap32(*((u32*)(public_key + certGetPublicKeySize(__builtin_bswap32(cert_common_block->pub_key_type))))) : 0);
return (certIsValidCertificate(cert) ? certGetPublicKeyBlockSizeFromSignedCertBlob(cert->data) : 0);
}

NX_INLINE bool certIsValidCertificate(void *buf)
NX_INLINE u8 *certGetPublicKeyFromCertificate(Certificate *cert)
{
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return (cert_common_block != NULL && certIsValidPublicKeyType(__builtin_bswap32(cert_common_block->pub_key_type)));
return (certIsValidCertificate(cert) ? certGetPublicKeyFromSignedCertBlob(cert->data) : NULL);
}

NX_INLINE u64 certGetSignedCertificateSize(void *buf)
NX_INLINE u8 *certGetPublicExponentFromCertificate(Certificate *cert)
{
if (!certIsValidCertificate(buf)) return 0;
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return (signatureGetBlockSize(signatureGetSigType(buf, true)) + (u64)sizeof(CertCommonBlock) + certGetPublicKeyBlockSize(__builtin_bswap32(cert_common_block->pub_key_type)));
return (certIsValidCertificate(cert) ? certGetPublicExponentFromSignedCertBlob(cert->data) : NULL);
}

NX_INLINE u64 certGetSignedCertificateHashAreaSize(void *buf)
NX_INLINE u64 certGetHashAreaSizeFromCertificate(Certificate *cert)
{
if (!certIsValidCertificate(buf)) return 0;
CertCommonBlock *cert_common_block = certGetCommonBlock(buf);
return ((u64)sizeof(CertCommonBlock) + certGetPublicKeyBlockSize(__builtin_bswap32(cert_common_block->pub_key_type)));
return (certIsValidCertificate(cert) ? certGetSignedCertBlobHashAreaSize(cert->data) : 0);
}

/// Helper inline functions for CertificateChain elements.

NX_INLINE void certFreeCertificateChain(CertificateChain *chain)
{
if (!chain) return;
if (chain->certs) free(chain->certs);
memset(chain, 0, sizeof(CertificateChain));
}

#ifdef __cplusplus
Expand Down
4 changes: 2 additions & 2 deletions include/core/nca.h
Original file line number Diff line number Diff line change
Expand Up @@ -95,7 +95,7 @@ typedef enum {
NcaKeyGeneration_Since1400NUP = 14, ///< 14.0.0 - 14.1.2.
NcaKeyGeneration_Since1500NUP = 15, ///< 15.0.0 - 15.0.1.
NcaKeyGeneration_Since1600NUP = 16, ///< 16.0.0 - 16.1.0.
NcaKeyGeneration_Since1700NUP = 17, ///< 17.0.0.
NcaKeyGeneration_Since1700NUP = 17, ///< 17.0.0+.
NcaKeyGeneration_Current = NcaKeyGeneration_Since1700NUP,
NcaKeyGeneration_Max = 32
} NcaKeyGeneration;
Expand All @@ -111,7 +111,7 @@ typedef enum {
/// TODO: update on signature keygen changes.
typedef enum {
NcaSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 17.0.0.
NcaSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0+.
NcaSignatureKeyGeneration_Current = NcaSignatureKeyGeneration_Since900NUP,
NcaSignatureKeyGeneration_Max = (NcaSignatureKeyGeneration_Current + 1)
} NcaSignatureKeyGeneration;
Expand Down
2 changes: 1 addition & 1 deletion include/core/npdm.h
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ extern "C" {
/// TODO: update on signature keygen changes.
typedef enum {
NpdmSignatureKeyGeneration_Since100NUP = 0, ///< 1.0.0 - 8.1.1.
NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0 - 17.0.0.
NpdmSignatureKeyGeneration_Since900NUP = 1, ///< 9.0.0+.
NpdmSignatureKeyGeneration_Current = NpdmSignatureKeyGeneration_Since900NUP,
NpdmSignatureKeyGeneration_Max = (NpdmSignatureKeyGeneration_Current + 1)
} NpdmSignatureKeyGeneration;
Expand Down
8 changes: 7 additions & 1 deletion include/core/rsa.h
Original file line number Diff line number Diff line change
Expand Up @@ -36,11 +36,17 @@ extern "C" {
#define RSA2048_PUBKEY_SIZE RSA2048_BYTES

/// Verifies a RSA-2048-PSS with SHA-256 signature.
/// Suitable for NCA and NPDM signatures.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
bool rsa2048VerifySha256BasedPssSignature(const void *data, size_t data_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size);

/// Verifies a RSA-2048-PKCS#1 v1.5 with SHA-256 signature.
/// Suitable for ticket and certificate chain signatures.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
bool rsa2048VerifySha256BasedPkcs1v15Signature(const void *data, size_t data_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size);

/// Performs RSA-2048-OAEP decryption.
/// Suitable to decrypt the titlekey block from tickets with personalized crypto.
/// Suitable to decrypt the titlekey block from personalized tickets.
/// The provided signature and modulus must have sizes of at least RSA2048_SIG_SIZE and RSA2048_PUBKEY_SIZE, respectively.
/// 'label' and 'label_size' arguments are optional -- if not needed, these may be set to NULL and 0, respectively.
bool rsa2048OaepDecrypt(void *dst, size_t dst_size, const void *signature, const void *modulus, const void *public_exponent, size_t public_exponent_size, const void *private_exponent, \
Expand Down
Loading

0 comments on commit c1b76fb

Please sign in to comment.