The Design and Implementation of the FreeBSD Operating System, Second Edition
Now available: The Design and Implementation of the FreeBSD Operating System (Second Edition)


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]

FreeBSD/Linux Kernel Cross Reference
sys/scripts/sign-file

Version: -  FREEBSD  -  FREEBSD-13-STABLE  -  FREEBSD-13-0  -  FREEBSD-12-STABLE  -  FREEBSD-12-0  -  FREEBSD-11-STABLE  -  FREEBSD-11-0  -  FREEBSD-10-STABLE  -  FREEBSD-10-0  -  FREEBSD-9-STABLE  -  FREEBSD-9-0  -  FREEBSD-8-STABLE  -  FREEBSD-8-0  -  FREEBSD-7-STABLE  -  FREEBSD-7-0  -  FREEBSD-6-STABLE  -  FREEBSD-6-0  -  FREEBSD-5-STABLE  -  FREEBSD-5-0  -  FREEBSD-4-STABLE  -  FREEBSD-3-STABLE  -  FREEBSD22  -  l41  -  OPENBSD  -  linux-2.6  -  MK84  -  PLAN9  -  xnu-8792 
SearchContext: -  none  -  3  -  10 

    1 #!/usr/bin/perl -w
    2 #
    3 # Sign a module file using the given key.
    4 #
    5 # Format:
    6 #
    7 #       ./scripts/sign-file [-v] <key> <x509> <module> [<dest>]
    8 #
    9 #
   10 use strict;
   11 use FileHandle;
   12 use IPC::Open2;
   13 
   14 my $verbose = 0;
   15 if ($#ARGV >= 0 && $ARGV[0] eq "-v") {
   16     $verbose = 1;
   17     shift;
   18 }
   19 
   20 die "Format: ./scripts/sign-file [-v] <key> <x509> <module> [<dest>]\n"
   21     if ($#ARGV != 2 && $#ARGV != 3);
   22 
   23 my $private_key = $ARGV[0];
   24 my $x509 = $ARGV[1];
   25 my $module = $ARGV[2];
   26 my $dest = ($#ARGV == 3) ? $ARGV[3] : $ARGV[2] . "~";
   27 
   28 die "Can't read private key\n" unless (-r $private_key);
   29 die "Can't read X.509 certificate\n" unless (-r $x509);
   30 die "Can't read module\n" unless (-r $module);
   31 
   32 #
   33 # Read the kernel configuration
   34 #
   35 my %config = (
   36     CONFIG_MODULE_SIG_SHA512 => 1
   37     );
   38 
   39 if (-r ".config") {
   40     open(FD, "<.config") || die ".config";
   41     while (<FD>) {
   42         if ($_ =~ /^(CONFIG_.*)=[ym]/) {
   43             $config{$1} = 1;
   44         }
   45     }
   46     close(FD);
   47 }
   48 
   49 #
   50 # Function to read the contents of a file into a variable.
   51 #
   52 sub read_file($)
   53 {
   54     my ($file) = @_;
   55     my $contents;
   56     my $len;
   57 
   58     open(FD, "<$file") || die $file;
   59     binmode FD;
   60     my @st = stat(FD);
   61     die $file if (!@st);
   62     $len = read(FD, $contents, $st[7]) || die $file;
   63     close(FD) || die $file;
   64     die "$file: Wanted length ", $st[7], ", got ", $len, "\n"
   65         if ($len != $st[7]);
   66     return $contents;
   67 }
   68 
   69 ###############################################################################
   70 #
   71 # First of all, we have to parse the X.509 certificate to find certain details
   72 # about it.
   73 #
   74 # We read the DER-encoded X509 certificate and parse it to extract the Subject
   75 # name and Subject Key Identifier.  Theis provides the data we need to build
   76 # the certificate identifier.
   77 #
   78 # The signer's name part of the identifier is fabricated from the commonName,
   79 # the organizationName or the emailAddress components of the X.509 subject
   80 # name.
   81 #
   82 # The subject key ID is used to select which of that signer's certificates
   83 # we're intending to use to sign the module.
   84 #
   85 ###############################################################################
   86 my $x509_certificate = read_file($x509);
   87 
   88 my $UNIV = 0 << 6;
   89 my $APPL = 1 << 6;
   90 my $CONT = 2 << 6;
   91 my $PRIV = 3 << 6;
   92 
   93 my $CONS = 0x20;
   94 
   95 my $BOOLEAN     = 0x01;
   96 my $INTEGER     = 0x02;
   97 my $BIT_STRING  = 0x03;
   98 my $OCTET_STRING = 0x04;
   99 my $NULL        = 0x05;
  100 my $OBJ_ID      = 0x06;
  101 my $UTF8String  = 0x0c;
  102 my $SEQUENCE    = 0x10;
  103 my $SET         = 0x11;
  104 my $UTCTime     = 0x17;
  105 my $GeneralizedTime = 0x18;
  106 
  107 my %OIDs = (
  108     pack("CCC", 85, 4, 3)       => "commonName",
  109     pack("CCC", 85, 4, 6)       => "countryName",
  110     pack("CCC", 85, 4, 10)      => "organizationName",
  111     pack("CCC", 85, 4, 11)      => "organizationUnitName",
  112     pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 1) => "rsaEncryption",
  113     pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 1, 5) => "sha1WithRSAEncryption",
  114     pack("CCCCCCCCC", 42, 134, 72, 134, 247, 13, 1, 9, 1) => "emailAddress",
  115     pack("CCC", 85, 29, 35)     => "authorityKeyIdentifier",
  116     pack("CCC", 85, 29, 14)     => "subjectKeyIdentifier",
  117     pack("CCC", 85, 29, 19)     => "basicConstraints"
  118 );
  119 
  120 ###############################################################################
  121 #
  122 # Extract an ASN.1 element from a string and return information about it.
  123 #
  124 ###############################################################################
  125 sub asn1_extract($$@)
  126 {
  127     my ($cursor, $expected_tag, $optional) = @_;
  128 
  129     return [ -1 ]
  130         if ($cursor->[1] == 0 && $optional);
  131 
  132     die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (elem ", $cursor->[1], ")\n"
  133         if ($cursor->[1] < 2);
  134 
  135     my ($tag, $len) = unpack("CC", substr(${$cursor->[2]}, $cursor->[0], 2));
  136 
  137     if ($expected_tag != -1 && $tag != $expected_tag) {
  138         return [ -1 ]
  139             if ($optional);
  140         die $x509, ": ", $cursor->[0], ": ASN.1 unexpected tag (", $tag,
  141         " not ", $expected_tag, ")\n";
  142     }
  143 
  144     $cursor->[0] += 2;
  145     $cursor->[1] -= 2;
  146 
  147     die $x509, ": ", $cursor->[0], ": ASN.1 long tag\n"
  148         if (($tag & 0x1f) == 0x1f);
  149     die $x509, ": ", $cursor->[0], ": ASN.1 indefinite length\n"
  150         if ($len == 0x80);
  151 
  152     if ($len > 0x80) {
  153         my $l = $len - 0x80;
  154         die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (len len $l)\n"
  155             if ($cursor->[1] < $l);
  156 
  157         if ($l == 0x1) {
  158             $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1));
  159         } elsif ($l == 0x2) {
  160             $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0], 2));
  161         } elsif ($l == 0x3) {
  162             $len = unpack("C", substr(${$cursor->[2]}, $cursor->[0], 1)) << 16;
  163             $len = unpack("n", substr(${$cursor->[2]}, $cursor->[0] + 1, 2));
  164         } elsif ($l == 0x4) {
  165             $len = unpack("N", substr(${$cursor->[2]}, $cursor->[0], 4));
  166         } else {
  167             die $x509, ": ", $cursor->[0], ": ASN.1 element too long (", $l, ")\n";
  168         }
  169 
  170         $cursor->[0] += $l;
  171         $cursor->[1] -= $l;
  172     }
  173 
  174     die $x509, ": ", $cursor->[0], ": ASN.1 data underrun (", $len, ")\n"
  175         if ($cursor->[1] < $len);
  176 
  177     my $ret = [ $tag, [ $cursor->[0], $len, $cursor->[2] ] ];
  178     $cursor->[0] += $len;
  179     $cursor->[1] -= $len;
  180 
  181     return $ret;
  182 }
  183 
  184 ###############################################################################
  185 #
  186 # Retrieve the data referred to by a cursor
  187 #
  188 ###############################################################################
  189 sub asn1_retrieve($)
  190 {
  191     my ($cursor) = @_;
  192     my ($offset, $len, $data) = @$cursor;
  193     return substr($$data, $offset, $len);
  194 }
  195 
  196 ###############################################################################
  197 #
  198 # Roughly parse the X.509 certificate
  199 #
  200 ###############################################################################
  201 my $cursor = [ 0, length($x509_certificate), \$x509_certificate ];
  202 
  203 my $cert = asn1_extract($cursor, $UNIV | $CONS | $SEQUENCE);
  204 my $tbs = asn1_extract($cert->[1], $UNIV | $CONS | $SEQUENCE);
  205 my $version = asn1_extract($tbs->[1], $CONT | $CONS | 0, 1);
  206 my $serial_number = asn1_extract($tbs->[1], $UNIV | $INTEGER);
  207 my $sig_type = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
  208 my $issuer = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
  209 my $validity = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
  210 my $subject = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
  211 my $key = asn1_extract($tbs->[1], $UNIV | $CONS | $SEQUENCE);
  212 my $issuer_uid = asn1_extract($tbs->[1], $CONT | $CONS | 1, 1);
  213 my $subject_uid = asn1_extract($tbs->[1], $CONT | $CONS | 2, 1);
  214 my $extension_list = asn1_extract($tbs->[1], $CONT | $CONS | 3, 1);
  215 
  216 my $subject_key_id = ();
  217 my $authority_key_id = ();
  218 
  219 #
  220 # Parse the extension list
  221 #
  222 if ($extension_list->[0] != -1) {
  223     my $extensions = asn1_extract($extension_list->[1], $UNIV | $CONS | $SEQUENCE);
  224 
  225     while ($extensions->[1]->[1] > 0) {
  226         my $ext = asn1_extract($extensions->[1], $UNIV | $CONS | $SEQUENCE);
  227         my $x_oid = asn1_extract($ext->[1], $UNIV | $OBJ_ID);
  228         my $x_crit = asn1_extract($ext->[1], $UNIV | $BOOLEAN, 1);
  229         my $x_val = asn1_extract($ext->[1], $UNIV | $OCTET_STRING);
  230 
  231         my $raw_oid = asn1_retrieve($x_oid->[1]);
  232         next if (!exists($OIDs{$raw_oid}));
  233         my $x_type = $OIDs{$raw_oid};
  234 
  235         my $raw_value = asn1_retrieve($x_val->[1]);
  236 
  237         if ($x_type eq "subjectKeyIdentifier") {
  238             my $vcursor = [ 0, length($raw_value), \$raw_value ];
  239 
  240             $subject_key_id = asn1_extract($vcursor, $UNIV | $OCTET_STRING);
  241         }
  242     }
  243 }
  244 
  245 ###############################################################################
  246 #
  247 # Determine what we're going to use as the signer's name.  In order of
  248 # preference, take one of: commonName, organizationName or emailAddress.
  249 #
  250 ###############################################################################
  251 my $org = "";
  252 my $cn = "";
  253 my $email = "";
  254 
  255 while ($subject->[1]->[1] > 0) {
  256     my $rdn = asn1_extract($subject->[1], $UNIV | $CONS | $SET);
  257     my $attr = asn1_extract($rdn->[1], $UNIV | $CONS | $SEQUENCE);
  258     my $n_oid = asn1_extract($attr->[1], $UNIV | $OBJ_ID);
  259     my $n_val = asn1_extract($attr->[1], -1);
  260 
  261     my $raw_oid = asn1_retrieve($n_oid->[1]);
  262     next if (!exists($OIDs{$raw_oid}));
  263     my $n_type = $OIDs{$raw_oid};
  264 
  265     my $raw_value = asn1_retrieve($n_val->[1]);
  266 
  267     if ($n_type eq "organizationName") {
  268         $org = $raw_value;
  269     } elsif ($n_type eq "commonName") {
  270         $cn = $raw_value;
  271     } elsif ($n_type eq "emailAddress") {
  272         $email = $raw_value;
  273     }
  274 }
  275 
  276 my $signers_name = $email;
  277 
  278 if ($org && $cn) {
  279     # Don't use the organizationName if the commonName repeats it
  280     if (length($org) <= length($cn) &&
  281         substr($cn, 0, length($org)) eq $org) {
  282         $signers_name = $cn;
  283         goto got_id_name;
  284     }
  285 
  286     # Or a signifcant chunk of it
  287     if (length($org) >= 7 &&
  288         length($cn) >= 7 &&
  289         substr($cn, 0, 7) eq substr($org, 0, 7)) {
  290         $signers_name = $cn;
  291         goto got_id_name;
  292     }
  293 
  294     $signers_name = $org . ": " . $cn;
  295 } elsif ($org) {
  296     $signers_name = $org;
  297 } elsif ($cn) {
  298     $signers_name = $cn;
  299 }
  300 
  301 got_id_name:
  302 
  303 die $x509, ": ", "X.509: Couldn't find the Subject Key Identifier extension\n"
  304     if (!$subject_key_id);
  305 
  306 my $key_identifier = asn1_retrieve($subject_key_id->[1]);
  307 
  308 ###############################################################################
  309 #
  310 # Create and attach the module signature
  311 #
  312 ###############################################################################
  313 
  314 #
  315 # Signature parameters
  316 #
  317 my $algo = 1;           # Public-key crypto algorithm: RSA
  318 my $hash = 0;           # Digest algorithm
  319 my $id_type = 1;        # Identifier type: X.509
  320 
  321 #
  322 # Digest the data
  323 #
  324 my ($dgst, $prologue) = ();
  325 if (exists $config{"CONFIG_MODULE_SIG_SHA1"}) {
  326     $prologue = pack("C*",
  327                      0x30, 0x21, 0x30, 0x09, 0x06, 0x05,
  328                      0x2B, 0x0E, 0x03, 0x02, 0x1A,
  329                      0x05, 0x00, 0x04, 0x14);
  330     $dgst = "-sha1";
  331     $hash = 2;
  332 } elsif (exists $config{"CONFIG_MODULE_SIG_SHA224"}) {
  333     $prologue = pack("C*",
  334                      0x30, 0x2d, 0x30, 0x0d, 0x06, 0x09,
  335                      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x04,
  336                      0x05, 0x00, 0x04, 0x1C);
  337     $dgst = "-sha224";
  338     $hash = 7;
  339 } elsif (exists $config{"CONFIG_MODULE_SIG_SHA256"}) {
  340     $prologue = pack("C*",
  341                      0x30, 0x31, 0x30, 0x0d, 0x06, 0x09,
  342                      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x01,
  343                      0x05, 0x00, 0x04, 0x20);
  344     $dgst = "-sha256";
  345     $hash = 4;
  346 } elsif (exists $config{"CONFIG_MODULE_SIG_SHA384"}) {
  347     $prologue = pack("C*",
  348                      0x30, 0x41, 0x30, 0x0d, 0x06, 0x09,
  349                      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x02,
  350                      0x05, 0x00, 0x04, 0x30);
  351     $dgst = "-sha384";
  352     $hash = 5;
  353 } elsif (exists $config{"CONFIG_MODULE_SIG_SHA512"}) {
  354     $prologue = pack("C*",
  355                      0x30, 0x51, 0x30, 0x0d, 0x06, 0x09,
  356                      0x60, 0x86, 0x48, 0x01, 0x65, 0x03, 0x04, 0x02, 0x03,
  357                      0x05, 0x00, 0x04, 0x40);
  358     $dgst = "-sha512";
  359     $hash = 6;
  360 } else {
  361     die "Can't determine hash algorithm";
  362 }
  363 
  364 #
  365 # Generate the digest and read from openssl's stdout
  366 #
  367 my $digest;
  368 $digest = readpipe("openssl dgst $dgst -binary $module") || die "openssl dgst";
  369 
  370 #
  371 # Generate the binary signature, which will be just the integer that comprises
  372 # the signature with no metadata attached.
  373 #
  374 my $pid;
  375 $pid = open2(*read_from, *write_to,
  376              "openssl rsautl -sign -inkey $private_key -keyform PEM") ||
  377     die "openssl rsautl";
  378 binmode write_to;
  379 print write_to $prologue . $digest || die "pipe to openssl rsautl";
  380 close(write_to) || die "pipe to openssl rsautl";
  381 
  382 binmode read_from;
  383 my $signature;
  384 read(read_from, $signature, 4096) || die "pipe from openssl rsautl";
  385 close(read_from) || die "pipe from openssl rsautl";
  386 $signature = pack("n", length($signature)) . $signature,
  387 
  388 waitpid($pid, 0) || die;
  389 die "openssl rsautl died: $?" if ($? >> 8);
  390 
  391 #
  392 # Build the signed binary
  393 #
  394 my $unsigned_module = read_file($module);
  395 
  396 my $magic_number = "~Module signature appended~\n";
  397 
  398 my $info = pack("CCCCCxxxN",
  399                 $algo, $hash, $id_type,
  400                 length($signers_name),
  401                 length($key_identifier),
  402                 length($signature));
  403 
  404 if ($verbose) {
  405     print "Size of unsigned module: ", length($unsigned_module), "\n";
  406     print "Size of signer's name  : ", length($signers_name), "\n";
  407     print "Size of key identifier : ", length($key_identifier), "\n";
  408     print "Size of signature      : ", length($signature), "\n";
  409     print "Size of informaton     : ", length($info), "\n";
  410     print "Size of magic number   : ", length($magic_number), "\n";
  411     print "Signer's name          : '", $signers_name, "'\n";
  412     print "Digest                 : $dgst\n";
  413 }
  414 
  415 open(FD, ">$dest") || die $dest;
  416 binmode FD;
  417 print FD
  418     $unsigned_module,
  419     $signers_name,
  420     $key_identifier,
  421     $signature,
  422     $info,
  423     $magic_number
  424     ;
  425 close FD || die $dest;
  426 
  427 if ($#ARGV != 3) {
  428     rename($dest, $module) || die $module;
  429 }

Cache object: 7e8a80b82e39464dac9bd4d78d3a4833


[ source navigation ] [ diff markup ] [ identifier search ] [ freetext search ] [ file search ] [ list types ] [ track identifier ]


This page is part of the FreeBSD/Linux Linux Kernel Cross-Reference, and was automatically generated using a modified version of the LXR engine.