Comment crypter des octets à l'aide du TPM (Trusted Platform Module)

110

Comment puis-je chiffrer des octets à l'aide du module TPM d'une machine?

CryptProtectData

Windows fournit une API (relativement) simple pour chiffrer un objet blob à l'aide de l' CryptProtectDataAPI, que nous pouvons encapsuler dans une fonction facile à utiliser:

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

Les détails de ProtectBytessont moins importants que l'idée que vous pouvez l'utiliser assez facilement:

  • voici les octets que je souhaite chiffrer par une clé secrète contenue dans le System
  • rendez-moi le blob chiffré

Le blob retourné est une structure de documentation non documentée qui contient tout le nécessaire pour décrypter et renvoyer les données d'origine (algorithme de hachage, algorithme de chiffrement, salt, signature HMAC, etc.).

Pour être complet, voici l'exemple d'implémentation de pseudocode de ProtectBytesqui utilise le Crypt APIpour protéger les octets:

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.
}

Comment faire de même avec le TPM?

Le code ci-dessus est utile pour crypter les données de la machine locale uniquement. Les données sont cryptées en utilisant le Systemcompte comme générateur de clé (les détails, bien qu'intéressants, ne sont pas importants ). Le résultat final est que je peux crypter des données (par exemple une clé principale de cryptage du disque dur) qui ne peuvent être décryptées que par la machine locale.

Il est maintenant temps d'aller plus loin. Je souhaite chiffrer certaines données (par exemple, une clé principale de chiffrement du disque dur) qui ne peuvent être déchiffrées que par le TPM local. En d'autres termes, je souhaite remplacer le Qualcomm Trusted Execution Environment ( TEE ) dans le diagramme ci-dessous pour Android, par le TPM sous Windows:

entrez la description de l'image ici

Remarque : je me rends compte que le TPM ne fait pas de signature de données (ou s'il le fait, il ne garantit pas que la signature des mêmes données donnera la même sortie binaire à chaque fois). C'est pourquoi je serais prêt à remplacer «signature RSA» par «chiffrement d'un objet blob 256 bits avec une clé liée au matériel» .

Alors, où est le code?

Le problème est que la programmation TPM est complètement non documentée sur MSDN . Aucune API n'est disponible pour effectuer des opérations. Au lieu de cela, vous devez vous trouver une copie de la pile logicielle du Trusted Computing Group (alias TSS) , déterminer les commandes à envoyer au TPM, avec les charges utiles, dans quel ordre, et appeler la fonction Tbsip_Submit_Command de Windows pour soumettre des commandes directement:

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 n'a pas d'API de niveau supérieur pour effectuer des actions.

C'est l'équivalent moral d'essayer de créer un fichier texte en émettant des commandes d'E / S SATA sur votre disque dur .

Pourquoi ne pas simplement utiliser des pantalons

Le Trusted Computing Group (TCG) a défini sa propre API: TCB Software Stack (TSS) . Une implémentation de cette API a été créée par certaines personnes et s'appelle TrouSerS . Un gars a ensuite porté ce projet sur Windows .

Le problème avec ce code est qu'il n'est pas portable dans le monde Windows. Par exemple, vous ne pouvez pas l'utiliser depuis Delphi, vous ne pouvez pas l'utiliser depuis C #. Cela demande:

  • OpenSSL
  • pThread

Je veux juste que le code crypte quelque chose avec mon TPM.

Ce qui précède CryptProtectDatane nécessite rien d'autre que ce qui est dans le corps de la fonction.

Quel est le code équivalent pour crypter des données à l'aide du TPM? Comme d'autres l'ont noté, vous devez probablement consulter les trois manuels TPM et créer vous-même les objets blob . Cela implique probablement la TPM_sealcommande. Bien que je pense que je ne veux pas sceller les données, je pense que je veux les lier :

Liaison - chiffre les données à l'aide de la clé de liaison TPM, une clé RSA unique descendant d'une clé de stockage. Scellement - crypte les données de la même manière que la liaison, mais spécifie en outre un état dans lequel le TPM doit être pour que les données soient décryptées (descellées)

J'essaye de lire les trois volumes requis afin de trouver les 20 lignes de code dont j'ai besoin:

Mais je n'ai aucune idée de ce que je lis. S'il y avait une sorte de tutoriel ou d'exemples, je pourrais avoir une chance. Mais je suis complètement perdu.

Nous demandons donc à Stackoverflow

De la même manière j'ai pu fournir:

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

quelqu'un peut-il fournir l'équivalent correspondant:

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

qui fait la même chose, sauf plutôt qu'une clé enfermée dans SystemLSA, est enfermée dans le TPM?

Début de la recherche

Je ne sais pas exactement ce que signifie bind . Mais en regardant TPM Main - Part 3 Commands - Specification Version 1.2, il y a une mention de bind :

10.3 TPM_UnBind

TPM_UnBind prend le blob de données qui est le résultat d'une commande Tspi_Data_Bind et le déchiffre pour l'exportation vers l'utilisateur. L'appelant doit autoriser l'utilisation de la clé qui décryptera l'objet blob entrant. TPM_UnBind fonctionne bloc par bloc et n'a aucune notion de relation entre un bloc et un autre.

Ce qui est déroutant, c'est qu'il n'y a pas de Tspi_Data_Bindcommande.

Effort de recherche

Il est horrible de voir que personne ne s'est jamais donné la peine de documenter le TPM ou son fonctionnement. C'est comme s'ils passaient tout leur temps à trouver ce truc sympa avec lequel jouer, mais ne voulaient pas faire face à l'étape douloureuse de le rendre utilisable pour quelque chose.

À partir du livre (maintenant) gratuit Un guide pratique de TPM 2.0: Utilisation du module Trusted Platform dans le nouvel âge de la sécurité :

Chapitre 3 - Tutoriel rapide sur TPM 2.0

Le TPM a accès à une clé privée auto-générée. Il peut donc chiffrer les clés avec une clé publique, puis stocker l'objet blob résultant sur le disque dur. De cette façon, le TPM peut conserver un nombre pratiquement illimité de clés disponibles pour une utilisation sans gaspiller de précieux stockage interne. Les clés stockées sur le disque dur peuvent être effacées, mais elles peuvent également être sauvegardées, ce qui a semblé aux concepteurs comme un compromis acceptable.

Comment puis-je chiffrer une clé avec la clé publique du TPM?

Chapitre 4 - Applications existantes utilisant des TPM

Applications qui devraient utiliser le TPM mais pas

Au cours des dernières années, le nombre d'applications Web a augmenté. Parmi eux, il y a la sauvegarde et le stockage sur le Web. Un grand nombre d'entreprises proposent désormais de tels services, mais à notre connaissance, aucun des clients de ces services ne permet à l'utilisateur de verrouiller la clé du service de sauvegarde sur un TPM. Si cela était fait, ce serait certainement bien si la clé TPM elle-même était sauvegardée en la dupliquant sur plusieurs machines. Cela semble être une opportunité pour les développeurs.

Comment un développeur verrouille-t-il une clé du TPM?

Chapitre 9 - Héritarchies

CAS D'UTILISATION: ENREGISTREMENT DES MOTS DE PASSE DE CONNEXION

Un fichier de mots de passe typique stocke des hachages salés de mots de passe. La vérification consiste à saler et hacher un mot de passe fourni et à le comparer à la valeur stockée. Étant donné que le calcul n'inclut pas de secret, il est soumis à une attaque hors ligne sur le fichier de mots de passe.

Ce cas d'utilisation utilise une clé HMAC générée par TPM. Le fichier de mot de passe stocke un HMAC du mot de passe salé. La vérification consiste à saler et HMACing le mot de passe fourni et à le comparer à la valeur enregistrée. Étant donné qu'un attaquant hors ligne n'a pas la clé HMAC, l'attaquant ne peut pas monter une attaque en effectuant le calcul.

Cela pourrait fonctionner. Si le TPM a une clé HMAC secrète et que seul mon TPM connaît la clé HMAC, alors je pourrais remplacer «Sign (aka TPM chiffrer avec sa clé privée)» par «HMAC». Mais alors dans la ligne suivante, il se renverse complètement:

TPM2_Create, spécification d'une clé HMAC

Ce n'est pas un secret TPM si je dois spécifier la clé HMAC. Le fait que la clé HMAC ne soit pas secrète a du sens lorsque vous réalisez qu'il s'agit du chapitre sur les utilitaires cryptographiques fournis par le TPM. Plutôt que de devoir écrire vous-même SHA2, AES, HMAC ou RSA, vous pouvez réutiliser ce que le TPM a déjà en place.

Chapitre 10 - Clés

En tant que dispositif de sécurité, la capacité d'une application à utiliser des clés tout en les gardant en sécurité dans un périphérique matériel est la plus grande force du TPM. Le TPM peut à la fois générer et importer des clés générées en externe. Il prend en charge les clés asymétriques et symétriques.

Excellent! Comment faites-vous!?

Générateur de clés

On peut soutenir que la plus grande force du TPM est sa capacité à générer une clé cryptographique et à protéger son secret dans une limite matérielle. Le générateur de clés est basé sur le propre générateur de nombres aléatoires du TPM et ne repose pas sur des sources externes d'aléa. Il élimine ainsi les faiblesses basées sur des logiciels faibles avec une source d'entropie insuffisante.

Le TPM a- t -il la capacité de générer des clés cryptographiques et de protéger ses secrets dans une limite matérielle? Est-ce vrai, comment?

Chapitre 12 - Registres de configuration de plate-forme

RAP pour autorisation

CAS D'UTILISATION: SCELLER UNE CLÉ DE CRYPTION DE DISQUE DUR À L'ÉTAT DE LA PLATEFORME

Les applications de chiffrement de disque complet sont beaucoup plus sécurisées si un TPM protège la clé de chiffrement que si elle est stockée sur le même disque, protégé uniquement par un mot de passe. Tout d'abord, le matériel TPM dispose d'une protection anti-marteau (voir le chapitre 8 pour une description détaillée de la protection contre les attaques par dictionnaire TPM), ce qui rend impossible une attaque par force brute sur le mot de passe. Une clé protégée uniquement par un logiciel est beaucoup plus vulnérable à un mot de passe faible. Deuxièmement, une clé logicielle stockée sur le disque est beaucoup plus facile à voler. Prenez le disque (ou une sauvegarde du disque) et vous obtenez la clé. Lorsqu'un TPM détient la clé, la plate-forme entière, ou au moins le disque et la carte mère, doit être volée.

Le scellement permet à la clé d'être protégée non seulement par un mot de passe, mais par une politique. Une politique typique verrouille la clé aux valeurs PCR (l'état du logiciel) en cours au moment du scellage. Cela suppose que l'état au premier démarrage n'est pas compromis. Tout logiciel malveillant préinstallé présent au premier démarrage serait mesuré dans les PCR, et ainsi la clé serait scellée à un état logiciel compromis. Une entreprise moins fiable peut avoir une image disque standard et être scellée aux PCR représentant cette image. Ces valeurs PCR seraient précalculées sur une plateforme vraisemblablement plus fiable. Une entreprise encore plus sophistiquée utiliserait TPM2_PolicyAuthorize et fournirait plusieurs tickets autorisant un ensemble de valeurs PCR fiables. Voir le chapitre 14 pour une description détaillée de la politique d'autorisation et de son application pour résoudre le problème de PCRbrittleness.

Bien qu'un mot de passe puisse également protéger la clé, il existe un gain de sécurité même sans mot de passe de clé TPM. Un attaquant pourrait démarrer la plateforme sans fournir de mot de passe TPMkey, mais ne pourrait pas se connecter sans le nom d'utilisateur et le mot de passe du système d'exploitation. La sécurité du système d'exploitation protège les données. L'attaquant pourrait démarrer un système d'exploitation alternatif, par exemple à partir d'un DVD ou d'une clé USB en direct plutôt qu'à partir du disque dur, pour contourner la sécurité de connexion du système d'exploitation. Cependant, cette configuration de démarrage et ce logiciel différents modifieraient les valeurs PCR. Étant donné que ces nouveaux PCR ne correspondraient pas aux valeurs scellées, le TPM ne libérerait pas la clé de déchiffrement et le disque dur ne pouvait pas être déchiffré.

Excellent! C'est exactement le cas d'utilisation que je souhaite. C'est également le cas d'utilisation pour lequel Microsoft utilise le TPM. Comment fait-on ça!?

J'ai donc lu tout ce livre, et il n'a rien fourni d'utile. Ce qui est assez impressionnant car il fait 375 pages. Vous vous demandez ce que contient le livre - et en y repensant, je n'en ai aucune idée.

Nous abandonnons donc le guide définitif de programmation du TPM et nous nous tournons plutôt vers une documentation de Microsoft:

À partir de la boîte à outils du fournisseur de chiffrement de la plate-forme Microsoft TPM . Il mentionne exactement ce que je veux faire:

La clé d'approbation ou EK

L'EK est conçu pour fournir un identifiant cryptographique fiable pour la plateforme. Une entreprise peut maintenir une base de données des clés d'approbation appartenant aux TPM de tous les PC de son entreprise, ou un contrôleur de structure de centre de données peut avoir une base de données des TPM dans toutes les lames. Sous Windows, vous pouvez utiliser le fournisseur NCrypt décrit dans la section «Platform Crypto Provider dans Windows 8» pour lire la partie publique de l'EK.

Quelque part à l'intérieur du TPM se trouve une clé privée RSA. Cette clé est enfermée là-dedans - pour ne jamais être vue par le monde extérieur. Je veux que le TPM signe quelque chose avec sa clé privée (c'est-à-dire le crypte avec sa clé privée).

Je veux donc l' opération la plus basique qui puisse exister:

entrez la description de l'image ici

Cryptez quelque chose avec votre clé privée. Je ne demande même pas (encore) les choses les plus compliquées:

  • "sceller" il en fonction de l'état de la PCR
  • créer une clé et la stocker dans une mémoire volatile ou non volatile
  • créer une clé symétrique et essayer de la charger dans le TPM

Je demande l'opération la plus élémentaire qu'un TPM peut effectuer. Pourquoi est-il impossible d'obtenir des informations sur la façon de procéder?

Je peux obtenir des données aléatoires

Je suppose que j'étais désinvolte quand j'ai dit que la signature RSA était la chose la plus élémentaire que le TPM puisse faire. La chose la plus basique que l'on puisse demander au TPM est de me donner des octets aléatoires. Que j'ai compris comment faire:

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 fantaisie

Je me rends compte que le nombre de personnes utilisant le TPM est très faible. C'est pourquoi personne sur Stackoverflow n'a de réponse. Je ne peux donc pas vraiment devenir trop gourmand pour trouver une solution à mon problème commun. Mais ce que je voudrais vraiment faire est de «sceller» certaines données:

entrez la description de l'image ici

  • présenter au TPM des données (par exemple 32 octets de matériel clé)
  • faire crypter les données par le TPM, renvoyant une structure blob opaque
  • demander plus tard au TPM de décrypter l'objet blob
  • le décryptage ne fonctionnera que si les registres PCR du TPM sont les mêmes que lors du cryptage.

En d'autres termes:

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

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

Cryptography Next Gen (Cng, alias BCrypt) prend en charge le TPM

L'API de cryptographie d'origine de Windows était connue sous le nom d'API Crypto.

À partir de Windows Vista, l'API Crypto a été remplacée par l' API Cryptography: Next Generation (connue en interne sous le nom de BestCrypt , en abrégé BCrypt , à ne pas confondre avec l'algorithme de hachage de mot de passe ).

Windows est livré avec deux fournisseurs BCrypt :

Le fournisseur Platform Crypto n'est pas documenté sur MSDN, mais dispose de la documentation d'un site Microsoft Research 2012:

Boîte à outils du fournisseur de chiffrement de la plateforme TPM

Le fournisseur de chiffrement et la boîte à outils de la plate-forme TPM contient des exemples de code, des utilitaires et de la documentation pour l'utilisation des fonctionnalités liées au TPM dans Windows 8. Les sous-systèmes décrits incluent le fournisseur de chiffrement de la plate-forme Crypto-Next-Gen (CNG) soutenu par TPM et comment les fournisseurs de services d'attestation peut utiliser les nouvelles fonctionnalités de Windows. Les systèmes basés sur TPM1.2 et TPM2.0 sont pris en charge.

Il semble que l'intention de Microsoft soit de présenter la fonctionnalité de cryptage TPM avec le fournisseur de cryptage de plate-forme Microsoft de l' API Cryptography NG .

Chiffrement par clé publique à l'aide de Microsoft BCrypt

Étant donné que:

une façon peut - être avant de savoir comment faire la signature numérique en utilisant l' API Microsoft Cryptography Next Gen .

Ma prochaine étape sera de trouver le code pour faire le cryptage dans BCrypt, avec une clé publique RSA, en utilisant le fournisseur standard ( MS_PRIMITIVE_PROVIDER). Par exemple:

  • 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

Avec ce code fonctionnant, je pourrai peut-être passer à l'utilisation du fournisseur TPM ( MS_PLATFORM_CRYPTO_PROVIDER).

22/02/2016: Et avec Apple étant obligé d'aider à déchiffrer les données des utilisateurs, il y a un regain d'intérêt pour la façon dont le TPM exécute la tâche la plus simple pour laquelle il a été inventé - chiffrer quelque chose.

C'est à peu près l'équivalent du fait que tout le monde possède une voiture, mais personne ne sait comment en démarrer une. Cela peut faire des choses vraiment utiles et intéressantes, si seulement nous pouvions passer l' étape 1 .

Lecture bonus

Ian Boyd
la source
Pour la liaison (cryptage), il n'y a pas de fonction explicite disponible et elle n'est pas non plus nécessaire. Il vous suffit de créer une clé de liaison dans le TPM et d'utiliser sa partie publique pour chiffrer une clé de chiffrement symétrique sek avec la fonction de chiffrement rsa des systèmes ("RSA / ECB / OAEPWithSHA1AndMGF1Padding") et de l'enregistrer dans la bonne structure ("TcTssConstants.TSS_ENCDATA_BIND"). Pour dissocier (déchiffrer) le sek, utilisez simplement la fonction de détachement du TPM et utilisez le sek dans la fonction de cryptage symétrique de votre choix. J'ai une base de code plutôt ancienne pour cela que j'ai faite il y a quelque temps, peut-être que cela aide: goo.gl/jV1Ouw
evildead
De wikipedia, Binding - chiffre les données à l'aide de la clé de liaison TPM, une clé RSA unique issue d'une clé de stockage. fr.wikipedia.org/wiki/Trusted_Platform_Module On dirait que cette paire de commandes (TSpi_Data_Bind / TPM_UnBind) devrait suffire à vos besoins ...
Alex Mazzariol
1
Je ne pense pas que vous deviez utiliser directement le TPM. Il est pris en charge via les API standard CNG / NCryptXXX et le «fournisseur de crypto-monnaies Microsoft Platform» (pour les plates-formes Windows récentes, et si le matériel est correct et activé bien sûr). Vous pourriez peut-être jeter un œil à la "Boîte à outils du fournisseur de crypto- monnaies de la plateforme TPM ici: research.microsoft.com/en-us/downloads/… vérifiez également ceci: tiw2013.cse.psu.edu/slides/…
Simon Mourier
CryptProtectData n'utilise pas nécessairement le TPM. D'autre part, si vous pouvez obtenir un handle CNG ou CSP valide pour le TPM, vous pouvez l'utiliser dans les fonctions de cryptage.
Michael Chourdakis
1
@ b3nj1 Non, je ne l'étais pas; personne n'a pu répondre à la question.
Ian Boyd

Réponses:

7

Apprêt

Tout ce qui suit concerne TPM 1.2. Gardez à l'esprit que Microsoft requiert un TPM 2.0 pour toutes les futures versions de Windows. La génération 2.0 est fondamentalement différente de la 1.2

Il n'y a pas de solution en une seule ligne en raison des principes de conception du TPM. Considérez le TPM comme un microcontrôleur avec des ressources limitées. Son objectif principal de conception était d'être bon marché, tout en restant sûr. Ainsi, le TPM a été déchiré de toute logique qui n'était pas nécessaire pour une opération sécurisée. Ainsi, un TPM ne fonctionne que lorsque vous avez au moins un logiciel plus ou moins gros , émettant beaucoup de commandes dans le bon ordre. Et ces séquences de commandes peuvent devenir très complexes. C'est pourquoi TCG a spécifié le TSS avec une API bien définie. Si vous souhaitez suivre la voie Java, il existe même une API Java de haut niveau . Je ne suis pas au courant d'un projet similaire pour C # / .net

Développement

Dans votre cas, je vous suggère de regarder le logiciel TPM d'IBM.

Dans le package, vous trouverez 3 composants très utiles:

  • un émulateur logiciel TPM
  • une bibliothèque tpm légère
  • quelques utilitaires de ligne de commande de base

Vous n'avez pas nécessairement besoin de l'émulateur logiciel TPM, vous pouvez également vous connecter au HW TPM de la machine. Cependant, vous pouvez intercepter les commandes émises et regarder les réponses, apprenant ainsi comment elles sont assemblées et comment elles correspondent à la spécification de commande.

Haut niveau

Conditions préalables:

  1. TPM est activé
  2. Le pilote TPM est chargé
  3. vous vous êtes approprié le TPM

Pour sceller un blob, vous devez effectuer les opérations suivantes:

  1. créer une clé
  2. stocker la touche quelque part
  3. assurez-vous que la clé est chargée dans le TPM
  4. sceller le blob

Pour desceller, vous devez:

  1. obtenir le key-blob
  2. charger la clé dans le TPM
  3. desceller le blob scellé

Vous pouvez stocker l'objet blob clé dans votre structure de données que vous utilisez pour stocker les octets protégés.

La plupart des commandes TPM dont vous avez besoin sont autorisées. Par conséquent, vous devez établir des sessions d'autorisation si nécessaire. AFAIR ce sont pour la plupart des sessions du RAFEO.

Commandes TPM

Actuellement, je ne peux pas exécuter une version de débogage, donc je ne peux pas vous fournir la séquence exacte. Considérez donc ceci comme une liste non ordonnée de commandes que vous devrez utiliser:

  • TPM_OSAP
  • TPM_CreateWrapKey
  • TPM_LoadKey2
  • TPM_Seal

Si vous souhaitez également lire les valeurs PCR actuelles:

  • TPM_PCRRead
Scolytus
la source
Microsoft a sa bibliothèque gérée C # .NET pour utiliser le TPM . Ils ont également un émulateur TPM , auquel la bibliothèque gérée peut se connecter comme alternative de débogage si un vrai TPM n'est pas présent. Ils disposent également du kit d'outils TPM Platform Provider , qui contient la documentation et un exemple de code pour l'utilisation du TPM. Maintenant, si seulement quelqu'un pouvait trouver comment utiliser le TPM pour crypter des octets.
Ian Boyd
Vos deux premiers liens sont uniquement TPM 2.0. Si vous voulez les utiliser, j'ai bien peur de ne pas vous aider.
Scolytus
4

Clés de confiance et cryptées

Les clés approuvées et chiffrées sont deux nouveaux types de clés ajoutés au service de trousseau de clés du noyau existant. Ces deux nouveaux types sont des clés symétriques de longueur variable, et dans les deux cas, toutes les clés sont créées dans le noyau, et l'espace utilisateur voit, stocke et charge uniquement les objets blob chiffrés. Les clés de confiance nécessitent la disponibilité d'une puce TPM (Trusted Platform Module) pour une plus grande sécurité, tandis que les clés chiffrées peuvent être utilisées sur n'importe quel système. Tous les objets blob de niveau utilisateur sont affichés et chargés en hexa ascii pour plus de commodité et leur intégrité est vérifiée.

Les clés approuvées utilisent un TPM à la fois pour générer et pour sceller les clés. Les clés sont scellées sous une clé RSA de 2048 bits dans le TPM, et éventuellement scellées aux valeurs de PCR (mesure d'intégrité) spécifiées, et uniquement descellées par le TPM, si les vérifications de l'intégrité des PCR et des blob correspondent. Une clé de confiance chargée peut être mise à jour avec de nouvelles (futures) valeurs PCR, de sorte que les clés sont facilement migrées vers de nouvelles valeurs pcr, par exemple lorsque le noyau et les initramfs sont mis à jour. La même clé peut avoir de nombreux objets blob enregistrés sous différentes valeurs PCR, de sorte que plusieurs démarrages sont facilement pris en charge.

Par défaut, les clés de confiance sont scellées sous le SRK, qui a la valeur d'autorisation par défaut (20 zéros). Cela peut être réglé au moment de takeownership avec l'utilité du pantalon: 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 printrenvoie une copie hexadécimale ascii de la clé scellée, qui est au format standard TPM_STORED_DATA. La longueur de clé pour les nouvelles clés est toujours en octets. Les clés de confiance peuvent être de 32 à 128 octets (256 à 1024 bits), la limite supérieure est de tenir dans la longueur de clé SRK (RSA) de 2048 bits, avec toute la structure / remplissage nécessaire.

Les clés chiffrées ne dépendent pas d'un TPM et sont plus rapides, car elles utilisent AES pour le chiffrement / déchiffrement. Les nouvelles clés sont créées à partir de nombres aléatoires générés par le noyau et sont chiffrées / déchiffrées à l'aide d'une clé «principale» spécifiée. La clé «principale» peut être de type clé de confiance ou clé utilisateur. Le principal inconvénient des clés cryptées est que si elles ne sont pas enracinées dans une clé de confiance, elles ne sont aussi sécurisées que la clé utilisateur qui les chiffre. La clé utilisateur principale doit donc être chargée de la manière la plus sécurisée possible, de préférence au début du démarrage.

La partie décryptée des clés cryptées peut contenir soit une clé symétrique simple, soit une structure plus complexe. Le format de la structure la plus complexe est spécifique à l'application, qui est identifiée par «format».

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'

Exemples d'utilisation de clés fiables et chiffrées

Créez et enregistrez une clé de confiance nommée "kmk" d'une longueur de 32 octets:

$ 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

Chargez une clé de confiance à partir de l'objet blob enregistré:

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

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

Resceller une clé de confiance sous les nouvelles valeurs pcr:

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

Le consommateur initial de clés de confiance est EVM, qui, au moment du démarrage, a besoin d'une clé symétrique de haute qualité pour la protection HMAC des métadonnées de fichier. L'utilisation d'une clé de confiance offre des garanties solides que la clé EVM n'a pas été compromise par un problème de niveau utilisateur et, lorsqu'elle est scellée à des valeurs PCR de démarrage spécifiques, protège contre les attaques de démarrage et hors ligne. Créez et enregistrez une clé chiffrée "evm" à l'aide de la clé de confiance "kmk" ci-dessus:

option 1: omettre 'format'

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

option 2: définir explicitement le 'format' comme 'par défaut'

$ 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

Chargez une clé chiffrée "evm" à partir de l'objet blob enregistré:

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

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

D'autres utilisations des clés de confiance et chiffrées, telles que le chiffrement de disque et de fichier, sont prévues. En particulier, le nouveau format «ecryptfs» a été défini afin d'utiliser des clés cryptées pour monter un système de fichiers eCryptfs. Plus de détails sur l'utilisation peuvent être trouvés dans le fichier 'Documentation / security / keys-ecryptfs.txt'.

Chandana
la source
Avez-vous une idée de l'endroit où ces deux nouveaux types de clés ont été ajoutés? Dans quelle version je veux dire. J'utilise actuellement 1.2 (packages d'entreprise) et celui-ci ne les prend pas en charge. Peut-être en 1.5+?
Acapulco
1
Quelle est la source de cette publication? La fin fait référence à un documentDocumentation/security/keys-ecryptfs.tx
goodguys_activate
Ceux-ci semblent tous être des appels à un programme de ligne de commande. Je ne vois pas de code sur la façon d'utiliser le TPM.
Ian Boyd
3

Comment puis-je chiffrer des octets à l'aide du module TPM d'une machine?

Dépend de votre intention et des circonstances:

  • Quel type de module TPM possédez-vous (1 famille ou 2 familles)?
  • Dans quel état se trouve le TPM? At-il été possédé? At-il été provisionné?
  • Quel est votre langage de programmation?
  • Voulez-vous chiffrer ou signer? (c'est vague du reste de la question)
  • Quelle est la taille des données que vous souhaitez chiffrer?
  • Voulez-vous utiliser une clé symétrique ou une clé asymétrique?
  • Voulez-vous utiliser une clé qui existe déjà sur le TPM ou souhaitez-vous qu'elle crée la clé en premier?
  • Par «chiffrer», vous entendez peut-être «envelopper une clé»?
  • Voulez-vous verrouiller les données chiffrées dans la configuration du système, de sorte qu'elles ne puissent être déchiffrées que lorsque le système est de retour dans la même configuration?
  • Voulez-vous exiger une autorisation pour le déchiffrement?
  • Peut-être n'avez-vous pas du tout besoin de chiffrer, mais plutôt de stocker les données dans le TPM?
  • Si vous stockez les données dans le TPM, voulez-vous exiger une autorisation, ou que le système soit dans une configuration particulière, pour la récupération?

Chacun de ces cas d'utilisation (et il y en a d'autres) - ou une combinaison de ceux-ci - présente un chemin d'implémentation différent. Considérez le TPM comme un couteau suisse de dispositifs cryptographiques: il n'y a pas grand-chose que vous ne pouvez pas faire avec, mais la facilité d'utilisation souffre de cette polyvalence. La question continue de rebondir entre le cryptage, la signature et le verrouillage de la configuration du système, mais la partie principale de cette réponse considérera que la commande Seal couvre la plupart des besoins décrits dans la question.

Il est maintenant temps d'aller plus loin. Je souhaite chiffrer certaines données (par exemple, une clé principale de chiffrement du disque dur) qui ne peuvent être déchiffrées que par le TPM local.

C'est à cela que sert la commande Bind (remplacée par la commande Create pour TPM 2). Vous chargez une clé qui dérive d'une clé liée au TPM et la chiffrez avec elle (ou directement avec une clé liée au matériel). De cette façon, les données ne peuvent être déchiffrées qu'avec l'accès au même TPM.

En d'autres termes, je souhaite remplacer le Qualcomm Trusted Execution Environment (TEE) dans le diagramme ci-dessous pour Android, par le TPM sous Windows:

Je ne sais pas si la réplication de tout ce processus est une bonne idée. D'une part, il n'est pas nécessaire d'utiliser une opération de signature n'importe où dans le processus. Il semblerait qu'à l'époque du développement d'Android 5, l' API Keystore se limitait aux opérations de signature et de vérification. Ma meilleure hypothèse est que l'équipe de chiffrement de disque a fait de son mieux pour travailler avec ce qu'elle avait et a conçu un algorithme dans lequel l'une des clés intermédiaires a été dérivée avec une opération de signature , en utilisant une clé TEE stockée, liant ainsi l'ensemble du processus à un matériel. clé liée uniquement disponible sur la plate-forme - car la signature était le seul moyen de le faire à l'époque. Cependant, il n'est pas nécessaire de vous contraindre de cette manière si vous avez accès à un TPM, ce qui vous donne plus de capacités que vous ne pensiez en avoir besoin!

Je me rends compte que le TPM ne fait pas de signature de données

Ceci est faux, les deux versions de TPM prennent en charge la signature.

(ou si c'est le cas, cela ne garantit pas que la signature des mêmes données donnera la même sortie binaire à chaque fois)

Cela n'a aucun sens. La signature des mêmes données avec la même clé sera de produire la même signature. Vous pouvez confondre l'opération de signature avec l'opération de devis, qui se mélangera dans un nonce.

C'est pourquoi je serais prêt à remplacer «signature RSA» par «chiffrement d'un objet blob 256 bits avec une clé liée au matériel».

Cela devrait en fait être l'option préférée, bien que les deux soient possibles avec un TPM. Voir au dessus.

Le problème est que la programmation TPM est complètement non documentée sur MSDN. Aucune API n'est disponible pour effectuer des opérations.

Malheureusement, il n'y a pas grand-chose à documenter. L'API Win est limitée à quelques fonctions TBS qui sont un niveau supprimé du pilote.

Au lieu de cela, vous devez vous trouver une copie de la pile logicielle du Trusted Computing Group (alias TSS), déterminer les commandes à envoyer au TPM, avec les charges utiles, dans quel ordre, et appeler la fonction Tbsip_Submit_Command de Windows pour soumettre des commandes directement:

En fait, non, si vous aviez un TSS, vous n'auriez pas à l'utiliser Tbsip_submit_Command(). C'est tout l'intérêt d'avoir un TSS - les détails de bas niveau sont abstraits.

Windows n'a pas d'API de niveau supérieur pour effectuer des actions.

Toujours vrai pour TPM 1, mais pour TPM 2, il y a TSS.MSR .

C'est l'équivalent moral d'essayer de créer un fichier texte en émettant des commandes d'E / S SATA sur votre disque dur.

Correct.

Pourquoi ne pas simplement utiliser Trousers ... Le problème avec ce code est qu'il n'est pas portable dans le monde Windows. Par exemple, vous ne pouvez pas l'utiliser depuis Delphi, vous ne pouvez pas l'utiliser depuis C #. Il nécessite: OpenSSL, pThread

Il n'est pas clair que ce soit un défi insurmontable. L'accès à TrouSerS via une interopérabilité devrait être préférable à la réécriture de tout le code de structuration des données. En outre, il y avait doTSSau moment de la rédaction de la question.

Quel est le code équivalent pour crypter des données à l'aide du TPM? Cela implique probablement la commande TPM_seal. Bien que je pense que je ne veux pas sceller les données, je pense que je veux les lier:

La question contient une citation décrivant la différence entre les deux commandes, il ne devrait donc pas y avoir beaucoup de confusion. Le scellement est similaire à la liaison, avec la contrainte supplémentaire que l'état du système doit être le même pour que les données soient descellées.

De la même manière j'ai pu fournir:

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

quelqu'un peut-il fournir l'équivalent correspondant:

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

qui fait la même chose, sauf plutôt qu'une clé verrouillée dans System LSA, est verrouillée dans le TPM?

Tout d'abord, il convient de souligner qu'il existe deux versions majeures de TPM, qui sont totalement incompatibles entre elles. Donc pratiquement aucun code que vous avez écrit pour TPM 1 ne fonctionnera pour TPM 2. L'API TBS est le seul code commun entre les deux et, pour être juste envers Microsoft, cela peut avoir été l'une des raisons pour lesquelles cette API n'a jamais grandi. La partie principale de la réponse présentera le code du TPM 1 pour deux raisons:

  • La question est chargée de concepts spécifiques au TPM 1, de sorte que les personnes utilisant TPM 1 sont plus susceptibles d'atterrir ici à leur recherche
  • Il existe une implémentation Microsoft de TSS pour TPM 2.

Deuxièmement, rendons la question plus précise. Je la réinterprète comme suit:

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?

La commande Sceller est la mieux adaptée pour cela, car elle remplit la même fonction que la commande Bind lorsque la taille de la sélection PCR est définie sur zéro, mais la sélection PCR peut facilement être modifiée pour inclure les PCR que vous souhaitez. On se demande pourquoi la commande Bind a été incluse dans la spécification, et comme indiqué, elle a été supprimée dans la spécification TPM 2 et les deux ont été combinées dans une commande Create.

Voici le code C # pour utiliser la commande TPM 1.2 Seal pour crypter des données avec uniquement des fonctions TBS (note: ce code n'est pas testé et ne fonctionnera probablement pas sans débogage) :

[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;

}

Analyse du code:

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

Voici quelques-unes des rares fonctions disponibles dans Tbs.h et les seules que nous utiliserons ici. Ils vous permettent essentiellement d'ouvrir un handle vers le périphérique et de communiquer avec lui en envoyant et en recevant des octets bruts.

    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 est big endian, Windows est little endian. Ainsi, l'ordre des octets devra être inversé pour toutes les données que nous envoyons. Nous devons seulement nous soucier de l'inversion des entiers non signés 32 bits et 16 bits ici.

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

Ici, nous utilisons Tbsi_Context_Create () pour ouvrir un handle pour parler au TPM. Le TBS_CONTEXT_PARAMSparamètre est juste une structure C avec un champ int 32 bits non signé qui doit être défini sur 1 pour parler à une instance TPM 1.2, et c'est ce à quoi nous l'avons défini.

    byte[] cmdBuf = new byte[768];

Il s'agit de la taille minimale de la mémoire tampon dans les spécifications du client PC TPM . Ce sera plus que suffisant pour nos besoins ici.

TPM 1.2 Spec Part 3 dit ce qui suit:

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.

Nous devons chiffrer XOR ce paramètre "secret" en utilisant un nonce généré lors d'une session du RAFEO. L'une des poignées d'entrée de la commande Seal est également une poignée OSAP:

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

Nous devons donc commencer par établir cette session du RAFEO. Le RAFEO est décrit dans TPM 1.2 Spec Part 1 . Le protocole RAFEO, ou protocole d'autorisation spécifique à un objet, a été inventé pour gérer le cas d'utilisation où vous souhaitez utiliser un objet TPM qui nécessite une autorisation plusieurs fois, mais que vous ne souhaitez pas fournir d'autorisation à chaque fois: une session RAFEO est utilisée à la place, ce qui repose sur sur le concept de "secret partagé", qui est un HMACqui mélange les données d'autorisation d'objet avec des nonces générés de chaque côté pour empêcher les attaques de réponse. Par conséquent, le "secret partagé" n'est connu que des deux côtés de cette session: le côté qui a lancé la session (utilisateur) et le côté qui l'a acceptée (TPM); aussi, les deux côtés doivent avoir les mêmes données d'autorisation d'objet pour que le «secret partagé» soit le même; de plus, le "secret partagé" utilisé dans une session sera invalide dans une autre. Ce diagramme de la spécification décrit le processus:

RAFEO

Nous n'utiliserons pas plusieurs sessions dans ce cas particulier (en fait, ce paramètre est ignoré avec la commande Seal!) Et la clé que nous utiliserons ne nécessite pas d'autorisation, mais malheureusement, nous sommes toujours liés par la spécification pour établir un RAFEO session.

    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;

Les opérandes de commande TPM_OSAP sont:

Opérandes TPM_OSAP

Chaque commande TPM 1.2 est présentée comme suit:

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

La balise est une valeur sur deux octets qui indique si ce qui suit est une entrée ou une sortie, et s'il existe des valeurs de données d'authentification après les paramètres de commande. Pour TPM_OSAP, la balise doit être TPM_TAG_RQU_COMMAND (0x00C1) selon la spécification, ce qui signifie «une commande sans autorisation».

La taille est une valeur de quatre octets qui spécifie la taille de la commande en octets, y compris l'étiquette et la taille elle-même. Nous définirons cette valeur plus tard, une fois que nous l'aurons calculée.

Le code de commande est une valeur de quatre octets qui sert d'ID de commande: il indique au TPM comment interpréter le reste de la commande. Notre code de commande ici est TPM_OSAP (0x0000000B).

Les deux éléments suivants à définir sont le type d'entité et la valeur d'entité. Puisque nous voulons utiliser une clé qui existe déjà dans le TPM, nous utiliserons le type d'entité "SRK" (0x0004), et puisque nous travaillons sous l'hypothèse que le TPM a déjà été possédé, il est sûr de supposer qu'il a un SRK chargé sous le handle permanent 0x40000000 selon la spécification, nous allons donc utiliser cette valeur de handle permanent pour notre valeur d'entité. (SRK signifie "Storage Root Key" et est la clé racine dont dérivent la plupart des autres clés appartenant au TPM)

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

Enfin, nous calculons la taille de la commande, la définissons et envoyons la commande.

    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);

Les données que nous sommes censés récupérer du TPM sur TPM_OSAP sont:

Réponse TPM_OSAP

Alors on revient:

  • Le handle d'autorisation à utiliser avec notre commande principale (Seal)
  • nonceEven: le nonce généré par le TPM à utiliser avec la commande principale
  • nonceEvenOSAP: le nonce OSAP qui est le contre-nonce au nonce que nous avons généré de notre côté avant d'envoyer la commande TPM_OSAP. Ces deux nonces seront utilisés pour générer le "secret partagé".

Nous extrayons ces valeurs et les stockons dans des variables.

    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);

Ensuite, nous calculons le "secret partagé". Selon les spécifications, les valeurs qui entrent dans le calcul sont les deux nonces RAFEO (un généré par l'utilisateur et l'autre généré par le TPM) et la valeur d'autorisation pour la clé que nous voulons utiliser - le SRK. Par convention, la valeur d'authentification SRK est "l'authentification bien connue": un tampon de 20 octets remis à zéro. Techniquement, on pourrait changer cette valeur en quelque chose d'autre lors de la prise en charge du TPM, mais cela n'est pas fait dans la pratique, nous pouvons donc supposer en toute sécurité que la valeur "authentification bien connue" est bonne.

Jetons ensuite un œil à ce qui se passe dans la commande TPM_Seal:

TPM_Seal

La plupart de ces paramètres sont simples à construire, à l'exception de deux d'entre eux: encAuthet pubAuth. Regardons-les un par un.

encAuthest "Les AuthData cryptées pour les données scellées." Notre AuthData ici est "l'authentification bien connue" d'avant, mais oui, nous devons encore le chiffrer. Parce que nous utilisons une session RAFEO, elle est cryptée selon ADIP, ou Authorization-Data Insertion Protocol. D'après la spécification: "L'ADIP permet la création de nouvelles entités et l'insertion sécurisée de la nouvelle entité AuthData. La transmission de la nouvelle AuthData utilise un cryptage avec la clé basée sur le secret partagé d'une session OSAP." De plus: "Pour l'algorithme de chiffrement XOR obligatoire, le créateur crée une clé de chiffrement à l'aide d'un hachage SHA-1 du secret partagé du RAFEO et d'un nonce de session. Le créateur XOR crypte la nouvelle AuthData en utilisant la clé de chiffrement comme tampon unique et envoie ces données chiffrées avec la demande de création au TPM. "

Le diagramme suivant explique le fonctionnement d'ADIP:

UN PLONGEON

pubAuthest "Le résumé de session d'autorisation pour les entrées et keyHandle." La partie 1 de la spécification, dans «Déclarations de paramètres pour les exemples OIAP et OSAP» explique comment interpréter la table de paramètres TPM_Seal ci-dessus: «La colonne HMAC # détaille les paramètres utilisés dans le calcul HMAC. Les paramètres 1S, 2S, etc. sont concaténés et haché en inParamDigest ou outParamDigest, appelé implicitement 1H1 et éventuellement 1H2 s'il y a deux sessions d'autorisation. Pour la première session, 1H1, 2H1, 3H1 et 4H1 sont concaténés et HMAC. Pour la deuxième session, 1H2, 2H2, 3H2, et 4H2 sont concaténés et HMAC. " Nous devrons donc hacher le texte en clair, sa taille, la taille des informations PCR, encAuthd'en haut et l'ordinal TPM_Seal, puis HMAC avec les deux nonces et le booléen "continue session" en utilisant le OSAP "

Rassembler tout cela dans un diagramme:

calcul pubAuth

Remarquez comment nous définissons "Taille des informations PCR" à zéro dans ce code, car nous voulons simplement chiffrer les données sans les verrouiller dans un état système. Cependant, il est trivial de fournir une structure "info PCR" si nécessaire.

    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);

Enfin, nous construisons la commande et l'envoyons.

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

Nous utilisons la fonction Tbsip_Context_Close () pour fermer notre handle de communication.

Nous renvoyons la réponse telle quelle ici. Dans l'idéal, vous voudriez inverser à nouveau les octets et le valider en recalculant la resAuthvaleur pour empêcher les attaques man-in-the-middle.


Ce qui est déroutant, c'est qu'il n'y a pas de commande Tspi_Data_Bind.

En effet, Tspi_Data_Bind est une commande TSS, pas une commande TPM. La raison en est que cela ne nécessite aucun secret (seule la clé publique est utilisée), donc cela peut être fait sans impliquer le TPM. Cela a toutefois semé la confusion et même les commandes qui ne nécessitent aucun secret sont désormais incluses dans la spécification TPM 2.

Comment puis-je chiffrer une clé avec la clé publique du TPM?

Dépend de la version du TPM. Avec la commande TPM_CreateWrapKey pour TPM 1.2. Avec la commande TPM2_Create pour TPM 2.

Comment un développeur verrouille-t-il une clé du TPM?

Créez-le dans le TPM, encapsulez-le ou utilisez toute autre méthode disponible.

TPM2_Create, spécification d'une clé HMAC

Le texte du livre est déroutant. Vous ne spécifiez pas la clé HMAC , vous spécifiez que vous voulez une clé HMAC .

Le fait que la clé HMAC ne soit pas secrète a du sens

Non, cela n'a pas de sens. La clé est secrète.

... utilisez les clés tout en les gardant en sécurité dans un périphérique matériel ... Excellent! Comment faites-vous!?

Il existe des commandes permettant de créer des clés ou de les importer pour les deux versions de TPM. Pour TPM 1, il n'y avait qu'une seule clé racine - la SRK - à partir de laquelle vous pouviez établir une hiérarchie de clés en créant des clés encapsulées. Avec TPM 2, vous pouvez avoir plusieurs clés principales ou racine.

Le TPM a-t-il la capacité de générer des clés cryptographiques et de protéger ses secrets dans une limite matérielle? Est-ce vrai, comment?

Voir au dessus.

Excellent! C'est exactement le cas d'utilisation que je souhaite. C'est également le cas d'utilisation pour lequel Microsoft utilise le TPM. Comment fait-on ça!?

Dépend probablement du type de lecteur. Dans le cas de lecteurs non SED, la clé de chiffrement de lecteur est probablement encapsulée avec une clé TPM. Dans le cas des lecteurs SED, le mot de passe Admin1 (ou autre) est scellé avec le TPM.

La clé d'approbation ou EK ... Quelque part dans le TPM se trouve une clé privée RSA. Cette clé est enfermée là-dedans - pour ne jamais être vue par le monde extérieur. Je veux que le TPM signe quelque chose avec sa clé privée (c'est-à-dire le crypte avec sa clé privée).

L'EK n'est pas une clé de signature - c'est une clé de chiffrement. Cependant, ce n'est pas une clé de chiffrement à usage général: elle ne peut être utilisée que dans certains contextes .

Mais la chose que je voudrais vraiment faire est de «sceller» certaines données

Voir au dessus.

mnistique
la source
2

Quand il dit

spécification de la clé HMAC

cela ne signifie PAS fournir la clé HMAC - cela signifie "pointer vers la clé HMAC que vous souhaitez utiliser" .

Les TPM peuvent utiliser un nombre pratiquement illimité de clés HMAC, comme indiqué dans le livre. Vous devez indiquer au TPM lequel utiliser.

DCC
la source
Existe-t-il peut-être un exemple de code montrant comment spécifier (pointer vers) la clé HMAC à utiliser en C # ou dans un autre langage?
Tchad