From d616977904781aca4a39b025b87915dbfe09ce49 Mon Sep 17 00:00:00 2001 From: Tim Foerster Date: Tue, 3 Mar 2026 12:58:43 +0000 Subject: [PATCH] Add SOURCE_DATE_EPOCH support for GPG signers Both the external GPG signer (--faked-system-time) and internal Go OpenPGP signer (signerConfig.Time) now honor SOURCE_DATE_EPOCH, producing reproducible signatures alongside the plain Release file dates. Adds system tests for both signer backends verifying byte-identical Release, Release.gpg and InRelease across repeated publishes. The signer tests (PublishRepo3[78]Test) are using an ed25519 key because ed25519 signatures are deterministic by design. The Go openpgp library uses a random nonce for DSA/ECDSA (see signature.go Sign calls using config.Random() link below) so those signatures vary across runs even with a fixed timestamp, making byte-identical verification impossible. In addition to 49f342878ad2b220d2f870dc69f71ec05e26f42e Ref: https://github.com/aptly-dev/aptly/pull/1537 Ref: https://github.com/ProtonMail/go-crypto/blob/v1.4.0/openpgp/packet/signature.go#L945-L979 --- go.mod | 2 +- go.sum | 19 +----- pgp/gnupg.go | 7 ++ pgp/internal.go | 9 +++ system/files/aptly-dual-binary.pub | Bin 0 -> 1169 bytes system/files/aptly3-binary.sec | Bin 0 -> 291 bytes system/t06_publish/PublishRepo38Test_gold | 14 ++++ system/t06_publish/PublishRepo39Test_gold | 14 ++++ system/t06_publish/repo.py | 74 ++++++++++++++++++++++ 9 files changed, 121 insertions(+), 18 deletions(-) create mode 100644 system/files/aptly-dual-binary.pub create mode 100644 system/files/aptly3-binary.sec create mode 100644 system/t06_publish/PublishRepo38Test_gold create mode 100644 system/t06_publish/PublishRepo39Test_gold diff --git a/go.mod b/go.mod index d76ce5eb..fbcfaaa6 100644 --- a/go.mod +++ b/go.mod @@ -118,7 +118,7 @@ require ( require ( github.com/Azure/azure-sdk-for-go/sdk/azcore v1.14.0 github.com/Azure/azure-sdk-for-go/sdk/storage/azblob v1.4.1 - github.com/ProtonMail/go-crypto v1.0.0 + github.com/ProtonMail/go-crypto v1.4.0 github.com/aws/aws-sdk-go-v2 v1.32.5 github.com/aws/aws-sdk-go-v2/config v1.28.5 github.com/aws/aws-sdk-go-v2/credentials v1.17.46 diff --git a/go.sum b/go.sum index d2475f90..9afb313b 100644 --- a/go.sum +++ b/go.sum @@ -18,8 +18,8 @@ github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55 h1:jbG github.com/DisposaBoy/JsonConfigReader v0.0.0-20171218180944-5ea4d0ddac55/go.mod h1:GCzqZQHydohgVLSIqRKZeTt8IGb1Y4NaFfim3H40uUI= github.com/KyleBanks/depth v1.2.1 h1:5h8fQADFrWtarTdtDudMmGsC7GPbOAu6RVB3ffsVFHc= github.com/KyleBanks/depth v1.2.1/go.mod h1:jzSb9d0L43HxTQfT+oSA1EEp2q+ne2uh6XgeJcm8brE= -github.com/ProtonMail/go-crypto v1.0.0 h1:LRuvITjQWX+WIfr930YHG2HNfjR1uOfyf5vE0kC2U78= -github.com/ProtonMail/go-crypto v1.0.0/go.mod h1:EjAoLdwvbIOoOQr3ihjnSoLZRtE8azugULFRteWMNc0= +github.com/ProtonMail/go-crypto v1.4.0 h1:Zq/pbM3F5DFgJiMouxEdSVY44MVoQNEKp5d5QxIQceQ= +github.com/ProtonMail/go-crypto v1.4.0/go.mod h1:e1OaTyu5SYVrO9gKOEhTc+5UcXtTUa+P3uLudwcgPqo= github.com/PuerkitoBio/purell v1.1.1 h1:WEQqlqaGbrPkxLJWfBwQmfEAE1Z7ONdDLqrN38tNFfI= github.com/PuerkitoBio/purell v1.1.1/go.mod h1:c11w/QuzBsJSee3cPx9rAFu61PvFxuPbtSwDGJws/X0= github.com/PuerkitoBio/urlesc v0.0.0-20170810143723-de5bf2ad4578 h1:d+Bc7a5rLufV/sSk/8dngufqelfh6jnri85riMAaF/M= @@ -64,7 +64,6 @@ github.com/aws/smithy-go v1.22.1 h1:/HPHZQ0g7f4eUeK6HKglFz8uwVfZKgoI25rb/J+dnro= github.com/aws/smithy-go v1.22.1/go.mod h1:irrKGvNn1InZwb2d7fkIRNucdfwR8R+Ts3wxYa/cJHg= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= -github.com/bwesterb/go-ristretto v1.2.3/go.mod h1:fUIoIZaG73pV5biE2Blr2xEzDoMj7NFEuV9ekS419A0= github.com/bytedance/sonic v1.5.0/go.mod h1:ED5hyg4y6t3/9Ku1R6dU/4KyJ48DZ4jPhfY1O2AihPM= github.com/bytedance/sonic v1.9.1 h1:6iJ6NqdoxCDr6mbY8h18oSO+cShGSMRGCEo7F2h0x8s= github.com/bytedance/sonic v1.9.1/go.mod h1:i736AoUSYt75HyZLoJW9ERYxcy6eaN6h4BZXU064P/U= @@ -322,14 +321,11 @@ golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACk golang.org/x/crypto v0.0.0-20191011191535-87dc89f01550/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI= golang.org/x/crypto v0.0.0-20200622213623-75b288015ac9/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/crypto v0.0.0-20210921155107-089bfa567519/go.mod h1:GvvjBRRGRdwPK5ydBHafDWAxML/pGHZbMvKqRZ5+Abc= -golang.org/x/crypto v0.3.1-0.20221117191849-2c476679df9a/go.mod h1:hebNnKkNXi2UzZN1eVRvBB7co0a+JxK6XbPiWVs/3J4= -golang.org/x/crypto v0.7.0/go.mod h1:pYwdfH91IfpZVANVyUOhSIPZaFoJGxTFbZhFTx+dXZU= golang.org/x/crypto v0.45.0 h1:jMBrvKuj23MTlT0bQEOBcAE0mjg8mK9RXFhRH6nyF3Q= golang.org/x/crypto v0.45.0/go.mod h1:XTGrrkGJve7CYK7J8PEww4aY7gM3qMCElcJQ8n8JdX4= golang.org/x/mod v0.2.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.3.0/go.mod h1:s0Qsj1ACt9ePp/hMypM3fl4fZqREWJwdYDEqhRiZZUA= golang.org/x/mod v0.6.0-dev.0.20220419223038-86c51ed26bb4/go.mod h1:jJ57K6gSWd91VN4djpZkiMVwK6gcyfeH4XE8wZrZaV4= -golang.org/x/mod v0.8.0/go.mod h1:iBbtSCu2XBx23ZKBPSOrRkjjQPZFPuis4dIYUhu/chs= golang.org/x/mod v0.29.0 h1:HV8lRxZC4l2cr3Zq1LvtOsi/ThTgWnUk/y64QSs8GwA= golang.org/x/mod v0.29.0/go.mod h1:NyhrlYXJ2H4eJiRy/WDBO6HMqZQ6q9nk4JzS3NuCK+w= golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= @@ -344,10 +340,7 @@ golang.org/x/net v0.0.0-20210428140749-89ef3d95e781/go.mod h1:OJAsFXCWl8Ukc7SiCT golang.org/x/net v0.0.0-20220225172249-27dd8689420f/go.mod h1:CfG3xpIq0wQ8r1q4Su4UZFWDARRcnwPjda9FqA0JpMk= golang.org/x/net v0.0.0-20220607020251-c690dde0001d/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= golang.org/x/net v0.0.0-20220722155237-a158d28d115b/go.mod h1:XRhObCWvk6IyKnWLug+ECip1KBveYUHfp+8e9klMJ9c= -golang.org/x/net v0.2.0/go.mod h1:KqCZLdyyvdV855qA2rE3GC2aiw5xGR5TEjj8smXukLY= -golang.org/x/net v0.6.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= golang.org/x/net v0.7.0/go.mod h1:2Tu9+aMcznHK/AK1HMvgo6xiTLG5rD5rZLDS+rp2Bjs= -golang.org/x/net v0.8.0/go.mod h1:QVkue5JL9kW//ek3r6jTKnTFis1tRmNAW2P1shuFdJc= golang.org/x/net v0.47.0 h1:Mx+4dIFzqraBXUugkia1OOvlD6LemFo1ALMHjrXDOhY= golang.org/x/net v0.47.0/go.mod h1:/jNxtkgq5yWUGYkaZGqo27cfGZ1c5Nen03aYrrKpVRU= golang.org/x/oauth2 v0.33.0 h1:4Q+qn+E5z8gPRJfmRy7C2gGG3T4jIprK6aSYgTXGRpo= @@ -358,7 +351,6 @@ golang.org/x/sync v0.0.0-20190911185100-cd5d95a43a6e/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20200317015054-43a5402ce75a/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20201020160332-67f06af15bc9/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.0.0-20220722155255-886fb9371eb4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= -golang.org/x/sync v0.1.0/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sync v0.18.0 h1:kr88TuHDroi+UVf+0hZnirlk8o8T+4MrK6mr60WkH/I= golang.org/x/sync v0.18.0/go.mod h1:9KTHXmSnoGruLpwFjVSX0lNNA75CykiMECbovNTZqGI= golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -383,26 +375,20 @@ golang.org/x/sys v0.0.0-20220520151302-bc2c85ada10a/go.mod h1:oPkhp1MJrh7nUepCBc golang.org/x/sys v0.0.0-20220704084225-05e143d24a9e/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220722155257-8c9f86f7a55f/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.0.0-20220811171246-fbc7d0a398ab/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.2.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= -golang.org/x/sys v0.3.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.5.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg= golang.org/x/sys v0.38.0 h1:3yZWxaJjBmCWXqhN1qh02AkOnCQ1poK6oF+a7xWL6Gc= golang.org/x/sys v0.38.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo= golang.org/x/term v0.0.0-20210927222741-03fcf44c2211/go.mod h1:jbD1KX2456YbFQfuXm/mYQcufACuNUgVhRMnK/tPxf8= -golang.org/x/term v0.2.0/go.mod h1:TVmDHMZPmdnySmBfhjOoOdhjzdE1h4u1VwSiw2l1Nuc= golang.org/x/term v0.5.0/go.mod h1:jMB1sMXY+tzblOD4FWmEbocvup2/aLOaQEp7JmGp78k= -golang.org/x/term v0.6.0/go.mod h1:m6U89DPEgQRMq3DNkDClhWw02AUbt2daBVO4cn4Hv9U= golang.org/x/term v0.37.0 h1:8EGAD0qCmHYZg6J17DvsMy9/wJ7/D/4pV/wfnld5lTU= golang.org/x/term v0.37.0/go.mod h1:5pB4lxRNYYVZuTLmy8oR2BH8dflOR+IbTYFD8fi3254= golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ= golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.6/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ= golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ= -golang.org/x/text v0.4.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= golang.org/x/text v0.7.0/go.mod h1:mrYo+phRRbMaCq/xk9113O4dZlRixOauAjOtrjsXDZ8= -golang.org/x/text v0.8.0/go.mod h1:e1OnstbJyHTd6l/uOt8jFFHp6TRDWZR/bV3emEE/zU8= golang.org/x/text v0.31.0 h1:aC8ghyu4JhP8VojJ2lEHBnochRno1sgL6nEi9WGFGMM= golang.org/x/text v0.31.0/go.mod h1:tKRAlv61yKIjGGHX/4tP1LTbc13YSec1pxVEWXzfoeM= golang.org/x/time v0.5.0 h1:o7cqy6amK/52YcAKIPlM3a+Fpj35zvRj2TP+e1xFSfk= @@ -413,7 +399,6 @@ golang.org/x/tools v0.0.0-20200619180055-7c47624df98f/go.mod h1:EkVYQZoAsY45+roY golang.org/x/tools v0.0.0-20201224043029-2b0845dc783e/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.0.0-20210106214847-113979e3529a/go.mod h1:emZCQorbCU4vsT4fOWvOPXz4eW1wZW4PmDk9uLelYpA= golang.org/x/tools v0.1.12/go.mod h1:hNGJHUnrk76NpqgfD5Aqm5Crs+Hm0VOH/i9J2+nxYbc= -golang.org/x/tools v0.6.0/go.mod h1:Xwgl3UAJ/d3gWutnCtw505GrjyAbvKui8lOU390QaIU= golang.org/x/tools v0.38.0 h1:Hx2Xv8hISq8Lm16jvBZ2VQf+RLmbd7wVUsALibYI/IQ= golang.org/x/tools v0.38.0/go.mod h1:yEsQ/d/YK8cjh0L6rZlY8tgtlKiBNTL14pGDJPJpYQs= golang.org/x/xerrors v0.0.0-20190717185122-a985d3407aa7/go.mod h1:I/5z698sn9Ka8TeJc9MKroUUfqBBauWjQqLJ2OPfmY0= diff --git a/pgp/gnupg.go b/pgp/gnupg.go index bab2db21..59e405f7 100644 --- a/pgp/gnupg.go +++ b/pgp/gnupg.go @@ -9,6 +9,7 @@ import ( "os" "os/exec" "path/filepath" + "strconv" "strings" ) @@ -89,6 +90,12 @@ func (g *GpgSigner) gpgArgs() []string { } } + if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { + if _, err := strconv.ParseInt(epoch, 10, 64); err == nil { + args = append(args, "--faked-system-time", epoch) + } + } + return args } diff --git a/pgp/internal.go b/pgp/internal.go index 4998697b..8bf214c4 100644 --- a/pgp/internal.go +++ b/pgp/internal.go @@ -7,6 +7,7 @@ import ( "os" "path/filepath" "sort" + "strconv" "strings" "syscall" "time" @@ -74,6 +75,14 @@ func (g *GoSigner) Init() error { }, } + if epoch := os.Getenv("SOURCE_DATE_EPOCH"); epoch != "" { + if sec, err := strconv.ParseInt(epoch, 10, 64); err == nil { + t := time.Unix(sec, 0).UTC() + g.signerConfig.Time = func() time.Time { return t } + g.signerConfig.NonDeterministicSignaturesViaNotation = packet.BoolPointer(false) + } + } + if g.passphraseFile != "" { passF, err := os.Open(g.passphraseFile) if err != nil { diff --git a/system/files/aptly-dual-binary.pub b/system/files/aptly-dual-binary.pub new file mode 100644 index 0000000000000000000000000000000000000000..834244a497a2b5cce824412056a220f028950317 GIT binary patch literal 1169 zcmbQqxQHd__tJhr7KWWWD(t^-x2bPreJiGW`n(6v_Er0W@4b(e+t#wiEeSw+{CZQg3J;;r$G@DH-N9!@*YEN58oR75do zwPmN%T)ta!PNqL+W(c$yvM}@(*Ouz)P_bslJ=biiCDVihP zJN>icq9VTa&Wm>H+s+URVC&XVkC$+HrEhR5%Vv42Zk<*6QHMM4Cw=615t+?B(@x@H znQ)-(n$+9(7-g9^M8Mn z0O^WpWp9e}&NVJ)T&vkR;dBK9`-ZcjFHh+h$z8e9meo65+dw}e=8pRf`x3ifjQeKK z=QvWV|NUIZm#^w3Y)MTW@o{HoTi;#%ZAY@wHJ1mE_om+c-gc`x^=`PXId4b&@}qts zQ+LTd5dSi{%3$dwbFoEv%>BLoW;beLuPJmmZ_#xuD9Nc*2uUq2Ni9;)NXgGrFHtBh zPF2V((NwT00SY@Lf>h{b=B4G^btJI}3oTmPuNfjhmB$otcSEl!J+ri-#RJT#>vqXbj<~fb>2dR_M-sOfX0xBO z_taoKvh>ErC--i@V37b%)x7Z#TF67t@*06S(I@&i0q*ch7kIJv3v> zbh{$4RrB00JBvL!?9JTL?RoHQaavc>r(Z$_h6^9(GCkIL@a*)LzXuj9sNn3nt|9!g z!A)iQQ$MkiC+AK4XE<&Zusxr?!mrjKPp?NW;s4r=y1zpCYZWZQy~{d0StNjA&It>1 z9^|04Y-3okw{2}|Nwna#+G|EmPP$&oJ3XKFGt9GoE$gfBxwGZ*6iLw+lIKk{zHMJR z!Iua?uQn&2nO1t@ zyR6R^eMk}sPEF3wOGzxMR0z&Y&&$k9SMW})RImZ34u{l=#N2|MRK4Wlyw#OlVfWwaC(MS;D!U=eKX^y}mSm$*D}+z3rEB`CoZn&}8_-Q_fwqt6_S& ea)I~qZU25Yygafm`7pz~N!Pa91hI>ivH$?8mi_kt literal 0 HcmV?d00001 diff --git a/system/files/aptly3-binary.sec b/system/files/aptly3-binary.sec new file mode 100644 index 0000000000000000000000000000000000000000..4fd637ed08323f88445c208aea701882f18b508a GIT binary patch literal 291 zcmbOd!IECa zD?RaD)`x-Nf9l2=ELU6PGxZB=gC}*D|GgIW>U#Z@FTr;Yi$4;ywaXFA-lFeVP?A%r z5S*HvpO=zY1Y~8V=Vj)lD|n|?D%d1~Bpp&K5_1c3QuUJabL~2&u?UNCG1!SIvaEZU z`$59gKmA{POwsNy52u*Cl=EX{0y&gPnwgc8`A|9oD>o+xI}@8I7bgcd6N?xVGb586 zBa?Uo0~e41aja kxr=r+Oix!X@Ls;{-_M4ZNA@KjW_UO0+IE{DcCk_x0O