Comment prouver qu'une photographie numérique n'a pas été modifiée avant publication ? En ajoutant des données d'authenticité à l'image, fonction disponible depuis plusieurs années chez Canon et Nikon. Dmitry Sklyarov de la société ElcomSoft a décrit en novembre 2010 [1] le principe général utilisé par les boîtiers de la marque rouge, mais sans livrer suffisamment de détails pour recréer ces données, appelées ici « Original Decision Data », ou ODD. Cet article se propose de décrire comment trouver les secrets de son appareil (sans fer à souder) et comment recalculer ces données. Le constructeur a depuis janvier 2011 supprimé cette fonctionnalité, sans doute en réaction à la présentation précédemment citée, et émis un avis produit [2]. Les pages suivantes se veulent didactiques et en prolongement de la présentation d'ElcomSoft et ne sauraient en aucun cas la remplacer. Elles décrivent des expérimentations faites début 2010, puis achevées en novembre 2010, grâce à la pièce manquante du puzzle...
1. Le format RAW/CR2 et l'usage du tag ODD
Les photographies numériques sont écrites sur la carte mémoire en format RAW (basé sur le format TIFF) ou au format Jpeg. Malgré les efforts de Dave Coffin avec Dcraw [3], Phil Harvey avec ExifTool [4] et de votre serviteur [5] pour le format CR2 de Canon, une partie des tags TIFF (ou Jpeg) ne sont pas documentés : vous ne maîtrisez pas complètement de quelle manière sont archivés les originaux de vos souvenirs de vacances. En l’occurrence, c'est le cas du tag « Original Decision Data », qui porte l'identifiant unique 0x0083 (131 en décimal). Lorsque cette option est activée, via le menu, l'appareil ajoute ce « sceau » à chaque image. L'authenticité des images peut ensuite être vérifiée grâce à un kit vendu par Canon [6]. Il est même possible de chiffrer les images sur les appareils haut de gamme uniquement, en insérant dans le boîtier une carte Secure Mobile dédiée, mais l'algorithme sous-jacent n'est pas décrit à ce jour. Les images au format Jpeg contiennent aussi des métadonnées, et peuvent également contenir un tag ODD, de format quasi-identique.
2. Que peut-on apprendre par observation du tag ODD ?
Pour commencer, analysons le contenu du tag ODD pour le modèle 20D, car son format (version 2) est plus simple que sur les appareils récents (version 3). Il ne s'agit pas dans cette première partie d'expliquer complètement le mécanisme de contrôle d'authenticité(Message Authentication Code, MAC dans la langue de Benny Hill), mais d'offrir un simple aperçu.
ExifTool nous donne l'offset du tag dans le fichier image :
$ exiftool.exe -H -U -OriginalDecisionDataOffset 20d/IMG_0102_20d.CR2
0x0083 Original Decision Data Offset : 7259830
Voici donc les données stockées à l'offset 7259830, (0x6ec6b6 en hexadécimal), quelque peu mises en forme. Les lignes sont numérotées pour faciliter le commentaire. Se reporter à l'encart pour appréhender le format TIFF/CR2 et comment localiser les données ODD dans un fichier image issu du 20D.
1:ffffffff 02000000
2:803f025b 99f65111 cc76f6fe c5180bc8 8fe0a342
3:04000000
4:00000000 ca340900 ec916500 f2980ecd fb706dad 20e04cb2 1620374c 061f2325
5:01000000 00000000 6e000000 4925704c a36ccdef 55a6f7ac 3c1038ca 5ff91ada
6:02000000 72000000 08030000 de69a0d4 b6a2fb99 937599cc 82d30d21 284b975d
7:03000000 7e030000 4c310900 4c428724 cbf0810c e516462f a06aa0a2 8b1e9527
Les lignes 2, 4, 5, 6 et 7 semblent contenir chacune une empreinte SHA1 (les 20 derniers octets, à grande entropie), ou plutôt des données de contrôle d'authenticité HMAC-SHA1 [7] puisque le rôle de ces données est d'être capable d'authentifier les données images. Les 4 dernières lignes sont au même format, commençant par un entier sur 32 bits (long) au format Intel (http://en.wikipedia.org/wiki/Endianness), sans doute le numéro de la « section ». La ligne 3 dénombre ces enregistrements identiques, toujours en Little Endian.
Revenons au format TIFF : l'offset des données de l'image principale et leur taille sont données par :
exiftool.exe -v 20d/IMG_0102_20d.CR2
[...]
+ [IFD3 directory with 5 entries]
| 0) Compression = 6
| 1) StripOffsets = 603338
| 2) StripByteCounts = 6656492
[…]
respectivement 603338 (0x934ca) et 6656492 (0x6591ec). On retrouve (au format Intel) ces 2 valeurs dans le Tag ODD à la ligne 4, colonnes 2 et 3. La colonne 2 contient donc l'offset vers l'image principale et la 3e colonne la taille de cette « section ». Enfin, on peut penser que les supposés HMAC-SHA1 sont calculés à chaque fois à partir de l'offset (colonne 2) et pour la taille indiquée en colonne 3.
Pourquoi donc avoir besoin de plusieurs HMAC au lieu d'un unique code d'authenticité pour le fichier ? C'est pour authentifier également les métadonnées. Même si le tag est à la fin du fichier (0x934ca + 0x6591ec = 0x6ec6b6), il faut respecter le format TIFF et écrire l'offset vers les données du tag ODD dans le répertoire TIFF, précisément entre les offsets 0x37a (ligne 6, 0x308+0x72) et 0x37e (ligne 7). La dernière zone exclue du calcul de la signature correspond à la valeur du tag « Orientation » (paysage ou portrait), que l'utilisateur peut modifier ultérieurement à l'enregistrement de l'image sur la carte mémoire.
En résumé, une empreinte garantissant l’intégrité est calculée sur chacune des 4 régions du fichier TIFF. La ligne 2 ne décrivant pas de région, elle concerne certainement le fichier dans son ensemble, c'est-à-dire, on peut le supposer, en agrégeant les quatre HMAC-SHA1 des lignes 4 à 7. Tout ceci est illustré dans la figure 1.
Il n'est malheureusement pas possible de comprendre la totalité de l'algorithme sans analyser le logiciel interne (firmware) de l'appareil photo. C'est ce qu'a fait Dmitry Sklyarov : reportez-vous à sa présentation [1] pour les détails, à l'exception toutefois des clés HMAC. Pour la version 2 du tag ODD, une clé unique est utilisée pour tous les boîtiers d'un même modèle. Ceci n'est plus vrai pour la version 3 du tag : chaque clé HMAC est unique par boîtier. J'ai pu reproduire les résultats décrits dans cette première partie et dans [1], en disposant de la clé utilisée par les boîtiers 20D, voir les valeurs calculées qui sont en bleu dans la figure 1.
Voyons donc dans la suite de cet article comment retrouver l'algorithme complet pour la version 3 du tag ODD, et surtout récupérer cette clé secrète et unique par boîtier, comme avec le 60D.
3. Exécutons, traçons du code sur notre appareil photo
Depuis 2009, grâce aux bidouilleurs du forum CHDK [8] et autour du firmware alternatif Magic Lantern [9], il est possible d’exécuter du code sur les reflex numériques Canon. On peut « sauvegarder » la mémoire vive et non volatile (Flash) sur la carte mémoire. Le processeur interne est le DIGIC, incluant un cœur ARM. Le système d'exploitation temps-réel de Canon s'appelle DryOS [10].
L'analyse statique du firmware avec FindCrypt [11] ou SignSrch [12] révèle entre autres des constantes liées aux implémentations SHA1, HMAC-SHA1, MD5 et SHA256. Les suppositions faites précédemment se renforcent...
Des fonctions de débogage sont même activables [13]. Ci-dessous, nous pouvons voir un extrait du journal de démarrage du 60D :
Sat Feb 19 20:25:15 2011
0: 11.679 [STARTUP]
K287 ICU Firmware Version 1.0.8 ( 3.3.1 )
1: 11.741 [STARTUP]
ICU Release DateTime 2010.11.08 08:40:51
[…]
4: 13.402 [PTPCOM] Magic Lantern
5: 13.420 [PTPCOM] Built on 2010-12-23 20:54:26 by user@ubuntu1004desktop
avec des traces pour les fonctions « MAC », pour Message Authentification Code :
794: 719.845 [MAC] MAC_Initialize
[…]
799: 720.641 [MAC] Key=0x4 Board=0x820fd801 Body=0x22970ba2
Les trois valeurs en ligne 799 sont respectivement KeyId, BoardId et BodyId, nous y reviendrons. Cette dernière valeur est également appelée SerialNumber par ExifTool, et elle est imprimée sous le boîtier de l'appareil.
Enfin, lorsqu'une photo est prise avec l'option « Original Decision Data » activée via le menu :
8660: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x60000, 0x10000000 OffLen:0xb566,
8661: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x90000, 0x0 OffLen:0xe39b, 0x1)
8662: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x70000, 0x20000000 OffLen:0xe39e,
8663: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x90000, 0x0 OffLen:0x1b61e9, 0x3)
8664: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x80000, 0x1000000 OffLen:0x1b61ec
8665: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x10000, 0x10000 OffLen:0x4e63fa,
8666: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x30000, 0x0 OffLen:0x6e, 0x4)
8667: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x40000, 0x0 OffLen:0xb2f6, 0x108)
8668: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x50000, 0x0 OffLen:0x5c0, 0x4)
8669: [MAC] macSetArea(Ref:0xffffffff, 0x0 Set:0x0, 0x0 OffLen:0xcc8, 0x260)
dans le log apparaît un calcul de digest sur 32*8 = 256 bits. Il s'agit peut être d'un calcul SHA256, une amélioration de SHA1, dont les constantes ont été détectées dans le firmware et qui produit une valeur sur 256 bits.
8686: 34693.045 [ENGP] [HASH] KaiserCompleteCBR
8687: 34693.085 [ENGP] [HASH] CrawReadCompleteCBR
8688: 34693.162 [ENGP] [HASH] digestA 0xa48a690b
8689: 34693.187 [ENGP] [HASH] digestB 0x8b3753f5
8690: 34693.210 [ENGP] [HASH] digestC 0xfe6bb8c2
8691: 34693.227 [ENGP] [HASH] digestD 0x18a08b9
8692: 34693.249 [ENGP] [HASH] digestE 0x20e4919c
8693: 34693.271 [ENGP] [HASH] digestF 0x26c42332
8694: 34693.293 [ENGP] [HASH] digestG 0x2e0440aa
8695: 34693.315 [ENGP] [HASH] digestH 0x606beefc
En analysant les chaînes de caractères du firmware, on trouve également ceci (la première colonne est l'offset dans le firmware) :
91604 MAC_SetArea RAW [THM] [%#x]
91644 MAC_SetArea RAW [THM Padding] [%#x]
91668 MAC_SetArea : RAW [DISP] [%#x]
91aa4 MAC_SetArea : RAW [TWAIN] [%#x]
91aec MAC_SetArea : RAW [MAIN] [%#x]
91b30 MAC_SetArea : RAW [ORIENT] [%#x]
91b58 MAC_SetArea : RAW [USER] [%#x]
91b78 MAC_SetArea : RAW [FLASH] [%#x]
91b9c MAC_SetArea : RAW [MAKER] [%#x]
4. Reconstitution des données du tag ODD, version 3
Comme précédemment, observons le contenu du tag ODD (version 3), produit par notre 60D :
01: ffffffff 03000000
02: 14000000 fd86c9ed c422e369 14a68036 ba9825e8 40064c9b
03: 14000000 ac0fd27d 41e00cbb 76800050 0231e8df c9b6c6cf
04: 28020000 04000000 089b5a74 03000000 991dce01 03000000 04000000 01d80f82 rand=320ef068
05: 08000000
06: [01000000 04000000 *6881ee0b
07: 14000000 63569076 b9283b0e 64c5a7a7 042c1f42 871d0fc9 01000000 fa634e00 9db97f01]
08: [02000000 04000000 *0dfc84d5
09: 14000000 026db83c b6181d6b bbf77ef9 5002282b 4d26ef11
10: 09000000
11: 00000000 6e000000, 72000000 c6050000, 3c060000 04070000
12: a00f0000 cca40000, 74b50000 02220000, e80d0100 0e000000
13: f6e33300 02000000, f8634e00 02000000, 971dce01 02000000]
14: [03000000 04000000 *6b252675
15: 14000000 23a93a3a a7d3a8bb e3753ecf bc571166 e196ba0e 01000000 6e000000 04000000]
16: [04000000 04000000 *e394364d
17: 14000000 0a245bdd 4c366931 cb08e16e da749e4d dc1ea6a0 01000000 6cb40000 08010000]
18: [05000000 04000000 *fef182b5
19: 14000000 03a013ab 309475c2 10d2aa9f 0a3d5641 209f2ee3 01000000 38060000 04000000]
20: [06000000 04000000 *f4fb870e
21: 14000000 aa374cb9 8d24bbae 9e004fa4 62ded429 7a6f5e63 01000000 76d70000 72360000]
22: [07000000 04000000 *fba7fe65
23: 14000000 954a9ee5 009dcfe9 e8d1872d 514baf05 fb2e6491 01000000 f60d0100 00d63200]
24: [08000000 04000000 *51c5f451
25: 14000000 cbd2fef0 fbd60871 e31c9812 e2ba5b71 308b357c 01000000 f8e33300 00801a00]
[...]
Il ne faut pas chercher dans un premier temps à tout comprendre. Nous retrouvons cette fois-ci 8 régions (entre crochets) avec deux différences : une valeur qui semble aléatoire sur 32 bits (précédée d'un * pour la lecture) et la région est définie (offset, puis taille) sur les deux dernières colonnes. Autres différences, les lignes 2 et 3 possèdent chacune un HMAC-SHA1 sans région ni aléa, enfin, la région 2 semble découpée en neuf sous-parties et utilise un seul aléa.
ExifTool nous indique que l'image principale est à l'offset 5137400 (0x4e63f8) et d'une taille de 25147809 (0x17fb9a1). Cela est cohérent avec les deux dernières valeurs de la région 1 (la différence de 4 octets est à négliger).
Sans rentrer dans les détails, les huit régions sont appelées Area dans le firmware et calculées par la fonction MAC_SetArea(), se trouvant à partir de l'offset 0x91600 dans le firmware. Nous avons vu précédemment les messages générés dans le journal par cette fonction. Voici maintenant l'algorithme à appliquer.
5. L'algorithme de signature, pour les boîtiers récents
L'algorithme suivant est utilisé pour la version 3 du tag et pour un sous-type vhash>1. Pour le 60D, vhash=3, il s'agit de la sixième valeur, ligne 4. Curieusement, l’aléa associé à chaque région n'est pas utilisé ici.
file_sha256 = sha256.new()
Pour chaque région:
Si region2:
region2_sha256 = sha256.new()
pour chaque sous-partie:
region2_sha256 = sha256.update( region )
sinon:
region_sha256 = sha256.new( region ).digest()
signature = hmac-sha1(region_sha256.digest(), hmac_key) //lignes 7,9,15,17,19,21,23,25
file_sha256 = sha256.update(region_sha256) // sha256 accumulation
odd_sha256 = sha256(tag_odd).digest()
odd_sign = hmac-sha1(odd_sha256, hmac_key) // hmac de la ligne 3
file_sha256 = sha256.update(odd_sha256)
file_sign = hmac-sha1(file_sha256.digest(), hmac_key) // hmac de la ligne 2
En résumé, un SHA256 puis HMAC-SHA1 sur chaque région et le contenu du tag ODD (en ignorant les 56 premiers octets, sinon il y a un problème de poule et d’œuf). Enfin un HMAC-SHA1 sur l'accumulation des SHA256 calculés aux étapes précédentes.
Comme il faut pouvoir calculer le tag ODD de chaque image, à raison de 6,3 d'entre elles par seconde, le calcul du SHA256 est effectué par accélération matérielle, nommée « Kaiser », et intégré au Digic. Pour le boîtier 450D, l'aléa par région est bien utilisé (sous-type vhash=1), et l'algorithme est basé sur MD5 au lieu de SHA256, mais je n'en connais pas les détails. L'algorithme ci-dessus a été vérifié également sur un 7D (vhash=3) et un 50D (vhash=2).
6. Calcul de hmac_key
Cette clé est calculée comme suit, dans la fonction ____MAC_Start() :
v1 = IKBoardID | BodyId | Hmac_rand
Soit la concaténation de 3 valeurs :
- IKBoardID, une clé unique au boîtier de 32 octets, générée ou injectée en usine.
- Un identifiant public du boîtier (BodyId), ici 0x22970ba2. Nous l'avons aperçu dans le log système, et il est disponible dans le tag TIFF 0x000c du MakerNote (appelé SerialNumber par ExifTool).
- Et l'aléa hmac_rand, dans notre exemple, la valeur est 0x68f00e32, ligne 4 et générée par la fonction Rand() de la bibliothèque C, appelée 2 fois successivement sur 16 bits.
Hmac_key = sha1sums(v1)
avec
def sha1sums(m):
sh = sha1()
sh.update(m)
sh.update('\x01')
r1 = sh.digest()
sh = sha1()
sh.update(m)
sh.update('\x02')
r2 = sh.digest()
return r1+r2[0:12] // les 8 derniers octets de r2 sont ignorés
Il y a deux manières de récupérer la valeur unique IKBoardID, en mémoire vive ou via une version stockée de manière persistante.
Au passage, le slide 23 de M. Sklyarov [1] intervertit BodyID et Hmac_rand, ce qui est incorrect.
7. Récupération du secret boîtier IKBoardId
7.1 En mémoire vive
La fonction macdispinf(), pour « MAC display info », utilise une structure qui contient des valeurs intéressantes :
LDR R5, =0x5AE8 ; 60D version 1.0.8
LDR R4, [R0]
LDR R0, [R5,#8] ; R0 = adresse de la structure
LDR R1, [R0,#8] ; BoardID
ADR R0, aBoardid0x08xD ; "BoardID=0x%08x(%d)\n"
MOV R2, R1
BL printf
...
ADR R0, aIkboardid ; "IKBoardID ="
BL printf
MOV R4, #0
loop LDR R0, [R5,#8]
ADD R0, R0, R4
LDRB R1, [R0,#0x2C] ;boucle affichant 32 octets depuis l'offset 0x2c
ADR R0, a02x_0 ; "%02x "
BL printf
ADD R4, R4, #1
CMP R4, #0x20
BCC loop
ADR R0, asc_FF1A5BC8 ; "\n"
BL printf_maybe
À l'offset 8 se trouve BoardID et à l'offset 0x2c (44 en décimal) notre IKBoardID sur 32 octets. Canon a dû estimer que le risque de récupération en mémoire vive était faible, et celui en mémoire persistante plus probable, d’où une meilleure protection, que nous allons détailler.
7.2 Stockée dans la mémoire flash
Les « propriétés » de DryOS sont des structures nommées, permettant de stocker en mémoire et sur la mémoire flash (ici, à l'adresse 0xff800000) la plupart des données applicatives, comme la configuration de l'appareil. On retrouve BoardID via la propriété 0x1060002 (stockée en Little Endian), et FKBoardID dispersée dans les propriétés 0x01060004, 0x01060000, 0x0100000B et 0x10000008.
FKBoardID (F pour Forward) est une version obfusquée de IKBoardID (I pour Inverse). Il faut appliquer la fonction CRP_IObfunc() du firmware pour retrouver notre valeur en clair : en clair il faut inverser obfuscation.
$ python grep_odd.py 60d/FF800000.BIN
0x01000006, 4 = 0x2d0078, a20b9722 (BodyID)
0x01000008, 16 = 0x2d0030, f5d7799d9a35575e6dc479294bdc3f79 (FKBoardID_16)
0x0100000b, 8 = 0x2d00ac, 17da42cdaffbefcb (FKBoardID_8)
0x01060000, 4 = 0x52f6cc, 62bcf06c (FKBoardID_4)
0x01060001, 4 = 0x52f6d8, 04000000 (KeyID)
0x01060002, 4 = 0x52f6e4, 01d80f82 (BoardID)
0x01060004, 4 = 0x52f718, 4f492fe6 (FKBoardID_0)
FKBoardID=4f492fe662bcf06c17da42cdaffbefcbf5d7799d9a35575e6dc479294bdc3f79
$ python iobfunc.py 4f492fe662bcf06c17da42cdaffbefcbf5d7799d9a35575e6dc479294bdc3f79
ce5969180f38bbab2a28f08b44b536dc407cd9ee54843226505ec35a[820fd801]
Le script grep_odd.py parcourt la zone de la mémoire flash où sont stockées les propriétés, à la recherche des propriétés qui nous intéressent. Le format de stockage d'une propriété en mémoire est : property_length+8 (32 bits), property_id (32 bits), property_value. Tout ceci est toujours stocké au format Intel.
En sortie, la première colonne est l'identifiant de la propriété, le 2e sa longueur, l'offset dans le dump, et enfin, sa valeur, octet par octet. On peut noter que l'on retrouve la valeur BoardID dans les 4 derniers octets de IKBoardID, ce qui confirme que cette dernière est la version en clair de FKBoardID.
8. Démo
Avec IKBoardID = 'ce5969180f38bbab2a28f08b44b536dc407cd9ee54843226505ec35a820fd801', nous pouvons calculer la clé hmac_key, et finalement vérifier l'authenticité du fichier image, via le script ci-dessous. La totalité de la sortie du script est reproduite ci-dessous afin d'expliciter la structure du tag, de récapituler les calculs intermédiaires pour reconstituer les données d'authentification, et enfin, d'afficher des valeurs test, permettant au lecteur qui le souhaite de reproduire l’opération. Ce script sera mis à disposition [15].
$ python odd_verif.py IMG_0006.CR2
hmac_key=1493d665407600fa692d05b06cee9df3ee8e2e3f9faa3685c65eac2ca9a0df29
0xffffffff , version = 0x00000003
0x00000014 fd86c9edc422e36914a68036ba9825e840064c9b (file hmac)
0x00000014 ac0fd27d41e00cbb768000500231e8dfc9b6c6cf (ODD hmac)
tag len=0x00000228 4=0x00000004 rand=0x745a9b08 3=0x00000003 filesize=0x01ce1d99
vhash=0x00000003 keyid=0x00000004 boardid=0x820fd801 hmac_rand=0x68f00e32
n_area = 8
1 4 salt=0x0bee8168 0x14 63569076b9283b0e64c5a7a7042c1f42871d0fc9 1
0x004e63fa 0x017fb99d
sha256=f498ae4693f7518ef6bf9350f1723278ac65dceb57726200dfa03f274a501462
hmac=63569076b9283b0e64c5a7a7042c1f42871d0fc9
2 4 0xd584fc0d 0x14 026db83cb6181d6bbbf77ef95002282b4d26ef11 n_other = 9
0x00000000 0x0000006e, 0x00000072 0x000005c6, 0x0000063c 0x00000704,
0x00000fa0 0x0000a4cc, 0x0000b574 0x00002202, 0x00010de8 0x0000000e,
0x0033e3f6 0x00000002, 0x004e63f8 0x00000002, 0x01ce1d97 0x00000002,
sha256=fb1a01710223fd236dc20db2296dda063dc8900748a8b9e8f28df8e2827bb586
hmac=026db83cb6181d6bbbf77ef95002282b4d26ef11
3 4 salt=0x7526256b 0x14 23a93a3aa7d3a8bbe3753ecfbc571166e196ba0e 1
0x0000006e 0x00000004
sha256=67abdd721024f0ff4e0b3f4c2fc13bc5bad42d0b7851d456d88d203d15aaa450
hmac=23a93a3aa7d3a8bbe3753ecfbc571166e196ba0e
4 4 salt=0x4d3694e3 0x14 0a245bdd4c366931cb08e16eda749e4ddc1ea6a0 1
0x0000b46c 0x00000108
sha256=44b8aa4d28701168922acf61435ea4bb442f97b0b14ad7a2510ed68874ee2a72
hmac=0a245bdd4c366931cb08e16eda749e4ddc1ea6a0
5 4 salt=0xb582f1fe 0x14 03a013ab309475c210d2aa9f0a3d5641209f2ee3 1
0x00000638 0x00000004
sha256=df3f619804a92fdb4057192dc43dd748ea778adc52bc498ce80524c014b81119
hmac=03a013ab309475c210d2aa9f0a3d5641209f2ee3
6 4 salt=0x0e87fbf4 0x14 aa374cb98d24bbae9e004fa462ded4297a6f5e63 1
0x0000d776 0x00003672
sha256=5e7756ad6be5f2c06555d7ce5721b15803da965bfa68dcc8f47616be8ec67456
hmac=aa374cb98d24bbae9e004fa462ded4297a6f5e63
7 4 salt=0x65fea7fb 0x14 954a9ee5009dcfe9e8d1872d514baf05fb2e6491 1
0x00010df6 0x0032d600
sha256=4cdc2e2f1d7b06aab6d8b1392d8c2d0c536cce59e42400b21628b301d5f17a1a
hmac=954a9ee5009dcfe9e8d1872d514baf05fb2e6491
8 4 salt=0x51f4c551 0x14 cbd2fef0fbd60871e31c9812e2ba5b71308b357c 1
0x0033e3f8 0x001a8000
sha256=9cc97295fc75896feb377b29637fa297a2cdce5045078cb67830de10192dba13
hmac=cbd2fef0fbd60871e31c9812e2ba5b71308b357c
ODD size = 0x228, offset = 0xd78
sha256=b5359104639139647ea152c648f04046795d62f3362d33dd0c5e2a8090f9d4a9
hmac=ac0fd27d41e00cbb768000500231e8dfc9b6c6cf
file hmac= fd86c9edc422e36914a68036ba9825e840064c9b ok
La valeur file_hmac calculée, à la dernière ligne, est bien identique au HMAC inclus par le firmware dans le tag ODD, autrement dit : le fichier est considéré comme authentique. On peut remarquer également que la valeur est disponible en clair dans l'en-tête des données ODD.
La figure 2 illustre l'organisation des régions et leur contenu. Le lecteur voulant comprendre l'intégralité du format du tag ODD en version 3 trouvera son bonheur dans les planches 17 à 21 de [1].
Pour information, des brevets Canon [14] décrivent comment créer des données de vérification d’authenticité d'une image et comment générer une clé secrète, la stocker sur un média « tamper resistant » : il s'agit sans doute de la carte Secure Mobile nécessaire au chiffrement des images.
Le format TIFF/CR2
Le format de fichier TIFF (pour Tagged Image File Format), dans sa déclinaison CR2 pour Canon, contient 4 sections, chacune comportant un répertoire de tags (Image File Directory, IFD), une image et des métadonnées. Les tags décrivent par exemple les dimensions de l’image, le modèle du boîtier, le nom du photographe. La plupart des tags sont officiellement documentés, sauf ceux présents dans le sous-répertoire MakerNotes (notes constructeur). Certains tags comme le tag EXIF pointent sur un sous-répertoire (SubIFD), ou un bloc de données lorsque sa taille est supérieure à 4 octets. Pour atteindre les données ODD, il faut donc parcourir l’IFD#0, l’IFD Exif, l’IFD MakerNotes, et enfin, trouver le tag 0x0083, qui stocke l’offset vers ces données.
9. Discussion
Nous sommes donc capables, pour la version 3 du tag, lorsque l'on dispose de IKBoardID, de vérifier l'authenticité du fichier image par rapport au contenu du tag ODDv3. Mais est-il facile de falsifier cette vérification : produire un tag valide pour une image modifiée ?
Usurper le boîtier d'origine d'une image reste, pour la même raison, difficile : comme l'algorithme pour générer le secret IKBoardID en fonction de valeurs publiques comme BoardID et KeyID est inconnu, il faut être capable d'extraire cette valeur secrèteet unique. Cela nécessite un accès physique au boîtier visé.
Par contre, forger un tag valide est plus simple, on peut en effet réutiliser le secret IKBoardIDextrait d'un boîtier,ainsi que ses valeurs liées BoardID et KeyID. C'est d'ailleurs ce que l'on peut observer pour quatre images modifiées par ElcomSoft : Sakura.jpg, Liberty.jpg, Stalin.jpg et Fuji.jpg, provenant selon les métadonnées (forgées) de quatre boîtiers différents. Les mêmes valeurs BoardID=0x82e933a7 et KeyID=1 sont utilisées à chaque fois, donc un même secret IKBoardIDa certainement été réutilisé pour ces quatre images : c'est une attaque de type « replay ». On peut imaginer alors black-lister ces valeurs, mais selon quels critères ? Et pour la version 2 du tag, il faut révoquer la clé utilisée pour tous les boîtiers d'un même modèle, la fonctionnalité est ici condamnée.
Conclusion
Dans cet article, nous avons vu comment sont produites des données permettant de vérifier l'authenticité des données d'un fichier image (le tag ODD donc) via une combinaison de primitives cryptographiques standards : SHA256 et HMAC-SHA1, ici pour le modèle 60D. Les hypothèses faites par le constructeur étaient que la clé IKBoardID resterait secrète, puisque présente uniquement en mémoire, et que l’algorithme de signature est inconnu.
Ceci n'est plus vrai. Canon n'a sans doute pas utilisé le « attack resistant device » pour protéger la clé IKBoardID pour des raisons de coût.
Nous avons également constaté qu'avant de se précipiter sur un désassembleur, on peut faire beaucoup d'hypothèses justes par observation de la structure des données du tag ODD et via les chaînes de caractères contenues dans le firmware.
Est-il finalement envisageable de résoudre la dernière énigme dont nous parlions dans le chapitre précédent : comment le kit de vérification de Canon [6] calcule-t-il ou vérifie-t-il la valeur IKBoardID de l'appareil par rapport à ses valeurs « publiques » KeyID et BoardID ? Lors de la vérification des images, il est nécessaire de connecter la carte Secure Mobile au PC [16] où sont stockés les fichiers images. L'appareil photo n'étant pas connecté au système, le kit OSK-E3 utilise uniquement des données provenant des images. Dans le meilleur des cas pour Canon, la décision de la vérification est prise par la carte, masquant les secrets utilisés. Le brevet US 7650511 [14] contient peut-être des éléments de réponse, semble-t-il à base de chiffrement. Peut-être un lien avec les fonctions CRP_Crypto3DesWrap du firmware ?
Remerciements
Merci au projet Magic Lantern d'offrir une plate-forme d’expérimentation inattendue. Je tiens à remercier également Dmitry Sklyarov pour m'avoir désigné la dernière pièce du puzzle (c'est-à-dire où se trouve hmac_rand) et pour la clé du 20D. Merci enfin au rédacteur en chef et aux deux relecteurs pour leurs conseils.
Bibliographie
[1] Forging Canon Original Decision Data , Dmitry Sklyarov, Confidence 2.0 conference, Prague, 2010, http://201002.confidence.org.pl/prelegenci/dmitry-sklyarov
[2] Service Notice: Information Regarding Original Data Security and Original Data Verification Kits, http://www.usa.canon.com/cusa/support/consumer?pageKeyCode=prdAdvDetail&docId=0901e0248027abcd
[3] Dcraw, Dave Coffin, http://www.cybercom.net/~dcoffin/dcraw/
[4] ExifTool, Phil Harvey, http://owl.phy.queensu.ca/~phil/exiftool/TagNames/
[5] Inside the Canon RAW format version 2, http://lclevy.free.fr/cr2/
[6] Image verification : http://cpn.canon-europe.com/content/education/infobank/image_verification/canon_data_verification_system.do
[7] HMAC-SHA1, http://tools.ietf.org/html/rfc2202
[8] Canon Hack Dev Kit, http://chdk.setepontos.com/index.php?board=33.0
[9] Magic Lantern firmware, http://magiclantern.wikia.com/wiki/Unified
[10] DryOS, http://www.canon.com/technology/canon_tech/explanation/cp_tech.html#dryos
[11] FindCrypt. Ilfak Guilfanov, http://www.hexblog.com/?p=28
[12] Signsrch . Luigi Auriemma, http://aluigi.altervista.org/mytoolz.htm
[13] http://magiclantern.wikia.com/wiki/Debugging_Magic_Lantern
[14] US patent 7650511. voir aussi : US patent 7930544, US patent 7949124, US patent 8005213.
[15] http://lclevy.free.fr/cr2/odd
[16] OSK-E3, verification steps, http://www.canon.co.jp/imaging/osk/osk-e3/verifies/method/index.html