Come crittografare i byte utilizzando il TPM (Trusted Platform Module)


110

Come posso crittografare i byte utilizzando il modulo TPM di una macchina?

CryptProtectData

Windows fornisce un'API (relativamente) semplice per crittografare un BLOB utilizzando l' CryptProtectDataAPI, che possiamo racchiudere in una funzione facile da usare:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //...
}

I dettagli di ProtectBytessono meno importanti dell'idea che puoi usarlo abbastanza facilmente:

  • ecco i byte che voglio crittografare da una chiave segreta contenuta nel file System
  • restituiscimi il BLOB crittografato

Il BLOB restituito è una documentazione non documentata struttura di che contiene tutto il necessario per decrittografare e restituire i dati originali (algoritmo hash, algoritmo di cifratura, salt, firma HMAC, ecc.).

Per completezza, ecco l'implementazione dello pseudocodice di esempio ProtectBytesche utilizza Crypt APIper proteggere i byte:

public Byte[] ProtectBytes(Byte[] plaintext)
{
   //Setup our n-byte plaintext blob
   DATA_BLOB dataIn;
   dataIn.cbData = plaintext.Length;
   dataIn.pbData = Addr(plaintext[0]);

   DATA_BLOB dataOut;

   //dataOut = EncryptedFormOf(dataIn)
   BOOL bRes = CryptProtectData(
         dataIn,
         null,     //data description (optional PWideChar)
         null,     //optional entropy (PDATA_BLOB)
         null,     //reserved
         null,     //prompt struct
         CRYPTPROTECT_UI_FORBIDDEN || CRYPTPROTECT_LOCAL_MACHINE,
         ref dataOut);
   if (!bRes) then
   {
      DWORD le = GetLastError();
      throw new Win32Error(le, "Error calling CryptProtectData");
   }

   //Copy ciphertext from dataOut blob into an actual array
   bytes[] result;
   SetLength(result, dataOut.cbData);
   CopyMemory(dataOut.pbData, Addr(result[0]), dataOut.cbData);

   //When you have finished using the DATA_BLOB structure, free its pbData member by calling the LocalFree function
   LocalFree(HANDLE(dataOut.pbData)); //LocalFree takes a handle, not a pointer. But that's what the SDK says.
}

Come fare lo stesso con il TPM?

Il codice sopra è utile per crittografare i dati solo per la macchina locale. I dati vengono crittografati utilizzando l' Systemaccount come generatore di chiavi (i dettagli, sebbene interessanti, non sono importanti ). Il risultato finale è che posso crittografare i dati (ad esempio una chiave master di crittografia del disco rigido) che possono essere decrittografati solo dalla macchina locale.

Ora è il momento di fare un ulteriore passo avanti. Desidero crittografare alcuni dati (ad esempio una chiave master di crittografia del disco rigido) che possono essere decrittografati solo dal TPM locale. In altre parole, voglio sostituire Qualcomm Trusted Execution Environment ( TEE ) nello schema a blocchi seguente per Android, con il TPM in Windows:

inserisci qui la descrizione dell'immagine

Nota : mi rendo conto che il TPM non esegue la firma dei dati (o se lo fa, non garantisce che la firma degli stessi dati fornirà ogni volta lo stesso output binario). Ecco perché sarei disposto a sostituire "firma RSA" con "crittografia di un BLOB a 256 bit con una chiave associata a hardware" .

Allora dov'è il codice?

Il problema è che la programmazione TPM è completamente non documentata su MSDN . Non sono disponibili API per eseguire operazioni. Invece devi trovarti una copia dello Stack software del Trusted Computing Group (noto anche come TSS) , capire quali comandi inviare al TPM, con i payload, in quale ordine e chiamare la funzione Tbsip_Submit_Command di Window per inviare direttamente i comandi:

TBS_RESULT Tbsip_Submit_Command(
  _In_     TBS_HCONTEXT hContext,
  _In_     TBS_COMMAND_LOCALITY Locality,
  _In_     TBS_COMMAND_PRIORITY Priority,
  _In_     const PCBYTE *pabCommand,
  _In_     UINT32 cbCommand,
  _Out_    PBYTE *pabResult,
  _Inout_  UINT32 *pcbOutput
);

Windows non dispone di API di livello superiore per eseguire azioni.

È l'equivalente morale di provare a creare un file di testo inviando comandi di I / O SATA al disco rigido .

Perché non usare solo pantaloni

Il Trusted Computing Group (TCG) ha definito la propria API: TCB Software Stack (TSS) . Un'implementazione di questa API è stata creata da alcune persone e si chiama TrouSerS . Un ragazzo ha poi trasferito quel progetto su Windows .

Il problema con quel codice è che non è portabile nel mondo Windows. Ad esempio, non puoi usarlo da Delphi, non puoi usarlo da C #. Richiede:

  • OpenSSL
  • pThread

Voglio solo il codice crittografi qualcosa con il mio TPM.

Quanto sopra CryptProtectData non richiede altro che ciò che è nel corpo della funzione.

Qual è il codice equivalente per crittografare i dati utilizzando il TPM? Come altri hanno notato, probabilmente è necessario consultare i tre manuali TPM e creare i BLOB da soli . Probabilmente coinvolge il TPM_sealcomando. Anche se penso di non voler sigillare i dati, penso di volerlo legare :

Binding : crittografa i dati utilizzando la chiave di binding TPM, una chiave RSA univoca derivata da una chiave di archiviazione. Sigillatura : crittografa i dati in modo simile all'associazione, ma in aggiunta specifica uno stato in cui deve essere TPM affinché i dati vengano decrittografati (non protetti)

Provo a leggere i tre volumi richiesti per trovare le 20 righe di codice di cui ho bisogno:

Ma non ho alcun idea di cosa sto leggendo. Se ci fosse qualche tipo di tutorial o esempi, potrei avere una possibilità. Ma sono completamente perso.

Quindi chiediamo Stackoverflow

Allo stesso modo ho potuto fornire:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

qualcuno può fornire l'equivalente corrispondente:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

che fa la stessa cosa, tranne che invece di una chiave bloccata in SystemLSA, è bloccata nel TPM?

Inizio della ricerca

Non so esattamente cosa significhi legare . Ma guardando TPM Main - Part 3 Commands - Specification Version 1.2, c'è una menzione di bind :

10.3 TPM_UnBind

TPM_UnBind prende il BLOB di dati che è il risultato di un comando Tspi_Data_Bind e lo decrittografa per l'esportazione all'utente. Il chiamante deve autorizzare l'uso della chiave che decrittograferà il BLOB in arrivo. TPM_UnBind opera blocco per blocco e non ha alcuna nozione di alcuna relazione tra un blocco e l'altro.

Ciò che è fonte di confusione è che non c'è alcun Tspi_Data_Bindcomando.

Sforzo di ricerca

È orribile come nessuno si sia mai preso la briga di documentare il TPM o il suo funzionamento. È come se passassero tutto il loro tempo a inventare questa cosa fantastica con cui giocare, ma non volessero affrontare il passo doloroso di renderla utilizzabile per qualcosa.

A partire dal libro (ora) gratuito A Practical Guide to TPM 2.0: Using the Trusted Platform Module in the New Age of Security :

Capitolo 3 - Esercitazione rapida su TPM 2.0

Il TPM ha accesso a una chiave privata autogenerata, quindi può crittografare le chiavi con una chiave pubblica e quindi archiviare il BLOB risultante sul disco rigido. In questo modo, il TPM può mantenere un numero virtualmente illimitato di chiavi disponibili per l'uso ma non sprecare preziosa memoria interna. Le chiavi memorizzate sul disco rigido possono essere cancellate, ma è anche possibile eseguirne il backup, il che è sembrato ai progettisti un compromesso accettabile.

Come posso crittografare una chiave con la chiave pubblica del TPM?

Capitolo 4 - Applicazioni esistenti che utilizzano TPM

Applicazioni che dovrebbero utilizzare il TPM ma non lo fanno

Negli ultimi anni, il numero di applicazioni basate sul web è aumentato. Tra questi ci sono backup e archiviazione basati sul Web. Un gran numero di aziende ora offre tali servizi, ma per quanto ne sappiamo, nessuno dei client per questi servizi consente all'utente di bloccare la chiave per il servizio di backup su un TPM. Se ciò fosse fatto, sarebbe sicuramente bello se fosse stato eseguito il backup della chiave TPM stessa duplicandola su più macchine. Questa sembra essere un'opportunità per gli sviluppatori.

In che modo uno sviluppatore blocca una chiave per il TPM?

Capitolo 9 - Heirarchies

CASO D'USO: MEMORIZZAZIONE DELLE PASSWORD DI ACCESSO

Un tipico file di password memorizza gli hash salati delle password. La verifica consiste nel saltare e hashing una password fornita e confrontarla con il valore memorizzato. Poiché il calcolo non include un segreto, è soggetto ad un attacco offline al file della password.

Questo caso d'uso usa una chiave HMAC generata da TPM. Il file della password memorizza un HMAC della password salata. La verifica consiste nel salare e HMAC la password fornita e confrontarla con il valore memorizzato. Poiché un utente malintenzionato offline non dispone della chiave HMAC, non può eseguire un attacco eseguendo il calcolo.

Questo potrebbe funzionare. Se il TPM ha una chiave HMAC segreta e solo il mio TPM conosce la chiave HMAC, allora potrei sostituire "Firma (ovvero crittografia TPM con la sua chiave privata)" con "HMAC". Ma poi nella riga successiva si inverte completamente:

TPM2_Create, specificando una chiave HMAC

Non è un segreto TPM se devo specificare la chiave HMAC. Il fatto che la chiave HMAC non sia segreta ha senso quando ti rendi conto che questo è il capitolo sulle utilità crittografiche fornite dal TPM. Invece di dover scrivere personalmente SHA2, AES, HMAC o RSA, puoi riutilizzare ciò che il TPM ha già in giro.

Capitolo 10 - Chiavi

In quanto dispositivo di sicurezza, la capacità di un'applicazione di utilizzare le chiavi mantenendole al sicuro in un dispositivo hardware è il più grande punto di forza del TPM. Il TPM può generare e importare chiavi generate esternamente. Supporta chiavi sia asimmetriche che simmetriche.

Eccellente! Come si fa!?

Generatore di chiavi

Probabilmente, il più grande punto di forza del TPM è la sua capacità di generare una chiave crittografica e proteggere il suo segreto all'interno di un confine hardware. Il generatore di chiavi si basa sul generatore di numeri casuali del TPM e non si basa su fonti esterne di casualità. Elimina così i punti deboli basati su un software debole con una fonte di entropia insufficiente.

Il TPM ha la capacità di generare chiavi crittografiche e proteggere i suoi segreti entro un limite hardware? È così, come?

Capitolo 12 - Registri di configurazione della piattaforma

PCR per l'autorizzazione

CASO D'USO: SIGILLARE UNA CHIAVE DI CRITTOGRAFIA SU DISCO RIGIDO ALLO STATO DELLA PIATTAFORMA

Le applicazioni di crittografia dell'intero disco sono molto più sicure se un TPM protegge la chiave di crittografia rispetto a se è archiviata sullo stesso disco, protetta solo da una password. Innanzitutto, l'hardware TPM dispone di una protezione anti-martellamento (vedere il Capitolo 8 per una descrizione dettagliata della protezione dagli attacchi del dizionario TPM), rendendo impraticabile un attacco di forza bruta alla password. Una chiave protetta solo dal software è molto più vulnerabile a una password debole. In secondo luogo, una chiave software memorizzata su disco è molto più facile da rubare. Prendi il disco (o un backup del disco) e ottieni la chiave. Quando un TPM detiene la chiave, l'intera piattaforma, o almeno il disco e la scheda madre, devono essere rubati.

La sigillatura consente di proteggere la chiave non solo da una password ma da una policy. Un criterio tipico blocca la chiave sui valori PCR (lo stato del software) correnti al momento della sigillatura. Ciò presuppone che lo stato al primo avvio non sia compromesso. Qualsiasi malware preinstallato presente al primo avvio verrebbe misurato nei PCR e quindi la chiave verrebbe sigillata in uno stato software compromesso. Un'impresa meno affidabile potrebbe avere un'immagine disco standard e un sigillo per PCR che rappresenta quell'immagine. Questi valori PCR sarebbero precalcolati su una piattaforma presumibilmente più affidabile. Un'azienda ancora più sofisticata utilizzerebbe TPM2_PolicyAuthorize e fornirebbe diversi ticket che autorizzano una serie di valori PCR affidabili. Vedere il Capitolo 14 per una descrizione dettagliata dell'autorizzazione della politica e della sua applicazione per risolvere il problema della fragilità PCR.

Sebbene una password possa anche proteggere la chiave, c'è un guadagno in termini di sicurezza anche senza una password della chiave TPM. Un utente malintenzionato potrebbe avviare la piattaforma senza fornire una password TPMkey ma non potrebbe accedere senza il nome utente e la password del sistema operativo. La sicurezza del sistema operativo protegge i dati. L'aggressore potrebbe avviare un sistema operativo alternativo, ad esempio da un DVD live o da una chiavetta USB piuttosto che dal disco rigido, per aggirare la sicurezza di accesso del sistema operativo. Tuttavia, questa diversa configurazione di avvio e software cambierebbero i valori PCR. Poiché queste nuove PCR non corrisponderebbero ai valori sigillati, il TPM non rilascerà la chiave di decrittografia e il disco rigido non potrà essere decrittografato.

Eccellente! Questo è esattamente il caso d'uso che desidero. È anche il caso d'uso per cui Microsoft utilizza il TPM. Come lo faccio!?

Così ho letto l'intero libro e non ha fornito nulla di utile. Il che è abbastanza impressionante perché sono 375 pagine. Ti chiedi cosa contenesse il libro e, ripensandoci, non ne ho idea.

Rinunciamo quindi alla guida definitiva alla programmazione del TPM, e ci rivolgiamo invece a qualche documentazione di Microsoft:

Dal toolkit del provider di crittografia della piattaforma Microsoft TPM . Menziona esattamente quello che voglio fare:

La chiave di approvazione o EK

L'EK è progettato per fornire un identificatore crittografico affidabile per la piattaforma. Un'azienda potrebbe mantenere un database delle chiavi di verifica dell'autenticità appartenenti ai TPM di tutti i PC della propria azienda, oppure un controller dell'infrastruttura del data center potrebbe avere un database dei TPM in tutti i blade. Su Windows è possibile utilizzare il provider NCrypt descritto nella sezione "Platform Crypto Provider in Windows 8" per leggere la parte pubblica dell'EK.

Da qualche parte all'interno del TPM c'è una chiave privata RSA. Quella chiave è chiusa lì dentro, per non essere mai vista dal mondo esterno. Voglio che il TPM firmi qualcosa con la sua chiave privata (cioè lo crittografi con la sua chiave privata).

Quindi voglio l' operazione più elementare che può eventualmente esistere:

inserisci qui la descrizione dell'immagine

Crittografa qualcosa con la tua chiave privata. Non sto nemmeno (ancora) chiedendo le cose più complicate:

  • "sigillarlo" in base allo stato della PCR
  • creazione di una chiave e memorizzazione in memoria volatile o non volatile
  • creando una chiave simmetrica e provando a caricarla nel TPM

Chiedo l'operazione più semplice che un TPM può eseguire. Perché è impossibile ottenere informazioni su come farlo?

Posso ottenere dati casuali

Suppongo di essere stato disinvolto quando ho detto che la firma RSA era la cosa più semplice che il TPM può fare. La cosa più semplice che si può chiedere al TPM è di darmi byte casuali. Che ho capito come fare:

public Byte[] GetRandomBytesTPM(int desiredBytes)
{
   //The maximum random number size is limited to 4,096 bytes per call
   Byte[] result = new Byte[desiredBytes];

   BCRYPT_ALG_HANDLE hAlgorithm;

   BCryptOpenAlgorithmProvider(
         out hAlgorithm,
         BCRYPT_RNG_ALGORITHM, //AlgorithmID: "RNG"
         MS_PLATFORM_CRYPTO_PROVIDER, //Implementation: "Microsoft Platform Crypto Provider" i.e. the TPM
         0 //Flags
   );
   try
   {                
      BCryptGenRandom(hAlgorithm, @result[0], desiredBytes, 0);
   }
   finally
   {
      BCryptCloseAlgorithmProvider(hAlgorithm);
   }

   return result;
}

La cosa fantastica

Mi rendo conto che il volume di persone che utilizzano il TPM è molto basso. Ecco perché nessuno su Stackoverflow ha una risposta. Quindi non posso davvero diventare troppo avido nell'ottenere una soluzione al mio problema comune. Ma la cosa che vorrei veramente fare è "sigillare" alcuni dati:

inserisci qui la descrizione dell'immagine

  • presentare al TPM alcuni dati (es. 32 byte di materiale chiave)
  • fare in modo che il TPM crittografi i dati, restituendo una struttura blob opaca
  • successivamente chiedere al TPM di decrittografare il BLOB
  • la decrittografia funzionerà solo se i registri PCR del TPM sono gli stessi di durante la crittografia.

In altre parole:

Byte[] ProtectBytes_TPM(Byte[] plaintext, Boolean sealToPcr)
{
   //...
}

Byte[] UnprotectBytes_TPM(Byte[] protectedBlob)
{
   //...
}

Cryptography Next Gen (Cng, aka BCrypt) supporta TPM

L'API di crittografia originale in Windows era nota come API di crittografia.

A partire da Windows Vista, la Crypto API è stata sostituita con Cryptography API: Next Generation (internamente nota come BestCrypt , abbreviato in BCrypt , da non confondere con l'algoritmo di hashing delle password ).

Windows viene fornito con due provider BCrypt :

Il provider Platform Crypto non è documentato su MSDN, ma dispone della documentazione di un sito di ricerca Microsoft del 2012:

Toolkit del provider di crittografia della piattaforma TPM

Il toolkit e il provider di crittografia della piattaforma TPM contengono codice di esempio, utilità e documentazione per l'utilizzo della funzionalità relativa a TPM in Windows 8. I sottosistemi descritti includono il provider di crittografia della piattaforma Crypto-Next-Gen (CNG) supportato da TPM e i provider di servizi di attestazione può utilizzare le nuove funzionalità di Windows. Sono supportati entrambi i sistemi basati su TPM1.2 e TPM2.0.

Sembra che l'intento di Microsoft sia quello di far emergere la funzionalità di crittografia TPM con il provider di crittografia della piattaforma Microsoft di Cryptography NG dell'API .

Crittografia a chiave pubblica utilizzando Microsoft BCrypt

Dato che:

una via d'uscita potrebbe essere quella di capire come fare la firma digitale utilizzando l' API Microsoft Cryptography Next Gen .

Il prossimo passo sarà trovare il codice per eseguire la crittografia in BCrypt, con una chiave pubblica RSA, utilizzando il provider standard ( MS_PRIMITIVE_PROVIDER). Per esempio:

  • modulus: 0xDC 67 FA F4 9E F2 72 1D 45 2C B4 80 79 06 A0 94 27 50 8209 DD 67 CE 57 B8 6C 4A 4F 40 9F D2 D1 69 FB 995D 85 0C 07 A1 F9 47 1B 56 16 6E F6 7F B9 CF 2A 58 36 37 99 29 AA 4F A8 12 E8 4F C7 82 2B 9D 72 2A 9C DE 6F C2 EE 12 6D CF F0 F2 B8 C4 DD 7C 5C 1A C8 17 51 A9 AC DF 08 22 04 9D 2B D7 F9 4B 09 DE 9A EB 5C 51 1A D8 F8 F9 56 9E F8 FB 37 9B 3F D3 74 65 24 0D FF 34 75 57 A4 F5 BF 55
  • publicExponent: 65537

Con quel codice funzionante, potrei essere in grado di passare all'utilizzo del provider TPM ( MS_PLATFORM_CRYPTO_PROVIDER).

22/02/2016: E con Apple costretta ad aiutare a decrittografare i dati degli utenti, c'è un rinnovato interesse su come fare in modo che il TPM esegua l'attività più semplice per cui è stato inventato: crittografare qualcosa.

È più o meno equivalente a chiunque possieda un'auto, ma nessuno sa come avviarne una. Può fare cose davvero utili e interessanti, se solo riuscissimo a superare il passaggio 1 .

Lettura bonus


Per l'associazione (crittografia) non è disponibile una funzione esplicita e non è necessaria. È sufficiente creare una chiave di associazione nel TPM e utilizzare la sua parte pubblica per crittografare una chiave di crittografia simmetrica sek con la funzione di crittografia rsa dei sistemi ("RSA / ECB / OAEPWithSHA1AndMGF1Padding") e salvarla nella struttura corretta ("TcTssConstants.TSS_ENCDATA_BIND"). Per disassociare (decrittografare) il sek, utilizza la funzione di annullamento dell'associazione dei TPM e utilizza il sek in qualsiasi funzione di crittografia simmetrica che preferisci. Ho una base di codice piuttosto vecchia per quello che ho fatto qualche tempo fa, forse aiuta: goo.gl/jV1Ouw
evildead

Da wikipedia, Binding: crittografa i dati utilizzando la chiave di associazione TPM, una chiave RSA univoca che discende da una chiave di archiviazione. en.wikipedia.org/wiki/Trusted_Platform_Module Suona come questa coppia di comandi (TSpi_Data_Bind / TPM_UnBind) dovrebbe essere sufficiente per le tue esigenze ...
Alex Mazzariol

1
Non penso che tu debba usare direttamente il TPM. È supportato tramite le API CNG / NCryptXXX standard e il "Microsoft Platform Crypto Provider" (per le recenti piattaforme del sistema operativo Windows, e se l'hardware è ok e abilitato ovviamente). Forse potresti dare un'occhiata al "TPM Platform Crypto-Provider Toolkit qui: research.microsoft.com/en-us/downloads/… controlla anche questo: tiw2013.cse.psu.edu/slides/…
Simon Mourier

CryptProtectData non utilizza necessariamente il TPM. D'altra parte, se è possibile ottenere un handle CNG o CSP valido per il TPM, è possibile utilizzarlo nelle funzioni crittografiche.
Michael Chourdakis

1
@ b3nj1 No, non lo ero; nessuno è stato in grado di rispondere alla domanda.
Ian Boyd

Risposte:


7

Primer

Tutto ciò che segue riguarda TPM 1.2. Tieni presente che Microsoft richiede un TPM 2.0 per tutte le future versioni di Windows. La generazione 2.0 è fondamentalmente diversa dalla 1.2

Non esiste una soluzione a una riga a causa dei principi di progettazione del TPM. Pensa al TPM come a un microcontrollore con risorse limitate. L'obiettivo principale del progetto era quello di essere economici, pur essendo sicuri. Quindi il TPM è stato strappato da tutta la logica che non era necessaria per un'operazione sicura. Pertanto un TPM funziona solo quando si dispone di almeno un software più o meno grasso , che emette molti comandi nell'ordine corretto. E quelle sequenze di comandi possono diventare molto complesse. Ecco perché TCG ha specificato il TSS con un'API ben definita. Se vuoi seguire la via Java, c'è anche un'API Java di alto livello . Non sono a conoscenza di un progetto simile per C # / .net

Sviluppo

Nel tuo caso ti suggerirei di guardare il software TPM di IBM.

Nella confezione troverai 3 componenti molto utili:

  • un emulatore software TPM
  • una libreria tpm leggera
  • alcune utilità di base della riga di comando

Non è necessario necessariamente l'emulatore del software TPM, è anche possibile connettersi all'HW TPM della macchina. Tuttavia, puoi intercettare i comandi emessi e guardare le risposte, imparando così come sono assemblati e come corrispondono alla specifica del comando.

Alto livello

Prerequisiti:

  1. TPM è attivato
  2. Il driver TPM viene caricato
  3. hai assunto la proprietà del TPM

Per sigillare un blob, devi eseguire le seguenti operazioni:

  1. creare una chiave
  2. archiviare il key-blob da qualche parte
  3. assicurarsi che la chiave sia caricata nel TPM
  4. sigillare il blob

Per rimuovere il sigillo è necessario:

  1. ottenere il key-blob
  2. caricare la chiave nel TPM
  3. togliere il sigillo dal blob sigillato

È possibile archiviare il key-blob nella struttura dati utilizzata per archiviare i byte protetti.

La maggior parte dei comandi TPM necessari sono quelli autorizzati. Pertanto è necessario stabilire sessioni di autorizzazione dove necessario. AFAIR quelle sono per lo più sessioni OSAP.

Comandi TPM

Al momento non posso eseguire una versione di debug, quindi non posso fornirti la sequenza esatta. Quindi considera questo un elenco non ordinato di comandi che dovrai usare:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Se vuoi leggere anche i valori PCR correnti:

  • TPM_PCRRead

Microsoft ha la propria libreria gestita C # .NET per l'utilizzo del TPM . Hanno anche un emulatore TPM , a cui la libreria gestita può connettersi come alternativa di debug se non è presente un TPM reale. Hanno anche il TPM Platform Provider Toolkit , che contiene la documentazione e il codice di esempio per l'utilizzo del TPM. Ora, se solo qualcuno potesse capire come utilizzare il TPM per crittografare i byte.
Ian Boyd

I primi due collegamenti sono solo TPM 2.0. Se vuoi usarli, temo di non essere di alcun aiuto.
Scolytus

4

Chiavi affidabili e crittografate

Le chiavi affidabili e crittografate sono due nuovi tipi di chiavi aggiunti al servizio di keyring del kernel esistente. Entrambi questi nuovi tipi sono chiavi simmetriche di lunghezza variabile e in entrambi i casi tutte le chiavi vengono create nel kernel e lo spazio utente vede, archivia e carica solo i BLOB crittografati. Le chiavi affidabili richiedono la disponibilità di un chip TPM (Trusted Platform Module) per una maggiore sicurezza, mentre le chiavi crittografate possono essere utilizzate su qualsiasi sistema. Tutti i BLOB a livello utente vengono visualizzati e caricati in ASCII esadecimale per comodità e vengono verificati l'integrità.

Le chiavi attendibili utilizzano un TPM sia per generare che per sigillare le chiavi. Le chiavi sono sigillate sotto una chiave RSA a 2048 bit nel TPM e facoltativamente sigillate ai valori PCR (misurazione dell'integrità) specificati e non sigillate solo dal TPM, se PCR e verifiche dell'integrità del blob corrispondono. Una chiave attendibile caricata può essere aggiornata con nuovi (futuri) valori PCR, quindi le chiavi possono essere facilmente migrate a nuovi valori pcr, come quando il kernel e initramfs vengono aggiornati. La stessa chiave può avere molti BLOB salvati con valori PCR diversi, quindi sono facilmente supportati più avviamenti.

Per impostazione predefinita, le chiavi attendibili sono sigillate sotto SRK, che ha il valore di autorizzazione predefinito (20 zeri). Questo può essere impostato al momento takeownership con l'utilità del pantalone: tpm_takeownership -u -z.

Usage:
    keyctl add trusted name "new keylen [options]" ring
    keyctl add trusted name "load hex_blob [pcrlock=pcrnum]" ring
    keyctl update key "update [options]"
    keyctl print keyid

    options:
    keyhandle= ascii hex value of sealing key default 0x40000000 (SRK)
    keyauth=   ascii hex auth for sealing key default 0x00...i
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    blobauth=  ascii hex auth for sealed data default 0x00...
        (40 ascii zeros)
    pcrinfo=   ascii hex of PCR_INFO or PCR_INFO_LONG (no default)
    pcrlock=   pcr number to be extended to "lock" blob
    migratable= 0|1 indicating permission to reseal to new PCR values,
                default 1 (resealing allowed)

keyctl printrestituisce una copia esadecimale ascii della chiave sigillata, che è nel formato standard TPM_STORED_DATA. La lunghezza della chiave per le nuove chiavi è sempre in byte. Le chiavi affidabili possono essere di 32 - 128 byte (256 - 1024 bit), il limite superiore è quello di rientrare nella lunghezza della chiave SRK (RSA) a 2048 bit, con tutta la struttura / imbottitura necessaria.

Le chiavi crittografate non dipendono da un TPM e sono più veloci, poiché utilizzano AES per la crittografia / decrittografia. Le nuove chiavi vengono create da numeri casuali generati dal kernel e vengono crittografate / decrittografate utilizzando una chiave "master" specificata. La chiave "master" può essere di tipo trusted-key o user-key. Il principale svantaggio delle chiavi crittografate è che se non sono radicate in una chiave attendibile, sono sicure solo quanto la chiave utente che le crittografa. La chiave dell'utente principale dovrebbe quindi essere caricata nel modo più sicuro possibile, preferibilmente all'inizio dell'avvio.

La parte decrittografata delle chiavi crittografate può contenere una semplice chiave simmetrica o una struttura più complessa. Il formato della struttura più complessa è specifico dell'applicazione, identificato da "formato".

Usage:
    keyctl add encrypted name "new [format] key-type:master-key-name keylen"
        ring
    keyctl add encrypted name "load hex_blob" ring
    keyctl update keyid "update key-type:master-key-name"

format:= 'default | ecryptfs'
key-type:= 'trusted' | 'user'

Esempi di utilizzo di chiavi affidabili e crittografate

Crea e salva una chiave attendibile denominata "kmk" di lunghezza 32 byte:

$ keyctl add trusted kmk "new 32" @u
440502848

$ keyctl show
Session Keyring
       -3 --alswrv    500   500  keyring: _ses
 97833714 --alswrv    500    -1   \_ keyring: _uid.500
440502848 --alswrv    500   500       \_ trusted: kmk

$ keyctl print 440502848
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

$ keyctl pipe 440502848 > kmk.blob

Carica una chiave attendibile dal BLOB salvato:

$ keyctl add trusted kmk "load `cat kmk.blob`" @u
268728824

$ keyctl print 268728824
0101000000000000000001005d01b7e3f4a6be5709930f3b70a743cbb42e0cc95e18e915
3f60da455bbf1144ad12e4f92b452f966929f6105fd29ca28e4d4d5a031d068478bacb0b
27351119f822911b0a11ba3d3498ba6a32e50dac7f32894dd890eb9ad578e4e292c83722
a52e56a097e6a68b3f56f7a52ece0cdccba1eb62cad7d817f6dc58898b3ac15f36026fec
d568bd4a706cb60bb37be6d8f1240661199d640b66fb0fe3b079f97f450b9ef9c22c6d5d
dd379f0facd1cd020281dfa3c70ba21a3fa6fc2471dc6d13ecf8298b946f65345faa5ef0
f1f8fff03ad0acb083725535636addb08d73dedb9832da198081e5deae84bfaf0409c22b
e4a8aea2b607ec96931e6f4d4fe563ba

Sigilla nuovamente una chiave attendibile con nuovi valori pcr:

$ keyctl update 268728824 "update pcrinfo=`cat pcr.blob`"
$ keyctl print 268728824
010100000000002c0002800093c35a09b70fff26e7a98ae786c641e678ec6ffb6b46d805
77c8a6377aed9d3219c6dfec4b23ffe3000001005d37d472ac8a44023fbb3d18583a4f73
d3a076c0858f6f1dcaa39ea0f119911ff03f5406df4f7f27f41da8d7194f45c9f4e00f2e
df449f266253aa3f52e55c53de147773e00f0f9aca86c64d94c95382265968c354c5eab4
9638c5ae99c89de1e0997242edfb0b501744e11ff9762dfd951cffd93227cc513384e7e6
e782c29435c7ec2edafaa2f4c1fe6e7a781b59549ff5296371b42133777dcc5b8b971610
94bc67ede19e43ddb9dc2baacad374a36feaf0314d700af0a65c164b7082401740e489c9
7ef6a24defe4846104209bf0c3eced7fa1a672ed5b125fc9d8cd88b476a658a4434644ef
df8ae9a178e9f83ba9f08d10fa47e4226b98b0702f06b3b8

Il consumatore iniziale di chiavi affidabili è EVM, che all'avvio necessita di una chiave simmetrica di alta qualità per la protezione HMAC dei metadati dei file. L'utilizzo di una chiave attendibile fornisce solide garanzie che la chiave EVM non sia stata compromessa da un problema a livello utente e, se sigillata a valori PCR di avvio specifici, protegge da attacchi di avvio e offline. Crea e salva una chiave crittografata "evm" utilizzando la chiave attendibile "kmk" di cui sopra:

opzione 1: omettere "formato"

$ keyctl add encrypted evm "new trusted:kmk 32" @u
159771175

opzione 2: definizione esplicita di "formato" come "predefinito"

$ keyctl add encrypted evm "new default trusted:kmk 32" @u
159771175

$ keyctl print 159771175
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

$ keyctl pipe 159771175 > evm.blob

Carica una chiave crittografata "evm" dal blob salvato:

$ keyctl add encrypted evm "load `cat evm.blob`" @u
831684262

$ keyctl print 831684262
default trusted:kmk 32 2375725ad57798846a9bbd240de8906f006e66c03af53b1b3
82dbbc55be2a44616e4959430436dc4f2a7a9659aa60bb4652aeb2120f149ed197c564e0
24717c64 5972dcb82ab2dde83376d82b2e3c09ffc

Sono previsti altri usi per chiavi affidabili e crittografate, ad esempio per la crittografia del disco e dei file. In particolare è stato definito il nuovo formato "ecryptfs" per poter utilizzare chiavi crittografate per montare un filesystem eCryptfs. Maggiori dettagli sull'utilizzo possono essere trovati nel file "Documentation / security / keys-ecryptfs.txt".


Hai idea di quando sono stati aggiunti questi due nuovi tipi di chiavi? In quale versione intendo. Attualmente sto usando 1.2 (pacchetti aziendali) e quello non li supporta. Forse in 1.5+?
Acapulco

1
Qual è la fonte di questo messaggio? La fine si riferisce a un documentoDocumentation/security/keys-ecryptfs.tx
goodguys_activate

Sembrano essere tutte chiamate a un programma a riga di comando. Non vedo alcun codice su come utilizzare il TPM.
Ian Boyd

3

Come posso crittografare i byte utilizzando il modulo TPM di una macchina?

Dipende dal tuo intento e dalle circostanze:

  • Che tipo di TPM hai (monofamiliare o bifamiliare)?
  • In che stato si trova il TPM? È stato posseduto? È stato eseguito il provisioning?
  • Qual è il tuo linguaggio di programmazione?
  • Vuoi crittografare o firmare? (questo è vago dal resto della domanda)
  • Quanto sono grandi i dati che vuoi crittografare?
  • Vuoi usare una chiave simmetrica o una chiave asimmetrica?
  • Si desidera utilizzare una chiave già esistente nel TPM o si desidera che crei prima la chiave?
  • Con "crittografare" intendi forse "avvolgere una chiave"?
  • Vuoi bloccare i dati crittografati nella configurazione del sistema, in modo che possano essere decrittografati solo quando il sistema è tornato nella stessa configurazione?
  • Vuoi richiedere l'autorizzazione per la decrittografia?
  • Forse non è necessario crittografare affatto, ma piuttosto archiviare i dati all'interno del TPM?
  • Se si stanno archiviando i dati all'interno del TPM, si desidera richiedere l'autorizzazione o che il sistema si trovi in ​​una configurazione particolare per il recupero?

Ciascuno di questi casi d'uso (e ce ne sono altri), o una combinazione di questi, presenta un diverso percorso di implementazione. Pensa al TPM come a un coltellino svizzero di dispositivi crittografici: non c'è molto che non puoi fare con esso, ma la facilità d'uso soffre di quella versatilità. La domanda continua a rimbalzare tra crittografia, firma e blocco della configurazione del sistema, ma la parte principale di questa risposta considererà il comando Seal per coprire la maggior parte delle esigenze descritte nella domanda.

Ora è il momento di fare un ulteriore passo avanti. Desidero crittografare alcuni dati (ad esempio una chiave master di crittografia del disco rigido) che possono essere decrittografati solo dal TPM locale.

Questo è lo scopo del comando Bind (sostituito dal comando Crea per TPM 2). Si carica una chiave che deriva da una chiave associata a TPM e la crittografa con essa (o direttamente con una chiave associata a hardware). In questo modo i dati possono essere decrittografati solo con l'accesso allo stesso TPM.

In altre parole, voglio sostituire Qualcomm Trusted Execution Environment (TEE) nello schema a blocchi seguente per Android, con il TPM in Windows:

Non sono sicuro che replicare l'intero processo sia una buona idea. Per prima cosa, non è necessario utilizzare un'operazione di firma in qualsiasi punto del processo. Sembrerebbe che, al momento dello sviluppo di Android 5, l' API Keystore fosse limitata alle operazioni di firma e verifica. La mia ipotesi migliore è che il team di crittografia del disco abbia fatto del suo meglio per lavorare con ciò che aveva e ha ideato un algoritmo in base al quale una delle chiavi intermedie è stata derivata con un'operazione di firma , utilizzando una chiave TEE memorizzata, legando così l'intero processo a un hardware- chiave vincolata disponibile solo sulla piattaforma, poiché la firma era l'unico modo per farlo in quel momento. Tuttavia, non è necessario limitarti in questo modo se hai accesso a un TPM, che ti offre più funzionalità di quelle che sapevi di aver bisogno!

Mi rendo conto che il TPM non esegue la firma dei dati

Questo è falso, entrambe le versioni di TPM supportano la firma.

(o se lo fa, non garantisce che la firma degli stessi dati darà lo stesso output binario ogni volta)

Questo non ha senso. Firma gli stessi dati con la stessa chiave si produrrà la stessa firma. Potresti confondere l'operazione di firma con l'operazione di citazione, che si mescolerà in un nonce.

Ecco perché sarei disposto a sostituire "firma RSA" con "crittografia di un BLOB a 256 bit con una chiave associata a hardware".

Questa dovrebbe effettivamente essere l'opzione preferita, sebbene entrambe siano possibili con un TPM. Vedi sopra.

Il problema è che la programmazione TPM è completamente non documentata su MSDN. Non sono disponibili API per eseguire operazioni.

Purtroppo non c'è molto da documentare. L'API Win è limitata a un paio di funzioni TBS rimosse a un livello dal driver.

Invece devi trovarti una copia dello Stack software del Trusted Computing Group (noto anche come TSS), capire quali comandi inviare al TPM, con i payload, in quale ordine e chiamare la funzione Tbsip_Submit_Command di Window per inviare direttamente i comandi:

In realtà, no, se avessi un TSS non dovresti usare Tbsip_submit_Command(). Questo è il punto centrale di avere un TSS: i dettagli di basso livello vengono astratti.

Windows non dispone di API di livello superiore per eseguire azioni.

Ancora vero per TPM 1, ma per TPM 2 c'è TSS.MSR .

È l'equivalente morale di provare a creare un file di testo inviando comandi di I / O SATA al disco rigido.

Corretta.

Perché non usare semplicemente Pantaloni ... Il problema con quel codice è che non è portabile nel mondo Windows. Ad esempio, non puoi usarlo da Delphi, non puoi usarlo da C #. Richiede: OpenSSL, pThread

Non è chiaro se questa sia una sfida insormontabile. L'accesso a TrouSerS tramite un'interoperabilità dovrebbe essere preferibile alla riscrittura di tutto il codice di strutturazione dei dati. Inoltre, c'era doTSSal momento della stesura della domanda.

Qual è il codice equivalente per crittografare i dati utilizzando il TPM? Probabilmente coinvolge il comando TPM_seal. Anche se penso di non voler sigillare i dati, penso di volerlo legare:

La domanda contiene una citazione che descrive la differenza tra i due comandi, quindi non dovrebbe esserci molta confusione. Il sigillamento è simile al binding, con il vincolo aggiuntivo che lo stato del sistema deve essere lo stesso affinché i dati vengano aperti.

Allo stesso modo ho potuto fornire:

Byte[] ProtectBytes_Crypt(Byte[] plaintext)
{
   //...
   CryptProtectData(...); 
   //...
}

qualcuno può fornire l'equivalente corrispondente:

Byte[] ProtectBytes_TPM(Byte[] plaintext)
{
   //...
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   Tbsip_Submit_Command(...);
   //...snip...
   Tbsip_Submit_Command(...);
   //...
}

che fa la stessa cosa, tranne che invece di una chiave bloccata in System LSA, è bloccata nel TPM?

Innanzitutto, vale la pena sottolineare che esistono due versioni principali di TPM, che sono totalmente incompatibili tra loro. Quindi praticamente nessun codice che potresti aver scritto per TPM 1 funzionerà per TPM 2. L'API TBS è l'unico codice comune tra i due e, per essere onesti con Microsoft, questo potrebbe essere stato uno dei motivi per cui quell'API non è mai cresciuta. La parte principale della risposta presenterà il codice per TPM 1 per due motivi:

  • La domanda è caricata con concetti specifici di TPM 1, quindi è più probabile che le persone che utilizzano TPM 1 arrivino qui a cercarli
  • Esiste un'implementazione Microsoft di TSS per TPM 2.

Secondo, rendiamo la domanda più specifica. Lo sto reinterpretando come segue:

How do I write code in C#, using only the TBS API, to interface with
an already owned and provisioned TPM to, without user interaction,
encrypt no more than 128 bytes of arbitrary data with an asymmetric
key already resident in the TPM and bound to it, but not protected
with a password, so that in order to decrypt the data the system may
need to be in the same state it was in at encryption time based on an
easily configurable variable?

Il comando Seal è più adatto a questo, poiché esegue la stessa funzione del comando Bind quando la dimensione della selezione PCR è impostata su zero, ma la selezione PCR può essere facilmente modificata per includere qualsiasi PCR si desideri. Viene da chiedersi perché il comando Bind sia stato incluso nella specifica e, come notato, è stato rimosso nella specifica TPM 2 ei due sono stati combinati in un unico comando Crea.

Di seguito è riportato il codice C # per l'utilizzo del comando TPM 1.2 Seal per crittografare i dati con le sole funzioni TBS (nota: questo codice non è testato e probabilmente non funzionerà senza debug) :

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsi_Context_Create (UInt32 * version, IntPtr * hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Context_Close (IntPtr hContext);

[DllImport ("tbs.dll")]
unsafe static extern UInt32 Tbsip_Submit_Command (
    IntPtr hContext, UInt32 Locality, 
    UInt32 Priority, 
    byte * pCommandBuf, 
    UInt32 CommandBufLen, 
    byte * pResultBuf, 
    UInt32 * pResultBufLen);

byte[] ProtectBytes_TPM (byte[] plaintext) {

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) {
        byte[] bytes = System.BitConverter.GetBytes (o);
        Array.Reverse (bytes);
        Array.Copy (bytes, 0, a, i, bytes.Length);
        i += bytes.Length;
    }
    void AddBool (byte[] a, byte b, ref int i) {
        a[i] = b;
        i += 1;
    }
    void AddBlob (byte[] a, byte[] b, ref int i) {
        Array.Copy (b, 0, a, i, b.Length);
        i += b.Length;
    }
    byte[] Xor (byte[] text, byte[] key) {
        byte[] xor = new byte[text.Length];
        for (int i = 0; i < text.Length; i++) {
            xor[i] = (byte) (text[i] ^ key[i % key.Length]);
        }
        return xor;
    }

    int offset;

    Random rnd = new Random ();

    IntPtr hContext = IntPtr.Zero;
    unsafe {
        UInt32 version = 1;
        IntPtr handle = hContext;
        UInt32 result = Tbsi_Context_Create ( & version, & handle);

        if (result == 0) {
            hContext = handle;
        }
    }

    byte[] cmdBuf = new byte[768];

    //OSAP
    System.UInt32 outSize;

    byte[] oddOsap = new byte[20];
    byte[] evenOsap = new byte[20];
    byte[] nonceEven = new byte[20];
    byte[] nonceOdd = new byte[20];
    System.UInt32 hAuth = 0;

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = (System.UInt32) (Marshal.SizeOf (hAuth) + nonceEven.Length + evenOsap.Length);

    byte[] response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        //uint cmdSize = (uint)offset;
        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
    }

    byte contSession = 0;
    System.UInt32 hKey = 0x40000000; //TPM_KH_SRK;
    System.UInt32 pcrInfoSize = 0;
    byte[] srkAuthdata = { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 };
    uint inDataSize = (uint) plaintext.Length;

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

    //shared-secret = HMAC(srk_auth, even_osap || odd_osap)
    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

    byte[] authSha1InBuf = new byte[sharedSecret.Length + nonceEven.Length];
    Array.Copy (sharedSecret, 0, authSha1InBuf, 0, sharedSecret.Length);
    Array.Copy (nonceEven, 0, authSha1InBuf, sharedSecret.Length, nonceEven.Length);
    System.Security.Cryptography.SHA1Managed sha1 = new System.Security.Cryptography.SHA1Managed ();
    byte[] authSha1 = sha1.ComputeHash (authSha1InBuf);
    byte[] encAuth = Xor (srkAuthdata, authSha1);

    //inParamDigest = sha1(1S ~ 6S) 
    int paramInDigestInBufSize =
        sizeof (System.UInt32) + 
        encAuth.Length +
        Marshal.SizeOf (pcrInfoSize) +
        Marshal.SizeOf (inDataSize) +
        (int) inDataSize;
    byte[] paramInDigestInBuf = new byte[paramInDigestInBufSize];
    offset = 0;
    AddUInt32Reversed (paramInDigestInBuf, 0x00000017, ref offset);
    AddBlob (paramInDigestInBuf, encAuth, ref offset);
    AddUInt32Reversed (paramInDigestInBuf, 0x0, ref offset); //PCR info size
    AddUInt32Reversed (paramInDigestInBuf, inDataSize, ref offset);
    AddBlob (paramInDigestInBuf, plaintext, ref offset);

    byte[] paramInDigest = sha1.ComputeHash (paramInDigestInBuf);

    int pubAuthInBufSize = paramInDigest.Length + nonceEven.Length + nonceOdd.Length + Marshal.SizeOf (contSession);
    byte[] pubAuthInBuf = new byte[pubAuthInBufSize];

    offset = 0;
    AddBlob (pubAuthInBuf, paramInDigest, ref offset);
    AddBlob (pubAuthInBuf, nonceEven, ref offset);
    AddBlob (pubAuthInBuf, nonceOdd, ref offset);
    AddBool (pubAuthInBuf, contSession, ref offset);
    System.Security.Cryptography.HMACSHA1 pubAuthHmac = new System.Security.Cryptography.HMACSHA1 (sharedSecret);
    byte[] pubAuth = pubAuthHmac.ComputeHash (pubAuthInBuf);

    //Seal
    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); // TPM_TAG_RQU_AUTH1_COMMAND;
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt32Reversed (cmdBuf, hKey, ref offset);
    AddBlob (cmdBuf, encAuth, ref offset);
    AddUInt32Reversed (cmdBuf, pcrInfoSize, ref offset);
    AddUInt32Reversed (cmdBuf, inDataSize, ref offset);
    AddBlob (cmdBuf, plaintext, ref offset);

    AddUInt32Reversed (cmdBuf, hAuth, ref offset);
    AddBlob (cmdBuf, nonceOdd, ref offset);
    AddBool (cmdBuf, contSession, ref offset);
    AddBlob (cmdBuf, pubAuth, ref offset);
    cmdSize = (System.UInt32) offset;
    offset = 2;
    AddUInt32Reversed (cmdBuf, cmdSize, ref offset);

    outSize = 768;
    uint responseSize = 0;

    response = new byte[outSize];
    unsafe {
        UInt32 result = 0;

        uint resSize = outSize;
        fixed (byte * pCmd = cmdBuf, pRes = response) {
            result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);
        }
        responseSize = resSize;
    }

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

}

Analisi del codice:

[DllImport ("tbs.dll")]
...

Queste sono alcune delle poche funzioni disponibili in Tbs.he le uniche che useremo qui. Fondamentalmente ti consentono di aprire un handle per il dispositivo e comunicare con esso inviando e ricevendo byte grezzi.

    void AddUInt32Reversed (byte[] a, System.UInt32 o, ref int i) { ... }
    void AddUInt16Reversed (byte[] a, System.UInt16 o, ref int i) { ... }
    void AddBool (byte[] a, byte b, ref int i) { ... }
    void AddBlob (byte[] a, byte[] b, ref int i) { ... }

TPM è big endian, Windows è little endian. Quindi l'ordine dei byte dovrà essere invertito per tutti i dati che stiamo inviando. Dobbiamo solo preoccuparci di invertire gli int senza segno a 32 e 16 bit qui.

    ...
    UInt32 result = Tbsi_Context_Create ( & version, & handle);
    ...

Qui usiamo Tbsi_Context_Create () per aprire un handle per parlare con il TPM. Il TBS_CONTEXT_PARAMSparametro è solo una struttura C con un campo int a 32 bit senza segno che deve essere impostato su 1 per parlare con un'istanza di TPM 1.2 e su cui è impostato.

    byte[] cmdBuf = new byte[768];

Questa è specificata come dimensione minima del buffer nella specifica del client PC TPM . Sarà più che sufficiente per i nostri bisogni qui.

TPM 1.2 Spec Part 3 dice quanto segue:

TPM_Seal requires the encryption of one parameter (“Secret”). For the
sake of uniformity with other commands that require the encryption of
more than one parameter, the string used for XOR encryption is
generated by concatenating a nonce (created during the OSAP session)
with the session shared secret and then hashing the result.

Abbiamo bisogno di crittografare XOR questo parametro "segreto" utilizzando un nonce generato durante una sessione OSAP. Uno degli handle di input del comando Seal è anche un handle OSAP:

The authorization session handle used for keyHandle authorization.
Must be an OSAP session for this command.

Quindi dobbiamo prima stabilire questa sessione OSAP. OSAP è descritto nella specifica TPM 1.2 Parte 1 . OSAP, o Object-Specific Authorization Protocol, è stato inventato per gestire il caso d'uso in cui si desidera utilizzare un oggetto TPM che richiede l'autorizzazione più volte, ma non si desidera fornire l'autorizzazione ogni volta: viene invece utilizzata una sessione OSAP, che si basa sul concetto di "segreto condiviso", che è un HMAC che combina i dati di autorizzazione degli oggetti con i nonce generati su ciascun lato per prevenire attacchi di risposta. Pertanto il "segreto condiviso" è noto solo alle due parti in questa sessione: la parte che ha avviato la sessione (utente) e la parte che l'ha accettata (TPM); inoltre, entrambe le parti devono avere gli stessi dati di autorizzazione dell'oggetto affinché il "segreto condiviso" sia lo stesso; inoltre, il "segreto condiviso" utilizzato in una sessione non sarà valido in un'altra. Questo diagramma dalle specifiche descrive il processo:

OSAP

Non useremo più sessioni in questo caso particolare (infatti, quel parametro viene ignorato con il comando Seal!) E la chiave che useremo non richiede autorizzazione, ma purtroppo siamo ancora vincolati dalle specifiche per stabilire un OSAP sessione.

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C1, ref offset);
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x0000000B, ref offset);

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for command code

    AddUInt16Reversed (cmdBuf, 0x0004, ref offset); //Entity Type SRK = 0x0004
    AddUInt32Reversed (cmdBuf, 0x40000000, ref offset); //Entity Value SRK = 0x40000000
    rnd.NextBytes (oddOsap);
    AddBlob (cmdBuf, oddOsap, ref offset);
    uint cmdSize = (System.UInt32) offset;

Gli operandi del comando TPM_OSAP sono:

Operandi TPM_OSAP

Ogni comando TPM 1.2 è strutturato in questo modo:

  2 bytes       4 bytes             4 bytes
+---------+------------------+------------------+---------------------------
|   Tag   |       Size       |   Command code   |    Command body    ....
+---------+------------------+------------------+---------------------------

Il tag è un valore a due byte che indica se ciò che segue è input o output e se sono presenti valori di dati di autenticazione che seguono i parametri del comando. Per TPM_OSAP, il tag deve essere TPM_TAG_RQU_COMMAND (0x00C1) secondo le specifiche, che significa "un comando senza autorizzazione".

La dimensione è un valore a quattro byte che specifica la dimensione del comando in byte, incluso il tag e la dimensione stessa. Imposteremo questo valore in seguito, una volta calcolato.

Il codice comando è un valore a quattro byte che funge da ID comando: indica al TPM come interpretare il resto del comando. Il nostro codice di comando qui è TPM_OSAP (0x0000000B).

Le due cose successive da impostare sono il tipo di entità e il valore dell'entità. Poiché si desidera utilizzare una chiave già esistente nel TPM, utilizzeremo il tipo di entità "SRK" (0x0004) e, poiché si presuppone che il TPM sia già stato di proprietà, è lecito ritenere che sia stato un SRK caricato sotto l'handle permanente 0x40000000 secondo le specifiche, quindi useremo questo valore di handle permanente per il nostro valore di entità. (SRK sta per "Storage Root Key" ed è la chiave root da cui derivano la maggior parte delle altre chiavi di proprietà del TPM)

    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

Infine calcoliamo la dimensione del comando, la impostiamo e inviamo il comando.

    offset = 2 + 4 + 4; //2 for tag, 4 for size and 4 for return code
    byte[] hauthbytes = new byte[Marshal.SizeOf (hAuth)];
    Array.Copy (response, offset, hauthbytes, 0, hauthbytes.Length);
    Array.Reverse (hauthbytes);
    hAuth = System.BitConverter.ToUInt32 (hauthbytes, 0);
    offset += Marshal.SizeOf (hAuth);
    Array.Copy (response, offset, nonceEven, 0, nonceEven.Length);
    offset += nonceEven.Length;
    Array.Copy (response, offset, evenOsap, 0, evenOsap.Length);

I dati che dovremmo recuperare dal TPM su TPM_OSAP sono:

Risposta TPM_OSAP

Quindi torniamo:

  • L'handle di autorizzazione da utilizzare con il nostro comando principale (Seal)
  • nonceEven: il nonce generato dal TPM da utilizzare con il comando principale
  • nonceEvenOSAP: il nonce OSAP che è il contro-nonce del nonce che abbiamo generato dalla nostra parte prima di inviare il comando TPM_OSAP. Queste due nonce verranno utilizzate per generare il "segreto condiviso".

Estraiamo questi valori e li memorizziamo in variabili.

    byte[] sharedSecretBuf = new byte[evenOsap.Length + oddOsap.Length];
    Array.Copy (evenOsap, 0, sharedSecretBuf, 0, evenOsap.Length);
    Array.Copy (oddOsap, 0, sharedSecretBuf, evenOsap.Length, oddOsap.Length);
    System.Security.Cryptography.HMACSHA1 sharedSecretHmac = new System.Security.Cryptography.HMACSHA1 (srkAuthdata);
    byte[] sharedSecret = sharedSecretHmac.ComputeHash (sharedSecretBuf);

Quindi calcoliamo il "segreto condiviso". Secondo la specifica, i valori che entrano nel calcolo sono i due nonce OSAP (uno generato dall'utente e uno generato dal TPM) e il valore di autorizzazione per la chiave che vogliamo utilizzare - SRK. Per convenzione, il valore di autenticazione SRK è "ben noto": un buffer di 20 byte azzerato. Tecnicamente, si potrebbe cambiare questo valore in qualcos'altro quando si assume la proprietà del TPM, ma ciò non viene fatto nella pratica, quindi possiamo tranquillamente presumere che il valore "well-known auth" sia buono.

Quindi diamo un'occhiata a cosa va nel comando TPM_Seal:

TPM_Seal

La maggior parte di questi parametri sono banali da costruire, tranne due di essi: encAuthe pubAuth. Vediamoli uno per uno.

encAuthè "L'AuthData crittografato per i dati sigillati". Il nostro AuthData qui è il "ben noto auth" di prima, ma sì, dobbiamo ancora crittografarlo. Poiché stiamo utilizzando una sessione OSAP, è crittografata secondo ADIP, o Authorization-Data Insertion Protocol. Dalla specifica: "L'ADIP consente la creazione di nuove entità e l'inserimento sicuro della nuova entità AuthData. La trasmissione del nuovo AuthData utilizza la crittografia con la chiave basata sul segreto condiviso di una sessione OSAP." Inoltre: "Per l'algoritmo di crittografia XOR obbligatorio, il creatore crea una chiave di crittografia utilizzando un hash SHA-1 del segreto condiviso OSAP e un nonce di sessione. Il creatore XOR crittografa il nuovo AuthData utilizzando la chiave di crittografia come un blocco unico e invia questi dati crittografati insieme alla richiesta di creazione al TPM. "

Il diagramma seguente spiega come funziona ADIP:

ADIP

pubAuthè "Il digest della sessione di autorizzazione per input e keyHandle." La parte 1 della specifica, in "Dichiarazioni dei parametri per esempi OIAP e OSAP", spiega come interpretare la tabella dei parametri TPM_Seal sopra: "La colonna HMAC # descrive i parametri utilizzati nel calcolo HMAC. I parametri 1S, 2S, ecc. Sono concatenati e hash in inParamDigest o outParamDigest, implicitamente chiamato 1H1 e possibilmente 1H2 se ci sono due sessioni di autorizzazione. Per la prima sessione, 1H1, 2H1, 3H1 e 4H1 vengono concatenati e HMAC. Per la seconda sessione, 1H2, 2H2, 3H2, e 4H2 sono concatenati e HMAC'ed. " Quindi dovremo hash il testo in chiaro, la sua dimensione, la dimensione delle informazioni sulla PCR, encAuthdall'alto e l'ordinale TPM_Seal, e quindi HMAC quello con i due nonc e il booleano "continue session" usando OSAP "

Mettendo tutto insieme in un diagramma:

pubAuth calcolo

Si noti come impostiamo la "dimensione delle informazioni PCR" su zero in questo codice, poiché vogliamo solo crittografare i dati senza bloccarli in uno stato di sistema. Tuttavia è banale fornire una struttura "PCR info" se necessario.

    offset = 0;
    AddUInt16Reversed (cmdBuf, 0x00C2, ref offset); 
    offset = 6;
    AddUInt32Reversed (cmdBuf, 0x00000017, ref offset); // TPM_ORD_SEAL;
    ...
    result = Tbsip_Submit_Command (hContext, 0, 200, pCmd, cmdSize, pRes, & resSize);

Infine costruiamo il comando e lo inviamo.

    byte[] retBuffer = new byte[responseSize - 10];
    Array.Copy (response, 10, retBuffer, 0, retBuffer.Length);
    Tbsip_Context_Close (hContext);
    return retBuffer;

Usiamo la funzione Tbsip_Context_Close () per chiudere il nostro handle di comunicazione.

Restituiamo la risposta così com'è qui. Idealmente, dovresti invertire nuovamente i byte e convalidarli ricalcolando il resAuthvalore per prevenire attacchi man-in-the-middle.


Ciò che è fonte di confusione è che non esiste un comando Tspi_Data_Bind.

Questo perché Tspi_Data_Bind è un comando TSS, non un comando TPM. Il motivo è perché non richiede segreti (viene utilizzata solo la chiave pubblica), quindi può essere eseguita senza coinvolgere il TPM. Ciò ha causato confusione, tuttavia, e anche i comandi che non richiedono segreti sono ora inclusi nelle specifiche TPM 2.

Come posso crittografare una chiave con la chiave pubblica del TPM?

Dipende dalla versione del TPM. Con il comando TPM_CreateWrapKey per TPM 1.2. Con il comando TPM2_Create per TPM 2.

In che modo uno sviluppatore blocca una chiave per il TPM?

Crealo nel TPM o racchiudilo o usa qualsiasi altro dei metodi disponibili.

TPM2_Create, specificando una chiave HMAC

Il testo nel libro è confuso. Non si specifica la chiave HMAC , si specifica che si desidera una chiave HMAC .

Il fatto che la chiave HMAC non sia segreta ha senso

No, non ha senso. La chiave è segreta.

... usa le chiavi tenendole al sicuro in un dispositivo hardware ... Eccellente! Come si fa!?

Sono disponibili comandi per creare chiavi o importarle per entrambe le versioni di TPM. Per TPM 1 c'era solo una chiave radice, l'SRK, da cui era possibile stabilire una gerarchia di chiavi creando chiavi avvolte. Con TPM 2 puoi avere più chiavi primarie o root.

Il TPM ha la capacità di generare chiavi crittografiche e proteggere i suoi segreti entro un limite hardware? È così, come?

Vedi sopra.

Eccellente! Questo è esattamente il caso d'uso che desidero. È anche il caso d'uso per cui Microsoft utilizza il TPM. Come lo faccio!?

Probabilmente dipende dal tipo di unità. Nel caso di unità non SED, la chiave di crittografia dell'unità è probabilmente racchiusa in una chiave TPM. Nel caso di unità SED, la password Admin1 (o simile) è sigillata con il TPM.

La chiave di verifica dell'autenticità o EK ... Da qualche parte all'interno del TPM c'è una chiave privata RSA. Quella chiave è chiusa lì dentro, per non essere mai vista dal mondo esterno. Voglio che il TPM firmi qualcosa con la sua chiave privata (cioè lo crittografi con la sua chiave privata).

L'EK non è una chiave di firma, è una chiave di crittografia. Tuttavia, non è una chiave di crittografia generica: può essere utilizzata solo in determinati contesti .

Ma la cosa che vorrei veramente fare è "sigillare" alcuni dati

Vedi sopra.


2

Quando dice

specificando la chiave HMAC

NON significa fornire la chiave HMAC - significa "puntare alla chiave HMAC che si desidera utilizzare" .

I TPM possono utilizzare un numero virtualmente illimitato di chiavi HMAC, come sottolineato nel libro. Devi dire al TPM quale usare.


Quindi esiste forse un esempio di codice che mostra come specificare (puntare a) la chiave HMAC da utilizzare in C # o in un altro linguaggio?
Ciad,
Utilizzando il nostro sito, riconosci di aver letto e compreso le nostre Informativa sui cookie e Informativa sulla privacy.
Licensed under cc by-sa 3.0 with attribution required.