I have been waiting forever for this!
My first objective was to generate an RSA keypair, generate some random data, generate a signature over that data, and then verify that the signature is correct. This worked, although there is some fine print in the descriptions of classes CRSAPKCS1v15Signer and CRSAPKCS1v15Verifier:
CRSAPKCS1v15Signer: This class creates RSA signatures following the RSA PKCS#1 v1.5 standard (with the one caveat noted below) and using PKCS#1 v1.5 signature padding. The only exception is that the SignL() function simply performs a ‘raw’ PKCS#1 v1.5 sign operation on whatever it is given. It does not hash or in any way manipulate the input data before signing.
CRSAPKCS1v15Verifier: This class verifies RSA signatures given a message and its supposed signature. It follows the RSA PKCS#1 v1.5 with PKCS#1 v1.5 padding specification with the following exception: the VerifyL() function does not hash or in any way manipulate the input data before checking.
Uh oh… complexity. The signature I generated and verified is not fully compliant with the relevant specifications. In particular, the widely used PKCS1 v1.5 padding isn’t implemented fully. Thus, when I went to verify a signature produced by an outside application, the verification failed. I couldn’t find any documentation that expressed precisely what was omitted from the Symbian implementation, so I had to do some detective work.
The signed data I am using is the output of a TPM_Quote operation performed by a Broadcom v1.2 TPM. Thus, I will restrict my discussion regarding the Symbian CRSAPKCS1v15* classes to verifying signatures. CRSAPKCS1v15Verifier::VerifyL is the function that refused to return ETrue. There is also CRSAPKCS1v15Verifier::InverseSignLC(), which returns the output of the RSA decrypt operation. Sounds good, since I’m trying to dissect the padding manipulation. The inverse signature for my test data was consistently (leading zeros are stripped):
It turns out the SHA-1 hash of my test data is d4bfa90ce1f23417b7d046911d6c35c6881f8282, leaving a strange header of 3021300906052b0e03021a05000414. I then turned to the trusty XySSL source code to look for what operations are performed between the RSA decrypt and the actual return of whether a verification was successful. The function of interest is rsa_pkcs1_verify(). When I ran my test data through that function, and dumped the data just the RSA decrypt, it looked like this: 0001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff003021300906052b0e03021a05000414d4bfa90ce1f23417b7d046911d6c35c6881f8282. This is actually padded out to the appropriate width for the RSA key, but the interesting bytes at the end match the data the Symbian function was returning. The strange header is defined in the XySSL rsa.h:
#define ASN1_HASH_SHA1 \
The Symbian Crypto APIs do include a CPaddingPKCS1Signature class, but after some testing its purpose appears to be soley the management of the leading 0xffs on the key-width padded signature. Thus, it looks like the Symbian CRSAPKCS1v15Verifier class actually makes use of the CPaddingPKCS1Signature internally to strip off the 0xff-based padding.
I haven’t yet taken the time to dig into the specs and figure out exactly what the purpose of ASN1_HASH_SHA1 is meant to be. For now, I’ve included it in my code as a literal and used
CRSAPKCS1v15Verifier::InverseSignLC. I then prepend ASN1_HASH_SHA1 to the output of the SHA-1 hash of the signed data, and compare to the output of InverseSignLC.
FYI, Symbian enables definition of binary literals by escaping octets with \x. For example:
Also FYI, use of of the RInteger class can cause memory leaks. RInteger is unique in that its NewL() method does not return a pointer. It is important to call the Close() method on an RInteger.
UPDATE: Getting to the crypto api documentation from anything but the original installer has proven to be beyond me. It turns out, on my system at least, that the crypto api docs live in c:\Symbian\9.1\S60_3rd_MR\documentation\CryptographyLibraries.chm. In Windows, one can just paste that into Start -> Open. In Linux, ‘gnochm’ will open and display that file successfully.