Ubuntu Weekly Recipe

第444回 Ubuntuにおけるセキュアブートの仕組み

この記事を読むのに必要な時間:およそ 11 分

実際に署名を検証してみる

最後に,ここまで取り上げてきたパーツを組み合わせて,手作業で署名を検証する手順を紹介します。

db変数から鍵を取り出す

まずdb変数から鍵リストを取り出し,分解してみます。

$ mkdir tmpsb && cd $_
$ cp /sys/firmware/efi/efivars/db-* db
$ hd db | head -n 4
00000000  27 00 00 00 a1 59 c0 a5  e4 94 a7 4a 87 b5 ab 15  |'....Y.....J....|
00000010  5c 2b f0 72 a8 05 00 00  00 00 00 00 8c 05 00 00  |\+.r............|
00000020  31 6b a9 f5 a0 db aa 4f  a4 2a 7a 0c 98 32 76 8e  |1k.....O.*z..2v.|
00000030  30 82 05 78 30 82 04 60  a0 03 02 01 02 02 10 56  |0..x0..`.......V|

db変数は複数の鍵リストを束ねたものとして保存されます。鍵リストは次のようなフォーマットになっています。

typedef struct _EFI_SIGNATURE_DATA {
    EFI_GUID            SignatureOwner;
    UINT8               SignatureData[...];
} EFI_SIGNATURE_DATA;

typedef struct _EFI_SIGNATURE_LIST {
    EFI_GUID            SignatureType;
    UINT32              SignatureListSize;
    UINT32              SignatureHeaderSize;
    UINT32              SignatureSize;
    UINT8               SignatureHeader[SignatureHeaderSize];
    EFI_SIGNATURE_DATA  Signatures[...][SignatureSize];
} EFI_SIGNATURE_LIST;

最初の4バイトは前述したとおり変数の属性です。以降の鍵リストの最初の部分を上記にしたがって解析してみましょう。

SignatureType       = EFI_CERT_X509_GUID(a5c059a1-94e4-4aa7-87b5ab155c2bf072)
SignatureListSize   = 0x5a8
SignatureHeaderSize = 0x0
SignatureSize       = 0x58c
SignatureHeader     = (なし)
SignatureOwner      = f5a96b31-dba0-4faa-a42a7a0c9832768e
SginatureData       = [0x30 .. (0x30 + 0x58c - 0x10)] = [0x30 .. 0x5ac]

SignatureSizeには16バイトのSignatureOwnerが含まれているため,SignatureDataはその分を引いています。では,最初の鍵リストの末尾部分を見てみましょう。

$ hd -s 0x5a0 db | head -n 4
000005a0  eb c9 36 54 97 a7 54 61  0c 34 60 40 a1 59 c0 a5  |..6T..Ta.4`@.Y..|
000005b0  e4 94 a7 4a 87 b5 ab 15  5c 2b f0 72 07 06 00 00  |...J....\+.r....|
000005c0  00 00 00 00 eb 05 00 00  bd 9a fa 77 59 03 32 4d  |...........wY.2M|
000005d0  bd 60 28 f4 e7 8f 78 4b  30 82 05 d7 30 82 03 bf  |.`(...xK0...0...|

0x5acあたりから再びEFI_SIGNATURE_LISTらしきものが始まっています。よって再び解析してみます。

SignatureType       = EFI_CERT_X509_GUID(a5c059a1-94e4-4aa7-87b5ab155c2bf072)
SignatureListSize   = 0x607
SignatureHeaderSize = 0x0
SignatureSize       = 0x5eb
SignatureHeader     = (なし)
SignatureOwner      = 77fa9abd-0359-4d32-bd6028f4e78f784b
SginatureData       = [0x5d8 .. (0x5d8 + 0x5eb - 0x10)] = [0x5d8 .. 0xbb3]
$ hd -s 0xbb0 db | head -n 4
00000bb0  1c 59 7e a1 59 c0 a5 e4  94 a7 4a 87 b5 ab 15 5c  |.Y~.Y.....J....\|
00000bc0  2b f0 72 40 06 00 00 00  00 00 00 24 06 00 00 bd  |+.r@.......$....|
00000bd0  9a fa 77 59 03 32 4d bd  60 28 f4 e7 8f 78 4b 30  |..wY.2M.`(...xK0|
00000be0  82 06 10 30 82 03 f8 a0  03 02 01 02 02 0a 61 08  |...0..........a.|

さらにEFI_SIGNATURE_LISTらしきものが始まっているようです。

SignatureType       = EFI_CERT_X509_GUID(a5c059a1-94e4-4aa7-87b5ab155c2bf072)
SignatureListSize   = 0x640
SignatureHeaderSize = 0x0
SignatureSize       = 0x624
SignatureHeader     = (なし)
SignatureOwner      = 77fa9abd-0359-4d32-bd6028f4e78f784b
SginatureData       = [0xbdf .. (0xbdf + 0x624 - 0x10)] = [0xbdf .. 0x11f3]

ここまでで,すべての鍵のオフセットがわかりました。ddコマンドで取り出しましょう。

$ dd if=db of=db1.der bs=1 skip=$((0x30)) count=$((0x57c))
$ dd if=db of=db2.der bs=1 skip=$((0x5d8)) count=$((0x5db))
$ dd if=db of=db3.der bs=1 skip=$((0xbdf)) count=$((0x614))

SignatureTypeからいずれの鍵もDERエンコードされたX.509証明書ですので,opensslコマンドを使ってこの証明書の内容を確認できます。

$ openssl x509 -inform der -in db1.der -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            56:74:a7:03:ef:39:09:10:8b:1f:47:53:68:73:6d:6d
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, O=Hewlett-Packard Company, CN=Hewlett-Packard Printing Device Infrastructure CA
        Validity
            Not Before: Aug 23 00:00:00 2013 GMT
            Not After : Aug 23 23:59:59 2033 GMT
        Subject: O=Hewlett-Packard Company, OU=Long Lived CodeSigning Certificate, CN=HP UEFI Secure Boot 2013 DB key
(後略)

$ openssl x509 -inform der -in db2.der -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            61:07:76:56:00:00:00:00:00:08
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Root Certificate Authority 2010
        Validity
            Not Before: Oct 19 18:41:42 2011 GMT
            Not After : Oct 19 18:51:42 2026 GMT
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Windows Production PCA 2011
(後略)

$ openssl x509 -inform der -in db3.der -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number:
            61:08:d3:c4:00:00:00:00:00:04
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation Third Party Marketplace Root
        Validity
            Not Before: Jun 27 21:22:45 2011 GMT
            Not After : Jun 27 21:32:45 2026 GMT
        Subject: C=US, ST=Washington, L=Redmond, O=Microsoft Corporation, CN=Microsoft Corporation UEFI CA 2011
(後略)

MicrosoftのPCA,UEFI CAについては,Microsoftのサイトからダウンロードできるファイルと同じです。

shimバイナリの署名を検証する

今度はdb変数の鍵を使って,UEFIバイナリの署名を検証します。⁠Ubuntuにおける鍵の取り扱い」でも述べたように,shimx64.efiはdb変数にあるMicrosoftのUEFI CAdb3.derを用いて署名を検証します。このEFIバイナリの実体は/usr/lib/shim/shim.efi.signedです。署名の検証は,sbsigntoolパッケージのsbverifyコマンドを使うと便利です。ただsbverifyはPEM形式しか受け付けてくれないのでdb3.derをPEMに変更してから使用します。

$ openssl x509 -inform der -in db3.der -out db3.pem
$ sbverify --cert db3.pem /usr/lib/shim/shim.efi.signed
warning: data remaining[1170360 vs 1289424]: gaps between PE/COFF sections?
Signature verification OK

警告は,PE/COFFのセクション解析時に「署名部分までのオフセット+署名サイズ(1170360⁠⁠」が「バイナリイメージのサイズ(1289424⁠⁠」より小さかったことを示しています。おそらくPE/COFFのいくつかのセクションが読み飛ばされただけだと思いますので,ここでは気にしなくても問題ないでしょう。

GRUBバイナリとLinuxカーネルの署名を検証する

GRUBバイナリとLinuxカーネルは,Canonicalの証明書を用いて署名を検証します。しかしながら,この証明書はPK/KEK/db変数などには保存されておらず,shimバイナリに埋め込まれています。そこで,shimパッケージのソースパッケージをダウンロードして,証明書を取り出します。

$ apt source shim
$ openssl x509 -inform der -in shim-0.8/debian/canonical-uefi-ca.der -text -noout
Certificate:
    Data:
        Version: 3 (0x2)
        Serial Number: 13348991040521802343 (0xb94124a0182c9267)
    Signature Algorithm: sha256WithRSAEncryption
        Issuer: C=GB, ST=Isle of Man, L=Douglas, O=Canonical Ltd., CN=Canonical Ltd. Master Certificate Authority
        Validity
            Not Before: Apr 12 11:12:51 2012 GMT
            Not After : Apr 11 11:12:51 2042 GMT
        Subject: C=GB, ST=Isle of Man, L=Douglas, O=Canonical Ltd., CN=Canonical Ltd. Master Certificate Authority

あとはこれを,PEM形式に変換したうえで検証するだけです。なお,カーネルはバイナリ部分と署名部分が分離しています。そのためsbverifyコマンドの--detachオプションで署名部分を別途指定する必要があります。

$ openssl x509 -inform der -in shim-0.8/debian/canonical-uefi-ca.der -out canonical.pem
$ sbverify --cert canonical.pem /usr/lib/grub/x86_64-efi-signed/grubx64.efi.signed
Signature verification OK
$ sudo sbverify --cert canonical.pem \
  --detach /usr/lib/linux/vmlinuz-`uname -r`.efi.signature /boot/vmlinuz-`uname -r`
Signature verification OK

このようにセキュアブートは,複数の「鍵」を用いて安全性を確保しています。もしUEFIシステムにインストールしたUbuntuがセキュアブートを有効にするとうまく動かない場合は,本記事の情報をもとに必要な鍵(特にMicrosoft UEFI CA)が足りているか,必要なパッケージ(shim-signedやgrub-efi-ARCH-signedなど)がインストールされているか,UEFIのブートオプションでshimx64.efiを指定しているかといった点を確認してみてください。

今週末は「オープンソースカンファレンス2016 Tokyo/Fall」

Ubuntu Weekly Topicsでも改めて告知する予定ですが,今週末の11月5日(土)から6日(日)にかけて,オープンソースカンファレンス2016 Tokyo/Fallが,行政区画上は東京都ということになっている明星大学日野キャンパスで開催されます。

Ubuntu Japanese Teamも両日参加予定で,ブースではいつものようにUbuntu PhoneやUbuntu Tablet,さらにはUbuntuのいずれかのフレーバーをインストールしたRaspberry Pi2などの実機を来場者が自由に触っていただける形で展示します。また土曜日の15時15分からは「サンドボックス型の次世代パッケージフォーマット『snap』入門」と題したセミナーも開催しますので,皆様お誘い合わせの上,お越しください。

セミナーの時間以外は,ブースに誰かが常駐している予定です。Ubuntuに関して質問がある場合はブースに座っている人に気軽にお声がけください。うまく人があいているタイミングをつかめない時は,他の来場者と話しているところに割り込んでもらっても大丈夫です。きっとなんとかします。ちなみにここだけの話,セミナーの資料はまだ完成していません。ようやく今週分のRecipeを提出できたので今から作ります。間に合うかどうかは当日のお楽しみです。

著者プロフィール

柴田充也(しばたみつや)

Ubuntu Japanese Team Member株式会社 創夢所属。数年前にLaunchpad上でStellariumの翻訳をしたことがきっかけで,Ubuntuの翻訳にも関わるようになりました。