summaryrefslogtreecommitdiff
path: root/Core/EM/CryptoPkg/CryptoDxe.c
blob: 8c40af11e1bb960fb95be8985ac4f519558b4124 (plain)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
422
423
424
425
426
427
428
429
430
431
432
433
434
435
436
437
438
439
440
441
442
443
444
445
446
447
448
449
450
451
452
453
454
455
456
457
458
459
460
461
462
463
464
465
466
467
468
469
470
471
472
473
474
475
476
477
478
479
480
481
482
483
484
485
486
487
488
489
490
491
492
493
494
495
496
497
498
499
500
501
502
503
504
505
506
507
508
509
510
511
512
513
514
515
516
517
518
519
520
521
522
523
524
525
526
527
528
529
530
531
532
533
534
535
536
537
538
539
540
541
542
543
544
545
546
547
548
549
550
551
552
553
554
555
556
557
558
559
560
561
562
563
564
565
566
567
568
569
570
571
572
573
574
575
576
577
578
579
580
581
582
583
584
585
586
587
588
589
590
591
592
593
594
595
596
597
598
599
600
601
602
603
604
605
606
607
608
609
610
611
612
613
614
615
616
617
618
619
620
621
622
623
624
625
626
627
628
629
630
631
632
633
634
635
636
637
638
639
640
641
642
643
644
645
646
647
648
649
650
651
652
653
654
655
656
657
658
659
660
661
662
663
664
665
666
667
668
669
670
671
672
673
674
675
676
677
678
679
680
681
682
683
684
685
686
687
688
689
690
691
692
693
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712
713
714
715
716
717
718
719
720
721
722
723
724
725
726
727
728
729
730
731
732
733
734
735
736
737
738
739
740
741
742
743
744
745
746
747
748
749
750
751
752
753
754
755
756
757
758
759
760
761
762
763
764
765
766
767
768
769
770
771
772
773
774
775
776
777
778
779
780
781
782
783
784
785
786
787
788
789
790
791
792
793
794
795
796
797
798
799
800
801
802
803
804
805
806
807
808
809
810
811
812
813
814
815
816
817
818
819
820
821
822
823
824
825
826
827
828
829
830
831
832
833
834
835
836
837
838
839
840
841
842
843
844
845
846
847
848
849
850
851
852
853
854
855
856
857
858
859
860
861
862
863
864
865
866
867
868
869
870
871
872
873
874
875
876
877
878
879
880
881
882
883
884
885
886
887
888
889
890
891
892
893
894
895
896
897
898
899
900
901
902
903
904
905
906
907
908
909
910
911
912
913
914
915
916
917
918
919
920
921
922
923
924
925
926
927
928
929
930
931
932
933
934
935
936
937
938
939
940
941
942
943
944
945
946
947
948
949
950
951
952
953
954
955
956
957
958
959
960
961
962
963
964
965
966
967
968
969
970
971
972
973
974
975
976
977
978
979
980
981
982
983
984
985
986
987
988
989
990
991
992
993
994
995
996
997
998
999
1000
1001
1002
1003
1004
1005
1006
1007
1008
1009
1010
1011
1012
1013
1014
1015
1016
1017
1018
1019
1020
1021
1022
1023
1024
1025
1026
1027
1028
1029
1030
1031
1032
1033
1034
1035
1036
1037
1038
1039
1040
1041
1042
1043
1044
1045
1046
1047
1048
1049
1050
1051
1052
1053
1054
1055
1056
1057
1058
1059
1060
1061
1062
1063
1064
1065
1066
1067
1068
1069
1070
1071
1072
1073
1074
1075
1076
1077
1078
1079
1080
1081
1082
1083
1084
1085
1086
1087
1088
1089
1090
1091
1092
1093
1094
1095
1096
1097
1098
1099
1100
1101
1102
1103
1104
1105
1106
1107
1108
//**********************************************************************
//**********************************************************************
//**                                                                  **
//**        (C)Copyright 1985-2013, American Megatrends, Inc.         **
//**                                                                  **
//**                       All Rights Reserved.                       **
//**                                                                  **
//**        5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093         **
//**                                                                  **
//**                       Phone: (770)-246-8600                      **
//**                                                                  **
//**********************************************************************
//**********************************************************************
//**********************************************************************
// $Header: /Alaska/BIN/Modules/CryptoPkg/CryptoDxe.c 31    8/15/13 9:43a Alexp $
//
// $Revision: 31 $
//
// $Date: 8/15/13 9:43a $
//**********************************************************************
// Revision History
// ----------------
// $Log: /Alaska/BIN/Modules/CryptoPkg/CryptoDxe.c $
// 
// 31    8/15/13 9:43a Alexp
// Pkcs7Verify() - add TimeStamp signature processing(UEFI ECR1009)
// Pkcs7Verify() - enable code branch for PKCS7_MUTEX_LOCK
// 
// 30    8/02/13 4:26p Alexp
// Moved EFI_HASH_ALGORITHM_SHA256_NOPAD_GUID under AmiDigSig 
// Protocol defining header file
// 
// 29    8/02/13 4:00p Alexp
// 1. Fix logic to get FwKey from a Hob. 
//  Key is copied into pre-allocated buffer by driver entry point
// 2. Optimized Pkcs7Verify. 
// 
// 27    6/20/13 1:55p Alexp
// Impove FwKey handling. CryptoPEI pass FwKey location to Dxe via Hob
// Add mor parameter checking including 
//  check for FwKey location to be mapped to Flash FV_BB
// 
// 26    1/11/13 10:07a Alexp
// GetKey(): fix MemCpy() 2nd argument. Was not copying a key buffer.
// 
// 25    12/28/12 2:50p Alexp
// EIP#110823:The system cannot boot to UEFI(WIN8) OS if the Secure Flash
// Pkg module is not supported
//  Update GetKey(); InSmmFunction() and NotInSmmFunction() routines 
//   to handle cases when Fw Key FFS file not present
// 
// 23    12/10/12 6:09p Alexp
// Bug fix: InSmmFunction() the Key pointer gKeyBuf was to updated to Key
// in SMM region
// 
// 21    12/07/12 4:08p Alexp
// disable debug crypto traces in SMM
// 
// 20    11/29/12 3:10p Alexp
// Pkcs7Verify()- 
// Fix error checking for input **Data parameter : Data == NULL
// 
// 18    11/16/12 5:10p Alexp
// EIP#105015: Add handling of x509 format for Platform FW Key in FFS
// files
// 
// 16    8/28/12 12:33p Alexp
// UEFI 2.3.1. ErrataC. 
// Added support for new Hash types 
// defined by a GUID with 'NOPAD' keyword:
// EFI_HASH_ALGORITHM_SHA1_NOPAD_GUID
// EFI_HASH_ALGORITHM_SHA256_NOPAD_GUID
// 
// 15    8/22/12 4:28p Alexp
//  Added support in Pkcs7Parse() for new operations:
//      Pkcs7CertValidateGetSignerKey -  n-modulus of Signer key
//      Pkcs7CertGetMatchInCertChain  - true if found leaf cert matching
// to
//         trust cert
//      Pkcs7CertValidateGetCAKey,     - n-modulus of CA key if detected
// in
//          x509 chain
//      Pkcs7GetCAKey, Pkcs7GetSignerKey
// 
// 12    7/23/12 4:12p Alexp
// Pkcs7Verify:
// Bug Fix. For condition:Pkcs7CertValidateGetMatchInCertChain return
// success if leaf cert is found. Before code fell down testing
// certificate and could return error potentially misleading Caller of this
// function.
// 
// 11    7/18/12 8:57p Alexp
// Pkcs7Verify
// fix bug - "reason" var was used uninitialized
// 
// 10    5/04/12 9:49a Alexp
// [TAG]          EIP89280
// [Category]      Improvement
// [Description]      When secure boot enable with secure flash disable, in
// Security page of setup menu same items will exist twice
// Fix: Install Crypto Protocols even if Root FW Key not found (brought in
// by Sec Flash module)
// [Files]          cryptoDxe.c
// 
// 9     4/11/12 5:27p Alexp
// Add new operand within Pkcs7Validate:
// Pkcs7CertValidateGetMatchInCertChain
// 
// 7     3/02/12 10:43a Alexp
// Remove dependency on SDL Token FWKEY_FORMAT to determine format of PR
// Key file
// Try different Ffs GUIDs to select proper Key format: SHA or RSA
// 
// 6     2/29/12 4:04p Alexp
// 1. Add VerifyKey
// 2. Fix GetKey (API call will be deprecated as not needed) to move PR
// Key from file to local storage
// 3. Use FWKEY_FORMAT to process VerifyKey according to file format. Hash
// input RSA2048 key for comparison if PR Key stored as Hash
// 
// 5     11/14/11 12:01p Alexp
// InSmmFunction()->InstallMultipleProtocols() Change the HAndle argument
// from ImageHandle to a DummyHandle=NULL
// 
// 4     6/30/11 5:45p Alexp
// GetRawImage: added freepool(HandleBuffer)
// 
// 3     6/27/11 5:30p Alexp
// removed commented out lines
// 
// 2     6/22/11 5:46p Alexp
// update deafult Crypto trace level
// 
// 1     6/13/11 5:19p Alexp
// 
// 7     6/10/11 6:21p Alexp
// added new Pkcs7 parse  Operation: Pkcs7CertValidateGetSignerKeyHash
// 
// 6     6/09/11 9:57a Alexp
// always parse PKCS7 cert
// 
// 5     5/18/11 1:18p Alexp
// edited text in func headers
// 
// 4     5/17/11 12:53p Alexp
// fix WDK Level4 compiler warnings
// 
// 3     5/11/11 7:38p Alexp
// remove set trace level call. If needed, modify Sdl token in Crypto Src
// 
// 2     5/10/11 6:55p Alexp
// use external Guid variable declarations
// 
// 1     5/06/11 6:11p Alexp
// initial module release
// 
// 10    4/22/11 4:20p Alexp
// cleaned up CryptoGetRawImage 
// 
// 9     4/18/11 7:09p Alexp
// 
// 8     4/11/11 12:52p Alexp
// remove ASN1 as possible Key buf format. 
// 
// 7     4/05/11 6:31p Alexp
// remove content from GetKey.  We may add get FwSig key in later
// revisions
// 
// 6     3/31/11 6:14p Alexp
// hashing of SpcIndirect data is done inside Pkcs Validate
// 
// 
//**********************************************************************
#include <Token.h>
#include <AmiDxeLib.h>
#include "Setup.h"
#include "AmiCertificate.h"
#include <Protocol\AmiDigitalSignature.h>
#include <Protocol\Hash.h>

#include <cryptlib.h>

#include <Protocol\SmmBase.h> // used for SMM Malloc

//
// Global variables
//
extern EFI_BOOT_SERVICES    *pBS;
extern EFI_RUNTIME_SERVICES *pRS;

static EFI_GUID gPRKeyGuid = PR_KEY_GUID;

// The ptr to a buffer containing the Fw Platform Key
// Key Image is copied from .ffs to a local buffer. 
// It is a safe location in case of AmiSig running in SMM
static CRYPT_HANDLE gKey = {{0},0,NULL};

// Second part of the Public key, public exponent (e), 
// must be hardwired in the FW elsewhere(recommended value of e=65537).
//
// SDL Hardwired value of publicExponent--e
const UINT8  KeyE[] = {E_CONST}; // 0x10001
const UINT32 LenE = sizeof(KeyE);
const INT32  saltlen = PSS_SIG_SALTLEN; // 8
static UINT8 Rsa2048Sig[DEFAULT_RSA_SIG_LEN];

// Pkcs7 Certificate private var
//struct pkcs7_signed_data_st* PKCS7cert = NULL;
// Mutex to control Memory reset during atomic Pkcs7 operations
static UINT8  MutexLock = RESET;

//----------------------------------------------------------------------------
// Crypto Function prototypes
//----------------------------------------------------------------------------
EFI_STATUS
Pkcs1Verify (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN CRYPT_HANDLE *PublicKey,
  IN CRYPT_HANDLE *Hash,
  IN VOID         *Signature,
  IN UINTN        SignatureSize,
  IN UINT32       Flags
  );

EFI_STATUS
Pkcs7Verify (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN CONST UINT8 *P7Data,
  IN UINTN        P7Size,
  IN CONST UINT8 *TrustedCert,
  IN UINTN        CertSize,
  IN OUT UINT8  **Data,
  IN OUT UINTN   *DataSize,
  IN UINT8        Operation,
  IN UINT32       Flags
  );

EFI_STATUS
Hash(
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL  *This,
  IN CONST EFI_GUID   *HashAlgorithm,
  IN UINTN            Num_elem,
  IN CONST UINT8      *Addr[],
  IN CONST UINTN      *Len,
  OUT UINT8           *Hash
  );

EFI_STATUS
GetKey (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL  *This,
  OUT CRYPT_HANDLE  *Key,
  IN  EFI_GUID      *AlgId,
  IN  UINTN          KeyLen,
  IN  UINT32         Flags
  );

EFI_STATUS
VerifyKey (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN EFI_GUID       *AlgId,
  IN CRYPT_HANDLE   *Key
  );
//----------------------------------------------------------------------------
// Crypto Protocol Identifiers
//----------------------------------------------------------------------------
AMI_DIGITAL_SIGNATURE_PROTOCOL  mAmiSig = {
  Pkcs1Verify,
  Pkcs7Verify,
  Hash,
  GetKey,
  VerifyKey
};

//----------------------------------------------------------------------------
// Crypto Function Implementation
//----------------------------------------------------------------------------

//**********************************************************************
//<AMI_PHDR_START>
//
// Procedure:  Hash
//
// Description:    Allows creating a hash of an arbitrary message digest using one or more hash algorithms
//
// Input:
//      This          Pointer to the AMI_DIGITAL_SIGNATURE_PROTOCOL instance.
//      HashAlgorithm Points to the EFI_GUID which identifies the algorithm to use.
//      num_elem      Number of blocks to be passed via next argument:addr[]
//      addr[]        Pointer to array of UINT8* addresses of data blocks to be hashed
//      len           Pointer to array of integers containing length of each block listed by addr[]
//      Hash          Holds the resulting hash computed from the message.
//
// Output:    
//      EFI_SUCCESS           Hash returned successfully.
//      EFI_INVALID_PARAMETER Message or Hash is NULL
//      EFI_UNSUPPORTED       The algorithm specified by HashAlgorithm is not supported by this
//                            driver.
//
//<AMI_PHDR_END>
//**********************************************************************
EFI_STATUS
Hash(
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL  *This,
  IN CONST EFI_GUID               *HashAlgorithm,
  IN UINTN                        num_elem,
  IN CONST UINT8                  *addr[],
  IN CONST UINTN                  *len,
  OUT UINT8                       *Hash
  )
{
    BOOLEAN     bSha1 = FALSE, bSha256 = FALSE;
    UINT32      HashLen=SHA256_DIGEST_SIZE;

// Support only SHA1 & SHA256 hashes
    if(!guidcmp((EFI_GUID*)HashAlgorithm, &gEfiHashAlgorithmSha1Guid) ||
       !guidcmp((EFI_GUID*)HashAlgorithm, &gEfiHashAlgorithmSha1NoPadGuid)) 
    {
        bSha1 = TRUE;
        HashLen = SHA1_DIGEST_SIZE;
    }
    else 
        if(!guidcmp((EFI_GUID*)HashAlgorithm, &gEfiHashAlgorithmSha256Guid) ||
           !guidcmp((EFI_GUID*)HashAlgorithm, &gEfiHashAlgorithmSha256NoPadGuid))
        {
            bSha256 = TRUE;
            HashLen = SHA256_DIGEST_SIZE;
        }
         else
            return EFI_UNSUPPORTED;

    MemSet(Hash, HashLen, 0);
    if(bSha1)
        sha1_vector(num_elem, addr, len, Hash);
    else
        sha256_vector(num_elem, addr, len, Hash);

    return  EFI_SUCCESS;
}

//**********************************************************************
//<AMI_PHDR_START>
//
// Procedure:  Pkcs7Verify
//
// Description:    Verifies the validity of a PKCS#7 signed data as described in "PKCS #7: Cryptographic
//                 Message Syntax Standard".
//                 Function perfors several operations on input Pkcs7 certificate 
//                 based on the input Operation argument
//                 1. Verifies validity of the signature contained inside the Certificate
//                    This function decrypts the signature with the Public key from the Signer certificate 
//                    and then compares the decrypted value to the input Data
//                 2. Extracts the Signer certificate in format of x509
//                 3. Extracts Root CA certificate that used to sign the Signer certificate.
//
//  Input:
//      This         Pointer to the AMI_DIGITAL_SIGNATURE_PROTOCOL instance.
//      P7Data       Pointer to the PKCS#7 DER encoded message to verify.
//      P7Size       Size of the PKCS#7 message in bytes.
//      TrustedCert  Pointer to a trusted/root X509 certificate encoded in DER, which
//                   is used for certificate chain verification.
//      CertSize     Size of the trusted certificate in bytes.
//      Data         Pointer to the content to be verified/returned at
//      DataSize     Size of Data in bytes
//      Operation    Specifies different tasks to perform:
//                   0-Function presence check. Must return EFI_SUCCESS 
//                   1-Validate Certificate
//                   2-Return Signer Certificate in *Data. Performs cert chaining and time based validity tests
//                   3-Return Root CA certificate referenced by Signer. Used to compare with Trusted Cert in FW
//                   4-Validate Root Certificate
//                   5-Validate Signer Certificate Chain
//                   6-Return Signature Digest Algorithm
//                   7-Validate Certificate, return Root CA Certificate
//                   8-Validate Certificate, return Signer Certificate
//                   9-Validate Certificate, return Signer Key Hash (SHA256)
//                   10-Validate Certificate and return Success if match is found between Trust and any leaf certificates in the Signer chain
//                   11-Validate Certificate, return ptr in *Data to n-modulus of a Signer Key
//                   12-Return Success if match is found between Trust and any leaf certificates in the Signer chain
//                   13-Validate Certificate, return ptr in *Data to n-modulus of a Root CA Key
//                   14-Return ptr in *Data to n-modulus of a Root CA Key
//                   15-Return ptr in *Data to n-modulus of a Signer Key
//                   16-Validate TimeStamp certificate chain in Pkcs7 Certificate
//                   17-255 reserved values
//      Flags        Specifies additional flags to further customize the signing/verifying behavior.
//
// Output:    
//    EFI_SUCCESS              The specified PKCS#7 signed data is valid
//    EFI_SECURITY_VIOLATION   Invalid PKCS#7 signed data.
//    EFI_ACCESS_DENIED        The Trusted certificate does not have a match in SignedData.certificate store.
//    EFI_INVALID_PARAMETER    The size of input message or signature does not meet the criteria 
//                             of the underlying signature algorithm.
//
//<AMI_PHDR_END>
//**********************************************************************
EFI_STATUS
Pkcs7Verify (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN CONST UINT8 *P7Data,
  IN UINTN        P7Size,
  IN CONST UINT8 *TrustedCert,
  IN UINTN        CertSize,
  IN OUT UINT8   **Data,
  IN OUT UINTN   *DataSize,
  IN UINT8        Operation,
  IN UINT32       Flags
  )
{
    EFI_STATUS  Status;
    INTN        err, reason;
    static struct pkcs7_signed_data_st* PKCS7cert;
    struct x509_certificate *x509SignCert;
    struct x509_certificate *x509TrustCert;
    UINT8  *x509SignCertPtr, *x509RootCertPtr;
    UINTN  SignCert_len, CARootCert_len;
    struct pkcs7_cert_revoke_info   revokeInfo;
    EFI_CERT_X509_SHA256     *revokeCert;

// Mutex functionality:
/*
Structure mutex
Mutual exclusion (mutex) semaphore locking mechanism used to serialise interthread intraprocess activities.
*/
#if PKCS7_MUTEX_LOCK == 1
    switch(Flags & 0x3) {
        case RESET:
            ResetCRmm();    // first time needs mem clean
            MutexLock = RESET;
            PKCS7cert = NULL;
          break;

        case LOCK:
            ResetCRmm();    // first time needs mem clean
            MutexLock = LOCK;
            PKCS7cert = NULL;
          break;

        case RELEASE:       // clear memory after execute routines
            if(MutexLock == RESET)
                ResetCRmm();
            MutexLock = RESET;
          break;

        case KEEP:      
            if(MutexLock == RESET)
                ResetCRmm();
          break;

        default: 
            ResetCRmm();
    }
#else
    PKCS7cert = NULL;
    ResetCRmm();
#endif
/*
    Notes of implementation:
    
    IN order to allocate the Pool for returned Cert (Signer or CA) 
    -run getCert with Size 0 -> it will return real cert size
    -allocate buffer of Size and re-run with new size
    OR caller is responsible for freeng the Data buffer
    OR caller always allocates sufficient size == Trusted Cert + 1k 

    GetSignerCert
        parse Pkcs7 to *Pkcs7cert
        get SignerName from SignerInfo.serialName
        find x509 cert matching Signer name
    GetCACert
        run GetSignerCert
        run cert chain valid for that cert

    ValidateSignCert - internal Cert validation, returns Root CA Cert ptr
    ValidateTrustCert - external Trust Cert validation, returns Sign Cert ptr
        
*/
    err = -1;
    reason=0;

    switch(Operation){
        case Pkcs7Arg0: 
            Status = EFI_SUCCESS; 
            err = 0;
            break;    
// Validate Digest and returns Root CA cert or Signer Cert
        case Pkcs7CertGetMatchInCertChain:
        case Pkcs7CertValidateGetMatchInCertChain: // obsolete
            reason = -1; // non-0 reason argument forces Pkcs7_Chain_Validate to scan for matching Cert in the Chain if other checks failed
        case Pkcs7CertValidate: 
        case Pkcs7CertValidateGetCAKey:
        case Pkcs7CertValidateGetCACert:
        case Pkcs7CertValidateGetSignerKey:
        case Pkcs7CertValidateGetSignerCert:
        case Pkcs7CertValidateGetSignerKeyHash:
        case Pkcs7TimeStampCertValidateGet:
        	if(Data == NULL || DataSize == NULL)
                break;
        case Pkcs7ValidateRootCert:
              if (!PKCS7cert) PKCS7cert = Pkcs7_parse_Authenticode_certificate(P7Data, P7Size);
            if (!PKCS7cert) 
                break;
            // verify Pkcs7 Signing Cert chain up to the TrustCert...if provided
            if(TrustedCert && CertSize) {
                x509TrustCert = x509_certificate_parse(TrustedCert, CertSize);
                if(x509TrustCert) {
                    err = Pkcs7_x509_certificate_chain_validate_with_timestamp(PKCS7cert, (Operation == Pkcs7TimeStampCertValidateGet), x509TrustCert, NULL, (int*)&reason);
                } else {
                // potentially a TimeStamped revocation cert 
                    revokeCert = (EFI_CERT_X509_SHA256*)TrustedCert;
                    if(CertSize == sizeof(EFI_CERT_X509_SHA256))
                        revokeInfo.ToBeSignedHashLen = 32;
                    else 
                        if(CertSize == sizeof(EFI_CERT_X509_SHA384))
                            revokeInfo.ToBeSignedHashLen = 48;
                        else 
                            if(CertSize == sizeof(EFI_CERT_X509_SHA512))
                                revokeInfo.ToBeSignedHashLen = 64;
                            else { // unsupported Hash struct
                                err = -1;
                                break;
                            }
                    revokeInfo.ToBeSignedHash = (UINT8*)&revokeCert->ToBeSignedHash;
                   	err = Pkcs7_x509_certificate_chain_validate_with_timestamp(PKCS7cert, FALSE, NULL, &revokeInfo, (int*)DataSize);
                }

                if(err == -1 || 
                    Operation==Pkcs7CertGetMatchInCertChain || 
                    Operation == Pkcs7ValidateRootCert
                )
                    break;     // break(err=0) if leaf found to match trust cert
            }
            // x509SignCert== NULL -> extract SignCert from Pkcs7 crt
            err = Pkcs7_certificate_validate_digest(PKCS7cert, NULL, (UINT8*)*Data, (UINTN)*DataSize);
            if(!err) {
                switch(Operation){
           //Returns CA Root cert after successfully validating Cert chain and signature digest
                    case Pkcs7CertValidateGetCACert:
                        err = Pkcs7_return_cerificate_ptr(PKCS7cert, Data, DataSize, &x509SignCertPtr, (size_t*)&SignCert_len);
                        break;
           //Returns Signing Key from Cert after successfully validating Cert chain and signature digest
                    case Pkcs7CertValidateGetSignerCert:
                        err = Pkcs7_return_cerificate_ptr(PKCS7cert, &x509RootCertPtr, (size_t*)&CARootCert_len, Data, DataSize);
                        break;
                    case Pkcs7CertValidateGetSignerKeyHash:
                        err = Pkcs7_x509_return_signing_Key(PKCS7cert, &x509SignCertPtr, &SignCert_len);
                        if(!err) {
                            Hash(This, &gEfiHashAlgorithmSha256Guid, 1, &x509SignCertPtr, (const UINTN*)&SignCert_len, *Data); 
                            *DataSize=SHA256_DIGEST_SIZE;
                        }
                        break;
                    case Pkcs7CertValidateGetSignerKey:
                        err = Pkcs7_x509_return_signing_Key(PKCS7cert, Data, DataSize);
                        break;
                    case Pkcs7CertValidateGetCAKey:
                        err = Pkcs7_return_cerificate_ptr(PKCS7cert, Data, DataSize, &x509SignCertPtr, (size_t*)&SignCert_len);
                        if(!err)
                            err = Pkcs7_x509_return_Cert_pubKey(*Data, *DataSize, Data, DataSize);
                        break;
                    case Pkcs7TimeStampCertValidateGet:
                        err = Pkcs7_certificate_validate_timestamp_digest(PKCS7cert, (long*)DataSize);
                        break;
                 } 
            }
            break;

// returns Ptr within input P7Data DER buffer to RootCA cert or Signing Cert
        case Pkcs7GetSignerCert: 
        case Pkcs7GetCACert: 
        case Pkcs7GetSignerKey:
        case Pkcs7GetCAKey:
            if(Data == NULL || DataSize == NULL) {
                break;    
            }
              if (!PKCS7cert) PKCS7cert = Pkcs7_parse_Authenticode_certificate(P7Data, P7Size);
            if (PKCS7cert) {
                err = Pkcs7_return_cerificate_ptr(PKCS7cert, &x509RootCertPtr, (size_t*)&CARootCert_len, &x509SignCertPtr, (size_t*)&SignCert_len);
                if(!err) {
                    switch(Operation){
                        case Pkcs7GetSignerCert: 
                            *DataSize = SignCert_len;
                            *Data   = x509SignCertPtr;
                            break;
                        case Pkcs7GetCACert: 
                            *DataSize = CARootCert_len;
                            *Data = x509RootCertPtr;
                            break;
                        case Pkcs7GetCAKey:
                            err = Pkcs7_x509_return_Cert_pubKey(x509RootCertPtr, CARootCert_len, Data, DataSize);
                            break;
                        case Pkcs7GetSignerKey:
                            err = Pkcs7_x509_return_Cert_pubKey(x509SignCertPtr, SignCert_len, Data, DataSize);
                            break;
                    }
                }
            }
            break; 

        case x509ValidateCertChain:
        //Validates Signer certificate's key against Trusted Cert
            x509SignCert = x509_certificate_parse(P7Data, P7Size);
            if(TrustedCert && CertSize)
                x509TrustCert = x509_certificate_parse(TrustedCert, CertSize);
            if (x509TrustCert && x509SignCert)
               err = x509_certificate_chain_validate(x509TrustCert, x509SignCert, (int*)&reason);
            break;

        case Pkcs7GetDigestAlgorithm: 
        // validate input params.
            if(Data == NULL || DataSize == NULL) {
                break;
            }
              if (!PKCS7cert) PKCS7cert = Pkcs7_parse_Authenticode_certificate(P7Data, P7Size);
            if (PKCS7cert) {
                err = Pkcs7_return_digestAlgorithm(PKCS7cert, (UINT8*)*Data);
                if(!err)
                    *DataSize = sizeof(UINT8);
             }
            break;

        default: 
            Status = EFI_INVALID_PARAMETER;
            err = -1;
    }

// Security concern, memory heap is being cleared on exit 
    if(err)  
        MutexLock = RESET;

#if PKCS7_MUTEX_LOCK == 1
    if(MutexLock == RESET) 
#endif
    {
        ResetCRmm();
        PKCS7cert = NULL;
    }

// failed to process
    Status = !err ? EFI_SUCCESS:EFI_SECURITY_VIOLATION;

    return Status;
}

//**********************************************************************
//<AMI_PHDR_START>
//
// Procedure:  Pkcs1Verify
//
// Description:    Function verifies that the specified signature matches the specified hash. 
//                 Verifies the RSA-SSA signature with EMSA-PKCS1-v1_5 encoding scheme defined in
//                 RSA PKCS#1.
//                 This function decrypts the signature with the provided key and then compares 
//                 the decrypted value to the specified hash value
//
//  Input:
//    This          Pointer to the AMI_DIGITAL_SIGNATURE_PROTOCOL instance.
//    PublicKey     Handle to a key used for verifying signatures. This handle must be identifying a public key.
//    Hash          Handle of the hash object to verify.
//    Signature     Pointer to the signature data to be verified.
//    SignatureSize The size, in bytes, of the signature data.
//    Flags         Specifies additional flags to further customize the signing/verifying behavior.
//
// Output:    
//    EFI_SUCCESS              The signature is successfully verified.
//    EFI_SECURITY_VIOLATION   The signature does not match the given message.
//    EFI_ACCESS_DENIED        The key could not be used in signature operation.
//    EFI_INVALID_PARAMETER    The size of input message or signature does not meet the criteria 
//                             of the underlying signature algorithm.
//
//<AMI_PHDR_END>
//**********************************************************************
EFI_STATUS
Pkcs1Verify (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN CRYPT_HANDLE   *PublicKey,
  IN CRYPT_HANDLE   *Hash,
  IN VOID           *Signature,
  IN UINTN           SignatureSize,
  IN UINT32          Flags
  )
{
    EFI_STATUS  Status;
    INTN        err;
    struct      crypto_rsa_key *key = NULL;
    UINT16      Size = (UINT16)(PublicKey->BlobSize);
    size_t     *sig_len=(size_t*)&SignatureSize;
    INT32       modulus_bitlen = DEFAULT_RSA_SIG_LEN << 3;
    UINT32      HashLen;

// Only supporting RSASSA_PKCS1V15 & PSS signature types
    if(!((Flags & EFI_CRYPT_RSASSA_PKCS1V15) || 
         (Flags & EFI_CRYPT_RSASSA_PSS)) )
            return EFI_INVALID_PARAMETER;

    if(!PublicKey || !Hash || !Signature)
        return EFI_INVALID_PARAMETER;

    ResetCRmm();
    MutexLock = RESET;

// For now Public Key is supported in 2 formats: RAW 256 bytes and ASN.1 Integer
    if(!guidcmp(&PublicKey->AlgGuid, &gEfiCertRsa2048Guid))
        key = crypto_import_rsa2048_public_key(PublicKey->Blob, Size, (UINT8*)&KeyE, LenE);
    else 
        return EFI_INVALID_PARAMETER;

// 2 hash types supporte: SHA1 & SHA256
    if(!guidcmp(&Hash->AlgGuid, &gEfiHashAlgorithmSha256Guid))
        HashLen = SHA256_DIGEST_SIZE;
    else if(!guidcmp(&Hash->AlgGuid, &gEfiHashAlgorithmSha1Guid))
            HashLen = SHA1_DIGEST_SIZE;
        else
            HashLen = SHA256_DIGEST_SIZE;

    if(key == NULL )
        err = -1;
    else
        err = crypto_rsa_exptmod((const UINT8*)Signature, (size_t)SignatureSize, (UINT8*)&Rsa2048Sig, sig_len, key, 0);

// locate Hash inside the decrypted signature body and compare it with given Hash;
    if(!err) {
        if(Flags & EFI_CRYPT_RSASSA_PKCS1V15)
        {
            // Validate PKCS#1 Padding
//            err = pkcs_1_v1_5_decode((const UINT8 *)&Hash->Blob, HashLen, (const UINT8 *)&Rsa2048Sig, (unsigned long)*sig_len);
            err = MemCmp((void*)(UINTN)Hash->Blob, (void*)(UINTN)((UINT32)(UINTN)Rsa2048Sig + (UINT32)(*sig_len - HashLen)), HashLen);
        }
        else //(Flags & EFI_CRYPT_RSASSA_PSS))
            // Validate PKCS_1 PSS Signature: padding & hash
            err = pkcs_1_pss_decode((UINT8*)Hash->Blob, HashLen,(const unsigned char *)&Rsa2048Sig, (unsigned long)*sig_len, 
                    saltlen,  modulus_bitlen);
    }

    Status = !err ? EFI_SUCCESS:EFI_SECURITY_VIOLATION;

// Security concern, memory heap is being cleared on exit 
    ResetCRmm();

    return Status;
}

//<AMI_PHDR_START>
//----------------------------------------------------------------------
// Procedure:   CryptoGetFwKey
//
// Description:    Passes FwKey info from a Hob to an external Key handler structure
//
//  Input:       Key       - ptr to the buffer to hold the target key parameters
//
// Output:       Status
//
//               EFI_NOT_FOUND  - Can't find the Key Hob
//               EFI_INVALID_PARAMETER - Wrong KeyID
//               EFI_LOAD_ERROR - Load fail.
//               EFI_SUCCESS    - Load success.
//
//----------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS
CryptoGetFwKey (
  IN EFI_SYSTEM_TABLE *SystemTable,
  IN CRYPT_HANDLE   *Key
)
{
    EFI_STATUS    Status=EFI_NOT_FOUND;
    FW_KEY_HOB     *pFwKeyHob;
    static EFI_GUID HobListGuid = HOB_LIST_GUID;

    if(!Key) return EFI_INVALID_PARAMETER;

    pFwKeyHob = GetEfiConfigurationTable(SystemTable, &HobListGuid);
    if (pFwKeyHob!=NULL) {
        Status = FindNextHobByGuid(&gPRKeyGuid, &pFwKeyHob);
        if(!EFI_ERROR(Status)) {
// make sure the Key buffer is mapped to FV_BB address space 
/*
            if(!((UINT32)pFwKeyHob->KeyAddress > FV_BB_BASE && 
                 (UINT64)((UINT32)pFwKeyHob->KeyAddress+pFwKeyHob->KeySize) < 
                    (UINT64)(FV_BB_BASE+(UINT64)FV_BB_BLOCKS*FLASH_BLOCK_SIZE))) {
                return EFI_NOT_FOUND;
*/
            Key->Blob = (UINT8*)(UINT32)pFwKeyHob->KeyAddress;
            Key->BlobSize = pFwKeyHob->KeySize;
            Key->AlgGuid  = pFwKeyHob->KeyGuid;
        }
    }

    TRACE(((UINTN) -1,"Get Key File %g: %r\n(%lx, %dbytes)=%X,%X\n", Key->AlgGuid, Status, Key->Blob, Key->BlobSize, Key->Blob[0], Key->Blob[1]));

    return Status;
}
//**********************************************************************
//<AMI_PHDR_START>
//
// Procedure:  GetKey
//
// Description:    Return Firmware Signing Key from a local storage
//
//  Input:
//    This         Pointer to the AMI_DIGITAL_SIGNATURE_PROTOCOL instance.
//    Key          ptr to the buffer to hold the target key
//    AlgId        Key GUID
//    KeyLen       Length of the target Key buffer
//    Flags        Specifies additional flags to further customize the GetKey behavior.
// Output:    
//    EFI_SUCCESS            
//    EFI_ACCESS_DENIED        The key could not be located
//    EFI_INVALID_PARAMETER    Wrong KeyID
//    BUFFER_TOO_SMALL         The Key->BlobSize will contain the size of the buffer to be prepared
//
//<AMI_PHDR_END>
//**********************************************************************
EFI_STATUS
GetKey (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  OUT     CRYPT_HANDLE               *Key,
  IN      EFI_GUID                   *AlgId,
  IN      UINTN                      KeyLen,
  IN      UINT32                     Flags
  )
{
    if(!AlgId || !Key)
        return EFI_INVALID_PARAMETER;

// Guid must match one of the valid keys we can use in Sig verification.
    if(guidcmp(AlgId, &gPRKeyGuid))
        return EFI_UNSUPPORTED;

    if(gKey.Blob && gKey.BlobSize){

// make sure the Key buffer is mapped to FV_BB address space 
/*        if(!((UINT32)gKey.Blob > FV_BB_BASE && 
             (UINT64)((UINT32)gKey.Blob+gKey.BlobSize) < 
                (UINT64)(FV_BB_BASE+(UINT64)FV_BB_BLOCKS*FLASH_BLOCK_SIZE)))
            return EFI_NOT_FOUND;
*/
        Key->BlobSize = gKey.BlobSize;
        Key->AlgGuid = gKey.AlgGuid;
        if(Key->Blob) {
            if(KeyLen < gKey.BlobSize)
                return EFI_BUFFER_TOO_SMALL;
            else
                MemCpy(Key->Blob, gKey.Blob, gKey.BlobSize);
        } else
            Key->Blob = gKey.Blob;// upd address to int buffer

        return EFI_SUCCESS;
    }

    return EFI_NOT_FOUND;
}
//**********************************************************************
//<AMI_PHDR_START>
//
// Procedure:  VerifyKey
//
// Description:    Function compares the input PublicKey against 
//                 Platform Signing Key (PK) image in the flash.
//
//  Input:
//    This            Pointer to the AMI_DIGITAL_SIGNATURE protocol instance.
//    KeyAlgorithm    Points to the EFI_GUID which identifies the PKpub algorithm to use.
//    PublicKey       Handle to a key used for verifying signatures.  This handle must be identifying a public key.
//
// Output:
//    EFI_SUCCESS               The Key is successfully verified.
//    EFI_SECURITY_VIOLATION    The Key does not match current FW key.
//    EFI_ACCESS_DENIED         The key could not be used in signature operation.
//    EFI_INVALID_PARAMETER     The size of input message or signature does not meet the criteria 
//                              of the underlying signature algorithm.
//
//<AMI_PHDR_END>
//**********************************************************************
EFI_STATUS
VerifyKey (
  IN CONST AMI_DIGITAL_SIGNATURE_PROTOCOL *This,
  IN EFI_GUID       *AlgId,
  IN CRYPT_HANDLE   *Key
  )
{
    EFI_STATUS          Status;
    CRYPT_HANDLE        PubKeyHndl;
    UINT8               Hash[SHA256_DIGEST_SIZE]={0};
    UINT8               *KeyFfs, *KeyCmp;
    UINTN               KeyLen;
    UINT32              Flags=0;
    INTN                err;

    if(!AlgId || !Key || !Key->Blob)
        return EFI_INVALID_PARAMETER;

// Guid must match one of the valid keys we can use in Sig verification.
    if(guidcmp(AlgId, &gPRKeyGuid))
        return EFI_UNSUPPORTED;

// Get PRKey
    PubKeyHndl.Blob = NULL;
    PubKeyHndl.BlobSize = 0;
    Status = GetKey(NULL, &PubKeyHndl, AlgId, 0, Flags);
    if(!EFI_ERROR(Status))
    {
        KeyFfs = PubKeyHndl.Blob;
        KeyLen = PubKeyHndl.BlobSize;
        KeyCmp = Key->Blob;

        // If FwKey is Hash of Rsa2048 Key and Key->Algo is Rsa2048 - 
        // prepare Key for SHA256 Hash compare
        if(!guidcmp(&Key->AlgGuid, &gEfiCertRsa2048Guid)) 
        {
            if(!guidcmp(&PubKeyHndl.AlgGuid, &gEfiCertSha256Guid)) 
            {
                KeyCmp = Hash;
                KeyLen = SHA256_DIGEST_SIZE;
                sha256_vector(1, (const UINT8**)&Key->Blob, (const UINTN*)&Key->BlobSize, Hash);
            } else 
                // if FwKey is x509 and Key->Algo - gEfiCertRsa2048Guid:
                // derive nModulus from x509 Key Cert for comparison
                if(!guidcmp(&PubKeyHndl.AlgGuid, &gEfiCertX509Guid)) 
                {
                    KeyFfs = &Rsa2048Sig[0];
                    KeyLen = DEFAULT_RSA_KEY_MODULUS_LEN;
                    ResetCRmm();
                    err = Pkcs7_x509_return_Cert_pubKey((UINT8*)PubKeyHndl.Blob, (UINTN)PubKeyHndl.BlobSize, &KeyFfs,&KeyLen);
                    ResetCRmm();
                    if(err) return EFI_SECURITY_VIOLATION;
                }
        }
        err = MemCmp(KeyFfs, KeyCmp, KeyLen);

        Status = !err ? EFI_SUCCESS:EFI_SECURITY_VIOLATION;
    }
    return Status;
}
//----------------------------------------------------------------------------
// END of Crypto DXE Function definitions
//----------------------------------------------------------------------------

//<AMI_PHDR_START>
//----------------------------------------------------------------------
// Procedure:   InSmmFunction
//
// Description: 
//
// Input:   
//
// Output: 
//
// Returns: 
//
//----------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS InSmmFunction(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS  Status;
    EFI_HANDLE  DummyHandle = NULL;
    UINT8       *pKey;

    TRACE(((UINTN) -1,"Init Crypto API in SMM\n"));
    //
    // Init Crypto lib internal state
    //
    RuntimeCryptLibConstructorInSmm(ImageHandle, SystemTable);
    //  
    // Update Crypto debug traces level
    //
#ifdef EFI_DEBUG
//    wpa_set_trace_level(CRYPTO_trace_level); 
#endif
    //
    // Get location of PRKey from .ffs
    // ReadFfs/FindHob are preboot services whereas Key needs to be available during EFI runtime
    //
    gKey.Blob = NULL;
    Status = CryptoGetFwKey(SystemTable, &gKey);
    if(!EFI_ERROR(Status) && gKey.Blob) {
    // re-allocate buffer in runtime mem 
        pKey = gKey.Blob; // preserve the pointer
        Status = pSmmBase->SmmAllocatePool(pSmmBase, EfiRuntimeServicesData, gKey.BlobSize, (void**)&pKey);
        ASSERT_EFI_ERROR (Status);
        if(!EFI_ERROR(Status)) {
            MemCpy(pKey, gKey.Blob, gKey.BlobSize);
            gKey.Blob = pKey;
        }
    }
    if(EFI_ERROR(Status)) 
        gKey.Blob = NULL;

    TRACE((TRACE_ALWAYS,"Init: GetKey %r (addr %x(%X, %X), %d bytes)\n", Status, gKey.Blob, gKey.Blob[0], gKey.Blob[1], gKey.BlobSize));    

///////////////////////////////////////////////////////////////////////////////////
// Install the SMM clone of Crypto protocols
///////////////////////////////////////////////////////////////////////////////////
    return pBS->InstallMultipleProtocolInterfaces (
            &DummyHandle,
            &gAmiSmmDigitalSignatureProtocolGuid, &mAmiSig,
            NULL
            );
}

//<AMI_PHDR_START>
//----------------------------------------------------------------------------
//
// Procedure:   NotInSmmFunction
//
// Description: This function is called from outside of SMM during SMM registration.
//
// Input:
//  IN EFI_HANDLE       ImageHandle
//  IN EFI_SYSTEM_TABLE *SystemTable
//
// Output: EFI_STATUS
//
//----------------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS NotInSmmFunction(EFI_HANDLE ImageHandle, EFI_SYSTEM_TABLE *SystemTable)
{
    EFI_STATUS  Status;
    UINT8       *pKey;

    TRACE(((UINTN) -1,"Init Crypto API NOT in SMM\n"));
    //
    // Init Crypto lib internal state
    //
    RuntimeCryptLibConstructor(ImageHandle, SystemTable);
    //  
    // Update Crypto debug traces level
    //
#ifdef EFI_DEBUG
    wpa_set_trace_level(CRYPTO_trace_level); 
#else
    wpa_set_trace_level(0); 
#endif
    //
    // Get location of PRKey from .ffs
    // ReadFfs/FindHob are preboot services whereas Key needs to be available during EFI runtime
    //
    gKey.Blob = NULL;
    Status = CryptoGetFwKey(SystemTable, &gKey);
    if(!EFI_ERROR(Status) && gKey.Blob) {
    // re-allocate buffer in runtime mem 
        pKey = gKey.Blob; // preserve the pointer
        Status = pBS->AllocatePool(EfiRuntimeServicesData, gKey.BlobSize, (void**)&pKey);
        ASSERT_EFI_ERROR (Status);
        if(!EFI_ERROR(Status)) {
            MemCpy(pKey, gKey.Blob, gKey.BlobSize);
            gKey.Blob = pKey;
        }
    }
    if(EFI_ERROR(Status)) 
        gKey.Blob = NULL;

    TRACE((TRACE_ALWAYS,"Init: GetKey %r (addr %x(%X, %X), %d bytes)\n", Status, gKey.Blob, gKey.Blob[0], gKey.Blob[1], gKey.BlobSize));    


///////////////////////////////////////////////////////////////////////////////
//
// Install the DXE Crypto protocols
//
///////////////////////////////////////////////////////////////////////////////
    return pBS->InstallMultipleProtocolInterfaces (
            &ImageHandle,
            &gAmiDigitalSignatureProtocolGuid, &mAmiSig,
            NULL
            );
}

//<AMI_PHDR_START>
//----------------------------------------------------------------------------
// Procedure:   CryptoDxe_Init
//
// Description: Entry point of Crypto DXE driver
//
// Input:       EFI_HANDLE           ImageHandle,
//              EFI_SYSTEM_TABLE     *SystemTable
//
// Output:      EFI_STATUS
//
// Modified:
//
// Referrals:
//
// Notes:
//----------------------------------------------------------------------------
//<AMI_PHDR_END>
EFI_STATUS
CryptoDxe_Init (
    IN EFI_HANDLE         ImageHandle,
    IN EFI_SYSTEM_TABLE   *SystemTable
)
{
    InitAmiLib(ImageHandle, SystemTable);
    //
    // Install DXE & SMM Crypto Services
    //

    return InitSmmHandler(ImageHandle, SystemTable, InSmmFunction, NotInSmmFunction);
}

//**********************************************************************
//**********************************************************************
//**                                                                  **
//**        (C)Copyright 1985-2013, American Megatrends, Inc.         **
//**                                                                  **
//**                       All Rights Reserved.                       **
//**                                                                  **
//**        5555 Oakbrook Pkwy, Suite 200, Norcross, GA 30093         **
//**                                                                  **
//**                       Phone: (770)-246-8600                      **
//**                                                                  **
//**********************************************************************
//**********************************************************************