Skip to content

Commit

Permalink
Merge pull request #17259 from mlschroe/master
Browse files Browse the repository at this point in the history
[backend] support signing and metadata creation for apk
  • Loading branch information
mlschroe authored Jan 10, 2025
2 parents eba5e62 + f94007f commit e1fc466
Show file tree
Hide file tree
Showing 7 changed files with 197 additions and 5 deletions.
1 change: 1 addition & 0 deletions dist/obs-server.spec
Original file line number Diff line number Diff line change
Expand Up @@ -905,6 +905,7 @@ fi
%{obs_backend_dir}/bs_getbinariesproxy
%{obs_backend_dir}/bs_mergechanges
%{obs_backend_dir}/bs_mkarchrepo
%{obs_backend_dir}/bs_mkapkrepo
%{obs_backend_dir}/bs_notar
%{obs_backend_dir}/bs_regpush
%{obs_backend_dir}/bs_dispatch
Expand Down
2 changes: 1 addition & 1 deletion src/backend/BSZip.pm
Original file line number Diff line number Diff line change
Expand Up @@ -52,7 +52,7 @@ sub extract_stored {
sub extract_inflate {
my ($handle, $size, $csize, $writer) = @_;
return unless $size > 0;
require Compress::Raw::Zlib unless defined &Compress::Raw::Zlib::Inflate;
require Compress::Raw::Zlib unless defined &Compress::Raw::Zlib::Inflate::new;
my ($decomp, $status) = Compress::Raw::Zlib::Inflate->new('-WindowBits' => -Compress::Raw::Zlib::MAX_WBITS(), '-Bufsize' => 65536);
die("Compress::Raw::Zlib::Inflate::new failed\n") unless $decomp && $status == Compress::Raw::Zlib::Z_OK();
while ($size > 0 || $csize > 0) {
Expand Down
98 changes: 98 additions & 0 deletions src/backend/bs_mkapkrepo
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
#!/usr/bin/perl

BEGIN {
my ($wd) = $0 =~ m-(.*)/- ;
$wd ||= '.';
unshift @INC, "$wd/build";
unshift @INC, "$wd";
}

use strict;

use Compress::Zlib;

use BSUtil;
use Build::Apk;

my $v3;
my $description;
my $rewrite_arch;

while (1) {
if (@ARGV && $ARGV[0] eq '--v3') {
$v3 = shift @ARGV;
} elsif (@ARGV && $ARGV[0] eq '--description') {
(undef, $description) = splice(@ARGV, 0, 2);
} elsif (@ARGV && $ARGV[0] eq '--rewrite-arch') {
(undef, $rewrite_arch) = splice(@ARGV, 0, 2);
} else {
last;
}
}

die("Usage: bs_mkapkrepo [--v3] [--description <desc>] [--rewrite-arch <arch] <dir>\n") unless @ARGV == 1;
my $dir = $ARGV[0];
my @pkgs = grep {/\.apk$/} sort(ls($dir));

die("apkv3 index support not implemented yet\n") if $v3;

my @vmap = (
'apkchksum' => 'C',
'pkgname' => 'P',
'pkgver' => 'V',
'arch' => 'A',
'filesize' => 'S',
'size' => 'I',
'pkgdesc' => 'T',
'url' => 'U',
'license' => 'L',
'origin' => 'o',
'maintainer' => 'm',
'builddate' => 't',
'commit' => 'c',
'provider_priority' => 'k',
'depend' => 'D',
'provides', => 'p',
'install_if' => 'i',
#'replaces' => 'r',
);

my $idx = '';
for my $pkg (@pkgs) {
my ($hash, $vars);
my @s = stat("$dir/$pkg");
next unless -f _;
eval {
$hash = Build::Apk::calcapkchksum("$dir/$pkg");
$vars = Build::Apk::queryvars("$dir/$pkg");
};
warn($@) if $@;
next unless $vars && $hash;
$vars->{'arch'} = $rewrite_arch if $rewrite_arch;
$vars->{'apkchksum'} = $hash;
$vars->{'filesize'} = $s[7];
my @v = @vmap;
while (@v) {
my ($vk, $vi) = splice(@v, 0, 2);
my $vv = $vars->{$vk};
next unless defined $vv;
$vv = join(' ', @$vv) if ref $vv;
$idx .= "$vi:$vv\n";
}
$idx .= "\n";
}

my $t = time();

my $tar = '';
$tar .= Build::Apk::mktar('DESCRIPTION', '0', $t, $description) if defined $description;
$tar .= Build::Apk::mktar('APKINDEX', '0', $t, $idx);
$tar = Compress::Zlib::memGzip($tar);

my $sigtar = Compress::Zlib::memGzip('');

my $fh;
open($fh, '>', "$dir/APKINDEX.tar.gz") || die("$dir/APKINDEX.tar.gz: $!\n");
print $fh $sigtar or die("write: $!\n");
print $fh $tar or die("write: $!\n");
close($fh) || die("write: $!\n");
2 changes: 1 addition & 1 deletion src/backend/bs_mkarchrepo
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ my %todo = (
die("Usage: bs_mkarchrepo <reponame> <dir>\n") unless @ARGV == 2;
my $reponame = $ARGV[0];
my $dir = $ARGV[1];
my @pkgs = grep {/\.pkg\.tar\.(?:gz|xz|zst)$/} ls($dir);
my @pkgs = grep {/\.pkg\.tar\.(?:gz|xz|zst)$/} sort(ls($dir));

my $rdir = "$dir/$reponame.db";
mkdir_p($rdir);
Expand Down
66 changes: 66 additions & 0 deletions src/backend/bs_publish
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ use BSVerify;
use BSStdRunner;
use BSUrlmapper;
use BSPGP;
use BSASN1;
use BSX509;
use BSHTTP;
use BSRekor;
use BSRepServer::Containerinfo;
Expand Down Expand Up @@ -1172,6 +1174,60 @@ sub deleterepo_arch {

##########################################################################

sub createrepo_apk {
my ($extrep, $projid, $repoid, $data, $options) = @_;
deleterepo_apk($extrep);
for my $arch (ls($extrep)) {
next unless -d "$extrep/$arch";
print " running bs_mkapkrepo $arch\n";
qsystem("$INC[0]/bs_mkapkrepo", '--rewrite-arch', $arch, "$extrep/$arch") && die(" repo creation failed: $?\n");
my $idxfile = "$extrep/$arch/APKINDEX.tar.gz";
if ($BSConfig::sign && -e $idxfile) {
my $pk = BSPGP::unarmor($data->{'pubkey'});
my $keydata = BSPGP::pk2keydata($pk);
my $algo = $keydata->{'algo'};
if ($algo ne 'rsa') {
warn("createrepo_apk: unsupported signing algo '$algo'\n");
next;
}
my $times = BSPGP::pk2times($pk);
my $userid = BSPGP::pk2userid($pk);
die("signapk: missing userid\n") unless $userid;
die("signapk: missing email in userid\n") unless $userid =~ /\<([^\.\/].*?)\>/;
my $email = $1;
my @signargs;
push @signargs, '--project', $projid if $BSConfig::sign_project;
push @signargs, '--signflavor', $data->{'signflavor'} if $data->{'signflavor'};
push @signargs, @{$data->{'signargs'} || []};
my $tbsdata = Build::Apk::gettbsdata($idxfile);
my $hash = ord($tbsdata) == 0x1f ? 'sha1' : 'sha512';
my $sig = BSUtil::xsystem($tbsdata, $BSConfig::sign, @signargs, '-O', '-h', $hash);
my $keyname = sprintf("%s-%08x.%s.pub", $email, $times->{'key_create'}, $algo);
$keyname =~ s/[\000-\037 \/]//g;
Build::Apk::replacesignature($idxfile, "$idxfile.signed", $sig, $data->{'time'}, $algo, $hash, $keyname);
rename("$idxfile.signed", $idxfile) || die("rename $idxfile.signed $idxfile: $!\n");
my $pubk = eval { BSX509::keydata2pubkey($keydata) };
writestr("$extrep/$arch/$keyname", undef, BSASN1::der2pem($pubk, 'PUBLIC KEY')) if $pubk;
}
}
}

sub deleterepo_apk {
my ($extrep) = @_;
for my $arch (ls($extrep)) {
next unless -d "$extrep/$arch";
next if $arch eq 'repodata' || $arch eq 'repocache' || $arch eq 'media.1' || $arch eq 'descr';
next unless -e "$extrep/$arch/APKINDEX.tar.gz";
unlink("$extrep/$arch/APKINDEX.tar.gz");
unlink("$extrep/$arch/APKINDEX.tar.gz.signed");
for (grep {/\.rsa\.pub$/} ls("$extrep/$arch")) {
unlink("$extrep/$arch/$_");
}
}
}

##########################################################################

sub createrepo_staticlinks {
my ($extrep, $projid, $repoid, $data, $options) = @_;

Expand Down Expand Up @@ -2399,6 +2455,10 @@ sub publish {
# special arch linux handling
$p = "$arch/$bin";
$p = "i686/$bin" if $arch eq 'i586'; # HACK
} elsif ($bin =~ /\.apk$/) {
# special apk handling
$p = "$arch/$bin";
$p = "x86/$bin" if $arch eq 'i586';
} elsif ($bin =~ /\.(?:$binsufsre)$/) {
# our default
my $q = Build::query("$r/$rbin", 'evra' => 1);
Expand Down Expand Up @@ -3312,6 +3372,7 @@ sub publish {
'sigs' => [],
'publishid' => $publishid,
'signflavor' => $signflavor,
'time' => $starttime,
};

if ($repotype{'rpm-md'}) {
Expand Down Expand Up @@ -3340,6 +3401,11 @@ sub publish {
} else {
deleterepo_arch($extrep, $projid, $xrepoid, $data);
}
if ($repotype{'apk'}) {
createrepo_apk($extrep, $projid, $xrepoid, $data, $repotype{'apk'});
} else {
deleterepo_apk($extrep, $projid, $xrepoid, $data);
}
if ($repotype{'vagrant'}) {
createrepo_vagrant($extrep, $projid, $xrepoid, $data, $repotype{'vagrant'});
} else {
Expand Down
2 changes: 1 addition & 1 deletion src/backend/bs_repserver
Original file line number Diff line number Diff line change
Expand Up @@ -2470,7 +2470,7 @@ sub putjob {

my $ev = {'type' => 'built', 'arch' => $arch, 'job' => $job};

if ($BSConfig::sign && (@{$kiwitree_tosign || []} || grep {$_->{'name'} eq '_slsa_provenance.json' || $_->{'name'} =~ /\.(?:d?rpm|sha256|iso|pkg\.tar\.gz|pkg\.tar\.xz|pkg\.tar\.zst|AppImage|deb|appx|helminfo|raw|efi|roothash|usrhash)$/} @$uploaded)) {
if ($BSConfig::sign && (@{$kiwitree_tosign || []} || grep {$_->{'name'} eq '_slsa_provenance.json' || $_->{'name'} =~ /\.(?:d?rpm|sha256|iso|pkg\.tar\.gz|pkg\.tar\.xz|pkg\.tar\.zst|AppImage|deb|appx|apk|helminfo|raw|efi|roothash|usrhash)$/} @$uploaded)) {
if (@{$kiwitree_tosign || []}) {
my $c = '';
$c .= BSHTTP::urlencode($_)."\n" for @$kiwitree_tosign;
Expand Down
31 changes: 29 additions & 2 deletions src/backend/bs_signer
Original file line number Diff line number Diff line change
Expand Up @@ -449,6 +449,28 @@ sub signappx {
die($@) if $@;
}

sub signapk {
my ($data, $signfile, @signargs) = @_;
my $pk = BSPGP::unarmor($data->{'pubkey'});
my $keydata = BSPGP::pk2keydata($pk);
my $algo = $keydata->{'algo'};
die("signapk: unsupported signing algo '$algo'\n") if $algo ne 'rsa';
my $times = BSPGP::pk2times($pk);
my $userid = BSPGP::pk2userid($pk);
die("signapk: missing userid\n") unless $userid;
die("signapk: missing email in userid\n") unless $userid =~ /\<(.+?)\>/;
my $email = $1;
my $tbsdata = Build::Apk::gettbsdata($signfile);
# if the first byte of tbsdata is not 0x1f, it's an apkv3 package
my $hash = ord($tbsdata) == 0x1f ? 'sha1' : 'sha512';
my $sig = BSUtil::xsystem($tbsdata, $BSConfig::sign, @signargs, '-O', '-h', $hash);
my $keyname = sprintf("%s-%08x.%s.pub", $email, $times->{'key_create'}, $algo);
my $tmpfile = "$signfile.sIgN$$";
unlink($tmpfile);
Build::Apk::replacesignature($signfile, $tmpfile, $sig, $data->{'time'}, $algo, $hash, $keyname);
rename($tmpfile, $signfile) || die("rename $tmpfile $signfile: $!\n");
}

sub signhelm {
my ($data, $signfile, @signargs) = @_;
my $chart = $signfile;
Expand Down Expand Up @@ -814,7 +836,7 @@ sub signjob {
my $packid = $info->{'package'};
$prpa_stats = "$projid/$info->{'repository'}/$arch";
my @files = sort(ls($jobdir));
my @signfiles = grep {$_ eq '_slsa_provenance.json' || /\.(?:d?rpm|sha256|iso|pkg\.tar\.gz|pkg\.tar\.xz|pkg\.tar\.zst|rsasign|AppImage|appx|helminfo)$/} @files;
my @signfiles = grep {$_ eq '_slsa_provenance.json' || /\.(?:d?rpm|sha256|iso|pkg\.tar\.gz|pkg\.tar\.xz|pkg\.tar\.zst|rsasign|AppImage|appx|helminfo|apk)$/} @files;
my $needpubkey;
if (grep {$_ eq '.kiwitree_tosign'} @files) {
for my $f (split("\n", readstr("$jobdir/.kiwitree_tosign"))) {
Expand All @@ -839,7 +861,7 @@ sub signjob {
die("Illegal signflavor '$signflavor'\n") unless grep {$_ eq $signflavor} @{$BSConfig::sign_flavor || []};
}
my $needcert = grep {/\.appx$/} @signfiles;
$needpubkey ||= grep {/\.iso$/} @signfiles;
$needpubkey ||= grep {/\.(?:iso|apk)$/} @signfiles;
$needpubkey = 1 if $info->{'file'} eq '_aggregate' && grep {/\.d?rpm$/} @signfiles;
my ($algo, $signkey, $pubkey, $cert) = getsignkey($projid, $packid, $needpubkey, $needcert, undef, $signflavor);
my $pubkey_create_time;
Expand Down Expand Up @@ -888,6 +910,7 @@ sub signjob {
'signkey' => $signkey,
'cert' => $cert,
'signflavor' => $signflavor,
'time' => time(),
};

eval {
Expand All @@ -904,6 +927,10 @@ sub signjob {
signappx($data, "$jobdir/$signfile", @signargs);
next;
}
if ($signfile =~ /\.apk$/) {
signapk($data, "$jobdir/$signfile", @signargs);
next;
}
if ($signfile =~ /\.iso$/) {
signiso($data, "$jobdir/$signfile", @signargs);
next;
Expand Down

0 comments on commit e1fc466

Please sign in to comment.