/* Microsoft Reference Implementation for TPM 2.0
 *
 *  The copyright in this software is being made available under the BSD License,
 *  included below. This software may be subject to other third party and
 *  contributor rights, including patent rights, and no such rights are granted
 *  under this license.
 *
 *  Copyright (c) Microsoft Corporation
 *
 *  All rights reserved.
 *
 *  BSD License
 *
 *  Redistribution and use in source and binary forms, with or without modification,
 *  are permitted provided that the following conditions are met:
 *
 *  Redistributions of source code must retain the above copyright notice, this list
 *  of conditions and the following disclaimer.
 *
 *  Redistributions in binary form must reproduce the above copyright notice, this
 *  list of conditions and the following disclaimer in the documentation and/or
 *  other materials provided with the distribution.
 *
 *  THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS ""AS IS""
 *  AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
 *  IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE
 *  DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR
 *  ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES
 *  (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES;
 *  LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
 *  ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
 *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS
 *  SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
 */
//** Includes
#include "Tpm.h"
#include "X509.h"
#include "TpmASN1_fp.h"
#include "X509_RSA_fp.h"
#include "X509_spt_fp.h"
#include "CryptHash_fp.h"
#include "CryptRsa_fp.h"

//** Functions

#if ALG_RSA

//*** X509AddSigningAlgorithmRSA()
// This creates the singing algorithm data.
//  Return Type: INT16
//      > 0         number of bytes added
//     == 0         failure
INT16
X509AddSigningAlgorithmRSA(
    OBJECT              *signKey,
    TPMT_SIG_SCHEME     *scheme,
    ASN1MarshalContext  *ctx
)
{
    TPM_ALG_ID           hashAlg = scheme->details.any.hashAlg;
    PHASH_DEF            hashDef = CryptGetHashDef(hashAlg);
//
    NOT_REFERENCED(signKey);
    // return failure if hash isn't implemented
    if(hashDef->hashAlg != hashAlg)
        return 0;
    switch(scheme->scheme)
    {
        case TPM_ALG_RSASSA:
        {
            // if the hash is implemented but there is no PKCS1 OID defined
            // then this is not a valid signing combination.
            if(hashDef->PKCS1[0] != ASN1_OBJECT_IDENTIFIER)
                break;
            if(ctx == NULL)
                return 1;
            return X509PushAlgorithmIdentifierSequence(ctx, hashDef->PKCS1);
        }
        case TPM_ALG_RSAPSS:
            // leave if this is just an implementation check
            if(ctx == NULL)
                return 1;
            // In the case of SHA1, everything is default and RFC4055 says that 
            // implementations that do signature generation MUST omit the parameter
            // when defaults are used. )-:
            if(hashDef->hashAlg == TPM_ALG_SHA1)
            {
                return X509PushAlgorithmIdentifierSequence(ctx, OID_RSAPSS);
            }
            else
            {
                // Going to build something that looks like:
                //  SEQUENCE (2 elem)
                //     OBJECT IDENTIFIER 1.2.840.113549.1.1.10 rsaPSS (PKCS #1)
                //     SEQUENCE (3 elem)
                //       [0] (1 elem)
                //         SEQUENCE (2 elem)
                //           OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 
                //           NULL
                //       [1] (1 elem)
                //         SEQUENCE (2 elem)
                //           OBJECT IDENTIFIER 1.2.840.113549.1.1.8 pkcs1-MGF
                //           SEQUENCE (2 elem)
                //             OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256
                //             NULL
                //       [2] (1 elem)  salt length
                //         INTEGER 32

                // The indentation is just to keep track of where we are in the 
                // structure
                ASN1StartMarshalContext(ctx); // SEQUENCE (2 elements)
                {
                    ASN1StartMarshalContext(ctx);   // SEQUENCE (3 elements)
                    {
                        // [2] (1 elem)  salt length
                        //    INTEGER 32
                        ASN1StartMarshalContext(ctx);
                        {
                            INT16       saltSize =
                                CryptRsaPssSaltSize((INT16)hashDef->digestSize,
                                (INT16)signKey->publicArea.unique.rsa.t.size);
                            ASN1PushUINT(ctx, saltSize);
                        }
                        ASN1EndEncapsulation(ctx, ASN1_APPLICAIION_SPECIFIC + 2);

                        // Add the mask generation algorithm
                        // [1] (1 elem)
                        //    SEQUENCE (2 elem) 1st
                        //      OBJECT IDENTIFIER 1.2.840.113549.1.1.8 pkcs1-MGF
                        //      SEQUENCE (2 elem) 2nd  
                        //        OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256
                        //        NULL
                        ASN1StartMarshalContext(ctx);   // mask context [1] (1 elem)
                        {
                            ASN1StartMarshalContext(ctx);   // SEQUENCE (2 elem) 1st
                            // Handle the 2nd Sequence (sequence (object, null))
                            {
                                // This adds a NULL, then an OID and a SEQUENCE
                                // wrapper.
                                X509PushAlgorithmIdentifierSequence(ctx,
                                    hashDef->OID);
                                // add the pkcs1-MGF OID 
                                ASN1PushOID(ctx, OID_MGF1);
                            }
                            // End outer sequence
                            ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE);
                        }
                        // End the [1] 
                        ASN1EndEncapsulation(ctx, ASN1_APPLICAIION_SPECIFIC + 1);

                        // Add the hash algorithm
                        // [0] (1 elem)
                        //   SEQUENCE (2 elem) (done by 
                        //              X509PushAlgorithmIdentifierSequence)
                        //     OBJECT IDENTIFIER 2.16.840.1.101.3.4.2.1 sha-256 (NIST)
                        //     NULL
                        ASN1StartMarshalContext(ctx); // [0] (1 elem)
                        {
                            X509PushAlgorithmIdentifierSequence(ctx, hashDef->OID);
                        }
                        ASN1EndEncapsulation(ctx, (ASN1_APPLICAIION_SPECIFIC + 0));
                    }
                    //  SEQUENCE (3 elements) end
                    ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE);

                    // RSA PSS OID
                    // OBJECT IDENTIFIER 1.2.840.113549.1.1.10 rsaPSS (PKCS #1)
                    ASN1PushOID(ctx, OID_RSAPSS);
                }
                // End Sequence (2 elements)
                return ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE);
            }
        default:
            break;
    }
    return 0;
}

//*** X509AddPublicRSA()
// This function will add the publicKey description to the DER data. If fillPtr is 
// NULL, then no data is transferred and this function will indicate if the TPM
// has the values for DER-encoding of the public key.
//  Return Type: INT16
//      > 0         number of bytes added
//     == 0         failure
INT16
X509AddPublicRSA(
    OBJECT                  *object,
    ASN1MarshalContext    *ctx
)
{
    UINT32          exp = object->publicArea.parameters.rsaDetail.exponent;
//
/*
    SEQUENCE (2 elem) 1st
      SEQUENCE (2 elem) 2nd
        OBJECT IDENTIFIER 1.2.840.113549.1.1.1 rsaEncryption (PKCS #1)
        NULL
      BIT STRING (1 elem)
        SEQUENCE (2 elem) 3rd
          INTEGER (2048 bit) 2197304513741227955725834199357401…
          INTEGER 65537
*/
    // If this is a check to see if the key can be encoded, it can. 
    // Need to mark the end sequence
    if(ctx == NULL)
        return 1;
    ASN1StartMarshalContext(ctx); // SEQUENCE (2 elem) 1st
    ASN1StartMarshalContext(ctx); // BIT STRING
    ASN1StartMarshalContext(ctx); // SEQUENCE *(2 elem) 3rd

    // Get public exponent in big-endian byte order.
    if(exp == 0)
        exp = RSA_DEFAULT_PUBLIC_EXPONENT;

    // Push a 4 byte integer. This might get reduced if there are leading zeros or
    // extended if the high order byte is negative.
    ASN1PushUINT(ctx, exp);
    // Push the public key as an integer
    ASN1PushInteger(ctx, object->publicArea.unique.rsa.t.size,
                             object->publicArea.unique.rsa.t.buffer);
    // Embed this in a SEQUENCE tag and length in for the key, exponent sequence
    ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE); // SEQUENCE (3rd)
                                 
    // Embed this in a BIT STRING
    ASN1EndEncapsulation(ctx, ASN1_BITSTRING);

    // Now add the formatted SEQUENCE for the RSA public key OID. This is a
    // fully constructed value so it doesn't need to have a context started
    X509PushAlgorithmIdentifierSequence(ctx, OID_PKCS1_PUB);

    return ASN1EndEncapsulation(ctx, ASN1_CONSTRUCTED_SEQUENCE);
}

#endif // ALG_RSA
