Skip to content

Commit

Permalink
Merge pull request #15647 from mlschroe/master
Browse files Browse the repository at this point in the history
[backend] clean up container signing code
  • Loading branch information
mlschroe committed Feb 15, 2024
2 parents c60b556 + 529bd70 commit d2a3f46
Show file tree
Hide file tree
Showing 5 changed files with 64 additions and 63 deletions.
54 changes: 28 additions & 26 deletions src/backend/BSConSign.pm
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@
#
################################################################
#
# Create an "atomic container signature"
# Create container signatures and handle attestations
#

package BSConSign;
Expand All @@ -39,7 +39,14 @@ sub canonical_json {
return JSON::XS->new->utf8->canonical->encode($_[0]);
}

sub createpayload {
sub create_entry {
my ($data, %extra) = @_;
my $blobid = 'sha256:'.Digest::SHA::sha256_hex($data);
my $ent = { %extra, 'size' => length($data), 'data' => $data, 'blobid' => $blobid };
return $ent;
}

sub create_signature_payload {
my ($type, $digest, $reference, $creator, $timestamp) = @_;
my $critical = {
'type' => $type,
Expand All @@ -53,9 +60,9 @@ sub createpayload {
return canonical_json($data);
}

sub createsig {
sub create_atomic_signature {
my ($signfunc, $digest, $reference, $creator, $timestamp) = @_;
my $payload = createpayload('atomic container signature', $digest, $reference, $creator, $timestamp);
my $payload = create_signature_payload('atomic container signature', $digest, $reference, $creator, $timestamp);
my $sig = $signfunc->($payload);
my $packets = BSPGP::onepass_signed_message($payload, $sig, 'rpmsig-req.bin');
# compress packets like gpg does
Expand All @@ -77,49 +84,44 @@ sub sig2openshift {
return $data;
}

sub createcosign_config {
my (@payload_layers) = @_;
sub create_cosign_config_ent {
my (@layer_ents) = @_;
my @diff_ids = map {$_->{'blobid'}} @layer_ents;
my $config = {
'architecture' => '',
'config' => {},
'created' => '0001-01-01T00:00:00Z',
'history' => [ { 'created' => '0001-01-01T00:00:00Z' } ],
'os' => '',
'rootfs' => { 'type' => 'layers', 'diff_ids' => [ map {$_->{'digest'}} @payload_layers ] },
'rootfs' => { 'type' => 'layers', 'diff_ids' => \@diff_ids },
};
return canonical_json($config);
return create_entry(canonical_json($config));
}

sub createcosign_payload {
sub create_cosign_layer_ent {
my ($payloadtype, $payload, $sig, $annotations) = @_;
my $payload_digest = 'sha256:'.Digest::SHA::sha256_hex($payload);
my $payload_layer_data = {
'annotations' => { 'dev.cosignproject.cosign/signature' => MIME::Base64::encode_base64($sig, ''), %{$annotations || {}} },
'digest' => $payload_digest,
'mediaType' => $payloadtype,
'size' => length($payload),
};
return (createcosign_config($payload_layer_data), $payload_layer_data, $payload, $sig);
my %annotations = %{$annotations || {}};
$annotations->{'dev.cosignproject.cosign/signature'} = MIME::Base64::encode_base64($sig, '') if defined $sig;
return create_entry($payload, 'mimetype' => $payloadtype, 'annotations' => \%annotations);
}

sub createcosign {
sub create_cosign_signature_ent {
my ($signfunc, $digest, $reference, $creator, $timestamp, $annotations) = @_;
my $payload = createpayload('cosign container image signature', $digest, $reference, $creator, $timestamp);
my $payload = create_signature_payload('cosign container image signature', $digest, $reference, $creator, $timestamp);
# signfunc must return the openssl rsa signature
my $sig = $signfunc->($payload);
return createcosign_payload($mt_cosign, $payload, $sig, $annotations);
return (create_cosign_layer_ent($mt_cosign, $payload, $sig, $annotations), $sig);
}

sub createcosign_attestation {
my ($digest, $attestations, $annotations) = @_;
sub create_cosign_attestation_ents {
my ($attestations, $annotations) = @_;
$attestations = [ $attestations ] if ref($attestations) ne 'ARRAY';
my @r = map { [ createcosign_payload($mt_dsse, $_, '', $annotations) ] } @$attestations;
return createcosign_config(map {$_->[1]} @r), map { ($_->[1], $_->[2]) } @r;
return map { create_cosign_layer_ent($mt_dsse, $_, '', $annotations) } @$attestations;
}

sub dsse_pae {
my ($type, $payload) = @_;
return sprintf("DSSEv1 %d %s %d ", length($type), $type, length($payload))."$payload";
return sprintf("DSSEv1 %d %s %d ", length($type), $type, length($payload)).$payload;
}

sub dsse_sign {
Expand Down Expand Up @@ -164,7 +166,7 @@ sub fixup_intoto_attestation {
return dsse_sign($attestation, $mt_intoto, $signfunc);
}

sub createcosigncookie {
sub create_cosign_cookie {
my ($gpgpubkey, $reference, $creator) = @_;
$creator ||= '';
my $pubkeyid = BSPGP::pk2fingerprint(BSPGP::unarmor($gpgpubkey));
Expand Down
2 changes: 1 addition & 1 deletion src/backend/BSPublisher/Container.pm
Original file line number Diff line number Diff line change
Expand Up @@ -553,7 +553,7 @@ sub upload_to_registry {
my $cosigncookie;
if (defined($pubkey) && $cosign) {
my $creator = 'OBS';
$cosigncookie = BSConSign::createcosigncookie($pubkey, $gun, $creator);
$cosigncookie = BSConSign::create_cosign_cookie($pubkey, $gun, $creator);
}

# check if the registry is up-to-date
Expand Down
33 changes: 15 additions & 18 deletions src/backend/BSPublisher/Registry.pm
Original file line number Diff line number Diff line change
Expand Up @@ -400,7 +400,7 @@ sub update_sigs {
next;
}
print "creating atomic signature for $gun:$tag $digest\n";
my $sig = BSConSign::createsig($signfunc, $digest, "$gun:$tag", $creator);
my $sig = BSConSign::create_atomic_signature($signfunc, $digest, "$gun:$tag", $creator);
push @d, [ $tag, $sig ];
}
$sigs->{'digests'}->{$digest} = \@d;
Expand All @@ -417,20 +417,16 @@ sub update_sigs {
}

sub create_cosign_manifest {
my ($repodir, $oci, $knownmanifests, $knownblobs, $config, @payload_layers) = @_;

my ($config_ent, $config_blobid) = BSContar::make_blob_entry('config.json', $config);
push_blob($repodir, $config_ent);
$knownblobs->{$config_blobid} = 1;
my ($repodir, $oci, $knownmanifests, $knownblobs, @layer_ents) = @_;
my $config_ent = BSConSign::create_cosign_config_ent(\@layer_ents);
my $config_data = BSContar::create_config_data($config_ent, $oci);
push_blob($repodir, $config_ent);
$knownblobs->{$config_ent->{'blobid'}} = 1;
my @layer_data;
while (@payload_layers >= 2) {
my ($payload_layer_data, $payload) = splice(@payload_layers, 0, 2);
my ($payload_ent, $payload_blobid) = BSContar::make_blob_entry($payload_layer_data->{'digest'}, $payload);
die unless $payload_blobid eq $payload_layer_data->{'digest'};
push_blob($repodir, $payload_ent);
$knownblobs->{$payload_blobid} = 1;
push @layer_data, $payload_layer_data;
for my $layer_ent (@layer_ents) {
push @layer_data, BSContar::create_layer_data($layer_ent, $oci);
push_blob($repodir, $layer_ent);
$knownblobs->{$layer_ent->{'blobid'}} = 1;
}
my $mani = BSContar::create_dist_manifest_data($config_data, \@layer_data, $oci);
my $mani_json = BSContar::create_dist_manifest($mani);
Expand Down Expand Up @@ -482,14 +478,15 @@ sub update_cosign {
next;
}
print "creating cosign signature for $gun $digest\n";
my ($config, $payload_layer_data, $payload, $sig) = BSConSign::createcosign($signfunc, $digest, $gun, $creator);
my $mani_id = create_cosign_manifest($repodir, $oci, $knownmanifests, $knownblobs, $config, $payload_layer_data, $payload);
my ($cosign_ent, $sig) = BSConSign::create_cosign_signature_ent($signfunc, $digest, $gun, $creator);
my $mani_id = create_cosign_manifest($repodir, $oci, $knownmanifests, $knownblobs, $cosign_ent);
$sigs->{'digests'}->{$digest} = $mani_id;
if ($rekorserver) {
print "uploading cosign signature to $rekorserver\n";
my $sslpubkey = BSX509::keydata2pubkey(BSPGP::pk2keydata($gpgpubkey));
$sslpubkey = BSASN1::der2pem($sslpubkey, 'PUBLIC KEY');
BSRekor::upload_hashedrekord($rekorserver, $payload_layer_data->{'digest'}, $sslpubkey, $sig);
my $hash = 'sha256:'.Digest::SHA::sha256_hex($cosign_ent->{'data'}); # must match signfunc
BSRekor::upload_hashedrekord($rekorserver, $hash, $sslpubkey, $sig);
}
}

Expand All @@ -512,8 +509,8 @@ sub update_cosign {
push @attestations, BSConSign::fixup_intoto_attestation($containerinfo->{'slsa_provenance'}, $signfunc, $digest, $gun) if $containerinfo->{'slsa_provenance'};
push @attestations, BSConSign::fixup_intoto_attestation(readstr($containerinfo->{'spdx_file'}), $signfunc, $digest, $gun) if $containerinfo->{'spdx_file'};
push @attestations, BSConSign::fixup_intoto_attestation(readstr($containerinfo->{'cyclonedx_file'}), $signfunc, $digest, $gun) if $containerinfo->{'cyclonedx_file'};
my ($config, @attestation_layers) = BSConSign::createcosign_attestation($digest, \@attestations);
my $mani_id = create_cosign_manifest($repodir, $oci, $knownmanifests, $knownblobs, $config, @attestation_layers);
my @attestation_ents = BSConSign::create_cosign_attestation_ents(\@attestations);
my $mani_id = create_cosign_manifest($repodir, $oci, $knownmanifests, $knownblobs, @attestation_ents);
$sigs->{'attestations'}->{$digest} = $mani_id;
if ($rekorserver) {
print "uploading cosign attestation to $rekorserver\n";
Expand Down
8 changes: 6 additions & 2 deletions src/backend/bs_notar
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,6 @@ use MIME::Base64 ();
use Digest::SHA ();
use Data::Dumper;

use BSConfiguration; # for BSConfig::sign
use BSRPC ':https';
use BSUtil;
use BSPGP;
Expand All @@ -49,7 +48,7 @@ my $targets_expire = 3 * 366 * 24 * 3600; # 3 years
my $notary_timeout = 300;
my $registry_timeout = 300;

my @signcmd = ( $BSConfig::sign );
my @signcmd;

sub multipartentry {
my ($name, $d) = @_;
Expand Down Expand Up @@ -258,6 +257,11 @@ sub getoldkey {
return BSRPC::rpc($param, \&JSON::XS::decode_json);
}


require BSConfiguration; # for BSConfig::sign
die("sign program is not configured!\n") unless $BSConfig::sign;
unshift @signcmd, $BSConfig::sign;

my $signfunc = sub { BSUtil::xsystem($_[0], @signcmd, '-O', '-h', 'sha256') };
#
# generate key material
Expand Down
30 changes: 14 additions & 16 deletions src/backend/bs_regpush
Original file line number Diff line number Diff line change
Expand Up @@ -259,18 +259,16 @@ sub manifest_upload_tags {
}

sub cosign_upload {
my ($tag, $config, @layers) = @_;
my ($tag, @layer_ents) = @_;
my $oci = 1;
my ($config_ent, $config_blobid) = BSContar::make_blob_entry('config.json', $config);
my $config_ent = BSConSign::create_cosign_config_ent(\@layer_ents);
my $config_data = BSContar::create_config_data($config_ent, $oci);
blob_upload($config_blobid, $config_ent);
blob_upload($config_data->{'digest'}, $config_ent);
my @layer_data;
while (@layers >= 2) {
my ($payload_layer_data, $payload) = splice(@layers, 0, 2);
my ($payload_ent, $payload_blobid) = BSContar::make_blob_entry($payload_layer_data->{'digest'}, $payload);
die unless $payload_blobid eq $payload_layer_data->{'digest'};
blob_upload($payload_blobid, $payload_ent);
push @layer_data, $payload_layer_data;
for my $layer_ent (@layer_ents) {
my $layer_data = BSContar::create_layer_data($layer_ent, $oci);
push @layer_data, $layer_data;
blob_upload($layer_data->{'digest'}, $layer_ent);
}
my $mani = BSContar::create_dist_manifest_data($config_data, \@layer_data, $oci);
my $mani_json = BSContar::create_dist_manifest($mani);
Expand Down Expand Up @@ -798,7 +796,6 @@ for my $tarfile (@tarfiles) {
my $layer_data = BSContar::create_layer_data($layer_ent, $oci);
push @layer_data, $layer_data;
$layer_datas{$layer_file} = $layer_data;

# upload to server
blob_upload($layer_data->{'digest'}, $layer_ent);
}
Expand Down Expand Up @@ -865,7 +862,7 @@ if ($writeinfofile) {
if ($cosign && %digests_to_sign) {
my $creator = 'OBS';
my $gpgpubkey = readstr($pubkeyfile);
$cosigncookie ||= BSConSign::createcosigncookie($gpgpubkey, $gun, $creator);
$cosigncookie ||= BSConSign::create_cosign_cookie($gpgpubkey, $gun, $creator);
# upload signatures
for my $digest (sort keys %digests_to_sign) {
my $sig_tag = "$digest.sig";
Expand All @@ -878,13 +875,14 @@ if ($cosign && %digests_to_sign) {
print "creating cosign signature for $gun $digest\n";
my $signfunc = sub { BSUtil::xsystem($_[0], @signcmd, '-O', '-h', 'sha256') };
my $annotations = { $cosign_cookie_name => $cosigncookie };
my ($config, $payload_layer_data, $payload, $sig) = BSConSign::createcosign($signfunc, $digest, $gun, $creator, undef, $annotations);
cosign_upload($sig_tag, $config, $payload_layer_data, $payload);
my ($cosign_ent, $sig) = BSConSign::create_cosign_signature_ent($signfunc, $digest, $gun, $creator, undef, $annotations);
cosign_upload($sig_tag, $cosign_ent);
if ($rekorserver) {
print "uploading cosign signature to $rekorserver\n";
my $sslpubkey = BSX509::keydata2pubkey(BSPGP::pk2keydata(BSPGP::unarmor($gpgpubkey)));
$sslpubkey = BSASN1::der2pem($sslpubkey, 'PUBLIC KEY');
BSRekor::upload_hashedrekord($rekorserver, $payload_layer_data->{'digest'}, $sslpubkey, $sig);
my $hash = 'sha256:'.Digest::SHA::sha256_hex($cosign_ent->{'data'}); # must match signfunc
BSRekor::upload_hashedrekord($rekorserver, $hash, $sslpubkey, $sig);
}
}
# upload attestations
Expand All @@ -907,8 +905,8 @@ if ($cosign && %digests_to_sign) {
push @attestations, BSConSign::fixup_intoto_attestation($provenance, $signfunc, $digest, $gun) if $provenance;
push @attestations, BSConSign::fixup_intoto_attestation($spdx_json, $signfunc, $digest, $gun) if $spdx_json;
push @attestations, BSConSign::fixup_intoto_attestation($cyclonedx_json, $signfunc, $digest, $gun) if $cyclonedx_json;
my ($config, @attestation_layers) = BSConSign::createcosign_attestation($digest, \@attestations, $annotations);
cosign_upload($att_tag, $config, @attestation_layers);
my @attestation_ents = BSConSign::create_cosign_attestation_ents(\@attestations, $annotations);
cosign_upload($att_tag, @attestation_ents);
if ($rekorserver) {
print "uploading cosign attestations to $rekorserver\n";
my $sslpubkey = BSX509::keydata2pubkey(BSPGP::pk2keydata(BSPGP::unarmor($gpgpubkey)));
Expand Down

0 comments on commit d2a3f46

Please sign in to comment.