Skip to content

Commit

Permalink
[backend] Refactor BSASN1 code
Browse files Browse the repository at this point in the history
- change order of asn1_unpack return values
- also return tag + body in asn1_unpack
- add asn1_tagged helper
- add asn1_unpack_sequence helper
- add asn1_unpack_integer_mpi helper
  • Loading branch information
mlschroe authored and coolo committed Nov 14, 2018
1 parent 7c60f49 commit 39fbde6
Show file tree
Hide file tree
Showing 2 changed files with 52 additions and 27 deletions.
47 changes: 36 additions & 11 deletions src/backend/BSASN1.pm
Original file line number Diff line number Diff line change
Expand Up @@ -80,7 +80,6 @@ sub gentime {

sub asn1_pack {
my ($tag, @data) = @_;
my $ret = pack("C", $tag);
my $data = join('', @data);
my $l = length($data);
return pack("CC", $tag, $l) . $data if $l < 128;
Expand All @@ -89,16 +88,16 @@ sub asn1_pack {
}

sub asn1_unpack {
my ($in, $allowed, $optional) = @_;
my ($in, $allowed, $optional, $exact) = @_;
$allowed = [ $allowed ] if defined($allowed) && !ref($allowed);
if (length($in) < 2) {
return (undef, undef, $in) if $optional;
die("unexpected end of string\n");
return ($in, undef, '', '') if $optional || grep {!defined($_)} @{$allowed || []};
die("unexpected end of asn1 data\n");
}
my ($tag, $l) = unpack("CC", $in);
if ($allowed) {
$allowed = [ $allowed ] unless ref($allowed);
if (!grep {$tag == $_} @$allowed) {
return (undef, undef, $in) if $optional;
if (!grep {defined($_) && $tag == $_} @$allowed) {
return ($in, undef, '', '') if $optional || grep {!defined($_)} @{$allowed || []};
die("unexpected tag $tag, expected @$allowed\n");
}
}
Expand All @@ -109,11 +108,11 @@ sub asn1_unpack {
die("unsupported asn1 length $l\n") if $l < 1 || $l > 4;
$l = unpack("\@${l}N", pack('xxxx').substr($in, 2, 4));
}
die("unexpected end of string\n") if length($in) < $off + $l;
return ($tag, substr($in, $off, $l), substr($in, $off + $l));
die("unexpected end of asn1 data\n") if length($in) < $off + $l;
die("tailing data at end of asn1 element\n") if $exact && length($in) != $off + $l;
return (substr($in, $off + $l), $tag, substr($in, $off, $l), substr($in, 0, $off + $l));
}


sub der2pem {
my ($in, $what) = @_;
my $ret = MIME::Base64::encode_base64($in, '');
Expand All @@ -129,7 +128,7 @@ sub pem2der {
return MIME::Base64::decode_base64($in);
}

# little helpers
# little pack helpers
sub asn1_null {
return asn1_pack($NULL);
}
Expand Down Expand Up @@ -175,4 +174,30 @@ sub asn1_octet_string {
return asn1_pack($OCTET_STRING, $_[0]);
}

sub asn1_tagged {
return asn1_pack($CONT | $CONS | $_[0], $_[1]);
}

# little unpack helpers
sub asn1_unpack_integer_mpi {
my ($in, $intag) = @_;
$intag = $INTEGER unless defined $intag;
(undef, undef, $in) = asn1_unpack($in, $intag ? $intag : undef, undef, 1);
$in = substr($in, 1) while $in ne '' && unpack('C', $in) == 0;
return $in;
}

sub asn1_unpack_sequence {
my ($in, $intag) = @_;
$intag = $CONS | $SEQUENCE unless defined $intag;
(undef, undef, $in) = asn1_unpack($in, $intag ? $intag : undef, undef, 1);
my @ret;
my $tagbody;
while ($in ne '') {
($in, undef, undef, $tagbody) = asn1_unpack($in);
push @ret, $tagbody;
}
return @ret;
}

1;
32 changes: 16 additions & 16 deletions src/backend/BSTUF.pm
Original file line number Diff line number Diff line change
Expand Up @@ -58,10 +58,10 @@ sub sign {

sub mktbscert {
my ($cn, $not_before, $not_after, $subjectkeyinfo) = @_;
my $serial = pack("CC", 0, 128 + int(rand(128)));
my $serial = pack("C", 128 + int(rand(128)));
$serial .= pack("C", int(rand(256))) for (1, 2, 3, 4, 5, 6, 7);
my $certversion = BSASN1::asn1_pack($BSASN1::CONT | $BSASN1::CONS | 0, BSASN1::asn1_integer(2));
my $certserial = BSASN1::asn1_pack($BSASN1::INTEGER, $serial);
my $certversion = BSASN1::asn1_tagged(0, BSASN1::asn1_integer(2));
my $certserial = BSASN1::asn1_integer_mpi($serial);
my $sigalgo = BSASN1::asn1_sequence($BSASN1::oid_sha256withrsaencryption, BSASN1::asn1_null());
my $cnattr = BSASN1::asn1_sequence($BSASN1::oid_common_name, BSASN1::asn1_pack($BSASN1::UTF8STRING, $cn));
my $issuer = BSASN1::asn1_sequence(BSASN1::asn1_set($cnattr));
Expand All @@ -70,7 +70,7 @@ sub mktbscert {
my $basic_constraints = BSASN1::asn1_sequence($BSASN1::oid_basic_constraints, $critical, BSASN1::asn1_octet_string(BSASN1::asn1_sequence()));
my $key_usage = BSASN1::asn1_sequence($BSASN1::oid_key_usage, $critical, BSASN1::asn1_octet_string(BSASN1::asn1_pack($BSASN1::BIT_STRING, pack("CC", 5, 160))));
my $ext_key_usage = BSASN1::asn1_sequence($BSASN1::oid_ext_key_usage, BSASN1::asn1_octet_string(BSASN1::asn1_sequence($BSASN1::oid_code_signing)));
my $extensions = BSASN1::asn1_pack($BSASN1::CONT | $BSASN1::CONS | 3, BSASN1::asn1_sequence($basic_constraints, $key_usage, $ext_key_usage));
my $extensions = BSASN1::asn1_tagged(3, BSASN1::asn1_sequence($basic_constraints, $key_usage, $ext_key_usage));
my $tbscert = BSASN1::asn1_sequence($certversion, $certserial, $sigalgo, $issuer, $validity, $issuer, $subjectkeyinfo, $extensions);
return $tbscert;
}
Expand All @@ -79,39 +79,39 @@ sub mkcert {
my ($tbscert, $signargs) = @_;
my $sigalgo = BSASN1::asn1_sequence($BSASN1::oid_sha256withrsaencryption, BSASN1::asn1_null());
my $signature = sign($tbscert, $signargs);
my $cert = BSASN1::asn1_sequence($tbscert, $sigalgo, BSASN1::asn1_pack($BSASN1::BIT_STRING, pack("C", 0), $signature));
my $cert = BSASN1::asn1_sequence($tbscert, $sigalgo, BSASN1::asn1_pack($BSASN1::BIT_STRING, pack("C", 0), $signature));
return BSASN1::der2pem($cert, 'CERTIFICATE');
}

# return the to-be-signed part of a certificate
sub gettbscert {
my ($cert) = @_;
$cert = BSASN1::pem2der($cert, 'CERTIFICATE');
(undef, $cert, undef) = BSASN1::asn1_unpack($cert, $BSASN1::CONS | $BSASN1::SEQUENCE);
(undef, $cert, undef) = BSASN1::asn1_unpack($cert, $BSASN1::CONS | $BSASN1::SEQUENCE);
return BSASN1::asn1_pack($BSASN1::CONS | $BSASN1::SEQUENCE, $cert);
(undef, undef, $cert) = BSASN1::asn1_unpack($cert, $BSASN1::CONS | $BSASN1::SEQUENCE);
(undef, undef, undef, $cert) = BSASN1::asn1_unpack($cert, $BSASN1::CONS | $BSASN1::SEQUENCE);
return $cert;
}

# remove the serial number from a tbs certificate. We need to do this because the
# serial is random and we want to compare two certs.
sub removecertserial {
my ($tbscert) = @_;
(undef, $tbscert, undef) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
(undef, undef, $tbscert) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
my $tail = $tbscert;
(undef, undef, $tail) = BSASN1::asn1_unpack($tail); # the version
($tail, undef, undef) = BSASN1::asn1_unpack($tail); # the version
my $l = length($tail);
(undef, undef, $tail) = BSASN1::asn1_unpack($tail, $BSASN1::INTEGER); # the serial
($tail, undef, undef) = BSASN1::asn1_unpack($tail, $BSASN1::INTEGER); # the serial
substr($tbscert, length($tbscert) - $l, $l - length($tail), '');
return BSASN1::asn1_pack($BSASN1::CONS | $BSASN1::SEQUENCE, $tbscert);
return BSASN1::asn1_sequence($tbscert); # pack back into a sequence
}

# get pubkey
sub getsubjectkeyinfo {
my ($tbscert) = @_;
(undef, $tbscert, undef) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
(undef, undef, $tbscert) = BSASN1::asn1_unpack($tbscert) for 1..6;
(undef, $tbscert, undef) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
return BSASN1::asn1_pack($BSASN1::CONS | $BSASN1::SEQUENCE, $tbscert);
(undef, undef, $tbscert) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
($tbscert, undef, undef) = BSASN1::asn1_unpack($tbscert) for 1..6;
(undef, undef, undef, $tbscert) = BSASN1::asn1_unpack($tbscert, $BSASN1::CONS | $BSASN1::SEQUENCE);
return $tbscert;
}

sub signdata {
Expand Down

0 comments on commit 39fbde6

Please sign in to comment.