1 /* keybox-blob.c - KBX Blob handling
2 * Copyright (C) 2000, 2001, 2002, 2003, 2008 Free Software Foundation, Inc.
4 * This file is part of GnuPG.
6 * GnuPG is free software; you can redistribute it and/or modify
7 * it under the terms of the GNU General Public License as published by
8 * the Free Software Foundation; either version 3 of the License, or
9 * (at your option) any later version.
11 * GnuPG is distributed in the hope that it will be useful,
12 * but WITHOUT ANY WARRANTY; without even the implied warranty of
13 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
14 * GNU General Public License for more details.
16 * You should have received a copy of the GNU General Public License
17 * along with this program; if not, see <http://www.gnu.org/licenses/>.
21 /* The keybox data formats
23 The KeyBox uses an augmented OpenPGP/X.509 key format. This makes
24 random access to a keyblock/certificate easier and also gives the
25 opportunity to store additional information (e.g. the fingerprint)
26 along with the key. All integers are stored in network byte order,
27 offsets are counted from the beginning of the Blob.
29 The first record of a plain KBX file has a special format:
31 u32 length of the first record
33 byte version number (1)
39 u32 last_maintenance_run
43 The OpenPGP and X.509 blob are very similiar, things which are
44 X.509 specific are noted like [X.509: xxx]
46 u32 length of this blob (including these 4 bytes)
47 byte Blob type (2) [X509: 3]
48 byte version number of this blob type (1)
50 bit 0 = contains secret key material
51 bit 1 = ephemeral blob (e.g. used while quering external resources)
53 u32 offset to the OpenPGP keyblock or X509 DER encoded certificate
55 u16 number of keys (at least 1!) [X509: always 1]
56 u16 size of additional key information
58 b20 The keys fingerprint
59 (fingerprints are always 20 bytes, MD5 left padded with zeroes)
60 u32 offset to the n-th key's keyID (a keyID is always 8 byte)
61 or 0 if not known which is the case only for X509.
63 bit 0 = qualified signature (not yet implemented}
65 u16 size of serialnumber(may be zero)
66 n u16 (see above) bytes of serial number
67 u16 number of user IDs
68 u16 size of additional user ID information
70 u32 offset to the n-th user ID
71 u32 length of this user ID.
72 u16 special user ID flags.
76 [For X509, the first user ID is the Issuer, the second the Subject
77 and the others are subjectAltNames]
78 u16 number of signatures
79 u16 size of signature information (4)
80 u32 expiration time of signature with some special values:
81 0x00000000 = not checked
82 0x00000001 = missing key
83 0x00000002 = bad signature
84 0x10000000 = valid and expires at some date in 1978.
85 0xffffffff = valid and does not expire
86 u8 assigned ownertrust [X509: not used]
88 OpenPGP: see ../g10/trustdb/TRUST_* [not yet used]
89 X509: Bit 4 set := key has been revoked. Note that this value
90 matches TRUST_FLAG_REVOKED
93 u32 Newest timestamp in the keyblock (useful for KS syncronsiation?)
95 u32 size of reserved space (not including this field)
98 Here we might want to put other data
100 Here comes the keyblock
102 maybe we put a signature here later.
104 b16 MD5 checksum (useful for KS syncronisation), we might also want to use
119 #include "keybox-defs.h"
122 #ifdef KEYBOX_WITH_OPENPGP
123 /* include stuff to parse the packets */
125 #ifdef KEYBOX_WITH_X509
131 /* special values of the signature status */
132 #define SF_NONE(a) ( !(a) )
133 #define SF_NOKEY(a) ((a) & (1<<0))
134 #define SF_BAD(a) ((a) & (1<<1))
135 #define SF_VALID(a) ((a) & (1<<29))
146 /* #if MAX_FINGERPRINT_LEN < 20 */
147 /* #error fingerprints are 20 bytes */
150 struct keyboxblob_key
{
156 struct keyboxblob_uid
{
158 char *name
; /* used only with x509 */
165 struct keyid_list
*next
;
171 struct fixup_list
*next
;
182 /* stuff used only by keybox_create_blob */
183 unsigned char *serialbuf
;
184 const unsigned char *serial
;
187 struct keyboxblob_key
*keys
;
189 struct keyboxblob_uid
*uids
;
192 struct fixup_list
*fixups
;
193 int fixup_out_of_core
;
195 struct keyid_list
*temp_kids
;
196 struct membuf bufbuf
; /* temporary store for the blob */
202 /* A simple implemention of a dynamic buffer. Use init_membuf() to
203 create a buffer, put_membuf to append bytes and get_membuf to
204 release and return the buffer. Allocation errors are detected but
205 only returned at the final get_membuf(), this helps not to clutter
206 the code with out of core checks. */
209 init_membuf (struct membuf
*mb
, int initiallen
)
212 mb
->size
= initiallen
;
214 mb
->buf
= xtrymalloc (initiallen
);
220 put_membuf (struct membuf
*mb
, const void *buf
, size_t len
)
225 if (mb
->len
+ len
>= mb
->size
)
229 mb
->size
+= len
+ 1024;
230 p
= xtryrealloc (mb
->buf
, mb
->size
);
238 memcpy (mb
->buf
+ mb
->len
, buf
, len
);
243 get_membuf (struct membuf
*mb
, size_t *len
)
257 mb
->out_of_core
= 1; /* don't allow a reuse */
263 put8 (struct membuf
*mb
, byte a
)
265 put_membuf (mb
, &a
, 1);
269 put16 (struct membuf
*mb
, u16 a
)
271 unsigned char tmp
[2];
274 put_membuf (mb
, tmp
, 2);
278 put32 (struct membuf
*mb
, u32 a
)
280 unsigned char tmp
[4];
285 put_membuf (mb
, tmp
, 4);
289 /* Store a value in the fixup list */
291 add_fixup (KEYBOXBLOB blob
, u32 off
, u32 val
)
293 struct fixup_list
*fl
;
295 if (blob
->fixup_out_of_core
)
298 fl
= xtrycalloc(1, sizeof *fl
);
300 blob
->fixup_out_of_core
= 1;
305 fl
->next
= blob
->fixups
;
316 make_timestamp (void)
323 #ifdef KEYBOX_WITH_OPENPGP
325 OpenPGP specific stuff
330 We must store the keyid at some place because we can't calculate the
331 offset yet. This is only used for v3 keyIDs. Function returns an
332 index value for later fixup or -1 for out of core. The value must be
335 pgp_temp_store_kid (KEYBOXBLOB blob
, PKT_public_key
*pk
)
337 struct keyid_list
*k
, *r
;
339 k
= xtrymalloc (sizeof *k
);
342 k
->kid
[0] = pk
->keyid
[0] >> 24 ;
343 k
->kid
[1] = pk
->keyid
[0] >> 16 ;
344 k
->kid
[2] = pk
->keyid
[0] >> 8 ;
345 k
->kid
[3] = pk
->keyid
[0] ;
346 k
->kid
[4] = pk
->keyid
[0] >> 24 ;
347 k
->kid
[5] = pk
->keyid
[0] >> 16 ;
348 k
->kid
[6] = pk
->keyid
[0] >> 8 ;
349 k
->kid
[7] = pk
->keyid
[0] ;
351 k
->next
= blob
->temp_kids
;
353 for (r
=k
; r
; r
= r
->next
)
360 pgp_create_key_part (KEYBOXBLOB blob
, KBNODE keyblock
)
366 for (n
=0, node
= keyblock
; node
; node
= node
->next
)
368 if ( node
->pkt
->pkttype
== PKT_PUBLIC_KEY
369 || node
->pkt
->pkttype
== PKT_PUBLIC_SUBKEY
)
371 PKT_public_key
*pk
= node
->pkt
->pkt
.public_key
;
374 fingerprint_from_pk (pk
, tmp
, &fprlen
);
375 memcpy (blob
->keys
[n
].fpr
, tmp
, 20);
376 if ( fprlen
!= 20 ) /*v3 fpr - shift right and fill with zeroes*/
378 assert (fprlen
== 16);
379 memmove (blob
->keys
[n
].fpr
+4, blob
->keys
[n
].fpr
, 16);
380 memset (blob
->keys
[n
].fpr
, 0, 4);
381 blob
->keys
[n
].off_kid
= pgp_temp_store_kid (blob
, pk
);
385 blob
->keys
[n
].off_kid
= 0; /* will be fixed up later */
387 blob
->keys
[n
].flags
= 0;
390 else if ( node
->pkt
->pkttype
== PKT_SECRET_KEY
391 || node
->pkt
->pkttype
== PKT_SECRET_SUBKEY
)
393 never_reached (); /* actually not yet implemented */
396 assert (n
== blob
->nkeys
);
401 pgp_create_uid_part (KEYBOXBLOB blob
, KBNODE keyblock
)
406 for (n
=0, node
= keyblock
; node
; node
= node
->next
)
408 if (node
->pkt
->pkttype
== PKT_USER_ID
)
410 PKT_user_id
*u
= node
->pkt
->pkt
.user_id
;
412 blob
->uids
[n
].len
= u
->len
;
413 blob
->uids
[n
].flags
= 0;
414 blob
->uids
[n
].validity
= 0;
418 assert (n
== blob
->nuids
);
423 pgp_create_sig_part (KEYBOXBLOB blob
, KBNODE keyblock
)
428 for (n
=0, node
= keyblock
; node
; node
= node
->next
)
430 if (node
->pkt
->pkttype
== PKT_SIGNATURE
)
432 PKT_signature
*sig
= node
->pkt
->pkt
.signature
;
434 blob
->sigs
[n
] = 0; /* FIXME: check the signature here */
438 assert( n
== blob
->nsigs
);
443 pgp_create_blob_keyblock (KEYBOXBLOB blob
, KBNODE keyblock
)
445 struct membuf
*a
= blob
->buf
;
449 u32 kbstart
= a
->len
;
451 add_fixup (blob
, kbstart
);
453 for (n
= 0, node
= keyblock
; node
; node
= node
->next
)
455 rc
= build_packet ( a
, node
->pkt
);
457 gpg_log_error ("build_packet(%d) for keyboxblob failed: %s\n",
458 node
->pkt
->pkttype
, gpg_errstr(rc
) );
459 return GPGERR_WRITE_FILE
;
461 if ( node
->pkt
->pkttype
== PKT_USER_ID
)
463 PKT_user_id
*u
= node
->pkt
->pkt
.user_id
;
464 /* build_packet has set the offset of the name into u ;
465 * now we can do the fixup */
466 add_fixup (blob
, blob
->uids
[n
].off_addr
, u
->stored_at
);
470 assert (n
== blob
->nuids
);
472 add_fixup (blob
, a
->len
- kbstart
);
476 #endif /*KEYBOX_WITH_OPENPGP*/
479 #ifdef KEYBOX_WITH_X509
484 /* Write the raw certificate out */
486 x509_create_blob_cert (KEYBOXBLOB blob
, ksba_cert_t cert
)
488 struct membuf
*a
= blob
->buf
;
489 const unsigned char *image
;
491 u32 kbstart
= a
->len
;
493 /* Store our offset for later fixup */
494 add_fixup (blob
, 8, kbstart
);
496 image
= ksba_cert_get_image (cert
, &length
);
498 return gpg_error (GPG_ERR_GENERAL
);
499 put_membuf (a
, image
, length
);
501 add_fixup (blob
, 12, a
->len
- kbstart
);
505 #endif /*KEYBOX_WITH_X509*/
507 /* Write a stored keyID out to the buffer */
509 write_stored_kid (KEYBOXBLOB blob
, int seqno
)
511 struct keyid_list
*r
;
513 for ( r
= blob
->temp_kids
; r
; r
= r
->next
)
515 if (r
->seqno
== seqno
)
517 put_membuf (blob
->buf
, r
->kid
, 8);
524 /* Release a list of key IDs */
526 release_kid_list (struct keyid_list
*kl
)
528 struct keyid_list
*r
, *r2
;
530 for ( r
= kl
; r
; r
= r2
)
540 create_blob_header (KEYBOXBLOB blob
, int blobtype
, int as_ephemeral
)
542 struct membuf
*a
= blob
->buf
;
545 put32 ( a
, 0 ); /* blob length, needs fixup */
547 put8 ( a
, 1 ); /* blob type version */
548 put16 ( a
, as_ephemeral
? 2:0 ); /* blob flags */
550 put32 ( a
, 0 ); /* offset to the raw data, needs fixup */
551 put32 ( a
, 0 ); /* length of the raw data, needs fixup */
553 put16 ( a
, blob
->nkeys
);
554 put16 ( a
, 20 + 4 + 2 + 2 ); /* size of key info */
555 for ( i
=0; i
< blob
->nkeys
; i
++ )
557 put_membuf (a
, blob
->keys
[i
].fpr
, 20);
558 blob
->keys
[i
].off_kid_addr
= a
->len
;
559 put32 ( a
, 0 ); /* offset to keyid, fixed up later */
560 put16 ( a
, blob
->keys
[i
].flags
);
561 put16 ( a
, 0 ); /* reserved */
564 put16 (a
, blob
->seriallen
); /*fixme: check that it fits into 16 bits*/
566 put_membuf (a
, blob
->serial
, blob
->seriallen
);
568 put16 ( a
, blob
->nuids
);
569 put16 ( a
, 4 + 4 + 2 + 1 + 1 ); /* size of uid info */
570 for (i
=0; i
< blob
->nuids
; i
++)
572 blob
->uids
[i
].off_addr
= a
->len
;
573 put32 ( a
, 0 ); /* offset to userid, fixed up later */
574 put32 ( a
, blob
->uids
[i
].len
);
575 put16 ( a
, blob
->uids
[i
].flags
);
576 put8 ( a
, 0 ); /* validity */
577 put8 ( a
, 0 ); /* reserved */
580 put16 ( a
, blob
->nsigs
);
581 put16 ( a
, 4 ); /* size of sig info */
582 for (i
=0; i
< blob
->nsigs
; i
++)
584 put32 ( a
, blob
->sigs
[i
]);
587 put8 ( a
, 0 ); /* assigned ownertrust */
588 put8 ( a
, 0 ); /* validity of all user IDs */
589 put16 ( a
, 0 ); /* reserved */
590 put32 ( a
, 0 ); /* time of next recheck */
591 put32 ( a
, 0 ); /* newest timestamp (none) */
592 put32 ( a
, make_timestamp() ); /* creation time */
593 put32 ( a
, 0 ); /* size of reserved space */
594 /* reserved space (which is currently of size 0) */
596 /* space where we write keyIDs and and other stuff so that the
597 pointers can actually point to somewhere */
598 if (blobtype
== BLOBTYPE_PGP
)
600 /* We need to store the keyids for all pgp v3 keys because those key
601 IDs are not part of the fingerprint. While we are doing that, we
602 fixup all the keyID offsets */
603 for (i
=0; i
< blob
->nkeys
; i
++ )
605 if (blob
->keys
[i
].off_kid
)
606 { /* this is a v3 one */
607 add_fixup (blob
, blob
->keys
[i
].off_kid_addr
, a
->len
);
608 write_stored_kid (blob
, blob
->keys
[i
].off_kid
);
611 { /* the better v4 key IDs - just store an offset 8 bytes back */
612 add_fixup (blob
, blob
->keys
[i
].off_kid_addr
,
613 blob
->keys
[i
].off_kid_addr
- 8);
618 if (blobtype
== BLOBTYPE_X509
)
620 /* We don't want to point to ASN.1 encoded UserIDs (DNs) but to
621 the utf-8 string represenation of them */
622 for (i
=0; i
< blob
->nuids
; i
++ )
624 if (blob
->uids
[i
].name
)
625 { /* this is a v3 one */
626 add_fixup (blob
, blob
->uids
[i
].off_addr
, a
->len
);
627 put_membuf (blob
->buf
, blob
->uids
[i
].name
, blob
->uids
[i
].len
);
638 create_blob_trailer (KEYBOXBLOB blob
)
646 create_blob_finish (KEYBOXBLOB blob
)
648 struct membuf
*a
= blob
->buf
;
654 /* write a placeholder for the checksum */
655 for (i
= 0; i
< 16; i
++ )
656 put32 (a
, 0); /* Hmmm: why put32() ?? */
658 /* get the memory area */
659 n
= 0; /* (Just to avoid compiler warning.) */
660 p
= get_membuf (a
, &n
);
662 return gpg_error (GPG_ERR_ENOMEM
);
665 /* fixup the length */
666 add_fixup (blob
, 0, n
);
669 if (blob
->fixup_out_of_core
)
670 return gpg_error (GPG_ERR_ENOMEM
);
673 struct fixup_list
*fl
;
674 for (fl
= blob
->fixups
; fl
; fl
= fl
->next
)
676 assert (fl
->off
+4 <= n
);
677 p
[fl
->off
+0] = fl
->val
>> 24;
678 p
[fl
->off
+1] = fl
->val
>> 16;
679 p
[fl
->off
+2] = fl
->val
>> 8;
680 p
[fl
->off
+3] = fl
->val
;
684 /* calculate and store the MD5 checksum */
685 gcry_md_hash_buffer (GCRY_MD_MD5
, p
+ n
- 16, p
, n
- 16);
689 return gpg_error_from_syserror ();
698 #ifdef KEYBOX_WITH_OPENPGP
701 _keybox_create_pgp_blob (KEYBOXBLOB
*r_blob
, KBNODE keyblock
, int as_ephemeral
)
708 blob
= xtrycalloc (1, sizeof *blob
);
710 return gpg_error_from_syserror ();
712 /* fixme: Do some sanity checks on the keyblock */
714 /* count userids and keys so that we can allocate the arrays */
715 for (node
= keyblock
; node
; node
= node
->next
)
717 switch (node
->pkt
->pkttype
)
721 case PKT_PUBLIC_SUBKEY
:
722 case PKT_SECRET_SUBKEY
: blob
->nkeys
++; break;
723 case PKT_USER_ID
: blob
->nuids
++; break;
724 case PKT_SIGNATURE
: blob
->nsigs
++; break;
729 blob
->keys
= xtrycalloc (blob
->nkeys
, sizeof *blob
->keys
);
730 blob
->uids
= xtrycalloc (blob
->nuids
, sizeof *blob
->uids
);
731 blob
->sigs
= xtrycalloc (blob
->nsigs
, sizeof *blob
->sigs
);
732 if (!blob
->keys
|| !blob
->uids
|| !blob
->sigs
)
734 rc
= gpg_error (GPG_ERR_ENOMEM
);
738 rc
= pgp_create_key_part ( blob
, keyblock
);
741 rc
= pgp_create_uid_part ( blob
, keyblock
);
744 rc
= pgp_create_sig_part ( blob
, keyblock
);
748 init_membuf (&blob
->bufbuf
, 1024);
749 blob
->buf
= &blob
->bufbuf
;
750 rc
= create_blob_header (blob
, BLOBTYPE_OPENPGP
, as_ephemeral
);
753 rc
= pgp_create_blob_keyblock (blob
, keyblock
);
756 rc
= create_blob_trailer (blob
);
759 rc
= create_blob_finish ( blob
);
765 release_kid_list (blob
->temp_kids
);
766 blob
->temp_kids
= NULL
;
769 keybox_release_blob (blob
);
778 #endif /*KEYBOX_WITH_OPENPGP*/
780 #ifdef KEYBOX_WITH_X509
782 /* Return an allocated string with the email address extracted from a
783 DN. Note hat we use this code also in ../sm/keylist.c. */
785 x509_email_kludge (const char *name
)
787 const char *p
, *string
;
794 p
= strstr (string
, "1.2.840.113549.1.9.1=#");
797 if (p
== name
|| (p
> string
+1 && p
[-1] == ',' && p
[-2] != '\\'))
806 /* This looks pretty much like an email address in the subject's DN
807 we use this to add an additional user ID entry. This way,
808 OpenSSL generated keys get a nicer and usable listing. */
809 for (n
=0, p
=name
; hexdigitp (p
) && hexdigitp (p
+1); p
+=2, n
++)
813 buf
= xtrymalloc (n
+3);
815 return NULL
; /* oops, out of core */
817 for (n
=1, p
=name
; hexdigitp (p
); p
+=2, n
++)
826 /* Note: We should move calculation of the digest into libksba and
827 remove that parameter */
829 _keybox_create_x509_blob (KEYBOXBLOB
*r_blob
, ksba_cert_t cert
,
830 unsigned char *sha1_digest
, int as_ephemeral
)
840 blob
= xtrycalloc (1, sizeof *blob
);
842 return gpg_error_from_syserror ();
844 sn
= ksba_cert_get_serial (cert
);
848 n
= gcry_sexp_canon_len (sn
, 0, NULL
, NULL
);
852 return gpg_error (GPG_ERR_GENERAL
);
854 blob
->serialbuf
= sn
;
855 sn
++; n
--; /* skip '(' */
856 for (len
=0; n
&& *sn
&& *sn
!= ':' && digitp (sn
); n
--, sn
++)
857 len
= len
*10 + atoi_1 (sn
);
860 xfree (blob
->serialbuf
);
861 blob
->serialbuf
= NULL
;
862 return gpg_error (GPG_ERR_GENERAL
);
866 blob
->seriallen
= len
;
871 /* create list of names */
874 names
= xtrymalloc (max_names
* sizeof *names
);
877 rc
= gpg_error_from_syserror ();
881 p
= ksba_cert_get_issuer (cert
, 0);
884 rc
= gpg_error (GPG_ERR_MISSING_VALUE
);
887 names
[blob
->nuids
++] = p
;
888 for (i
=0; (p
= ksba_cert_get_subject (cert
, i
)); i
++)
890 if (blob
->nuids
>= max_names
)
895 tmp
= xtryrealloc (names
, max_names
* sizeof *names
);
898 rc
= gpg_error_from_syserror ();
902 names
[blob
->nuids
++] = p
;
903 if (!i
&& (p
=x509_email_kludge (p
)))
904 names
[blob
->nuids
++] = p
; /* due to !i we don't need to check bounds*/
907 /* space for signature information */
910 blob
->keys
= xtrycalloc (blob
->nkeys
, sizeof *blob
->keys
);
911 blob
->uids
= xtrycalloc (blob
->nuids
, sizeof *blob
->uids
);
912 blob
->sigs
= xtrycalloc (blob
->nsigs
, sizeof *blob
->sigs
);
913 if (!blob
->keys
|| !blob
->uids
|| !blob
->sigs
)
915 rc
= gpg_error (GPG_ERR_ENOMEM
);
919 memcpy (blob
->keys
[0].fpr
, sha1_digest
, 20);
920 blob
->keys
[0].off_kid
= 0; /* We don't have keyids */
921 blob
->keys
[0].flags
= 0;
923 /* issuer and subject names */
924 for (i
=0; i
< blob
->nuids
; i
++)
926 blob
->uids
[i
].name
= names
[i
];
927 blob
->uids
[i
].len
= strlen(names
[i
]);
929 blob
->uids
[i
].flags
= 0;
930 blob
->uids
[i
].validity
= 0;
936 blob
->sigs
[0] = 0; /* not yet checked */
938 /* Create a temporary buffer for further processing */
939 init_membuf (&blob
->bufbuf
, 1024);
940 blob
->buf
= &blob
->bufbuf
;
941 /* write out what we already have */
942 rc
= create_blob_header (blob
, BLOBTYPE_X509
, as_ephemeral
);
945 rc
= x509_create_blob_cert (blob
, cert
);
948 rc
= create_blob_trailer (blob
);
951 rc
= create_blob_finish ( blob
);
957 release_kid_list (blob
->temp_kids
);
958 blob
->temp_kids
= NULL
;
961 for (i
=0; i
< blob
->nuids
; i
++)
967 _keybox_release_blob (blob
);
976 #endif /*KEYBOX_WITH_X509*/
981 _keybox_new_blob (KEYBOXBLOB
*r_blob
,
982 unsigned char *image
, size_t imagelen
, off_t off
)
987 blob
= xtrycalloc (1, sizeof *blob
);
989 return gpg_error_from_syserror ();
992 blob
->bloblen
= imagelen
;
993 blob
->fileoffset
= off
;
1000 _keybox_release_blob (KEYBOXBLOB blob
)
1005 /* hmmm: release membuf here?*/
1006 xfree (blob
->keys
);
1007 xfree (blob
->serialbuf
);
1008 for (i
=0; i
< blob
->nuids
; i
++)
1009 xfree (blob
->uids
[i
].name
);
1010 xfree (blob
->uids
);
1011 xfree (blob
->sigs
);
1012 xfree (blob
->blob
);
1018 const unsigned char *
1019 _keybox_get_blob_image ( KEYBOXBLOB blob
, size_t *n
)
1026 _keybox_get_blob_fileoffset (KEYBOXBLOB blob
)
1028 return blob
->fileoffset
;
1034 _keybox_update_header_blob (KEYBOXBLOB blob
)
1036 if (blob
->bloblen
>= 32 && blob
->blob
[4] == BLOBTYPE_HEADER
)
1038 u32 val
= make_timestamp ();
1040 /* Update the last maintenance run times tamp. */
1041 blob
->blob
[20] = (val
>> 24);
1042 blob
->blob
[20+1] = (val
>> 16);
1043 blob
->blob
[20+2] = (val
>> 8);
1044 blob
->blob
[20+3] = (val
);