To verify the correctness of downloaded ISO images you would usually calculate and compare some checksum (digest), for example SHA256, to a reference value obtained from some website.
To make this process easier, the digest can be embedded into the ISO itself.
While this guards against transmission errors it won’t prevent anyone from manipulating the ISO. To ensure authenticity of the ISO image, the embedded meta data can be signed.
checkmedia
is a tool to verify installation media. Its counterpart
tagmedia
creates and embeds the necessary meta data into the ISO image.
The checking functionality is also provided as shared library libmediacheck.so
.
The tool set supports openSUSE / SLE ('SUSE') and Fedora / RHEL ('RH') - including their derivatives - media.
The digest data tagmedia
creates for RH media is compatible with the implantisomd5
/ checkisomd5
tool set.
Packages for openSUSE and SLES are built at the openSUSE Build Service. You can grab
-
latest stable versions from my ports project
The digest data is stored in the application specific data in the primary volume descriptor of the ISO. This an area of 512 bytes that are reserved by the ISO9660 standard to be used as applications see fit. The area starts at offset 0x8373 of the ISO9660 file system and is usually filled with spaces.
The data is structured roughly as key = value
pairs, separated by semicolons (;
).
Here are some examples:
-
openSUSE
# tagmedia --show openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso check = 1 pad = 150 sha256sum = a722af5e8ff16b07bc38c0971a79f8581016e0d39d3ccc520ac02f6a81f08158 partition = 10432,9135936,c31a435ff7bef3ef3f27575985084060a493227684bd335934d8ca94c85f0a71 signature = 20864
and the corresponding raw data
# tagmedia --export-tags - openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso check=1;pad=150;sha256sum=a722af5e8ff16b07bc38c0971a79f8581016e0d39d3ccc520ac02f6a81f08158;partition=10432,9135936,c31a435ff7bef3ef3f27575985084060a493227684bd335934d8ca94c85f0a71;signature=20864
-
Fedora
# tagmedia --show Fedora-Server-dvd-x86_64-34-1.2.iso ISO MD5SUM = 8e80cc6b4a27cf1f76c8f9e665eb22d5 SKIPSECTORS = 15 RHLISOSTATUS = 1 FRAGMENT SUMS = 1c62fb9659fa3637af7088c71c537657e323935cf12d22de58432182cb58 FRAGMENT COUNT = 20 THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!
and the corresponding raw data
# tagmedia --export-tags - Fedora-Server-dvd-x86_64-34-1.2.iso ISO MD5SUM = 8e80cc6b4a27cf1f76c8f9e665eb22d5;SKIPSECTORS = 15;RHLISOSTATUS=1;FRAGMENT SUMS = 1c62fb9659fa3637af7088c71c537657e323935cf12d22de58432182cb58;FRAGMENT COUNT = 20;THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!
Notes
-
SUSE
-
uses lower-case keys with no spaces around the equal sign (
=
) -
supported digests are MD5, SHA1, SHA224, SHA256, SHA384, and SHA512
-
at least the
<digest>sum
key must be present
-
-
RH
-
uses upper-case, with a single space around the equal sign - except for
RHLISOSTATUS
which must not have spaces around the equal sign -
the only supported digest is MD5
-
at least the
ISO MD5SUM
,SKIPSECTORS
,FRAGMENT SUMS
, andFRAGMENT COUNT
keys must be present
-
-
for
checkmedia
, case does not matter and there can be any number of spaces around the equal sign -
for
checkisomd5
, case does matter and also the acceptable number of spaces is fixed
The digest is calculated over the whole ISO image with some exceptions:
-
0x8373-0x8572 (application specific area) is replaced with an empty application specific area (all spaces)
-
SUSE only
-
0x0000 - 0x01ff (MBR) is replaced by an area with all zeros; this is done to avoid problems with isohybrid images
-
if the
pad
key is set,pad
2 kiB blocks at the end of the ISO are replaced with all zeros -
if a signature is present, the 2 kiB block containing the signature is replaced with an empty signature block (magic string + all zeros)
-
-
RH only
-
SKIPSECTORS
2 kiB blocks at the end of the ISO are skipped
-
In addition to the digest over the whole ISO, SUSE can optionally store a digest over a partition (usually the partition containing the installer). This is useful for checking the medium after copying the image to an USB disk.
The format of the partition
entry is
partition=<startblock>,<blocks>,<digest>
- with a block size of 0.5 kiB.
<digest>
is the same digest type as used for the <digest>sum
entry.
RH stores also a 'progressive' digest in the FRAGMENT SUMS
key. For this,
the image is partitioned into FRAGMENT COUNT
roughly equidistant
fragments and a 'mini digest' is checked for each fragment. This allows to
detect errors early without completing the check of the entire ISO.
The exact algorithm is a bit tricky and is detailed separately below.
Note that the digest over the entire ISO has to be checked in addition since the end of the last fragment does not coincide with the image end.
This section describes how to calculate the digest stored in the FRAGMENT SUMS
field.
-
FRAGMENT SUMS
has a fixed length of 60 chars -
FRAGMENT COUNT
is the number of fragments; the number of fragments must be an integral divisor of 60; for RH, it must be >= 4 (see below) -
FRAGMENT SUMS
is made up ofFRAGMENT COUNT
parts (from left to right) with a length of 60 /FRAGMENT COUNT
each -
calculate (all integer arithmetic, block size is 2 kiB):
FRAGMENT_SUM_SIZE = 60 / FRAGMENT_COUNT TOTAL_BYTES = (ISO_BLOCKS - SKIP_SECTORS) << 11 FRAGMENT = TOTAL_BYTES / (FRAGMENT_COUNT + 1) FRAGMENT_BLOCKS(N) = ((N * FRAGMENT & -0x8000) + 0x8000) >> 11;
-
the N-th fragment starts at block 0 and has a length of FRAGMENT_BLOCKS(N)
-
N runs from 1 to
FRAGMENT COUNT
-
for each fragment calculate the digest, taking into account the specifics described above
-
convert each of the leading FRAGMENT_SUM_SIZE bytes of the binary digest to a single hex digit by choosing the leading char of the hexadecimal representation of the byte; think of
sprintf("%x", digest[i])[0]
-
FRAGMENT_SUM_SIZE
should not be larger than the size of the digest used - else there’s not enough data andFRAGMENT SUMS
will be shorter than 60; this meansFRAGMENT COUNT
should be at least 4 (resulting in aFRAGMENT_SUM_SIZE
of 15) when MD5 (which has a size of 16 bytes) is used
Notes
-
the implementation of all this looks much simpler; it will just read 32 kiB blobs and finalize the digest whenever it crosses into a new fragment
-
the original implementation does the digest calculation before checking the fragment boundary - hence the
+ 0x8000
in the formalized description above as the 32 kiB buffer has already been added at this point
Here’s an example using the Fedora image from above (0.5 kiB blocks):
# tagmedia --digest md5 -v -v Fedora-Server-dvd-x86_64-34-1.2.iso
iso blocks = 4102876
detected rh style
full blocks = 4102816, fragment bytes = 100030561
fragment 1, blocks 195456: md5 1c0c6a5ac5b34ea8df3a552b9c3bf1ef - 1c6
fragment 2, blocks 390848: md5 2df6bd74166e100c5391e0bed3347672 - 2fb
fragment 3, blocks 586240: md5 9466514b11c38db8b06866ec8e0d8ac8 - 965
fragment 4, blocks 781568: md5 97fbaf84ef507cd6a5252fc7a6786265 - 9fa
fragment 5, blocks 976960: md5 386b3236af93ba0dfb5cd0e5b91bb86a - 363
fragment 6, blocks 1172352: md5 74a6f64e4d5ff6ba37339915ebc11b58 - 7af
fragment 7, blocks 1367680: md5 75008cc3eebffc7a0ce27278ebaaf4a1 - 708
fragment 8, blocks 1563072: md5 83c976c7f0c8a5f6a8ff692493d5b292 - 8c7
fragment 9, blocks 1758464: md5 16cb57a24f96b40a71608a441820bbe7 - 1c5
fragment 10, blocks 1953792: md5 3c076f8c23f6d87a643f9317354d91af - 376
fragment 11, blocks 2149184: md5 5f7eee102db6ae4880053c24045b094f - 57e
fragment 12, blocks 2344576: md5 3f22364d872f86976dbb19ee8cf2b94b - 323
fragment 13, blocks 2539904: md5 9e305dfa6b282781d9817749874cdc3d - 935
fragment 14, blocks 2735296: md5 cffa1252febd54126a7e30098e5865ce - cf1
fragment 15, blocks 2930688: md5 26dc02a13d4c514241d1da19ab4abfc6 - 2d2
fragment 16, blocks 3126080: md5 2bd8ef5f7887abd1f2a95da3b4b33d9e - 2de
fragment 17, blocks 3321408: md5 5c8e46a88df449fd8536f0193ad10b59 - 584
fragment 18, blocks 3516800: md5 03271070cbaf72cd7b14bbd2a095ad0a - 321
fragment 19, blocks 3712192: md5 8729ca3dc084ef9702cd5ce2cf209f36 - 82c
fragment 20, blocks 3907520: md5 b35789e21563f2336bbe8dbc315c855a - b58
ISO MD5SUM = 8e80cc6b4a27cf1f76c8f9e665eb22d5
SKIPSECTORS = 15
RHLISOSTATUS = 0
FRAGMENT SUMS = 1c62fb9659fa3637af7088c71c537657e323935cf12d22de58432182cb58
FRAGMENT COUNT = 20
THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!
If the meta data contains a signature
(or SIGNATURE
) key, the value is the starting block
(in 0.5 kiB units) of a 2 kiB area that may contain signing data.
This signature block starts with the magic string
7984fc91-a43f-4e45-bf27-6d3aa08b24cf
followed by 28 zero bytes. After that
may follow an OpenPGP signature in ASCII armor. The signature is calculated
over the 512 bytes of the application specific area.
Here’s an example using the Tumbleweed image from above:
# tagmedia --export-signature - openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso
-----BEGIN PGP SIGNATURE-----
Version: GnuPG v1.0.7 (GNU/Linux)
iQEVAwUAYSQahLiLL9Q9vcKEAQhNawgAoLq1PcxFDXPv7WkODxyxr/tatoSNOkQt
RdnQNOc/0XFBq0gr3WYmyfHbH2tkirYC6egW/J6ro7AnW9XSkTBXHlgLyamYE8tH
fluKi2xw5Qalv3YKD+6fFWvAF0BL9NSDAXvpRClH6tS9DWTVJ7c0W/sMFMlwwEtj
3GAP7jRLRPqwaL7wKyDnoMb4+umTPqlDNqARh2XXwQ7lnahY5UNzQbmmMQ359Tqq
4JTAueBakpdUoCY6BJt2H/HFUqVaVWa8JPetmfKKVI2HGUaiOzYav7JqVcQXgAQz
WdzM7QJMJuIAr/q3zNlwWLus6xU8hUhzfsRDvkr+BoXAaFWwWpeIpg==
=zvYC
-----END PGP SIGNATURE-----
The raw signature block looks like this:
# tagmedia --show openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso | grep -i signature
signature = 20864
# dd if=openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso bs=512 count=4 skip=20864 status=none | hexdump -C
00000000 37 39 38 34 66 63 39 31 2d 61 34 33 66 2d 34 65 |7984fc91-a43f-4e|
00000010 34 35 2d 62 66 32 37 2d 36 64 33 61 61 30 38 62 |45-bf27-6d3aa08b|
00000020 32 34 63 66 0a 00 00 00 00 00 00 00 00 00 00 00 |24cf............|
00000030 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000040 2d 2d 2d 2d 2d 42 45 47 49 4e 20 50 47 50 20 53 |-----BEGIN PGP S|
00000050 49 47 4e 41 54 55 52 45 2d 2d 2d 2d 2d 0a 56 65 |IGNATURE-----.Ve|
00000060 72 73 69 6f 6e 3a 20 47 6e 75 50 47 20 76 31 2e |rsion: GnuPG v1.|
00000070 30 2e 37 20 28 47 4e 55 2f 4c 69 6e 75 78 29 0a |0.7 (GNU/Linux).|
00000080 0a 69 51 45 56 41 77 55 41 59 53 51 61 68 4c 69 |.iQEVAwUAYSQahLi|
00000090 4c 4c 39 51 39 76 63 4b 45 41 51 68 4e 61 77 67 |LL9Q9vcKEAQhNawg|
000000a0 41 6f 4c 71 31 50 63 78 46 44 58 50 76 37 57 6b |AoLq1PcxFDXPv7Wk|
000000b0 4f 44 78 79 78 72 2f 74 61 74 6f 53 4e 4f 6b 51 |ODxyxr/tatoSNOkQ|
000000c0 74 0a 52 64 6e 51 4e 4f 63 2f 30 58 46 42 71 30 |t.RdnQNOc/0XFBq0|
000000d0 67 72 33 57 59 6d 79 66 48 62 48 32 74 6b 69 72 |gr3WYmyfHbH2tkir|
000000e0 59 43 36 65 67 57 2f 4a 36 72 6f 37 41 6e 57 39 |YC6egW/J6ro7AnW9|
000000f0 58 53 6b 54 42 58 48 6c 67 4c 79 61 6d 59 45 38 |XSkTBXHlgLyamYE8|
00000100 74 48 0a 66 6c 75 4b 69 32 78 77 35 51 61 6c 76 |tH.fluKi2xw5Qalv|
00000110 33 59 4b 44 2b 36 66 46 57 76 41 46 30 42 4c 39 |3YKD+6fFWvAF0BL9|
00000120 4e 53 44 41 58 76 70 52 43 6c 48 36 74 53 39 44 |NSDAXvpRClH6tS9D|
00000130 57 54 56 4a 37 63 30 57 2f 73 4d 46 4d 6c 77 77 |WTVJ7c0W/sMFMlww|
00000140 45 74 6a 0a 33 47 41 50 37 6a 52 4c 52 50 71 77 |Etj.3GAP7jRLRPqw|
00000150 61 4c 37 77 4b 79 44 6e 6f 4d 62 34 2b 75 6d 54 |aL7wKyDnoMb4+umT|
00000160 50 71 6c 44 4e 71 41 52 68 32 58 58 77 51 37 6c |PqlDNqARh2XXwQ7l|
00000170 6e 61 68 59 35 55 4e 7a 51 62 6d 6d 4d 51 33 35 |nahY5UNzQbmmMQ35|
00000180 39 54 71 71 0a 34 4a 54 41 75 65 42 61 6b 70 64 |9Tqq.4JTAueBakpd|
00000190 55 6f 43 59 36 42 4a 74 32 48 2f 48 46 55 71 56 |UoCY6BJt2H/HFUqV|
000001a0 61 56 57 61 38 4a 50 65 74 6d 66 4b 4b 56 49 32 |aVWa8JPetmfKKVI2|
000001b0 48 47 55 61 69 4f 7a 59 61 76 37 4a 71 56 63 51 |HGUaiOzYav7JqVcQ|
000001c0 58 67 41 51 7a 0a 57 64 7a 4d 37 51 4a 4d 4a 75 |XgAQz.WdzM7QJMJu|
000001d0 49 41 72 2f 71 33 7a 4e 6c 77 57 4c 75 73 36 78 |IAr/q3zNlwWLus6x|
000001e0 55 38 68 55 68 7a 66 73 52 44 76 6b 72 2b 42 6f |U8hUhzfsRDvkr+Bo|
000001f0 58 41 61 46 57 77 57 70 65 49 70 67 3d 3d 0a 3d |XAaFWwWpeIpg==.=|
00000200 7a 76 59 43 0a 2d 2d 2d 2d 2d 45 4e 44 20 50 47 |zvYC.-----END PG|
00000210 50 20 53 49 47 4e 41 54 55 52 45 2d 2d 2d 2d 2d |P SIGNATURE-----|
00000220 0a 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
00000230 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 00 |................|
*
00000800
Although the standard RH tools (implantisomd5
/ checkisomd5
) do not
support signing the meta data, it is still possible to do it. checkmedia
will verify it and checkisomd5
will accept the image as well. So there is no conflict.
The way to do it is to place the signature block in the skipped area.
tagmedia
will look at the first block in the skipped area when it searches
for a signature block and add a SIGNATURE
key (upper-case to align with
the RH style) to the meta data in this case.
-
SUSE
# checkmedia openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso app: openSUSE-Tumbleweed-DVD-x86_64-Build2850.3-Media iso size: 4572572 kiB pad: 300 kiB partition: start 5216 kiB, size 4567968 kiB checking: 100% result: iso sha256 ok, partition sha256 ok sha256: 90b9fdd5f2332ed6728a6a67f8fe78c0b16af4369637bff0e661c507dd5d69eb signature: ok signed by: openSUSE Project Signing Key <[email protected]>
-
RH
# checkmedia Fedora-Server-dvd-x86_64-34-1.2.iso app: Fedora-S-dvd-x86_64-34 iso size: 2051438 kiB skip: 30 kiB checking: 100% result: iso md5 ok, fragments md5 ok md5: b3c174a7cd4ef9095e46365acba41cbd signature: not signed
Notes
-
the ISO in openSUSE example is signed and the signature was sucessfully verified
-
the ISO in the Fedora example has additionally a fragments checksum
-
SUSE
Let’s take the openSUSE image from above and change the digest to SHA1 and add a RH-like fragment sum:
# tagmedia --clean --digest sha1 --pad 150 --fragments 20 openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso pad = 150 sha1sum = 612df62c3c0c604ae8fe98197c17bff6c0af313e fragment sums = cda2f8ff1721a96b8a1b9725936784b45a13bfa5d86be8d33166b14c5374cec fragment count = 20 partition = 10432,9135936,530bcfb94183501626347de64a67061e2c8ba005 signature = 20864
Verifying the ISO now uses SHA1, but the signature is no longer valid:
# checkmedia openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso app: openSUSE-Tumbleweed-DVD-x86_64-Build2850.3-Media iso size: 4572572 kiB pad: 300 kiB partition: start 5216 kiB, size 4567968 kiB checking: 100% result: iso sha1 ok, partition sha1 ok, fragments sha1 ok sha1: aacd1518d496b938378c95f4ee81ea7d3f82b912 signature: bad
-
RH
tagmedia
also works with the Fedora image; let’s change the number of fragments:# tagmedia --clean --digest md5 --fragments 30 Fedora-Server-dvd-x86_64-34-1.2.iso RHLISOSTATUS = 0 SKIPSECTORS = 15 FRAGMENT SUMS = 769fd46965c792aa2d94e235e9121e4766539c7c532896bef5e2b379afa4 FRAGMENT COUNT = 30 ISO MD5SUM = 8e80cc6b4a27cf1f76c8f9e665eb22d5 THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!
Both
checkmedia
andcheckisomd5
like it:# checkmedia Fedora-Server-dvd-x86_64-34-1.2.iso app: Fedora-S-dvd-x86_64-34 iso size: 2051438 kiB skip: 30 kiB checking: 100% result: iso md5 ok, fragments md5 ok md5: 41cba2ffaacf529e90429bcea4de380b signature: not signed
# checkisomd5 --verbose Fedora-Server-dvd-x86_64-34-1.2.iso Fedora-Server-dvd-x86_64-34-1.2.iso: 8e80cc6b4a27cf1f76c8f9e665eb22d5 Fragment sums: 769fd46965c792aa2d94e235e9121e4766539c7c532896bef5e2b379afa4 Fragment count: 30 Supported ISO: no Press [Esc] to abort check. Checking: 100.0% The media check is complete, the result is: PASS. It is OK to use this media.
Let’s continue with the examples from the last section and sign the media with our own key.
# tagmedia --create-signature Testkey openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso
# checkmedia --key-file openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso.key openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso
app: openSUSE-Tumbleweed-DVD-x86_64-Build2850.3-Media
iso size: 4572572 kiB
pad: 300 kiB
partition: start 5216 kiB, size 4567968 kiB
checking: 100%
result: iso sha1 ok, partition sha1 ok, fragments sha1 ok
sha1: 49143cb1e1bd51b6d3d62e5fe0908b97027bfbbb
signature: ok
signed by: Testkey Signing Key
Note that tagmedia
stored the matching public key part in openSUSE-Tumbleweed-DVD-x86_64-Snapshot20210823-Media.iso.key
.
This also works with the Fedora image:
# tagmedia --create-signature Testkey Fedora-Server-dvd-x86_64-34-1.2.iso
# tagmedia Fedora-Server-dvd-x86_64-34-1.2.iso
RHLISOSTATUS = 0
SKIPSECTORS = 15
FRAGMENT SUMS = 769fd46965c792aa2d94e235e9121e4766539c7c532896bef5e2b379afa4
FRAGMENT COUNT = 30
ISO MD5SUM = 8e80cc6b4a27cf1f76c8f9e665eb22d5
THIS IS NOT THE SAME AS RUNNING MD5SUM ON THIS ISO!!
SIGNATURE = 4102816
You can see that tagmedia
automatically found a place for the signature block and added a link to it to the meta data.
# checkmedia --key-file Fedora-Server-dvd-x86_64-34-1.2.iso.key Fedora-Server-dvd-x86_64-34-1.2.iso
app: Fedora-S-dvd-x86_64-34
iso size: 2051438 kiB
skip: 30 kiB
checking: 100%
result: iso md5 ok, fragments md5 ok
md5: 5d945293a66a6fb8987dc8b077d5143f
signature: ok
signed by: Testkey Signing Key
checkisomd5
also likes it:
# checkisomd5 Fedora-Server-dvd-x86_64-34-1.2.iso
Press [Esc] to abort check.
The media check is complete, the result is: PASS.
It is OK to use this media.
To build, simply run make
. Install with make install
.
Basically every new commit into the master branch of the repository will be auto-submitted to all current SUSE products. No further action is needed except accepting the pull request.
Submissions are managed by a SUSE internal jenkins node in the InstallTools tab.
Each time a new commit is integrated into the master branch of the repository, a new submit request is created to the openSUSE Build Service. The devel project is system:install:head.
*.changes
and version numbers are auto-generated from git commits, you don’t have to worry about this.
The spec file is maintained in the Build Service only. If you need to change it for the master
branch,
submit to the
devel project
in the build service directly.
Development happens exclusively in the master
branch. The branch is used for all current products.
You can find more information about the changes auto-generation and the tools used for jenkis submissions in the linuxrc-devtools documentation.