locked
Verify signature from TPM wih BCryptVerifySignature RRS feed

  • Question

  • I have a use case where I have to verify a signature that comes from the TPM created with the TSS-lib from https://github.com/Microsoft/TSS.MSR/. When creating the key inside the TPM I am able to retrieve and return the modulus for the public key. Using the .NET approach with C# I am able to create an RsaCryptoServiceProvider with this code:

    private RSACryptoServiceProvider ConvertTpmPublicKeyToRSACryptoServiceProvider(TpmPublic tpmPublic)
            {
                var exponent = new byte[] { 0x01, 0x00, 0x01 };
                var modulus = ((Tpm2bPublicKeyRsa)tpmPublic.unique).buffer;
    
                RSAParameters rsaParameters = new RSAParameters
                {
                    Exponent = exponent,
                    Modulus = modulus
                };
    
                RSACryptoServiceProvider rsa = new RSACryptoServiceProvider();
                rsa.ImportParameters(rsaParameters);
                return rsa;
            }

    This RsaCryptoServiceProvider can now be used to successfully verify the signature I get from the TPM.

    Now I have to do the same thing using C++ and I just cannot figure out why it does not work.

    After some fiddling I am able to import the public part with BCryptImportKeyPair and create the hash of the data to be signed (this is equal to the hashed data in TPM code). The public key bytes I import look like this (converted to hex with extra linefeeds for better readability):

    52534131
    00080000
    03000000
    00010000
    00000000
    00000000
    010001
    315bc89ad278e3bcb0e3a01bf3401a3ef79b0fbfe4767b076f505f08a2a4084bf1d6b270807b55eac1d9b45e4c72e8dc4e9be5aba6aaa266ab107ad2b3f748c366c3e30e40ffadeb3d7a4fdc09848301a5761196e34fbc93103327ca559a194243dc83f2ea0a724ec2070a8ea7a189987ff8dd9cfa672d54a5d4f8e06d14fd10928dacd262c21b9cf28e23ad64db9044bfbca156aaccbadf0c74578d2bd66527046af9b3f9391237dc77f6ddeeeb1a64ba113de6be3dd4d7fcf909636e889d821920b1b05a2ac740d0dd70c833e576ece7d1e783bc9a8fe1171245f1941238953cdc7a26218d3a8f1a8c5e3fbafb65dfa1c68edd06afbc5fe110291e9b0c49df

    But when I try to verify the signature with BCryptVerifySignature, I always get 0xc000a000 which means "The cryptographic signature is invalid.". I already tried to change the endianess of exponent, modulus and signature in different combinations but still the same result.

    The import of the public key looks like this:

    TpmCpp::TPMU_PUBLIC_ID* publicId = publicKey.unique;
    std::vector<unsigned char> publicBuffer = publicId->ToBuf();   
    
    // Strip of the first two bytes as they are length indicators:
    std::vector<unsigned char> modulus(publicBuffer.begin() + 2, publicBuffer.end());
    
    long keyLength = sizeof(BCRYPT_RSAKEY_BLOB);
    const int modulusLength = ((modulus.size() * 8) + 7) / 8;
    std::vector<unsigned char> exponent({ 0x01, 0x00, 0x01 }); // This is a fixed value inside TPM
    const int exponentLength = 3;
    keyLength += modulusLength;
    keyLength += exponentLength;
    
    std::vector<unsigned char> publicKeyBytes(keyLength);
    ZeroMemory(&publicKeyBytes[0], keyLength);
    
    // Create the Public Key Blob Header
    BCRYPT_RSAKEY_BLOB * rsaBlob = reinterpret_cast<BCRYPT_RSAKEY_BLOB *>(&publicKeyBytes[0]);
    rsaBlob->Magic = BCRYPT_RSAPUBLIC_MAGIC;
    rsaBlob->BitLength = 2048;
    rsaBlob->cbPublicExp = exponentLength;
    rsaBlob->cbModulus = modulusLength;
    rsaBlob->cbPrime1 = 0;
    rsaBlob->cbPrime2 = 0;
    
    PBYTE keyAsBytes = reinterpret_cast<PBYTE>(rsaBlob + 1);
    
    // Copy exponent Big Endian 
    ReverseMemCopy(keyAsBytes, &exponent.front(), exponentLength);
    keyAsBytes += exponentLength;
    
    // Copy modulus Big Endian 
    ReverseMemCopy(keyAsBytes, &modulus.front(), modulusLength);
    
    BCryptOpenAlgorithmProvider(
    	&signAlgorithm,
    	BCRYPT_RSA_SIGN_ALGORITHM,
    	nullptr,
    	0);
    
    BCRYPT_KEY_HANDLE tmpKeyHandle = nullptr;
    
    long importResult = BCryptImportKeyPair(
    	signAlgorithm,
    	nullptr,
    	BCRYPT_RSAPUBLIC_BLOB,
    	&tmpKeyHandle,
    	&publicKeyBytes[0],
    	keyLength,
    	0);

    Creating the hash is nothing special and as I said it gives the same result as the TPM hash. But calling BCryptVerifySignature fails:

    long result = BCryptVerifySignature(
            tmpKeyHandle,
            &paddingInfo,
            hashBuffer,
            hashLength,
            reversedSig,
            signature.size(),
            BCRYPT_PAD_PKCS1);

    Where is my error?


    Friday, August 17, 2018 12:19 PM

Answers

  • OK, found it out myself. It had nothing to do with the endianess or anything else. 

    The problem was that the C++ implementation works a little bit different from the C# one.

    While C# returns the plain signature bytes when calling signature.sig.toBuffer(), in C++ there are four trailing bytes that do not belong to the signature when doing the same. 

    • Marked as answer by Frank Ertl Wednesday, August 22, 2018 9:18 AM
    Wednesday, August 22, 2018 9:18 AM