From 25e800a59c492052fae870e5e5f4b1e47472b692 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 12:43:12 -0400 Subject: [PATCH 01/11] Re-generate 'tests/repository_data' to include metadata unix timestamps. --- .../client/metadata/current/root.json | Bin 3781 -> 3766 bytes .../client/metadata/current/snapshot.json | Bin 1396 -> 1381 bytes .../client/metadata/current/targets.json | Bin 1954 -> 1939 bytes .../client/metadata/current/targets.json.gz | Bin 1209 -> 1205 bytes .../metadata/current/targets/role1.json | Bin 986 -> 971 bytes .../client/metadata/current/timestamp.json | Bin 934 -> 919 bytes .../client/metadata/previous/root.json | Bin 3781 -> 3766 bytes .../client/metadata/previous/snapshot.json | Bin 1396 -> 1381 bytes .../client/metadata/previous/targets.json | Bin 1954 -> 1939 bytes .../client/metadata/previous/targets.json.gz | Bin 1209 -> 1205 bytes .../metadata/previous/targets/role1.json | Bin 986 -> 971 bytes .../client/metadata/previous/timestamp.json | Bin 934 -> 919 bytes tests/repository_data/generate.py | 11 ++-- tests/repository_data/keystore/delegation_key | 52 +++++++++--------- .../keystore/delegation_key.pub | 14 ++--- tests/repository_data/keystore/root_key | 52 +++++++++--------- tests/repository_data/keystore/root_key.pub | 14 ++--- tests/repository_data/keystore/snapshot_key | 52 +++++++++--------- .../repository_data/keystore/snapshot_key.pub | 14 ++--- tests/repository_data/keystore/targets_key | 52 +++++++++--------- .../repository_data/keystore/targets_key.pub | 14 ++--- tests/repository_data/keystore/timestamp_key | 52 +++++++++--------- .../keystore/timestamp_key.pub | 14 ++--- .../repository/metadata.staged/root.json | Bin 3781 -> 3766 bytes .../repository/metadata.staged/snapshot.json | Bin 1396 -> 1381 bytes .../repository/metadata.staged/targets.json | Bin 1954 -> 1939 bytes .../metadata.staged/targets.json.gz | Bin 1209 -> 1205 bytes .../metadata.staged/targets/role1.json | Bin 986 -> 971 bytes .../repository/metadata.staged/timestamp.json | Bin 934 -> 919 bytes .../repository/metadata/root.json | Bin 3781 -> 3766 bytes .../repository/metadata/snapshot.json | Bin 1396 -> 1381 bytes .../repository/metadata/targets.json | Bin 1954 -> 1939 bytes .../repository/metadata/targets.json.gz | Bin 1209 -> 1205 bytes .../repository/metadata/targets/role1.json | Bin 986 -> 971 bytes .../repository/metadata/timestamp.json | Bin 934 -> 919 bytes .../{ => deprecated}/test_util_test_tools.py | 0 36 files changed, 171 insertions(+), 170 deletions(-) rename tests/unit/{ => deprecated}/test_util_test_tools.py (100%) diff --git a/tests/repository_data/client/metadata/current/root.json b/tests/repository_data/client/metadata/current/root.json index bfe2d99035e399db0dcc288ddf034528d535dd9a..448c2d0af83146ae98debd826aaf089c7178551b 100644 GIT binary patch literal 3766 zcmd6q+pgj`7KZQZDYo`4)53??vHLDSfN&<^Jd9dSY(qFCfh0g^)w_?`yQ_oJ#}X`Z|ztPlkxvIve8 zWt0($p@I=5z{Y}8tvL#HD6kbpp#`CYSSTVb70L)G4Br3t_A%gD75nsKL_ctX!1-ws z1mD|?hSyXf#3(`lg;GLd3#dpKQ4K6cIkzIE1lCMp!l>lf0+xTkkQj!@ zvMsJS*0v%LF{rgxAmj*P$)Q%<=2!s26f%7Y0z}J*2xx`EXu{geVLC>NL)&`E0U|Ox z0)Q1sDK){`LN-;}YzjsMaHC(Dr_#vcwvvMCh@voJj1#j}*enTfq{5I&z|7WB!igkA zbBseFg$)6;g>3>Gam1+L5GjUAWC-64C||$4{o_T<2bb!_%ZtbNUy3SIFGl<{ zEk3b%kJ2QM^Fk%XFL@%ed~aHQe$(QVs}JVh)gy~PxrWRTLnr{?v&GQ;!u=EVA13_U zKp%(ZyUuGazR~^h_$E%@=QFFx%J39NujTyo`ZU~*H+q}Q8w0QT)_2z*54XwC^BUg4 z88vpt%l;U5_#HHysoQp(anqSH=hfca_050Rc`P=^@idg>D6CXn-stUe-zLl9vNP(n z8rdvcG_LOP={#F?=C4?p0zL~Jw_d5Wv|QeQTKBGh-Y<)}gQu7EWv*TOw%R7UL%++i zgr#!jY);n^^}@)VP<$M#p5rsm598S-7^mTh^I#d5_JbDfhjYJ4Fg~jB)2!m^Pg~W~ z9mIJs7!J?16&UMf{dq2wUUp&UP~Gp1D_Fbrg+4BE2VijV>dAE2&Z^FQr?$zVzs{>h z1!(Vp4);;7RQ%lFa6!|)*Phw^{F-|@Jd$%tI?G~qD2nJQymRRYeF-tyfjg*5vqukmMVtN8n>>4$D&belhNI4Ia8;x zO+JF-j#~d3uNS-@_?n+@DUT70F`IKJBAX+NY9)lWg^aL>AXO?d>C2p)kPF0tNdPtg zN?O#sim5r@=r>|s+4wI4t7;A6?&UPNnw;w$Fu~8t+a^!@*v*Q#rrk{@p4He-r^gmm zE01gz`{t=9p6LL=PA6g0s5|SQL%WmtPJF?c4EHx{xlQ~zYQ(pzheg^7Ziw2KJY3#) zi}W;?*UkBG0ZwnwTm=>Hg#ZLumBXxfALUebIvljB;zsvY!s-5;)qb=)R_daQy+3-kc=QKB1e|DJR$ zvLD=5vyEWqXf03YoG^fl&wp!;`SI zme?|tiBg7MDP96aP4xjn>c|$DAgM7@FcgHFI{H1UewO*a2&|LO8m*?6PkOygvs_c! zZ8+P+^N&92O`mNJ`-R_fc1P(4_4#a(CFg0kGnSnhmq4tWF&WotohkJ0XY9_%tS*-0 z);ilJv~Zp4U|z=OT5V}pH1?XUSz{095VgZmE9jL0qh-A5*hmk{^Rax^Ew;%{yXW5S zW;c_u^mHcXr?X!_C6i@|FN3OK*LNX{UEh7~66eyNY=*VL9@f{xYRCB_3{eHIEv7+BOs7%Y0Dy985cxsKp_=UDVs)+VITk!j2Wg(#)={djZLz_ z|3k*gh*M-bAl5pNV*gmXleIaiqfMIU&Dzw)a=m%pP7$;(Fe_)#vDKc8qu{vOUq_Q# zufGISyz#z-1PN|FYZb;clBv&en_Apy)lRmd}H(sikEO5C-zD- zp6sIln4aK+yEzxzkzd8*E~*>&(MAS^7WrC>qN}} literal 3781 zcmd5cLL!ahEe7C0s#_kAzZ5d_c5Gvs%kWv>GV8I z5uhke_FmgwUVHhMA3i=W={gt1^{g+SKYjfDhmVh6?C;}qryr@ZcRn4-l+X!ASP7t% zgf60#7^JWu*hM&T9ET(dJH{mfN}fo^;Y?zJ?Xytu&%b@V17^C|9NrN}V{bh6ejJX+ z-^MJ4S1Kl{jY$Y+nh~BrffJwsw(*gx5n{k`1*Q}u$T?#q0X#vB1Dl>Of^AaQF@zh< z5my{KSU3(P0x=B?lR{w|NxQ(|P#A!Lb`)|G2|2Z31tZ$kN;9aiqL3kl5`t|`gaD>o z3J#G<9Ks|LSONl=i=~?gmnfEKpcQfiQ+7V6kv1bX33d>bTyv?grh+45p-Ux^bRd*=avhEm3rI0!fJ_)rnm`T^cZ{6_X_zpC z08XKSwra;-Ae}fUG4^!?xPXca!yqs&vc(!q0T2vmsU2-6jA{viMAUVqWAPYfpR$EB z$4~|!w=Xj7VgNPfAQ46?M2I4gQUi5j^Chl^N^9*h;o1~NVPuF=7J@`f+I4gpeG!4# z(v+~!2-DJG*j2(_3k%xS0%}F{rzOg-KYaY-#h5oQ^@}erK7RgOJdXOskXD(QnRu>iaSM!s|~282b@G`~3jmr}y_`I`O|ovSfd; z{|ocKoA6%)z4y>J*yoG*O7{2dQtZFwv&9{+$v%~@>VABEYC*FdehjCzUfciZ2J!pm zD(|=3wf5EvYwO)0hG8?Oa9_8r}ZIQPZgjH^qWHvOaU3-5e{(RAdS3cCXCp;cb+ zQF(WDFUUvk_K1@J-u&l4tv8~3I;s)NB=K&feHuMw-bpcDIP*=trk%b&3*v`xR{6A; z)M;F8EQ=#p9$HS`W@VgcOXgdhbQHlRK+i?ak4W{@GsCA<-tlNUYtDxODZ}CJG>FZD zX4zp&ooadPvlD1;l9X3am6jli_oKBIBZmA+J2!d&%XF1L=v-b=-*waZeVio?aN8(a z^A;j(?H-2~^MbvjhO>KXUBs+IpDI2K&wdE1`@55^r@d8v<|dGn)1wm6ZWTPc!$FhN z#svDO(IJMHau~)#%vxEJPohb+492&Et0viG3Wv8|1$9?>`Dciga6?}m1tDpq#?*?l zCMbc(p)S;1SUuJ*AyR1DfsB(lfC|A~ZTlT%wtM~+h;^Wit2bHiALG%0S1;}|2&vtv zadF$q3k_=ndhNG|`3_ZrJlyxrA<5(%>xMArZn#)<;%KoPR;mdqKo?%p-01+OXRCZa zi4Kl48Wi)vtQ+>)lV)rh`lc_=?=tVw*U>=0P1X;0D3a@Mc&?4WrsNy)2xl%%D;&2)4(0BYh5wTfWO+Vu^M8 z6N+WoM6?54j9sD)O{^p-?h>l3E74Fe!L^23V(XqLFbM%z*(p*I?g=nzc($1)Wc%cyH>)i>>&Zn=sEf9-Ph0tNoz((Xwq)idCZ9g#loq{N zj`4lxh~0DEtWDa%*|p@Z_1rtl`|ECa>OJ=De!V@bwWjTAW9Rkzxsfv7^rCUbAJv)H zs9xulYI&ve$u@fA`@oCsqL$I})Vue5o@FsitGihDl1t~lJlvj*8|j+aBI<4RY>N>% zvmQ`9KJV#-FI%p6_YYR{w!VAvX4MG~hSkP6$Q}~|`iZ}+&+3c)=$48K2$Bk!?&hOj zbGPlR%k{>uK7Bc;_w;>HuJX$Nq*yP9_r;u@5Bv9#<=okw1$k?;0o z?{54ymalNd#@fQoVgIt1@HO*}O?$;lF{U;@tK6d~B diff --git a/tests/repository_data/client/metadata/current/snapshot.json b/tests/repository_data/client/metadata/current/snapshot.json index 3e46e1fd7e3f610b04e4c8f5cd4fc61efcaf3990..2b3323dd90d7c44cb2a369f9cae10006ea6046a7 100644 GIT binary patch literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 literal 1396 zcma)+!EPKk42JK1iou+dl%hn6$}Q*%6y5ez6a^_!Ubl(Y*xeRQ1HXGYW2fjL28^8< zEJlL(^Z!52mz!n1+}+;m_0O|k*7wUVH_P%R?q&VMKi`dbvs#-qS0cndyO&C-I-w=Z zm6JEcNzO!Fz4kG)WvHr+-G^7nWb2z{Kk$=ZkEi{_9}gc6hYxRmI2^vNg@&zEX(;sIED@K=OWsWg&@x*z|0!Fa~yjXIY44RE>3bve^_1IfE zq=8UYWYe4on`ACCEb>Ie7E*bJbMDj!C+wwi<(5rqt1M=%xD*%wAW51@D-@z}M#I`C zXKG_|AsLZI5rvr$2oR*DQuDAlDg38V^570isWCYTG8&}Zc}5k=JaHn;C|=U+**SN^ z(Y1*dOB8@s(Px^`gSyNiWUvM(k2lM&8!|X+@i10OzIlZ)i5AZ3Aotm2(PC&H4FQ##Ds{uHT1l-6^%X3!Lokz z``ha=4$f85w)DKRUiEzI*URgL)!yc;8{m|^2f-SE#xJ4|e+AHF=9mSKzb!$ba6UuN zJqF2gH1~qA)Tq29?A`4@uQIJN$|#fK!@)Y-0QEU?GmJa}aX#4Jb`snHP)(MdgW#-X^4LZs;6{+?3h6S%>Q1IG0oT}K3q{UnWy)k^- hV3i%!|C0M(rcHQP7mue{*T4LHx!XyLupe(8{{>5zX`%oC diff --git a/tests/repository_data/client/metadata/current/targets.json b/tests/repository_data/client/metadata/current/targets.json index 067100674e3d7e2c7ab3176430787ac05029a0ee..2c60420ffde3e41c912b5608935066625af10040 100644 GIT binary patch delta 1109 zcmbu7u~wru5QR^Y-4xkkA0p`}ENP^X)Gc5G#>O@n+rXjGj7A1yY%nb0r*WC>k{sS3 z50EA=808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jci?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+ng^7 zHfGSY8}Vmvoz=t3;(B$uycl)u-rn@Y-JR&W(ZaM|H~PXaR_8Vxh>4x2@#X8j%-73F zSgdE`*6gl#KkH1Fi(}IsAZ5zraR604lj>8%k|UKTQFQ62aB8O@OjjI+^p}z zw$=B;?kwN-E?&}lV_T{<*m=Er-W^}B#`}qT@%Z-4{yMqo_|9?S$>r!^JZV;&)P(i) zmUkxI?sRl`(Z311Gj})}-#ri7cKx(oH%-4P^!!=tcJy^%?C@>g8!Y^^+}_M#7sgei zz1wQ@bouc0d^0?V-2*qfJid56O8b}RgO}dkvVHJ)y=q5j-=DMxqoXnO)ro&Oo%U|t zIxR=x2elwr%JWgJ_&%=jid9^9MhpXAc(c>8%p7dL@p1)r8-amch|9`B%KYsg3 Qhggq?N~wQ_KaRiq7scgE!2kdN delta 1124 zcmbu6yKd*-&9>IgC>K3FcSjNkgoGC%EF_^KnM=1oh##?BrG#)dvt4=v zKY$azpvQg%S4~YaJ=N#?&ggcw2&-s_ymvV&-mV-nVw=yd`QQh;{CMQ5B-ln+@UcwJT-V90@NW*@Yp z7!{K=QEM~-l0hkng60%-0Yw{2SkNgr0P;vW7goqxhC<~%h(cXhDaAmkyh$by+J-1h z5IL1AiR`>O$dbop;{pcjlZY6DB$*MFL(T#rX_*ZgsTfLLDwS&%=(ANm%F;;0WSP*3 zBq@7ug9%oG^C>IsU1^OSj=Q9 zQetHK7D>YEZ(m;>8Ix3snu#^3k~mrrl31h=Rdm6j4bmi`K%3Gh$W*GfKqx3mAj7Xe zeDnFRpqJ_P;U?MXwP zw?0{MwehRdTH|~d!}DFUrMjYJpYzG(JzXxT+KO?tZFMH~J}+ZF3&iZ|)0;pVmGa$KGC!$XgT+>q;PvWY?cOMX( Z`w2uj?*V@P@4O#gH9!1)^xdyt{R3*QP6Ge{ diff --git a/tests/repository_data/client/metadata/current/targets.json.gz b/tests/repository_data/client/metadata/current/targets.json.gz index 848797c02423d7d82a56a8a44bbb58888909414b..8aed9cb52b1814978351ad158605afda3b2234ee 100644 GIT binary patch literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJW#1c-4G}L&bj1dr{V8lwiS&>Ad109of z(t!6O0asp1rW^^1aSq^Q3Qi(UrahK~$lweiU>V4WP)U^BAt-AJMbHQpf_D&bk0&5h zYUinPGzNSG8jTekvoTQ_$DFo-dgdJm9vKscrAQeGl1If97l~lDfV{{=nT=Xl&#@k! z1)YEy8aPC2wNp-WVr+;)T7%Ri=>^~w))2>;(kTGmrMUD64TK9y2MUT~4#sPU$|ue_ zCc>>t#ONp;QBs)bd=d!FI?Je}%txy{{)sWI1>wRH{P4rGf}vCP)$lmd|TKIV2jK7v4C-wBssJL8L-%2oFe`Ccr3oPC%viNV~v4 zP^=1f(C&VHt|JZ4_WUJ#a)W$cyJgCrw^QR4 zE4_IduP4X#?(<`7I(q0V>h*`z_Up;gt>e|=^`!CGg!y_?-VI-eU+-J(_GXaVZh87N zyPb5N9{TZu&Bo;RS>|?D%t_7OF>y9?g!U%)_x*W4oXOhr-EZYpQEQxM&GSKi ze|||Cq45g6(aUMny~*UF2>s>hsLn5%D@||Kwm3VkUp#sD^0mG{rQm4&y+8V`nw-&7Ee%-WbxeWd;Eb3-< z+*l6sldgO0KtI)anU6L;j!zeTB)&7BOl~hHleV}je z!pq%kr^a&M6SXZ6`k~8(D@T6;P)p+}@L$X4@*knf5e|USe7bEo`jO(dqaSHj;dwFM z4~Z%x#(W|a-xWeW@7|UEP2(-!ZF`^EftyNo3i+@cVPH;w=4=Ow z{bx`@vH&>Zj3gu)Buxmk<%p(Aa2!aqK^$4=(uQkr7C6!-6@}!f69I?7zk;$yhf*J^ Xyn$7LZnkIP?da`Ku^BNLq6Yu~rl(7G diff --git a/tests/repository_data/client/metadata/current/targets/role1.json b/tests/repository_data/client/metadata/current/targets/role1.json index c72f2ba93ff1fa70fe1d8b77b2348d243b46e754..16a6a8562e20cdb86843e22cd770de08c7df0726 100644 GIT binary patch literal 971 zcmX|=-EI^y42AFe6sx)B$8qe$;jV9hSng1T9LL#RQCid;5D4|%@oc+Xj7Ety$H&Lt z{Jq&N=hNXq*Pl;*UOw!;-0XILDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ literal 986 zcmX|A!EPKk488j+26L_(ilQhfw;(?dbkkE16cj~z7jbRFJ1r6f{`YX!-T;~zh#b8~ zK0f`tS=RIE?m@Qyp6$H8U%uQd%g=b0^_%@T&4{ebN;-<07o*8IDx_?lR0fSE4Xb3< zL{NAZhOAk71;b<`gR!rFEEm9kZ96_*5dS)SI2=CQemWekxu{{MYGf=1H7k`0Jmvt% zo@}~$>RF8#)d}WcL57)!HXms+jNZCvooNWE3QnvfLS8^;w=p`m(Ulx)ZUyqFxwaao zXlg_22sx`qqobl*i)bPm(QS?vP`MD#j!l9SnYj#5z)YxvM^A!LnA4=B$r2i;*m%ml zL!9oF69)T4tCgBV^-)O+&f06iK<#a2Es3O==GIV#U{xv7C4`pgwMhXOY%Pss+!CmX zMn9}sXKR*InE~CCx@R`06n{^RVTrbhJ;GG_Ah0${<(`|zo2inO*}cz95G_&nked6P z(t&$O2O8utZffFHckPv|stadKm9n!>gHpJ(G=Yj$R7#00AWALW8$gXSeJ2c2Cql?z z5@U!7wM6k{B1E5g${I#_xmmvMj=A{KcE9X?T>sgAJlXD%&+>d{+xgWw>$H2jlkN2Q zu)FuSb1$2hH{(4&-oJYL>kfYjuJ--u^y>H(p|x8`u@->$m$m%;`QsbG_C|LFf9I!r z%YSa)w=3VW9_4(zFkJYf*z-}a@Lp}A@0b0W$8jY}_lk`5Z zah<7i&rZX7b)eK*8>qyRn*vYcTnuQYIeALMK1xbi6N*R#SxfVtYYrPWLtURlnr4k! z$tTw|l}0!B_?b2uRB|4ea@d3H92ZA814JsV)}B%`%(*nSTBfD~LsG*=Ors)JbOVDP z%3;7{@K%xXa6s3lWyCaV3{A|?EN1Mp@{IFKMRMaWE$>386*X z5)Hj-)r^@Vlshq$*(J0PO6go%2xB40;@v=O#L*!oNoXn=ZQGb+q=?36v!O|wY(Bxb z7Y@Z$^F~$T+7U2uO+aOp<-TGYMiC{`atF*J!B^B2_FF zOJ5(h{d)QIq_=-Q`*nZ6{r0eJUvqE!U;gE?@??+c&1+#IZXVqR`x0(_mD#-LP;oCl zNk{7t)_~UNMQrq1JTUejwmaaX-_Fl>#9vMyPNxs=emt(3c0gPVE z2d-G1X2bkDo9DK?tl&V57SXa5p2kuPZ5ApVy;=x+qDXDU>XC^p>82{d(hHVpbxkB{ zrHm$FQi*6mw;&~{mDB{$YvTrUSvD*x5ml%vdKX4+3!>FnQ|AOuXv>l*a+iU#uZm-q z8BKUusFO!EYHN7}Rs%Gyk{YUY#h~7-QsdBxvWBZhh>yWBngaB-lzXIfk?Aw(prC;F zhObUIXbuTzr}bO17CO#aKQRi0G(EU9_pk?K-%1wal=qllyo zN;%U2>K!B}vZqnLJ#4=pj=B5N55F9K-2b?JdGW&|A1{x7z3Jo2t9N$)@8$C9-5p_! zcTjR|0PpW>`}N~bZ;9zYeM3v|Cw;k|pKm|@d3}ERe!A^vy`Jyy;Qj`(?DeeJ_^8`4 z$vbte1s1~~n}XyiaV#W;9Gs%2d4@R&D2)y_mp0c?QRdNGcK7-iw*7B^`gA)daH+ya VDEoek?*IDd>*Y?XJmmSe{{g}N_yGU_ diff --git a/tests/repository_data/client/metadata/previous/root.json b/tests/repository_data/client/metadata/previous/root.json index bfe2d99035e399db0dcc288ddf034528d535dd9a..448c2d0af83146ae98debd826aaf089c7178551b 100644 GIT binary patch literal 3766 zcmd6q+pgj`7KZQZDYo`4)53??vHLDSfN&<^Jd9dSY(qFCfh0g^)w_?`yQ_oJ#}X`Z|ztPlkxvIve8 zWt0($p@I=5z{Y}8tvL#HD6kbpp#`CYSSTVb70L)G4Br3t_A%gD75nsKL_ctX!1-ws z1mD|?hSyXf#3(`lg;GLd3#dpKQ4K6cIkzIE1lCMp!l>lf0+xTkkQj!@ zvMsJS*0v%LF{rgxAmj*P$)Q%<=2!s26f%7Y0z}J*2xx`EXu{geVLC>NL)&`E0U|Ox z0)Q1sDK){`LN-;}YzjsMaHC(Dr_#vcwvvMCh@voJj1#j}*enTfq{5I&z|7WB!igkA zbBseFg$)6;g>3>Gam1+L5GjUAWC-64C||$4{o_T<2bb!_%ZtbNUy3SIFGl<{ zEk3b%kJ2QM^Fk%XFL@%ed~aHQe$(QVs}JVh)gy~PxrWRTLnr{?v&GQ;!u=EVA13_U zKp%(ZyUuGazR~^h_$E%@=QFFx%J39NujTyo`ZU~*H+q}Q8w0QT)_2z*54XwC^BUg4 z88vpt%l;U5_#HHysoQp(anqSH=hfca_050Rc`P=^@idg>D6CXn-stUe-zLl9vNP(n z8rdvcG_LOP={#F?=C4?p0zL~Jw_d5Wv|QeQTKBGh-Y<)}gQu7EWv*TOw%R7UL%++i zgr#!jY);n^^}@)VP<$M#p5rsm598S-7^mTh^I#d5_JbDfhjYJ4Fg~jB)2!m^Pg~W~ z9mIJs7!J?16&UMf{dq2wUUp&UP~Gp1D_Fbrg+4BE2VijV>dAE2&Z^FQr?$zVzs{>h z1!(Vp4);;7RQ%lFa6!|)*Phw^{F-|@Jd$%tI?G~qD2nJQymRRYeF-tyfjg*5vqukmMVtN8n>>4$D&belhNI4Ia8;x zO+JF-j#~d3uNS-@_?n+@DUT70F`IKJBAX+NY9)lWg^aL>AXO?d>C2p)kPF0tNdPtg zN?O#sim5r@=r>|s+4wI4t7;A6?&UPNnw;w$Fu~8t+a^!@*v*Q#rrk{@p4He-r^gmm zE01gz`{t=9p6LL=PA6g0s5|SQL%WmtPJF?c4EHx{xlQ~zYQ(pzheg^7Ziw2KJY3#) zi}W;?*UkBG0ZwnwTm=>Hg#ZLumBXxfALUebIvljB;zsvY!s-5;)qb=)R_daQy+3-kc=QKB1e|DJR$ zvLD=5vyEWqXf03YoG^fl&wp!;`SI zme?|tiBg7MDP96aP4xjn>c|$DAgM7@FcgHFI{H1UewO*a2&|LO8m*?6PkOygvs_c! zZ8+P+^N&92O`mNJ`-R_fc1P(4_4#a(CFg0kGnSnhmq4tWF&WotohkJ0XY9_%tS*-0 z);ilJv~Zp4U|z=OT5V}pH1?XUSz{095VgZmE9jL0qh-A5*hmk{^Rax^Ew;%{yXW5S zW;c_u^mHcXr?X!_C6i@|FN3OK*LNX{UEh7~66eyNY=*VL9@f{xYRCB_3{eHIEv7+BOs7%Y0Dy985cxsKp_=UDVs)+VITk!j2Wg(#)={djZLz_ z|3k*gh*M-bAl5pNV*gmXleIaiqfMIU&Dzw)a=m%pP7$;(Fe_)#vDKc8qu{vOUq_Q# zufGISyz#z-1PN|FYZb;clBv&en_Apy)lRmd}H(sikEO5C-zD- zp6sIln4aK+yEzxzkzd8*E~*>&(MAS^7WrC>qN}} literal 3781 zcmd5cLL!ahEe7C0s#_kAzZ5d_c5Gvs%kWv>GV8I z5uhke_FmgwUVHhMA3i=W={gt1^{g+SKYjfDhmVh6?C;}qryr@ZcRn4-l+X!ASP7t% zgf60#7^JWu*hM&T9ET(dJH{mfN}fo^;Y?zJ?Xytu&%b@V17^C|9NrN}V{bh6ejJX+ z-^MJ4S1Kl{jY$Y+nh~BrffJwsw(*gx5n{k`1*Q}u$T?#q0X#vB1Dl>Of^AaQF@zh< z5my{KSU3(P0x=B?lR{w|NxQ(|P#A!Lb`)|G2|2Z31tZ$kN;9aiqL3kl5`t|`gaD>o z3J#G<9Ks|LSONl=i=~?gmnfEKpcQfiQ+7V6kv1bX33d>bTyv?grh+45p-Ux^bRd*=avhEm3rI0!fJ_)rnm`T^cZ{6_X_zpC z08XKSwra;-Ae}fUG4^!?xPXca!yqs&vc(!q0T2vmsU2-6jA{viMAUVqWAPYfpR$EB z$4~|!w=Xj7VgNPfAQ46?M2I4gQUi5j^Chl^N^9*h;o1~NVPuF=7J@`f+I4gpeG!4# z(v+~!2-DJG*j2(_3k%xS0%}F{rzOg-KYaY-#h5oQ^@}erK7RgOJdXOskXD(QnRu>iaSM!s|~282b@G`~3jmr}y_`I`O|ovSfd; z{|ocKoA6%)z4y>J*yoG*O7{2dQtZFwv&9{+$v%~@>VABEYC*FdehjCzUfciZ2J!pm zD(|=3wf5EvYwO)0hG8?Oa9_8r}ZIQPZgjH^qWHvOaU3-5e{(RAdS3cCXCp;cb+ zQF(WDFUUvk_K1@J-u&l4tv8~3I;s)NB=K&feHuMw-bpcDIP*=trk%b&3*v`xR{6A; z)M;F8EQ=#p9$HS`W@VgcOXgdhbQHlRK+i?ak4W{@GsCA<-tlNUYtDxODZ}CJG>FZD zX4zp&ooadPvlD1;l9X3am6jli_oKBIBZmA+J2!d&%XF1L=v-b=-*waZeVio?aN8(a z^A;j(?H-2~^MbvjhO>KXUBs+IpDI2K&wdE1`@55^r@d8v<|dGn)1wm6ZWTPc!$FhN z#svDO(IJMHau~)#%vxEJPohb+492&Et0viG3Wv8|1$9?>`Dciga6?}m1tDpq#?*?l zCMbc(p)S;1SUuJ*AyR1DfsB(lfC|A~ZTlT%wtM~+h;^Wit2bHiALG%0S1;}|2&vtv zadF$q3k_=ndhNG|`3_ZrJlyxrA<5(%>xMArZn#)<;%KoPR;mdqKo?%p-01+OXRCZa zi4Kl48Wi)vtQ+>)lV)rh`lc_=?=tVw*U>=0P1X;0D3a@Mc&?4WrsNy)2xl%%D;&2)4(0BYh5wTfWO+Vu^M8 z6N+WoM6?54j9sD)O{^p-?h>l3E74Fe!L^23V(XqLFbM%z*(p*I?g=nzc($1)Wc%cyH>)i>>&Zn=sEf9-Ph0tNoz((Xwq)idCZ9g#loq{N zj`4lxh~0DEtWDa%*|p@Z_1rtl`|ECa>OJ=De!V@bwWjTAW9Rkzxsfv7^rCUbAJv)H zs9xulYI&ve$u@fA`@oCsqL$I})Vue5o@FsitGihDl1t~lJlvj*8|j+aBI<4RY>N>% zvmQ`9KJV#-FI%p6_YYR{w!VAvX4MG~hSkP6$Q}~|`iZ}+&+3c)=$48K2$Bk!?&hOj zbGPlR%k{>uK7Bc;_w;>HuJX$Nq*yP9_r;u@5Bv9#<=okw1$k?;0o z?{54ymalNd#@fQoVgIt1@HO*}O?$;lF{U;@tK6d~B diff --git a/tests/repository_data/client/metadata/previous/snapshot.json b/tests/repository_data/client/metadata/previous/snapshot.json index 3e46e1fd7e3f610b04e4c8f5cd4fc61efcaf3990..2b3323dd90d7c44cb2a369f9cae10006ea6046a7 100644 GIT binary patch literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 literal 1396 zcma)+!EPKk42JK1iou+dl%hn6$}Q*%6y5ez6a^_!Ubl(Y*xeRQ1HXGYW2fjL28^8< zEJlL(^Z!52mz!n1+}+;m_0O|k*7wUVH_P%R?q&VMKi`dbvs#-qS0cndyO&C-I-w=Z zm6JEcNzO!Fz4kG)WvHr+-G^7nWb2z{Kk$=ZkEi{_9}gc6hYxRmI2^vNg@&zEX(;sIED@K=OWsWg&@x*z|0!Fa~yjXIY44RE>3bve^_1IfE zq=8UYWYe4on`ACCEb>Ie7E*bJbMDj!C+wwi<(5rqt1M=%xD*%wAW51@D-@z}M#I`C zXKG_|AsLZI5rvr$2oR*DQuDAlDg38V^570isWCYTG8&}Zc}5k=JaHn;C|=U+**SN^ z(Y1*dOB8@s(Px^`gSyNiWUvM(k2lM&8!|X+@i10OzIlZ)i5AZ3Aotm2(PC&H4FQ##Ds{uHT1l-6^%X3!Lokz z``ha=4$f85w)DKRUiEzI*URgL)!yc;8{m|^2f-SE#xJ4|e+AHF=9mSKzb!$ba6UuN zJqF2gH1~qA)Tq29?A`4@uQIJN$|#fK!@)Y-0QEU?GmJa}aX#4Jb`snHP)(MdgW#-X^4LZs;6{+?3h6S%>Q1IG0oT}K3q{UnWy)k^- hV3i%!|C0M(rcHQP7mue{*T4LHx!XyLupe(8{{>5zX`%oC diff --git a/tests/repository_data/client/metadata/previous/targets.json b/tests/repository_data/client/metadata/previous/targets.json index 067100674e3d7e2c7ab3176430787ac05029a0ee..2c60420ffde3e41c912b5608935066625af10040 100644 GIT binary patch delta 1109 zcmbu7u~wru5QR^Y-4xkkA0p`}ENP^X)Gc5G#>O@n+rXjGj7A1yY%nb0r*WC>k{sS3 z50EA=808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jci?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+ng^7 zHfGSY8}Vmvoz=t3;(B$uycl)u-rn@Y-JR&W(ZaM|H~PXaR_8Vxh>4x2@#X8j%-73F zSgdE`*6gl#KkH1Fi(}IsAZ5zraR604lj>8%k|UKTQFQ62aB8O@OjjI+^p}z zw$=B;?kwN-E?&}lV_T{<*m=Er-W^}B#`}qT@%Z-4{yMqo_|9?S$>r!^JZV;&)P(i) zmUkxI?sRl`(Z311Gj})}-#ri7cKx(oH%-4P^!!=tcJy^%?C@>g8!Y^^+}_M#7sgei zz1wQ@bouc0d^0?V-2*qfJid56O8b}RgO}dkvVHJ)y=q5j-=DMxqoXnO)ro&Oo%U|t zIxR=x2elwr%JWgJ_&%=jid9^9MhpXAc(c>8%p7dL@p1)r8-amch|9`B%KYsg3 Qhggq?N~wQ_KaRiq7scgE!2kdN delta 1124 zcmbu6yKd*-&9>IgC>K3FcSjNkgoGC%EF_^KnM=1oh##?BrG#)dvt4=v zKY$azpvQg%S4~YaJ=N#?&ggcw2&-s_ymvV&-mV-nVw=yd`QQh;{CMQ5B-ln+@UcwJT-V90@NW*@Yp z7!{K=QEM~-l0hkng60%-0Yw{2SkNgr0P;vW7goqxhC<~%h(cXhDaAmkyh$by+J-1h z5IL1AiR`>O$dbop;{pcjlZY6DB$*MFL(T#rX_*ZgsTfLLDwS&%=(ANm%F;;0WSP*3 zBq@7ug9%oG^C>IsU1^OSj=Q9 zQetHK7D>YEZ(m;>8Ix3snu#^3k~mrrl31h=Rdm6j4bmi`K%3Gh$W*GfKqx3mAj7Xe zeDnFRpqJ_P;U?MXwP zw?0{MwehRdTH|~d!}DFUrMjYJpYzG(JzXxT+KO?tZFMH~J}+ZF3&iZ|)0;pVmGa$KGC!$XgT+>q;PvWY?cOMX( Z`w2uj?*V@P@4O#gH9!1)^xdyt{R3*QP6Ge{ diff --git a/tests/repository_data/client/metadata/previous/targets.json.gz b/tests/repository_data/client/metadata/previous/targets.json.gz index 848797c02423d7d82a56a8a44bbb58888909414b..8aed9cb52b1814978351ad158605afda3b2234ee 100644 GIT binary patch literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJW#1c-4G}L&bj1dr{V8lwiS&>Ad109of z(t!6O0asp1rW^^1aSq^Q3Qi(UrahK~$lweiU>V4WP)U^BAt-AJMbHQpf_D&bk0&5h zYUinPGzNSG8jTekvoTQ_$DFo-dgdJm9vKscrAQeGl1If97l~lDfV{{=nT=Xl&#@k! z1)YEy8aPC2wNp-WVr+;)T7%Ri=>^~w))2>;(kTGmrMUD64TK9y2MUT~4#sPU$|ue_ zCc>>t#ONp;QBs)bd=d!FI?Je}%txy{{)sWI1>wRH{P4rGf}vCP)$lmd|TKIV2jK7v4C-wBssJL8L-%2oFe`Ccr3oPC%viNV~v4 zP^=1f(C&VHt|JZ4_WUJ#a)W$cyJgCrw^QR4 zE4_IduP4X#?(<`7I(q0V>h*`z_Up;gt>e|=^`!CGg!y_?-VI-eU+-J(_GXaVZh87N zyPb5N9{TZu&Bo;RS>|?D%t_7OF>y9?g!U%)_x*W4oXOhr-EZYpQEQxM&GSKi ze|||Cq45g6(aUMny~*UF2>s>hsLn5%D@||Kwm3VkUp#sD^0mG{rQm4&y+8V`nw-&7Ee%-WbxeWd;Eb3-< z+*l6sldgO0KtI)anU6L;j!zeTB)&7BOl~hHleV}je z!pq%kr^a&M6SXZ6`k~8(D@T6;P)p+}@L$X4@*knf5e|USe7bEo`jO(dqaSHj;dwFM z4~Z%x#(W|a-xWeW@7|UEP2(-!ZF`^EftyNo3i+@cVPH;w=4=Ow z{bx`@vH&>Zj3gu)Buxmk<%p(Aa2!aqK^$4=(uQkr7C6!-6@}!f69I?7zk;$yhf*J^ Xyn$7LZnkIP?da`Ku^BNLq6Yu~rl(7G diff --git a/tests/repository_data/client/metadata/previous/targets/role1.json b/tests/repository_data/client/metadata/previous/targets/role1.json index c72f2ba93ff1fa70fe1d8b77b2348d243b46e754..16a6a8562e20cdb86843e22cd770de08c7df0726 100644 GIT binary patch literal 971 zcmX|=-EI^y42AFe6sx)B$8qe$;jV9hSng1T9LL#RQCid;5D4|%@oc+Xj7Ety$H&Lt z{Jq&N=hNXq*Pl;*UOw!;-0XILDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ literal 986 zcmX|A!EPKk488j+26L_(ilQhfw;(?dbkkE16cj~z7jbRFJ1r6f{`YX!-T;~zh#b8~ zK0f`tS=RIE?m@Qyp6$H8U%uQd%g=b0^_%@T&4{ebN;-<07o*8IDx_?lR0fSE4Xb3< zL{NAZhOAk71;b<`gR!rFEEm9kZ96_*5dS)SI2=CQemWekxu{{MYGf=1H7k`0Jmvt% zo@}~$>RF8#)d}WcL57)!HXms+jNZCvooNWE3QnvfLS8^;w=p`m(Ulx)ZUyqFxwaao zXlg_22sx`qqobl*i)bPm(QS?vP`MD#j!l9SnYj#5z)YxvM^A!LnA4=B$r2i;*m%ml zL!9oF69)T4tCgBV^-)O+&f06iK<#a2Es3O==GIV#U{xv7C4`pgwMhXOY%Pss+!CmX zMn9}sXKR*InE~CCx@R`06n{^RVTrbhJ;GG_Ah0${<(`|zo2inO*}cz95G_&nked6P z(t&$O2O8utZffFHckPv|stadKm9n!>gHpJ(G=Yj$R7#00AWALW8$gXSeJ2c2Cql?z z5@U!7wM6k{B1E5g${I#_xmmvMj=A{KcE9X?T>sgAJlXD%&+>d{+xgWw>$H2jlkN2Q zu)FuSb1$2hH{(4&-oJYL>kfYjuJ--u^y>H(p|x8`u@->$m$m%;`QsbG_C|LFf9I!r z%YSa)w=3VW9_4(zFkJYf*z-}a@Lp}A@0b0W$8jY}_lk`5Z zah<7i&rZX7b)eK*8>qyRn*vYcTnuQYIeALMK1xbi6N*R#SxfVtYYrPWLtURlnr4k! z$tTw|l}0!B_?b2uRB|4ea@d3H92ZA814JsV)}B%`%(*nSTBfD~LsG*=Ors)JbOVDP z%3;7{@K%xXa6s3lWyCaV3{A|?EN1Mp@{IFKMRMaWE$>386*X z5)Hj-)r^@Vlshq$*(J0PO6go%2xB40;@v=O#L*!oNoXn=ZQGb+q=?36v!O|wY(Bxb z7Y@Z$^F~$T+7U2uO+aOp<-TGYMiC{`atF*J!B^B2_FF zOJ5(h{d)QIq_=-Q`*nZ6{r0eJUvqE!U;gE?@??+c&1+#IZXVqR`x0(_mD#-LP;oCl zNk{7t)_~UNMQrq1JTUejwmaaX-_Fl>#9vMyPNxs=emt(3c0gPVE z2d-G1X2bkDo9DK?tl&V57SXa5p2kuPZ5ApVy;=x+qDXDU>XC^p>82{d(hHVpbxkB{ zrHm$FQi*6mw;&~{mDB{$YvTrUSvD*x5ml%vdKX4+3!>FnQ|AOuXv>l*a+iU#uZm-q z8BKUusFO!EYHN7}Rs%Gyk{YUY#h~7-QsdBxvWBZhh>yWBngaB-lzXIfk?Aw(prC;F zhObUIXbuTzr}bO17CO#aKQRi0G(EU9_pk?K-%1wal=qllyo zN;%U2>K!B}vZqnLJ#4=pj=B5N55F9K-2b?JdGW&|A1{x7z3Jo2t9N$)@8$C9-5p_! zcTjR|0PpW>`}N~bZ;9zYeM3v|Cw;k|pKm|@d3}ERe!A^vy`Jyy;Qj`(?DeeJ_^8`4 z$vbte1s1~~n}XyiaV#W;9Gs%2d4@R&D2)y_mp0c?QRdNGcK7-iw*7B^`gA)daH+ya VDEoek?*IDd>*Y?XJmmSe{{g}N_yGU_ diff --git a/tests/repository_data/generate.py b/tests/repository_data/generate.py index ca1f2e1e..2bc1c67c 100755 --- a/tests/repository_data/generate.py +++ b/tests/repository_data/generate.py @@ -22,6 +22,7 @@ """ import shutil +import datetime from tuf.repository_tool import * import tuf.util @@ -105,11 +106,11 @@ # Set the top-level expiration times far into the future so that # they do not expire anytime soon, or else the tests fail. Unit tests may # modify the expiration datetimes (of the copied files), if they wish. -repository.root.expiration = "2088-01-01 00:00:00" -repository.targets.expiration = "2088-01-01 00:00:00" -repository.snapshot.expiration = "2088-01-01 00:00:00" -repository.timestamp.expiration = "2088-01-01 00:00:00" -repository.targets('role1').expiration = "2088-01-01 00:00:00" +repository.root.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.targets.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.snapshot.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.timestamp.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.targets('role1').expiration = datetime.datetime(2030, 01, 01, 00, 00) # Compress the 'targets.json' role so that the unit tests have a pre-generated # example of compressed metadata. diff --git a/tests/repository_data/keystore/delegation_key b/tests/repository_data/keystore/delegation_key index 89210ee6..81ea872d 100644 --- a/tests/repository_data/keystore/delegation_key +++ b/tests/repository_data/keystore/delegation_key @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,54482FB3B9B400FE +DEK-Info: DES-EDE3-CBC,596A63102102188D -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 -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/delegation_key.pub b/tests/repository_data/keystore/delegation_key.pub index 1ff0f7d3..5ac00bbb 100644 --- a/tests/repository_data/keystore/delegation_key.pub +++ b/tests/repository_data/keystore/delegation_key.pub @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAoezivMqfuB87dx9HyAwa -o/V269HKfCz292vwiuk+BPxjIlhYNpBBYfKbukqaudRpzDCjEeouvtWgzgZXIKKv -9PtFwmVkNwYUdJ2mi0Vx5n9mso0/9W24Gma01QkXXUYY0RJRsG5/xWLtRs/CHAEH -XHO0CcCzeQhyFd7QkkJscUqFhB3JEr71Tu9sG+BJwbayZCE4K1E7ydDQgeQqenv1 -vEHh/APs8+iTlRb/NeN4/t22GsjqFXi57fvllIhdKP/c94PZvK1tqcbWcpB8m+Cq -DPajNeUfB3tnhvbdiFpU66cNokkVOkiAM6F1Rc4TUWKOk1+nnKYK5xm2PUIeBOrI -qwIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyTjf6y2NyDye7RzEnpvj +QlXPI4BBVM5kM1kPj2/xr1X7jnT48Fe29EsSYxCegolU8joWS/WkKmWEVljJ2DPd +zQg7c6WV+yok6YJElossz82cfqNjhVOtPIqrom8/L7OIWdiKXvaor4/0/N+xZtAS +SCacK9JiR4wUhE7EJacUYPFSUynray8oVic+UIIVPGXLh8AW5GWSktND4osooyyL +SK7R/iPuN24OzgKNj7Vfihg3A3SynrDA/DVlpuTrOF9IF5WYJXxHaCYTNvKBlDFq +D93DmMDNPHS3L0M7vQVKhzeQClNmeQALygg+qUxItOpylZrfKpZWpHqRbGML/WK7 +KwIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/root_key b/tests/repository_data/keystore/root_key index 4b892c3e..1ed780e8 100644 --- a/tests/repository_data/keystore/root_key +++ b/tests/repository_data/keystore/root_key @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,3FF3C80523E9427F +DEK-Info: DES-EDE3-CBC,5862527BBA778D16 -+5YZryf4xbife7N75y5pB8udqxCFexQW4z/O9Loh7pIyCOx0lhZOWnHrPAcAyI97 -hHS35IID+CYv9RXpspXjXrsxSjp3Bj+RH7oXaorirmjjWzS7hm8uc4gahr5HmVn8 -EIarmyI7/MDQWUcWzHw6qgjZZLRDOtyOPq9c6u/qoS71j2WSntJlJudzsxYoBYP7 -/sg1gpUZLk6590clXVSog1YmPmIzy/mYsTGo/GJ/h/14NcNJk3T4bqYmClSb04dp -WcpvhNl5n2zmNVJ3xBGUX8XihRNV13tsh4tjCQSYgj1M2mIxTGFCToYKQqsrkp6/ -ce7KHboQiJZn6xIhO0pkKcOJaCH7sjhl7TbB1sh2sU86igJmIf3NRvW8B3MjAUeV -KsQqJF5mAqB+UtRQbsfrkZfrlCCtRqqacMZ/qa3HFIF8Ji8wAaVaLDQ7si95HKMn -VAgRnL6doSJBuqBSYwueM6Js5mbWbN4bHUMZgmsiEEy4Ufd9fwoO305UqfEMhN9O -HvyZsBajZmpOvbncU/kavSlZxzluBj2Iy14t0uLukuwughFQyhSviLWeDlO/6RYO -SHQPb3/xR4UbUWct6mXuUJRazIj2p1n7iOzIYmvUDzOZ8Upzw4nPycYDCqTDZQMY -Bjrtxesgun2GLq2VcDaCBXY9KTrmM8gwCdFR9OGpVD7Ptxm6zvDIriEKC4Tjq6VX -xMVB2Pop+F+OU8vEomWEkXO4CMaaWe/bLgLLYw7IttWlXKVylk5A0UqlT1L6W+3c -ZrG6u0YtIaUb1AgynoRbgVQTIFWk55jNRdEkTX2nqXpElUWzA/6RyTd5Y3gsfifF -ZdCEd2B5pUCyif+k+oP+ifikcWhZj7OmGDyVVKauwWeayw8ZpTPkPnynWkgADdMF -GUu1xW2bXbjk/SjOfa2KIUG5yRqOrkKg6+gpZAZmeNedQMeob3afP8XhyV7zZEh0 -Dfl8GnAUtsRua0REYFL/ibmFFAeCivlxaXAWjiTa/uYRYmaBZQ++lbhGd9RvWTg0 -+scf2p/TbFyVnrsQNHmXaNR9a1vH+Zd3KRxBDxKGS5sdYdWVYiPNdKDKat2+PCDP -mRCr5SOd2E5xeHG5Pp0PwNUvpbytPyukyZdaY8Ri1vBNenJ2NGqCSNzH4dubMY/n -OWm1typ9wy6f36VWp4UKnHgTr8xqqXbipf1hu2usjdQu1OMBZICJ00vk4DTN8lmk -iU9WGO/nZX43JkwhzIJU0st4/kpoDoe63A+oAlK9xsD7jDhWX9oUVKO2aFeBktvD -lrJt9Bm/+XKAC0mV3GpDEHBtJ8ruFSKGNIhcTFdVEZ80OiiLfhsBmNRnCZm91RJg -2rPa3cJ0mMA+o0AkQOnxlwywxYe8z1gsmNPO13oiowh7g6OE43SXaVzsEobQ9FqP -ibZGxBj3xhFu3HcylqjHSu8l/Pe47QC5ZHD2mLMLkx78M+HOVFjDqCUVlSm/+LeK -xiVHcCHCxEyTmgvjgXoVBbR2ioQsKGvrFdhMCf1pE0OQ4h3sX5Z3oEy9ks6oe6TH -dE2up0TVrJjcNh1wnR9qtoGZaI80He1rZqoxaQWCzIsO6N7JQ4hK+A== +cif3iUP69fRvucD6GehZavc+uWi4pwhkAJk5HUqsGIsprxZfR83v1gu8/gu+n88w +JgvkRZINxbE4PFgqkwTVqXbDRvgfd2IcXExebvdMo+B7jKHe1QBa3xx4DrFafRY8 +cCHZaFeXJuM4YYePw3Hj340ZW4IAfJ83iy/Sw34Fwbtv4596T8cM+Ivr2KsbsLmj +f1Oyy/J9UCAzrS4wn//IB6gIw6Y0Qr4gcPKTIVr8vlN91eR7qeqKcHE9eV/hxFog +S5w5xRUw1Y1WWFp1MS0+hf5t2OPq3T3QgFrEXptKj6Mbrj2IrX0bEvjtyKPmhvYh +BZHQo8C4mlrvo1eN6wscg62mAJf2owBDtFvhj3VjvrrxzY6drJquDJQHguvG0uFC +KEkk+hGjajkqlZwDGvfhKiYnmcg5Z5sojSDHx2vNmI7Xy/tp8Lxau8Z99WOUTDIx +v4JyhElWNxD9nAi8DTOZzuF/tnTNHYc/FFTJK2VS7wM8RIIO9enhnBuTeaEedlun ++i+6stGDvT0SqBHHsJhhCdenWCVzmLTuNipu+orhrgBRxMdEtCvnFjjBQD28Aa/P +fJASF317x/ngxA93bWIFKa1u3zRSJy8KaWPwzzPwp861ZQ/ozCELno1zIxmlO2Pb +2fVG+MB1LeAlT9QiG9gVRsx7CM7U1WPhzCg5v0p3O2CSVmY7WbKFq1ZO+wTFQUOJ +FsclObgALo/9Z0nEBVcZpte+xDEUEww7docy60Xs3vixnU0gFF41IxSOFxdv+ZK5 +MyIJ4x7TrkHxOUjC2BJRq6TiCDi7ARtv5FIbaSyuEY3J5vH/zaOfwokg0bgWKMlY +6gMND0/ZhDVAGinSPkl7By9z/3pZviZ4M/TrmYqX8JpuVDXGvhvIMt2HqAVFtiJ5 +715KJZATOkLhMcJzm+Pc5Q8Tnves8Xnpkfq11eh0OoCUqYKYCkCxMzmcLQiU3MNv +gZ8grv9fFBrGjDueq3M9cVX7C5wL+aK0OeipIcCGX9TBG8m6z9lBCdnFF3nwrrNU +G9Galo64tro++Fpw8UawIjo1rcHaN1W2zbswurqkazleaftvSbIUg4a9ofZv1ZW2 +NSXfySDNKQH55TOHVtc1QjedUckCxP0Z23lC3PLiJr87dsk+w0HKgReeK7Mz35hq +srtxJoKGObiihYu2Z8b9s8B57p1Xu58HrCHKmpZFqnx60N7kBI5yhK1R2RTWDbHX +QF5Jlprn42n1oaHQic9Kstw3ECxJs+ZzmgKS2NFCiJvqy7+mu1G83OZSqeEZWsQI +BoMN4J9jFpI6O0WBfngyVL10qiB0o8/yevFU301slEk7eJMU7VuPzPP3tx8a4g2c +i16Fu0X6V06Z44HRtJChrMUfgXeY6e3KpS9R4b076W4WdnIuSxHTZSfnYjsKYokQ +YJOTuRwgPTZIwJLFnxkULCFNivk7HByh8cw8HNoWzS5qmamnHP4tg4+B11g5ITUZ +Fz1ZGxhSwdb5EVeGSXHrsv4ux5ngxUPGu6qG67crQMWKj7vFK4okWlHhxdHO5plx +fbw1PgVW6MfZsTgMD3WeRSzt+8QbxxIcxJpbp732hzQGVYVYgreXTopj0HbYGK9C -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/root_key.pub b/tests/repository_data/keystore/root_key.pub index 89949d85..91bfc4b0 100644 --- a/tests/repository_data/keystore/root_key.pub +++ b/tests/repository_data/keystore/root_key.pub @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtEYs4bpRKf6eVbpOvNvg -aD7rVnFStWzYIdnWdCvzASga5pM3eI14nCalfS/Z4iGCpaCtArp1QURCf+8u09A2 -t9Rvx6eLEnR6jp4bE1wCzOdghaKqpdR8mc5Aw5IqRzcnJi44X3WhDBe3MCVEYya3 -UtTD7Y/FZtp0ZoH3nI8vYnaiYO/jPWmrz254GRGmbR+eu9YOmmtezJg3xQSX5/j1 -hBGl7cnEDIa4kZi1aqdL9UsM2uikhC0f7A/OyNdlgvXVRAZd6fnFFQb44HDix0VG -VdAmRosI+U4J9JDVW7G4Bt+Z47CfQ5CCW1wCKPRwx9F681WpdkLFG2YQ0zYjfAvo -UQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAxVZkOQbduNbye/dv6vqw +WMWGNJDBpTpVBrEkzAzD/8RX8uo09TbAE/yeYd4E9Kz4wHRmhWtUA3QqYqUfE7vX +gjKH8pn8odXAZlrN6IbcEP63kOeJAR8IRbiTqSOobl99SWiu7x6tFxAwvd0OkeOz +MrRQDyzELisSLMMm+X0OfW/zmtefWH1GjywwBr0YE/VfkW3G030qI/nQMFpyGUge +jKYsyBy06Jj2jwNJue9mB91V6oKIFT7KsrsIfbk5mo5GWJnXk13oSzrlbl+KT0iQ +y08gAcs8XnAyu1O68Shp68T12Vs1OQfcL5DP6p4uqC2+Bbec5u4etMz+94rLxVBt +NwIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/snapshot_key b/tests/repository_data/keystore/snapshot_key index 5979b9f0..638b4963 100644 --- a/tests/repository_data/keystore/snapshot_key +++ b/tests/repository_data/keystore/snapshot_key @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,7349E671E697C1CC +DEK-Info: DES-EDE3-CBC,ECAA70F42108EEAA -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 -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/snapshot_key.pub b/tests/repository_data/keystore/snapshot_key.pub index 4225472b..74c3a677 100644 --- a/tests/repository_data/keystore/snapshot_key.pub +++ b/tests/repository_data/keystore/snapshot_key.pub @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAo7FuATglySRO9/s6ZEN7 -VYtwjcs7NBO7uMIPnk2+EnNlLrN5mcW4eFafrKNXXJYQXZP+/b2t0K1sin3J08eV -lTQo33ROtWOVKNLITGYfFewesGN8Rz0nREAkQmD9w8kFTCphNruCiDE0P8m7jRrC -AHYl7zqKuuhNqghfYqDiAa+f8GOhIhNxJPf6NreYCGHW2kaMURnOKoD1v+mV2QDs -QpTEP+/gPvck7rLdfCNUB7iBZnnQw2fVSga44klDQ0tmhHnik0YakX0F7F0dra2e -Kz0N385MnW1vqEH1/QnSZBsJuqSNwf8JdPQBjisX3Qvm1ddH10jpWFizPtwnkm1b -vwIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAlR8BDCIsPJJZCuY66HBA +IRkR2JQzF91KtRDAgkdRS/UTVpnmQHGOdGT9d0aYCi5O++GQ1Iwm3ET5T/aWODYp +6tEArLUuim++W7y6iICDTBh1U12FbNDSJuS86uiZG72fMumkuz/48Zplgzh7Hpvz +fQTaUlmR/lnPWu3qLyB7/gb8iEREzgnAqKPZM+Lh1/YMpWj4y1D1FgQila2x54/G +WZSjv19C/ySG428R8wIVGPJcdk9LGbopaznmeIC4wIFwFbHzb0XxyuBdF3WfiwRs +uQfC/nD33cQYJIqwLAoYaM6tqYdW+dDvUCdZCSsP6DO0tdyLTszR7yZfxkEENxwR +fQIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/targets_key b/tests/repository_data/keystore/targets_key index c18a6806..bbf0812b 100644 --- a/tests/repository_data/keystore/targets_key +++ b/tests/repository_data/keystore/targets_key @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,25ACB134E522BFFF +DEK-Info: DES-EDE3-CBC,AB64325FA489FFA9 -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 -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/targets_key.pub b/tests/repository_data/keystore/targets_key.pub index 7a99a115..1e77131a 100644 --- a/tests/repository_data/keystore/targets_key.pub +++ b/tests/repository_data/keystore/targets_key.pub @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEArto5S3epNKVy+k8BbqAx -0/YDt968iu/crducSS1Ys4HxvWFZ+XuEqegZb8zVp8aem4WbNFw4XpUDConwOW4w -wIaqmxJ4EgW0OzEmX+AeBlc6OQYW0wD6GEOWAMj5WjebJd54fE2zXn9p2dLdzm4q -JA7iVGWPE5vNPkqOYfX7mmoj73/vpM8q0Ghbi9+2/7v0QYlRgeaR82C+InfeO1vi -y7Wcu2M66iWxSmbF0whd7BAXaNHkyoH8AEl3dPVxHgtY8J7zdYoPrCN0/lx3mgUL -r9fEcTqy+Q7K/0zKPOG97FT1CqRoY1svPNYP48HmbnTQT/vESwo6dTmTU1PwL+2K -vQIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAp5AtpfS54YSdahxYEPYB +NZossC+Q73dYZzvlc17q1puTckDFPOcSkXhrNP+JKW0Q3XRm5rY/w2qbVLV2IUJm +LsuZAqEhl75YN6iby1sUbU6fKrksxMZM2yoG1trN96+U4HnyRAMBuFlPRup0nlfA +itRy5mI2tIEIgz7IWP1Gvfrr3qEXeCg/HtndN5ODHN9XBqD7dy5GMWE/3T+ph1pV +K94fHiWei5V3uVnwvd+5//JC06ato+UmFc2Wm3WPw0+ts5Kr8CXqfuxlizsCeGSG +foPCulYxDiBdzyJ/c5kCsQAxu9fusquPly8ElAUOxsKbSy3D6vmh3nLSiU4o2Sw1 +HwIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/timestamp_key b/tests/repository_data/keystore/timestamp_key index dcedad06..48d6eba9 100644 --- a/tests/repository_data/keystore/timestamp_key +++ b/tests/repository_data/keystore/timestamp_key @@ -1,30 +1,30 @@ -----BEGIN RSA PRIVATE KEY----- Proc-Type: 4,ENCRYPTED -DEK-Info: DES-EDE3-CBC,FE4027E2AF1DF4E3 +DEK-Info: DES-EDE3-CBC,D632B5396F30EA4B -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 -----END RSA PRIVATE KEY----- \ No newline at end of file diff --git a/tests/repository_data/keystore/timestamp_key.pub b/tests/repository_data/keystore/timestamp_key.pub index 6f129cb0..23f5bed6 100644 --- a/tests/repository_data/keystore/timestamp_key.pub +++ b/tests/repository_data/keystore/timestamp_key.pub @@ -1,9 +1,9 @@ -----BEGIN PUBLIC KEY----- -MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAtmyfY/TyvGDt/N/L+x8+ -hVpku3JNOvPrFG0QUEQYWjlNjS+DSqigQvo4hluKz++qGeSYFl+LJBw4YLa0RFAg -b5jz9NAVBZJgTseT762jYC7HnZgmBE8vHcmAbfnUzcU7tLVn44xk3akzWGBTIEr6 -VBL6x8nMgKNqLylIMDIVBHUI/FkAMMnfccYhLQSm9y/VAF/uW+/vu7WTjQynlEAY -vvQZqLxLC9mmY1i/kYeLbsJxZowzFw5KfVXQLjeVj420r8L0d9Yrl7T9ZH6AxCoi -jC6qAV/T9EP/FSEmyTf0MbCZDVDXlRwia+0Eb+5UkWRLGkjJgvghC/zCcODLexXv -+QIDAQAB +MIIBIjANBgkqhkiG9w0BAQEFAAOCAQ8AMIIBCgKCAQEAyDLiHqlLrd93Ij353zyI +z7kHpti+fEZpazyORoQkD6eXI5ZVhClJazoL21GGn8QcHTKmb7GpRAiq3pdbhv4W +RU2BivrI3aoDSv267qsbWwgVolUdrZmjq0AJLCXSy9Jb00Spys1ptxs/zopjuyGd +GKWiwXTd8opZqaGiwqrjlGjzcljwawQxb9BIO/XYnrqosTTdO+S8yg1R9/RMX54k +SexXSxD0J01R2nfBEKwnH45hLvXTZa8mcXNFINoK+NRfSkJQjmN5IVome5dP0CwB +OnC6hpvlim4vZh8X82ahh0DBo6D5hJO3K1zNRvXjKEbiMtR2E9qXYuWq6XQTfGh2 +vQIDAQAB -----END PUBLIC KEY----- \ No newline at end of file diff --git a/tests/repository_data/repository/metadata.staged/root.json b/tests/repository_data/repository/metadata.staged/root.json index bfe2d99035e399db0dcc288ddf034528d535dd9a..448c2d0af83146ae98debd826aaf089c7178551b 100644 GIT binary patch literal 3766 zcmd6q+pgj`7KZQZDYo`4)53??vHLDSfN&<^Jd9dSY(qFCfh0g^)w_?`yQ_oJ#}X`Z|ztPlkxvIve8 zWt0($p@I=5z{Y}8tvL#HD6kbpp#`CYSSTVb70L)G4Br3t_A%gD75nsKL_ctX!1-ws z1mD|?hSyXf#3(`lg;GLd3#dpKQ4K6cIkzIE1lCMp!l>lf0+xTkkQj!@ zvMsJS*0v%LF{rgxAmj*P$)Q%<=2!s26f%7Y0z}J*2xx`EXu{geVLC>NL)&`E0U|Ox z0)Q1sDK){`LN-;}YzjsMaHC(Dr_#vcwvvMCh@voJj1#j}*enTfq{5I&z|7WB!igkA zbBseFg$)6;g>3>Gam1+L5GjUAWC-64C||$4{o_T<2bb!_%ZtbNUy3SIFGl<{ zEk3b%kJ2QM^Fk%XFL@%ed~aHQe$(QVs}JVh)gy~PxrWRTLnr{?v&GQ;!u=EVA13_U zKp%(ZyUuGazR~^h_$E%@=QFFx%J39NujTyo`ZU~*H+q}Q8w0QT)_2z*54XwC^BUg4 z88vpt%l;U5_#HHysoQp(anqSH=hfca_050Rc`P=^@idg>D6CXn-stUe-zLl9vNP(n z8rdvcG_LOP={#F?=C4?p0zL~Jw_d5Wv|QeQTKBGh-Y<)}gQu7EWv*TOw%R7UL%++i zgr#!jY);n^^}@)VP<$M#p5rsm598S-7^mTh^I#d5_JbDfhjYJ4Fg~jB)2!m^Pg~W~ z9mIJs7!J?16&UMf{dq2wUUp&UP~Gp1D_Fbrg+4BE2VijV>dAE2&Z^FQr?$zVzs{>h z1!(Vp4);;7RQ%lFa6!|)*Phw^{F-|@Jd$%tI?G~qD2nJQymRRYeF-tyfjg*5vqukmMVtN8n>>4$D&belhNI4Ia8;x zO+JF-j#~d3uNS-@_?n+@DUT70F`IKJBAX+NY9)lWg^aL>AXO?d>C2p)kPF0tNdPtg zN?O#sim5r@=r>|s+4wI4t7;A6?&UPNnw;w$Fu~8t+a^!@*v*Q#rrk{@p4He-r^gmm zE01gz`{t=9p6LL=PA6g0s5|SQL%WmtPJF?c4EHx{xlQ~zYQ(pzheg^7Ziw2KJY3#) zi}W;?*UkBG0ZwnwTm=>Hg#ZLumBXxfALUebIvljB;zsvY!s-5;)qb=)R_daQy+3-kc=QKB1e|DJR$ zvLD=5vyEWqXf03YoG^fl&wp!;`SI zme?|tiBg7MDP96aP4xjn>c|$DAgM7@FcgHFI{H1UewO*a2&|LO8m*?6PkOygvs_c! zZ8+P+^N&92O`mNJ`-R_fc1P(4_4#a(CFg0kGnSnhmq4tWF&WotohkJ0XY9_%tS*-0 z);ilJv~Zp4U|z=OT5V}pH1?XUSz{095VgZmE9jL0qh-A5*hmk{^Rax^Ew;%{yXW5S zW;c_u^mHcXr?X!_C6i@|FN3OK*LNX{UEh7~66eyNY=*VL9@f{xYRCB_3{eHIEv7+BOs7%Y0Dy985cxsKp_=UDVs)+VITk!j2Wg(#)={djZLz_ z|3k*gh*M-bAl5pNV*gmXleIaiqfMIU&Dzw)a=m%pP7$;(Fe_)#vDKc8qu{vOUq_Q# zufGISyz#z-1PN|FYZb;clBv&en_Apy)lRmd}H(sikEO5C-zD- zp6sIln4aK+yEzxzkzd8*E~*>&(MAS^7WrC>qN}} literal 3781 zcmd5cLL!ahEe7C0s#_kAzZ5d_c5Gvs%kWv>GV8I z5uhke_FmgwUVHhMA3i=W={gt1^{g+SKYjfDhmVh6?C;}qryr@ZcRn4-l+X!ASP7t% zgf60#7^JWu*hM&T9ET(dJH{mfN}fo^;Y?zJ?Xytu&%b@V17^C|9NrN}V{bh6ejJX+ z-^MJ4S1Kl{jY$Y+nh~BrffJwsw(*gx5n{k`1*Q}u$T?#q0X#vB1Dl>Of^AaQF@zh< z5my{KSU3(P0x=B?lR{w|NxQ(|P#A!Lb`)|G2|2Z31tZ$kN;9aiqL3kl5`t|`gaD>o z3J#G<9Ks|LSONl=i=~?gmnfEKpcQfiQ+7V6kv1bX33d>bTyv?grh+45p-Ux^bRd*=avhEm3rI0!fJ_)rnm`T^cZ{6_X_zpC z08XKSwra;-Ae}fUG4^!?xPXca!yqs&vc(!q0T2vmsU2-6jA{viMAUVqWAPYfpR$EB z$4~|!w=Xj7VgNPfAQ46?M2I4gQUi5j^Chl^N^9*h;o1~NVPuF=7J@`f+I4gpeG!4# z(v+~!2-DJG*j2(_3k%xS0%}F{rzOg-KYaY-#h5oQ^@}erK7RgOJdXOskXD(QnRu>iaSM!s|~282b@G`~3jmr}y_`I`O|ovSfd; z{|ocKoA6%)z4y>J*yoG*O7{2dQtZFwv&9{+$v%~@>VABEYC*FdehjCzUfciZ2J!pm zD(|=3wf5EvYwO)0hG8?Oa9_8r}ZIQPZgjH^qWHvOaU3-5e{(RAdS3cCXCp;cb+ zQF(WDFUUvk_K1@J-u&l4tv8~3I;s)NB=K&feHuMw-bpcDIP*=trk%b&3*v`xR{6A; z)M;F8EQ=#p9$HS`W@VgcOXgdhbQHlRK+i?ak4W{@GsCA<-tlNUYtDxODZ}CJG>FZD zX4zp&ooadPvlD1;l9X3am6jli_oKBIBZmA+J2!d&%XF1L=v-b=-*waZeVio?aN8(a z^A;j(?H-2~^MbvjhO>KXUBs+IpDI2K&wdE1`@55^r@d8v<|dGn)1wm6ZWTPc!$FhN z#svDO(IJMHau~)#%vxEJPohb+492&Et0viG3Wv8|1$9?>`Dciga6?}m1tDpq#?*?l zCMbc(p)S;1SUuJ*AyR1DfsB(lfC|A~ZTlT%wtM~+h;^Wit2bHiALG%0S1;}|2&vtv zadF$q3k_=ndhNG|`3_ZrJlyxrA<5(%>xMArZn#)<;%KoPR;mdqKo?%p-01+OXRCZa zi4Kl48Wi)vtQ+>)lV)rh`lc_=?=tVw*U>=0P1X;0D3a@Mc&?4WrsNy)2xl%%D;&2)4(0BYh5wTfWO+Vu^M8 z6N+WoM6?54j9sD)O{^p-?h>l3E74Fe!L^23V(XqLFbM%z*(p*I?g=nzc($1)Wc%cyH>)i>>&Zn=sEf9-Ph0tNoz((Xwq)idCZ9g#loq{N zj`4lxh~0DEtWDa%*|p@Z_1rtl`|ECa>OJ=De!V@bwWjTAW9Rkzxsfv7^rCUbAJv)H zs9xulYI&ve$u@fA`@oCsqL$I})Vue5o@FsitGihDl1t~lJlvj*8|j+aBI<4RY>N>% zvmQ`9KJV#-FI%p6_YYR{w!VAvX4MG~hSkP6$Q}~|`iZ}+&+3c)=$48K2$Bk!?&hOj zbGPlR%k{>uK7Bc;_w;>HuJX$Nq*yP9_r;u@5Bv9#<=okw1$k?;0o z?{54ymalNd#@fQoVgIt1@HO*}O?$;lF{U;@tK6d~B diff --git a/tests/repository_data/repository/metadata.staged/snapshot.json b/tests/repository_data/repository/metadata.staged/snapshot.json index 3e46e1fd7e3f610b04e4c8f5cd4fc61efcaf3990..2b3323dd90d7c44cb2a369f9cae10006ea6046a7 100644 GIT binary patch literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 literal 1396 zcma)+!EPKk42JK1iou+dl%hn6$}Q*%6y5ez6a^_!Ubl(Y*xeRQ1HXGYW2fjL28^8< zEJlL(^Z!52mz!n1+}+;m_0O|k*7wUVH_P%R?q&VMKi`dbvs#-qS0cndyO&C-I-w=Z zm6JEcNzO!Fz4kG)WvHr+-G^7nWb2z{Kk$=ZkEi{_9}gc6hYxRmI2^vNg@&zEX(;sIED@K=OWsWg&@x*z|0!Fa~yjXIY44RE>3bve^_1IfE zq=8UYWYe4on`ACCEb>Ie7E*bJbMDj!C+wwi<(5rqt1M=%xD*%wAW51@D-@z}M#I`C zXKG_|AsLZI5rvr$2oR*DQuDAlDg38V^570isWCYTG8&}Zc}5k=JaHn;C|=U+**SN^ z(Y1*dOB8@s(Px^`gSyNiWUvM(k2lM&8!|X+@i10OzIlZ)i5AZ3Aotm2(PC&H4FQ##Ds{uHT1l-6^%X3!Lokz z``ha=4$f85w)DKRUiEzI*URgL)!yc;8{m|^2f-SE#xJ4|e+AHF=9mSKzb!$ba6UuN zJqF2gH1~qA)Tq29?A`4@uQIJN$|#fK!@)Y-0QEU?GmJa}aX#4Jb`snHP)(MdgW#-X^4LZs;6{+?3h6S%>Q1IG0oT}K3q{UnWy)k^- hV3i%!|C0M(rcHQP7mue{*T4LHx!XyLupe(8{{>5zX`%oC diff --git a/tests/repository_data/repository/metadata.staged/targets.json b/tests/repository_data/repository/metadata.staged/targets.json index 067100674e3d7e2c7ab3176430787ac05029a0ee..2c60420ffde3e41c912b5608935066625af10040 100644 GIT binary patch delta 1109 zcmbu7u~wru5QR^Y-4xkkA0p`}ENP^X)Gc5G#>O@n+rXjGj7A1yY%nb0r*WC>k{sS3 z50EA=808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jci?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+ng^7 zHfGSY8}Vmvoz=t3;(B$uycl)u-rn@Y-JR&W(ZaM|H~PXaR_8Vxh>4x2@#X8j%-73F zSgdE`*6gl#KkH1Fi(}IsAZ5zraR604lj>8%k|UKTQFQ62aB8O@OjjI+^p}z zw$=B;?kwN-E?&}lV_T{<*m=Er-W^}B#`}qT@%Z-4{yMqo_|9?S$>r!^JZV;&)P(i) zmUkxI?sRl`(Z311Gj})}-#ri7cKx(oH%-4P^!!=tcJy^%?C@>g8!Y^^+}_M#7sgei zz1wQ@bouc0d^0?V-2*qfJid56O8b}RgO}dkvVHJ)y=q5j-=DMxqoXnO)ro&Oo%U|t zIxR=x2elwr%JWgJ_&%=jid9^9MhpXAc(c>8%p7dL@p1)r8-amch|9`B%KYsg3 Qhggq?N~wQ_KaRiq7scgE!2kdN delta 1124 zcmbu6yKd*-&9>IgC>K3FcSjNkgoGC%EF_^KnM=1oh##?BrG#)dvt4=v zKY$azpvQg%S4~YaJ=N#?&ggcw2&-s_ymvV&-mV-nVw=yd`QQh;{CMQ5B-ln+@UcwJT-V90@NW*@Yp z7!{K=QEM~-l0hkng60%-0Yw{2SkNgr0P;vW7goqxhC<~%h(cXhDaAmkyh$by+J-1h z5IL1AiR`>O$dbop;{pcjlZY6DB$*MFL(T#rX_*ZgsTfLLDwS&%=(ANm%F;;0WSP*3 zBq@7ug9%oG^C>IsU1^OSj=Q9 zQetHK7D>YEZ(m;>8Ix3snu#^3k~mrrl31h=Rdm6j4bmi`K%3Gh$W*GfKqx3mAj7Xe zeDnFRpqJ_P;U?MXwP zw?0{MwehRdTH|~d!}DFUrMjYJpYzG(JzXxT+KO?tZFMH~J}+ZF3&iZ|)0;pVmGa$KGC!$XgT+>q;PvWY?cOMX( Z`w2uj?*V@P@4O#gH9!1)^xdyt{R3*QP6Ge{ diff --git a/tests/repository_data/repository/metadata.staged/targets.json.gz b/tests/repository_data/repository/metadata.staged/targets.json.gz index 848797c02423d7d82a56a8a44bbb58888909414b..8aed9cb52b1814978351ad158605afda3b2234ee 100644 GIT binary patch literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJW#1c-4G}L&bj1dr{V8lwiS&>Ad109of z(t!6O0asp1rW^^1aSq^Q3Qi(UrahK~$lweiU>V4WP)U^BAt-AJMbHQpf_D&bk0&5h zYUinPGzNSG8jTekvoTQ_$DFo-dgdJm9vKscrAQeGl1If97l~lDfV{{=nT=Xl&#@k! z1)YEy8aPC2wNp-WVr+;)T7%Ri=>^~w))2>;(kTGmrMUD64TK9y2MUT~4#sPU$|ue_ zCc>>t#ONp;QBs)bd=d!FI?Je}%txy{{)sWI1>wRH{P4rGf}vCP)$lmd|TKIV2jK7v4C-wBssJL8L-%2oFe`Ccr3oPC%viNV~v4 zP^=1f(C&VHt|JZ4_WUJ#a)W$cyJgCrw^QR4 zE4_IduP4X#?(<`7I(q0V>h*`z_Up;gt>e|=^`!CGg!y_?-VI-eU+-J(_GXaVZh87N zyPb5N9{TZu&Bo;RS>|?D%t_7OF>y9?g!U%)_x*W4oXOhr-EZYpQEQxM&GSKi ze|||Cq45g6(aUMny~*UF2>s>hsLn5%D@||Kwm3VkUp#sD^0mG{rQm4&y+8V`nw-&7Ee%-WbxeWd;Eb3-< z+*l6sldgO0KtI)anU6L;j!zeTB)&7BOl~hHleV}je z!pq%kr^a&M6SXZ6`k~8(D@T6;P)p+}@L$X4@*knf5e|USe7bEo`jO(dqaSHj;dwFM z4~Z%x#(W|a-xWeW@7|UEP2(-!ZF`^EftyNo3i+@cVPH;w=4=Ow z{bx`@vH&>Zj3gu)Buxmk<%p(Aa2!aqK^$4=(uQkr7C6!-6@}!f69I?7zk;$yhf*J^ Xyn$7LZnkIP?da`Ku^BNLq6Yu~rl(7G diff --git a/tests/repository_data/repository/metadata.staged/targets/role1.json b/tests/repository_data/repository/metadata.staged/targets/role1.json index c72f2ba93ff1fa70fe1d8b77b2348d243b46e754..16a6a8562e20cdb86843e22cd770de08c7df0726 100644 GIT binary patch literal 971 zcmX|=-EI^y42AFe6sx)B$8qe$;jV9hSng1T9LL#RQCid;5D4|%@oc+Xj7Ety$H&Lt z{Jq&N=hNXq*Pl;*UOw!;-0XILDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ literal 986 zcmX|A!EPKk488j+26L_(ilQhfw;(?dbkkE16cj~z7jbRFJ1r6f{`YX!-T;~zh#b8~ zK0f`tS=RIE?m@Qyp6$H8U%uQd%g=b0^_%@T&4{ebN;-<07o*8IDx_?lR0fSE4Xb3< zL{NAZhOAk71;b<`gR!rFEEm9kZ96_*5dS)SI2=CQemWekxu{{MYGf=1H7k`0Jmvt% zo@}~$>RF8#)d}WcL57)!HXms+jNZCvooNWE3QnvfLS8^;w=p`m(Ulx)ZUyqFxwaao zXlg_22sx`qqobl*i)bPm(QS?vP`MD#j!l9SnYj#5z)YxvM^A!LnA4=B$r2i;*m%ml zL!9oF69)T4tCgBV^-)O+&f06iK<#a2Es3O==GIV#U{xv7C4`pgwMhXOY%Pss+!CmX zMn9}sXKR*InE~CCx@R`06n{^RVTrbhJ;GG_Ah0${<(`|zo2inO*}cz95G_&nked6P z(t&$O2O8utZffFHckPv|stadKm9n!>gHpJ(G=Yj$R7#00AWALW8$gXSeJ2c2Cql?z z5@U!7wM6k{B1E5g${I#_xmmvMj=A{KcE9X?T>sgAJlXD%&+>d{+xgWw>$H2jlkN2Q zu)FuSb1$2hH{(4&-oJYL>kfYjuJ--u^y>H(p|x8`u@->$m$m%;`QsbG_C|LFf9I!r z%YSa)w=3VW9_4(zFkJYf*z-}a@Lp}A@0b0W$8jY}_lk`5Z zah<7i&rZX7b)eK*8>qyRn*vYcTnuQYIeALMK1xbi6N*R#SxfVtYYrPWLtURlnr4k! z$tTw|l}0!B_?b2uRB|4ea@d3H92ZA814JsV)}B%`%(*nSTBfD~LsG*=Ors)JbOVDP z%3;7{@K%xXa6s3lWyCaV3{A|?EN1Mp@{IFKMRMaWE$>386*X z5)Hj-)r^@Vlshq$*(J0PO6go%2xB40;@v=O#L*!oNoXn=ZQGb+q=?36v!O|wY(Bxb z7Y@Z$^F~$T+7U2uO+aOp<-TGYMiC{`atF*J!B^B2_FF zOJ5(h{d)QIq_=-Q`*nZ6{r0eJUvqE!U;gE?@??+c&1+#IZXVqR`x0(_mD#-LP;oCl zNk{7t)_~UNMQrq1JTUejwmaaX-_Fl>#9vMyPNxs=emt(3c0gPVE z2d-G1X2bkDo9DK?tl&V57SXa5p2kuPZ5ApVy;=x+qDXDU>XC^p>82{d(hHVpbxkB{ zrHm$FQi*6mw;&~{mDB{$YvTrUSvD*x5ml%vdKX4+3!>FnQ|AOuXv>l*a+iU#uZm-q z8BKUusFO!EYHN7}Rs%Gyk{YUY#h~7-QsdBxvWBZhh>yWBngaB-lzXIfk?Aw(prC;F zhObUIXbuTzr}bO17CO#aKQRi0G(EU9_pk?K-%1wal=qllyo zN;%U2>K!B}vZqnLJ#4=pj=B5N55F9K-2b?JdGW&|A1{x7z3Jo2t9N$)@8$C9-5p_! zcTjR|0PpW>`}N~bZ;9zYeM3v|Cw;k|pKm|@d3}ERe!A^vy`Jyy;Qj`(?DeeJ_^8`4 z$vbte1s1~~n}XyiaV#W;9Gs%2d4@R&D2)y_mp0c?QRdNGcK7-iw*7B^`gA)daH+ya VDEoek?*IDd>*Y?XJmmSe{{g}N_yGU_ diff --git a/tests/repository_data/repository/metadata/root.json b/tests/repository_data/repository/metadata/root.json index bfe2d99035e399db0dcc288ddf034528d535dd9a..448c2d0af83146ae98debd826aaf089c7178551b 100644 GIT binary patch literal 3766 zcmd6q+pgj`7KZQZDYo`4)53??vHLDSfN&<^Jd9dSY(qFCfh0g^)w_?`yQ_oJ#}X`Z|ztPlkxvIve8 zWt0($p@I=5z{Y}8tvL#HD6kbpp#`CYSSTVb70L)G4Br3t_A%gD75nsKL_ctX!1-ws z1mD|?hSyXf#3(`lg;GLd3#dpKQ4K6cIkzIE1lCMp!l>lf0+xTkkQj!@ zvMsJS*0v%LF{rgxAmj*P$)Q%<=2!s26f%7Y0z}J*2xx`EXu{geVLC>NL)&`E0U|Ox z0)Q1sDK){`LN-;}YzjsMaHC(Dr_#vcwvvMCh@voJj1#j}*enTfq{5I&z|7WB!igkA zbBseFg$)6;g>3>Gam1+L5GjUAWC-64C||$4{o_T<2bb!_%ZtbNUy3SIFGl<{ zEk3b%kJ2QM^Fk%XFL@%ed~aHQe$(QVs}JVh)gy~PxrWRTLnr{?v&GQ;!u=EVA13_U zKp%(ZyUuGazR~^h_$E%@=QFFx%J39NujTyo`ZU~*H+q}Q8w0QT)_2z*54XwC^BUg4 z88vpt%l;U5_#HHysoQp(anqSH=hfca_050Rc`P=^@idg>D6CXn-stUe-zLl9vNP(n z8rdvcG_LOP={#F?=C4?p0zL~Jw_d5Wv|QeQTKBGh-Y<)}gQu7EWv*TOw%R7UL%++i zgr#!jY);n^^}@)VP<$M#p5rsm598S-7^mTh^I#d5_JbDfhjYJ4Fg~jB)2!m^Pg~W~ z9mIJs7!J?16&UMf{dq2wUUp&UP~Gp1D_Fbrg+4BE2VijV>dAE2&Z^FQr?$zVzs{>h z1!(Vp4);;7RQ%lFa6!|)*Phw^{F-|@Jd$%tI?G~qD2nJQymRRYeF-tyfjg*5vqukmMVtN8n>>4$D&belhNI4Ia8;x zO+JF-j#~d3uNS-@_?n+@DUT70F`IKJBAX+NY9)lWg^aL>AXO?d>C2p)kPF0tNdPtg zN?O#sim5r@=r>|s+4wI4t7;A6?&UPNnw;w$Fu~8t+a^!@*v*Q#rrk{@p4He-r^gmm zE01gz`{t=9p6LL=PA6g0s5|SQL%WmtPJF?c4EHx{xlQ~zYQ(pzheg^7Ziw2KJY3#) zi}W;?*UkBG0ZwnwTm=>Hg#ZLumBXxfALUebIvljB;zsvY!s-5;)qb=)R_daQy+3-kc=QKB1e|DJR$ zvLD=5vyEWqXf03YoG^fl&wp!;`SI zme?|tiBg7MDP96aP4xjn>c|$DAgM7@FcgHFI{H1UewO*a2&|LO8m*?6PkOygvs_c! zZ8+P+^N&92O`mNJ`-R_fc1P(4_4#a(CFg0kGnSnhmq4tWF&WotohkJ0XY9_%tS*-0 z);ilJv~Zp4U|z=OT5V}pH1?XUSz{095VgZmE9jL0qh-A5*hmk{^Rax^Ew;%{yXW5S zW;c_u^mHcXr?X!_C6i@|FN3OK*LNX{UEh7~66eyNY=*VL9@f{xYRCB_3{eHIEv7+BOs7%Y0Dy985cxsKp_=UDVs)+VITk!j2Wg(#)={djZLz_ z|3k*gh*M-bAl5pNV*gmXleIaiqfMIU&Dzw)a=m%pP7$;(Fe_)#vDKc8qu{vOUq_Q# zufGISyz#z-1PN|FYZb;clBv&en_Apy)lRmd}H(sikEO5C-zD- zp6sIln4aK+yEzxzkzd8*E~*>&(MAS^7WrC>qN}} literal 3781 zcmd5cLL!ahEe7C0s#_kAzZ5d_c5Gvs%kWv>GV8I z5uhke_FmgwUVHhMA3i=W={gt1^{g+SKYjfDhmVh6?C;}qryr@ZcRn4-l+X!ASP7t% zgf60#7^JWu*hM&T9ET(dJH{mfN}fo^;Y?zJ?Xytu&%b@V17^C|9NrN}V{bh6ejJX+ z-^MJ4S1Kl{jY$Y+nh~BrffJwsw(*gx5n{k`1*Q}u$T?#q0X#vB1Dl>Of^AaQF@zh< z5my{KSU3(P0x=B?lR{w|NxQ(|P#A!Lb`)|G2|2Z31tZ$kN;9aiqL3kl5`t|`gaD>o z3J#G<9Ks|LSONl=i=~?gmnfEKpcQfiQ+7V6kv1bX33d>bTyv?grh+45p-Ux^bRd*=avhEm3rI0!fJ_)rnm`T^cZ{6_X_zpC z08XKSwra;-Ae}fUG4^!?xPXca!yqs&vc(!q0T2vmsU2-6jA{viMAUVqWAPYfpR$EB z$4~|!w=Xj7VgNPfAQ46?M2I4gQUi5j^Chl^N^9*h;o1~NVPuF=7J@`f+I4gpeG!4# z(v+~!2-DJG*j2(_3k%xS0%}F{rzOg-KYaY-#h5oQ^@}erK7RgOJdXOskXD(QnRu>iaSM!s|~282b@G`~3jmr}y_`I`O|ovSfd; z{|ocKoA6%)z4y>J*yoG*O7{2dQtZFwv&9{+$v%~@>VABEYC*FdehjCzUfciZ2J!pm zD(|=3wf5EvYwO)0hG8?Oa9_8r}ZIQPZgjH^qWHvOaU3-5e{(RAdS3cCXCp;cb+ zQF(WDFUUvk_K1@J-u&l4tv8~3I;s)NB=K&feHuMw-bpcDIP*=trk%b&3*v`xR{6A; z)M;F8EQ=#p9$HS`W@VgcOXgdhbQHlRK+i?ak4W{@GsCA<-tlNUYtDxODZ}CJG>FZD zX4zp&ooadPvlD1;l9X3am6jli_oKBIBZmA+J2!d&%XF1L=v-b=-*waZeVio?aN8(a z^A;j(?H-2~^MbvjhO>KXUBs+IpDI2K&wdE1`@55^r@d8v<|dGn)1wm6ZWTPc!$FhN z#svDO(IJMHau~)#%vxEJPohb+492&Et0viG3Wv8|1$9?>`Dciga6?}m1tDpq#?*?l zCMbc(p)S;1SUuJ*AyR1DfsB(lfC|A~ZTlT%wtM~+h;^Wit2bHiALG%0S1;}|2&vtv zadF$q3k_=ndhNG|`3_ZrJlyxrA<5(%>xMArZn#)<;%KoPR;mdqKo?%p-01+OXRCZa zi4Kl48Wi)vtQ+>)lV)rh`lc_=?=tVw*U>=0P1X;0D3a@Mc&?4WrsNy)2xl%%D;&2)4(0BYh5wTfWO+Vu^M8 z6N+WoM6?54j9sD)O{^p-?h>l3E74Fe!L^23V(XqLFbM%z*(p*I?g=nzc($1)Wc%cyH>)i>>&Zn=sEf9-Ph0tNoz((Xwq)idCZ9g#loq{N zj`4lxh~0DEtWDa%*|p@Z_1rtl`|ECa>OJ=De!V@bwWjTAW9Rkzxsfv7^rCUbAJv)H zs9xulYI&ve$u@fA`@oCsqL$I})Vue5o@FsitGihDl1t~lJlvj*8|j+aBI<4RY>N>% zvmQ`9KJV#-FI%p6_YYR{w!VAvX4MG~hSkP6$Q}~|`iZ}+&+3c)=$48K2$Bk!?&hOj zbGPlR%k{>uK7Bc;_w;>HuJX$Nq*yP9_r;u@5Bv9#<=okw1$k?;0o z?{54ymalNd#@fQoVgIt1@HO*}O?$;lF{U;@tK6d~B diff --git a/tests/repository_data/repository/metadata/snapshot.json b/tests/repository_data/repository/metadata/snapshot.json index 3e46e1fd7e3f610b04e4c8f5cd4fc61efcaf3990..2b3323dd90d7c44cb2a369f9cae10006ea6046a7 100644 GIT binary patch literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 literal 1396 zcma)+!EPKk42JK1iou+dl%hn6$}Q*%6y5ez6a^_!Ubl(Y*xeRQ1HXGYW2fjL28^8< zEJlL(^Z!52mz!n1+}+;m_0O|k*7wUVH_P%R?q&VMKi`dbvs#-qS0cndyO&C-I-w=Z zm6JEcNzO!Fz4kG)WvHr+-G^7nWb2z{Kk$=ZkEi{_9}gc6hYxRmI2^vNg@&zEX(;sIED@K=OWsWg&@x*z|0!Fa~yjXIY44RE>3bve^_1IfE zq=8UYWYe4on`ACCEb>Ie7E*bJbMDj!C+wwi<(5rqt1M=%xD*%wAW51@D-@z}M#I`C zXKG_|AsLZI5rvr$2oR*DQuDAlDg38V^570isWCYTG8&}Zc}5k=JaHn;C|=U+**SN^ z(Y1*dOB8@s(Px^`gSyNiWUvM(k2lM&8!|X+@i10OzIlZ)i5AZ3Aotm2(PC&H4FQ##Ds{uHT1l-6^%X3!Lokz z``ha=4$f85w)DKRUiEzI*URgL)!yc;8{m|^2f-SE#xJ4|e+AHF=9mSKzb!$ba6UuN zJqF2gH1~qA)Tq29?A`4@uQIJN$|#fK!@)Y-0QEU?GmJa}aX#4Jb`snHP)(MdgW#-X^4LZs;6{+?3h6S%>Q1IG0oT}K3q{UnWy)k^- hV3i%!|C0M(rcHQP7mue{*T4LHx!XyLupe(8{{>5zX`%oC diff --git a/tests/repository_data/repository/metadata/targets.json b/tests/repository_data/repository/metadata/targets.json index 067100674e3d7e2c7ab3176430787ac05029a0ee..2c60420ffde3e41c912b5608935066625af10040 100644 GIT binary patch delta 1109 zcmbu7u~wru5QR^Y-4xkkA0p`}ENP^X)Gc5G#>O@n+rXjGj7A1yY%nb0r*WC>k{sS3 z50EA=808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jci?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+ng^7 zHfGSY8}Vmvoz=t3;(B$uycl)u-rn@Y-JR&W(ZaM|H~PXaR_8Vxh>4x2@#X8j%-73F zSgdE`*6gl#KkH1Fi(}IsAZ5zraR604lj>8%k|UKTQFQ62aB8O@OjjI+^p}z zw$=B;?kwN-E?&}lV_T{<*m=Er-W^}B#`}qT@%Z-4{yMqo_|9?S$>r!^JZV;&)P(i) zmUkxI?sRl`(Z311Gj})}-#ri7cKx(oH%-4P^!!=tcJy^%?C@>g8!Y^^+}_M#7sgei zz1wQ@bouc0d^0?V-2*qfJid56O8b}RgO}dkvVHJ)y=q5j-=DMxqoXnO)ro&Oo%U|t zIxR=x2elwr%JWgJ_&%=jid9^9MhpXAc(c>8%p7dL@p1)r8-amch|9`B%KYsg3 Qhggq?N~wQ_KaRiq7scgE!2kdN delta 1124 zcmbu6yKd*-&9>IgC>K3FcSjNkgoGC%EF_^KnM=1oh##?BrG#)dvt4=v zKY$azpvQg%S4~YaJ=N#?&ggcw2&-s_ymvV&-mV-nVw=yd`QQh;{CMQ5B-ln+@UcwJT-V90@NW*@Yp z7!{K=QEM~-l0hkng60%-0Yw{2SkNgr0P;vW7goqxhC<~%h(cXhDaAmkyh$by+J-1h z5IL1AiR`>O$dbop;{pcjlZY6DB$*MFL(T#rX_*ZgsTfLLDwS&%=(ANm%F;;0WSP*3 zBq@7ug9%oG^C>IsU1^OSj=Q9 zQetHK7D>YEZ(m;>8Ix3snu#^3k~mrrl31h=Rdm6j4bmi`K%3Gh$W*GfKqx3mAj7Xe zeDnFRpqJ_P;U?MXwP zw?0{MwehRdTH|~d!}DFUrMjYJpYzG(JzXxT+KO?tZFMH~J}+ZF3&iZ|)0;pVmGa$KGC!$XgT+>q;PvWY?cOMX( Z`w2uj?*V@P@4O#gH9!1)^xdyt{R3*QP6Ge{ diff --git a/tests/repository_data/repository/metadata/targets.json.gz b/tests/repository_data/repository/metadata/targets.json.gz index 848797c02423d7d82a56a8a44bbb58888909414b..8aed9cb52b1814978351ad158605afda3b2234ee 100644 GIT binary patch literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJW#1c-4G}L&bj1dr{V8lwiS&>Ad109of z(t!6O0asp1rW^^1aSq^Q3Qi(UrahK~$lweiU>V4WP)U^BAt-AJMbHQpf_D&bk0&5h zYUinPGzNSG8jTekvoTQ_$DFo-dgdJm9vKscrAQeGl1If97l~lDfV{{=nT=Xl&#@k! z1)YEy8aPC2wNp-WVr+;)T7%Ri=>^~w))2>;(kTGmrMUD64TK9y2MUT~4#sPU$|ue_ zCc>>t#ONp;QBs)bd=d!FI?Je}%txy{{)sWI1>wRH{P4rGf}vCP)$lmd|TKIV2jK7v4C-wBssJL8L-%2oFe`Ccr3oPC%viNV~v4 zP^=1f(C&VHt|JZ4_WUJ#a)W$cyJgCrw^QR4 zE4_IduP4X#?(<`7I(q0V>h*`z_Up;gt>e|=^`!CGg!y_?-VI-eU+-J(_GXaVZh87N zyPb5N9{TZu&Bo;RS>|?D%t_7OF>y9?g!U%)_x*W4oXOhr-EZYpQEQxM&GSKi ze|||Cq45g6(aUMny~*UF2>s>hsLn5%D@||Kwm3VkUp#sD^0mG{rQm4&y+8V`nw-&7Ee%-WbxeWd;Eb3-< z+*l6sldgO0KtI)anU6L;j!zeTB)&7BOl~hHleV}je z!pq%kr^a&M6SXZ6`k~8(D@T6;P)p+}@L$X4@*knf5e|USe7bEo`jO(dqaSHj;dwFM z4~Z%x#(W|a-xWeW@7|UEP2(-!ZF`^EftyNo3i+@cVPH;w=4=Ow z{bx`@vH&>Zj3gu)Buxmk<%p(Aa2!aqK^$4=(uQkr7C6!-6@}!f69I?7zk;$yhf*J^ Xyn$7LZnkIP?da`Ku^BNLq6Yu~rl(7G diff --git a/tests/repository_data/repository/metadata/targets/role1.json b/tests/repository_data/repository/metadata/targets/role1.json index c72f2ba93ff1fa70fe1d8b77b2348d243b46e754..16a6a8562e20cdb86843e22cd770de08c7df0726 100644 GIT binary patch literal 971 zcmX|=-EI^y42AFe6sx)B$8qe$;jV9hSng1T9LL#RQCid;5D4|%@oc+Xj7Ety$H&Lt z{Jq&N=hNXq*Pl;*UOw!;-0XILDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ literal 986 zcmX|A!EPKk488j+26L_(ilQhfw;(?dbkkE16cj~z7jbRFJ1r6f{`YX!-T;~zh#b8~ zK0f`tS=RIE?m@Qyp6$H8U%uQd%g=b0^_%@T&4{ebN;-<07o*8IDx_?lR0fSE4Xb3< zL{NAZhOAk71;b<`gR!rFEEm9kZ96_*5dS)SI2=CQemWekxu{{MYGf=1H7k`0Jmvt% zo@}~$>RF8#)d}WcL57)!HXms+jNZCvooNWE3QnvfLS8^;w=p`m(Ulx)ZUyqFxwaao zXlg_22sx`qqobl*i)bPm(QS?vP`MD#j!l9SnYj#5z)YxvM^A!LnA4=B$r2i;*m%ml zL!9oF69)T4tCgBV^-)O+&f06iK<#a2Es3O==GIV#U{xv7C4`pgwMhXOY%Pss+!CmX zMn9}sXKR*InE~CCx@R`06n{^RVTrbhJ;GG_Ah0${<(`|zo2inO*}cz95G_&nked6P z(t&$O2O8utZffFHckPv|stadKm9n!>gHpJ(G=Yj$R7#00AWALW8$gXSeJ2c2Cql?z z5@U!7wM6k{B1E5g${I#_xmmvMj=A{KcE9X?T>sgAJlXD%&+>d{+xgWw>$H2jlkN2Q zu)FuSb1$2hH{(4&-oJYL>kfYjuJ--u^y>H(p|x8`u@->$m$m%;`QsbG_C|LFf9I!r z%YSa)w=3VW9_4(zFkJYf*z-}a@Lp}A@0b0W$8jY}_lk`5Z zah<7i&rZX7b)eK*8>qyRn*vYcTnuQYIeALMK1xbi6N*R#SxfVtYYrPWLtURlnr4k! z$tTw|l}0!B_?b2uRB|4ea@d3H92ZA814JsV)}B%`%(*nSTBfD~LsG*=Ors)JbOVDP z%3;7{@K%xXa6s3lWyCaV3{A|?EN1Mp@{IFKMRMaWE$>386*X z5)Hj-)r^@Vlshq$*(J0PO6go%2xB40;@v=O#L*!oNoXn=ZQGb+q=?36v!O|wY(Bxb z7Y@Z$^F~$T+7U2uO+aOp<-TGYMiC{`atF*J!B^B2_FF zOJ5(h{d)QIq_=-Q`*nZ6{r0eJUvqE!U;gE?@??+c&1+#IZXVqR`x0(_mD#-LP;oCl zNk{7t)_~UNMQrq1JTUejwmaaX-_Fl>#9vMyPNxs=emt(3c0gPVE z2d-G1X2bkDo9DK?tl&V57SXa5p2kuPZ5ApVy;=x+qDXDU>XC^p>82{d(hHVpbxkB{ zrHm$FQi*6mw;&~{mDB{$YvTrUSvD*x5ml%vdKX4+3!>FnQ|AOuXv>l*a+iU#uZm-q z8BKUusFO!EYHN7}Rs%Gyk{YUY#h~7-QsdBxvWBZhh>yWBngaB-lzXIfk?Aw(prC;F zhObUIXbuTzr}bO17CO#aKQRi0G(EU9_pk?K-%1wal=qllyo zN;%U2>K!B}vZqnLJ#4=pj=B5N55F9K-2b?JdGW&|A1{x7z3Jo2t9N$)@8$C9-5p_! zcTjR|0PpW>`}N~bZ;9zYeM3v|Cw;k|pKm|@d3}ERe!A^vy`Jyy;Qj`(?DeeJ_^8`4 z$vbte1s1~~n}XyiaV#W;9Gs%2d4@R&D2)y_mp0c?QRdNGcK7-iw*7B^`gA)daH+ya VDEoek?*IDd>*Y?XJmmSe{{g}N_yGU_ diff --git a/tests/unit/test_util_test_tools.py b/tests/unit/deprecated/test_util_test_tools.py similarity index 100% rename from tests/unit/test_util_test_tools.py rename to tests/unit/deprecated/test_util_test_tools.py From 91242ec6aba4bf15e19d2d1552767b7f23afebb0 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 12:50:40 -0400 Subject: [PATCH 02/11] Modify the expiration date format included metadata. Convert the 'expires' field of metadata to a Unix/POSIX timestamp (previously a custom string format.) Replace tuf.formats.py functions 'format_time()' and 'parse_time()' with unix_timestamp_to_datetime() and datetime_to_unix_timestamp(). Update affected schemas. --- tuf/formats.py | 113 ++++++++++++++++++++++++++----------------------- 1 file changed, 61 insertions(+), 52 deletions(-) diff --git a/tuf/formats.py b/tuf/formats.py index 50e5bbbc..052f2a1f 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -64,6 +64,7 @@ import calendar import re import string +import datetime import time import tuf @@ -75,11 +76,7 @@ # easily backwards compatible with clients that are already deployed. # A date in 'YYYY-MM-DD HH:MM:SS UTC' format. -# TODO: Support timestamps according to the ISO 8601 standard. -TIME_SCHEMA = SCHEMA.RegularExpression(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2} UTC') - -# A date in 'YYYY-MM-DD HH:MM:SS UTC' format. -DATETIME_SCHEMA = SCHEMA.RegularExpression(r'\d{4}-\d{2}-\d{2} \d{2}:\d{2}:\d{2}') +UNIX_TIMESTAMP_SCHEMA = SCHEMA.Integer(lo=0, hi=2147483647) # A hexadecimal value in '23432df87ab..' format. HASH_SCHEMA = SCHEMA.RegularExpression(r'[a-fA-F0-9]+') @@ -367,7 +364,7 @@ # The number of seconds before metadata expires. The minimum is 86400 seconds # (= 1 day). This schema is used for the initial expiration date. Repository -# maintainers may later modify this value (TIME_SCHEMA). +# maintainers may later modify this value. EXPIRATION_SCHEMA = SCHEMA.Integer(lo=86400) # Supported compression extension (e.g., 'gz'). @@ -384,7 +381,7 @@ signing_keyids = SCHEMA.Optional(KEYIDS_SCHEMA), threshold = THRESHOLD_SCHEMA, version = SCHEMA.Optional(METADATAVERSION_SCHEMA), - expires = SCHEMA.Optional(SCHEMA.OneOf([EXPIRATION_SCHEMA, TIME_SCHEMA])), + expires = SCHEMA.Optional(SCHEMA.OneOf([EXPIRATION_SCHEMA, UNIX_TIMESTAMP_SCHEMA])), signatures = SCHEMA.Optional(SIGNATURES_SCHEMA), compressions = SCHEMA.Optional(COMPRESSIONS_SCHEMA), paths = SCHEMA.Optional(RELPATHS_SCHEMA), @@ -398,7 +395,7 @@ _type = SCHEMA.String('Root'), version = METADATAVERSION_SCHEMA, consistent_snapshot = BOOLEAN_SCHEMA, - expires = TIME_SCHEMA, + expires = UNIX_TIMESTAMP_SCHEMA, keys = KEYDICT_SCHEMA, roles = ROLEDICT_SCHEMA) @@ -407,7 +404,7 @@ object_name = 'TARGETS_SCHEMA', _type = SCHEMA.String('Targets'), version = METADATAVERSION_SCHEMA, - expires = TIME_SCHEMA, + expires = UNIX_TIMESTAMP_SCHEMA, targets = FILEDICT_SCHEMA, delegations = SCHEMA.Optional(DELEGATIONS_SCHEMA)) @@ -416,7 +413,7 @@ object_name = 'SNAPSHOT_SCHEMA', _type = SCHEMA.String('Snapshot'), version = METADATAVERSION_SCHEMA, - expires = TIME_SCHEMA, + expires = UNIX_TIMESTAMP_SCHEMA, meta = FILEDICT_SCHEMA) # Timestamp role: indicates the latest version of the snapshot file. @@ -424,7 +421,7 @@ object_name = 'TIMESTAMP_SCHEMA', _type = SCHEMA.String('Timestamp'), version = METADATAVERSION_SCHEMA, - expires = TIME_SCHEMA, + expires = UNIX_TIMESTAMP_SCHEMA, meta = FILEDICT_SCHEMA) # A schema containing information a repository mirror may require, @@ -451,7 +448,7 @@ object_name = 'MIRRORLIST_SCHEMA', _type = SCHEMA.String('Mirrors'), version = METADATAVERSION_SCHEMA, - expires = TIME_SCHEMA, + expires = UNIX_TIMESTAMP_SCHEMA, mirrors = SCHEMA.ListOf(MIRROR_SCHEMA)) # Any of the role schemas (e.g., TIMESTAMP_SCHEMA, SNAPSHOT_SCHEMA, etc.) @@ -512,8 +509,9 @@ def from_metadata(object): TIMESTAMP_SCHEMA.check_match(object) version = object['version'] - expires = parse_time(object['expires']) + expires = object['expires'] filedict = object['meta'] + return TimestampFile(version, expires, filedict) @@ -551,7 +549,7 @@ def from_metadata(object): ROOT_SCHEMA.check_match(object) version = object['version'] - expires = parse_time(object['expires']) + expires = object['expires'] keys = object['keys'] roles = object['roles'] consistent_snapshot = object['consistent_snapshot'] @@ -594,7 +592,7 @@ def from_metadata(object): SNAPSHOT_SCHEMA.check_match(object) version = object['version'] - expires = parse_time(object['expires']) + expires = object['expires'] filedict = object['meta'] return SnapshotFile(version, expires, filedict) @@ -637,7 +635,7 @@ def from_metadata(object): TARGETS_SCHEMA.check_match(object) version = object['version'] - expires = parse_time(object['expires']) + expires = object['expires'] filedict = object.get('targets') delegations = object.get('delegations') @@ -708,75 +706,86 @@ def make_metadata(): -def format_time(timestamp): +def datetime_to_unix_timestamp(datetime_object): """ - Encode 'timestamp' in 'YYYY-MM-DD HH:MM:SS UTC' format. - 'timestamp' is a Unix timestamp value. For example, it is the time - format returned by calendar.timegm(). + Convert 'datetime_object' (in datetime.datetime()) format) to a Unix/POSIX + timestamp. For example, Python's time.time() returns a Unix timestamp, and + includes the number of microseconds. 'datetime_object' is converted to UTC. - >>> format_time(499137720) - '1985-10-26 01:22:00 UTC' + >>> datetime_object = datetime.datetime(1985, 10, 26, 01, 22) + >>> timestamp = datetime_to_unix_timestamp(datetime_object) + >>> timestamp + 499137720 - timestamp: - The time to format. This is a Unix timestamp. + datetime_object: + The datetime.datetime() object to convert to a Unix timestamp. - tuf.Error, if 'timestamp' is invalid. + tuf.FormatError, if 'datetime_object' is not a datetime.datetime() object. None. - A string in 'YYYY-MM-DD HH:MM:SS UTC' format. + A unix (posix) timestamp (e.g., 499137660). """ + + # Is 'datetime_object' a datetime.datetime() object? + # Raise 'tuf.FormatError' if not. + if not isinstance(datetime_object, datetime.datetime): + message = repr(datetime_object) + ' is not a datetime.datetime() object.' + raise tuf.FormatError(message) - try: - # Convert the timestamp to 'yyyy-mm-dd HH:MM:SS' format. - formatted_time = time.strftime('%Y-%m-%d %H:%M:%S', time.gmtime(timestamp)) - - # Attach 'UTC' to the formatted time string prior to returning. - return formatted_time+' UTC' + unix_timestamp = calendar.timegm(datetime_object.timetuple()) - except (ValueError, TypeError): - raise tuf.FormatError('Invalid argument value') + return unix_timestamp -def parse_time(string): + +def unix_timestamp_to_datetime(unix_timestamp): """ - Parse 'string', in 'YYYY-MM-DD HH:MM:SS UTC' format, to a Unix timestamp. + Convert 'unix_timestamp' (i.e., POSIX time, in UNIX_TIMESTAMP_SCHEMA format) + to a datetime.datetime() object. 'unix_timestamp' is the number of seconds + since the epoch (January 1, 1970.) + + >>> datetime_object = unix_timestamp_to_datetime(1445455680) + >>> datetime_object + datetime.datetime(2015, 10, 21, 19, 28) - string: - A string representing the time (e.g., '1985-10-26 01:20:00 UTC'). + unix_timestamp: + An integer representing the time (e.g., 1445455680). Conformant to + 'tuf.formats.UNIX_TIMESTAMP_SCHEMA'. - tuf.FormatError, if parsing 'string' fails. + tuf.FormatError, if 'unix_timestamp' is improperly formatted. None. - A timestamp (e.g., 499137660). + A datetime.datetime() object corresponding to 'unix_timestamp'. """ - # Is 'string' properly formatted? + # Is 'unix_timestamp' properly formatted? # Raise 'tuf.FormatError' if there is a mismatch. - TIME_SCHEMA.check_match(string) - - # Strip the ' UTC' attached to the string. The string time, minus the ' UTC', - # is the time format expected by the time functions called below. - string = string[0:string.rfind(' UTC')] - try: - return calendar.timegm(time.strptime(string, '%Y-%m-%d %H:%M:%S')) - - except ValueError: - raise tuf.FormatError('Malformed time: '+repr(string)) + UNIX_TIMESTAMP_SCHEMA.check_match(unix_timestamp) + # Convert 'unix_timestamp' to a 'time.struct_time', in UTC. The Daylight + # Savings Time (DST) flag is set to zero. datetime.fromtimestamp() is not + # used because it returns a local datetime. + struct_time = time.gmtime(unix_timestamp) + + # Extract the (year, month, day, hour, minutes, seconds) arguments for the + # datetime object to be returned. + datetime_object = datetime.datetime(*struct_time[:6]) + + return datetime_object @@ -988,7 +997,7 @@ def make_role_metadata(keyids, threshold, name=None, paths=None, if paths is not None and path_hash_prefixes is not None: raise \ - tuf.FormatError('Both "paths" and "path_hash_prefixes" are specified!') + tuf.FormatError('Both "paths" and "path_hash_prefixes" are specified.') if path_hash_prefixes is not None: role_meta['path_hash_prefixes'] = path_hash_prefixes From 1935b1de2b58dafae27919b2e0172854493ad9ab Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 12:52:35 -0400 Subject: [PATCH 03/11] Update TUF modules affected by the change to the 'expires' format. --- tuf/__init__.py | 7 ++-- tuf/client/updater.py | 26 ++++++++---- tuf/repository_tool.py | 93 +++++++++++++++++++----------------------- 3 files changed, 64 insertions(+), 62 deletions(-) diff --git a/tuf/__init__.py b/tuf/__init__.py index 14a61f6c..c0ac4c45 100755 --- a/tuf/__init__.py +++ b/tuf/__init__.py @@ -22,6 +22,7 @@ import urlparse + # Import 'tuf.formats' if a module tries to import the # entire tuf package (i.e., from tuf import *). __all__ = ['formats'] @@ -135,11 +136,11 @@ class ForbiddenTargetError(RepositoryError): class ExpiredMetadataError(Error): """Indicate that a TUF Metadata file has expired.""" - def __init__(self, expiry_time): - self.expiry_time = expiry_time # UTC + def __init__(self, expiration_string): + self.expiration_string = expiration_string def __str__(self): - return 'Metadata expired on '+str(self.expiry_time)+'.' + return 'Metadata expired on '+str(expiration_string)+'.' diff --git a/tuf/client/updater.py b/tuf/client/updater.py index ea465383..69ef3fc3 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -1757,14 +1757,24 @@ def _ensure_not_expired(self, metadata_role): expires = self.metadata['current'][metadata_role]['expires'] # If the current time has surpassed the expiration date, raise - # an exception. 'expires' is in YYYY-MM-DD HH:MM:SS format, so - # convert it to seconds since the epoch, which is the time format - # returned by time.time() (i.e., current time), before comparing. - current_time = time.time() - expiry_time = tuf.formats.parse_time(expires) - if expiry_time < current_time: - logger.error('Metadata '+repr(rolepath)+' expired on '+repr(expires)+'.') - raise tuf.ExpiredMetadataError(expires) + # an exception. 'expires' is in 'tuf.formats.UNIX_TIMESTAMP_SCHEMA' format, + # the number of seconds since the epoch (January 1, 1970.) Compare it + # against the current time.time() (also in Unix/POSIX time format, although + # with microseconds attached.) + current_time = int(time.time()) + + # Generate a user-friendly error message if 'expires' is less than the + # current time (i.e., a local time). In contrast, the logger message + # contains the 'expires' unix timestamp. + datetime_object = tuf.formats.unix_timestamp_to_datetime(expires) + local_time = datetime_object.ctime() + if expires < current_time: + message = 'Metadata '+repr(rolepath)+' expired on '+repr(expires)+'.' + logger.error(message) + + # Expired metadata message generated by + # tuf.ExpiredMetadataError (defined in __init__.py) + raise tuf.ExpiredMetadataError(local_time) diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index 5e1a3866..dca32b6e 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -29,6 +29,7 @@ import errno import sys import time +import datetime import getpass import logging import tempfile @@ -120,7 +121,7 @@ class Repository(object): access by default: repository.root.version = 2 - repository.timestamp.expiration = "2015-08-08 12:00:00" + repository.timestamp.expiration = datetime.datetime(2015, 08, 08, 12, 00) repository.snapshot.add_verification_key(...) repository.targets.delegate('unclaimed', ...) @@ -1087,39 +1088,37 @@ def expiration(self): None. - The role's expiration datetime, conformant to - 'tuf.formats.DATETIME_SCHEMA'. + The role's expiration datetime, a datetime.datetime() object. """ roleinfo = tuf.roledb.get_roleinfo(self.rolename) + expires = roleinfo['expires'] - return roleinfo['expires'] + return tuf.formats.unix_timestamp_to_datetime(expires) @expiration.setter - def expiration(self, expiration_datetime_utc): + def expiration(self, datetime_object): """ A setter method for the role's expiration datetime. The top-level roles have a default expiration (e.g., ROOT_EXPIRATION), but may later be modified by this setter method. - TODO: expiration_datetime_utc in ISO 8601 format. - >>> >>> >>> - expiration_datetime_utc: - The datetime expiration of the role, conformant to - 'tuf.formats.DATETIME_SCHEMA'. + datetime_object: + The datetime expiration of the role, a datetime.datetime() object. - tuf.FormatError, if 'expiration_datetime_utc' is improperly formatted, - or invalid (e.g., already expired). - + tuf.FormatError, if 'datetime_object' is not a datetime.datetime() object. + + tuf.Error, if 'datetime_object' has already expired. + Modifies the expiration attribute of the Repository object. @@ -1127,31 +1126,23 @@ def expiration(self, expiration_datetime_utc): None. """ - # Does 'expiration_datetime_utc' have the correct format? - # Ensure the arguments have the appropriate number of objects and object - # types, and that all dict keys are properly named. - # Raise 'tuf.FormatError' if any are improperly formatted. - tuf.formats.DATETIME_SCHEMA.check_match(expiration_datetime_utc) - - # Further validate the datetime, such as a correct date, time, expiration. - # Convert 'expiration_datetime_utc' to a unix timestamp so that it can be - # compared with time.time(). - expiration_datetime_utc = expiration_datetime_utc + ' UTC' - try: - unix_timestamp = tuf.formats.parse_time(expiration_datetime_utc) - - except (tuf.FormatError, ValueError), e: - message = 'Invalid datetime argument: '+repr(expiration_datetime_utc) - raise tuf.FormatError(message) - - # Ensure the expiration has not already passed. - if unix_timestamp < time.time(): + # Is 'datetime_object' a datetime.datetime() object? + # Raise 'tuf.FormatError' if not. + if not isinstance(datetime_object, datetime.datetime): + message = repr(datetime_object) + ' is not a datetime.datetime() object.' + raise tuf.FormatError(message) + + # Ensure the expiration has not already passed. + current_datetime = tuf.formats.unix_timestamp_to_datetime(int(time.time())) + if datetime_object < current_datetime: message = 'The expiration date must occur after the current date.' - raise tuf.FormatError(message) + raise tuf.Error(message) # Update the role's 'expires' entry in 'tuf.roledb.py'. roleinfo = tuf.roledb.get_roleinfo(self.rolename) - roleinfo['expires'] = expiration_datetime_utc + unix_timestamp = tuf.formats.datetime_to_unix_timestamp(datetime_object) + roleinfo['expires'] = unix_timestamp + tuf.roledb.update_roleinfo(self.rolename, roleinfo) @@ -1307,7 +1298,7 @@ def __init__(self): # By default, 'snapshot' metadata is set to expire 1 week from the current # time. The expiration may be modified. - expiration = tuf.formats.format_time(time.time()+ROOT_EXPIRATION) + expiration = int(time.time()+ROOT_EXPIRATION) roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, 'signatures': [], 'version': 0, 'consistent_snapshot': False, @@ -1367,7 +1358,7 @@ def __init__(self): # By default, 'snapshot' metadata is set to expire 1 week from the current # time. The expiration may be modified. - expiration = tuf.formats.format_time(time.time()+TIMESTAMP_EXPIRATION) + expiration = int(time.time()+TIMESTAMP_EXPIRATION) roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, 'signatures': [], 'version': 0, 'compressions': [''], @@ -1421,7 +1412,7 @@ def __init__(self): # By default, 'snapshot' metadata is set to expire 1 week from the current # time. The expiration may be modified. - expiration = tuf.formats.format_time(time.time()+SNAPSHOT_EXPIRATION) + expiration = int(time.time()+SNAPSHOT_EXPIRATION) roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, 'signatures': [], 'version': 0, 'compressions': [''], @@ -1507,7 +1498,7 @@ def __init__(self, targets_directory, rolename='targets', roleinfo=None): # By default, Targets objects are set to expire 3 months from the current # time. May be later modified. - expiration = tuf.formats.format_time(time.time()+TARGETS_EXPIRATION) + expiration = int(time.time()+TARGETS_EXPIRATION) # If 'roleinfo' is not provided, set an initial default. if roleinfo is None: @@ -2049,7 +2040,7 @@ def delegate(self, rolename, public_keys, list_of_targets, # Create a new Targets object for the 'rolename' delegation. An initial # expiration is set (3 months from the current time). - expiration = tuf.formats.format_time(time.time()+TARGETS_EXPIRATION) + expiration = int(time.time()+TARGETS_EXPIRATION) roleinfo = {'name': full_rolename, 'keyids': keyids, 'signing_keyids': [], 'threshold': threshold, 'version': 0, 'compressions': [''], 'expires': expiration, 'signatures': [], 'partial_loaded': False, @@ -3860,8 +3851,8 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot): trusted. expiration_date: - The expiration date, in UTC, of the metadata file. Conformant to - 'tuf.formats.TIME_SCHEMA'. + The expiration date of the metadata file. Conformant to + 'tuf.formats.UNIX_TIMESTAMP_SCHEMA'. consistent_snapshot: Boolean. If True, a file digest is expected to be prepended to the @@ -3887,7 +3878,7 @@ def generate_root_metadata(version, expiration_date, consistent_snapshot): # types, and that all dict keys are properly named. # Raise 'tuf.FormatError' if any of the arguments are improperly formatted. tuf.formats.METADATAVERSION_SCHEMA.check_match(version) - tuf.formats.TIME_SCHEMA.check_match(expiration_date) + tuf.formats.UNIX_TIMESTAMP_SCHEMA.check_match(expiration_date) tuf.formats.BOOLEAN_SCHEMA.check_match(consistent_snapshot) # The role and key dictionaries to be saved in the root metadata object. @@ -3980,8 +3971,8 @@ def generate_targets_metadata(targets_directory, target_files, version, trusted. expiration_date: - The expiration date, in UTC, of the metadata file. Conformant to - 'tuf.formats.TIME_SCHEMA'. + The expiration date of the metadata file. Conformant to + 'tuf.formats.UNIX_TIMESTAMP_SCHEMA'. delegations: The delegations made by the targets role to be generated. 'delegations' @@ -4011,7 +4002,7 @@ def generate_targets_metadata(targets_directory, target_files, version, tuf.formats.PATH_SCHEMA.check_match(targets_directory) tuf.formats.PATHS_SCHEMA.check_match(target_files) tuf.formats.METADATAVERSION_SCHEMA.check_match(version) - tuf.formats.TIME_SCHEMA.check_match(expiration_date) + tuf.formats.UNIX_TIMESTAMP_SCHEMA.check_match(expiration_date) tuf.formats.BOOLEAN_SCHEMA.check_match(write_consistent_targets) if delegations is not None: @@ -4090,8 +4081,8 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, trusted. expiration_date: - The expiration date, in UTC, of the metadata file. - Conformant to 'tuf.formats.TIME_SCHEMA'. + The expiration date of the metadata file. + Conformant to 'tuf.formats.UNIX_TIMESTAMP_SCHEMA'. root_filename: The filename of the top-level root role. The hash and file size of this @@ -4125,7 +4116,7 @@ def generate_snapshot_metadata(metadata_directory, version, expiration_date, # Raise 'tuf.FormatError' if the check fails. tuf.formats.PATH_SCHEMA.check_match(metadata_directory) tuf.formats.METADATAVERSION_SCHEMA.check_match(version) - tuf.formats.TIME_SCHEMA.check_match(expiration_date) + tuf.formats.UNIX_TIMESTAMP_SCHEMA.check_match(expiration_date) tuf.formats.PATH_SCHEMA.check_match(root_filename) tuf.formats.PATH_SCHEMA.check_match(targets_filename) tuf.formats.BOOLEAN_SCHEMA.check_match(consistent_snapshot) @@ -4212,8 +4203,8 @@ def generate_timestamp_metadata(snapshot_filename, version, trusted. expiration_date: - The expiration date, in UTC, of the metadata file, conformant to - 'tuf.formats.TIME_SCHEMA'. + The expiration date of the metadata file, conformant to + 'tuf.formats.UNIX_TIMESTAMP_SCHEMA'. compressions: Compression extensions (e.g., 'gz'). If 'snapshot.json' is also saved in @@ -4238,7 +4229,7 @@ def generate_timestamp_metadata(snapshot_filename, version, # Raise 'tuf.FormatError' if the check fails. tuf.formats.PATH_SCHEMA.check_match(snapshot_filename) tuf.formats.METADATAVERSION_SCHEMA.check_match(version) - tuf.formats.TIME_SCHEMA.check_match(expiration_date) + tuf.formats.UNIX_TIMESTAMP_SCHEMA.check_match(expiration_date) tuf.formats.COMPRESSIONS_SCHEMA.check_match(compressions) # Retrieve the fileinfo of the snapshot metadata file. From b6cd82d75cdafe33d062ea770a711dc028467ba9 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 12:53:46 -0400 Subject: [PATCH 04/11] Update the unit tests affected by the change to the 'expires' format. --- tests/unit/test_formats.py | 54 ++++++++++++++++-------------- tests/unit/test_keydb.py | 4 +-- tests/unit/test_repository_tool.py | 44 +++++++++++++----------- tests/unit/test_roledb.py | 2 +- tests/unit/test_updater.py | 2 +- 5 files changed, 58 insertions(+), 48 deletions(-) diff --git a/tests/unit/test_formats.py b/tests/unit/test_formats.py index 387c1a25..a20b57b1 100755 --- a/tests/unit/test_formats.py +++ b/tests/unit/test_formats.py @@ -18,6 +18,7 @@ """ import unittest +import datetime import tuf import tuf.formats @@ -39,7 +40,7 @@ def tearDown(self): def test_schemas(self): # Test conditions for valid schemas. valid_schemas = { - 'TIME_SCHEMA': (tuf.formats.TIME_SCHEMA, '2012-10-14 06:42:12 UTC'), + 'UNIX_TIMESTAMP_SCHEMA': (tuf.formats.UNIX_TIMESTAMP_SCHEMA, 499137720), 'HASH_SCHEMA': (tuf.formats.HASH_SCHEMA, 'A4582BCF323BCEF'), @@ -53,7 +54,7 @@ def test_schemas(self): 'KEYIDS_SCHEMA': (tuf.formats.KEYIDS_SCHEMA, ['123456789abcdef', '123456789abcdef']), - 'SIG_METHOD_SCHEMA': (tuf.formats.SIG_METHOD_SCHEMA, 'evp'), + 'SIG_METHOD_SCHEMA': (tuf.formats.SIG_METHOD_SCHEMA, 'ed25519'), 'RELPATH_SCHEMA': (tuf.formats.RELPATH_SCHEMA, 'metadata/root/'), @@ -184,7 +185,7 @@ def test_schemas(self): {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, @@ -195,7 +196,7 @@ def test_schemas(self): 'TARGETS_SCHEMA': (tuf.formats.TARGETS_SCHEMA, {'_type': 'Targets', 'version': 8, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'targets': {'metadata/targets.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}, @@ -209,7 +210,7 @@ def test_schemas(self): 'SNAPSHOT_SCHEMA': (tuf.formats.SNAPSHOT_SCHEMA, {'_type': 'Snapshot', 'version': 8, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'meta': {'metadata/snapshot.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}}), @@ -217,7 +218,7 @@ def test_schemas(self): 'TIMESTAMP_SCHEMA': (tuf.formats.TIMESTAMP_SCHEMA, {'_type': 'Timestamp', 'version': 8, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'meta': {'metadata/timestamp.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}}), @@ -239,7 +240,7 @@ def test_schemas(self): 'MIRRORLIST_SCHEMA': (tuf.formats.MIRRORLIST_SCHEMA, {'_type': 'Mirrors', 'version': 8, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'mirrors': [{'url_prefix': 'http://localhost:8001', 'metadata_path': 'metadata/', 'targets_path': 'targets/', @@ -289,7 +290,7 @@ def __init__(self, version, expires): def test_TimestampFile(self): # Test conditions for valid instances of 'tuf.formats.TimestampFile'. version = 8 - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 filedict = {'metadata/timestamp.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -323,7 +324,7 @@ def test_RootFile(self): # Test conditions for valid instances of 'tuf.formats.RootFile'. version = 8 consistent_snapshot = False - expires = '2018-10-16 06:42:12 UTC' + expires = 499137720 keydict = {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}} @@ -373,7 +374,7 @@ def test_RootFile(self): def test_SnapshotFile(self): # Test conditions for valid instances of 'tuf.formats.SnapshotFile'. version = 8 - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 filedict = {'metadata/snapshot.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -405,7 +406,7 @@ def test_SnapshotFile(self): def test_TargetsFile(self): # Test conditions for valid instances of 'tuf.formats.TargetsFile'. version = 8 - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 filedict = {'metadata/targets.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -444,27 +445,30 @@ def test_TargetsFile(self): - def test_format_time(self): + def test_unix_timestamp_to_datetime(self): # Test conditions for valid arguments. - TIME_SCHEMA = tuf.formats.TIME_SCHEMA - self.assertTrue(TIME_SCHEMA.matches(tuf.formats.format_time(499137720))) - self.assertEqual('1985-10-26 01:22:00 UTC', tuf.formats.format_time(499137720)) + UNIX_TIMESTAMP_SCHEMA = tuf.formats.UNIX_TIMESTAMP_SCHEMA + self.assertTrue(datetime.datetime, tuf.formats.unix_timestamp_to_datetime(499137720)) + datetime_object = datetime.datetime(1985, 10, 26, 01, 22) + self.assertEqual(datetime_object, tuf.formats.unix_timestamp_to_datetime(499137720)) # Test conditions for invalid arguments. - self.assertRaises(tuf.FormatError, tuf.formats.format_time, 'bad') - self.assertRaises(tuf.FormatError, tuf.formats.format_time, 1000000000000000000000) - self.assertRaises(tuf.FormatError, tuf.formats.format_time, ['5']) + self.assertRaises(tuf.FormatError, tuf.formats.unix_timestamp_to_datetime, 'bad') + self.assertRaises(tuf.FormatError, tuf.formats.unix_timestamp_to_datetime, 1000000000000000000000) + self.assertRaises(tuf.FormatError, tuf.formats.unix_timestamp_to_datetime, -1) + self.assertRaises(tuf.FormatError, tuf.formats.unix_timestamp_to_datetime, ['5']) - def test_parse_time(self): + def test_datetime_to_unix_timestamp(self): # Test conditions for valid arguments. - self.assertEqual(499137600, tuf.formats.parse_time('1985-10-26 01:20:00 UTC')) + datetime_object = datetime.datetime(2015, 10, 21, 19, 28) + self.assertEqual(1445455680, tuf.formats.datetime_to_unix_timestamp(datetime_object)) # Test conditions for invalid arguments. - self.assertRaises(tuf.FormatError, tuf.formats.parse_time, 'bad') - self.assertRaises(tuf.FormatError, tuf.formats.parse_time, 1000000000000000000000) - self.assertRaises(tuf.FormatError, tuf.formats.parse_time, ['1']) + self.assertRaises(tuf.FormatError, tuf.formats.datetime_to_unix_timestamp, 'bad') + self.assertRaises(tuf.FormatError, tuf.formats.datetime_to_unix_timestamp, 1000000000000000000000) + self.assertRaises(tuf.FormatError, tuf.formats.datetime_to_unix_timestamp, ['1']) @@ -498,7 +502,7 @@ def test_make_signable(self): root = {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, @@ -625,7 +629,7 @@ def test_check_signable_object_format(self): root = {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': '2012-10-16 06:42:12 UTC', + 'expires': 499137720, 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, diff --git a/tests/unit/test_keydb.py b/tests/unit/test_keydb.py index 7fd0d293..0bd27ce2 100755 --- a/tests/unit/test_keydb.py +++ b/tests/unit/test_keydb.py @@ -171,7 +171,7 @@ def test_create_keydb_from_root_metadata(self): 'Targets': {'keyids': [keyid2], 'threshold': 1}} version = 8 consistent_snapshot = False - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 root_metadata = tuf.formats.RootFile.make_metadata(version, expires, @@ -208,7 +208,7 @@ def test_create_keydb_from_root_metadata(self): rsakey3['keytype'] = 'bad_keytype' keydict[keyid3] = rsakey3 version = 8 - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 root_metadata = tuf.formats.RootFile.make_metadata(version, expires, diff --git a/tests/unit/test_repository_tool.py b/tests/unit/test_repository_tool.py index 62dc92f4..eba82a77 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/unit/test_repository_tool.py @@ -17,6 +17,7 @@ import os import time +import datetime import unittest import logging import tempfile @@ -225,7 +226,7 @@ def test_write_and_write_partial(self): # is made to a role. repo_tool.load_repository(repository_directory) - repository.timestamp.expiration = '2084-01-01 12:00:00' + repository.timestamp.expiration = datetime.datetime(2030, 01, 01, 12, 00) self.assertRaises(tuf.UnsignedMetadataError, repository.write) # Verify that a write_partial() is allowed. @@ -300,7 +301,7 @@ def __init__(self): self._rolename = 'metadata_role' # Expire in 86400 seconds (1 day). - expiration = tuf.formats.format_time(time.time() + 86400) + expiration = int(time.time() + 86400) roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, 'signatures': [], 'version': 0, @@ -354,31 +355,35 @@ def test_threshold(self): def test_expiration(self): # Test expiration getter. expiration = self.metadata.expiration - self.assertTrue(tuf.formats.TIME_SCHEMA.matches(expiration)) + self.assertTrue(isinstance(expiration, datetime.datetime)) # Test expiration setter. - self.metadata.expiration = '2088-01-01 12:00:00' + self.metadata.expiration = datetime.datetime(2030, 01, 01, 12, 00) expiration = self.metadata.expiration - self.assertTrue(tuf.formats.TIME_SCHEMA.matches(expiration)) + self.assertTrue(isinstance(expiration, datetime.datetime)) # Test improperly formatted datetime. try: self.metadata.expiration = '3' + except tuf.FormatError: pass + else: self.fail('Setter failed to detect improperly formatted datetime.') # Test invalid argument (i.e., expiration has already expired.) - expired_datetime = tuf.formats.format_time(time.time() - 1) + expired_datetime = tuf.formats.unix_timestamp_to_datetime(int(time.time() - 1)) try: self.metadata.expiration = expired_datetime - except tuf.FormatError: + + except tuf.Error: pass + else: - self.fail('Setter failted to detect an expired datetime.') + self.fail('Setter failed to detect an expired datetime.') @@ -1525,24 +1530,24 @@ def test_generate_root_metadata(self): tuf.roledb.create_roledb_from_root_metadata(root_signable['signed']) tuf.keydb.create_keydb_from_root_metadata(root_signable['signed']) - root_metadata = repo_tool.generate_root_metadata(1, '2088-01-01 12:00:00 UTC', + root_metadata = repo_tool.generate_root_metadata(1, 189345000, consistent_snapshot=False) self.assertTrue(tuf.formats.ROOT_SCHEMA.matches(root_metadata)) # Test improperly formatted arguments. self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, - '3', '2088-01-01 12:00:00 UTC', False) + '3', 189345000, False) self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, - 1, 3, False) + 1, '3', False) self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, - 1, '2088-01-01 12:00:00 UTC', 3) + 1, 189345000, 3) # Test for missing required roles and keys. tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() self.assertRaises(tuf.Error, repo_tool.generate_root_metadata, - 1, '2088-01-01 12:00:00 UTC', False) + 1, 189345000, False) @@ -1558,7 +1563,8 @@ def test_generate_targets_metadata(self): # Set valid generate_targets_metadata() arguments. version = 1 - expiration_date = '2088-01-01 12:00:00 UTC' + datetime_object = datetime.datetime(2030, 01, 01, 12, 00) + expiration_date = tuf.formats.datetime_to_unix_timestamp(datetime_object) target_files = ['file.txt'] delegations = {"keys": { @@ -1611,7 +1617,7 @@ def test_generate_targets_metadata(self): self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, targets_directory, target_files, '3', expiration_date) self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, - targets_directory, target_files, version, 3) + targets_directory, target_files, version, '3') # Improperly formatted 'delegations' and 'write_consistent_targets' self.assertRaises(tuf.FormatError, repo_tool.generate_targets_metadata, @@ -1643,7 +1649,7 @@ def test_generate_snapshot_metadata(self): targets_filename = os.path.join(metadata_directory, repo_tool.TARGETS_FILENAME) version = 1 - expiration_date = '2088-01-01 12:00:00 UTC' + expiration_date = 189345000 snapshot_metadata = \ repo_tool.generate_snapshot_metadata(metadata_directory, version, @@ -1661,7 +1667,7 @@ def test_generate_snapshot_metadata(self): metadata_directory, '3', expiration_date, root_filename, targets_filename, consistent_snapshot=False) self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, - metadata_directory, version, 3, + metadata_directory, version, '3', root_filename, targets_filename, consistent_snapshot=False) self.assertRaises(tuf.FormatError, repo_tool.generate_snapshot_metadata, metadata_directory, version, expiration_date, @@ -1689,7 +1695,7 @@ def test_generate_timestamp_metadata(self): # Set valid generate_timestamp_metadata() arguments. version = 1 - expiration_date = '2088-01-01 12:00:00 UTC' + expiration_date = 189345000 compressions = ['gz'] snapshot_metadata = \ @@ -1704,7 +1710,7 @@ def test_generate_timestamp_metadata(self): self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, snapshot_filename, '3', expiration_date, compressions) self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, - snapshot_filename, version, 3, compressions) + snapshot_filename, version, '3', compressions) self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, snapshot_filename, version, expiration_date, 3) self.assertRaises(tuf.FormatError, repo_tool.generate_timestamp_metadata, diff --git a/tests/unit/test_roledb.py b/tests/unit/test_roledb.py index 630ccaef..6df80df1 100755 --- a/tests/unit/test_roledb.py +++ b/tests/unit/test_roledb.py @@ -320,7 +320,7 @@ def test_create_roledb_from_root_metadata(self): 'targets': {'keyids': [keyid2], 'threshold': 1}} version = 8 consistent_snapshot = False - expires = '2012-10-16 06:42:12 UTC' + expires = 499137720 root_metadata = tuf.formats.RootFile.make_metadata(version, expires, diff --git a/tests/unit/test_updater.py b/tests/unit/test_updater.py index d8e1a5b4..2e362802 100755 --- a/tests/unit/test_updater.py +++ b/tests/unit/test_updater.py @@ -465,7 +465,7 @@ def test_2__ensure_not_expired(self): # 'tuf.ExpiredMetadataError' should be raised in this next test condition, # because the expiration_date has expired by 10 seconds. - expires = tuf.formats.format_time(time.time() - 10) + expires = int(time.time() - 10) self.repository_updater.metadata['current']['root']['expires'] = expires # Ensure the 'expires' value of the root file is valid by checking the From fa37cffd5408519571142c748d01e9c9c737ae65 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 12:54:27 -0400 Subject: [PATCH 05/11] Update the integration tests affected by the change to the 'expires' format. --- tests/integration/test_indefinite_freeze_attack.py | 13 ++++++------- tests/integration/test_replay_attack.py | 5 +++-- 2 files changed, 9 insertions(+), 9 deletions(-) diff --git a/tests/integration/test_indefinite_freeze_attack.py b/tests/integration/test_indefinite_freeze_attack.py index a5fa933f..87de4c2b 100755 --- a/tests/integration/test_indefinite_freeze_attack.py +++ b/tests/integration/test_indefinite_freeze_attack.py @@ -177,8 +177,7 @@ def test_without_tuf(self): 'timestamp.json') timestamp_metadata = tuf.util.load_json_file(timestamp_path) - timestamp_metadata['signed']['expires'] = \ - tuf.formats.format_time(time.time() - 10) + timestamp_metadata['signed']['expires'] = int(time.time() - 10) tuf.formats.check_signable_object_format(timestamp_metadata) @@ -226,9 +225,8 @@ def test_with_tuf(self): repository.timestamp.load_signing_key(timestamp_private) # expire in 1 second. - utc_timestamp = tuf.formats.format_time(time.time() + 1) - repository.timestamp.expiration = \ - utc_timestamp[0:utc_timestamp.rfind(' UTC')] + datetime_object = tuf.formats.unix_timestamp_to_datetime(int(time.time() + 1)) + repository.timestamp.expiration = datetime_object repository.write() # Move the staged metadata to the "live" metadata. @@ -237,8 +235,9 @@ def test_with_tuf(self): os.path.join(self.repository_directory, 'metadata')) # Verify that the TUF client detects outdated metadata and refuses to - # continue the update process. - time.sleep(1) + # continue the update process. Sleep for at least 2 seconds to ensure + # 'repository.timestamp.expiration' is reached. + time.sleep(2) self.assertRaises(tuf.ExpiredMetadataError, self.repository_updater.refresh) diff --git a/tests/integration/test_replay_attack.py b/tests/integration/test_replay_attack.py index 1f0064e4..79aa60cb 100755 --- a/tests/integration/test_replay_attack.py +++ b/tests/integration/test_replay_attack.py @@ -39,6 +39,7 @@ import tempfile import random import time +import datetime import shutil import json import subprocess @@ -209,7 +210,7 @@ def test_without_tuf(self): # Set an arbitrary expiration so that the repository tool generates a new # version. - repository.timestamp.expiration = '2088-01-01 12:12:00' + repository.timestamp.expiration = datetime.datetime(2030, 01, 01, 12, 12) repository.write() # Move the staged metadata to the "live" metadata. @@ -280,7 +281,7 @@ def test_with_tuf(self): # Set an arbitrary expiration so that the repository tool generates a new # version. - repository.timestamp.expiration = '2088-01-01 12:12:00' + repository.timestamp.expiration = datetime.datetime(2030, 01, 01, 12, 12) repository.write() # Move the staged metadata to the "live" metadata. From f497f19998654c2e60b83bfeef0f33cefbe5739e Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 13:00:34 -0400 Subject: [PATCH 06/11] Update README.md Update the expiration object expected by Targets().expiration --- tuf/README.md | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/tuf/README.md b/tuf/README.md index 52f9214c..404438e5 100644 --- a/tuf/README.md +++ b/tuf/README.md @@ -170,6 +170,7 @@ Not enough signatures for 'path/to/repository/metadata.staged/targets.json' ```python # Continuing from the previous section . . . +import datetime # Generate keys for the remaining top-level roles. The root keys have been set above. # The password argument may be omitted if a password prompt is needed. @@ -200,7 +201,7 @@ repository.timestamp.load_signing_key(private_timestamp_key) # Optionally set the expiration date of the timestamp role. By default, roles are set to expire # as follows: root(1 year), targets(3 months), snapshot(1 week), timestamp(1 day). -repository.timestamp.expiration = "2014-10-28 12:08:00" +repository.timestamp.expiration = datetime.datetime(2014, 10, 28, 12, 08) # Metadata files may also be compressed. Only "gz" is currently supported. repository.targets.compressions = ["gz"] From 0f86447eac867b22a64ae08d124218612c0662db Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 13:23:41 -0400 Subject: [PATCH 07/11] Update tuf-spec.txt Update time format. --- docs/tuf-spec.txt | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/docs/tuf-spec.txt b/docs/tuf-spec.txt index e54069a4..e2652002 100644 --- a/docs/tuf-spec.txt +++ b/docs/tuf-spec.txt @@ -476,7 +476,8 @@ The KEYID of a key is the hexdigest of the SHA-256 hash of the canonical JSON form of the key, where the "private" object key is excluded. - All times are given as strings of the format "YYYY-MM-DD HH:MM:SS UTC". + All times are in Unix / POSIX format, an integer representing the number + of seconds since the epoch (January 1, 1970.) 4.3. File formats: root.json From 62f5b0689b7d70da619c565b58bfc5aaccbc5690 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Tue, 15 Apr 2014 13:25:33 -0400 Subject: [PATCH 08/11] Update UNIX_TIMESTAMP_SCHEMA comment. --- tuf/formats.py | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/tuf/formats.py b/tuf/formats.py index 052f2a1f..4c224178 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -75,7 +75,10 @@ # additional keys which are not defined. Thus, any additions to them will be # easily backwards compatible with clients that are already deployed. -# A date in 'YYYY-MM-DD HH:MM:SS UTC' format. +# A Unix/POSIX time format. An integer representing the number of seconds +# since the epoch (January 1, 1970.) Metadata uses this format for the +# 'expires' field. Set 'hi' to the upper timestamp limit (year 2038), the max +# value of an int. UNIX_TIMESTAMP_SCHEMA = SCHEMA.Integer(lo=0, hi=2147483647) # A hexadecimal value in '23432df87ab..' format. From 919fb0ff8f7f21dacb3d677e7c6afabd11a1f1b4 Mon Sep 17 00:00:00 2001 From: Vladimir Diaz Date: Thu, 17 Apr 2014 12:27:28 -0400 Subject: [PATCH 09/11] Log warning if top-level metadata expires soon. --- tuf/__init__.py | 7 +---- tuf/client/updater.py | 9 ++---- tuf/formats.py | 9 ++---- tuf/log.py | 13 ++------ tuf/repository_tool.py | 68 +++++++++++++++++++++++++++++++++++++----- 5 files changed, 69 insertions(+), 37 deletions(-) diff --git a/tuf/__init__.py b/tuf/__init__.py index c0ac4c45..eefde2d8 100755 --- a/tuf/__init__.py +++ b/tuf/__init__.py @@ -135,12 +135,7 @@ class ForbiddenTargetError(RepositoryError): class ExpiredMetadataError(Error): """Indicate that a TUF Metadata file has expired.""" - - def __init__(self, expiration_string): - self.expiration_string = expiration_string - - def __str__(self): - return 'Metadata expired on '+str(expiration_string)+'.' + pass diff --git a/tuf/client/updater.py b/tuf/client/updater.py index 69ef3fc3..eadab736 100755 --- a/tuf/client/updater.py +++ b/tuf/client/updater.py @@ -1764,17 +1764,14 @@ def _ensure_not_expired(self, metadata_role): current_time = int(time.time()) # Generate a user-friendly error message if 'expires' is less than the - # current time (i.e., a local time). In contrast, the logger message - # contains the 'expires' unix timestamp. + # current time (i.e., a local time.) datetime_object = tuf.formats.unix_timestamp_to_datetime(expires) local_time = datetime_object.ctime() if expires < current_time: - message = 'Metadata '+repr(rolepath)+' expired on '+repr(expires)+'.' + message = 'Metadata '+repr(rolepath)+' expired on '+repr(local_time)+'.' logger.error(message) - # Expired metadata message generated by - # tuf.ExpiredMetadataError (defined in __init__.py) - raise tuf.ExpiredMetadataError(local_time) + raise tuf.ExpiredMetadataError(message) diff --git a/tuf/formats.py b/tuf/formats.py index 4c224178..411efed9 100755 --- a/tuf/formats.py +++ b/tuf/formats.py @@ -7,7 +7,7 @@ Vladimir Diaz - Refactored April 30, 2012. -Vlad + Refactored April 30, 2012. -vladimir.v.diaz See LICENSE for licensing information. @@ -365,11 +365,6 @@ keys = KEYDICT_SCHEMA, roles = ROLELIST_SCHEMA) -# The number of seconds before metadata expires. The minimum is 86400 seconds -# (= 1 day). This schema is used for the initial expiration date. Repository -# maintainers may later modify this value. -EXPIRATION_SCHEMA = SCHEMA.Integer(lo=86400) - # Supported compression extension (e.g., 'gz'). COMPRESSION_SCHEMA = SCHEMA.OneOf([SCHEMA.String(''), SCHEMA.String('gz')]) @@ -384,7 +379,7 @@ signing_keyids = SCHEMA.Optional(KEYIDS_SCHEMA), threshold = THRESHOLD_SCHEMA, version = SCHEMA.Optional(METADATAVERSION_SCHEMA), - expires = SCHEMA.Optional(SCHEMA.OneOf([EXPIRATION_SCHEMA, UNIX_TIMESTAMP_SCHEMA])), + expires = SCHEMA.Optional(UNIX_TIMESTAMP_SCHEMA), signatures = SCHEMA.Optional(SIGNATURES_SCHEMA), compressions = SCHEMA.Optional(COMPRESSIONS_SCHEMA), paths = SCHEMA.Optional(RELPATHS_SCHEMA), diff --git a/tuf/log.py b/tuf/log.py index fed3a0e8..d99a5c41 100755 --- a/tuf/log.py +++ b/tuf/log.py @@ -74,19 +74,10 @@ # Set the format for logging messages. # Example format for '_FORMAT_STRING': -# [2013-08-13 15:21:18,068 UTC] [tuf] [INFO][_update_metadata:851@updater.py] -_FORMAT_STRING = '[%(asctime)s UTC] [%(name)s] [%(levelname)s] '+\ +# [2013-08-13 15:21:18,068 localtime] [tuf] [INFO][_update_metadata:851@updater.py] +_FORMAT_STRING = '[%(asctime)s localtime] [%(name)s] [%(levelname)s] '+\ '[%(funcName)s:%(lineno)s@%(filename)s]\n%(message)s\n' -# Ask all Formatter instances to talk GMT. Set the 'converter' attribute of -# 'logging.Formatter' so that all formatters use Greenwich Mean Time. -# http://docs.python.org/2/library/logging.html#logging.Formatter.formatTime -# The 2nd paragraph in the link above contains the relevant information. -# GMT = UTC (Coordinated Universal Time). TUF metadata stores timestamps in UTC. -# We previously displayed the local time but this lead to confusion when -# visually comparing logger events and metadata information. Unix time stamps -# are fine but they may be less human-readable than UTC. -logging.Formatter.converter = time.gmtime formatter = logging.Formatter(_FORMAT_STRING) # Set the handlers for the logger. The console handler is unset by default. A diff --git a/tuf/repository_tool.py b/tuf/repository_tool.py index dca32b6e..fdcd56a1 100755 --- a/tuf/repository_tool.py +++ b/tuf/repository_tool.py @@ -109,6 +109,13 @@ # Initial 'timestamp.json' expiration time of 1 day. TIMESTAMP_EXPIRATION = 86400 +# Log warning when metadata expires in n days, or less. +# root = 1 month, snapshot = 1 day, targets = 10 days, timestamp = 1 day. +ROOT_EXPIRES_WARN_SECONDS = 2630000 +SNAPSHOT_EXPIRES_WARN_SECONDS = 86400 +TARGETS_EXPIRES_WARN_SECONDS = 864000 +TIMESTAMP_EXPIRES_WARN_SECONDS = 86400 + class Repository(object): """ @@ -1133,15 +1140,18 @@ def expiration(self, datetime_object): raise tuf.FormatError(message) # Ensure the expiration has not already passed. - current_datetime = tuf.formats.unix_timestamp_to_datetime(int(time.time())) - if datetime_object < current_datetime: - message = 'The expiration date must occur after the current date.' + datetime_object_unix_timestamp = \ + tuf.formats.datetime_to_unix_timestamp(datetime_object) + + current_unix_timestamp = int(time.time()) + if datetime_object_unix_timestamp < current_unix_timestamp: + message = repr(self.rolename) + ' has already expired.' raise tuf.Error(message) - # Update the role's 'expires' entry in 'tuf.roledb.py'. + # Update the role's 'expires' entry in 'tuf.roledb.py'. roleinfo = tuf.roledb.get_roleinfo(self.rolename) unix_timestamp = tuf.formats.datetime_to_unix_timestamp(datetime_object) - roleinfo['expires'] = unix_timestamp + roleinfo['expires'] = unix_timestamp tuf.roledb.update_roleinfo(self.rolename, roleinfo) @@ -1498,7 +1508,7 @@ def __init__(self, targets_directory, rolename='targets', roleinfo=None): # By default, Targets objects are set to expire 3 months from the current # time. May be later modified. - expiration = int(time.time()+TARGETS_EXPIRATION) + expiration = int(time.time() + TARGETS_EXPIRATION) # If 'roleinfo' is not provided, set an initial default. if roleinfo is None: @@ -2041,6 +2051,7 @@ def delegate(self, rolename, public_keys, list_of_targets, # Create a new Targets object for the 'rolename' delegation. An initial # expiration is set (3 months from the current time). expiration = int(time.time()+TARGETS_EXPIRATION) + roleinfo = {'name': full_rolename, 'keyids': keyids, 'signing_keyids': [], 'threshold': threshold, 'version': 0, 'compressions': [''], 'expires': expiration, 'signatures': [], 'partial_loaded': False, @@ -2361,6 +2372,9 @@ def _generate_and_write_metadata(rolename, metadata_filename, write_partial, if rolename == 'root': metadata = generate_root_metadata(roleinfo['version'], roleinfo['expires'], consistent_snapshot) + + _log_warning_if_expires_soon(ROOT_FILENAME, roleinfo['expires'], + ROOT_EXPIRES_WARN_SECONDS) # Check for the Targets role, including delegated roles. elif rolename.startswith('targets'): @@ -2370,6 +2384,9 @@ def _generate_and_write_metadata(rolename, metadata_filename, write_partial, roleinfo['expires'], roleinfo['delegations'], consistent_snapshot) + if rolename == 'targets': + _log_warning_if_expires_soon(TARGETS_FILENAME, roleinfo['expires'], + TARGETS_EXPIRES_WARN_SECONDS) elif rolename == 'snapshot': root_filename = filenames['root'] @@ -2378,7 +2395,10 @@ def _generate_and_write_metadata(rolename, metadata_filename, write_partial, roleinfo['version'], roleinfo['expires'], root_filename, targets_filename, - consistent_snapshot ) + consistent_snapshot) + + _log_warning_if_expires_soon(SNAPSHOT_FILENAME, roleinfo['expires'], + SNAPSHOT_EXPIRES_WARN_SECONDS) elif rolename == 'timestamp': snapshot_filename = filenames['snapshot'] @@ -2386,6 +2406,9 @@ def _generate_and_write_metadata(rolename, metadata_filename, write_partial, roleinfo['version'], roleinfo['expires'], snapshot_compressions) + + _log_warning_if_expires_soon(TIMESTAMP_FILENAME, roleinfo['expires'], + TIMESTAMP_EXPIRES_WARN_SECONDS) signable = sign_metadata(metadata, roleinfo['signing_keyids'], metadata_filename) @@ -3024,6 +3047,7 @@ def load_repository(repository_directory): if metadata_name.endswith(METADATA_EXTENSION): extension_length = len(METADATA_EXTENSION) metadata_name = metadata_name[:-extension_length] + else: continue @@ -3148,6 +3172,9 @@ def _load_top_level_metadata(repository, top_level_filenames): if _metadata_is_partially_loaded('root', signable, roleinfo): roleinfo['partial_loaded'] = True + _log_warning_if_expires_soon(ROOT_FILENAME, roleinfo['expires'], + ROOT_EXPIRES_WARN_SECONDS) + tuf.roledb.update_roleinfo('root', roleinfo) # Ensure the 'consistent_snapshot' field is extracted. @@ -3175,6 +3202,9 @@ def _load_top_level_metadata(repository, top_level_filenames): if _metadata_is_partially_loaded('timestamp', signable, roleinfo): roleinfo['partial_loaded'] = True + _log_warning_if_expires_soon(TIMESTAMP_FILENAME, roleinfo['expires'], + TIMESTAMP_EXPIRES_WARN_SECONDS) + tuf.roledb.update_roleinfo('timestamp', roleinfo) else: @@ -3205,6 +3235,9 @@ def _load_top_level_metadata(repository, top_level_filenames): if _metadata_is_partially_loaded('snapshot', signable, roleinfo): roleinfo['partial_loaded'] = True + _log_warning_if_expires_soon(SNAPSHOT_FILENAME, roleinfo['expires'], + SNAPSHOT_EXPIRES_WARN_SECONDS) + tuf.roledb.update_roleinfo('snapshot', roleinfo) else: @@ -3237,6 +3270,9 @@ def _load_top_level_metadata(repository, top_level_filenames): if _metadata_is_partially_loaded('targets', signable, roleinfo): roleinfo['partial_loaded'] = True + + _log_warning_if_expires_soon(TARGETS_FILENAME, roleinfo['expires'], + TARGETS_EXPIRES_WARN_SECONDS) tuf.roledb.update_roleinfo('targets', roleinfo) @@ -3273,6 +3309,24 @@ def _load_top_level_metadata(repository, top_level_filenames): +def _log_warning_if_expires_soon(rolename, expires_unix_timestamp, + seconds_remaining_to_warn): + + datetime_object = \ + tuf.formats.unix_timestamp_to_datetime(expires_unix_timestamp) + seconds_until_expires = expires_unix_timestamp - int(time.time()) + + if seconds_until_expires <= seconds_remaining_to_warn: + days_until_expires = seconds_until_expires / 86400 + + message = repr(rolename) + ' expires ' + datetime_object.ctime() + \ + '.\n' + repr(days_until_expires) + ' day(s) until it expires.' + + logger.warn(message) + + + + def generate_and_write_rsa_keypair(filepath, bits=DEFAULT_RSA_KEY_BITS, password=None): From 5f94d5be0d9c2140e440a7076c6ccfa6263864dd Mon Sep 17 00:00:00 2001 From: vladdd Date: Sat, 19 Apr 2014 14:27:53 -0400 Subject: [PATCH 10/11] Support ISO 8601, vendor iso8601, clean codebase. --- docs/tuf-spec.txt | 6 +- .../client}/example_client.py | 26 +- examples/client/metadata/current/root.json | Bin 0 -> 1831 bytes .../client/metadata/current/snapshot.json | Bin 0 -> 1007 bytes examples/client/metadata/current/targets.json | Bin 0 -> 1184 bytes .../metadata/current/targets/project.json | Bin 0 -> 604 bytes .../client/metadata/current/timestamp.json | Bin 0 -> 544 bytes examples/client/metadata/previous/root.json | Bin 0 -> 1831 bytes .../client/metadata/previous/timestamp.json | Bin 0 -> 544 bytes examples/client/targets/file1.txt | 1 + examples/client/targets/file2.txt | 1 + examples/client/targets/project/file3.txt | 1 + examples/generate.py | 131 ++ examples/keystore/project_key | 1 + examples/keystore/project_key.pub | 1 + examples/keystore/root_key | 1 + examples/keystore/root_key.pub | 1 + examples/keystore/snapshot_key | 1 + examples/keystore/snapshot_key.pub | 1 + examples/keystore/targets_key | 1 + examples/keystore/targets_key.pub | 1 + examples/keystore/timestamp_key | 1 + examples/keystore/timestamp_key.pub | 1 + examples/repository/metadata.staged/root.json | Bin 0 -> 1831 bytes .../repository/metadata.staged/snapshot.json | Bin 0 -> 1007 bytes .../repository/metadata.staged/targets.json | Bin 0 -> 1184 bytes .../metadata.staged/targets.json.gz | Bin 0 -> 599 bytes .../metadata.staged/targets/project.json | Bin 0 -> 604 bytes .../repository/metadata.staged/timestamp.json | Bin 0 -> 544 bytes examples/repository/metadata/root.json | Bin 0 -> 1831 bytes examples/repository/metadata/snapshot.json | Bin 0 -> 1007 bytes examples/repository/metadata/targets.json | Bin 0 -> 1184 bytes examples/repository/metadata/targets.json.gz | Bin 0 -> 599 bytes .../repository/metadata/targets/project.json | Bin 0 -> 604 bytes examples/repository/metadata/timestamp.json | Bin 0 -> 544 bytes examples/repository/targets/file1.txt | 1 + examples/repository/targets/file2.txt | 1 + examples/repository/targets/project/file3.txt | 1 + setup.py | 2 +- {tuf/pushtools => tests}/__init__.py | 0 tests/{unit => }/aggregate_tests.py | 0 tests/integration/test_delegations.py | 541 ------ .../client/metadata/current/root.json | Bin 3766 -> 3778 bytes .../client/metadata/current/snapshot.json | Bin 1381 -> 1393 bytes .../client/metadata/current/targets.json | Bin 1939 -> 1951 bytes .../client/metadata/current/targets.json.gz | Bin 1205 -> 1211 bytes .../metadata/current/targets/role1.json | Bin 971 -> 983 bytes .../client/metadata/current/timestamp.json | Bin 919 -> 931 bytes .../client/metadata/previous/root.json | Bin 3766 -> 3778 bytes .../client/metadata/previous/snapshot.json | Bin 1381 -> 1393 bytes .../client/metadata/previous/targets.json | Bin 1939 -> 1951 bytes .../client/metadata/previous/targets.json.gz | Bin 1205 -> 1211 bytes .../metadata/previous/targets/role1.json | Bin 971 -> 983 bytes .../client/metadata/previous/timestamp.json | Bin 919 -> 931 bytes tests/repository_data/keystore/delegation_key | 52 +- .../keystore/delegation_key.pub | 14 +- tests/repository_data/keystore/root_key | 52 +- tests/repository_data/keystore/root_key.pub | 14 +- tests/repository_data/keystore/snapshot_key | 52 +- .../repository_data/keystore/snapshot_key.pub | 14 +- tests/repository_data/keystore/targets_key | 52 +- .../repository_data/keystore/targets_key.pub | 14 +- tests/repository_data/keystore/timestamp_key | 52 +- .../keystore/timestamp_key.pub | 14 +- .../repository/metadata.staged/root.json | Bin 3766 -> 3778 bytes .../repository/metadata.staged/snapshot.json | Bin 1381 -> 1393 bytes .../repository/metadata.staged/targets.json | Bin 1939 -> 1951 bytes .../metadata.staged/targets.json.gz | Bin 1205 -> 1211 bytes .../metadata.staged/targets/role1.json | Bin 971 -> 983 bytes .../repository/metadata.staged/timestamp.json | Bin 919 -> 931 bytes .../repository/metadata/root.json | Bin 3766 -> 3778 bytes .../repository/metadata/snapshot.json | Bin 1381 -> 1393 bytes .../repository/metadata/targets.json | Bin 1939 -> 1951 bytes .../repository/metadata/targets.json.gz | Bin 1205 -> 1211 bytes .../repository/metadata/targets/role1.json | Bin 971 -> 983 bytes .../repository/metadata/timestamp.json | Bin 919 -> 931 bytes tests/{integration => }/simple_server.py | 0 .../slow_retrieval_server.py | 2 +- .../test_arbitrary_package_attack.py | 5 +- tests/{unit => }/test_download.py | 12 +- tests/{unit => }/test_ed25519_keys.py | 0 .../test_endless_data_attack.py | 5 +- .../test_extraneous_dependencies_attack.py | 5 +- tests/{unit => }/test_formats.py | 28 +- tests/{unit => }/test_hash.py | 0 .../test_indefinite_freeze_attack.py | 10 +- tests/{unit => }/test_keydb.py | 4 +- tests/{unit => }/test_keys.py | 0 tests/{unit => }/test_mirrors.py | 5 +- .../test_mix_and_match_attack.py | 5 +- tests/{unit => }/test_pycrypto_keys.py | 0 tests/{integration => }/test_replay_attack.py | 5 +- tests/{unit => }/test_repository_tool.py | 87 +- tests/{unit => }/test_roledb.py | 2 +- tests/{unit => }/test_schema.py | 0 tests/{unit => }/test_sig.py | 0 .../test_slow_retrieval_attack.py | 5 +- tests/{unit => }/test_updater.py | 8 +- tests/{unit => }/test_util.py | 4 +- tests/unit/deprecated/test_keystore.py | 355 ---- tests/unit/deprecated/test_push.py | 166 -- tests/unit/deprecated/test_pushtoolslib.py | 173 -- tests/unit/deprecated/test_quickstart.py | 195 -- tests/unit/deprecated/test_signercli.py | 1573 ----------------- tests/unit/deprecated/test_signerlib.py | 986 ----------- tests/unit/deprecated/test_util_test_tools.py | 171 -- tests/unit/simple_server.py | 48 - tests/unit/statement_coverage.py | 86 - {tuf/tests => tests}/unittest_toolbox.py | 4 +- tuf/_vendor/__init__.py | 0 tuf/_vendor/iso8601/LICENSE | 20 + tuf/_vendor/iso8601/README.rst | 170 ++ tuf/_vendor/iso8601/__init__.py | 1 + tuf/_vendor/iso8601/iso8601.py | 202 +++ tuf/_vendor/iso8601/test_iso8601.py | 97 + tuf/build_updater.py | 177 -- tuf/client/updater.py | 19 +- tuf/evp.py | 425 ----- .../client/metadata/current/release.txt | 28 - .../client/metadata/current/root.txt | 83 - .../client/metadata/current/targets.txt | 28 - .../client/metadata/current/timestamp.txt | 22 - .../client/metadata/previous/release.txt | 28 - .../client/metadata/previous/root.txt | 83 - .../client/metadata/previous/targets.txt | 28 - .../client/metadata/previous/timestamp.txt | 22 - ...15b0b18ab9203c639a220f3db7aff78d784ab2.key | 1 - ...7f8f648e2c58093cd47986531e80747bd2c559.key | 1 - ...9ee3a647b6978ca156f93b7eba730038e5327b.key | 1 - ...d8f634a194134a117c5980d3ff22bc54a25de4.key | 1 - ...b17c069702b637d59f2e9c4f88894d2cb5cabf.key | 1 - .../repository_basic/repository/config.cfg | 23 - .../repository/metadata/release.txt | 28 - .../repository/metadata/root.txt | 83 - .../repository/metadata/targets.txt | 28 - .../repository/metadata/timestamp.txt | 22 - .../repository/targets/LICENSE.txt | 66 - .../repository/targets/helloworld.py | 1 - .../client/metadata/current/release.txt | 28 - .../client/metadata/current/root.txt | 70 - .../client/metadata/current/targets.txt | 22 - .../client/metadata/current/timestamp.txt | 22 - .../client/metadata/previous/release.txt | 28 - .../client/metadata/previous/root.txt | 70 - .../client/metadata/previous/targets.txt | 22 - .../client/metadata/previous/timestamp.txt | 22 - ...264fb4be4aa88f9668ac6fb86bfc1f891458d8.key | 1 - ...69ae3edeabbf781b7f6a2e56a71cc1955e202f.key | 1 - ...3a106787ca75d026ec60db5c2c0af3b9b60085.key | 1 - ...f7d03cc8b42a8b464355141d06a61c13849d92.key | 1 - ...4234a90d84332b295d31bea42cf482fec0c885.key | 1 - ...62ce68e360e9ac5f50abefacb749e20dad23d8.key | 1 - .../repository/config.cfg | 23 - .../repository/metadata/release.txt | 40 - .../repository/metadata/root.txt | 70 - .../repository/metadata/targets.txt | 44 - .../repository/metadata/targets/role1.txt | 44 - .../metadata/targets/role1/role2.txt | 22 - .../repository/metadata/timestamp.txt | 22 - .../repository/targets/LICENSE.txt | 66 - .../targets/role1/delegated_target1.txt | 1 - .../targets/role2/delegated_target2.txt | 1 - tuf/formats.py | 18 +- tuf/log.py | 11 +- tuf/pushtools/push.cfg.sample | 10 - tuf/pushtools/push.py | 187 -- tuf/pushtools/pushtoolslib.py | 154 -- tuf/pushtools/receivetools/receive.cfg.sample | 6 - tuf/pushtools/receivetools/receive.py | 861 --------- tuf/pushtools/transfer/__init__.py | 0 tuf/pushtools/transfer/scp.py | 172 -- tuf/repo/__init__.py | 0 tuf/repo/keystore.py | 696 -------- tuf/repo/quickstart.py | 478 ----- tuf/repo/signercli.py | 1566 ---------------- tuf/repo/signerlib.py | 1529 ---------------- tuf/repository_tool.py | 86 +- tuf/sig.py | 2 + tuf/tests/__init__.py | 4 - tuf/tests/repository_setup.py | 382 ---- tuf/tests/util_test_tools.py | 681 ------- tuf/time_ed25519.py | 38 - 182 files changed, 1006 insertions(+), 13188 deletions(-) rename {tuf/examples => examples/client}/example_client.py (85%) create mode 100644 examples/client/metadata/current/root.json create mode 100644 examples/client/metadata/current/snapshot.json create mode 100644 examples/client/metadata/current/targets.json create mode 100644 examples/client/metadata/current/targets/project.json create mode 100644 examples/client/metadata/current/timestamp.json create mode 100644 examples/client/metadata/previous/root.json create mode 100644 examples/client/metadata/previous/timestamp.json create mode 100644 examples/client/targets/file1.txt create mode 100644 examples/client/targets/file2.txt create mode 100644 examples/client/targets/project/file3.txt create mode 100755 examples/generate.py create mode 100644 examples/keystore/project_key create mode 100644 examples/keystore/project_key.pub create mode 100644 examples/keystore/root_key create mode 100644 examples/keystore/root_key.pub create mode 100644 examples/keystore/snapshot_key create mode 100644 examples/keystore/snapshot_key.pub create mode 100644 examples/keystore/targets_key create mode 100644 examples/keystore/targets_key.pub create mode 100644 examples/keystore/timestamp_key create mode 100644 examples/keystore/timestamp_key.pub create mode 100644 examples/repository/metadata.staged/root.json create mode 100644 examples/repository/metadata.staged/snapshot.json create mode 100644 examples/repository/metadata.staged/targets.json create mode 100644 examples/repository/metadata.staged/targets.json.gz create mode 100644 examples/repository/metadata.staged/targets/project.json create mode 100644 examples/repository/metadata.staged/timestamp.json create mode 100644 examples/repository/metadata/root.json create mode 100644 examples/repository/metadata/snapshot.json create mode 100644 examples/repository/metadata/targets.json create mode 100644 examples/repository/metadata/targets.json.gz create mode 100644 examples/repository/metadata/targets/project.json create mode 100644 examples/repository/metadata/timestamp.json create mode 100644 examples/repository/targets/file1.txt create mode 100644 examples/repository/targets/file2.txt create mode 100644 examples/repository/targets/project/file3.txt rename {tuf/pushtools => tests}/__init__.py (100%) rename tests/{unit => }/aggregate_tests.py (100%) delete mode 100755 tests/integration/test_delegations.py rename tests/{integration => }/simple_server.py (100%) rename tests/{integration => }/slow_retrieval_server.py (99%) rename tests/{integration => }/test_arbitrary_package_attack.py (98%) rename tests/{unit => }/test_download.py (95%) rename tests/{unit => }/test_ed25519_keys.py (100%) rename tests/{integration => }/test_endless_data_attack.py (98%) rename tests/{integration => }/test_extraneous_dependencies_attack.py (98%) rename tests/{unit => }/test_formats.py (97%) rename tests/{unit => }/test_hash.py (100%) rename tests/{integration => }/test_indefinite_freeze_attack.py (97%) rename tests/{unit => }/test_keydb.py (99%) rename tests/{unit => }/test_keys.py (100%) rename tests/{unit => }/test_mirrors.py (97%) rename tests/{integration => }/test_mix_and_match_attack.py (98%) rename tests/{unit => }/test_pycrypto_keys.py (100%) rename tests/{integration => }/test_replay_attack.py (98%) rename tests/{unit => }/test_repository_tool.py (96%) rename tests/{unit => }/test_roledb.py (99%) rename tests/{unit => }/test_schema.py (100%) rename tests/{unit => }/test_sig.py (100%) rename tests/{integration => }/test_slow_retrieval_attack.py (98%) rename tests/{unit => }/test_updater.py (99%) rename tests/{unit => }/test_util.py (99%) delete mode 100755 tests/unit/deprecated/test_keystore.py delete mode 100755 tests/unit/deprecated/test_push.py delete mode 100755 tests/unit/deprecated/test_pushtoolslib.py delete mode 100755 tests/unit/deprecated/test_quickstart.py delete mode 100755 tests/unit/deprecated/test_signercli.py delete mode 100755 tests/unit/deprecated/test_signerlib.py delete mode 100755 tests/unit/deprecated/test_util_test_tools.py delete mode 100755 tests/unit/simple_server.py delete mode 100755 tests/unit/statement_coverage.py rename {tuf/tests => tests}/unittest_toolbox.py (99%) mode change 100644 => 100755 mode change 100644 => 100755 tuf/_vendor/__init__.py create mode 100644 tuf/_vendor/iso8601/LICENSE create mode 100644 tuf/_vendor/iso8601/README.rst create mode 100644 tuf/_vendor/iso8601/__init__.py create mode 100644 tuf/_vendor/iso8601/iso8601.py create mode 100644 tuf/_vendor/iso8601/test_iso8601.py delete mode 100755 tuf/build_updater.py delete mode 100755 tuf/evp.py delete mode 100644 tuf/examples/repository_basic/client/metadata/current/release.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/current/root.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/current/targets.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/current/timestamp.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/previous/release.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/previous/root.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/previous/targets.txt delete mode 100644 tuf/examples/repository_basic/client/metadata/previous/timestamp.txt delete mode 100644 tuf/examples/repository_basic/keystore/4ac0adf8fac99d3603b02ee88715b0b18ab9203c639a220f3db7aff78d784ab2.key delete mode 100644 tuf/examples/repository_basic/keystore/7e6d7b7d2d60f175d4e48b11347f8f648e2c58093cd47986531e80747bd2c559.key delete mode 100644 tuf/examples/repository_basic/keystore/86e0a0c096d2244436911f15d89ee3a647b6978ca156f93b7eba730038e5327b.key delete mode 100644 tuf/examples/repository_basic/keystore/9411398cb36dbce77727208a3ed8f634a194134a117c5980d3ff22bc54a25de4.key delete mode 100644 tuf/examples/repository_basic/keystore/c57c4439c8cd88e0571d651696b17c069702b637d59f2e9c4f88894d2cb5cabf.key delete mode 100644 tuf/examples/repository_basic/repository/config.cfg delete mode 100644 tuf/examples/repository_basic/repository/metadata/release.txt delete mode 100644 tuf/examples/repository_basic/repository/metadata/root.txt delete mode 100644 tuf/examples/repository_basic/repository/metadata/targets.txt delete mode 100644 tuf/examples/repository_basic/repository/metadata/timestamp.txt delete mode 100644 tuf/examples/repository_basic/repository/targets/LICENSE.txt delete mode 100755 tuf/examples/repository_basic/repository/targets/helloworld.py delete mode 100644 tuf/examples/repository_delegations/client/metadata/current/release.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/current/root.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/current/targets.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/current/timestamp.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/previous/release.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/previous/root.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/previous/targets.txt delete mode 100644 tuf/examples/repository_delegations/client/metadata/previous/timestamp.txt delete mode 100644 tuf/examples/repository_delegations/keystore/026d535ad9bd3a7b9dd399612f264fb4be4aa88f9668ac6fb86bfc1f891458d8.key delete mode 100644 tuf/examples/repository_delegations/keystore/0e4b16ecd2fdde03651fe4d7e069ae3edeabbf781b7f6a2e56a71cc1955e202f.key delete mode 100644 tuf/examples/repository_delegations/keystore/649077bc2acbc020e340a4c8f13a106787ca75d026ec60db5c2c0af3b9b60085.key delete mode 100644 tuf/examples/repository_delegations/keystore/b6422d4c8a4acb07317b032b89f7d03cc8b42a8b464355141d06a61c13849d92.key delete mode 100644 tuf/examples/repository_delegations/keystore/c577a726a369d412247f1eb5d14234a90d84332b295d31bea42cf482fec0c885.key delete mode 100644 tuf/examples/repository_delegations/keystore/caa188c0925736af382a61fcdc62ce68e360e9ac5f50abefacb749e20dad23d8.key delete mode 100644 tuf/examples/repository_delegations/repository/config.cfg delete mode 100644 tuf/examples/repository_delegations/repository/metadata/release.txt delete mode 100644 tuf/examples/repository_delegations/repository/metadata/root.txt delete mode 100644 tuf/examples/repository_delegations/repository/metadata/targets.txt delete mode 100644 tuf/examples/repository_delegations/repository/metadata/targets/role1.txt delete mode 100644 tuf/examples/repository_delegations/repository/metadata/targets/role1/role2.txt delete mode 100644 tuf/examples/repository_delegations/repository/metadata/timestamp.txt delete mode 100644 tuf/examples/repository_delegations/repository/targets/LICENSE.txt delete mode 100644 tuf/examples/repository_delegations/repository/targets/role1/delegated_target1.txt delete mode 100644 tuf/examples/repository_delegations/repository/targets/role2/delegated_target2.txt delete mode 100755 tuf/pushtools/push.cfg.sample delete mode 100755 tuf/pushtools/push.py delete mode 100755 tuf/pushtools/pushtoolslib.py delete mode 100755 tuf/pushtools/receivetools/receive.cfg.sample delete mode 100755 tuf/pushtools/receivetools/receive.py delete mode 100755 tuf/pushtools/transfer/__init__.py delete mode 100755 tuf/pushtools/transfer/scp.py delete mode 100755 tuf/repo/__init__.py delete mode 100755 tuf/repo/keystore.py delete mode 100755 tuf/repo/quickstart.py delete mode 100755 tuf/repo/signercli.py delete mode 100755 tuf/repo/signerlib.py delete mode 100644 tuf/tests/__init__.py delete mode 100644 tuf/tests/repository_setup.py delete mode 100644 tuf/tests/util_test_tools.py delete mode 100755 tuf/time_ed25519.py diff --git a/docs/tuf-spec.txt b/docs/tuf-spec.txt index e2652002..a4c679da 100644 --- a/docs/tuf-spec.txt +++ b/docs/tuf-spec.txt @@ -476,8 +476,10 @@ The KEYID of a key is the hexdigest of the SHA-256 hash of the canonical JSON form of the key, where the "private" object key is excluded. - All times are in Unix / POSIX format, an integer representing the number - of seconds since the epoch (January 1, 1970.) + Metadata date-time data follows the ISO 8601 standard. The expected format + of the combined date and time string is "YYYY-MM-DDTHH:MM:SSZ". Time is + always in UTC, and the "Z" time zone designator is attached to indicate a + zero UTC offset. An example date-time string is "1985-10-21T01:21:00Z". 4.3. File formats: root.json diff --git a/tuf/examples/example_client.py b/examples/client/example_client.py similarity index 85% rename from tuf/examples/example_client.py rename to examples/client/example_client.py index 78b1103f..53b18142 100755 --- a/tuf/examples/example_client.py +++ b/examples/client/example_client.py @@ -6,7 +6,7 @@ Vladimir Diaz - September 2012 + September 2012. See LICENSE for licensing information. @@ -16,13 +16,16 @@ utilizing The Update Framework may write to securely update files. The 'basic_client.py' script can be used on the command-line to perform an update that will download and update all available targets; writing - custom code is not required in this case. + custom code is not required with 'basic_client.py'. The custom examples below demonstrate: (1) updating all targets (2) updating all the targets of a specified role (3) updating a specific target explicitely named. + It assumes a server is listening on 'http://localhost:8001'. One can be + started by navigating to the 'examples/repository/' and starting: + $ python -m SimpleHTTPServer 8001 """ import logging @@ -30,7 +33,7 @@ import tuf.client.updater # Uncomment the line below to enable printing of debugging information. -#tuf.log.set_log_level(logging.DEBUG) +tuf.log.set_log_level(logging.INFO) # Set the local repository directory containing the metadata files. tuf.conf.repository_directory = '.' @@ -61,7 +64,8 @@ for target in updated_targets: try: updater.download_target(target, destination_directory) - except tuf.DownloadError, e: + + except tuf.DownloadError as e: pass # Remove any files from the destination directory that are no longer being @@ -69,26 +73,22 @@ updater.remove_obsolete_targets(destination_directory) -""" -# Example demonstrating an update that only downloads the targets of -# a specific role (i.e., 'targets/role1') +# Example demonstrating an update that only downloads the targets of +# a specific role (i.e., 'targets/project') updater.refresh() -targets_of_role1 = updater.targets_of_role('targets/role1') +targets_of_role1 = updater.targets_of_role('targets/project') updated_targets = updater.updated_targets(targets_of_role1, destination_directory) for target in updated_targets: updater.download_target(target, destination_directory) -""" -""" + # Example demonstrating an update that downloads a specific target. - updater.refresh() -target = updater.target('LICENSE.txt') +target = updater.target('/file2.txt') updated_target = updater.updated_targets([target], destination_directory) for target in updated_target: updater.download_target(target, destination_directory) -""" diff --git a/examples/client/metadata/current/root.json b/examples/client/metadata/current/root.json new file mode 100644 index 0000000000000000000000000000000000000000..8128041ef45e56d4591a42de13645ec4801e5396 GIT binary patch literal 1831 zcmbtU+lm`W41J%kF#Vi`l2jMfe8#d*82dS%VYB(}pUw2QecN_FnFjV0F!aD;60!uTP|;eJI8v+z zC6NELAs|vkAr}QBD_AOpo~I9+wc)ePPlvUmbxaBBt>6Hb{}BRgRY_YEXt|QHHPK`S zDpfVWz??xLk^_L!wE;kaG73@&9O@ucu0eC-;0OXF7L7J6^w_x&6hsQ86pmVi3NuBF zwTH+Q^5iIA9yWh1a92`n0lSbfeVV@=ZNd2a;V@rdPkPv&x98dR^QZG(j^`(@JZ^^U z&bAIb*+0kah13ZHh2H}B^G67e>+|QejnlmFe&HMhTLcIdOOM(FQ^AZQL*$yF)G;`z zzyL}l5n4iwK4FO}1)R5AqgA~d<{#N!OJ8O8dUZ1$zqH*}{a{*%WJ^sGvx{em2ZM4V z>XM_N@qm<*!cZ*(6UCaT#xXQuil_^PD~bMfRo%NNDs|K15^_cKIS7MlXQh~tVjJGe zU6e3$^ZT`ip0JRhP81ffJ%52iJr6`dG56Yt!C=Ei+wS$ddtr4qPZXvlt{Fow; zo5`-moY%x_@zlOZe!7{_gZ9-hb`9T=LlHR zdn2h;9iYliLyf6>%p(q>7%&e4>uk}3n7~`O2`U?R@;7uyPS(9D1<(wGyOv(cP;-%J zM4!o%kfLWfC3BIwlvE7WQ893r&M9~#vb`N?iSZ?Yr^9YtYL{@Gmh8Tze%o&wZ`pTu ztGarB-5yK3`N^~Ybl5FB1eVe*5O0>nitD;*e}HnY8b1kTmeXgOU)Rq&NbXMW&m!4A g+j*Af<9`Tr_j3F{EMC7F=c}F0+r$1M-OIzvzcTO6{r~^~ literal 0 HcmV?d00001 diff --git a/examples/client/metadata/current/snapshot.json b/examples/client/metadata/current/snapshot.json new file mode 100644 index 0000000000000000000000000000000000000000..3294c89ede76ef6f75d6401edfc3f2856f6b3bab GIT binary patch literal 1007 zcmb7@-Hy{h42AFe6j82W8Qb&Y+1G$85JK}~x7~%dY64 zoafCjPW$bl%-_#4jrYT+%`iOMGmMY&wC{E^YLna)TH{z<_Myk(JSvfQ7?nUeix+_a z4uKUi3wN?NbO_|~_-0reev^53Tsum~5a4gY0Myx^{;$O5~VF@Bk!PO>n*Ka|t7JI+^4qx?MWufC1w$h~!7=Yw-qTdl?g8| zordvzJkECy)A8`p`QH#FD&i_DrcRC**r2R*zplsVpi81;{jzg_6TVxtz uB>OkO5@ZWrnouJX literal 0 HcmV?d00001 diff --git a/examples/client/metadata/current/targets.json b/examples/client/metadata/current/targets.json new file mode 100644 index 0000000000000000000000000000000000000000..6387d30ea4f8d4fdde51dc0e1edddfc007567407 GIT binary patch literal 1184 zcmbtT%WfMn47~d*7CN^LMZNPKIkgB1N}{|@9ow)sL4v^lUhXSKZUwr~F0^`(!W~aVDK!>jJsO)^XtD|p zeavz`C8!RAAtaO4w{8mf%tL>iB63CNbXqqAz?g?qs}=xa%Y@zp7s<4yYjLSbOp9cb z2EvHJCTdB$mNS#Kpmm7s2sA>1f*2e4nswzI9MxEB4Q#<1N5$!CGgt$<6^2#=0tCw- zHT}_-<1(^ z_ELMxL6ZloQHvxsT4S^^o(qv~HWCzvrbW@T1djy9mVm*7Z#2>(k1(@-uZy7Pqz#Xib(E?Du~X*YL?LNM?hw2TLU`)T}KOtFAk#ZXg9J*8Yc(y5*< z{Y2g583BCh37sG1@-D~XLvy;MBub@-4w?lb7s%*Pqq7ObIRrK)XT23A*~TF27V09` zB2lOIeAo`X1U6Gm%lZro{~45ZSU4Jp4xmQE&04gvMiz&labjVe5;oZiOo>eZc3yMK hnrxvowbn|bRC@!JMxsY+Efl}<`6BH!OK{y>{{m+DCgcDB literal 0 HcmV?d00001 diff --git a/examples/client/metadata/current/targets/project.json b/examples/client/metadata/current/targets/project.json new file mode 100644 index 0000000000000000000000000000000000000000..57f4195ab32db50b3e1a9274a056355a40072b4a GIT binary patch literal 604 zcmX|-+iu%15Jcbo6#}2zW_ej&@*R0<5fn>mX(@GWLt={rf&aZr*(yK+ zt@H6r?a#uidLDiphT&88pnmbkJc^qq{M4k450Q8&$33IhBL&-IT1kPg-O@c(RlAlZRnR$%N zft?vsOeti|kqeLy5f}ra#{fQtu{uIbBYI2dlflSTG*)?KO*Zn#kKke{a>MGuDxtg` zhS!d}mBJmim!W>Pk1Kb^7g~d~N`@i<5>YV)z{KctuVp6};}f5cZ4zS>`#=iY QfAyD_RjQDYK8Sw%2bv+AF8}}l literal 0 HcmV?d00001 diff --git a/examples/client/metadata/current/timestamp.json b/examples/client/metadata/current/timestamp.json new file mode 100644 index 0000000000000000000000000000000000000000..93e124e167dcb22a37eea5f2206c5523b52d0fda GIT binary patch literal 544 zcmXw$%}yOL42AdiDWa^QvYmgPgH;hiPU0}r_Wp>ORv^T?*E9DbIU74V{?75|!PsSf zJ@NYYO&0rLo({%*>NEDYe9T=t7MZF?ghHwNR+&PgNF$=pC1NULAWjSrx37w4-{bAQ=k(=Y>8tdj$9m<5mG{_B%ukk;(8Ams?tLQY z+&vW8V7D;r%ldJVjq!0l%ChqDa%H#jewnYqSpo<5fLf0L4}1N*{Zi+AlTzgqUzX{- z{`j?=Pv2c*Ctjxg0(<(pbD1>hxGinyS*m7QhF97KJ-d$NGu476`Jl((KAhBqAg-cN rO+|7?Jy*=Q8B=T5GZ_0vPOs~v!U&MIk($0u?LT>2<{b+U+W-0wX6=oH literal 0 HcmV?d00001 diff --git a/examples/client/metadata/previous/root.json b/examples/client/metadata/previous/root.json new file mode 100644 index 0000000000000000000000000000000000000000..8128041ef45e56d4591a42de13645ec4801e5396 GIT binary patch literal 1831 zcmbtU+lm`W41J%kF#Vi`l2jMfe8#d*82dS%VYB(}pUw2QecN_FnFjV0F!aD;60!uTP|;eJI8v+z zC6NELAs|vkAr}QBD_AOpo~I9+wc)ePPlvUmbxaBBt>6Hb{}BRgRY_YEXt|QHHPK`S zDpfVWz??xLk^_L!wE;kaG73@&9O@ucu0eC-;0OXF7L7J6^w_x&6hsQ86pmVi3NuBF zwTH+Q^5iIA9yWh1a92`n0lSbfeVV@=ZNd2a;V@rdPkPv&x98dR^QZG(j^`(@JZ^^U z&bAIb*+0kah13ZHh2H}B^G67e>+|QejnlmFe&HMhTLcIdOOM(FQ^AZQL*$yF)G;`z zzyL}l5n4iwK4FO}1)R5AqgA~d<{#N!OJ8O8dUZ1$zqH*}{a{*%WJ^sGvx{em2ZM4V z>XM_N@qm<*!cZ*(6UCaT#xXQuil_^PD~bMfRo%NNDs|K15^_cKIS7MlXQh~tVjJGe zU6e3$^ZT`ip0JRhP81ffJ%52iJr6`dG56Yt!C=Ei+wS$ddtr4qPZXvlt{Fow; zo5`-moY%x_@zlOZe!7{_gZ9-hb`9T=LlHR zdn2h;9iYliLyf6>%p(q>7%&e4>uk}3n7~`O2`U?R@;7uyPS(9D1<(wGyOv(cP;-%J zM4!o%kfLWfC3BIwlvE7WQ893r&M9~#vb`N?iSZ?Yr^9YtYL{@Gmh8Tze%o&wZ`pTu ztGarB-5yK3`N^~Ybl5FB1eVe*5O0>nitD;*e}HnY8b1kTmeXgOU)Rq&NbXMW&m!4A g+j*Af<9`Tr_j3F{EMC7F=c}F0+r$1M-OIzvzcTO6{r~^~ literal 0 HcmV?d00001 diff --git a/examples/client/metadata/previous/timestamp.json b/examples/client/metadata/previous/timestamp.json new file mode 100644 index 0000000000000000000000000000000000000000..93e124e167dcb22a37eea5f2206c5523b52d0fda GIT binary patch literal 544 zcmXw$%}yOL42AdiDWa^QvYmgPgH;hiPU0}r_Wp>ORv^T?*E9DbIU74V{?75|!PsSf zJ@NYYO&0rLo({%*>NEDYe9T=t7MZF?ghHwNR+&PgNF$=pC1NULAWjSrx37w4-{bAQ=k(=Y>8tdj$9m<5mG{_B%ukk;(8Ams?tLQY z+&vW8V7D;r%ldJVjq!0l%ChqDa%H#jewnYqSpo<5fLf0L4}1N*{Zi+AlTzgqUzX{- z{`j?=Pv2c*Ctjxg0(<(pbD1>hxGinyS*m7QhF97KJ-d$NGu476`Jl((KAhBqAg-cN rO+|7?Jy*=Q8B=T5GZ_0vPOs~v!U&MIk($0u?LT>2<{b+U+W-0wX6=oH literal 0 HcmV?d00001 diff --git a/examples/client/targets/file1.txt b/examples/client/targets/file1.txt new file mode 100644 index 00000000..7bf3499f --- /dev/null +++ b/examples/client/targets/file1.txt @@ -0,0 +1 @@ +This is an example target file. \ No newline at end of file diff --git a/examples/client/targets/file2.txt b/examples/client/targets/file2.txt new file mode 100644 index 00000000..606f18ef --- /dev/null +++ b/examples/client/targets/file2.txt @@ -0,0 +1 @@ +This is an another example target file. \ No newline at end of file diff --git a/examples/client/targets/project/file3.txt b/examples/client/targets/project/file3.txt new file mode 100644 index 00000000..60464604 --- /dev/null +++ b/examples/client/targets/project/file3.txt @@ -0,0 +1 @@ +This is role1's target file. \ No newline at end of file diff --git a/examples/generate.py b/examples/generate.py new file mode 100755 index 00000000..f9f3530a --- /dev/null +++ b/examples/generate.py @@ -0,0 +1,131 @@ +""" + + generate.py + + + Vladimir Diaz + + + February 26, 2014. + + + See LICENSE for licensing information. + + + Provide a set of pre-generated key files and a basic repository that unit + tests can use in their test cases. The pre-generated files created by this + script should be copied by the unit tests as needed. The original versions + should be preserved. 'tuf/tests/unit/repository_files/' will store the files + generated. 'generate.py' should not require re-execution if the pre-generated + repository files have already been created, unless they need to change in some + way. +""" + +import shutil +import datetime + +from tuf.repository_tool import * +import tuf.util + +repository = create_new_repository('repository') + +# Generate and save the top-level role keys, including the delegated roles. +# The unit tests should only have to import the keys they need from these +# pre-generated key files. +root_key_file = 'keystore/root_key' +targets_key_file = 'keystore/targets_key' +snapshot_key_file = 'keystore/snapshot_key' +timestamp_key_file = 'keystore/timestamp_key' +project_key_file = 'keystore/project_key' + +# Generate public and private key files for the top-level roles, and two +# delegated roles (these number of keys should be sufficient for most of the +# unit tests). Unit tests may generate additional keys, if needed. +generate_and_write_ed25519_keypair(root_key_file, password='password') +generate_and_write_ed25519_keypair(targets_key_file, password='password') +generate_and_write_ed25519_keypair(snapshot_key_file, password='password') +generate_and_write_ed25519_keypair(timestamp_key_file, password='password') +generate_and_write_ed25519_keypair(project_key_file, password='password') + +# Import the public keys. These keys are needed so that metadata roles are +# assigned verification keys, which clients use to verify the signatures created +# by the corresponding private keys. +root_public = import_ed25519_publickey_from_file(root_key_file+'.pub') +targets_public = import_ed25519_publickey_from_file(targets_key_file+'.pub') +snapshot_public = import_ed25519_publickey_from_file(snapshot_key_file+'.pub') +timestamp_public = import_ed25519_publickey_from_file(timestamp_key_file+'.pub') +project_public = import_ed25519_publickey_from_file(project_key_file+'.pub') + +# Import the private keys. These private keys are needed to generate the +# signatures included in metadata. +root_private = import_ed25519_privatekey_from_file(root_key_file, 'password') +targets_private = import_ed25519_privatekey_from_file(targets_key_file, 'password') +snapshot_private = import_ed25519_privatekey_from_file(snapshot_key_file, 'password') +timestamp_private = import_ed25519_privatekey_from_file(timestamp_key_file, 'password') +project_private = import_ed25519_privatekey_from_file(project_key_file, 'password') + +# Add the verification keys to the top-level roles. +repository.root.add_verification_key(root_public) +repository.targets.add_verification_key(targets_public) +repository.snapshot.add_verification_key(snapshot_public) +repository.timestamp.add_verification_key(timestamp_public) + +# Load the signing keys, previously imported, for the top-level roles so that +# valid metadata can be written. +repository.root.load_signing_key(root_private) +repository.targets.load_signing_key(targets_private) +repository.snapshot.load_signing_key(snapshot_private) +repository.timestamp.load_signing_key(timestamp_private) + +# Create the target files (downloaded by clients) whose file size and digest +# are specified in the 'targets.json' file. +target1_filepath = 'repository/targets/file1.txt' +tuf.util.ensure_parent_dir(target1_filepath) +target2_filepath = 'repository/targets/file2.txt' +tuf.util.ensure_parent_dir(target2_filepath) +target3_filepath = 'repository/targets/project/file3.txt' +tuf.util.ensure_parent_dir(target3_filepath) + +with open(target1_filepath, 'wb') as file_object: + file_object.write('This is an example target file.') + +with open(target2_filepath, 'wb') as file_object: + file_object.write('This is an another example target file.') + +with open(target3_filepath, 'wb') as file_object: + file_object.write('This is role1\'s target file.') + +# Add target files to the top-level 'targets.json' role. These target files +# should already exist. +repository.targets.add_target(target1_filepath) +repository.targets.add_target(target2_filepath) + +repository.targets.delegate('project', [project_public], [target3_filepath]) +repository.targets('project').load_signing_key(project_private) + +# Set the top-level expiration times far into the future so that +# they do not expire anytime soon, or else the tests fail. Unit tests may +# modify the expiration datetimes (of the copied files), if they wish. +repository.root.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.targets.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.snapshot.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.timestamp.expiration = datetime.datetime(2030, 01, 01, 00, 00) +repository.targets('project').expiration = datetime.datetime(2030, 01, 01, 00, 00) + +# Compress the 'targets.json' role so that the unit tests have a pre-generated +# example of compressed metadata. +repository.targets.compressions = ['gz'] + +# Create the actual metadata files, which are saved to 'metadata.staged'. +repository.write() + +# Move the staged.metadata to 'metadata' and create the client folder. The +# client folder, which includes the required directory structure and metadata +# files for clients to successfully load an 'tuf.client.updater.py' object. +staged_metadata_directory = 'repository/metadata.staged' +metadata_directory = 'repository/metadata' +shutil.copytree(staged_metadata_directory, metadata_directory) + +# Create the client files (required directory structure and minimal metadata) +# required by the 'tuf.interposition' and 'tuf.client.updater.py' updaters. +create_tuf_client_directory('repository', 'client') diff --git a/examples/keystore/project_key b/examples/keystore/project_key new file mode 100644 index 00000000..ad2c31d8 --- /dev/null +++ b/examples/keystore/project_key @@ -0,0 +1 @@ +5bc5cdc1e18ff3ccbfd7c33a88bec596@@@@313030303030@@@@39656230336534656264303863303733353832386465383466346232633863356362616234306430333863353239393966643333373730353231386262623339@@@@31caf11a40e9eb48f499dcc6f80c88e8@@@@0400a24989069098ce702d6a8f81826ba482ffd9a17fff5a10c6f986b9179bae1be4d246db2fceda93ce12bd465e896e4e4e847c934eb120c794e12fbcc8569e4aa6964995f1fda9af50057644236c4194ebb78a588804363dbce492db7e4ef2ce39391013a7c2d68260a0e785b6cec39fc02cdff11e9c7a168ae133292fdbe5f2f968d2a0ae098ca9453ec33d175de181f771b016c760ceba51d9c20c431702656e8b09ebda6cf884096b89d80dd6c2c1752c04312e934cd79d49b91e8c7ef4448bf3b969fa6ed9727cac48f91e3921f41d5bf8ec4130ff45fda51e07cd16a7fa8daca1feb6f2dced6881c736565a1b72dc705a97606a0480c0dd681e65bea82fc261762cdad203fd49d45f \ No newline at end of file diff --git a/examples/keystore/project_key.pub b/examples/keystore/project_key.pub new file mode 100644 index 00000000..0f1c3193 --- /dev/null +++ b/examples/keystore/project_key.pub @@ -0,0 +1 @@ +{"keytype": "ed25519", "keyval": {"public": "b6e40fb71a6041212a3d84331336ecaa1f48a0c523f80ccc762a034c727606fa"}} \ No newline at end of file diff --git a/examples/keystore/root_key b/examples/keystore/root_key new file mode 100644 index 00000000..0f87c374 --- /dev/null +++ b/examples/keystore/root_key @@ -0,0 +1 @@ +70b05482c00cad0fccfcab4ac2b86b50@@@@313030303030@@@@62626137313336626662363862333962636161306561326437336237643436306162323966303038326636306363613965396161353566646330346666356635@@@@5f6612c00c8ed8627dd33754812bb7e1@@@@f651c3db5ac02aa8337264f0badd1f40fbe174ec3f8de8fd95188d31cf41b4863d7a28db297b8a5abd25e49d2af3c6cb2e25789088dce2b5113b7d7db16deea1eadf109d5ec004a2b5bcbdc29e13a9e4c803659def851800969918fb5930c56b816b119be490667f2d629309bc7578dad43c6b0ca6ef0c6f48ad68390fb1fc711b8a40bb5b1c197ae6f72d2b1e83bbc9050f46a3c69efe3b11c55a52d3f68f6e9fe58ad7e67dd6b136681b6d800fed22f15d31ee71ad1ed78f36b6d19b0771c22123f1dcb54a6b2e9742d2661014931cbab8fbbb001e3ac836bc5c64b19fa4cb881485acdafa6a0fd87044534608f50ae13920517c6f2ab2669f8edd4bc6cbe2fc1fc272264a819e2e34bc83 \ No newline at end of file diff --git a/examples/keystore/root_key.pub b/examples/keystore/root_key.pub new file mode 100644 index 00000000..7cf01390 --- /dev/null +++ b/examples/keystore/root_key.pub @@ -0,0 +1 @@ +{"keytype": "ed25519", "keyval": {"public": "66dd78c5c2a78abc6fc6b267ff1a8017ba0e8bfc853dd97af351949bba021275"}} \ No newline at end of file diff --git a/examples/keystore/snapshot_key b/examples/keystore/snapshot_key new file mode 100644 index 00000000..9f999f94 --- /dev/null +++ b/examples/keystore/snapshot_key @@ -0,0 +1 @@ +cff2ed748f0281d65c8eb535e81dbfd0@@@@313030303030@@@@30353732326564313863306632626336656631636635313666636330383537376466393833313937363261303638336632373530303938623665346239646230@@@@1eda960b65beefb9a2a4afcc9be973f6@@@@ea7c468f9e5ab91e874b2a0fa692a18056293261886400e5976a6c884b13cdd3905b0188f41b0c2b2846d36dabbeecf4d9d1c87123b5b236ccbce24f4b30c9ba25d00ccce270d3da1d202d2fd27a0a007381bf2284c58bb01a812050086264617f94a47cd3931b9129c6b105eec4ff56d1176e6fbde6bf5ade513a0f1c7551cecb04c678582e7fd0656936a1f232fa3a739df3dc4af07ad69580f54b1dd366c78e96c07249732621929aac056f4892af07772011246101cfb9886558fe2cfd1d09e9c280cbecdf2fb8dbeded4a628ef2fa04d5360dae327ccf15a12a00f2f32964ca03abaeaeb60bbece207e09c897413b8a630331df568822880be86359181664aa0806770ab11e30663594 \ No newline at end of file diff --git a/examples/keystore/snapshot_key.pub b/examples/keystore/snapshot_key.pub new file mode 100644 index 00000000..cebf0249 --- /dev/null +++ b/examples/keystore/snapshot_key.pub @@ -0,0 +1 @@ +{"keytype": "ed25519", "keyval": {"public": "01c61f8dc7d77fcef973f4267927541e355e8ceda757e2c402818dad850f856e"}} \ No newline at end of file diff --git a/examples/keystore/targets_key b/examples/keystore/targets_key new file mode 100644 index 00000000..f56772a5 --- /dev/null +++ b/examples/keystore/targets_key @@ -0,0 +1 @@ +a0fdf15a675c849cc7bd4cfb46273164@@@@313030303030@@@@62303735356165326132633766333932633137643234323961636166356135666433333234376163356665656330306362366363383332386430356133663831@@@@0d7ce5bc8687edc1a292ebbe203284b6@@@@155dee106e192009ed4dace179e514f518d403d7a83d136047683683090c8dd1e80d6d5f6bd480e1e9e76bf63a265dfdc7e5bce07418039c6457c6d454fd0114fd7311c8508ead020e59e0b5b2ee55101d49535980e598c02c70ecb7bca77a54d04040080ca9403cd23bd0ae4cc5e33131321d2a4c8a4544553f5c54b7af0986a437f37696b4e7d76efed24be9a4c6928542fb93cfd88018537703a1ff0f6ac8fcb625714a161aaa7a548b39963ded1481b47020b1346544a8a9d96f1eb03a6cad326af0f5be43c6756dc1d4e6c7dee550077d7689949a671acd370680d6b29e594675ade9d7525a4ad8af7a1b13a3e4692192d70c189115d52b814af5a53bc9d0528aec6c6417f6f7d0bcb3 \ No newline at end of file diff --git a/examples/keystore/targets_key.pub b/examples/keystore/targets_key.pub new file mode 100644 index 00000000..5e1d1e1a --- /dev/null +++ b/examples/keystore/targets_key.pub @@ -0,0 +1 @@ +{"keytype": "ed25519", "keyval": {"public": "68ead6e54a43f8f36f9717b10669d1ef0ebb38cee6b05317669341309f1069cb"}} \ No newline at end of file diff --git a/examples/keystore/timestamp_key b/examples/keystore/timestamp_key new file mode 100644 index 00000000..42dc9bda --- /dev/null +++ b/examples/keystore/timestamp_key @@ -0,0 +1 @@ +dd0f6b3bff19df2c4ab89e34a5b190e6@@@@313030303030@@@@37613435636666623966633230613439376661303631616635323365326636643039383236333638633162326262616231653531353066656262643265623566@@@@e6d72cc0a99144c4dbf509fa56092454@@@@10bb33112d4083f81d700740e78b315574a0d7b8c042921b1dcb7b8593caff1dbb10b8886ea2f4b7edcb49d3aabc9ec25db59dc0b121890555dc5d69291856f739b280de6fa6216e0ac92b9b95689b9f6ba1a414cb78ee6547968ea5ebd84e34972cf6d0e56cf0443b653f51d8e2742a7454e70039e548e4a69e97e73475940964307b5d5da440767531479b0c940dc8ffebafdf562e3a68d456f9438cb3c2253117180efd868b8b9fd4ea3e717501db8c0a9afe0bcdb34068eef4858103b2126b47d4c22a3d0f16cec0e5cd452201487eb6695139d8235a17a3c1a42fa7552d7ca45625b0000650f22851679ac00c7b71368d4dfc862a1823437b5cc244c282be0c01138fa39dc13511bd59 \ No newline at end of file diff --git a/examples/keystore/timestamp_key.pub b/examples/keystore/timestamp_key.pub new file mode 100644 index 00000000..3224de62 --- /dev/null +++ b/examples/keystore/timestamp_key.pub @@ -0,0 +1 @@ +{"keytype": "ed25519", "keyval": {"public": "72378e5bc588793e58f81c8533da64a2e8f1565c1fcc7f253496394ffc52542c"}} \ No newline at end of file diff --git a/examples/repository/metadata.staged/root.json b/examples/repository/metadata.staged/root.json new file mode 100644 index 0000000000000000000000000000000000000000..8128041ef45e56d4591a42de13645ec4801e5396 GIT binary patch literal 1831 zcmbtU+lm`W41J%kF#Vi`l2jMfe8#d*82dS%VYB(}pUw2QecN_FnFjV0F!aD;60!uTP|;eJI8v+z zC6NELAs|vkAr}QBD_AOpo~I9+wc)ePPlvUmbxaBBt>6Hb{}BRgRY_YEXt|QHHPK`S zDpfVWz??xLk^_L!wE;kaG73@&9O@ucu0eC-;0OXF7L7J6^w_x&6hsQ86pmVi3NuBF zwTH+Q^5iIA9yWh1a92`n0lSbfeVV@=ZNd2a;V@rdPkPv&x98dR^QZG(j^`(@JZ^^U z&bAIb*+0kah13ZHh2H}B^G67e>+|QejnlmFe&HMhTLcIdOOM(FQ^AZQL*$yF)G;`z zzyL}l5n4iwK4FO}1)R5AqgA~d<{#N!OJ8O8dUZ1$zqH*}{a{*%WJ^sGvx{em2ZM4V z>XM_N@qm<*!cZ*(6UCaT#xXQuil_^PD~bMfRo%NNDs|K15^_cKIS7MlXQh~tVjJGe zU6e3$^ZT`ip0JRhP81ffJ%52iJr6`dG56Yt!C=Ei+wS$ddtr4qPZXvlt{Fow; zo5`-moY%x_@zlOZe!7{_gZ9-hb`9T=LlHR zdn2h;9iYliLyf6>%p(q>7%&e4>uk}3n7~`O2`U?R@;7uyPS(9D1<(wGyOv(cP;-%J zM4!o%kfLWfC3BIwlvE7WQ893r&M9~#vb`N?iSZ?Yr^9YtYL{@Gmh8Tze%o&wZ`pTu ztGarB-5yK3`N^~Ybl5FB1eVe*5O0>nitD;*e}HnY8b1kTmeXgOU)Rq&NbXMW&m!4A g+j*Af<9`Tr_j3F{EMC7F=c}F0+r$1M-OIzvzcTO6{r~^~ literal 0 HcmV?d00001 diff --git a/examples/repository/metadata.staged/snapshot.json b/examples/repository/metadata.staged/snapshot.json new file mode 100644 index 0000000000000000000000000000000000000000..3294c89ede76ef6f75d6401edfc3f2856f6b3bab GIT binary patch literal 1007 zcmb7@-Hy{h42AFe6j82W8Qb&Y+1G$85JK}~x7~%dY64 zoafCjPW$bl%-_#4jrYT+%`iOMGmMY&wC{E^YLna)TH{z<_Myk(JSvfQ7?nUeix+_a z4uKUi3wN?NbO_|~_-0reev^53Tsum~5a4gY0Myx^{;$O5~VF@Bk!PO>n*Ka|t7JI+^4qx?MWufC1w$h~!7=Yw-qTdl?g8| zordvzJkECy)A8`p`QH#FD&i_DrcRC**r2R*zplsVpi81;{jzg_6TVxtz uB>OkO5@ZWrnouJX literal 0 HcmV?d00001 diff --git a/examples/repository/metadata.staged/targets.json b/examples/repository/metadata.staged/targets.json new file mode 100644 index 0000000000000000000000000000000000000000..6387d30ea4f8d4fdde51dc0e1edddfc007567407 GIT binary patch literal 1184 zcmbtT%WfMn47~d*7CN^LMZNPKIkgB1N}{|@9ow)sL4v^lUhXSKZUwr~F0^`(!W~aVDK!>jJsO)^XtD|p zeavz`C8!RAAtaO4w{8mf%tL>iB63CNbXqqAz?g?qs}=xa%Y@zp7s<4yYjLSbOp9cb z2EvHJCTdB$mNS#Kpmm7s2sA>1f*2e4nswzI9MxEB4Q#<1N5$!CGgt$<6^2#=0tCw- zHT}_-<1(^ z_ELMxL6ZloQHvxsT4S^^o(qv~HWCzvrbW@T1djy9mVm*7Z#2>(k1(@-uZy7Pqz#Xib(E?Du~X*YL?LNM?hw2TLU`)T}KOtFAk#ZXg9J*8Yc(y5*< z{Y2g583BCh37sG1@-D~XLvy;MBub@-4w?lb7s%*Pqq7ObIRrK)XT23A*~TF27V09` zB2lOIeAo`X1U6Gm%lZro{~45ZSU4Jp4xmQE&04gvMiz&labjVe5;oZiOo>eZc3yMK hnrxvowbn|bRC@!JMxsY+Efl}<`6BH!OK{y>{{m+DCgcDB literal 0 HcmV?d00001 diff --git a/examples/repository/metadata.staged/targets.json.gz b/examples/repository/metadata.staged/targets.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..85fa08905468c640a312e213f5e76b10bd882ad5 GIT binary patch literal 599 zcmV-d0;v5TiwFRh=TTDv|E*KoZrd;nea}}Ie%^&5b?tZbX+tqk5*2$(lOT4B7DfJj z@+C=&VZa^+$c9Z(M~CN-Z=0qa`t6>_r;`lrUGsg@G;j53+F$b8=Nf4PNy0=~l1mAE zKvXUeLQ2^u(iTa_;!2HzLF;494*{}4M~qCd{nX5Wk1}?L8BsDCqrzpQ5}2c*WDNkm zL`F-_c&1WRP9~I!sgZS1!06HGz$GYCViZ;ylyY7SgF*<95PbogqskJU;p}tHPBd6D zoQ%pkf+kQU36$&*z-fVcvaMv!o91C+HH#t>*-4D{$M|}biQ_$=wlWS2^ES&)wmkNS z{jg@vvR!|gHVIX1aTq*A2#IYmQrLPGoFa%+KTwIuV4V>ZNWM_gPQap#k*`QI@P)-k z{g%CF-rbU$_V^TceVUS^t+kiv6kE^=Rkb90O{9pdBxY5#XGjLA`08nL78ytp?)h=nVO-q!dORI|N*Zr^9C_?MP`IRg zE&WdD%lI<>0XlYd0=mO)GF{z@eb`(IZsq0JFH_PY5PSwz*FC`9yuPhXv68wr#C)Zi zue`QJ$3rK>J9CGQ(bx&un&^{t#i$fQbt-r?P&6(F=!u*wz@hg+PfOp zkXNx*i<)*P`|a3OU{dd`n@Qn6lhOv0cmi(#vL`88qxRWL?GPkW-BTf8sasAs2ssdA lm6WJhCl2ASq{3#^d6oECPD351#e(zZ{2OhVPXM3<006dDAiMwo literal 0 HcmV?d00001 diff --git a/examples/repository/metadata.staged/targets/project.json b/examples/repository/metadata.staged/targets/project.json new file mode 100644 index 0000000000000000000000000000000000000000..57f4195ab32db50b3e1a9274a056355a40072b4a GIT binary patch literal 604 zcmX|-+iu%15Jcbo6#}2zW_ej&@*R0<5fn>mX(@GWLt={rf&aZr*(yK+ zt@H6r?a#uidLDiphT&88pnmbkJc^qq{M4k450Q8&$33IhBL&-IT1kPg-O@c(RlAlZRnR$%N zft?vsOeti|kqeLy5f}ra#{fQtu{uIbBYI2dlflSTG*)?KO*Zn#kKke{a>MGuDxtg` zhS!d}mBJmim!W>Pk1Kb^7g~d~N`@i<5>YV)z{KctuVp6};}f5cZ4zS>`#=iY QfAyD_RjQDYK8Sw%2bv+AF8}}l literal 0 HcmV?d00001 diff --git a/examples/repository/metadata.staged/timestamp.json b/examples/repository/metadata.staged/timestamp.json new file mode 100644 index 0000000000000000000000000000000000000000..93e124e167dcb22a37eea5f2206c5523b52d0fda GIT binary patch literal 544 zcmXw$%}yOL42AdiDWa^QvYmgPgH;hiPU0}r_Wp>ORv^T?*E9DbIU74V{?75|!PsSf zJ@NYYO&0rLo({%*>NEDYe9T=t7MZF?ghHwNR+&PgNF$=pC1NULAWjSrx37w4-{bAQ=k(=Y>8tdj$9m<5mG{_B%ukk;(8Ams?tLQY z+&vW8V7D;r%ldJVjq!0l%ChqDa%H#jewnYqSpo<5fLf0L4}1N*{Zi+AlTzgqUzX{- z{`j?=Pv2c*Ctjxg0(<(pbD1>hxGinyS*m7QhF97KJ-d$NGu476`Jl((KAhBqAg-cN rO+|7?Jy*=Q8B=T5GZ_0vPOs~v!U&MIk($0u?LT>2<{b+U+W-0wX6=oH literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/root.json b/examples/repository/metadata/root.json new file mode 100644 index 0000000000000000000000000000000000000000..8128041ef45e56d4591a42de13645ec4801e5396 GIT binary patch literal 1831 zcmbtU+lm`W41J%kF#Vi`l2jMfe8#d*82dS%VYB(}pUw2QecN_FnFjV0F!aD;60!uTP|;eJI8v+z zC6NELAs|vkAr}QBD_AOpo~I9+wc)ePPlvUmbxaBBt>6Hb{}BRgRY_YEXt|QHHPK`S zDpfVWz??xLk^_L!wE;kaG73@&9O@ucu0eC-;0OXF7L7J6^w_x&6hsQ86pmVi3NuBF zwTH+Q^5iIA9yWh1a92`n0lSbfeVV@=ZNd2a;V@rdPkPv&x98dR^QZG(j^`(@JZ^^U z&bAIb*+0kah13ZHh2H}B^G67e>+|QejnlmFe&HMhTLcIdOOM(FQ^AZQL*$yF)G;`z zzyL}l5n4iwK4FO}1)R5AqgA~d<{#N!OJ8O8dUZ1$zqH*}{a{*%WJ^sGvx{em2ZM4V z>XM_N@qm<*!cZ*(6UCaT#xXQuil_^PD~bMfRo%NNDs|K15^_cKIS7MlXQh~tVjJGe zU6e3$^ZT`ip0JRhP81ffJ%52iJr6`dG56Yt!C=Ei+wS$ddtr4qPZXvlt{Fow; zo5`-moY%x_@zlOZe!7{_gZ9-hb`9T=LlHR zdn2h;9iYliLyf6>%p(q>7%&e4>uk}3n7~`O2`U?R@;7uyPS(9D1<(wGyOv(cP;-%J zM4!o%kfLWfC3BIwlvE7WQ893r&M9~#vb`N?iSZ?Yr^9YtYL{@Gmh8Tze%o&wZ`pTu ztGarB-5yK3`N^~Ybl5FB1eVe*5O0>nitD;*e}HnY8b1kTmeXgOU)Rq&NbXMW&m!4A g+j*Af<9`Tr_j3F{EMC7F=c}F0+r$1M-OIzvzcTO6{r~^~ literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/snapshot.json b/examples/repository/metadata/snapshot.json new file mode 100644 index 0000000000000000000000000000000000000000..3294c89ede76ef6f75d6401edfc3f2856f6b3bab GIT binary patch literal 1007 zcmb7@-Hy{h42AFe6j82W8Qb&Y+1G$85JK}~x7~%dY64 zoafCjPW$bl%-_#4jrYT+%`iOMGmMY&wC{E^YLna)TH{z<_Myk(JSvfQ7?nUeix+_a z4uKUi3wN?NbO_|~_-0reev^53Tsum~5a4gY0Myx^{;$O5~VF@Bk!PO>n*Ka|t7JI+^4qx?MWufC1w$h~!7=Yw-qTdl?g8| zordvzJkECy)A8`p`QH#FD&i_DrcRC**r2R*zplsVpi81;{jzg_6TVxtz uB>OkO5@ZWrnouJX literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/targets.json b/examples/repository/metadata/targets.json new file mode 100644 index 0000000000000000000000000000000000000000..6387d30ea4f8d4fdde51dc0e1edddfc007567407 GIT binary patch literal 1184 zcmbtT%WfMn47~d*7CN^LMZNPKIkgB1N}{|@9ow)sL4v^lUhXSKZUwr~F0^`(!W~aVDK!>jJsO)^XtD|p zeavz`C8!RAAtaO4w{8mf%tL>iB63CNbXqqAz?g?qs}=xa%Y@zp7s<4yYjLSbOp9cb z2EvHJCTdB$mNS#Kpmm7s2sA>1f*2e4nswzI9MxEB4Q#<1N5$!CGgt$<6^2#=0tCw- zHT}_-<1(^ z_ELMxL6ZloQHvxsT4S^^o(qv~HWCzvrbW@T1djy9mVm*7Z#2>(k1(@-uZy7Pqz#Xib(E?Du~X*YL?LNM?hw2TLU`)T}KOtFAk#ZXg9J*8Yc(y5*< z{Y2g583BCh37sG1@-D~XLvy;MBub@-4w?lb7s%*Pqq7ObIRrK)XT23A*~TF27V09` zB2lOIeAo`X1U6Gm%lZro{~45ZSU4Jp4xmQE&04gvMiz&labjVe5;oZiOo>eZc3yMK hnrxvowbn|bRC@!JMxsY+Efl}<`6BH!OK{y>{{m+DCgcDB literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/targets.json.gz b/examples/repository/metadata/targets.json.gz new file mode 100644 index 0000000000000000000000000000000000000000..85fa08905468c640a312e213f5e76b10bd882ad5 GIT binary patch literal 599 zcmV-d0;v5TiwFRh=TTDv|E*KoZrd;nea}}Ie%^&5b?tZbX+tqk5*2$(lOT4B7DfJj z@+C=&VZa^+$c9Z(M~CN-Z=0qa`t6>_r;`lrUGsg@G;j53+F$b8=Nf4PNy0=~l1mAE zKvXUeLQ2^u(iTa_;!2HzLF;494*{}4M~qCd{nX5Wk1}?L8BsDCqrzpQ5}2c*WDNkm zL`F-_c&1WRP9~I!sgZS1!06HGz$GYCViZ;ylyY7SgF*<95PbogqskJU;p}tHPBd6D zoQ%pkf+kQU36$&*z-fVcvaMv!o91C+HH#t>*-4D{$M|}biQ_$=wlWS2^ES&)wmkNS z{jg@vvR!|gHVIX1aTq*A2#IYmQrLPGoFa%+KTwIuV4V>ZNWM_gPQap#k*`QI@P)-k z{g%CF-rbU$_V^TceVUS^t+kiv6kE^=Rkb90O{9pdBxY5#XGjLA`08nL78ytp?)h=nVO-q!dORI|N*Zr^9C_?MP`IRg zE&WdD%lI<>0XlYd0=mO)GF{z@eb`(IZsq0JFH_PY5PSwz*FC`9yuPhXv68wr#C)Zi zue`QJ$3rK>J9CGQ(bx&un&^{t#i$fQbt-r?P&6(F=!u*wz@hg+PfOp zkXNx*i<)*P`|a3OU{dd`n@Qn6lhOv0cmi(#vL`88qxRWL?GPkW-BTf8sasAs2ssdA lm6WJhCl2ASq{3#^d6oECPD351#e(zZ{2OhVPXM3<006dDAiMwo literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/targets/project.json b/examples/repository/metadata/targets/project.json new file mode 100644 index 0000000000000000000000000000000000000000..57f4195ab32db50b3e1a9274a056355a40072b4a GIT binary patch literal 604 zcmX|-+iu%15Jcbo6#}2zW_ej&@*R0<5fn>mX(@GWLt={rf&aZr*(yK+ zt@H6r?a#uidLDiphT&88pnmbkJc^qq{M4k450Q8&$33IhBL&-IT1kPg-O@c(RlAlZRnR$%N zft?vsOeti|kqeLy5f}ra#{fQtu{uIbBYI2dlflSTG*)?KO*Zn#kKke{a>MGuDxtg` zhS!d}mBJmim!W>Pk1Kb^7g~d~N`@i<5>YV)z{KctuVp6};}f5cZ4zS>`#=iY QfAyD_RjQDYK8Sw%2bv+AF8}}l literal 0 HcmV?d00001 diff --git a/examples/repository/metadata/timestamp.json b/examples/repository/metadata/timestamp.json new file mode 100644 index 0000000000000000000000000000000000000000..93e124e167dcb22a37eea5f2206c5523b52d0fda GIT binary patch literal 544 zcmXw$%}yOL42AdiDWa^QvYmgPgH;hiPU0}r_Wp>ORv^T?*E9DbIU74V{?75|!PsSf zJ@NYYO&0rLo({%*>NEDYe9T=t7MZF?ghHwNR+&PgNF$=pC1NULAWjSrx37w4-{bAQ=k(=Y>8tdj$9m<5mG{_B%ukk;(8Ams?tLQY z+&vW8V7D;r%ldJVjq!0l%ChqDa%H#jewnYqSpo<5fLf0L4}1N*{Zi+AlTzgqUzX{- z{`j?=Pv2c*Ctjxg0(<(pbD1>hxGinyS*m7QhF97KJ-d$NGu476`Jl((KAhBqAg-cN rO+|7?Jy*=Q8B=T5GZ_0vPOs~v!U&MIk($0u?LT>2<{b+U+W-0wX6=oH literal 0 HcmV?d00001 diff --git a/examples/repository/targets/file1.txt b/examples/repository/targets/file1.txt new file mode 100644 index 00000000..7bf3499f --- /dev/null +++ b/examples/repository/targets/file1.txt @@ -0,0 +1 @@ +This is an example target file. \ No newline at end of file diff --git a/examples/repository/targets/file2.txt b/examples/repository/targets/file2.txt new file mode 100644 index 00000000..606f18ef --- /dev/null +++ b/examples/repository/targets/file2.txt @@ -0,0 +1 @@ +This is an another example target file. \ No newline at end of file diff --git a/examples/repository/targets/project/file3.txt b/examples/repository/targets/project/file3.txt new file mode 100644 index 00000000..60464604 --- /dev/null +++ b/examples/repository/targets/project/file3.txt @@ -0,0 +1 @@ +This is role1's target file. \ No newline at end of file diff --git a/setup.py b/setup.py index 98e3b093..17867ca0 100755 --- a/setup.py +++ b/setup.py @@ -101,7 +101,7 @@ 'Topic :: Software Development' ], install_requires = [], - packages = find_packages(exclude=['tests', 'tuf.tests']), + packages = find_packages(exclude=['tests']), extras_require = extras, scripts = [ 'tuf/client/basic_client.py' diff --git a/tuf/pushtools/__init__.py b/tests/__init__.py similarity index 100% rename from tuf/pushtools/__init__.py rename to tests/__init__.py diff --git a/tests/unit/aggregate_tests.py b/tests/aggregate_tests.py similarity index 100% rename from tests/unit/aggregate_tests.py rename to tests/aggregate_tests.py diff --git a/tests/integration/test_delegations.py b/tests/integration/test_delegations.py deleted file mode 100755 index 8b216efe..00000000 --- a/tests/integration/test_delegations.py +++ /dev/null @@ -1,541 +0,0 @@ -#!/usr/bin/env python - -""" - - test_delegations.py - - - Konstantin Andrianov - - - February 19, 2012 - - - See LICENSE for licensing information. - - - Ensure that TUF meets expectations about target delegations. -""" - -import os -import time -import tempfile -import unittest - -import tuf.formats -import tuf.repo.keystore as keystore -import tuf.repo.signercli as signercli -import tuf.repo.signerlib as signerlib -import tuf.tests.util_test_tools as util_test_tools - -version = 1 -# Modify the number of iterations (from the higher default count) so the unit -# tests run faster. -keystore._PBKDF2_ITERATIONS = 1000 - - -class TestDelegationFunctions(unittest.TestCase): - - - def do_update(self): - # Client side repository. - tuf_client = os.path.join(self.root_repo, 'tuf_client') - downloads_dir = os.path.join(self.root_repo, 'downloads') - - # Adjust client's configuration file. - tuf.conf.repository_directory = tuf_client - - updater = tuf.client.updater.Updater('my_repo', self.mirrors) - - # Refresh the repository's top-level roles, store the target information for - # all the targets tracked, and determine which of these targets have been - # updated. - updater.refresh() - - # Obtain a list of available targets. - targets = [] - relative_target_filepaths = self.relpath_from_targets(self.target_filepaths) - for target_filepath in relative_target_filepaths: - target_info = updater.target(target_filepath) - targets.append(target_info) - - # Download each of these updated targets and save them locally. - updated_targets = updater.updated_targets(targets, downloads_dir) - for target in updated_targets: - updater.download_target(target, downloads_dir) - - # Return metadata about downloaded targets. - make_fileinfo = signerlib.get_metadata_file_info - targets_metadata = {} - for target_filepath in relative_target_filepaths: - download_filepath = os.path.join(downloads_dir, target_filepath) - target_fileinfo = signerlib.get_metadata_file_info(download_filepath) - targets_metadata[target_filepath] = target_fileinfo - return targets_metadata - - - def make_targets_metadata(self): - """Subclasses will override this method to generate metadata for all - targets roles, with the understanding that there is a fixed structure of - the targets roles.""" - - raise NotImplementedError() - - - def relpath_from_targets(self, target_filepaths): - """Ex: 'targets/more_targets/somefile.txt' -> 'more_targets/somefile.txt' - i.e. 'targets/' is removed from 'target'.""" - - new_target_filepaths = [] - for target in target_filepaths: - relative_targetpath = os.path.sep.join(target.split(os.path.sep)[1:]) - new_target_filepaths.append(relative_targetpath) - return new_target_filepaths - - - def setUp(self): - """ - The target delegations tree is fixed as such: - targets -> [T1, T2] - T1 -> [T3] - """ - global version - version = version+1 - expiration = tuf.formats.format_time(time.time()+86400) - - root_repo, url, server_proc, keyids = util_test_tools.init_repo(using_tuf=True) - - # Server side repository. - tuf_repo = os.path.join(root_repo, 'tuf_repo') - keystore_dir = os.path.join(tuf_repo, 'keystore') - metadata_dir = os.path.join(tuf_repo, 'metadata') - targets_dir = os.path.join(tuf_repo, 'targets') - - # We need to provide clients with a way to reach the tuf repository. - tuf_repo_relpath = os.path.basename(tuf_repo) - tuf_url = url+tuf_repo_relpath - - # Add files to the server side repository. - # target1 = 'targets_dir/[random].txt' - # target2 = 'targets_dir/[random].txt' - add_target = util_test_tools.add_file_to_repository - target1_path = add_target(targets_dir, data='target1') - target2_path = add_target(targets_dir, data='target2') - - # Target paths relative to the 'targets_dir'. - # Ex: targetX = 'targets/delegator/delegatee.txt' - target1 = os.path.relpath(target1_path, tuf_repo) - target2 = os.path.relpath(target2_path, tuf_repo) - - # Relative to repository's targets directory. - target_filepaths = [target1, target2] - - # Store in self only the variables relevant for tests. - self.root_repo = root_repo - self.tuf_repo = tuf_repo - self.server_proc = server_proc - self.target_filepaths = target_filepaths - # Targets delegated from A to B. - self.delegated_targets = {} - # Targets actually signed by B. - self.signed_targets = {} - self.mirrors = { - "mirror1": { - "url_prefix": tuf_url, - "metadata_path": "metadata", - "targets_path": "targets", - "confined_target_dirs": [""] - } - } - # Aliases for targets roles. - self.T0 = 'targets' - self.T1 = 'targets/T1' - self.T2 = 'targets/T2' - self.T3 = 'targets/T1/T3' - - # Get tracked and assigned targets, and generate targets metadata. - self.make_targets_metadata() - assert hasattr(self, 'T0_metadata') - assert hasattr(self, 'T1_metadata') - assert hasattr(self, 'T2_metadata') - assert hasattr(self, 'T3_metadata') - - # Make delegation directories at the server's repository. - metadata_targets_dir = os.path.join(metadata_dir, 'targets') - metadata_T1_dir = os.path.join(metadata_targets_dir, 'T1') - os.makedirs(metadata_T1_dir) - - # Delegations metadata paths for the 3 delegated targets roles. - T0_path = os.path.join(metadata_dir, 'targets.txt') - T1_path = os.path.join(metadata_targets_dir, 'T1.txt') - T2_path = os.path.join(metadata_targets_dir, 'T2.txt') - T3_path = os.path.join(metadata_T1_dir, 'T3.txt') - - # Generate RSA keys for the 3 delegatees. - key1 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T1') - key2 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T2') - key3 = signerlib.generate_and_save_rsa_key(keystore_dir, 'T3') - - # ID for each of the 3 keys. - key1_id = key1['keyid'] - key2_id = key2['keyid'] - key3_id = key3['keyid'] - - # ID, in a list, for each of the 3 keys. - key1_ids = [key1_id] - key2_ids = [key2_id] - key3_ids = [key3_id] - - # Public-key JSON for each of the 3 keys. - key1_val = tuf.rsa_key.create_in_metadata_format(key1['keyval']) - key2_val = tuf.rsa_key.create_in_metadata_format(key2['keyval']) - key3_val = tuf.rsa_key.create_in_metadata_format(key3['keyval']) - - # Create delegation role metadata for each of the 3 delegated targets roles. - make_role_metadata = tuf.formats.make_role_metadata - - T1_targets = self.relpath_from_targets(self.delegated_targets[self.T1]) - T1_role = make_role_metadata(key1_ids, 1, name=self.T1, paths=T1_targets) - - T2_targets = self.relpath_from_targets(self.delegated_targets[self.T2]) - T2_role = make_role_metadata(key2_ids, 1, name=self.T2, paths=T2_targets) - - T3_targets = self.relpath_from_targets(self.delegated_targets[self.T3]) - T3_role = make_role_metadata(key3_ids, 1, name=self.T3, paths=T3_targets) - - # Assign 'delegations' object for 'targets': - self.T0_metadata['signed']['delegations'] = { - 'keys': {key1_id: key1_val, key2_id: key2_val}, - 'roles': [T1_role, T2_role] - } - - # Assign 'delegations' object for 'targets/T1': - self.T1_metadata['signed']['delegations'] = { - 'keys': {key3_id: key3_val}, - 'roles': [T3_role] - } - - sign = signerlib.sign_metadata - write = signerlib.write_metadata_file - - # Sign new metadata objects. - T0_signable = sign(self.T0_metadata, keyids, T0_path) - T1_signable = sign(self.T1_metadata, key1_ids, T1_path) - T2_signable = sign(self.T2_metadata, key2_ids, T2_path) - T3_signable = sign(self.T3_metadata, key3_ids, T3_path) - # Save new metadata objects. - write(T0_signable, T0_path) - write(T1_signable, T1_path) - write(T2_signable, T2_path) - write(T3_signable, T3_path) - - # Timestamp a new release to reflect latest targets. - signerlib.build_release_file(keyids, metadata_dir, version, expiration) - signerlib.build_timestamp_file(keyids, metadata_dir, version, expiration) - - # Unload all keys. - keystore.clear_keystore() - - - def tearDown(self): - util_test_tools.cleanup(self.root_repo, server_process=self.server_proc) - - - - - -class TestInitialUpdateWithTargetDelegations(TestDelegationFunctions): - """We show that making target delegations results in a successful initial - update of targets.""" - - - def make_targets_metadata(self): - global version - version = version+1 - expiration = tuf.formats.format_time(time.time()+86400) - make_metadata = signerlib.generate_targets_metadata - target1, target2 = self.target_filepaths - - # Targets signed for by each of the targets roles. - self.signed_targets[self.T0] = [target1] - self.signed_targets[self.T1] = [target1] - self.signed_targets[self.T2] = [target2] - self.signed_targets[self.T3] = [target1, target2] - - # Targets delegated to each of the delegated targets roles. - self.delegated_targets[self.T1] = [target1] - self.delegated_targets[self.T2] = [target2] - self.delegated_targets[self.T3] = [target1, target2] - - self.T0_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T0], - version, expiration) - self.T1_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T1], - version, expiration) - self.T2_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T2], - version, expiration) - self.T3_metadata = \ - make_metadata(self.tuf_repo, self.signed_targets[self.T3], - version, expiration) - - - def test_that_initial_update_works_with_target_delegations(self): - # Get relative target paths, because that is what TUF recognizes. - relative_target_filepaths = self.relpath_from_targets(self.target_filepaths) - # Get metadata about downloaded targets. - targets_metadata = self.do_update() - # Do we have metadata about all the expected targets? - for target_filepath in relative_target_filepaths: - self.assertIn(target_filepath, targets_metadata) - - - - - -class TestBreachOfTargetDelegation(TestDelegationFunctions): - """We show that a delegated targets role B cannot talk about targets that A - did not delegate to B.""" - - - def make_targets_metadata(self): - global version - version = version+1 - expiration = tuf.formats.format_time(time.time()+86400) - - make_metadata = signerlib.generate_targets_metadata - target1, target2 = self.target_filepaths - - # Targets signed for by each of the targets roles. - self.signed_targets[self.T0] = [] - self.signed_targets[self.T1] = [target2] - self.signed_targets[self.T2] = [target1] - self.signed_targets[self.T3] = [] - - # Targets delegated to each of the delegated targets roles. - self.delegated_targets[self.T1] = [target1] - self.delegated_targets[self.T2] = [target2] - self.delegated_targets[self.T3] = [] - - self.T0_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T0], - version, expiration) - self.T1_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T1], - version, expiration) - self.T2_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T2], - version, expiration) - self.T3_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T3], - version, expiration) - - - def test_that_initial_update_fails_with_undelegated_signing_of_targets(self): - """We expect to see ForbiddenTargetError on initial update because - delegated targets roles sign for targets that they were not delegated - to.""" - - # http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises - with self.assertRaises(tuf.NoWorkingMirrorError) as context_manager: - self.do_update() - - mirror_errors = context_manager.exception.mirror_errors - forbidden_target_error = False - - for mirror_url, mirror_error in mirror_errors.iteritems(): - if isinstance(mirror_error, tuf.ForbiddenTargetError): - forbidden_target_error = True - break - - self.assertEqual(forbidden_target_error, True) - - - - - -class TestOrderOfTargetDelegationWithSuccess(TestDelegationFunctions): - """We show that when multiple delegated targets roles talk about a target, - the first one in order of appearance of delegation wins. - - In this case, the first role has the correct metadata about the target.""" - - - def make_targets_metadata(self): - global version - version = version+1 - expiration = tuf.formats.format_time(time.time()+86400) - - make_metadata = signerlib.generate_targets_metadata - target1, target2 = self.target_filepaths - - # Targets signed for by each of the targets roles. - self.signed_targets[self.T0] = [target2] - self.signed_targets[self.T1] = [] - self.signed_targets[self.T2] = [target1] - self.signed_targets[self.T3] = [target1] - - # Targets delegated to each of the delegated targets roles. - self.delegated_targets[self.T1] = [target1] - self.delegated_targets[self.T2] = [target1] - self.delegated_targets[self.T3] = [target1] - - self.T0_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T0], - version, expiration) - self.T1_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T1], - version, expiration) - self.T2_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T2], - version, expiration) - self.T3_metadata = \ - make_metadata(self.tuf_repo, self.signed_targets[self.T3], - version, expiration) - - # Modify the hash for target1 in T2. - for target_filepath in self.relpath_from_targets([target1]): - target_metadata = self.T2_metadata['signed']['targets'][target_filepath] - sha256_hash = target_metadata['hashes']['sha256'] - last_character = sha256_hash[-1] - last_character = chr(ord(last_character)-1) - # "Subtract" the last character of the hash. - target_metadata['hashes']['sha256'] = sha256_hash[:-1] + last_character - - - def test_that_initial_update_works_with_many_roles_sharing_a_target(self): - # Get relative target paths, because that is what TUF recognizes. - relative_target_filepaths = self.relpath_from_targets(self.target_filepaths) - # Get metadata about downloaded targets. - targets_metadata = self.do_update() - # Do we have metadata about all the expected targets? - for target_filepath in relative_target_filepaths: - self.assertIn(target_filepath, targets_metadata) - - - - - -class TestOrderOfTargetDelegationWithFailure(TestDelegationFunctions): - """We show that when multiple delegated targets roles talk about a target, - the first one in order of appearance of delegation wins. - - In this case, the first role has the wrong metadata about the target.""" - - - def make_targets_metadata(self): - global version - version = version+1 - expiration = tuf.formats.format_time(time.time()+86400) - make_metadata = signerlib.generate_targets_metadata - target1, target2 = self.target_filepaths - - # Targets signed for by each of the targets roles. - self.signed_targets[self.T0] = [target2] - self.signed_targets[self.T1] = [] - self.signed_targets[self.T2] = [target1] - self.signed_targets[self.T3] = [target1] - - # Targets delegated to each of the delegated targets roles. - self.delegated_targets[self.T1] = [target1] - self.delegated_targets[self.T2] = [target1] - self.delegated_targets[self.T3] = [target1] - - self.T0_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T0], - version, expiration) - self.T1_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T1], - version, expiration) - self.T2_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T2], - version, expiration) - self.T3_metadata = \ - make_metadata(self.tuf_repo, self.signed_targets[self.T3], - version, expiration) - - # Modify the hash for target1 in T3. - for target_filepath in self.relpath_from_targets([target1]): - target_metadata = self.T3_metadata['signed']['targets'][target_filepath] - sha256_hash = target_metadata['hashes']['sha256'] - last_character = sha256_hash[-1] - last_character = chr(ord(last_character)-1) - # "Subtract" the last character of the hash. - target_metadata['hashes']['sha256'] = sha256_hash[:-1] + last_character - - - def test_that_initial_update_fails_with_many_roles_sharing_a_target(self): - """We expect to see BadHashError on initial update because the hash - metadata mismatches the target.""" - - # http://docs.python.org/2/library/unittest.html#unittest.TestCase.assertRaises - with self.assertRaises(tuf.NoWorkingMirrorError) as context_manager: - self.do_update() - - mirror_errors = context_manager.exception.mirror_errors - bad_hash_error = False - - for mirror_url, mirror_error in mirror_errors.iteritems(): - if isinstance(mirror_error, tuf.BadHashError): - bad_hash_error = True - break - - self.assertEqual(bad_hash_error, True) - - - - - -class TestConservationOfTargetDelegation(TestDelegationFunctions): - """We show that delegated targets roles have to neither sign for targets - delegated to them nor further delegate them.""" - - - def make_targets_metadata(self): - global version - expiration = tuf.formats.format_time(time.time()+86400) - - make_metadata = signerlib.generate_targets_metadata - target1, target2 = self.target_filepaths - - # Targets signed for by each of the targets roles. - self.signed_targets[self.T0] = [] - self.signed_targets[self.T1] = [target1] - self.signed_targets[self.T2] = [target2] - self.signed_targets[self.T3] = [] - - # Targets delegated to each of the delegated targets roles. - self.delegated_targets[self.T1] = [target1, target2] - self.delegated_targets[self.T2] = [target1, target2] - self.delegated_targets[self.T3] = [] - - self.T0_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T0], - version, expiration) - self.T1_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T1], - version, expiration) - self.T2_metadata =\ - make_metadata(self.tuf_repo, self.signed_targets[self.T2], - version, expiration) - self.T3_metadata = \ - make_metadata(self.tuf_repo, self.signed_targets[self.T3], - version, expiration) - - - def test_that_initial_update_works_with_unconserved_targets(self): - # Get relative target paths, because that is what TUF recognizes. - relative_target_filepaths = self.relpath_from_targets(self.target_filepaths) - # Get metadata about downloaded targets. - targets_metadata = self.do_update() - # Do we have metadata about all the expected targets? - for target_filepath in relative_target_filepaths: - self.assertIn(target_filepath, targets_metadata) - - - - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/repository_data/client/metadata/current/root.json b/tests/repository_data/client/metadata/current/root.json index 448c2d0af83146ae98debd826aaf089c7178551b..37993bf1f278ddb52705a2df93ea8feb3d186b57 100644 GIT binary patch literal 3778 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 diff --git a/tests/repository_data/client/metadata/current/snapshot.json b/tests/repository_data/client/metadata/current/snapshot.json index 2b3323dd90d7c44cb2a369f9cae10006ea6046a7..9a960dcdc477548232e206d0dd1112a191f55da0 100644 GIT binary patch literal 1393 zcma)*-EJf`5QXpa6r;XomvY%!42Wfwcc*5OMO+XmlDk`K zcbCsO_0^Y~-FCXWyQcOC zk>ypqR2^(lmX8Swv~Um(YpS*3tdTMxN()0UmxfVmWvx@zf)Ty2M@P>u7fEMI;Z`Qv zvZ%~cWRzxARjo63Y$&Ctc+va;d8Q+56_^4M^C4N0X7^@9g;|GD87?@TT)-!@b(pg@ zThPYrt49EeFR81{7QzaFy1W3aV#5TQ%;M?R!qrFXrI83%i_Em8zFu^wXr{ijO``Ny z1`I4kn85~lZc}4Z8ktU3SVep1w4Oc?Qw>!Ls^Hu!gRZ&8fZ9e$S$S%iYD0Q~bT@2* z)EcZ5)`%R{Q}4u(*+DZNK_JT(#8?A(!Cc&F;RqWgW9PBr+QG;uG*+wGA60756=6z+ zY(e@)_M}L;VhLL{J0fm{w*=+sX7}@jm@Ail;pM{P_RIP6gI^fg->-+$;dp*VXY;=v z?w--z5GuTblIsV657+hM%U8+Xda1PA9*@WK`(IDT`>$_y+hLs!SN^Z;=W(aQLh6_E znAS9sDU`4enrx&l23iMaV$oz(pUJ%Y)GAQYG^L*cPAy*v$kg*haRU5CuIh8aa9^n?1uSu$jVC5Bb;Rc*=r z|7iR6@%YKh+oOwhnJF|iZ^`{a^ctFBHfz1*WX`DBNYY#onz42+z15O|W{_7-|Lxp= adOEpY)#dHEt=pgec)Giimb#yAp8f&iFK$Qx literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 diff --git a/tests/repository_data/client/metadata/current/targets.json b/tests/repository_data/client/metadata/current/targets.json index 2c60420ffde3e41c912b5608935066625af10040..0a7b4a8bf43e2d63977e1804a83f4fae07449b15 100644 GIT binary patch delta 1121 zcmbu7&2nNl5QQs0lT8-0%L7cZyOpYL{kF0)2F3x8F~--%ro53_k}(D@3#wPKN)8)q!O8G6*6YoRvPaGh_%ADJxNeA#gq=fW!&OMB|mS3S4rb zG`WC|lZ-y+BrF*w=Y5hwri4k6_Fko2p0g1=E09QF*#r(AAOk3E4>AEp#X-l+-Udug zm0j|}2*?FZNfbs?vLPf32_kva7BKt^r2`si(GeOIg3-39Dl9$$iKvTQOr)K*Nn3-; zWT2>(Q$8mxLAc=3!2~Q9Tp|HjOpxBA^v*JrF{GRpRE`rUW{4&m7ARPNlOpAmy^oH) z0W534>~nFE3S6*0W1&&DTqxpF!E@eP5K3id0_kEpFl7tNNd=UlsMJWh zD4^&;lss5SRobDTa}Z#BP=!!LQ(OtbCL&YR1aFF4Imco>DQegyiL9r1zMj1$w z<}zA1St9wGT-I;DeOVu|VT&3)7*`^eWF+Jgy~C_asHS`foe~!T6sQ>Lz_CmQqYgr& zl=SxfSHB!KovNDB>CNf%bAR=E`@Eaf-MghdS=XJ@Gr4VzKhgSxFP6KhZ4bhFq{fTM z=k~=6Uq8C(xjt`QSM!tWPStNr*WC>k{li& zZ;&Q0808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jCi?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+g!{G zHfGSY8}a9Fo#pyvezQDVTn@W-Z*Owq?oagnaBf%n06+M`LSsa^Ud<@Y*sm^lbvRDk5|W?#p>zlEf_A3gZb^`^m*8Q+^inL zw$=Bi-D$q-UB0B%#iF{YDD7We3|@MBi}u0e&9WV#{czGA439?8S111EY|^`Z zle7KB;6cuI`^{{&^EiI(KA*0e#r3B2*4NYZ(c?KCp7dMOp1)c4-amch|9`B%KYsg3 Qhggq?N~wSPKaRfp7r5F>y8r+H diff --git a/tests/repository_data/client/metadata/current/targets.json.gz b/tests/repository_data/client/metadata/current/targets.json.gz index 8aed9cb52b1814978351ad158605afda3b2234ee..757cff5064add4a5eb85863404672b95fe28df7c 100644 GIT binary patch literal 1211 zcmV;s1VsBEiwFR;o>L&dPnn z%d6z-DtR}$y88YMG3*`~9v%10811d*E(WWNGE^9$IpstdVjTb>5P%w$DkrQK#Dewc zWIeS3cS*Uijjy35S%h44QTH{6CkN^RBA%~j=Jt_0WmCN z)CQ*r#cxlei`-(+ukBpqyq3Im}!= zFN^BvxmWJnk47`7psA#eD2r<0MsX5RMV3a1`cSAX2@sHu@dk@yi47bSO{Hg&a&F!t z?E`;D@l?5^b`SQA%YQgFnya-)WrnZuiFf;=JE)_ZP{q zJ$rb}9_FWZOWMh}_c2K>I?321yXT$R8Q%Ajs@rO1=IrxX_B2?1y?x%?O?vl*K7E{Y z&(8TenSL^lr(s-dGTj;a$4fC?-aU233;Ok=%`W6ca?{FBZ@R6)^Y!Ox>*Zp!ZCx&g z_r+WmeL^T*bvbET*2vMM`eS$Al}Yam zl71a#$$haBqrsN-m&5H~zJ^+ECnH)_laP-_)3z$(!_s{HTn?^Zwv+y}m4$IOG-fg0 zh~Dg(=J^66Oz(Uzedw=-&x@sMC%wlwit}|+tMkN!WqP^l=B+_NI7#S3Uk?XmRjoIZ z5P#clF5&LtzL;+N-DI4!?~8qLdc*GD>(x9I_x0rM^Yu8rp33}fcs71k%RK#I{y(I6 z2=wJmc!gW;)mR^T;{6T?{h`a1tFylVcpv9E@L%ia`X8a{3>QFF=DUWYKPY}V`A)M5 z&#U=iNi-QI@*SagO$hn0zwV9=d4%%TjQb<${>ZnM%v_a0^;2V2nPXDzA}i@lP&iF# zu$HIB8YXe}66Yi?6{>Nb2uq0PjtFCM6X8teINdec1xek3*Re2-W)8)y&M+{iKNoBd ziv4F$Lb3pc6C(-Ed=60vCQNET5!{5*GD@w*nP#{KoZlRGgH#lvr%nWC?Z1MuC&xS= ZQ@nwv3P;(Y1g|Hre*$kK5ka2^000K=T{i## literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJH{JlTJ%JFi6Q{ePw5U5E5bC?**>=%n4sqho&gb9X ztKD)s-aY90^T|)k&F=HnZud9--SW-<97jZ!-a27Lg(gKPbIUMAU2s-WZG}s9?@)=2 z$*X|$>-t5kT-+etio)tgt-|zSDuRrYfm$T?$E5(By8;GL!thrZ_rU|gJ z&5 zi{_Nc`xtY$bIIAYz%)o9YR*Ok%~d3Y12byKLc5(+FS}$tu@kGXJoWA zCOHsk9VA#8^3Fc!IIDR#J6kLCEodS#krdER#$CH1<%S zQ7@6KBudE|iX~GHYX}n2MAo4(9!rk37d7j{pmL0+V6Kj!@lFHmkkL~%LaIHcUK}xO zV#~^geT0(@K(ch7$rZuSv}MCcj<1&9O;ZoWoP@)JX{aT%N-<%Ukz!gCCA{e1L6BRK z){)%Zpd|1mKzY8}ec6aPbLkr|8;{G^_0JFA7`fG_J6}&P8 z=U4Ea9`9e+{<6uRgNy(EaeN_uNn!0ZV60n!oAdhgs=mI~x+uS!$9vCj*Wc@<*ls!K z>2Q`m7mrR)2TiqYtzyARflDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ diff --git a/tests/repository_data/client/metadata/current/timestamp.json b/tests/repository_data/client/metadata/current/timestamp.json index dd6e2ab8a58a32f3fff305ca79a3eb3dea2524b5..55099610b35a8cadb01e245225072042e06e45c4 100644 GIT binary patch literal 931 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 diff --git a/tests/repository_data/client/metadata/previous/snapshot.json b/tests/repository_data/client/metadata/previous/snapshot.json index 2b3323dd90d7c44cb2a369f9cae10006ea6046a7..9a960dcdc477548232e206d0dd1112a191f55da0 100644 GIT binary patch literal 1393 zcma)*-EJf`5QXpa6r;XomvY%!42Wfwcc*5OMO+XmlDk`K zcbCsO_0^Y~-FCXWyQcOC zk>ypqR2^(lmX8Swv~Um(YpS*3tdTMxN()0UmxfVmWvx@zf)Ty2M@P>u7fEMI;Z`Qv zvZ%~cWRzxARjo63Y$&Ctc+va;d8Q+56_^4M^C4N0X7^@9g;|GD87?@TT)-!@b(pg@ zThPYrt49EeFR81{7QzaFy1W3aV#5TQ%;M?R!qrFXrI83%i_Em8zFu^wXr{ijO``Ny z1`I4kn85~lZc}4Z8ktU3SVep1w4Oc?Qw>!Ls^Hu!gRZ&8fZ9e$S$S%iYD0Q~bT@2* z)EcZ5)`%R{Q}4u(*+DZNK_JT(#8?A(!Cc&F;RqWgW9PBr+QG;uG*+wGA60756=6z+ zY(e@)_M}L;VhLL{J0fm{w*=+sX7}@jm@Ail;pM{P_RIP6gI^fg->-+$;dp*VXY;=v z?w--z5GuTblIsV657+hM%U8+Xda1PA9*@WK`(IDT`>$_y+hLs!SN^Z;=W(aQLh6_E znAS9sDU`4enrx&l23iMaV$oz(pUJ%Y)GAQYG^L*cPAy*v$kg*haRU5CuIh8aa9^n?1uSu$jVC5Bb;Rc*=r z|7iR6@%YKh+oOwhnJF|iZ^`{a^ctFBHfz1*WX`DBNYY#onz42+z15O|W{_7-|Lxp= adOEpY)#dHEt=pgec)Giimb#yAp8f&iFK$Qx literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 diff --git a/tests/repository_data/client/metadata/previous/targets.json b/tests/repository_data/client/metadata/previous/targets.json index 2c60420ffde3e41c912b5608935066625af10040..0a7b4a8bf43e2d63977e1804a83f4fae07449b15 100644 GIT binary patch delta 1121 zcmbu7&2nNl5QQs0lT8-0%L7cZyOpYL{kF0)2F3x8F~--%ro53_k}(D@3#wPKN)8)q!O8G6*6YoRvPaGh_%ADJxNeA#gq=fW!&OMB|mS3S4rb zG`WC|lZ-y+BrF*w=Y5hwri4k6_Fko2p0g1=E09QF*#r(AAOk3E4>AEp#X-l+-Udug zm0j|}2*?FZNfbs?vLPf32_kva7BKt^r2`si(GeOIg3-39Dl9$$iKvTQOr)K*Nn3-; zWT2>(Q$8mxLAc=3!2~Q9Tp|HjOpxBA^v*JrF{GRpRE`rUW{4&m7ARPNlOpAmy^oH) z0W534>~nFE3S6*0W1&&DTqxpF!E@eP5K3id0_kEpFl7tNNd=UlsMJWh zD4^&;lss5SRobDTa}Z#BP=!!LQ(OtbCL&YR1aFF4Imco>DQegyiL9r1zMj1$w z<}zA1St9wGT-I;DeOVu|VT&3)7*`^eWF+Jgy~C_asHS`foe~!T6sQ>Lz_CmQqYgr& zl=SxfSHB!KovNDB>CNf%bAR=E`@Eaf-MghdS=XJ@Gr4VzKhgSxFP6KhZ4bhFq{fTM z=k~=6Uq8C(xjt`QSM!tWPStNr*WC>k{li& zZ;&Q0808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jCi?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+g!{G zHfGSY8}a9Fo#pyvezQDVTn@W-Z*Owq?oagnaBf%n06+M`LSsa^Ud<@Y*sm^lbvRDk5|W?#p>zlEf_A3gZb^`^m*8Q+^inL zw$=Bi-D$q-UB0B%#iF{YDD7We3|@MBi}u0e&9WV#{czGA439?8S111EY|^`Z zle7KB;6cuI`^{{&^EiI(KA*0e#r3B2*4NYZ(c?KCp7dMOp1)c4-amch|9`B%KYsg3 Qhggq?N~wSPKaRfp7r5F>y8r+H diff --git a/tests/repository_data/client/metadata/previous/targets.json.gz b/tests/repository_data/client/metadata/previous/targets.json.gz index 8aed9cb52b1814978351ad158605afda3b2234ee..757cff5064add4a5eb85863404672b95fe28df7c 100644 GIT binary patch literal 1211 zcmV;s1VsBEiwFR;o>L&dPnn z%d6z-DtR}$y88YMG3*`~9v%10811d*E(WWNGE^9$IpstdVjTb>5P%w$DkrQK#Dewc zWIeS3cS*Uijjy35S%h44QTH{6CkN^RBA%~j=Jt_0WmCN z)CQ*r#cxlei`-(+ukBpqyq3Im}!= zFN^BvxmWJnk47`7psA#eD2r<0MsX5RMV3a1`cSAX2@sHu@dk@yi47bSO{Hg&a&F!t z?E`;D@l?5^b`SQA%YQgFnya-)WrnZuiFf;=JE)_ZP{q zJ$rb}9_FWZOWMh}_c2K>I?321yXT$R8Q%Ajs@rO1=IrxX_B2?1y?x%?O?vl*K7E{Y z&(8TenSL^lr(s-dGTj;a$4fC?-aU233;Ok=%`W6ca?{FBZ@R6)^Y!Ox>*Zp!ZCx&g z_r+WmeL^T*bvbET*2vMM`eS$Al}Yam zl71a#$$haBqrsN-m&5H~zJ^+ECnH)_laP-_)3z$(!_s{HTn?^Zwv+y}m4$IOG-fg0 zh~Dg(=J^66Oz(Uzedw=-&x@sMC%wlwit}|+tMkN!WqP^l=B+_NI7#S3Uk?XmRjoIZ z5P#clF5&LtzL;+N-DI4!?~8qLdc*GD>(x9I_x0rM^Yu8rp33}fcs71k%RK#I{y(I6 z2=wJmc!gW;)mR^T;{6T?{h`a1tFylVcpv9E@L%ia`X8a{3>QFF=DUWYKPY}V`A)M5 z&#U=iNi-QI@*SagO$hn0zwV9=d4%%TjQb<${>ZnM%v_a0^;2V2nPXDzA}i@lP&iF# zu$HIB8YXe}66Yi?6{>Nb2uq0PjtFCM6X8teINdec1xek3*Re2-W)8)y&M+{iKNoBd ziv4F$Lb3pc6C(-Ed=60vCQNET5!{5*GD@w*nP#{KoZlRGgH#lvr%nWC?Z1MuC&xS= ZQ@nwv3P;(Y1g|Hre*$kK5ka2^000K=T{i## literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJH{JlTJ%JFi6Q{ePw5U5E5bC?**>=%n4sqho&gb9X ztKD)s-aY90^T|)k&F=HnZud9--SW-<97jZ!-a27Lg(gKPbIUMAU2s-WZG}s9?@)=2 z$*X|$>-t5kT-+etio)tgt-|zSDuRrYfm$T?$E5(By8;GL!thrZ_rU|gJ z&5 zi{_Nc`xtY$bIIAYz%)o9YR*Ok%~d3Y12byKLc5(+FS}$tu@kGXJoWA zCOHsk9VA#8^3Fc!IIDR#J6kLCEodS#krdER#$CH1<%S zQ7@6KBudE|iX~GHYX}n2MAo4(9!rk37d7j{pmL0+V6Kj!@lFHmkkL~%LaIHcUK}xO zV#~^geT0(@K(ch7$rZuSv}MCcj<1&9O;ZoWoP@)JX{aT%N-<%Ukz!gCCA{e1L6BRK z){)%Zpd|1mKzY8}ec6aPbLkr|8;{G^_0JFA7`fG_J6}&P8 z=U4Ea9`9e+{<6uRgNy(EaeN_uNn!0ZV60n!oAdhgs=mI~x+uS!$9vCj*Wc@<*ls!K z>2Q`m7mrR)2TiqYtzyARflDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ diff --git a/tests/repository_data/client/metadata/previous/timestamp.json b/tests/repository_data/client/metadata/previous/timestamp.json index dd6e2ab8a58a32f3fff305ca79a3eb3dea2524b5..55099610b35a8cadb01e245225072042e06e45c4 100644 GIT binary patch literal 931 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 diff --git a/tests/repository_data/repository/metadata.staged/snapshot.json b/tests/repository_data/repository/metadata.staged/snapshot.json index 2b3323dd90d7c44cb2a369f9cae10006ea6046a7..9a960dcdc477548232e206d0dd1112a191f55da0 100644 GIT binary patch literal 1393 zcma)*-EJf`5QXpa6r;XomvY%!42Wfwcc*5OMO+XmlDk`K zcbCsO_0^Y~-FCXWyQcOC zk>ypqR2^(lmX8Swv~Um(YpS*3tdTMxN()0UmxfVmWvx@zf)Ty2M@P>u7fEMI;Z`Qv zvZ%~cWRzxARjo63Y$&Ctc+va;d8Q+56_^4M^C4N0X7^@9g;|GD87?@TT)-!@b(pg@ zThPYrt49EeFR81{7QzaFy1W3aV#5TQ%;M?R!qrFXrI83%i_Em8zFu^wXr{ijO``Ny z1`I4kn85~lZc}4Z8ktU3SVep1w4Oc?Qw>!Ls^Hu!gRZ&8fZ9e$S$S%iYD0Q~bT@2* z)EcZ5)`%R{Q}4u(*+DZNK_JT(#8?A(!Cc&F;RqWgW9PBr+QG;uG*+wGA60756=6z+ zY(e@)_M}L;VhLL{J0fm{w*=+sX7}@jm@Ail;pM{P_RIP6gI^fg->-+$;dp*VXY;=v z?w--z5GuTblIsV657+hM%U8+Xda1PA9*@WK`(IDT`>$_y+hLs!SN^Z;=W(aQLh6_E znAS9sDU`4enrx&l23iMaV$oz(pUJ%Y)GAQYG^L*cPAy*v$kg*haRU5CuIh8aa9^n?1uSu$jVC5Bb;Rc*=r z|7iR6@%YKh+oOwhnJF|iZ^`{a^ctFBHfz1*WX`DBNYY#onz42+z15O|W{_7-|Lxp= adOEpY)#dHEt=pgec)Giimb#yAp8f&iFK$Qx literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 diff --git a/tests/repository_data/repository/metadata.staged/targets.json b/tests/repository_data/repository/metadata.staged/targets.json index 2c60420ffde3e41c912b5608935066625af10040..0a7b4a8bf43e2d63977e1804a83f4fae07449b15 100644 GIT binary patch delta 1121 zcmbu7&2nNl5QQs0lT8-0%L7cZyOpYL{kF0)2F3x8F~--%ro53_k}(D@3#wPKN)8)q!O8G6*6YoRvPaGh_%ADJxNeA#gq=fW!&OMB|mS3S4rb zG`WC|lZ-y+BrF*w=Y5hwri4k6_Fko2p0g1=E09QF*#r(AAOk3E4>AEp#X-l+-Udug zm0j|}2*?FZNfbs?vLPf32_kva7BKt^r2`si(GeOIg3-39Dl9$$iKvTQOr)K*Nn3-; zWT2>(Q$8mxLAc=3!2~Q9Tp|HjOpxBA^v*JrF{GRpRE`rUW{4&m7ARPNlOpAmy^oH) z0W534>~nFE3S6*0W1&&DTqxpF!E@eP5K3id0_kEpFl7tNNd=UlsMJWh zD4^&;lss5SRobDTa}Z#BP=!!LQ(OtbCL&YR1aFF4Imco>DQegyiL9r1zMj1$w z<}zA1St9wGT-I;DeOVu|VT&3)7*`^eWF+Jgy~C_asHS`foe~!T6sQ>Lz_CmQqYgr& zl=SxfSHB!KovNDB>CNf%bAR=E`@Eaf-MghdS=XJ@Gr4VzKhgSxFP6KhZ4bhFq{fTM z=k~=6Uq8C(xjt`QSM!tWPStNr*WC>k{li& zZ;&Q0808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jCi?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+g!{G zHfGSY8}a9Fo#pyvezQDVTn@W-Z*Owq?oagnaBf%n06+M`LSsa^Ud<@Y*sm^lbvRDk5|W?#p>zlEf_A3gZb^`^m*8Q+^inL zw$=Bi-D$q-UB0B%#iF{YDD7We3|@MBi}u0e&9WV#{czGA439?8S111EY|^`Z zle7KB;6cuI`^{{&^EiI(KA*0e#r3B2*4NYZ(c?KCp7dMOp1)c4-amch|9`B%KYsg3 Qhggq?N~wSPKaRfp7r5F>y8r+H diff --git a/tests/repository_data/repository/metadata.staged/targets.json.gz b/tests/repository_data/repository/metadata.staged/targets.json.gz index 8aed9cb52b1814978351ad158605afda3b2234ee..757cff5064add4a5eb85863404672b95fe28df7c 100644 GIT binary patch literal 1211 zcmV;s1VsBEiwFR;o>L&dPnn z%d6z-DtR}$y88YMG3*`~9v%10811d*E(WWNGE^9$IpstdVjTb>5P%w$DkrQK#Dewc zWIeS3cS*Uijjy35S%h44QTH{6CkN^RBA%~j=Jt_0WmCN z)CQ*r#cxlei`-(+ukBpqyq3Im}!= zFN^BvxmWJnk47`7psA#eD2r<0MsX5RMV3a1`cSAX2@sHu@dk@yi47bSO{Hg&a&F!t z?E`;D@l?5^b`SQA%YQgFnya-)WrnZuiFf;=JE)_ZP{q zJ$rb}9_FWZOWMh}_c2K>I?321yXT$R8Q%Ajs@rO1=IrxX_B2?1y?x%?O?vl*K7E{Y z&(8TenSL^lr(s-dGTj;a$4fC?-aU233;Ok=%`W6ca?{FBZ@R6)^Y!Ox>*Zp!ZCx&g z_r+WmeL^T*bvbET*2vMM`eS$Al}Yam zl71a#$$haBqrsN-m&5H~zJ^+ECnH)_laP-_)3z$(!_s{HTn?^Zwv+y}m4$IOG-fg0 zh~Dg(=J^66Oz(Uzedw=-&x@sMC%wlwit}|+tMkN!WqP^l=B+_NI7#S3Uk?XmRjoIZ z5P#clF5&LtzL;+N-DI4!?~8qLdc*GD>(x9I_x0rM^Yu8rp33}fcs71k%RK#I{y(I6 z2=wJmc!gW;)mR^T;{6T?{h`a1tFylVcpv9E@L%ia`X8a{3>QFF=DUWYKPY}V`A)M5 z&#U=iNi-QI@*SagO$hn0zwV9=d4%%TjQb<${>ZnM%v_a0^;2V2nPXDzA}i@lP&iF# zu$HIB8YXe}66Yi?6{>Nb2uq0PjtFCM6X8teINdec1xek3*Re2-W)8)y&M+{iKNoBd ziv4F$Lb3pc6C(-Ed=60vCQNET5!{5*GD@w*nP#{KoZlRGgH#lvr%nWC?Z1MuC&xS= ZQ@nwv3P;(Y1g|Hre*$kK5ka2^000K=T{i## literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJH{JlTJ%JFi6Q{ePw5U5E5bC?**>=%n4sqho&gb9X ztKD)s-aY90^T|)k&F=HnZud9--SW-<97jZ!-a27Lg(gKPbIUMAU2s-WZG}s9?@)=2 z$*X|$>-t5kT-+etio)tgt-|zSDuRrYfm$T?$E5(By8;GL!thrZ_rU|gJ z&5 zi{_Nc`xtY$bIIAYz%)o9YR*Ok%~d3Y12byKLc5(+FS}$tu@kGXJoWA zCOHsk9VA#8^3Fc!IIDR#J6kLCEodS#krdER#$CH1<%S zQ7@6KBudE|iX~GHYX}n2MAo4(9!rk37d7j{pmL0+V6Kj!@lFHmkkL~%LaIHcUK}xO zV#~^geT0(@K(ch7$rZuSv}MCcj<1&9O;ZoWoP@)JX{aT%N-<%Ukz!gCCA{e1L6BRK z){)%Zpd|1mKzY8}ec6aPbLkr|8;{G^_0JFA7`fG_J6}&P8 z=U4Ea9`9e+{<6uRgNy(EaeN_uNn!0ZV60n!oAdhgs=mI~x+uS!$9vCj*Wc@<*ls!K z>2Q`m7mrR)2TiqYtzyARflDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ diff --git a/tests/repository_data/repository/metadata.staged/timestamp.json b/tests/repository_data/repository/metadata.staged/timestamp.json index dd6e2ab8a58a32f3fff305ca79a3eb3dea2524b5..55099610b35a8cadb01e245225072042e06e45c4 100644 GIT binary patch literal 931 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 diff --git a/tests/repository_data/repository/metadata/snapshot.json b/tests/repository_data/repository/metadata/snapshot.json index 2b3323dd90d7c44cb2a369f9cae10006ea6046a7..9a960dcdc477548232e206d0dd1112a191f55da0 100644 GIT binary patch literal 1393 zcma)*-EJf`5QXpa6r;XomvY%!42Wfwcc*5OMO+XmlDk`K zcbCsO_0^Y~-FCXWyQcOC zk>ypqR2^(lmX8Swv~Um(YpS*3tdTMxN()0UmxfVmWvx@zf)Ty2M@P>u7fEMI;Z`Qv zvZ%~cWRzxARjo63Y$&Ctc+va;d8Q+56_^4M^C4N0X7^@9g;|GD87?@TT)-!@b(pg@ zThPYrt49EeFR81{7QzaFy1W3aV#5TQ%;M?R!qrFXrI83%i_Em8zFu^wXr{ijO``Ny z1`I4kn85~lZc}4Z8ktU3SVep1w4Oc?Qw>!Ls^Hu!gRZ&8fZ9e$S$S%iYD0Q~bT@2* z)EcZ5)`%R{Q}4u(*+DZNK_JT(#8?A(!Cc&F;RqWgW9PBr+QG;uG*+wGA60756=6z+ zY(e@)_M}L;VhLL{J0fm{w*=+sX7}@jm@Ail;pM{P_RIP6gI^fg->-+$;dp*VXY;=v z?w--z5GuTblIsV657+hM%U8+Xda1PA9*@WK`(IDT`>$_y+hLs!SN^Z;=W(aQLh6_E znAS9sDU`4enrx&l23iMaV$oz(pUJ%Y)GAQYG^L*cPAy*v$kg*haRU5CuIh8aa9^n?1uSu$jVC5Bb;Rc*=r z|7iR6@%YKh+oOwhnJF|iZ^`{a^ctFBHfz1*WX`DBNYY#onz42+z15O|W{_7-|Lxp= adOEpY)#dHEt=pgec)Giimb#yAp8f&iFK$Qx literal 1381 zcma)+O>f&b494&I6vqGDb<_(TeVDajJ zQAf1W48&%rbnmSpSn1NJB5G~OJY|+%Myosx(W;N(Vz$g7BlB4AuGvnQ1$1>*b5(aATAb`A zRUo@;Gl#TvS`3=kp{FR)rb^aj_L@0VQ_X}?NUMMf4;>SEY1okHB?_l=Ij`9E@KGn8+Xvgi@F~rY|(gtXGt-ePkyu;P19C{&;^mWa7p|*RAh6+eJ_J zemTEg7*KkS4a!Q(tXN*dnSDdhSgp9G=bST6LzY=%ujzFsc9!Xu4!$O=NR!uuy}$qW zO+!nV5M5YfVY2OuBuX+Vl&B_edEZDHOUOiDOto@8Bc3-g`O3IgwN=3X(f0G{_{GcH zM`sheRA+9QSkf#IL0KA=z;L$2m{T30#f3D$eS^xk5 diff --git a/tests/repository_data/repository/metadata/targets.json b/tests/repository_data/repository/metadata/targets.json index 2c60420ffde3e41c912b5608935066625af10040..0a7b4a8bf43e2d63977e1804a83f4fae07449b15 100644 GIT binary patch delta 1121 zcmbu7&2nNl5QQs0lT8-0%L7cZyOpYL{kF0)2F3x8F~--%ro53_k}(D@3#wPKN)8)q!O8G6*6YoRvPaGh_%ADJxNeA#gq=fW!&OMB|mS3S4rb zG`WC|lZ-y+BrF*w=Y5hwri4k6_Fko2p0g1=E09QF*#r(AAOk3E4>AEp#X-l+-Udug zm0j|}2*?FZNfbs?vLPf32_kva7BKt^r2`si(GeOIg3-39Dl9$$iKvTQOr)K*Nn3-; zWT2>(Q$8mxLAc=3!2~Q9Tp|HjOpxBA^v*JrF{GRpRE`rUW{4&m7ARPNlOpAmy^oH) z0W534>~nFE3S6*0W1&&DTqxpF!E@eP5K3id0_kEpFl7tNNd=UlsMJWh zD4^&;lss5SRobDTa}Z#BP=!!LQ(OtbCL&YR1aFF4Imco>DQegyiL9r1zMj1$w z<}zA1St9wGT-I;DeOVu|VT&3)7*`^eWF+Jgy~C_asHS`foe~!T6sQ>Lz_CmQqYgr& zl=SxfSHB!KovNDB>CNf%bAR=E`@Eaf-MghdS=XJ@Gr4VzKhgSxFP6KhZ4bhFq{fTM z=k~=6Uq8C(xjt`QSM!tWPStNr*WC>k{li& zZ;&Q0808t#O)+!les|`t&+niA_~lD+kPQov^{kTdwpi<;7>CX))R?k}N7eyUQYM8U znzBXatVw}af99QSOhnN}qZP6$$)p^GAQRhUBNs)A*+h&bgK*$dL_1G9szk2X2myU? zAyWyE>NI<@sFG&wC8HGtPRddyf>2US)u1py0gcgy5VpkP0+q<1Dv8yIXl+p*W5S5w zH8>^KSs~6~Em?x(O;piSHITs9)!8NSb;p02IZ#Cf1Ih)Ry&w`PABqHEAF||VQUY<9 z8G<%jM$D0NDAk=aN}EtPK`ap^aVE(%%z`!+SIZQ9AX`GRUZPmd$ub533@W3_rXmO{ z1%k!9qBvU@B-m($>j1gh&9SH|4oMr0T5l`5080dl#Sla<;EO9YyQnppK$MKC5XO{3 zwM}aU$&o=RKGo!-XQXTbpf}7IwU6kkyy4`E;}Qxg@|6UfP(?$oS=FydCTDc{gN|t4 zfB*jCi?^W|W2#+2XKQj5I5|xu^{u$B%`C+_h^_)@L%AN-C9A-?8Zh|xpMUsm+g!{G zHfGSY8}a9Fo#pyvezQDVTn@W-Z*Owq?oagnaBf%n06+M`LSsa^Ud<@Y*sm^lbvRDk5|W?#p>zlEf_A3gZb^`^m*8Q+^inL zw$=Bi-D$q-UB0B%#iF{YDD7We3|@MBi}u0e&9WV#{czGA439?8S111EY|^`Z zle7KB;6cuI`^{{&^EiI(KA*0e#r3B2*4NYZ(c?KCp7dMOp1)c4-amch|9`B%KYsg3 Qhggq?N~wSPKaRfp7r5F>y8r+H diff --git a/tests/repository_data/repository/metadata/targets.json.gz b/tests/repository_data/repository/metadata/targets.json.gz index 8aed9cb52b1814978351ad158605afda3b2234ee..757cff5064add4a5eb85863404672b95fe28df7c 100644 GIT binary patch literal 1211 zcmV;s1VsBEiwFR;o>L&dPnn z%d6z-DtR}$y88YMG3*`~9v%10811d*E(WWNGE^9$IpstdVjTb>5P%w$DkrQK#Dewc zWIeS3cS*Uijjy35S%h44QTH{6CkN^RBA%~j=Jt_0WmCN z)CQ*r#cxlei`-(+ukBpqyq3Im}!= zFN^BvxmWJnk47`7psA#eD2r<0MsX5RMV3a1`cSAX2@sHu@dk@yi47bSO{Hg&a&F!t z?E`;D@l?5^b`SQA%YQgFnya-)WrnZuiFf;=JE)_ZP{q zJ$rb}9_FWZOWMh}_c2K>I?321yXT$R8Q%Ajs@rO1=IrxX_B2?1y?x%?O?vl*K7E{Y z&(8TenSL^lr(s-dGTj;a$4fC?-aU233;Ok=%`W6ca?{FBZ@R6)^Y!Ox>*Zp!ZCx&g z_r+WmeL^T*bvbET*2vMM`eS$Al}Yam zl71a#$$haBqrsN-m&5H~zJ^+ECnH)_laP-_)3z$(!_s{HTn?^Zwv+y}m4$IOG-fg0 zh~Dg(=J^66Oz(Uzedw=-&x@sMC%wlwit}|+tMkN!WqP^l=B+_NI7#S3Uk?XmRjoIZ z5P#clF5&LtzL;+N-DI4!?~8qLdc*GD>(x9I_x0rM^Yu8rp33}fcs71k%RK#I{y(I6 z2=wJmc!gW;)mR^T;{6T?{h`a1tFylVcpv9E@L%ia`X8a{3>QFF=DUWYKPY}V`A)M5 z&#U=iNi-QI@*SagO$hn0zwV9=d4%%TjQb<${>ZnM%v_a0^;2V2nPXDzA}i@lP&iF# zu$HIB8YXe}66Yi?6{>Nb2uq0PjtFCM6X8teINdec1xek3*Re2-W)8)y&M+{iKNoBd ziv4F$Lb3pc6C(-Ed=60vCQNET5!{5*GD@w*nP#{KoZlRGgH#lvr%nWC?Z1MuC&xS= ZQ@nwv3P;(Y1g|Hre*$kK5ka2^000K=T{i## literal 1205 zcmV;m1WNlKiwFQuIZaan|E*QqQesySefKLUUd@DkzvS(3(LoeJ1a!hwYF~PDI|9Pd z0jK=;G~nnYsZ`}5R6!kf_gUSmd+qM;C#CXcI$pTqX$_n5Z>8T)N~Q1kEtTi6n?^h- zC&?oVz`f9cIHpAsLRw%_Nlg@0E&^y3sNuvB=8OUAqfjA4>Iv7R{7dNo_yEOZc|g4B zWxZbZsnhHIuwf2+gLU3pVHqVVkmQ()7CetW2q$bv#FG%1RW?Nq!Gx56O6{p7&dVe$ zH%c36qECi9d^>0_6mc|A4N(bDJUAkJ^q!mKtV13UI8U4^D~vHdfHXeYzzOmdR4FAy zB3fCetm2w-NkB*eK142r7oI!KEV0^Se8)9Lj?#g~5PQgIJQjpAPNbnxgHqlTZ4wwB zw22TcbB=>lF>pg!P%2sC4@PEzjyg&dGX43LE6R$_H1^AR;7ogiFDY!=t=NC)5_ zS*$niO}oeQXP19CmddLqpHIWy@zefOZJamTrB1(k*{qdXjr+slXmQnSR-3b|T^-LK zC-dpK{YI);w{ezb*R`x`vc0`FZsBty+uhER+OhVoz5{*p(papXXQM?ozw0zbwK}+x z^D8><%vfc+rgwU_xE1CMm~Cu&_uCqbmwDgJmcw3UIBz`+8-sjy!RnnjTD)|}*tg-} zbhn(V`-?`t+-zP93+b^vn+&dtPV;fSd@z+uecc?!Y3uIUE!Uz#D(%zlmm=$p7QLDa zEqgJ&5#RcghHhNA(7*4T_4>QT+U?A8Fb${uW^>Rvzq_26Y$(r%y?N2Di{)my-0dzA zp`~vs)6P?yiR+hft3A_$G@Xoj#(RiS&nopnzIwV{U!U3LnH=6<+-*O*+Wl?&xmC^U zXOE*r-SYawRlVK$+~b$zNsdFejuwaJG}`sQ_p5wt?(50>@9W$2S}*hW;aU1wEz|gM{C`OC z2=w(`cnkM%P-Aht6P0};^h1{`S4{qrKqXCc;J+5z;vZ1O1Q)<$neQ(g{h;{u}8YwIxIBbb9RthCJH{JlTJ%JFi6Q{ePw5U5E5bC?**>=%n4sqho&gb9X ztKD)s-aY90^T|)k&F=HnZud9--SW-<97jZ!-a27Lg(gKPbIUMAU2s-WZG}s9?@)=2 z$*X|$>-t5kT-+etio)tgt-|zSDuRrYfm$T?$E5(By8;GL!thrZ_rU|gJ z&5 zi{_Nc`xtY$bIIAYz%)o9YR*Ok%~d3Y12byKLc5(+FS}$tu@kGXJoWA zCOHsk9VA#8^3Fc!IIDR#J6kLCEodS#krdER#$CH1<%S zQ7@6KBudE|iX~GHYX}n2MAo4(9!rk37d7j{pmL0+V6Kj!@lFHmkkL~%LaIHcUK}xO zV#~^geT0(@K(ch7$rZuSv}MCcj<1&9O;ZoWoP@)JX{aT%N-<%Ukz!gCCA{e1L6BRK z){)%Zpd|1mKzY8}ec6aPbLkr|8;{G^_0JFA7`fG_J6}&P8 z=U4Ea9`9e+{<6uRgNy(EaeN_uNn!0ZV60n!oAdhgs=mI~x+uS!$9vCj*Wc@<*ls!K z>2Q`m7mrR)2TiqYtzyARflDq$h2C`39JH^ktqX}kqo>RBTD96TjVj53@AN(gtkWjlDg)m zP+FrwwQ?i~T&yIOs-q?vT^bQ=lK|v0&6twcnR61sa443-Bi5WvC33YK%0gp$L5Iu&u^Rz4hpZiV z8@>O@y&Rrj!FzhVe_{J>lfPUp{`<%2h4>^bW-kEnQdwX1uWIk->E83Z_4j&huv?CL zK3*g*4a42}sK{lrmnh9jFgaHVs!W->D1*j7WIakw!DG_aWGbp`|B9-i0ia0fTLf2^ eA$;!-hxHi3C|e=IxlZuQpUyFMl+sPyKmP*?R{^5{ diff --git a/tests/repository_data/repository/metadata/timestamp.json b/tests/repository_data/repository/metadata/timestamp.json index dd6e2ab8a58a32f3fff305ca79a3eb3dea2524b5..55099610b35a8cadb01e245225072042e06e45c4 100644 GIT binary patch literal 931 zcmXw2%Z?m347~4GH2NF|d`P6|oBtrl_>>?BM2Yf@lYPK531S%j_poQzfEo=@-Nh=h z>f6J%UoW4Y`t6@DaoyiNe%jxvvE;$^nXj;)Wm%27E?ZrUa zv_@*(IA5a#R>#pNGw4PiN(8;ML3gus%ll5bdR!b@b-iHyF=$^jQ?UAZZlPMVs? zn7{Vi5f$yC8VF5uPHQCPx^OnuiWnr3ga)bLbkVWwAm3d;ds8Os(U}chMPjC=?hHMP zI3%b9_wZPWT$iz68^!7|9nZ~kb6YES!JU1qwW3mLtIFEJrZFr8aw({Op9?10sHKgu zFie)GhZ=L+(@0b0W$8jY}_lk`5Z zah<7i&rZX7b)eK*8>qyRn*vYcTnuQYIeALMK1xbi6N*R#SxfVtYYrPWLtURlnr4k! z$tTw|l}0!B_?b2uRB|4ea@d3H92ZA814JsV)}B%`%(*nSTBfD~LsG*=Ors)JbOVDP z%3;7{@K%xXa6s3lWyCaV3{A|?EN1Mp@{IFKMRMaWE$>386*X z5)Hj-)r^@Vlshq$*(J0PO6go%2xB40;@v=O#L*!oNoXn=ZQGb+q=?36v!O|wY(Bxb z7Y - Konstantin Andrianov + Konstantin Andrianov. March 26, 2012. @@ -14,12 +14,10 @@ See LICENSE for licensing information. - Test download.py module. - - -NOTE: Make sure test_download.py is ran in 'tuf/tests/' directory. -Otherwise, module that launches simple server would not be found. + Unit test for 'download.py'. + NOTE: Make sure test_download.py is ran in 'tuf/tests/' directory. + Otherwise, module that launches simple server would not be found. """ @@ -37,7 +35,7 @@ import tuf.conf as conf import tuf.download as download import tuf.log -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox logger = logging.getLogger('tuf.test_download') diff --git a/tests/unit/test_ed25519_keys.py b/tests/test_ed25519_keys.py similarity index 100% rename from tests/unit/test_ed25519_keys.py rename to tests/test_ed25519_keys.py diff --git a/tests/integration/test_endless_data_attack.py b/tests/test_endless_data_attack.py similarity index 98% rename from tests/integration/test_endless_data_attack.py rename to tests/test_endless_data_attack.py index f4a47209..7a820327 100755 --- a/tests/integration/test_endless_data_attack.py +++ b/tests/test_endless_data_attack.py @@ -51,7 +51,7 @@ import tuf.util import tuf.log import tuf.client.updater as updater -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox logger = logging.getLogger('tuf.test_endless_data_attack') @@ -113,8 +113,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) diff --git a/tests/integration/test_extraneous_dependencies_attack.py b/tests/test_extraneous_dependencies_attack.py similarity index 98% rename from tests/integration/test_extraneous_dependencies_attack.py rename to tests/test_extraneous_dependencies_attack.py index 9545b5b1..0fa1c1d1 100755 --- a/tests/integration/test_extraneous_dependencies_attack.py +++ b/tests/test_extraneous_dependencies_attack.py @@ -52,7 +52,7 @@ import tuf.util import tuf.log import tuf.client.updater as updater -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox logger = logging.getLogger('tuf.test_extraneous_dependencies_attack') @@ -116,8 +116,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) diff --git a/tests/unit/test_formats.py b/tests/test_formats.py similarity index 97% rename from tests/unit/test_formats.py rename to tests/test_formats.py index a20b57b1..10133b08 100755 --- a/tests/unit/test_formats.py +++ b/tests/test_formats.py @@ -40,6 +40,9 @@ def tearDown(self): def test_schemas(self): # Test conditions for valid schemas. valid_schemas = { + 'ISO8601_DATETIME_SCHEMA': (tuf.formats.ISO8601_DATETIME_SCHEMA, + '1985-10-21T13:20:00Z'), + 'UNIX_TIMESTAMP_SCHEMA': (tuf.formats.UNIX_TIMESTAMP_SCHEMA, 499137720), 'HASH_SCHEMA': (tuf.formats.HASH_SCHEMA, 'A4582BCF323BCEF'), @@ -185,7 +188,7 @@ def test_schemas(self): {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, @@ -196,7 +199,7 @@ def test_schemas(self): 'TARGETS_SCHEMA': (tuf.formats.TARGETS_SCHEMA, {'_type': 'Targets', 'version': 8, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'targets': {'metadata/targets.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}, @@ -210,7 +213,7 @@ def test_schemas(self): 'SNAPSHOT_SCHEMA': (tuf.formats.SNAPSHOT_SCHEMA, {'_type': 'Snapshot', 'version': 8, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'meta': {'metadata/snapshot.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}}), @@ -218,7 +221,7 @@ def test_schemas(self): 'TIMESTAMP_SCHEMA': (tuf.formats.TIMESTAMP_SCHEMA, {'_type': 'Timestamp', 'version': 8, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'meta': {'metadata/timestamp.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}}}), @@ -240,7 +243,7 @@ def test_schemas(self): 'MIRRORLIST_SCHEMA': (tuf.formats.MIRRORLIST_SCHEMA, {'_type': 'Mirrors', 'version': 8, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'mirrors': [{'url_prefix': 'http://localhost:8001', 'metadata_path': 'metadata/', 'targets_path': 'targets/', @@ -290,7 +293,7 @@ def __init__(self, version, expires): def test_TimestampFile(self): # Test conditions for valid instances of 'tuf.formats.TimestampFile'. version = 8 - expires = 499137720 + expires = '1985-10-21T13:20:00Z' filedict = {'metadata/timestamp.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -324,7 +327,8 @@ def test_RootFile(self): # Test conditions for valid instances of 'tuf.formats.RootFile'. version = 8 consistent_snapshot = False - expires = 499137720 + expires = '1985-10-21T13:20:00Z' + keydict = {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}} @@ -374,7 +378,8 @@ def test_RootFile(self): def test_SnapshotFile(self): # Test conditions for valid instances of 'tuf.formats.SnapshotFile'. version = 8 - expires = 499137720 + expires = '1985-10-21T13:20:00Z' + filedict = {'metadata/snapshot.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -406,7 +411,8 @@ def test_SnapshotFile(self): def test_TargetsFile(self): # Test conditions for valid instances of 'tuf.formats.TargetsFile'. version = 8 - expires = 499137720 + expires = '1985-10-21T13:20:00Z' + filedict = {'metadata/targets.json': {'length': 1024, 'hashes': {'sha256': 'ABCD123'}, 'custom': {'type': 'metadata'}}} @@ -502,7 +508,7 @@ def test_make_signable(self): root = {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, @@ -629,7 +635,7 @@ def test_check_signable_object_format(self): root = {'_type': 'Root', 'version': 8, 'consistent_snapshot': False, - 'expires': 499137720, + 'expires': '1985-10-21T13:20:00Z', 'keys': {'123abc': {'keytype': 'rsa', 'keyval': {'public': 'pubkey', 'private': 'privkey'}}}, diff --git a/tests/unit/test_hash.py b/tests/test_hash.py similarity index 100% rename from tests/unit/test_hash.py rename to tests/test_hash.py diff --git a/tests/integration/test_indefinite_freeze_attack.py b/tests/test_indefinite_freeze_attack.py similarity index 97% rename from tests/integration/test_indefinite_freeze_attack.py rename to tests/test_indefinite_freeze_attack.py index 87de4c2b..2c90a409 100755 --- a/tests/integration/test_indefinite_freeze_attack.py +++ b/tests/test_indefinite_freeze_attack.py @@ -45,7 +45,7 @@ import tuf.log import tuf.client.updater as updater import tuf.repository_tool as repo_tool -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox # The repository tool is imported and logs console messages by default. Disable # console log messages generated by this unit test. @@ -111,8 +111,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) @@ -177,8 +176,9 @@ def test_without_tuf(self): 'timestamp.json') timestamp_metadata = tuf.util.load_json_file(timestamp_path) - timestamp_metadata['signed']['expires'] = int(time.time() - 10) - + expires = tuf.formats.unix_timestamp_to_datetime(int(time.time() - 10)) + expires = expires.isoformat() + 'Z' + timestamp_metadata['signed']['expires'] = expires tuf.formats.check_signable_object_format(timestamp_metadata) with open(timestamp_path, 'wb') as file_object: diff --git a/tests/unit/test_keydb.py b/tests/test_keydb.py similarity index 99% rename from tests/unit/test_keydb.py rename to tests/test_keydb.py index 0bd27ce2..df405b63 100755 --- a/tests/unit/test_keydb.py +++ b/tests/test_keydb.py @@ -171,7 +171,7 @@ def test_create_keydb_from_root_metadata(self): 'Targets': {'keyids': [keyid2], 'threshold': 1}} version = 8 consistent_snapshot = False - expires = 499137720 + expires = '1985-10-21T01:21:00Z' root_metadata = tuf.formats.RootFile.make_metadata(version, expires, @@ -208,7 +208,7 @@ def test_create_keydb_from_root_metadata(self): rsakey3['keytype'] = 'bad_keytype' keydict[keyid3] = rsakey3 version = 8 - expires = 499137720 + expires = '1985-10-21T01:21:00Z' root_metadata = tuf.formats.RootFile.make_metadata(version, expires, diff --git a/tests/unit/test_keys.py b/tests/test_keys.py similarity index 100% rename from tests/unit/test_keys.py rename to tests/test_keys.py diff --git a/tests/unit/test_mirrors.py b/tests/test_mirrors.py similarity index 97% rename from tests/unit/test_mirrors.py rename to tests/test_mirrors.py index 8aac13a4..603d5bf1 100755 --- a/tests/unit/test_mirrors.py +++ b/tests/test_mirrors.py @@ -12,8 +12,7 @@ See LICENSE for licensing information. - Test mirrors.py module. - + Unit test for 'mirrors.py'. """ import unittest @@ -21,7 +20,7 @@ import tuf import tuf.formats as formats import tuf.mirrors as mirrors -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox diff --git a/tests/integration/test_mix_and_match_attack.py b/tests/test_mix_and_match_attack.py similarity index 98% rename from tests/integration/test_mix_and_match_attack.py rename to tests/test_mix_and_match_attack.py index 7d4512a0..5d7aa0bb 100755 --- a/tests/integration/test_mix_and_match_attack.py +++ b/tests/test_mix_and_match_attack.py @@ -50,7 +50,7 @@ import tuf.log import tuf.client.updater as updater import tuf.repository_tool as repo_tool -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox # The repository tool is imported and logs console messages by default. Disable # console log messages generated by this unit test. @@ -117,8 +117,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) diff --git a/tests/unit/test_pycrypto_keys.py b/tests/test_pycrypto_keys.py similarity index 100% rename from tests/unit/test_pycrypto_keys.py rename to tests/test_pycrypto_keys.py diff --git a/tests/integration/test_replay_attack.py b/tests/test_replay_attack.py similarity index 98% rename from tests/integration/test_replay_attack.py rename to tests/test_replay_attack.py index 79aa60cb..8906e665 100755 --- a/tests/integration/test_replay_attack.py +++ b/tests/test_replay_attack.py @@ -51,7 +51,7 @@ import tuf.log import tuf.client.updater as updater import tuf.repository_tool as repo_tool -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox # The repository tool is imported and logs console messages by default. Disable # console log messages generated by this unit test. @@ -118,8 +118,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) diff --git a/tests/unit/test_repository_tool.py b/tests/test_repository_tool.py similarity index 96% rename from tests/unit/test_repository_tool.py rename to tests/test_repository_tool.py index eba82a77..67fc3f23 100755 --- a/tests/unit/test_repository_tool.py +++ b/tests/test_repository_tool.py @@ -107,7 +107,7 @@ def test_write_and_write_partial(self): temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) targets_directory = os.path.join(temporary_directory, 'repository', repo_tool.TARGETS_DIRECTORY_NAME) - original_targets_directory = os.path.join(os.pardir, 'repository_data', + original_targets_directory = os.path.join('repository_data', 'repository', 'targets') shutil.copytree(original_targets_directory, targets_directory) @@ -121,7 +121,7 @@ def test_write_and_write_partial(self): # (1) Load the public and private keys of the top-level roles, and one # delegated role. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') # Load the public keys. root_pubkey_path = os.path.join(keystore_directory, 'root_key.pub') @@ -262,8 +262,8 @@ def test_write_and_write_partial(self): def test_get_filepaths_in_directory(self): # Test normal case. # Use the pre-generated metadata directory for testing. - metadata_directory = os.path.join(os.pardir, 'repository_data', - 'repository', 'metadata') + metadata_directory = os.path.join('repository_data', + 'repository', 'metadata') # Test improperly formatted arguments. @@ -301,8 +301,9 @@ def __init__(self): self._rolename = 'metadata_role' # Expire in 86400 seconds (1 day). - expiration = int(time.time() + 86400) - + expiration = \ + tuf.formats.unix_timestamp_to_datetime(int(time.time() + 86400)) + expiration = expiration.isoformat() + 'Z' roleinfo = {'keyids': [], 'signing_keyids': [], 'threshold': 1, 'signatures': [], 'version': 0, 'consistent_snapshot': False, @@ -393,7 +394,7 @@ def test_keys(self): # Test keys() getter after a verification key has been loaded. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key.pub') key_object = repo_tool.import_rsa_publickey_from_file(key_path) self.metadata.add_verification_key(key_object) @@ -409,7 +410,7 @@ def test_signing_keys(self): # Test signing_keys() getter after a signing key has been loaded. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key') key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') self.metadata.load_signing_key(key_object) @@ -441,7 +442,7 @@ def test_compressions(self): def test_add_verification_key(self): # Add verification key and verify with keys() that it was added. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key.pub') key_object = repo_tool.import_rsa_publickey_from_file(key_path) self.metadata.add_verification_key(key_object) @@ -457,7 +458,7 @@ def test_add_verification_key(self): def test_remove_verification_key(self): # Add verification key so that remove_verifiation_key() can be tested. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key.pub') key_object = repo_tool.import_rsa_publickey_from_file(key_path) self.metadata.add_verification_key(key_object) @@ -476,7 +477,7 @@ def test_remove_verification_key(self): # Test non-existent public key argument. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'targets_key.pub') unused_key_object = repo_tool.import_rsa_publickey_from_file(key_path) @@ -487,7 +488,7 @@ def test_remove_verification_key(self): def test_load_signing_key(self): # Test normal case. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key') key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') self.metadata.load_signing_key(key_object) @@ -501,7 +502,7 @@ def test_load_signing_key(self): # Test non-private key. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key.pub') key_object = repo_tool.import_rsa_publickey_from_file(key_path) self.assertRaises(tuf.Error, self.metadata.load_signing_key, key_object) @@ -510,7 +511,7 @@ def test_load_signing_key(self): def test_unload_signing_key(self): # Load a signing key so that unload_signing_key() can have a key to unload. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'root_key') key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') self.metadata.load_signing_key(key_object) @@ -528,7 +529,7 @@ def test_unload_signing_key(self): # Test non-existent key argument. - key_path = os.path.join(os.pardir, 'repository_data', + key_path = os.path.join('repository_data', 'keystore', 'targets_key') unused_key_object = repo_tool.import_rsa_privatekey_from_file(key_path, 'password') @@ -542,7 +543,7 @@ def test_add_signature(self): # Test normal case. # Load signature list from any of pre-generated metadata; needed for # testing. - metadata_directory = os.path.join(os.pardir, 'repository_data', + metadata_directory = os.path.join('repository_data', 'repository', 'metadata') root_filepath = os.path.join(metadata_directory, 'root.json') root_signable = tuf.util.load_json_file(root_filepath) @@ -561,7 +562,7 @@ def test_add_signature(self): def test_remove_signature(self): # Test normal case. # Add a signature so remove_signature() has some signature to remove. - metadata_directory = os.path.join(os.pardir, 'repository_data', + metadata_directory = os.path.join('repository_data', 'repository', 'metadata') root_filepath = os.path.join(metadata_directory, 'root.json') root_signable = tuf.util.load_json_file(root_filepath) @@ -592,7 +593,7 @@ def test_signatures(self): # Test getter after adding an example signature. - metadata_directory = os.path.join(os.pardir, 'repository_data', + metadata_directory = os.path.join('repository_data', 'repository', 'metadata') root_filepath = os.path.join(metadata_directory, 'root.json') root_signable = tuf.util.load_json_file(root_filepath) @@ -705,7 +706,7 @@ def setUp(self): temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) self.targets_directory = os.path.join(temporary_directory, 'repository', 'targets') - original_targets_directory = os.path.join(os.pardir, 'repository_data', + original_targets_directory = os.path.join('repository_data', 'repository', 'targets') shutil.copytree(original_targets_directory, self.targets_directory) self.targets_object = repo_tool.Targets(self.targets_directory) @@ -757,7 +758,7 @@ def test_get_delegated_rolenames(self): # Test normal case. # Perform two delegations so that get_delegated_rolenames() has roles to # return. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) target1_filepath = os.path.join(self.targets_directory, 'file1.txt') @@ -798,7 +799,7 @@ def test_delegations(self): # Test normal case. # Perform a delegation so that delegations() has a Targets() object to # return. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) target1_filepath = os.path.join(self.targets_directory, 'file1.txt') @@ -920,7 +921,7 @@ def test_delegate(self): # Test normal case. # Need at least one public key and valid target paths required by # delegate(). - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) target1_filepath = os.path.join(self.targets_directory, 'file1.txt') @@ -981,7 +982,7 @@ def test_delegate(self): def test_delegate_hashed_bins(self): # Test normal case. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) target1_filepath = os.path.join(self.targets_directory, 'file1.txt') @@ -1038,7 +1039,7 @@ def test_add_restricted_paths(self): # Test normal case. # Perform a delegation so that add_restricted_paths() has a child role # to restrict. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) @@ -1087,7 +1088,7 @@ def test_add_restricted_paths(self): def test_revoke(self): # Test normal case. # Perform a delegation so that revoke() has a delegation to revoke. - keystore_directory = os.path.join(os.pardir, 'repository_data', 'keystore') + keystore_directory = os.path.join('repository_data', 'keystore') public_keypath = os.path.join(keystore_directory, 'root_key.pub') public_key = repo_tool.import_rsa_publickey_from_file(public_keypath) target1_filepath = os.path.join(self.targets_directory, 'file1.txt') @@ -1193,7 +1194,7 @@ def test_create_new_repository(self): def test_load_repository(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - original_repository_directory = os.path.join(os.pardir, 'repository_data', + original_repository_directory = os.path.join('repository_data', 'repository') repository_directory = os.path.join(temporary_directory, 'repository') shutil.copytree(original_repository_directory, repository_directory) @@ -1284,7 +1285,7 @@ def test_import_rsa_privatekey_from_file(self): # Load one of the pre-generated key files from 'tuf/tests/repository_data'. # 'password' unlocks the pre-generated key files. - key_filepath = os.path.join(os.pardir, 'repository_data', 'keystore', + key_filepath = os.path.join('repository_data', 'keystore', 'root_key') self.assertTrue(os.path.exists(key_filepath)) @@ -1319,7 +1320,7 @@ def test_import_rsa_publickey_from_file(self): temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) # Load one of the pre-generated key files from 'tuf/tests/repository_data'. - key_filepath = os.path.join(os.pardir, 'repository_data', 'keystore', + key_filepath = os.path.join('repository_data', 'keystore', 'root_key.pub') self.assertTrue(os.path.exists(key_filepath)) @@ -1521,7 +1522,7 @@ def test_get_target_hash(self): def test_generate_root_metadata(self): # Test normal case. # Load the root metadata provided in 'tuf/tests/repository_data/'. - root_filepath = os.path.join(os.pardir, 'repository_data', 'repository', + root_filepath = os.path.join('repository_data', 'repository', 'metadata', 'root.json') root_signable = tuf.util.load_json_file(root_filepath) @@ -1529,25 +1530,26 @@ def test_generate_root_metadata(self): # available in 'tuf.keydb' and 'tuf.roledb'. tuf.roledb.create_roledb_from_root_metadata(root_signable['signed']) tuf.keydb.create_keydb_from_root_metadata(root_signable['signed']) + expires = '1985-10-21T01:22:00Z' - root_metadata = repo_tool.generate_root_metadata(1, 189345000, + root_metadata = repo_tool.generate_root_metadata(1, expires, consistent_snapshot=False) self.assertTrue(tuf.formats.ROOT_SCHEMA.matches(root_metadata)) # Test improperly formatted arguments. self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, - '3', 189345000, False) + '3', expires, False) self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, 1, '3', False) self.assertRaises(tuf.FormatError, repo_tool.generate_root_metadata, - 1, 189345000, 3) + 1, expires, 3) # Test for missing required roles and keys. tuf.roledb.clear_roledb() tuf.keydb.clear_keydb() self.assertRaises(tuf.Error, repo_tool.generate_root_metadata, - 1, 189345000, False) + 1, expires, False) @@ -1564,7 +1566,7 @@ def test_generate_targets_metadata(self): # Set valid generate_targets_metadata() arguments. version = 1 datetime_object = datetime.datetime(2030, 01, 01, 12, 00) - expiration_date = tuf.formats.datetime_to_unix_timestamp(datetime_object) + expiration_date = datetime_object.isoformat() + 'Z' target_files = ['file.txt'] delegations = {"keys": { @@ -1639,7 +1641,7 @@ def test_generate_targets_metadata(self): def test_generate_snapshot_metadata(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - original_repository_path = os.path.join(os.pardir, 'repository_data', + original_repository_path = os.path.join('repository_data', 'repository') repository_directory = os.path.join(temporary_directory, 'repository') shutil.copytree(original_repository_path, repository_directory) @@ -1649,7 +1651,7 @@ def test_generate_snapshot_metadata(self): targets_filename = os.path.join(metadata_directory, repo_tool.TARGETS_FILENAME) version = 1 - expiration_date = 189345000 + expiration_date = '1985-10-21T13:20:00Z' snapshot_metadata = \ repo_tool.generate_snapshot_metadata(metadata_directory, version, @@ -1684,7 +1686,7 @@ def test_generate_snapshot_metadata(self): def test_generate_timestamp_metadata(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - original_repository_path = os.path.join(os.pardir, 'repository_data', + original_repository_path = os.path.join('repository_data', 'repository') repository_directory = os.path.join(temporary_directory, 'repository') shutil.copytree(original_repository_path, repository_directory) @@ -1695,7 +1697,8 @@ def test_generate_timestamp_metadata(self): # Set valid generate_timestamp_metadata() arguments. version = 1 - expiration_date = 189345000 + expiration_date = '1985-10-21T13:20:00Z' + compressions = ['gz'] snapshot_metadata = \ @@ -1722,9 +1725,9 @@ def test_generate_timestamp_metadata(self): def test_sign_metadata(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - metadata_path = os.path.join(os.pardir, 'repository_data', + metadata_path = os.path.join('repository_data', 'repository', 'metadata') - keystore_path = os.path.join(os.pardir, 'repository_data', + keystore_path = os.path.join('repository_data', 'keystore') root_filename = os.path.join(metadata_path, 'root.json') root_metadata = tuf.util.load_json_file(root_filename)['signed'] @@ -1763,7 +1766,7 @@ def test_sign_metadata(self): def test_write_metadata_file(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - metadata_directory = os.path.join(os.pardir, 'repository_data', + metadata_directory = os.path.join('repository_data', 'repository', 'metadata') root_filename = os.path.join(metadata_directory, 'root.json') root_signable = tuf.util.load_json_file(root_filename) @@ -1793,7 +1796,7 @@ def test_write_metadata_file(self): def test_create_tuf_client_directory(self): # Test normal case. temporary_directory = tempfile.mkdtemp(dir=self.temporary_directory) - repository_directory = os.path.join(os.pardir, 'repository_data', + repository_directory = os.path.join('repository_data', 'repository') client_directory = os.path.join(temporary_directory, 'client') diff --git a/tests/unit/test_roledb.py b/tests/test_roledb.py similarity index 99% rename from tests/unit/test_roledb.py rename to tests/test_roledb.py index 6df80df1..117d4eba 100755 --- a/tests/unit/test_roledb.py +++ b/tests/test_roledb.py @@ -320,7 +320,7 @@ def test_create_roledb_from_root_metadata(self): 'targets': {'keyids': [keyid2], 'threshold': 1}} version = 8 consistent_snapshot = False - expires = 499137720 + expires = '1985-10-21T01:21:00Z' root_metadata = tuf.formats.RootFile.make_metadata(version, expires, diff --git a/tests/unit/test_schema.py b/tests/test_schema.py similarity index 100% rename from tests/unit/test_schema.py rename to tests/test_schema.py diff --git a/tests/unit/test_sig.py b/tests/test_sig.py similarity index 100% rename from tests/unit/test_sig.py rename to tests/test_sig.py diff --git a/tests/integration/test_slow_retrieval_attack.py b/tests/test_slow_retrieval_attack.py similarity index 98% rename from tests/integration/test_slow_retrieval_attack.py rename to tests/test_slow_retrieval_attack.py index 7e696ee4..3e4cf023 100755 --- a/tests/integration/test_slow_retrieval_attack.py +++ b/tests/test_slow_retrieval_attack.py @@ -53,7 +53,7 @@ import tuf.util import tuf.log import tuf.client.updater as updater -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox logger = logging.getLogger('tuf.test_slow_retrieval_attack') @@ -124,8 +124,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf/tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) diff --git a/tests/unit/test_updater.py b/tests/test_updater.py similarity index 99% rename from tests/unit/test_updater.py rename to tests/test_updater.py index 2e362802..97348b84 100755 --- a/tests/unit/test_updater.py +++ b/tests/test_updater.py @@ -56,7 +56,7 @@ import tuf.keydb import tuf.roledb import tuf.repository_tool as repo_tool -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox import tuf.client.updater as updater logger = logging.getLogger('tuf.test_updater') @@ -120,8 +120,7 @@ def setUp(self): # Copy the original repository files provided in the test folder so that # any modifications made to repository files are restricted to the copies. # The 'repository_data' directory is expected to exist in 'tuf.tests/'. - original_repository_files = os.path.join(os.getcwd(), os.pardir, - 'repository_data') + original_repository_files = os.path.join(os.getcwd(), 'repository_data') temporary_repository_root = \ self.make_temp_directory(directory=self.temporary_directory) @@ -465,7 +464,8 @@ def test_2__ensure_not_expired(self): # 'tuf.ExpiredMetadataError' should be raised in this next test condition, # because the expiration_date has expired by 10 seconds. - expires = int(time.time() - 10) + expires = tuf.formats.unix_timestamp_to_datetime(int(time.time() - 10)) + expires = expires.isoformat() + 'Z' self.repository_updater.metadata['current']['root']['expires'] = expires # Ensure the 'expires' value of the root file is valid by checking the diff --git a/tests/unit/test_util.py b/tests/test_util.py similarity index 99% rename from tests/unit/test_util.py rename to tests/test_util.py index 465ed0c8..4f4ae895 100755 --- a/tests/unit/test_util.py +++ b/tests/test_util.py @@ -14,7 +14,7 @@ See LICENSE for licensing information. - util.py unit tests. + Unit test for 'util.py' """ import os @@ -29,7 +29,7 @@ import tuf.log import tuf.hash import tuf.util as util -import tuf.tests.unittest_toolbox as unittest_toolbox +import tests.unittest_toolbox as unittest_toolbox logger = logging.getLogger('tuf.test_util') diff --git a/tests/unit/deprecated/test_keystore.py b/tests/unit/deprecated/test_keystore.py deleted file mode 100755 index e1a8c7f2..00000000 --- a/tests/unit/deprecated/test_keystore.py +++ /dev/null @@ -1,355 +0,0 @@ -""" - - test_keystore.py - - - Konstantin Andrianov - - - April 27, 2012. - - - See LICENSE for licensing information. - - - Unit test for keystore.py. -""" - -import unittest -import shutil -import os -import logging -import Crypto.Random -import Crypto.Protocol.KDF - -import tuf -import tuf.repo.keystore -import tuf.keys -import tuf.formats -import tuf.util -import tuf.log - -logger = logging.getLogger('tuf.test_keystore') - -# We'll need json module for testing '_encrypt()' and '_decrypt()' -# internal function. -json = tuf.util.import_json() - -tuf.repo.keystore._PBKDF2_ITERATIONS = 1000 - -# Creating a directory string in current directory. -_CURRENT_DIR = os.getcwd() -_DIR = os.path.join(_CURRENT_DIR, 'test_keystore') - -# Check if directory '_DIR' exists. -if os.path.exists(_DIR): - msg = ('\''+_DIR+'\' directory already exists,'+ - ' please change '+'\'_DIR\''+' to something else.') - raise tuf.Error(msg) - -KEYSTORE = tuf.repo.keystore -RSAKEYS = [] -PASSWDS = [] -temp_keys_info = [] -temp_keys_vals = [] - -for i in range(3): - # Populating the original 'RSAKEYS' and 'PASSWDS' lists. - RSAKEYS.append(tuf.keys.generate_rsa_key()) - PASSWDS.append('passwd_'+str(i)) - - # Saving original copies of 'RSAKEYS' and 'PASSWDS' to temp variables - # in order to repopulate them at the start of every test. - temp_keys_info.append(RSAKEYS[i].values()) - temp_keys_vals.append(RSAKEYS[i]['keyval'].values()) - -temp_passwds=list(PASSWDS) - - - -class TestKeystore(unittest.TestCase): - def setUp(self): - # Returning 'RSAKEY' and 'PASSWDS' to original state. - for i in range(len(temp_keys_info)): - RSAKEYS[i]['keytype'] = temp_keys_info[i][0] - RSAKEYS[i]['keyid'] = temp_keys_info[i][1] - RSAKEYS[i]['keyval'] = temp_keys_info[i][2] - RSAKEYS[i]['keyval']['public'] = temp_keys_vals[i][0] - RSAKEYS[i]['keyval']['private'] = temp_keys_vals[i][1] - PASSWDS[i] = temp_passwds[i] - - - - def tearDown(self): - # Empty keystore's databases. - KEYSTORE.clear_keystore() - - # Check if directory '_DIR' exists, remove it if it does. - if os.path.exists(_DIR): - shutil.rmtree(_DIR) - - - - def test_clear_keystore(self): - # Populate KEYSTORE's internal databases '_keystore' and '_derived_keys'. - for i in range(3): - KEYSTORE.add_rsakey(RSAKEYS[i], PASSWDS[i], RSAKEYS[i]['keyid']) - - # Verify KEYSTORE's internal databases ARE NOT EMPTY. - self.assertTrue(len(KEYSTORE._keystore) > 0) - self.assertTrue(len(KEYSTORE._derived_keys) > 0) - - # Clear KEYSTORE's internal databases. - KEYSTORE.clear_keystore() - - # Verify KEYSTORE's internal databases ARE EMPTY. - self.assertFalse(len(KEYSTORE._keystore) > 0) - self.assertFalse(len(KEYSTORE._derived_keys) > 0) - - - - def test_add_rsakey(self): - # Passing 2 arguments to the function and verifying that the internal - # databases have been modified. - KEYSTORE.add_rsakey(RSAKEYS[0], PASSWDS[0]) - - self.assertEqual(RSAKEYS[0], KEYSTORE._keystore[RSAKEYS[0]['keyid']], - 'Adding an rsa key dict was unsuccessful.') - - self.assertTrue(len(KEYSTORE._derived_keys) == 1, - 'Adding a password pertaining to \'_keyid\' was unsuccessful.') - - # Passing three arguments to the function, i.e. including the 'keyid'. - KEYSTORE.add_rsakey(RSAKEYS[1], PASSWDS[1], RSAKEYS[1]['keyid']) - - self.assertEqual(RSAKEYS[1], - KEYSTORE._keystore[RSAKEYS[1]['keyid']], - 'Adding an rsa key dict was unsuccessful.') - - self.assertTrue(len(KEYSTORE._derived_keys) == 2, - 'Adding a password pertaining to \'_keyid\' was unsuccessful.') - - # Passing a keyid that does not match the keyid in 'rsakey_dict'. - _keyid = 'somedifferentkey123456789' - self.assertRaises(tuf.Error, KEYSTORE.add_rsakey, RSAKEYS[2], - PASSWDS[2], _keyid) - - # Passing an existing 'rsakey_dict' object. - self.assertRaises(tuf.KeyAlreadyExistsError, KEYSTORE.add_rsakey, - RSAKEYS[1], PASSWDS[1], RSAKEYS[1]['keyid']) - - # Passing an 'rsakey_dict' that does not conform to the 'RSAKEY_SCHEMA'. - del RSAKEYS[2]['keytype'] - - self.assertRaises(tuf.FormatError, KEYSTORE.add_rsakey, - RSAKEYS[2], PASSWDS[2], RSAKEYS[2]['keyid']) - - - - def test_save_keystore_to_keyfiles(self): - # Extract and store keyids in '_keyids' list. - keyids = [] - - # Populate KEYSTORE's internal databases '_keystore' and '_derived_keys'. - for i in range(3): - KEYSTORE.add_rsakey(RSAKEYS[i], PASSWDS[i], RSAKEYS[i]['keyid']) - keyids.append(RSAKEYS[i]['keyid']) - - # Check if directory '_DIR' exists, remove it if it does. - if os.path.exists(_DIR): - shutil.rmtree(_DIR) - - KEYSTORE.save_keystore_to_keyfiles(_DIR) - - # Check if directory '_DIR' has been created. - self.assertTrue(os.path.exists(_DIR), 'Creating directory failed.') - - # Check if all of the key files where created and that they are not empty. - for keyid in keyids: - key_file = os.path.join(_DIR, str(keyid)+'.key') - # Checks if key file has been created. - self.assertTrue(os.path.exists(key_file), 'Key file does not exist.') - - file_stats = os.stat(key_file) - # Checks if key file is not empty. - self.assertTrue(file_stats.st_size > 0) - - # Passing an invalid 'directory_name' argument - an integer value. - self.assertRaises(tuf.FormatError, KEYSTORE.save_keystore_to_keyfiles, 222) - - - - def test_load_keystore_from_keyfiles(self): - keyids = [] - # Check if '_DIR' directory exists, if not - create it. - if not os.path.exists(_DIR): - # Populate KEYSTORE's internal databases. - for i in range(3): - KEYSTORE.add_rsakey(RSAKEYS[i], PASSWDS[i], RSAKEYS[i]['keyid']) - keyids.append(RSAKEYS[i]['keyid']) - - # Create the key files. - KEYSTORE.save_keystore_to_keyfiles(_DIR) - - # Clearing internal databases. - KEYSTORE.clear_keystore() - - # Test normal conditions where two valid arguments are passed. - loaded_keys = KEYSTORE.load_keystore_from_keyfiles(_DIR, keyids, PASSWDS) - - # Loaded keys should all be contained in 'keyids'. - loaded_keys_set = set(loaded_keys) - keyids_set = set(keyids) - intersect = keyids_set.intersection(loaded_keys_set) - self.assertEquals(len(intersect), len(keyids)) - - for i in range(3): - self.assertEqual(RSAKEYS[i], KEYSTORE._keystore[RSAKEYS[i]['keyid']]) - - # Clearing internal databases. - KEYSTORE.clear_keystore() - - _invalid_dir = os.path.join(_CURRENT_DIR, 'invalid_directory') - - # Passing an invalid 'directory_name' argument - a directory that - # does not exist. AS EXPECTED, THIS CALL SHOULDN'T RAISE ANY ERRORS. - KEYSTORE.load_keystore_from_keyfiles(_invalid_dir, keyids, PASSWDS) - - # The keystore should not have loaded any keys. - self.assertEqual(0, len(KEYSTORE._keystore)) - self.assertEqual(0, len(KEYSTORE._derived_keys)) - - # Passing nonexistent 'keyids'. - # AS EXPECTED, THIS CALL SHOULDN'T RAISE ANY ERRORS. - invalid_keyids = ['333', '333', '333'] - KEYSTORE.load_keystore_from_keyfiles(_DIR, invalid_keyids, PASSWDS) - - # The keystore should not have loaded any keys. - self.assertEqual(0, len(KEYSTORE._keystore)) - self.assertEqual(0, len(KEYSTORE._derived_keys)) - - # Passing an invalid 'directory_name' argument - an integer value. - self.assertRaises(tuf.FormatError, KEYSTORE.load_keystore_from_keyfiles, - 333, keyids, PASSWDS) - - # Passing an invalid 'passwords' argument - a string value. - self.assertRaises(tuf.FormatError, KEYSTORE.load_keystore_from_keyfiles, - _DIR, keyids, '333') - - # Passing an invalid 'passwords' argument - an integer value. - self.assertRaises(tuf.FormatError, KEYSTORE.load_keystore_from_keyfiles, - _DIR, keyids, 333) - - # Passing an invalid 'keyids' argument - a string value. - self.assertRaises(tuf.FormatError, KEYSTORE.load_keystore_from_keyfiles, - _DIR, '333', PASSWDS) - - # Passing an invalid 'keyids' argument - an integer value. - self.assertRaises(tuf.FormatError, KEYSTORE.load_keystore_from_keyfiles, - _DIR, 333, PASSWDS) - - - - def test_change_password(self): - # Populate KEYSTORE's internal databases. - for i in range(2): - KEYSTORE.add_rsakey(RSAKEYS[i], PASSWDS[i], RSAKEYS[i]['keyid']) - - derived_key_0 = KEYSTORE._derived_keys[RSAKEYS[0]['keyid']] - # Create a new password. - new_passwd = 'changed_password' - - # Change a password - normal case. - KEYSTORE.change_password(RSAKEYS[0]['keyid'], PASSWDS[0], new_passwd) - - # Check if password was changed. - new_derived_key = KEYSTORE._derived_keys[RSAKEYS[0]['keyid']]['derived_key'] - self.assertNotEqual(new_derived_key, - derived_key_0['derived_key']) - - # Passing an invalid keyid (i.e., RSAKEY[2] that was not loaded into - # the '_keystore'). - self.assertRaises(tuf.UnknownKeyError, KEYSTORE.change_password, - RSAKEYS[2]['keyid'], PASSWDS[1], new_passwd) - - # Passing an incorrect old password. - self.assertRaises(tuf.BadPasswordError, KEYSTORE.change_password, - RSAKEYS[1]['keyid'], PASSWDS[2], new_passwd) - - - - def test_get_key(self): - # Populate KEYSTORE's internal databases. - for i in range(2): - KEYSTORE.add_rsakey(RSAKEYS[i], PASSWDS[i], RSAKEYS[i]['keyid']) - - # Get a key - normal case. - self.assertEqual(KEYSTORE.get_key(RSAKEYS[0]['keyid']), RSAKEYS[0]) - - # Passing an invalid keyid. - self.assertRaises(tuf.UnknownKeyError, - KEYSTORE.get_key, RSAKEYS[2]['keyid']) - - # Passing an invalid keyid format. - self.assertRaises(tuf.FormatError, KEYSTORE.get_key, 123) - - - - def test_internal_encrypt(self): - # Test for valid arguments to '_encrypt()' and a valid return type. - salt = Crypto.Random.new().read(16) - iterations = tuf.repo.keystore._PBKDF2_ITERATIONS - derived_key = Crypto.Protocol.KDF.PBKDF2(PASSWDS[0], salt) - derived_key_information = {'salt': salt, 'derived_key': derived_key, - 'iterations': iterations} - encrypted_key = KEYSTORE._encrypt(json.dumps(RSAKEYS[0]), - derived_key_information) - self.assertEqual(type(encrypted_key), str) - - - - def test_internal_decrypt(self): - del RSAKEYS[0]['keyid'] - tuf.formats.KEY_SCHEMA.check_match(RSAKEYS[0]) - - salt = Crypto.Random.new().read(16) - salt, iterations, derived_key = \ - tuf.repo.keystore._generate_derived_key(PASSWDS[0], salt) - derived_key_information = {'salt': salt, - 'iterations': iterations, - 'derived_key': derived_key} - - # Getting a valid encrypted key using '_encrypt()'. - encrypted_key = KEYSTORE._encrypt(json.dumps(RSAKEYS[0]), - derived_key_information) - - # Decrypting and decoding (using json's loads()) an encrypted file. - #tuf.util.load_json_string(KEYSTORE._decrypt(encrypted_key, PASSWDS[0])) - json.dumps(KEYSTORE._decrypt(encrypted_key, PASSWDS[0])) - - self.assertEqual(RSAKEYS[0], tuf.util.load_json_string( - KEYSTORE._decrypt(encrypted_key, PASSWDS[0]))) - - # Passing an invalid password to try to decrypt the file. - self.assertRaises(tuf.CryptoError, KEYSTORE._decrypt, - encrypted_key, PASSWDS[1]) - - - -def setUpModule(): - # setUpModule() is called before any test cases run. - # Ensure the keystore has not been modified by a previous test, which may - # affect assumptions (i.e., empty keystore) made by the tests cases in this - # unit test. - tuf.repo.keystore.clear_keystore() - -def tearDownModule(): - # tearDownModule() is called after all the tests have run. - # Ensure we clean up the keystore. They say courtesy is contagious. - tuf.repo.keystore.clear_keystore() - - - -# Run the unit tests. -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_push.py b/tests/unit/deprecated/test_push.py deleted file mode 100755 index 4acc8d75..00000000 --- a/tests/unit/deprecated/test_push.py +++ /dev/null @@ -1,166 +0,0 @@ -""" - - test_push.py - - - Konstantin Andrianov - - - April 2013. - - - See LICENSE for licensing information. - - - Test push.py. - -""" - -import os -import getpass -import logging -import tempfile -import unittest -import ConfigParser - -import tuf -import tuf.log -import tuf.pushtools.push as push -import tuf.pushtools.transfer.scp as scp -import tuf.pushtools.pushtoolslib as pushtoolslib -import tuf.tests.util_test_tools as util_test_tools - -logger = logging.getLogger('tuf.test_push') - - -class TestPush(unittest.TestCase): - src_push_dict = {} - - ORIGINAL_PUSH_CONFIG = pushtoolslib.PUSH_CONFIG - - - - @staticmethod - def _mock_scp_transfer(config_dict): - pass - - - - @staticmethod - def write_config_file(config_filename, config_dictionary): - """Create a configuration file by writing supplied configuration - dictionary ('config_dictionary') into the file ('config_filename').""" - - config = ConfigParser.RawConfigParser() - - for section, values_dict in config_dictionary.iteritems(): - config.add_section(section) - for key in values_dict: - config.set(section, key, values_dict[key]) - - # Writing our configuration file to 'config_filename'. - with open(config_filename, 'wb') as configfile: - config.write(configfile) - - - - def setUp(self): - # Create push configuration file, general temporary dir 'root_repo'. - cwd = os.getcwd() - self.push_config = tempfile.mkstemp(prefix='tmp_push_conf_', dir=cwd)[1] - self.root_repo = tempfile.mkdtemp(prefix='tmp_tuf_repo_', dir=cwd) - - # Drop target files into the working project directory 'reg_repo'. - # When targets metadata updates, the target files in the 'reg_repo' - # will be copied into the tuf's targets directory. - self.reg_repo = os.path.join(self.root_repo, 'reg_repo') - os.mkdir(self.reg_repo) - - # Add a file to the 'reg_repo'. - util_test_tools.add_file_to_repository(self.reg_repo, data='Test String') - - # Create TUF repository. - util_test_tools.init_tuf(self.root_repo) - - # Update the tuf targets metadata. - util_test_tools.make_targets_meta(self.root_repo) - - # Populate 'src_push_dict'. - targets_dir = os.path.join(self.root_repo, 'tuf_repo', 'targets') - metadate_path = os.path.join(self.root_repo, 'tuf_repo', 'metadata', - 'targets.txt') - remote_dir = tempfile.mkdtemp(prefix='tmp_tuf_repo_', dir=self.root_repo) - - self.src_push_dict = {'general':{'transfer_module':'scp', - 'metadata_path':metadate_path, - 'targets_directory':targets_dir}, - 'scp':{'host':'localhost', - 'identity_file':None, - 'user':getpass.getuser(), - 'remote_directory':remote_dir}} - - # Write the config dictionary into the push configuration file. - self.write_config_file(self.push_config, self.src_push_dict) - - # Patch 'pushtoolslib.PUSH_CONFIG'. - pushtoolslib.PUSH_CONFIG = os.path.basename(self.push_config) - - - - def tearDown(self): - # Remove tempfile. - os.remove(self.push_config) - - # Remove TUF repository. - util_test_tools.cleanup(self.root_repo) - - # Clear 'src_push_dict'. - self.src_push_dict.clear() - - # # Reassign 'pushtoolslib.PUSH_CONFIG'. - pushtoolslib.PUSH_CONFIG = self.ORIGINAL_PUSH_CONFIG - - - - def test_expected_behaviour_of_push_without_scp(self): - # Patch 'scp.transfer' function. - ORIGINAL_SCP_TRANSFER_MODULE = scp.transfer - scp.transfer = self._mock_scp_transfer - - push.push(self.push_config) - - # Restore 'scp.transfer' function. - scp.transfer = ORIGINAL_SCP_TRANSFER_MODULE - - - - def test_exceptions_handeling_of_push_without_scp(self): - # Patch 'scp.transfer' function. - ORIGINAL_SCP_TRANSFER_MODULE = scp.transfer - scp.transfer = self._mock_scp_transfer - - self.assertRaises(tuf.Error, push.push, self.root_repo) - self.assertRaises(tuf.FormatError, push.push, None) - self.assertRaises(tuf.FormatError, push.push, 12345) - self.assertRaises(tuf.FormatError, push.push, ['test']) - self.assertRaises(tuf.FormatError, push.push, {'test':'test'}) - - # Restore 'scp.transfer' function. - scp.transfer = ORIGINAL_SCP_TRANSFER_MODULE - - - - # Unit test bellow executes secure copy 'scp' command. Hence user's - # password is requered. Comment-out or remove the line bellow to - # un-skip the unit test. - @unittest.skip("Requires user's password!") - def test_expected_behaviour_of_push_with_scp(self): - push.push(self.push_config) - - - - - -# Run the unittests -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_pushtoolslib.py b/tests/unit/deprecated/test_pushtoolslib.py deleted file mode 100755 index e0cf6694..00000000 --- a/tests/unit/deprecated/test_pushtoolslib.py +++ /dev/null @@ -1,173 +0,0 @@ -""" - - test_pushtoolslib.py - - - Konstantin Andrianov - - - April 2013. - - - See LICENSE for licensing information. - - - Test pushtoolslib.py. - -""" - -import os -import tempfile -import unittest -import ConfigParser -import logging - -import tuf -import tuf.log -import tuf.formats -import tuf.pushtools.pushtoolslib as pushtoolslib - -logger = logging.getLogger('tuf.test_pushtoolslib') - - -class TestPushtoolslib(unittest.TestCase): - src_push_dict = {} - src_receive_dict = {} - ORIGINAL_PUSH_CONFIG = pushtoolslib.PUSH_CONFIG - ORIGINAL_RECEIVE_CONFIG = pushtoolslib.RECEIVE_CONFIG - - - - def setUp(self): - # Create config tempfiles. - cwd = os.getcwd() - self.push_config_file = tempfile.mkstemp(prefix='tmp_push_conf_', dir=cwd)[1] - self.receive_config_file = tempfile.mkstemp(prefix='tmp_receive_conf_', dir=cwd)[1] - - # Populate 'src_push_dict' and 'src_receive_dict'. - self.src_push_dict = {'general':{'transfer_module':'scp', - 'metadata_path':'some/path', - 'targets_directory':'some/path'}, - 'scp':{'host':'localhost', - 'user':'user', - 'identity_file':None, - 'remote_directory':'~/pushes'}} - - self.src_receive_dict = {'general':{'pushroots':['some/path'], - 'repository_directory':'some/path', - 'metadata_directory':'some/path', - 'targets_directory':'some/path', - 'backup_directory':'some/path'}} - - # Patch the 'pushtoolslib.PUSH_CONFIG' and 'pushtoolslib.RECEIVE_CONFIG' - pushtoolslib.PUSH_CONFIG = os.path.basename(self.push_config_file) - pushtoolslib.RECEIVE_CONFIG = os.path.basename(self.receive_config_file) - - - - def tearDown(self): - # Remove tempfile. - os.remove(self.push_config_file) - os.remove(self.receive_config_file) - - # Clear dictionaries. - self.src_push_dict.clear() - self.src_receive_dict.clear() - - # Reassign 'pushtoolslib.PUSH_CONFIG' and 'pushtoolslib.RECEIVE_CONFIG' - # to original values. - pushtoolslib.PUSH_CONFIG = self.ORIGINAL_PUSH_CONFIG - pushtoolslib.RECEIVE_CONFIG = self.ORIGINAL_RECEIVE_CONFIG - - - - @staticmethod - def write_config_file(config_filename, config_dictionary): - """Create a configuration file by writing supplied configuration - dictionary ('config_dictionary') into the file ('config_filename').""" - - config = ConfigParser.RawConfigParser() - - for section, values_dict in config_dictionary.iteritems(): - config.add_section(section) - for key in values_dict: - config.set(section, key, values_dict[key]) - - # Writing our configuration file to 'config_filename'. - with open(config_filename, 'wb') as configfile: - config.write(configfile) - - - - def test_expected_behavior_of_read_config_file(self): - # Test 'push' configuration type. - self.write_config_file(self.push_config_file, self.src_push_dict) - config_dict = pushtoolslib.read_config_file(self.push_config_file, 'push') - tuf.formats.SCPCONFIG_SCHEMA.check_match(config_dict) - - # Test 'receive' configuration type. - self.write_config_file(self.receive_config_file, self.src_receive_dict) - config_dict = pushtoolslib.read_config_file(self.receive_config_file, 'receive') - tuf.formats.RECEIVECONFIG_SCHEMA.check_match(config_dict) - - - - def test_exceptions_handeling_of_read_config_file(self): - # Test an incorrect configuration file. - with open(self.push_config_file, 'wb') as configfile: - configfile.write('test') - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'push') - - self.write_config_file(self.push_config_file, {}) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'push') - - self.write_config_file(self.receive_config_file, self.src_receive_dict) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.receive_config_file, 'push') - - self.write_config_file(self.push_config_file, self.src_push_dict) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'receive') - - # Test incorrect configuration type. - self.write_config_file(self.push_config_file, self.src_push_dict) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'junk') - - # Test 'push' type configuration with 'transfer_module' absent from - # config_dict['general']. - saved_transfer_module = self.src_push_dict['general']['transfer_module'] - del self.src_push_dict['general']['transfer_module'] - self.write_config_file(self.push_config_file, self.src_push_dict) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'push') - - # Test 'push' type configuration with - # config_dict['general']['transfer_module'] != 'scp'. - self.src_push_dict['general']['transfer_module'] = 'test' - self.write_config_file(self.push_config_file, self.src_push_dict) - self.assertRaises(tuf.Error, pushtoolslib.read_config_file, - self.push_config_file, 'push') - - # Test 'push' type configuration with one of the keys missing. - self.src_push_dict['general']['transfer_module'] = 'scp' - del self.src_push_dict['general']['metadata_path'] - self.write_config_file(self.push_config_file, self.src_push_dict) - self.assertRaises(tuf.FormatError, pushtoolslib.read_config_file, - self.push_config_file, 'push') - - # Test 'receive' type configuration with one of the keys missing. - del self.src_receive_dict['general']['repository_directory'] - self.write_config_file(self.receive_config_file, self.src_receive_dict) - self.assertRaises(tuf.FormatError, pushtoolslib.read_config_file, - self.receive_config_file, 'receive') - - - - - -# Run the unittests -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_quickstart.py b/tests/unit/deprecated/test_quickstart.py deleted file mode 100755 index e1bca8c4..00000000 --- a/tests/unit/deprecated/test_quickstart.py +++ /dev/null @@ -1,195 +0,0 @@ -""" - - test_quickstart.py - - - Konstantin Andrianov - - - September 6, 2012 - - - See LICENSE for licensing information. - - - To test quickstart.py for expected/unexpected input by the user, verifying - that all unexpected input is caught and an exception is raised. - - Given that all message prompts don't change - this will work pretty well - for running quickstart without having to manually enter input to prompts - every time you want to run quickstart. -""" - -import os -import shutil -import unittest -import logging - -import tuf -import tuf.log -import tuf.repo.quickstart as quickstart -import tuf.util -import tuf.tests.unittest_toolbox as unittest_toolbox - -logger = logging.getLogger('tuf.test_quickstart') -unit_tbox = unittest_toolbox.Modified_TestCase - - -logger.info('from test_quickstart') - -class TestQuickstart(unit_tbox): - def test_1_get_password(self): - - # SETUP - original_getpass = quickstart.getpass.getpass - - # A quick test of _get_password. - password = self.random_string() - def _mock_getpass(junk1, junk2, pw = password): - return pw - # Monkey patch getpass.getpass(). - quickstart.getpass.getpass = _mock_getpass - # Run _get_password(). - self.assertEqual(quickstart._get_password(), password) - - # RESTORE - quickstart.getpass.getpass = original_getpass - - - - def test_2_build_repository(self): - - # SETUP - original_prompt = quickstart._prompt - original_get_password = quickstart._get_password - - # Create the project directories. - repo_dir = os.path.join(os.getcwd(), 'repository') - keystore_dir = os.path.join(os.getcwd(), 'keystore') - client_dir = os.path.join(os.getcwd(), 'client') - - proj_files = self.make_temp_directory_with_data_files() - proj_dir = os.path.join(proj_files[0], 'targets') - - input_dict = {'expiration':'12/12/2020', - 'root':{'threshold':1, 'password':'pass'}, - 'targets':{'threshold':1, 'password':'pass'}, - 'release':{'threshold':1, 'password':'pass'}, - 'timestamp':{'threshold':1, 'password':'pass'}} - - - def _mock_prompt(message, confirm=False, input_parameters=input_dict): - if message.startswith('\nWhen would you like your '+ - '"root.txt" metadata to expire?'): - return input_parameters['expiration'] - for role in self.role_list: # role_list=['root', 'targets', ...] - if message.startswith('\nEnter the desired threshold '+ - 'for the role '+repr(role)): - return input_parameters[role]['threshold'] - elif message.startswith('Enter a password for '+repr(role)): - for threshold in range(input_parameters[role]['threshold']): - if message.endswith(repr(role)+' ('+str(threshold+1)+'): '): - return input_parameters[role]['password'] - print 'Cannot recognize message: '+message - - # Monkey patching quickstart's _prompt() and _get_password. - quickstart._prompt = _mock_prompt - quickstart._get_password = _mock_prompt - - - def _remove_repository_directories(repo_dir, keystore_dir, client_dir): - """ - quickstart.py creates the 'client', 'keystore', and 'repository' - directories in the current working directory. Remove these - directories after every quickstart.build_repository() call. - """ - - try: - shutil.rmtree(repo_dir) - shutil.rmtree(keystore_dir) - shutil.rmtree(client_dir) - except OSError, e: - pass - - - - # TESTS - - # TEST: various input parameters. - # Supplying bogus expiration. - input_dict['expiration'] = '5/8/2011' - self.assertRaises(tuf.RepositoryError, quickstart.build_repository, - proj_dir) - # Random string. - input_dict['expiration'] = self.random_string() - self.assertRaises(tuf.RepositoryError, quickstart.build_repository, - proj_dir) - _remove_repository_directories(repo_dir, keystore_dir, client_dir) - - # Restore expiration. - input_dict['expiration'] = '10/10/2020' - - # Supplying bogus 'root' threshold. Doing this for all roles slows - # the test significantly. - input_dict['root']['threshold'] = self.random_string() - self.assertRaises(tuf.RepositoryError, quickstart.build_repository, - proj_dir) - _remove_repository_directories(repo_dir, keystore_dir, client_dir) - - input_dict['root']['threshold'] = 0 - self.assertRaises(tuf.RepositoryError, quickstart.build_repository, - proj_dir) - _remove_repository_directories(repo_dir, keystore_dir, client_dir) - - # Restore keystore directory. - input_dict['root']['threshold'] = 1 - - - # TEST: normal case. - try: - quickstart.build_repository(proj_dir) - except Exception, e: - raise - - # Verify the existence of metadata, target, and keystore files. - meta_dir = os.path.join(repo_dir, 'metadata') - targets_dir = os.path.join(repo_dir, 'targets') - client_current_meta_dir = os.path.join(client_dir, 'metadata', 'current') - client_previous_meta_dir = os.path.join(client_dir, 'metadata', 'previous') - target_files = os.listdir(targets_dir) - - # Verify repository, keystore, metadata, and targets directories. - self.assertTrue(os.path.exists(repo_dir)) - self.assertTrue(os.path.exists(keystore_dir)) - self.assertTrue(os.path.exists(meta_dir)) - self.assertTrue(os.path.exists(targets_dir)) - self.assertTrue(os.path.exists(client_current_meta_dir)) - self.assertTrue(os.path.exists(client_previous_meta_dir)) - - # Verify that target_files exist. - self.assertTrue(target_files) - - for role in self.role_list: - meta_file = role+'.txt' - # Verify metadata file for a 'role'. - self.assertTrue(os.path.isfile(os.path.join(meta_dir, meta_file))) - # Get the metadata. - signable = tuf.util.load_json_file(os.path.join(meta_dir, meta_file)) - for signature in range(len(signable['signatures'])): - # Extract a keyid. - keyid = signable['signatures'][signature]['keyid'] - key_file = os.path.join(keystore_dir, keyid+'.key') - # Verify existence of a key for the keyid that belong to the 'role'. - self.assertTrue(os.path.isfile(key_file)) - - _remove_repository_directories(repo_dir, keystore_dir, client_dir) - - # RESTORE - quickstart._prompt = original_prompt - quickstart._get_password = original_get_password - - - -# Run the unit tests. -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_signercli.py b/tests/unit/deprecated/test_signercli.py deleted file mode 100755 index 297eee18..00000000 --- a/tests/unit/deprecated/test_signercli.py +++ /dev/null @@ -1,1573 +0,0 @@ -#!/usr/bin/env python - -""" - - test_signercli.py - - - Konstantin Andrianov - - - September 20, 2012 - - - See LICENSE for licensing information. - - - test_signercli.py provides collection of methods that tries to test all the - units (methods) of the module under test. - - unittest_toolbox module was created to provide additional testing tools for - tuf's modules. For more info see unittest_toolbox.py. - - - - Unittests must follow a specific structure i.e. independent methods should - be tested prior to dependent methods. More accurately: least dependent - methods are tested before most dependent methods. There is no reason to - rewrite or construct other methods that replicate already-tested methods - solely for testing purposes. This is possible because 'unittest.TestCase' - class guarantees the order of unit tests. So that, 'test_something_A' - method would be tested before 'test_something_B'. To ensure the structure - a number will be placed after 'test' and before methods name like so: - 'test_1_check_directory'. The number is a measure of dependence, where 1 - is less dependent than 2. - -""" - - -import os -import time -import logging -import unittest - -import tuf -import tuf.log -import tuf.formats -import tuf.util -import tuf.repo.keystore as keystore -import tuf.repo.signerlib as signerlib - -# Module to test: signercli.py -import tuf.repo.signercli as signercli - -# Helper module unittest_toolbox.py -import tuf.tests.unittest_toolbox as unittest_toolbox - -logger = logging.getLogger('tuf.test_signercli') - - - -class TestSignercli(unittest_toolbox.Modified_TestCase): - # SETUP - original_prompt = signercli._prompt - signercli._prompt = original_prompt - - original_get_metadata_directory = signercli._get_metadata_directory - signercli._get_metadata_directory = original_get_metadata_directory - - original_get_password = signercli._get_password - signercli._get_password = original_get_password - - # HELPER METHODS. - - # Generic patch for signerlib._prompt(). - def mock_prompt(self, output): - - # Method to patch signercli._prompt(). - def _mock_prompt(junk1, junk2, ret=output): - return ret - - # Patch signercli._prompt() - signercli._prompt = _mock_prompt - - - - # Patch signercli._get_metadata_directory() - def mock_get_metadata_directory(self, directory=None): - - # Create method to patch signercli._get_metadata_directory() - def _mock_get_meta_dir(directory=directory): - - # If directory was specified, return that directory. - if directory: - return directory - - # Else create a temporary directory and return it. - else: - return self.make_temp_directory() - - # Patch signercli._get_metadata_directory() - signercli._get_metadata_directory = _mock_get_meta_dir - - - - # This method patches signercli._prompt() that are called from - # make_role_metadata methods (e.g., tuf.signercli.make_root_metadata()). - def make_metadata_mock_prompts(self, targ_dir, conf_path, expiration): - def _mock_prompt(msg, junk): - if msg.startswith('\nInput may be a directory, directories, or'): - return targ_dir - elif msg.startswith('\nEnter the configuration file path'): - return conf_path - elif msg.startswith('\nCurrent time:'): - return expiration - else: - error_msg = ('Prompt: '+'\''+msg[1:]+'\''+ - ' did not match any predefined mock prompts.') - self.fail(error_msg) - - # Patch signercli._prompt(). - signercli._prompt = _mock_prompt - - - - # This mock method can be easily modified, by altering unittest_toolbox's - # dictionaries. For instance, if you want to modify password for certain - # keyid just save the existing 'self.rsa_passwords[keyid]' and set it - # to some other value like self.random_string(), after the test reassign - # the saved value back to 'self.rsa_passwords[keyid]'. - def get_passwords(self): - # Mock '_get_password' method. - def _mock_get_password(msg): - for role in self.role_list: - if msg.startswith('\nEnter the password for the '+role): - for keyid in self.top_level_role_info[role]['keyids']: - if msg.endswith(keyid+'): '): - return self.rsa_passwords[keyid] - error_msg = ('Prompt: '+'\''+msg+'\''+ - ' did not match any predefined mock prompts.') - raise tuf.Error(error_msg) - - # Monkey patch '_prompt'. - signercli._get_password = _mock_get_password - - - - - - # UNIT TESTS. - # If a unit test starts with test_# followed by two underscores, - # (test_#__method) this means that it's an internal method of signercli. - # For instance the unit test for signercli._get_password() would - # look like this: test_1__get_password, whereas unit test for - # signercli.change_password would look like this: - # test_3_change_password(). - - def test_1__check_directory(self): - - # SETUP - directory = self.make_temp_directory() - no_such_dir = self.random_path() - - - # TESTS - # Test: normal case. - self.assertEqual(signercli._check_directory(directory), directory) - - # Test: invalid directory. - self.assertRaises(tuf.RepositoryError, signercli._check_directory, - no_such_dir) - - # Test: invalid directory type. - self.assertRaises(tuf.RepositoryError, signercli._check_directory, - [no_such_dir]) - self.assertRaises(tuf.RepositoryError, signercli._check_directory, - 1234) - self.assertRaises(tuf.RepositoryError, signercli._check_directory, - {'directory':no_such_dir}) - - - - - - def test_1__get_password(self): - - # SETUP - original_getpass = signercli.getpass.getpass - - password = self.random_string() - def _mock_getpass(junk1, junk2, pw=password): - return pw - - # Patch getpass.getpass(). - signercli.getpass.getpass = _mock_getpass - - - # Test: normal case. - self.assertEqual(signercli._get_password(), password) - - # RESTORE - signercli.getpass.getpass = original_getpass - - - - - def test_2__get_metadata_directory(self): - - # SETUP - original_prompt = signercli._prompt - - meta_directory = self.make_temp_directory() - self.mock_prompt(meta_directory) - - - # TESTS - self.assertEqual(signercli._get_metadata_directory(), meta_directory) - self.assertTrue(os.path.exists(signercli._get_metadata_directory())) - self.mock_prompt(self.random_string()) - self.assertRaises(tuf.RepositoryError, signercli._get_metadata_directory) - self.mock_prompt([self.random_string()]) - self.assertRaises(tuf.RepositoryError, signercli._get_metadata_directory) - - # RESTORE - signercli._prompt = original_prompt - - - - - def test_4__list_keyids(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating root and target metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # The 'root.txt' and 'targets.txt' metadata files are - # needed for _list_keyids() to determine the roles - # associated with each keyid. - keystore_dir = self.create_temp_keystore_directory() - repo_dir = self.make_temp_directory() - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create the metadata directory needed by _list_keyids(). - meta_dir = self.make_temp_directory() - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._prompt(). - self.mock_prompt(config_filepath) - - # Patch signercli._get_password(). - self.get_passwords() - - # Create the root metadata file that will be loaded by _list_keyids() - # to extract the keyids for the top-level roles. - signercli.make_root_metadata(keystore_dir) - - # Create a directory containing target files. - targets_dir, targets_paths =\ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Create the target metadata file that will be loaded by _list_keyids() - # to extract the keyids for all the targets roles. - signercli.make_targets_metadata(keystore_dir) - - - # TESTS - # Test: normal case. - signercli._list_keyids(keystore_dir, meta_dir) - - # Test: Improperly formatted 'root.txt' file. - root_filename = os.path.join(meta_dir, 'root.txt') - root_signable = tuf.util.load_json_file(root_filename) - saved_roles = root_signable['signed']['roles'] - del root_signable['signed']['roles'] - tuf.repo.signerlib.write_metadata_file(root_signable, root_filename) - - self.assertRaises(tuf.RepositoryError, - signercli._list_keyids, keystore_dir, meta_dir) - - # Restore the properly formatted 'root.txt' file. - root_signable['signed']['roles'] = saved_roles - tuf.repo.signerlib.write_metadata_file(root_signable, root_filename) - - # Test: Improperly formatted 'targets.txt' file. - targets_filename = os.path.join(meta_dir, 'targets.txt') - targets_signable = tuf.util.load_json_file(targets_filename) - saved_targets = targets_signable['signed']['targets'] - del targets_signable['signed']['targets'] - tuf.repo.signerlib.write_metadata_file(targets_signable, targets_filename) - - self.assertRaises(tuf.RepositoryError, - signercli._list_keyids, keystore_dir, meta_dir) - - # Restore the properly formatted 'targets.txt' file. - targets_signable['signed']['targets'] = saved_targets - tuf.repo.signerlib.write_metadata_file(targets_signable, targets_filename) - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - def test_2__get_keyids(self): - - # SETUP - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Create a temp keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # List of keyids including keyword 'quit'. - keyids = ['quit'] + self.rsa_keyids - - # Patching signercli._prompt() - def _mock_prompt(msg, junk): - - # Pop 'keyids' everytime signercli._prompt() is called. - keyid = keyids.pop() - if keyid != 'quit': - get_password(keyid) - return keyid - - signercli._prompt = _mock_prompt - - # Patching signercli._get_password(). - def get_password(keyid): - password = self.rsa_passwords[keyid] - def _mock_get_password(msg): - return password - - signercli._get_password = _mock_get_password - - - # TESTS - # Test: normal case. - loaded_keyids = signercli._get_keyids(keystore_dir) - self.assertTrue(tuf.formats.KEYIDS_SCHEMA.matches(loaded_keyids)) - - # Check if all the keysids were loaded. - for keyid in self.rsa_keyids: - if keyid not in loaded_keyids: - msg = 'Could not load the keyid: '+repr(keyid) - self.fail(msg) - - # Test: invalid password. - keyids = ['quit', self.rsa_keyids[0]] - saved_pw = self.rsa_passwords[self.rsa_keyids[0]] - - # Invalid password - self.rsa_passwords[self.rsa_keyids[0]] = self.random_string() - self.assertEqual(signercli._get_keyids(keystore_dir), []) - - # Restore the password. - self.rsa_passwords[self.rsa_keyids[0]] = saved_pw - - # Test: invalid keyid. - keyid = self.random_string() - keyids = ['quit', keyid] - - # Create an invalid entry in the passwords dictionary. - self.rsa_passwords[keyid] = self.random_string() - self.assertEqual(signercli._get_keyids(keystore_dir), []) - - # Restore passwords dictionary. - del self.rsa_passwords[keyid] - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - - - - def test_2__get_all_config_keyids(self): - - # SETUP - original_get_password = signercli._get_password - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build the config file needed by '_get_all_config_keyids. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # 'sample_keyid' used to test invalid keyid. - sample_keyid = self.rsa_keyids[0] - - # Patch signercli._get_password() - self.get_passwords() - - - # TESTS - # Test: an incorrect password. - saved_pw = self.rsa_passwords[sample_keyid] - self.rsa_passwords[sample_keyid] = self.random_string() - self.assertRaises(tuf.Error, signercli._get_all_config_keyids, - config_filepath, keystore_dir) - - # Restore the password. - self.rsa_passwords[sample_keyid] = saved_pw - - # Test: missing top-level role in the config file. - # Clear keystore's dictionaries. - keystore.clear_keystore() - - # Remove a role from 'top_level_role_info' which is used to construct - # config file. - targets_holder = self.top_level_role_info['targets'] - del self.top_level_role_info['targets'] - - # Build config file without 'targets' role. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - self.assertRaises(tuf.Error, signercli._get_all_config_keyids, - config_filepath, keystore_dir) - - # Rebuild config file and 'top_level_role_info'. - self.top_level_role_info['targets'] = targets_holder - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Test: non-existing config file path. - keystore.clear_keystore() - self.assertRaises(tuf.Error, signercli._get_all_config_keyids, - self.random_path(), keystore_dir) - - # Test: normal case. - keystore.clear_keystore() - signercli._get_all_config_keyids(config_filepath, keystore_dir) - - # RESTORE - signercli._get_password = original_get_password - - - - - def test_2__get_role_config_keyids(self): - - # SETUP - original_get_password = signercli._get_password - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - # Create a temp keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Patch '_get_password' method. - self.get_passwords() - - # TESTS - for role in self.role_list: - # Test: normal cases. - keystore.clear_keystore() - signercli._get_role_config_keyids(config_filepath, keystore_dir, role) - - # Test: incorrect passwords. - keystore.clear_keystore() - role_keyids = self.top_level_role_info[role]['keyids'] - for keyid in role_keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.Error, signercli._get_role_config_keyids, - config_filepath, keystore_dir, role) - - # Restore the password. - self.rsa_passwords[keyid] = saved_pw - - # Test: non-existing config file path. - keystore.clear_keystore() - self.assertRaises(tuf.Error, signercli._get_role_config_keyids, - self.random_path(), keystore_dir, 'release') - - # Test: non-existing role. - keystore.clear_keystore() - self.assertRaises(tuf.Error, signercli._get_role_config_keyids, - config_filepath, keystore_dir, 'no_such_role') - - # RESTORE - signercli._get_password = original_get_password - - - - - def test_1__sign_and_write_metadata(self): - - # SETUP - # Role to test. - role = 'root' - - # Create temp directory. - temp_dir = self.make_temp_directory() - - # File name. - filename = os.path.join(temp_dir, role+'.txt') - - # Role's keyids. - keyids = self.top_level_role_info[role]['keyids'] - - # Create a temp keystore directory. - keystore_dir =\ - self.create_temp_keystore_directory(keystore_dicts=True) - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create role's metadata. - signable_meta = signerlib.generate_root_metadata(config_filepath, 8) - - - # TESTS - # Test: normal case. - signercli._sign_and_write_metadata(signable_meta, keyids, filename) - - # Verify that the root meta file was created. - self.assertTrue(os.path.exists(filename)) - - Errors = (tuf.Error, tuf.FormatError) - - # Test: invalid metadata. - self.assertRaises(Errors, signercli._sign_and_write_metadata, - self.random_string(), keyids, filename) - - # Test: invalid keyids - invalid_keyids = self.random_string() - self.assertRaises(Errors, signercli._sign_and_write_metadata, - signable_meta, invalid_keyids, filename) - - # Test: invalid filename - self.assertRaises(Errors, signercli._sign_and_write_metadata, - signable_meta, invalid_keyids, True) - - - - - def test_4_change_password(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating root and target metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # Create keystore and repo directories. - keystore_dir = self.create_temp_keystore_directory() - repo_dir = self.make_temp_directory() - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp metadata directory. - meta_dir = self.make_temp_directory() - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._prompt(). - self.mock_prompt(config_filepath) - - # Patch '_get_password' method. - self.get_passwords() - - signercli.make_root_metadata(keystore_dir) - - # Create a directory containing target files. - targets_dir, targets_paths =\ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - signercli.make_targets_metadata(keystore_dir) - - test_keyid = self.rsa_keyids[0] - self.mock_prompt(test_keyid) - - # Specify old password and create a new password. - old_password = self.rsa_passwords[test_keyid] - new_password = self.random_string() - - # Mock method for signercli._get_password() - def _mock_get_password(msg, confirm=False, old_pw=old_password, - new_pw=new_password): - if msg.startswith('\nEnter the old password for the keyid: '): - return old_pw - else: - return new_pw - - # Patch signercli._get_password. - signercli._get_password = _mock_get_password - - - # TESTS - # Test: normal case. - # Verify that the derived key is modified. A new salt is generated, so - # we cannot predict or verify a specific derived key corresponding for - # the new password. Save the derived key for 'test_keyid' and check that - # is updated. - old_derived_key = keystore._derived_keys[test_keyid] - signercli.change_password(keystore_dir) - - # Verify password change. - self.assertNotEqual(keystore._derived_keys[test_keyid], old_derived_key) - - # Test: non-existing keyid. - keystore.clear_keystore() - self.mock_prompt(self.random_string(15)) - self.assertRaises(tuf.RepositoryError, signercli.change_password, - keystore_dir) - - # Restore the prompt input to existing keyid. - self.mock_prompt(test_keyid) - - # Test: non-existing old password. - keystore.clear_keystore() - old_password = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.change_password, - keystore_dir) - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_2_generate_rsa_key(self): - - # SETUP - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Method to patch signercli._get_password() - def _mock_get_password(junk, confirm=False): - return self.random_string() - - # Patch signercli._get_password() - signercli._get_password = _mock_get_password - - # Create a temp keystore directory. - keystore_dir = self.make_temp_directory() - - - # TESTS - # Test: invalid rsa bits. - self.mock_prompt(1024) - self.assertRaises(tuf.RepositoryError, signercli.generate_rsa_key, - keystore_dir) - # Input appropriate number of rsa bits. - self.mock_prompt(3072) - - # Test: normal case. - signercli.generate_rsa_key(keystore_dir) - - # Was the key file added to the directory? - self.assertTrue(os.listdir(keystore_dir)) - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - - - - - def test_4_dump_key(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating root and target metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # Create keystore and repo directories. - keystore_dir = self.create_temp_keystore_directory() - repo_dir = self.make_temp_directory() - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp metadata directory. - meta_dir = self.make_temp_directory() - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). - self.get_passwords() - - # Patch signercli._prompt(). - self.mock_prompt(config_filepath) - - signercli.make_root_metadata(keystore_dir) - - # Create a directory containing target files. - targets_dir, targets_paths =\ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - signercli.make_targets_metadata(keystore_dir) - - - keyid = self.rsa_keyids[0] - password = self.rsa_passwords[keyid] - show_priv = 'private' - - - # Mock method for signercli._get_password(). - def _mock_get_password(msg): - return password - - # Mock method for signercli._prompt(). - def _mock_prompt(msg, junk): - if msg.startswith('\nEnter the keyid'): - return keyid - else: - return show_priv - - # Patch signercli._get_password(). - signercli._get_password = _mock_get_password - - # Patch signercli._prompt(). - signercli._prompt = _mock_prompt - - - # TESTS - # Test: normal case. - signercli.dump_key(keystore_dir) - - # Test: incorrect password. - saved_pw = password - password = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.dump_key, - keystore_dir) - - # Restore the correct password. - password = saved_pw - - # Test: non-existing keyid. - keyid = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.dump_key, - keystore_dir) - keyid = self.rsa_keyids[0] - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_3_make_root_metadata(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp metadata directory. - meta_dir = self.make_temp_directory() - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._prompt(). - self.mock_prompt(config_filepath) - - # Patch signercli._get_password(). - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - - # TESTS - # Test: normal case. - signercli.make_root_metadata(keystore_dir) - - # Verify that the root metadata path was created. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - - # Test: invalid config path. - # Clear keystore's dictionaries. - keystore.clear_keystore() - - # Supply a non-existing path to signercli._prompt(). - self.mock_prompt(self.random_path()) - self.assertRaises(tuf.RepositoryError, signercli.make_root_metadata, - keystore_dir) - - # Re-patch signercli._prompt() with valid config path. - self.mock_prompt(config_filepath) - - # Test: incorrect 'root' passwords. - # Clear keystore's dictionaries. - keystore.clear_keystore() - keyids = self.top_level_role_info['root']['keyids'] - for keyid in keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.make_root_metadata, - keystore_dir) - self.rsa_passwords[keyid] = saved_pw - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_3_make_targets_metadata(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating target metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # Create a temp repository and metadata directories. - repo_dir = self.make_temp_directory() - meta_dir = self.make_temp_directory(directory=repo_dir) - - # Create a directory containing target files. - targets_dir, targets_paths =\ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Patch signercli._get_metadata_directory() - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). Used in _get_role_config_keyids() - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - - # TESTS - # Test: normal case. - signercli.make_targets_metadata(keystore_dir) - - # Verify that targets metadata file was created. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - - # Test: invalid targets path. - # Clear keystore's dictionaries. - keystore.clear_keystore() - - # Supply a non-existing targets directory. - """ - self.make_metadata_mock_prompts(targ_dir=self.random_path(), - conf_path=config_filepath, - expiration=expiration_date) - self.assertRaises(tuf.RepositoryError, signercli.make_targets_metadata, - keystore_dir) - """ - - # Restore the targets directory. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Test: invalid config path. - # Clear keystore's dictionaries. - keystore.clear_keystore() - - # Supply a non-existing config path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=self.random_path(), - expiration=expiration_date) - self.assertRaises(tuf.RepositoryError, signercli.make_targets_metadata, - keystore_dir) - - # Restore the config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Test: invalid expiration date. - # Clear keystore's dictionaries - keystore.clear_keystore() - - # Supply invalid expiration date. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=self.random_string()) - self.assertRaises(tuf.RepositoryError, signercli.make_targets_metadata, - keystore_dir) - - # Restore the config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Test: incorrect 'targets' passwords. - # Clear keystore's dictionaries. - keystore.clear_keystore() - keyids = self.top_level_role_info['targets']['keyids'] - for keyid in keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.make_targets_metadata, - keystore_dir) - self.rsa_passwords[keyid] = saved_pw - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_4_make_release_metadata(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating release metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # In order to build release metadata file (release.txt), - # root and targets metadata files (root.txt, targets.txt) - # must exist in the metadata directory. - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp repository and metadata directories. - repo_dir = self.make_temp_directory() - meta_dir = self.make_temp_directory(repo_dir) - - # Create a directory containing target files. - targets_dir, targets_paths = \ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). Used in _get_role_config_keyids(). - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - - # TESTS - # Test: no root.txt in the metadata dir. - signercli.make_targets_metadata(keystore_dir) - - # Verify that 'tuf.RepositoryError' is raised due to a missing root.txt. - keystore.clear_keystore() - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - self.assertRaises(tuf.RepositoryError, signercli.make_release_metadata, - keystore_dir) - os.remove(os.path.join(meta_dir,'targets.txt')) - keystore.clear_keystore() - - # Test: no targets.txt in the metadatadir. - signercli.make_root_metadata(keystore_dir) - keystore.clear_keystore() - - # Verify that 'tuf.RepositoryError' is raised due to a missing targets.txt. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - self.assertRaises(tuf.RepositoryError, signercli.make_release_metadata, - keystore_dir) - os.remove(os.path.join(meta_dir,'root.txt')) - keystore.clear_keystore() - - # Test: normal case. - signercli.make_root_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_targets_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_release_metadata(keystore_dir) - keystore.clear_keystore() - - # Verify if the root, targets and release meta files were created. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'release.txt'))) - - # Test: invalid config path. - # Supply a non-existing config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=self.random_path(), expiration=expiration_date) - self.assertRaises(tuf.RepositoryError, signercli.make_release_metadata, - keystore_dir) - - # Restore the config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, expiration=expiration_date) - - # Test: incorrect 'release' passwords. - # Clear keystore's dictionaries. - keystore.clear_keystore() - keyids = self.top_level_role_info['release']['keyids'] - for keyid in keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.make_release_metadata, - keystore_dir) - self.rsa_passwords[keyid] = saved_pw - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_5_make_timestamp_metadata(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating the top-level metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # In order to build timestamp metadata file (timestamp.txt), - # root, targets and release metadata files (root.txt, targets.txt - # release.txt) must exist in the metadata directory. - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp repository and metadata directories. - repo_dir = self.make_temp_directory() - meta_dir = self.make_temp_directory(repo_dir) - - # Create a directory containing target files. - targets_dir, targets_paths = \ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). Used in _get_role_config_keyids(). - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - - # TESTS - # Test: no root.txt in the metadata dir. - signercli.make_targets_metadata(keystore_dir) - - # Verify if the targets metadata file was created. - keystore.clear_keystore() - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - self.assertRaises(tuf.RepositoryError, signercli.make_timestamp_metadata, - keystore_dir) - os.remove(os.path.join(meta_dir,'targets.txt')) - keystore.clear_keystore() - - # Test: no targets.txt in the metadatadir. - signercli.make_root_metadata(keystore_dir) - - # Verify if the root metadata file was created. - keystore.clear_keystore() - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - self.assertRaises(tuf.RepositoryError, signercli.make_timestamp_metadata, - keystore_dir) - os.remove(os.path.join(meta_dir,'root.txt')) - keystore.clear_keystore() - - # Test: no release.txt in the metadatadir. - signercli.make_root_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_targets_metadata(keystore_dir) - keystore.clear_keystore() - - # Verify that 'tuf.Repository' is raised due to a missing release.txt. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - self.assertRaises(tuf.RepositoryError, signercli.make_timestamp_metadata, - keystore_dir) - os.remove(os.path.join(meta_dir,'root.txt')) - os.remove(os.path.join(meta_dir,'targets.txt')) - keystore.clear_keystore() - - # Test: normal case. - signercli.make_root_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_targets_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_release_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_timestamp_metadata(keystore_dir) - keystore.clear_keystore() - - # Verify if the root, targets and release metadata files were created. - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'targets.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'release.txt'))) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'timestamp.txt'))) - - # Test: invalid config path. - # Supply a non-existing config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=self.random_path(), - expiration=expiration_date) - self.assertRaises(tuf.RepositoryError, - signercli.make_release_metadata, keystore_dir) - - # Restore the config file path. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Test: incorrect 'release' passwords. - - # Clear keystore's dictionaries. - keystore.clear_keystore() - - keyids = self.top_level_role_info['release']['keyids'] - for keyid in keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.RepositoryError, - signercli.make_release_metadata, keystore_dir) - self.rsa_passwords[keyid] = saved_pw - - # RESTORE - signercli._get_password = original_get_password - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_6_sign_metadata_file(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating the top-level metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # To test this method, an RSA key will be created with - # a password in addition to the existing RSA keys. - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Create a temp repository and metadata directories. - repo_dir = self.make_temp_directory() - meta_dir = self.make_temp_directory(repo_dir) - - # Create a directory containing target files. - targets_dir, targets_paths = \ - self.make_temp_directory_with_data_files(directory=repo_dir) - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). Used in _get_role_config_keyids(). - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Mock method for signercli._prompt(). - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # Create metadata files. - signercli.make_root_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_targets_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_release_metadata(keystore_dir) - keystore.clear_keystore() - signercli.make_timestamp_metadata(keystore_dir) - keystore.clear_keystore() - - # Verify if the root, targets and release meta files were created. - root_meta_filepath = os.path.join(meta_dir, 'root.txt') - targets_meta_filepath = os.path.join(meta_dir, 'targets.txt') - release_meta_filepath = os.path.join(meta_dir, 'release.txt') - timestamp_meta_filepath = os.path.join(meta_dir, 'timestamp.txt') - - self.assertTrue(os.path.exists(root_meta_filepath)) - self.assertTrue(os.path.exists(targets_meta_filepath)) - self.assertTrue(os.path.exists(release_meta_filepath)) - self.assertTrue(os.path.exists(timestamp_meta_filepath)) - - - # Create a new RSA key, indicate metadata filename. - new_keyid = self.generate_rsakey() - meta_filename = targets_meta_filepath - - # Create keystore directory. New key is untouched. - keystore_dir = self.create_temp_keystore_directory(keystore_dicts=True) - - # List of keyids to be returned by _get_keyids() - signing_keyids = [] - - # Method to patch signercli._get_keyids() - def _mock_get_keyids(junk): - return signing_keyids - - # Method to patch signercli._prompt(). - def _mock_prompt(msg, junk): - return meta_filename - - # Patch signercli._get_keyids() - signercli._get_keyids = _mock_get_keyids - - # Patch signercli._prompt(). - signercli._prompt = _mock_prompt - - - # TESTS - # Test: no loaded keyids. - self.assertRaises(tuf.RepositoryError, - signercli.sign_metadata_file, keystore_dir) - - # Load new keyid. - signing_keyids = [new_keyid] - - # Test: normal case. - signercli.sign_metadata_file(keystore_dir) - - # Verify the change. - self.assertTrue(os.path.exists(targets_meta_filepath)) - - # Load targets metadata from the file ('targets.txt'). - targets_metadata = tuf.util.load_json_file(targets_meta_filepath) - keyid_exists = False - for signature in targets_metadata['signatures']: - if new_keyid == signature['keyid']: - keyid_exists = True - break - - self.assertTrue(keyid_exists) - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - - - - def test_7_make_delegation(self): - - # SETUP - original_get_metadata_directory = signercli._get_metadata_directory - original_prompt = signercli._prompt - original_get_password = signercli._get_password - - # Creating the top-level metadata requires an expiration date to be set. - # Expiration date set to expires 100 seconds from the current time. - expiration_date = tuf.formats.format_time(time.time()+100) - expiration_date = expiration_date[0:expiration_date.rfind(' UTC')] - - # Create a temp repository and metadata directories. - repo_dir = self.make_temp_directory() - meta_dir = self.make_temp_directory(directory=repo_dir) - - # Create targets directories. - targets_dir, targets_paths =\ - self.make_temp_directory_with_data_files(directory=repo_dir) - delegated_targets_dir = os.path.join(targets_dir,'targets', - 'delegated_level1') - - # Assign parent role and name of the delegated role. - parent_role = 'targets' - delegated_role = 'delegated_role_1' - - # Create couple new RSA keys for delegation levels 1 and 2. - new_keyid_1 = self.generate_rsakey() - new_keyid_2 = self.generate_rsakey() - - # Create temp directory for config file. - config_dir = self.make_temp_directory() - - # Build a config file. - config_filepath = signerlib.build_config_file(config_dir, 365, - self.top_level_role_info) - - # Patch signercli._get_metadata_directory(). - self.mock_get_metadata_directory(directory=meta_dir) - - # Patch signercli._get_password(). Get passwords for parent's keyids. - self.get_passwords() - - # Create keystore directory. - keystore_dir = self.create_temp_keystore_directory() - - # Mock method for signercli._prompt() to generate targets.txt file. - self.make_metadata_mock_prompts(targ_dir=targets_dir, - conf_path=config_filepath, - expiration=expiration_date) - - # List of keyids to be returned by _get_keyids() - signing_keyids = [new_keyid_1] - - # Load keystore. - load_keystore = keystore.load_keystore_from_keyfiles - - # Build the root metadata file (root.txt). - signercli.make_root_metadata(keystore_dir) - - # Build targets metadata file (targets.txt). - signercli.make_targets_metadata(keystore_dir) - - # Clear kestore's dictionaries. - keystore.clear_keystore() - - # Mock method for signercli._prompt(). - def _mock_prompt(msg, junk): - if msg.startswith('\nThe paths entered'): - return delegated_targets_dir - elif msg.startswith('\nChoose and enter the parent'): - return parent_role - elif msg.startswith('\nEnter the delegated role\'s name: '): - return delegated_role - elif msg.startswith('\nCurrent time:'): - return expiration_date - else: - error_msg = ('Prompt: '+'\''+msg+'\''+ - ' did not match any predefined mock prompts.') - self.fail(error_msg) - - # Mock method for signercli._get_password(). - def _mock_get_password(msg): - for keyid in self.rsa_keyids: - if msg.endswith('('+keyid+'): '): - return self.rsa_passwords[keyid] - - # Method to patch signercli._get_keyids() - def _mock_get_keyids(junk): - if signing_keyids: - for keyid in signing_keyids: - password = self.rsa_passwords[keyid] - # Load the keyfile. - load_keystore(keystore_dir, [keyid], [password]) - return signing_keyids - - # Patch signercli._prompt(). - signercli._prompt = _mock_prompt - - # Patch signercli._get_password(). - signercli._get_password = _mock_get_password - - # Patch signercli._get_keyids(). - signercli._get_keyids = _mock_get_keyids - - - # TESTS - # Test: invalid parent role. - # Assign a non-existing parent role. - parent_role = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.make_delegation, - keystore_dir) - - # Restore parent role. - parent_role = 'targets' - - # Test: invalid password(s) for parent's keyids. - keystore.clear_keystore() - parent_keyids = self.top_level_role_info[parent_role]['keyids'] - for keyid in parent_keyids: - saved_pw = self.rsa_passwords[keyid] - self.rsa_passwords[keyid] = self.random_string() - self.assertRaises(tuf.RepositoryError, signercli.make_delegation, - keystore_dir) - self.rsa_passwords[keyid] = saved_pw - - # Test: delegated_keyids == 0. - keystore.clear_keystore() - - # Load 0 keyids (== 0). - signing_keyids = [] - self.assertRaises(tuf.RepositoryError, signercli.make_delegation, - keystore_dir) - keystore.clear_keystore() - - # Restore signing_keyids (== 1). - signing_keyids = [new_keyid_1] - - # Test: normal case 1. - # Testing first level delegation. - signercli.make_delegation(keystore_dir) - - # Verify delegated metadata file exists. - delegated_meta_file = os.path.join(meta_dir, parent_role, - delegated_role+'.txt') - self.assertTrue(os.path.exists(delegated_meta_file)) - - # Test: normal case 2. - # Testing second level delegation. - keystore.clear_keystore() - - # Make necessary adjustments for the test. - signing_keyids = [new_keyid_2] - delegated_targets_dir = os.path.join(delegated_targets_dir, - 'delegated_level2') - parent_role = os.path.join(parent_role, delegated_role) - delegated_role = 'delegated_role_2' - - signercli.make_delegation(keystore_dir) - - # Verify delegated metadata file exists. - delegated_meta_file = os.path.join(meta_dir, parent_role, - delegated_role+'.txt') - self.assertTrue(os.path.exists(delegated_meta_file)) - - # Test: normal case 3. - # Testing delegated_keyids > 1. - # Ensure make_delegation() sets 'threshold' = 2 for the delegated role. - keystore.clear_keystore() - - # Populate 'signing_keyids' with multiple keys, so the - # the delegated metadata is set to a threshold > 1. - signing_keyids = [new_keyid_1, new_keyid_2] - parent_role = 'targets' - delegated_role = 'delegated_role_1' - - signercli.make_delegation(keystore_dir) - - # Verify delegated metadata file exists. - delegated_meta_file = os.path.join(meta_dir, parent_role, - delegated_role+'.txt') - self.assertTrue(os.path.exists(delegated_meta_file)) - - # Verify the threshold value of the delegated metadata file - # by inspecting the parent role's 'delegations' field. - parent_role_file = os.path.join(meta_dir, parent_role+'.txt') - signable = signerlib.read_metadata_file(parent_role_file) - delegated_rolename = parent_role+'/'+delegated_role - - roles = signable['signed']['delegations']['roles'] - role_index = signerlib.find_delegated_role(roles, delegated_rolename) - self.assertIsNotNone(role_index) - role = roles[role_index] - - threshold = role['threshold'] - self.assertTrue(threshold == 2) - - # RESTORE - signercli._get_password = original_get_password - signercli._prompt = original_prompt - signercli._get_metadata_directory = original_get_metadata_directory - - -def setUpModule(): - # setUpModule() is called before any test cases run. - # Populating 'rsa_keystore' and 'rsa_passwords' dictionaries. - # We will need them when creating keystore directories. - unittest_toolbox.Modified_TestCase.bind_keys_to_roles() - - -def tearDownModule(): - # tearDownModule() is called after all the test cases have run. - unittest_toolbox.Modified_TestCase.clear_toolbox() - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_signerlib.py b/tests/unit/deprecated/test_signerlib.py deleted file mode 100755 index a09f78fa..00000000 --- a/tests/unit/deprecated/test_signerlib.py +++ /dev/null @@ -1,986 +0,0 @@ -""" - - test_signerlib.py - - - Konstantin Andrianov - - - September 6, 2012 - - - See LICENSE for licensing information. - - - Test_signerlib.py provides collection of methods that tries to test all the - units (methods) of the module under test. - - This unittest module requires setting of rsa keys, keyids and such. - There is a method in unittest_toolbox.Modified_TestCase class - 'bind_keys_to_roles()'. This method will set dictionaries - 'top_level_role_info' and 'rsa_keystore'. - - 'top_level_role_info' corresponds to ROLEDICT_SCHEMA and it looks like this: - {'rolename': {'keyids': ['34345df32093bd12...'], 'threshold': 1}, ...} - - 'rsa_keystore' looks like this: {keyid : { -- RSAKEY_SCHEMA --}, ... } or - {keyid : {'keytype': 'rsa', 'keyid': keyid, - 'keyval': {'public': 'PUBLIC KEY', - 'private ': 'PRIVATE KEY'}}, ... } - - unittest_toolbox module was created to provide additional testing tools for - tuf's modules. For more info see unittest_toolbox.py. - - - - Unittests must follow a specific structure i.e. independent methods should - be tested prior to dependent methods. More accurately: least dependent methods - are tested before most dependent methods. There is no reason to rewrite or - construct other methods that replicate already-tested methods solely for - testing purposes. This is possible because 'unittest.TestCase' class guarantees - the order of unit tests. So that, 'test_something_A' method would be tested - before 'test_something_B'. To ensure the structure a number will be placed - after 'test' and before methods name like so 'test_1_check_directory'. The - number is sort of a measure of dependence, where 1 is less dependent than 2. - -""" - -import os -import tempfile -import filecmp -import shutil -import ConfigParser -import gzip -import logging -import unittest - -import tuf -import tuf.log -import tuf.util -import tuf.formats as formats -import tuf.repo.signerlib as signerlib -import tuf.repo.keystore -import tuf.tests.unittest_toolbox as unittest_toolbox - - -logger = logging.getLogger('tuf.test_signerlib') - -# 'unittest_toolbox.Modified_TestCase' is too long, I'll set it to 'unit_tbox'. -unit_tbox = unittest_toolbox.Modified_TestCase - - - -class TestSignerlib(unit_tbox): - - def setUp(self): - unit_tbox.setUp(self) - - - - - def tearDown(self): - unit_tbox.tearDown(self) - - - - - # Test methods. - def test_1_get_metadata_filenames(self): - - # SETUP - metadata_dir = self.make_temp_directory() - empty_dir = '' - - def _get_metadata_filenames(test_metadata_dir): - filenames = signerlib.get_metadata_filenames(test_metadata_dir) - if test_metadata_dir is None: - test_metadata_dir = '.' - - # Check if a dictionary instance with 4 mappings is returned. - self.assertTrue(isinstance(filenames, dict)) - self.assertFalse(not filenames, 'Empty dictionary returned.') - self.assertEqual(len(filenames), 4) - - # Check if all the keys in 'filenames' dictionary - # correspond to 'role_list' items i.e. all top level - # roles are include in the 'filenames' with their - # appropriate paths as values. - for role in unit_tbox.role_list: - value_at_role = os.path.join(test_metadata_dir, role+'.txt') - self.assertTrue(role in filenames) - self.assertEqual(filenames[role], value_at_role) - - # Run _get_metadata_filenames(arg) trying different arguments. - self.assertRaises(tuf.FormatError, signerlib.get_metadata_filenames, 123) - _get_metadata_filenames(metadata_dir) - _get_metadata_filenames(empty_dir) - - - - - - def test_1_get_metadata_file_info(self): - - # SETUP - temp_file_path = self.make_temp_data_file() - rand_str = self.random_string() - - - # TESTS - # Test: improper arguments that should raise exceptions. - self.assertRaises(tuf.Error, signerlib.get_metadata_file_info, '') - self.assertRaises(tuf.FormatError, signerlib.get_metadata_file_info, - 123) - self.assertRaises(tuf.FormatError, signerlib.get_metadata_file_info, - {rand_str: rand_str}) - self.assertRaises(tuf.FormatError, signerlib.get_metadata_file_info, - [rand_str, rand_str]) - - # Make sure the format return by 'get_metadata_file_info' - # matches tuf.formats.FILEINFO_SCHEMA. - file_info = signerlib.get_metadata_file_info(temp_file_path) - self.assertTrue(formats.FILEINFO_SCHEMA.matches(file_info)) - - - - - - def test_1_generate_and_save_rsa_key(self): - """ - generate_and_save_rsa_key() is independent from all the other methods in - signerlib. In order to test this method all we need is to create a temp - directory and a sample password. - """ - - # SETUP - keystore_dir = self.make_temp_directory() - password = self.random_string() - - - # TESTS - # Test: Run generate_and_save_rsa_key(). - rsakey = signerlib.generate_and_save_rsa_key(keystore_dir, password) - self.assertTrue(formats.RSAKEY_SCHEMA.matches(rsakey)) - - # Test: Check if rsa key file was created. - key_path = os.path.join(keystore_dir, rsakey['keyid']+'.key') - self.assertTrue(os.path.exists(key_path)) - - - - - - def test_1_read_config_file(self): - """ - A short test of 'read_config_file' method. Using a tuple - that contains config dictionary and a config file containing - the same dictionary, test if 'read_config_file' returns a - dictionary corresponding to the supplied config dictionary when - config file is passes. - """ - - # SETUP - # 'base_config' is a tuple containing a config file path and - # a corresponding config dictionary. Note, make sure appropriate - # suffix is set. In our case it will be 'signerlib.CONFIG_FILENAME'. - base_config = self.make_temp_config_file(suffix=signerlib.CONFIG_FILENAME) - - - # TESTS - # Test: normal case. - self.assertTrue(signerlib.read_config_file(base_config[0]), - base_config[1]) - - # Test: Incorrect arguments. - self.assertRaises(tuf.FormatError, signerlib.read_config_file, 123) - self.assertRaises((tuf.Error, tuf.FormatError), signerlib.read_config_file, - '') - self.assertRaises((tuf.Error, tuf.FormatError), signerlib.read_config_file, - 'junk/dir/'+self.random_string()) - self.assertRaises(tuf.FormatError, signerlib.read_config_file, - [self.random_string()]) - - - - - - def test_1_generate_targets_metadata(self): - - # SETUP - generate_targets_meta = signerlib.generate_targets_metadata - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - # Generate target files. - # 'repo_dir' represents repository base. - # 'target_files' represents a list of relative target paths. - repo_dir, target_files = self.make_temp_directory_with_data_files() - - - # TESTS - # Test: Run the generate_targets_metadata(). Test its return value. - # Its return value should correspond to tuf.formats.SIGNABLE_SCHEMA - target_signable_obj = generate_targets_meta(repo_dir, target_files, - version, expiration_date) - - # Test: Validate input. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(target_signable_obj)) - - # Test: Incorrect arguments. - self.assertRaises(tuf.FormatError, generate_targets_meta, - self.random_string(), expiration_date, - repo_dir, target_files) - self.assertRaises(tuf.FormatError, generate_targets_meta, - repo_dir, self.random_string(), - repo_dir, target_files) - self.assertRaises(tuf.FormatError, generate_targets_meta, - version, expiration_date, - self.random_string(), target_files) - self.assertRaises(tuf.FormatError, generate_targets_meta, - version, expiration_date, - repo_dir, self.random_string()) - self.assertRaises(tuf.FormatError, generate_targets_meta, - version, expiration_date, - repo_dir, [self.random_string(), 1234]) - self.assertRaises(tuf.Error, generate_targets_meta, - version, expiration_date, - self.random_path(), target_files) - - - - - - def test_1_check_directory(self): - """ - Quick test to ensure that the method returns valid output. - """ - - # SETUP - temp_dir, _junk = self.make_temp_directory_with_data_files() - rand_str = self.random_string() - - - # TESTS - # Test: normal case, check proper output. - self.assertEqual(signerlib.check_directory(temp_dir), temp_dir) - - # Test: Incorrect arguments. - self.assertRaises(tuf.FormatError, signerlib.check_directory, 1234) - self.assertRaises(tuf.FormatError, signerlib.check_directory, [rand_str]) - self.assertRaises(tuf.FormatError, signerlib.check_directory, - {rand_str: rand_str}) - self.assertRaises(tuf.Error, signerlib.check_directory, self.random_path()) - - - - - - def test_1_write_metadata_file(self): - - # SETUP - # Create temp directory to be prevent any relative path discrepancies. - meta_dir = self.make_temp_directory() - - # Create a temp file to store 'metadata' info in. - meta_file = self.make_temp_file(directory=meta_dir) - - # Use valid input for json obj. - signable_dict = {'signatures':[], 'signed':{'role':'info'}} - - - # TESTS - # Test: normal case. - signerlib.write_metadata_file(signable_dict, meta_file) - - # Extract the content of the temp file. - stored_signable_dict = tuf.util.load_json_file(meta_file) - - # Check if object stored in the file corresponds to SIGNABLE_SCHEMA. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(stored_signable_dict)) - - # Does original dictionary 'signable_dict' matches dictionary retrieved - # from the file - 'stored_signable_dict'? - self.assertEqual(signable_dict, stored_signable_dict) - - # Test: Incorrect arguments. - self.assertRaises(tuf.FormatError, signerlib.write_metadata_file,'','') - self.assertRaises(tuf.FormatError, signerlib.write_metadata_file, - [self.random_string()], meta_file) - self.assertRaises(tuf.FormatError, signerlib.write_metadata_file, - signable_dict, [self.random_string()]) - self.assertRaises(tuf.Error, signerlib.write_metadata_file, signable_dict, - self.random_path()) - self.assertRaises(tuf.FormatError, signerlib.write_metadata_file, - {self.random_string(): self.random_string()}, - self.random_path()) - - - - def test_2_build_config_file(self): - """ - This method tests build_config_file(). - Previously tested signerlib's read_config_file() is used here. - """ - - # SETUP - # Declare timeout. - days = 365 # number of days - - # Make a temp directory for config file. - config_dir = self.make_temp_directory() - - # For 'role_info' argument we going to use 'self.top_level_role_info' - # dictionary. There is more info in the beginning of this test - # module, also in the test.unittest_toolbox module. - roledict_info = self.top_level_role_info - - - # TESTS - # Test: normal case. - # Run build_config_file(). The method is expected to return file - # path of the config file. We'll compare it to 'roledict_info'. - build_config = signerlib.build_config_file - config_path = build_config(config_file_directory=config_dir, - timeout=days, role_info=roledict_info) - - # Check if 'config_path' directory exists. - self.assertTrue(os.path.exists(config_path)) - - # Using 'signerlib.read_config_file' method extract config dictionary - # that was stored. - config_dict = signerlib.read_config_file(config_path) - - # Remove 'expiration' key from the extracted config dictionary, since - # initial role dictionary does not have this field. - del config_dict['expiration'] - - # Compare the initial dictionary 'roledict_info' with extracted - # dictionary 'config_dict'. They have to match. - self.assertTrue(config_dict, roledict_info) - - # Test: exceptions on bogus arguments. - self.assertRaises(tuf.Error, signerlib.build_config_file, - self.random_path(), 365, roledict_info) - self.assertRaises(tuf.FormatError, signerlib.build_config_file, - config_dir, -1, roledict_info) - self.assertRaises(tuf.FormatError, signerlib.build_config_file, - config_dir, 365, self.directory_dictionary) - - - - def test_3_generate_root_metadata(self): - """ - test_3_build_root_metadata() is based on two other signerlib methods - i.e. build_config_file() and read_config_file(). Hence, 3rd level. - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - build_config = signerlib.build_config_file - version = 8 - - # Create a temp directory to hold a config file. - config_dir = self.make_temp_directory() - - # Create config file using previously tested build_config_file(). - config_path = build_config(config_dir, 365, self.top_level_role_info) - - # Create a config file without a 'targets' role section. - notargets_conf_dir = self.make_temp_directory() - saved_targets_role = self.top_level_role_info['targets'] - del self.top_level_role_info['targets'] - notargets_conf_path = build_config(notargets_conf_dir, 365, - self.top_level_role_info) - - # Restore top_level_role_info to initial state. - self.top_level_role_info['targets'] = saved_targets_role - - - # TESTS - # Test: What if keystore is not set up? - self.assertRaises(tuf.UnknownKeyError, signerlib.generate_root_metadata, - config_path, version) - - # Patch keystore's get_key method. No harm is done here since correct - # arguments are passed and keystore methods are tested separately. - tuf.repo.keystore.get_key = self.get_keystore_key - - # Test: normal case. Pass a correct config path. - root_meta = signerlib.generate_root_metadata(config_path, version) - - # Check if the returned dictionary corresponds to SIGNABLE_SCHEMA. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(root_meta)) - - # Test: bogus arguments. - self.assertRaises(tuf.Error, signerlib.generate_root_metadata, - notargets_conf_path, version) - self.assertRaises(tuf.Error, signerlib.generate_root_metadata, '', version) - self.assertRaises(tuf.Error, signerlib.generate_root_metadata, - self.random_string(), version) - self.assertRaises(tuf.Error, signerlib.generate_root_metadata, - {self.random_string(): self.random_string()}, version) - self.assertRaises(tuf.FormatError, signerlib.generate_root_metadata, - config_path, self.random_string()) - - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_4_sign_metadata(self): - """ - test_4_sign_metadata() will require us to create metadata using one of - the generate_role_metadata() and use monkey patched keystore's get_key(). - """ - - # SETUP. - original_get_key = tuf.repo.keystore.get_key - - for role in ['root', 'targets']: - - role_info = self._get_role_info(role) - filename = role+'.txt' - - - # TESTS - # Test: normal case. - signable = signerlib.sign_metadata(role_info[0], role_info[1], - filename) - - # Check if signable is returned. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(signable)) - - # Test: Incorrect arguments. - self.assertRaises(tuf.FormatError, signerlib.sign_metadata, - self.random_string(), role_info[1], filename) - self.assertRaises(tuf.FormatError, signerlib.sign_metadata, - role_info[0], 12345, filename) - - # Test: Verifying 'keytype' value, once is sufficient. - if role == 'root': - # Alter 'keytype' value of the rsa key. Restore it after. - for keyid in role_info[1]: - key = self.get_keystore_key(keyid) - key['keytype'] = 'unknown_type' - self.assertRaises(tuf.Error, signerlib.sign_metadata, role_info[0], - role_info[1], filename) - - # Restoring the initial state of rsa_keystore. - for keyid in role_info[1]: - key = self.get_keystore_key(keyid) - key['keytype'] = 'rsa' - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - def test_5_build_root_file(self): - """ - test_5_build_root_file() relies on previously tested signerlib's - generate_root_metadata(), sign_metadata() and write_metadata_file(). - build_root_file() basically joins these methods together to create - root.txt. - - Test Outline: Get signed metadata and other info of a root role. - 'root_meta' is a tuple - see _get_role_meta() and _get_signed_role_info(). - Run build_root_file() with created parameters 'config_path', 'root_keyids' - and 'meta_dir'. Verify existence of the created directory. Extract - content of the file and verify that it matches original 'signed_root_meta' - dictionary. Test various bogus parameters. - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - - signed_root_meta, root_info = self._get_signed_role_info('root') - root_keyids = root_info[1] - config_path = root_info[3] - meta_dir = root_info[2] # Reuse config's directory. - - - # TESTS - # Test: normal case. - root_filepath = signerlib.build_root_file(config_path, root_keyids, - meta_dir, version) - - # Check existence of the file and validity of it's content. - self.assertTrue(os.path.exists(root_filepath)) - file_content = tuf.util.load_json_file(root_filepath) - self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(file_content)) - root_metadata = file_content['signed'] - self.assertTrue(tuf.formats.ROOT_SCHEMA.matches(root_metadata)) - - # Test: various exceptions. - self.assertRaises(tuf.Error, signerlib.build_root_file, - self.random_path(), root_keyids, meta_dir, version) - self.assertRaises(tuf.FormatError, signerlib.build_root_file, - config_path, self.random_string(), meta_dir, version) - self.assertRaises(tuf.Error, signerlib.build_root_file, - config_path, root_keyids, self.random_path(), version) - self.assertRaises(tuf.Error, signerlib.build_root_file, - config_path, root_keyids, meta_dir, self.random_string()) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_5_build_targets_file(self): - """ - test_5_build_targets_file() relies on previously tested signerlib's - generate_targets_metadata(), sign_metadata() and write_metadata_file(). - build_targets_file() basically joins these methods together to create - targets.txt. - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - signed_targets_meta, targets_info = self._get_signed_role_info('targets') - - # 'targets_info' is a tuple that includes targets meta, repository dir, - # list of target files. - targets_keyids = targets_info[1] - repo_dir = targets_info[2] - meta_dir = os.path.join(repo_dir, 'metadata') - os.mkdir(meta_dir) - targets_dir = os.path.join(repo_dir, 'targets') - - # TESTS - # Test: normal case. - targets_filepath = signerlib.build_targets_file([targets_dir], - targets_keyids, meta_dir, - version, expiration_date) - - # Check existence of the file and validity of it's content. - self.assertTrue(os.path.exists(targets_filepath)) - file_content = tuf.util.load_json_file(targets_filepath) - self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(file_content)) - targets_metadata = file_content['signed'] - self.assertTrue(tuf.formats.TARGETS_SCHEMA.matches(targets_metadata)) - - # Test: various exceptions. - self.assertRaises(tuf.FormatError, signerlib.build_targets_file, - [targets_dir], self.random_string(), meta_dir, version, expiration_date) - self.assertRaises((tuf.FormatError, tuf.Error), signerlib.build_targets_file, - [targets_dir], targets_keyids, self.random_path(), version, expiration_date) - self.assertRaises((tuf.FormatError, tuf.Error), signerlib.build_targets_file, - [targets_dir], targets_keyids, meta_dir, self.random_string(), expiration_date) - self.assertRaises((tuf.FormatError, tuf.Error), signerlib.build_targets_file, - [targets_dir], targets_keyids, meta_dir, version, self.random_string()) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_6_generate_release_metadata(self): - """ - test_6_generate_release_metadata() uses previously tested - singnerlib's build_root_file(), build_targets_file() - and get_metadata_file_info. In order to use generate_release_metadata() - we need to have root.txt and targets.txt in the metadata directory, - plus we need to have targets directory (with target files/directories). - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - # Create root.txt and targets.txt as described above. - meta_dir = self._create_root_and_targets_meta_files() - - - # TESTS - # Test: Run generate_release_metadata(). - release_meta = signerlib.generate_release_metadata(meta_dir, - version, expiration_date) - - # Verify that created metadata dictionary corresponds to - # SIGNABLE_SCHEMA and its 'signed' value to RELEASE_SCHEMA. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(release_meta)) - self.assertTrue(formats.RELEASE_SCHEMA.matches(release_meta['signed'])) - - # Test: exceptions. - self.assertRaises(tuf.Error, signerlib.generate_release_metadata, - self.random_path(), version, expiration_date) - self.assertRaises(tuf.FormatError, signerlib.generate_release_metadata, - ['junk'], version, expiration_date) - self.assertRaises(tuf.Error, signerlib.generate_release_metadata, - meta_dir, self.random_string(), expiration_date) - self.assertRaises(tuf.Error, signerlib.generate_release_metadata, - meta_dir, version, self.random_string()) - - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_7_build_release_file(self): - """ - test_7_build_release_file() uses previously tested - generate_release_metadata(). - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - # Create root.txt and targets.txt as described above. Also, get signed - # release metadata to compare it with the content of the file - signed_release_meta, release_info = self._get_signed_role_info('release') - meta_dir = release_info[2] - release_keyids = release_info[1] - - - # TESTS - # Test: normal case. - release_filepath = signerlib.build_release_file(release_keyids, meta_dir, - version, expiration_date) - - # Check if 'release.txt' file was created in metadata directory. - self.assertTrue(os.path.exists(release_filepath)) - file_content = tuf.util.load_json_file(release_filepath) - self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(file_content)) - release_metadata = file_content['signed'] - self.assertTrue(tuf.formats.RELEASE_SCHEMA.matches(release_metadata)) - - # Test: exceptions. - self.assertRaises(tuf.Error, signerlib.build_release_file, release_keyids, - self.random_path(), version, expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_release_file, - self.random_string(), meta_dir, version, expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_release_file, - release_keyids, meta_dir, self.random_string(), - expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_release_file, - release_keyids, meta_dir, version, self.random_string()) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_8_generate_timestamp_metadata(self): - """ - test_8_generate_timestamp_metadata() uses previously tested - build_release_file() - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - generate_timestamp_meta = signerlib.generate_timestamp_metadata - - # Create release metadata and create 'release.txt' file. - junk, release_keyids, meta_dir = self._get_role_info('release') - signerlib.build_release_file(release_keyids, meta_dir, version, - expiration_date) - release_filepath = os.path.join(meta_dir, 'release.txt') - - # To test compression we need to create compressed 'release.txt'. - # The 'release.txt' should exist at this point, compress it. - release_file = open(release_filepath, 'rb') - gzipped_release = open(release_filepath+'.gz', 'wb') - gzipped_release.writelines(release_file) - gzipped_release.close() - release_file.close() - - - # TESTS - # Test: normal case. - timestamp_meta = generate_timestamp_meta(release_filepath, version, - expiration_date) - - # Verify metadata formats. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(timestamp_meta)) - self.assertTrue(formats.TIMESTAMP_SCHEMA.matches(timestamp_meta['signed'])) - - # Test: normal case (with compression). - timestamp_meta = generate_timestamp_meta(release_filepath+'.gz', version, - expiration_date) - - # Verify metadata formats. - self.assertTrue(formats.SIGNABLE_SCHEMA.matches(timestamp_meta)) - self.assertTrue(formats.TIMESTAMP_SCHEMA.matches(timestamp_meta['signed'])) - - # Test: invalid path. - self.assertRaises(tuf.Error, generate_timestamp_meta, self.random_path(), - version, expiration_date) - self.assertRaises(tuf.FormatError, generate_timestamp_meta, release_filepath, - self.random_string(), expiration_date) - self.assertRaises(tuf.FormatError, generate_timestamp_meta, release_filepath, - version, self.random_string()) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_9_build_timestamp_file(self): - """ - test_9_build_timestamp_file() uses previously tested - generate_timestamp_metadata(). - """ - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - # Create all necessary files and metadata i.e. signed timestamp - # metadata, timestamp keyids, 'release.txt', 'root.txt', 'targets.txt', - # target files, etc. - signed_timestamp_meta, timestamp_info = \ - self._get_signed_role_info('timestamp') - - timestamp_keyids = timestamp_info[1] - meta_dir = timestamp_info[2] - - - # TESTS - # Test: normal case. - timestamp_filepath = signerlib.build_timestamp_file(timestamp_keyids, - meta_dir, version, - expiration_date) - - # Check if 'timestamp.txt' file was created in metadata directory. - self.assertTrue(os.path.exists(timestamp_filepath)) - file_content = tuf.util.load_json_file(timestamp_filepath) - self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(file_content)) - timestamp_metadata = file_content['signed'] - self.assertTrue(tuf.formats.TIMESTAMP_SCHEMA.matches(timestamp_metadata)) - - # Test: try bogus parameters. - self.assertRaises(tuf.Error, signerlib.build_timestamp_file, - timestamp_keyids, self.random_path(), version, - expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_timestamp_file, - self.random_string(), meta_dir, version, expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_timestamp_file, - timestamp_keyids, meta_dir, self.random_string(), - expiration_date) - self.assertRaises(tuf.FormatError, signerlib.build_timestamp_file, - timestamp_keyids, meta_dir, version, self.random_string()) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - def test_9_get_target_keyids(self): - - # SETUP - original_get_key = tuf.repo.keystore.get_key - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - # Create metadata directory and targets metadata file. - meta_dir = self._create_root_and_targets_meta_files() - - signed_targets_meta, targets_info = self._get_signed_role_info('targets') - - # 'targets_info' is a tuple that includes targets meta, repository dir, - # list of target files. - targets_keyids = targets_info[1] - repo_dir = targets_info[2] - meta_dir = os.path.join(repo_dir, 'metadata') - os.mkdir(meta_dir) - targets_dir = os.path.join(repo_dir, 'targets') - - # TESTS - # Test: normal case. - targets_filepath = signerlib.build_targets_file([targets_dir], - targets_keyids, meta_dir, - version, expiration_date) - - # Check existence of the file and validity of it's content. - self.assertTrue(os.path.exists(targets_filepath)) - file_content = tuf.util.load_json_file(targets_filepath) - self.assertTrue(tuf.formats.SIGNABLE_SCHEMA.matches(file_content)) - targets_metadata = file_content['signed'] - self.assertTrue(tuf.formats.TARGETS_SCHEMA.matches(targets_metadata)) - # TODO: Generate some delegation metadata files. - - # Test: normal case. - _target_keyids = signerlib.get_target_keyids(meta_dir) - for keyid in targets_keyids: - self.assertTrue(keyid in _target_keyids['targets']) - - # RESTORE - tuf.repo.keystore.get_key = original_get_key - - - - - - # HELPER METHODS - # Call these non-test methods ONLY in methods that begin with 'test'. - def _create_root_and_targets_meta_files(self, repo_dir=None): - """ - This method generates temp root.txt and target.txt, it uses following - previously tested signerlib's methods: - build_root_file() - build_targets_file() - """ - - # The version number and expiration date for the root and target - # metadata created. - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - if not repo_dir: - # Create repository directory. - repo_dir = self.make_temp_directory() - - # Create metadata directory. - meta_dir = os.path.join(repo_dir, 'metadata') - os.mkdir(meta_dir) - - # Create root.txt. - junk, root_keyids, repo_dir, config_path = \ - self._get_role_info('root', directory=repo_dir) - signerlib.build_root_file(config_path, root_keyids, meta_dir, version) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - - # Create targets.txt. - junk, targets_keyids, repo_dir, target_files = \ - self._get_role_info('targets', directory=repo_dir) - path_to_targets = os.path.join(repo_dir, 'targets') - signerlib.build_targets_file([path_to_targets], targets_keyids, meta_dir, - version, expiration_date) - self.assertTrue(os.path.exists(os.path.join(meta_dir, 'root.txt'))) - - return meta_dir - - - - - - def _get_role_info(self, role, directory=None): - """ - This method generates role's metadata dictionary, it uses previously - tested signerlib's methods. Note that at everything maintains the order. - Nothing that has not been tested previously is used in any of the - following conditions. - - - directory: - Directory of a config file. - - - Tuple (role's metadata(not signed), role's keyids, directory, optional) - - """ - - # The version number and expiration date for metadata files created. - version = 8 - expiration_date = '1985-10-26 01:20:00 UTC' - - - if not directory: - # Create a temp directory to hold a config file. - directory = self.make_temp_directory() - - # Get role's keyids. - role_keyids = self.top_level_role_info[role]['keyids'] - - - if role == 'root': - # Create config file using previously tested build_config_file(). - config_path = signerlib.build_config_file(directory, 365, - self.top_level_role_info) - - # Patch keystore's get_key method. - tuf.repo.keystore.get_key = self.get_keystore_key - - # Create root metadata. - root_meta = signerlib.generate_root_metadata(config_path, version) - return root_meta, role_keyids, directory, config_path - - elif role == 'targets': - # Generate target files. - # 'repo_dir' represents repository base. - # 'target_files' represents a list of relative target paths. - repo_dir, target_files = \ - self.make_temp_directory_with_data_files(directory=directory) - - # Patch keystore's get_key method. - tuf.repo.keystore.get_key = self.get_keystore_key - - # Run the 'signerlib.generate_targets_metadata'. Test its return value. - # Its return value should correspond to tuf.formats.SIGNABLE_SCHEMA - targets_meta = signerlib.generate_targets_metadata(repo_dir, target_files, - version, - expiration_date) - return targets_meta, role_keyids, repo_dir, target_files - - elif role == 'release': - # Generate 'root.txt' and 'targets.txt' with targets directory in - # the repository containing files and directories. - meta_dir = self._create_root_and_targets_meta_files() - release_meta = signerlib.generate_release_metadata(meta_dir, version, - expiration_date) - return release_meta, role_keyids, meta_dir - - elif role == 'timestamp': - # Generate 'release.txt' which includes creation of 'root.txt', - # 'targets.txt' and target files. - junk, release_keyids, meta_dir = self._get_role_info('release') - signerlib.build_release_file(release_keyids, meta_dir, version, - expiration_date) - release_filepath = os.path.join(meta_dir, 'release.txt') - - # Generate timestamp metadata. - timestamp_meta = signerlib.generate_timestamp_metadata(release_filepath, - version, - expiration_date) - return timestamp_meta, role_keyids, meta_dir - - else: - logger.warning('\nUnrecognized top-level role.') - - - - - - def _get_signed_role_info(self, role, directory=None): - role_info = self._get_role_info(role, directory=directory) - filename = repr(role+'.txt') - - # Try sign_metadata(), see if signable is returned. - signed_meta = signerlib.sign_metadata(role_info[0], role_info[1], - filename) - return signed_meta, role_info - - -def setUpModule(): - # setUpModule() is called before any test cases run. - # Generate rsa keys and roles dictionary dictionaries. - unit_tbox.bind_keys_to_roles() - -def tearDownModule(): - # tearDownModule() is called after all the test cases have run. - unit_tbox.clear_toolbox() - tuf.repo.keystore.clear_keystore() - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/deprecated/test_util_test_tools.py b/tests/unit/deprecated/test_util_test_tools.py deleted file mode 100755 index bfd86d7f..00000000 --- a/tests/unit/deprecated/test_util_test_tools.py +++ /dev/null @@ -1,171 +0,0 @@ -#!/usr/bin/env python - -""" - - test_util_test_tools.py - - - Konstantin Andrianov - - - February 26, 2012 - - - See LICENSE for licensing information. - - - Test util_test_tools. - -""" -import os -import urllib -import unittest - -import tuf.tests.util_test_tools as util_test_tools -import tuf.repo.keystore - - -class test_UtilTestTools(unittest.TestCase): - def setUp(self): - unittest.TestCase.setUp(self) - - # Ensure the keystore is empty prior to initializing the repository - # generated by 'util_test_tools'. - tuf.repo.keystore.clear_keystore() - - # Unpacking necessary parameters returned from init_repo() - essential_params = util_test_tools.init_repo(using_tuf=True) - self.root_repo = essential_params[0] - self.url = essential_params[1] - self.server_proc = essential_params[2] - self.keyids = essential_params[3] - # TODO: In the line below, 'util_test_tools.init_repo' does - # not return the interposition config and this unit test - # does not directly use it. WIP? - #self.interpose_json = essential_params[4] - - def tearDown(self): - unittest.TestCase.tearDown(self) - - # 'util_test_tools.cleanup()' should clear the keystore... - util_test_tools.cleanup(self.root_repo, self.server_proc) - - # Clear the keystore here just in case. - tuf.repo.keystore.clear_keystore() - - -#================================================# -# Below are a few quick tests that make sure # -# everything works smoothly in util_test_tools. # -#================================================# - - # A few quick internal tests to see if everything runs smoothly. - def test_direct_download(self): - # Setup. - reg_repo = os.path.join(self.root_repo, 'reg_repo') - downloads = os.path.join(self.root_repo, 'downloads') - filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test') - file_basename = os.path.basename(filepath) - url_to_reg_repo = self.url+'reg_repo/'+file_basename - downloaded_file = os.path.join(downloads, file_basename) - - # Test direct download using 'urllib.urlretrieve'. - urllib.urlretrieve(url_to_reg_repo, downloaded_file) - self.assertTrue(os.path.isfile(downloaded_file)) - - # Verify the content of the downloaded file. - downloaded_content = util_test_tools.read_file_content(downloaded_file) - self.assertEquals('Test', downloaded_content) - - - - - - def test_correct_directory_structure(self): - # Verify following directories exists: '{root_repo}/reg_repo/', - # '{root_repo}/downloads/. - self.assertTrue(os.path.isdir(os.path.join(self.root_repo, 'reg_repo'))) - self.assertTrue(os.path.isdir(os.path.join(self.root_repo, 'downloads'))) - - # Verify that all necessary TUF-paths exist. - tuf_repo = os.path.join(self.root_repo, 'tuf_repo') - tuf_client = os.path.join(self.root_repo, 'tuf_client') - metadata_dir = os.path.join(tuf_repo, 'metadata') - current_dir = os.path.join(tuf_client, 'metadata', 'current') - - # Verify '{root_repo}/tuf_repo/metadata/role.json' paths exists. - for role in ['root', 'targets', 'snapshot', 'timestamp']: - # Repository side. - role_file = os.path.join(metadata_dir, role+'.json') - self.assertTrue(os.path.isfile(role_file)) - - # Client side. - role_file = os.path.join(current_dir, role+'.json') - self.assertTrue(os.path.isfile(role_file)) - - # Verify '{root_repo}/tuf_repo/keystore/keyid.key' exists. - keys_list = os.listdir(os.path.join(tuf_repo, 'keystore')) - self.assertEquals(len(keys_list), 1) - - # Verify '{root_repo}/tuf_repo/targets/' directory exists. - self.assertTrue(os.path.isdir(os.path.join(tuf_repo, 'targets'))) - - - - - - def test_methods(self): - """ - Making sure following methods work as intended: - - add_file_to_repository(data) - - modify_file_at_repository(filepath, data) - - delete_file_at_repository(filepath) - - read_file_content(filepath) - - tuf_refresh_repo() - - tuf_refresh_and_download() - - Note: here file at the 'filepath' and the 'target' file at tuf-targets - directory are identical files. - Ex: filepath = '{root_repo}/reg_repo/file.json' - target = '{root_repo}/tuf_repo/targets/file.json' - """ - - reg_repo = os.path.join(self.root_repo, 'reg_repo') - tuf_repo = os.path.join(self.root_repo, 'tuf_repo') - downloads = os.path.join(self.root_repo, 'downloads') - - # Test 'add_file_to_repository(directory, data)' and - # read_file_content(filepath) methods. - filepath = util_test_tools.add_file_to_repository(reg_repo, 'Test') - self.assertTrue(os.path.isfile(filepath)) - self.assertEquals(os.path.dirname(filepath), reg_repo) - filepath_content = util_test_tools.read_file_content(filepath) - self.assertEquals('Test', filepath_content) - - # Test 'modify_file_at_repository(filepath, data)' method. - filepath = util_test_tools.modify_file_at_repository(filepath, 'Modify') - self.assertTrue(os.path.exists(filepath)) - filepath_content = util_test_tools.read_file_content(filepath) - self.assertEquals('Modify', filepath_content) - - # Test 'tuf_refresh_repo' method. - util_test_tools.tuf_refresh_repo(self.root_repo, self.keyids) - file_basename = os.path.basename(filepath) - target = os.path.join(tuf_repo, 'targets', file_basename) - self.assertTrue(os.path.isfile(target)) - - # Test 'delete_file_at_repository(filepath)' method. - util_test_tools.delete_file_at_repository(filepath) - self.assertFalse(os.path.exists(filepath)) - - # Test 'tuf_refresh_repo' method once more. - util_test_tools.tuf_refresh_repo(self.root_repo, self.keyids) - file_basename = os.path.basename(filepath) - target = os.path.join(tuf_repo, 'targets', file_basename) - self.assertFalse(os.path.isfile(target)) - - - - -if __name__ == '__main__': - unittest.main() diff --git a/tests/unit/simple_server.py b/tests/unit/simple_server.py deleted file mode 100755 index 217bdeed..00000000 --- a/tests/unit/simple_server.py +++ /dev/null @@ -1,48 +0,0 @@ -""" - - simple_server.py - - - Konstantin Andrianov - - - February 15, 2012 - - - See LICENSE for licensing information. - - - This is a basic server that was designed to be used in conjunction with - test_download.py to test download.py module. - - - SimpleHTTPServer: - http://docs.python.org/library/simplehttpserver.html#module-SimpleHTTPServer - -""" - -import sys -import random -import SimpleHTTPServer -import SocketServer - -PORT = 0 - -def _port_gen(): - return random.randint(30000, 45000) - -if len(sys.argv) > 1: - try: - PORT = int(sys.argv[1]) - if PORT < 30000 or PORT > 45000: - raise ValueError - except ValueError: - PORT = _port_gen() -else: - PORT = _port_gen() - -Handler = SimpleHTTPServer.SimpleHTTPRequestHandler -httpd = SocketServer.TCPServer(("", PORT), Handler) - -#print "PORT: ", PORT -httpd.serve_forever() diff --git a/tests/unit/statement_coverage.py b/tests/unit/statement_coverage.py deleted file mode 100755 index 6c40b0d5..00000000 --- a/tests/unit/statement_coverage.py +++ /dev/null @@ -1,86 +0,0 @@ -""" - - statement_coverage.py - - - Konstantin Andrianov - - - March 20, 2013. - - - See LICENSE for licensing information. - - - Measure test coverage. - -NOTE: This script is based on third party software. In order to use this -script install Ned Batchelder's coverage.py: -http://nedbatchelder.com/code/coverage/ - - -""" - -import os -import sys - -# Try to import coverage.py. Coverage.py is a third party software. -try: - coverage = __import__('coverage') -except ImportError, error: - error_msg = ("\nIt appears that coverage.py is not installed. Install "+ - "Ned Batchelder's coverage.py "+ - "('http://nedbatchelder.com/code/coverage/') and try again.\n") - print error_msg - raise - - -cov = coverage.coverage() -cov.start() - -try: - current_directory = os.getcwd() - current_directory_content = os.listdir(current_directory) - - # Find test scripts and import them. - test_modules = [] # Test modules. - tested_modules = [] # Modules that are tested by the 'test_modules'. - - for _file in current_directory_content: - if _file.startswith('test') and _file.endswith('.py'): - - _file = os.path.splitext(_file)[0] - test_modules.append(_file) - - # Import the module. - try: - module = __import__(_file) - except ImportError, error: - print 'Unable to load the module: '+_file - raise - - _file = '.'+_file[5:] - tested_modules.append(_file) - -finally: - cov.stop() - - -# Include quickstart. -tested_modules.remove('.quickstart') -tested_modules.append('quickstart') - -# Extracting tuf modules. -tuf_modules = {} # list of all loaded tuf modules. -for module_name in sys.modules: - if module_name.startswith('tuf') or module_name.startswith('quickstart'): - tuf_modules[module_name] = sys.modules[module_name] - -# Tested module paths. -tested_module_paths = [] -for module_name in tuf_modules: - for tested_module in tested_modules: - if module_name.endswith(tested_module): - tested_module_paths.append(tuf_modules[module_name].__file__) - -cov.report(tested_module_paths) \ No newline at end of file diff --git a/tuf/tests/unittest_toolbox.py b/tests/unittest_toolbox.py old mode 100644 new mode 100755 similarity index 99% rename from tuf/tests/unittest_toolbox.py rename to tests/unittest_toolbox.py index d425fe76..ee0ac170 --- a/tuf/tests/unittest_toolbox.py +++ b/tests/unittest_toolbox.py @@ -27,11 +27,11 @@ import ConfigParser import tuf.keys -import tuf.repo.keystore as keystore +#import tuf.repo.keystore as keystore # Modify the number of iterations (from the higher default count) so the unit # tests run faster. -keystore._PBKDF2_ITERATIONS = 1000 +#keystore._PBKDF2_ITERATIONS = 1000 class Modified_TestCase(unittest.TestCase): diff --git a/tuf/_vendor/__init__.py b/tuf/_vendor/__init__.py old mode 100644 new mode 100755 diff --git a/tuf/_vendor/iso8601/LICENSE b/tuf/_vendor/iso8601/LICENSE new file mode 100644 index 00000000..471acb7b --- /dev/null +++ b/tuf/_vendor/iso8601/LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2007 - 2013 Michael Twomey + +Permission is hereby granted, free of charge, to any person obtaining a +copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be included +in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS +OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY +CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, +TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE +SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/tuf/_vendor/iso8601/README.rst b/tuf/_vendor/iso8601/README.rst new file mode 100644 index 00000000..e854dee9 --- /dev/null +++ b/tuf/_vendor/iso8601/README.rst @@ -0,0 +1,170 @@ +Simple module to parse ISO 8601 dates + +This module parses the most common forms of ISO 8601 date strings (e.g. +2007-01-14T20:34:22+00:00) into datetime objects. + +>>> import iso8601 +>>> iso8601.parse_date("2007-01-25T12:00:00Z") +datetime.datetime(2007, 1, 25, 12, 0, tzinfo=) +>>> + +See the LICENSE file for the license this package is released under. + +If you want more full featured parsing look at: + +- http://labix.org/python-dateutil - python-dateutil + +Parsed Formats +============== + +You can parse full date + times, or just the date. In both cases a datetime instance is returned but with missing times defaulting to 0, and missing days / months defaulting to 1. + +Dates +----- + +- YYYY-MM-DD +- YYYYMMDD +- YYYY-MM (defaults to 1 for the day) +- YYYY (defaults to 1 for month and day) + +Times +----- + +- hh:mm:ss.nn +- hhmmss.nn +- hh:mm (defaults to 0 for seconds) +- hhmm (defaults to 0 for seconds) +- hh (defaults to 0 for minutes and seconds) + +Time Zones +---------- + +- Nothing uses the default timezone given (UTC). +- Z (UTC) +- +/-hh:mm +- +/-hhmm +- +/-hh + +Where it Differs From ISO 8601 +============================== + +Known differences from the ISO 8601 spec: + +- You can use a " " (space) instead of T for separating date from time. +- Days and months without a leading 0 (2 vs 02) will be parsed. +- If time zone information is omitted the default time zone given is used (which in turn defaults to UTC). Use a default of None to yield naive datetime instances. + +Homepage +======== + +- https://bitbucket.org/micktwomey/pyiso8601/ + +This was originally hosted at https://code.google.com/p/pyiso8601/ + +References +========== + +- http://en.wikipedia.org/wiki/ISO_8601 + +- http://www.cl.cam.ac.uk/~mgk25/iso-time.html - simple overview + +- http://hydracen.com/dx/iso8601.htm - more detailed enumeration of valid formats. + +Testing +======= + +1. pip install -r dev-requirements.txt +2. tox + +Note that you need all the pythons installed to perform a tox run (see below). Homebrew helps a lot on the mac, however you wind up having to add cellars to your PATH or symlinking the pythonX.Y executables. + +Alternatively, to test only with your current python: + +1. pip install -r dev-requirements.txt +2. py.test --verbose iso8601 + +Supported Python Versions +========================= + +Tested against: + +- Python 2.6 +- Python 2.7 +- Python 3.2 +- Python 3.3 +- PyPy + +Python 3.0 and 3.1 are untested but should work (tests didn't run under them when last tried). + +Jython is untested but should work (tests failed to run). + +Python 2.5 is not supported (too old for the tests for the most part). It could work with some small changes but I'm not supporting it. + +Changes +======= + +0.1.10 +------ + +* Fixes https://bitbucket.org/micktwomey/pyiso8601/issue/14/regression-yyyy-mm-no-longer-parses (thanks to Kevin Gill for reporting) +* Adds YYYY as a valid date (uses 1 for both month and day) +* Woo, semantic versioning, .10 at last. + +0.1.9 +----- + +* Lots of fixes tightening up parsing from jdanjou. In particular more invalid cases are treated as errors. Also includes fixes for tests (which is how these invalid cases got in in the first place). +* Release addresses https://bitbucket.org/micktwomey/pyiso8601/issue/13/new-release-based-on-critical-bug-fix + +0.1.8 +----- + +* Remove +/- chars from README.rst and ensure tox tests run using LC_ALL=C. The setup.py egg_info command was failing in python 3.* on some setups (basically any where the system encoding wasn't UTF-8). (https://bitbucket.org/micktwomey/pyiso8601/issue/10/setuppy-broken-for-python-33) (thanks to klmitch) + +0.1.7 +----- + +* Fix parsing of microseconds (https://bitbucket.org/micktwomey/pyiso8601/issue/9/regression-parsing-microseconds) (Thanks to dims and bnemec) + +0.1.6 +----- + +* Correct negative timezone offsets (https://bitbucket.org/micktwomey/pyiso8601/issue/8/015-parses-negative-timezones-incorrectly) (thanks to Jonathan Lange) + +0.1.5 +----- + +* Wow, it's alive! First update since 2007 +* Moved over to https://bitbucket.org/micktwomey/pyiso8601 +* Add support for python 3. https://code.google.com/p/pyiso8601/issues/detail?id=23 (thanks to zefciu) +* Switched to py.test and tox for testing +* Make seconds optional in date format ("1997-07-16T19:20+01:00" now valid). https://bitbucket.org/micktwomey/pyiso8601/pull-request/1/make-the-inclusion-of-seconds-optional-in/diff (thanks to Chris Down) +* Correctly raise ParseError for more invalid inputs (https://bitbucket.org/micktwomey/pyiso8601/issue/1/raise-parseerror-for-invalid-input) (thanks to manish.tomar) +* Support more variations of ISO 8601 dates, times and time zone specs. +* Fix microsecond rounding issues (https://bitbucket.org/micktwomey/pyiso8601/issue/2/roundoff-issues-when-parsing-decimal) (thanks to nielsenb@jetfuse.net) +* Fix pickling and deepcopy of returned datetime objects (https://bitbucket.org/micktwomey/pyiso8601/issue/3/dates-returned-by-parse_date-do-not) (thanks to fogathmann and john@openlearning.com) +* Fix timezone offsets without a separator (https://bitbucket.org/micktwomey/pyiso8601/issue/4/support-offsets-without-a-separator) (thanks to joe.walton.gglcd) +* "Z" produces default timezone if one is specified (https://bitbucket.org/micktwomey/pyiso8601/issue/5/z-produces-default-timezone-if-one-is) (thanks to vfaronov). This one may cause problems if you've been relying on default_timezone to use that timezone instead of UTC. Strictly speaking that was wrong but this is potentially backwards incompatible. +* Handle compact date format (https://bitbucket.org/micktwomey/pyiso8601/issue/6/handle-compact-date-format) (thanks to rvandolson@esri.com) + +0.1.4 +----- + +* The default_timezone argument wasn't being passed through correctly, UTC was being used in every case. Fixes issue 10. + +0.1.3 +----- + +* Fixed the microsecond handling, the generated microsecond values were way too small. Fixes issue 9. + +0.1.2 +----- + +* Adding ParseError to __all__ in iso8601 module, allows people to import it. Addresses issue 7. +* Be a little more flexible when dealing with dates without leading zeroes. This violates the spec a little, but handles more dates as seen in the field. Addresses issue 6. +* Allow date/time separators other than T. + +0.1.1 +----- + +* When parsing dates without a timezone the specified default is used. If no default is specified then UTC is used. Addresses issue 4. diff --git a/tuf/_vendor/iso8601/__init__.py b/tuf/_vendor/iso8601/__init__.py new file mode 100644 index 00000000..11b1adcb --- /dev/null +++ b/tuf/_vendor/iso8601/__init__.py @@ -0,0 +1 @@ +from .iso8601 import * diff --git a/tuf/_vendor/iso8601/iso8601.py b/tuf/_vendor/iso8601/iso8601.py new file mode 100644 index 00000000..becdd958 --- /dev/null +++ b/tuf/_vendor/iso8601/iso8601.py @@ -0,0 +1,202 @@ +"""ISO 8601 date time string parsing + +Basic usage: +>>> import iso8601 +>>> iso8601.parse_date("2007-01-25T12:00:00Z") +datetime.datetime(2007, 1, 25, 12, 0, tzinfo=) +>>> + +""" + +from datetime import ( + datetime, + timedelta, + tzinfo +) +from decimal import Decimal +import logging +import sys +import re + +__all__ = ["parse_date", "ParseError"] + +LOG = logging.getLogger(__name__) + +if sys.version_info >= (3, 0, 0): + _basestring = str +else: + _basestring = basestring + + +# Adapted from http://delete.me.uk/2005/03/iso8601.html +ISO8601_REGEX = re.compile( + r""" + (?P[0-9]{4}) + ( + ( + (-(?P[0-9]{1,2})) + | + (?P[0-9]{2}) + (?!$) # Don't allow YYYYMM + ) + ( + ( + (-(?P[0-9]{1,2})) + | + (?P[0-9]{2}) + ) + ( + ( + (?P[ T]) + (?P[0-9]{2}) + (:{0,1}(?P[0-9]{2})){0,1} + ( + :{0,1}(?P[0-9]{1,2}) + (\.(?P[0-9]+)){0,1} + ){0,1} + (?P + Z + | + ( + (?P[-+]) + (?P[0-9]{2}) + :{0,1} + (?P[0-9]{2}){0,1} + ) + ){0,1} + ){0,1} + ) + ){0,1} # YYYY-MM + ){0,1} # YYYY only + $ + """, + re.VERBOSE +) + +class ParseError(Exception): + """Raised when there is a problem parsing a date string""" + +# Yoinked from python docs +ZERO = timedelta(0) +class Utc(tzinfo): + """UTC + + """ + def utcoffset(self, dt): + return ZERO + + def tzname(self, dt): + return "UTC" + + def dst(self, dt): + return ZERO + +UTC = Utc() + +class FixedOffset(tzinfo): + """Fixed offset in hours and minutes from UTC + + """ + def __init__(self, offset_hours, offset_minutes, name): + self.__offset_hours = offset_hours # Keep for later __getinitargs__ + self.__offset_minutes = offset_minutes # Keep for later __getinitargs__ + self.__offset = timedelta(hours=offset_hours, minutes=offset_minutes) + self.__name = name + + def __eq__(self, other): + if isinstance(other, FixedOffset): + return ( + (other.__offset == self.__offset) + and + (other.__name == self.__name) + ) + if isinstance(other, tzinfo): + return other == self + return False + + def __getinitargs__(self): + return (self.__offset_hours, self.__offset_minutes, self.__name) + + def utcoffset(self, dt): + return self.__offset + + def tzname(self, dt): + return self.__name + + def dst(self, dt): + return ZERO + + def __repr__(self): + return "" % (self.__name, self.__offset) + +def to_int(d, key, default_to_zero=False, default=None, required=True): + """Pull a value from the dict and convert to int + + :param default_to_zero: If the value is None or empty, treat it as zero + :param default: If the value is missing in the dict use this default + + """ + value = d.get(key) or default + LOG.debug("Got %r for %r with default %r", value, key, default) + if (value in ["", None]) and default_to_zero: + return 0 + if value is None: + if required: + raise ParseError("Unable to read %s from %s" % (key, d)) + else: + return int(value) + +def parse_timezone(matches, default_timezone=UTC): + """Parses ISO 8601 time zone specs into tzinfo offsets + + """ + + if matches["timezone"] == "Z": + return UTC + # This isn't strictly correct, but it's common to encounter dates without + # timezones so I'll assume the default (which defaults to UTC). + # Addresses issue 4. + if matches["timezone"] is None: + return default_timezone + sign = matches["tz_sign"] + hours = to_int(matches, "tz_hour") + minutes = to_int(matches, "tz_minute", default_to_zero=True) + description = "%s%02d:%02d" % (sign, hours, minutes) + if sign == "-": + hours = -hours + minutes = -minutes + return FixedOffset(hours, minutes, description) + +def parse_date(datestring, default_timezone=UTC): + """Parses ISO 8601 dates into datetime objects + + The timezone is parsed from the date string. However it is quite common to + have dates without a timezone (not strictly correct). In this case the + default timezone specified in default_timezone is used. This is UTC by + default. + """ + if not isinstance(datestring, _basestring): + raise ParseError("Expecting a string %r" % datestring) + m = ISO8601_REGEX.match(datestring) + if not m: + raise ParseError("Unable to parse date string %r" % datestring) + groups = m.groupdict() + LOG.debug("Parsed %s into %s with default timezone %s", datestring, groups, default_timezone) + + tz = parse_timezone(groups, default_timezone=default_timezone) + + groups["second_fraction"] = int(Decimal("0.%s" % (groups["second_fraction"] or 0)) * Decimal("1000000.0")) + + try: + return datetime( + year=to_int(groups, "year"), + month=to_int(groups, "month", default=to_int(groups, "monthdash", required=False, default=1)), + day=to_int(groups, "day", default=to_int(groups, "daydash", required=False, default=1)), + hour=to_int(groups, "hour", default_to_zero=True), + minute=to_int(groups, "minute", default_to_zero=True), + second=to_int(groups, "second", default_to_zero=True), + microsecond=groups["second_fraction"], + tzinfo=tz, + ) + except Exception as e: + raise ParseError(e) diff --git a/tuf/_vendor/iso8601/test_iso8601.py b/tuf/_vendor/iso8601/test_iso8601.py new file mode 100644 index 00000000..ed2d45a0 --- /dev/null +++ b/tuf/_vendor/iso8601/test_iso8601.py @@ -0,0 +1,97 @@ +# coding=UTF-8 +from __future__ import absolute_import + +import copy +import datetime +import pickle + +import pytest + +from iso8601 import iso8601 + +def test_iso8601_regex(): + assert iso8601.ISO8601_REGEX.match("2006-10-11T00:14:33Z") + +def test_parse_no_timezone_different_default(): + tz = iso8601.FixedOffset(2, 0, "test offset") + d = iso8601.parse_date("2007-01-01T08:00:00", default_timezone=tz) + assert d == datetime.datetime(2007, 1, 1, 8, 0, 0, 0, tz) + assert d.tzinfo == tz + +def test_parse_utc_different_default(): + """Z should mean 'UTC', not 'default'. + + """ + tz = iso8601.FixedOffset(2, 0, "test offset") + d = iso8601.parse_date("2007-01-01T08:00:00Z", default_timezone=tz) + assert d == datetime.datetime(2007, 1, 1, 8, 0, 0, 0, iso8601.UTC) + +@pytest.mark.parametrize("invalid_date, error_string", [ + ("2013-10-", "Unable to parse date string"), + ("2013-", "Unable to parse date string"), + ("", "Unable to parse date string"), + (None, "Expecting a string"), + ("wibble", "Unable to parse date string"), + ("23", "Unable to parse date string"), + ("131015T142533Z", "Unable to parse date string"), + ("131015", "Unable to parse date string"), + ("20141", "Unable to parse date string"), + ("201402", "Unable to parse date string"), + ("2007-06-23X06:40:34.00Z", "Unable to parse date string"), # https://code.google.com/p/pyiso8601/issues/detail?id=14 + ("2007-06-23 06:40:34.00Zrubbish", "Unable to parse date string"), # https://code.google.com/p/pyiso8601/issues/detail?id=14 + ("20114-01-03T01:45:49", "Unable to parse date string"), +]) +def test_parse_invalid_date(invalid_date, error_string): + assert isinstance(invalid_date, str) or invalid_date is None # Why? 'cos I've screwed up the parametrize before :) + with pytest.raises(iso8601.ParseError) as exc: + iso8601.parse_date(invalid_date) + assert exc.errisinstance(iso8601.ParseError) + assert str(exc.value).startswith(error_string) + +@pytest.mark.parametrize("valid_date,expected_datetime,isoformat", [ + ("2007-06-23 06:40:34.00Z", datetime.datetime(2007, 6, 23, 6, 40, 34, 0, iso8601.UTC), "2007-06-23T06:40:34+00:00"), # Handle a separator other than T + ("1997-07-16T19:20+01:00", datetime.datetime(1997, 7, 16, 19, 20, 0, 0, iso8601.FixedOffset(1, 0, "+01:00")), "1997-07-16T19:20:00+01:00"), # Parse with no seconds + ("2007-01-01T08:00:00", datetime.datetime(2007, 1, 1, 8, 0, 0, 0, iso8601.UTC), "2007-01-01T08:00:00+00:00"), # Handle timezone-less dates. Assumes UTC. http://code.google.com/p/pyiso8601/issues/detail?id=4 + ("2006-10-20T15:34:56.123+02:30", datetime.datetime(2006, 10, 20, 15, 34, 56, 123000, iso8601.FixedOffset(2, 30, "+02:30")), None), + ("2006-10-20T15:34:56Z", datetime.datetime(2006, 10, 20, 15, 34, 56, 0, iso8601.UTC), "2006-10-20T15:34:56+00:00"), + ("2007-5-7T11:43:55.328Z", datetime.datetime(2007, 5, 7, 11, 43, 55, 328000, iso8601.UTC), "2007-05-07T11:43:55.328000+00:00"), # http://code.google.com/p/pyiso8601/issues/detail?id=6 + ("2006-10-20T15:34:56.123Z", datetime.datetime(2006, 10, 20, 15, 34, 56, 123000, iso8601.UTC), "2006-10-20T15:34:56.123000+00:00"), + ("2013-10-15T18:30Z", datetime.datetime(2013, 10, 15, 18, 30, 0, 0, iso8601.UTC), "2013-10-15T18:30:00+00:00"), + ("2013-10-15T22:30+04", datetime.datetime(2013, 10, 15, 22, 30, 0, 0, iso8601.FixedOffset(4, 0, "+04:00")), "2013-10-15T22:30:00+04:00"), #