From a7ca5f7c958a3ed2fec7eae1eec153250413fad6 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 25 Sep 2023 17:28:56 +0100 Subject: [PATCH 01/22] Add an overview of interruptible operation functionality --- doc/crypto/figure/interruptible_operation.pdf | Bin 0 -> 37853 bytes .../interruptible_operation.pdf.license | 2 + .../figure/interruptible_operation.puml | 44 +++++++++ doc/crypto/figure/interruptible_operation.svg | 19 ++++ .../interruptible_operation.svg.license | 2 + doc/crypto/overview/functionality.rst | 90 +++++++++++++++++- 6 files changed, 155 insertions(+), 2 deletions(-) create mode 100644 doc/crypto/figure/interruptible_operation.pdf create mode 100644 doc/crypto/figure/interruptible_operation.pdf.license create mode 100644 doc/crypto/figure/interruptible_operation.puml create mode 100644 doc/crypto/figure/interruptible_operation.svg create mode 100644 doc/crypto/figure/interruptible_operation.svg.license diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..81472000390439f607d973bdad5919eeb9a4b5c6 GIT binary patch literal 37853 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®>tfIj8wm z4_^r2V1Y5)6>m9&>zs*GU~1s)d`nnuEyI&4(A99i&1k^CPRCs1t(cL41FWYeHi9Hn z)Q1920F-ddQZ_r0g6VlOCdTC|Q`(iuR#}Js-?c2A`y`cLNE1gFk;2XJT`b%{$qk;% z<;v1av;4LSn$1bl4dIo44+ChCdx#54W7NM7z%K|t8z1W5dnS1|8%!=)WNCr>UYT_) z@T4Y?jdE&EC`F2pG&8mo~$~*C(YzRgz|}Oor&m3)cN!?a!46 zWpfcYu`~4ivLFT8^(C!3(^tztStTvzF;4=iFM+a5PwQte&7E=v1y_Vs$e?i8&^ zs0PgE$gcK05kXPi3cW68>KDPU`~hLBQLwA=h@{A~ zgy#-=gIV8zN3eCPIfh>wK1$H5fq*FMA63l=gTbj!85t$*b|mh3iEMS7KNC6iR&S6U zljvsk`g4UlGl8F=v+HIX}a-gx(aar^!~&Z{F+cWdg$?cQDqv3-V3pqsTNY5_?LUy^HhHTJux0 zj_&Eb!}{sV(1h#Lk4qcT^yaS44!3#z&lOk;p9(V&(-D(B>5h0)2C@iTNHM;N=)w}# zePU{}j7Wihg^INEn?lH@?ty)kL3mlYZBBNy##?Xb@h0{gw6#8VV0G>Gd0F2a(S)dj zTBDY??Rp(NvXn;*db+hrGWpuJi{DLoQEh1iKUy`UY+;~OqV=W2H93c8YM=tNUkm0i z*;aHt!c&Zhnj-TEPZUu59Je*!>^RRcvkX5_*O`N~l!6m(E^OY1d(Nxbf7ka0G)IZ_ z24iev^cQ7&|Mo%lJ{aD|^_}A}vv9I~H25eo|HB3UV1J^nPU1>V@5~Rt!}Ae%f6?nR zycYoU!h!%s0KK99`{z3!e6RTPlU~f$#_37m7Vq!BR>Slj{hQe;yV)57=;ifIjRB+p#y?gk zukT>|KG}cz(#skfnd=MLx&k!cJ2C)R*jVW}*_as^nE|ZK>~xIGOiT<+0PR2e3coM& zy|W|WgFXLAmBk%wo$da$^gr+TNA&O9DFNt}9rSG+?LO{o==OJ565!o#nIfI=l3*q-5vPrc=49bAC5bAiL4Z}SB^~o+t)-oiNIs!K z!6*kd1Z9)9FSqJN9>xZNRvuOb#xYy-BbpbIK^IjR!=FD@r79tn3>sZLC6EjvWlZv= z-Fs^F5Q|5QevX@$N;`1z+`8L*>NvAPmV#nTKn+ATw_)tMF8gJgO`cyKiDYAOdfOL& zFS()=c#jCG;`uh~H=tQ$Ga>K>F4V6}@TJ-Dt?O-Q%9G&^RLBz+a{ub7)Ahh*3I&A= z+z<~^zx&qv0RMLv>`PieL`aO&naO_6o91ss8r-xo_BUx5BQ9F)w5=O{I)t~c zAYmz(43aay1Udw&q6{iT&tM3-MPEWA4KnsBpg_d=$6;0*Glf+{5``yI8L(hiTQekn zB2m&4Q3p~;ucDJ8AtOqIf@)5i&U>fcOb|?(Z)!|%5X6KI3K)Dsg;uJPqFTJ=4F&O6 z%@yrYqt5oLn~Qx-O+kAW)Z}VQEvsDD=D&l?hmoav_pdG|{c2p#{C)(Fei0Y?g(p6N zTM^kXL>0n~qj)1I?KYzIxmQ#7hn@1g2P!Z1)Vp9{Wv=gtnZ0Lw6-xrqn8 z^!N%mqxw;ncG!2JtZlh^;OtKCJ(Fm!%y#d2lN7D<8Z?FI1m)TiNX#5KjTq>dW6tNm zXGq$A!h(|vsR!k%O9s@EVFv`45zi=&{+Y5t71$O!?%wxn?h%w!72r!VnV&4N>nEsD zJ*2N70S!kIoMGThOoHcFJGy{)1u&|jL6n_3b#%AUpFk@1QI0@dAslld(VgzsN-?;! zSE9l;F3$667)cw)y`r#f0v^wdD<{c@-acBl&3k11f$@aX`%SJ{%JOuEA#pH@3cniG zWFW*u`}48cXf6_wfzg6N2lTs@1GsBdp?QYvNwtvE(wv<*5c|%JIeW-pM*{hW1m~wb z61&KK{h0FjbG;N_h#_EXCwNDBWUb7cgOn07yxis|9i)wdw(_mc_K|Aa!%Sb|vr8Yxjb*ZO-<^Ig zG==P|$qXTI)3>;)xh~LU`K-&28|g}%bv=9RTBN-DCi!dI&TH=U$u`6N;rgPZ0{?BG zE*&Fle3loaY%QMOb8GvIr9%$%w9eN9^+A6SJ0j51-#dHA2HmYj??6^=ozx5p^%HqL zlAkF^bFZ^I)H2{4FeOPM4BTzo{Y$V>W@v$Pk>!(etoRMh`43_v`I<7?-a$eu2=)sJGfudX7r~9M;VbB*bM>my+VVB9%vu^~Yj^M(ADc^`ZP@Pg!$ANX(=M zH_^%v$8s;3vX^$5!XZRzz_&xii--GuP>8RE`U(cv`U#h?K}#8nvWS!hvlbQmhmPWO zz+eihQTRGL{($EJpLkj2!H1W(o(;3=L5MDTRe>tCV=-@+GacK zBv}c@jxI%v(t*j!Pp^$7>@OeRz3rX&5u~cnK7F38Ko;K7l*q*)|J_u~waSZ2YN9a- zHXY1wrVzr%D`?G(1-c=u&Erbihq%?EHSISh8@J=*C=LFqxnsriLkIO@aPlb1;;NP7 zYGfe}(Zp6SH?H5w8*>{Fwx{^uZ>Qyo>K7rK9j4L|-(TZlhgshcZugYftYH|P(TX@` zA(W6qHSs?QqlbX6u}te_Ecx2y?zB>sRE3aH<_YY@iEkJmdx(LWQaaAk8BW_f1o^~k zpRdy4Rc>e~G2{M{Tj9l|Ef(F-8*1{!V~>}+6dS~k65;xu>D=Jwkn+7>tA6i`zv8B_ zzIihO$Rt&g2L>`r#3?TP)?=S5n?$h@jBkGQyxaA|*dqg5MyFs=^s; zp*AO*DiPXe@YF3Da16ZV9ki1*nq2rD_xK$rxBU_>G&C!jjb@hKZ@J@@ds>>sE_c?5 zXUmOFneCTzo3^q}E7{(T*+wZ!G|Mg&Yx|9^G>AOMm9f<8(MC@a5miTpb8~BNKj-7L zX=nVxIvY2Jg0&_xy;vu?M!v5!x!s@!%M~TGXV;Le^|9ts`FtDcb~F$a*5IZ@_eb-m z*rWyYY0M*qpGLsA$_UEiZDO~A^FPR{^A4vSw6diDabpx8(sRl+aYtbiw07m=;F464 zXE{qo9+Y;0jqiH)WIA*)tV);X7W#=;S&EBJUGF%3zU($sdLU2F6RYZ@BMnxXFqBOF zl6q=B;dM8-L9V$xi4wuSO&#+!Bux?iM`g)TW5%!x{8zX{jR1%0+5-Odw91r(guZXw zw2D<#hzzFe?)|ya=%Q9I177mYf(FnMu_K;VNx@ z^6ZlaKWE-`mPIwxwO9_+Er}sUUUd-h5%UogP}^up^gb|4TD41}PL1B5%mQg(lylQF zR`R|1E>~A8gJLC=Y5W^$2H6jw?GrjeTO7`-54A@XSJY7e$D5NR@@2>zpN;K;!UNYr zDN7T~-$~sDSnlj`ID#2y^DfV^fr|C;(f@$TvykQuzrg zBj86mq|;Jlm{-Tt)?rAbQWN1mF&O=AFpPMu&jtTba~1x0g(6e)H92%dCuWn@pR` zG@X*`p>;At(Y&U|R)rQz?$xzV6iaQr+#~{gDwb(L(5@ScilDw~*H$QfAfGDouBDF> z=?ms8(P$2-Sh;XbtA{v?s3u5<=5S37&A4gfh_{AUvrjtRS%lKhLU=2FP zw%b*x?K6}wFdSo}k1wy(aSv^Nd%M*R0x6_tV{Yg$s5_r5II``!x;8wOsPVR6RwY9;28891$lIjg)s6^Q&8tTC_UuG|YQ>~Q650sX&y~m1O&APd z!TKbAT&w|fgzSu$gstR8%eobegd*-)85OIvCYv<*W&r7CcEWS z(;0r(ThccrACBOyn2cjBSCg%<7~QlJE!VH7_e1m(=tl-W@XlqSS5q%~CHzT<;2oSr zyN_y;44kc~=54k=xf%34hL)j~B=3lDnSkS~wFUk`3G-l9|v&jKiLe#>wF>K$gg zR$NbXFgd8~feaU~mv$XU?r^H+nmoQVSxxcDNxW1%ucPf*!mOxHE~-W+aT{C$I4jjg z#?BJ%I6NGvTcMzrRo**3&#jRsg15>lQeUZ6Eq*4n6*sHr*)7LANR+Hxzy8xFGCHrk zLrc3o(OX12WMdK`ab_JKPD1$>k!R#jlvm9(WMWa^nJUO-C>(xwepkjx5HAon<7!AS z-{1wNQZq|d3~$)=uFFmPm*>2(zPndJyy!H5TuS{CQcY?fEnWIKC!B=7!hArTym!Na z$OIFxYZAn0G~Kn?p`HF*b)zdiqi?Y ztYsz#HA6_=m?|l%{BzlnM&9oS5NA=;^e}6)K1(ihW_^gd>8?LUJWRP-6Q~28W9DE# zA8V*f4(1VGZSf^{?$Xr{NZz}nK64T?W7V~?I#q&y)+v27VF^W{vgeYs3}CZH@^Ydi zz#@r0#P3-RYG=A!|j zR_&2wH_pJC)nGvFPiA}}!nH#NF=|WYJFo?bz*D3NNoT7Ch4tgVSs<7-Y^^4+_GSete9zs6s5UIM?RF@!`Z#WxXFnhSAj9i%YlC!+@>Ahc^gpJ-4>4 zWeq#b=}48ISQXQRzrKBFHZ&Rwc*3>WH6%@*a2yKp(7?->GQUg|30BfK93h1q)8%=q zL-tlH*> z54GGi$K+Qh#G{<^n1;cmj!N~(~)fZ}8fwWq7p4@j5K z!9V5o+!PfDhscGc$Z%p4(Ha$3%JWkpuAUOzkJBJXN&TY06f+326K|!*#@r+sV?O`V z6Y1q-Zse+7h=cvv{SyQx%&6{hwVDG{1?{B%7N+MbUKP;ZEJ2DsrpCYk3(GHVHksvPp{Hsnhb&~b0 zMa|$wU?wnEfinsCEcjuM;CrZqIrsXHSJfwxcH$w?R;yyO>b7_FN>U9%Q79p6C)H^Z zuW9!uL$yc>KYeE~83|1OyQS6`AaOw9fh%ggsLH6Jyj8ASLQo-HLTS#$!3?Lvu%#JTqb2M z{+Ai-q**`*1unyIkwG{u_FaKt_zP8?-6dy^oye43zb5rS0ig<3^XiYiJwi8$$tf!3 zQ*O(m`Q2df3_DsKAiURk^&qidDZ8~K1u25zme#7_WmoY;zAV^riMX3iCPP0tB@7F? zIWc~{taS1~@`9xyM3v4yaAW3>jKNd}3GBO(Fx5 zc4y(`h zT-s~^GMkpihQ19c3gVcslXI3cF}9wv)HNa>`nWiM!U9%Oa~>nUQ4C!-t{$G7%Din=XcnO% zD1U^)vmj4O@k;A=97B4}>ptJ}(*ET5@+@ZP$T=ax+wYjdl=*%B0fH|#Rf(Te;Teb7 zW$tzH^RBRTbX($(c0Y~3(T@8_@U0=4;(6kQ&1}VD@ksy)@ zC&v57JK!CssyZi@Vi|93Nhnq02s;D84yuMpno{(nUSqBSYJ)HF8?Yo;neS>@t~wAxF$${)UCgX#h!NCY*$b(7v2Ku8jc=>;qJ^xU} z{?a{JI2alJRtg#a8xsuU|Gf)FJ8sG*fB|ms%ngR1O;|LCx*kO+tra%pWo8-QmrM@R zJdh$Ea?{&h%Xt=ITle@3c9~A@#qrE4aEEu?q8n!DV;+9Jvty(Ir2z zgh~&rmm>~Bw3YS~=hC%in}n0k7cK6ARN2z6q^y=&&&3Rpr~Y|KwE>H%ayfHv7RD3F zbqZUCNY-95ij+zck>e6tOey``zqEILF*?G8S^i36@mM^joO&cXqW%@m0&j`E1A67R z%z$GsQ9eSwLQPr4Q5E{oMkycABv-7mI^q?4h%JPztD}{LBRkI3ezrK3rS06syxdA9X>L~w=TIrj<3#d&0G!gn!@1qa)(?G|i5bAj z$nlrZ`{#&)Hl|j_0ERz$2|5~n$i|!;?0*zL=JipgWnyIhBOt7ACt+-EYWCiQk>ihu zl9REuDuCn9?jHq-zY8A=dzWX8nE-!ixc?S)|NAWbIi9nD(;srS^1E#OAv6CG71DP! z{zHBLA2VX^;OHc5rtk3g`epS0Dl`5y(f`adfbk#Qo?g`E-TGy2V+x>GGq(}6aWwxs z@W-vy%#EDP9RHHdKcw^j@BUaBK28-2^N0M+!o~<-ewXZ7*jU~bdwpVQ2gE1iVZ3wkkH}AI>+xAO9x)y-AE8 zk1_r}tA8u+|2)U|Z{2hMR^FKz-w(yRy#J4#E>qRoDqRho@9yPVjSrVx4u}21+2&>D zu_3+S!R}i+Hn-d`(YzCbk&A!Fq*}ykv^;#hicH$QU*Hb{VY5(40I&g6{t*P@C!a@1 zXhN{3puj+g=hXfwICH2)iXSai{Ikh$C11SXy?CdbQ!V=$`<>ol$Z^>^gqaZRo%bM3 zw{q=bMxM}%&$p8enoU)kbb@ctaW|(8>Galbk9+s~HMe*~FI7uT*y(pZ&znagJ!<;t zZ3uncwvT)WFD}!cb-QkQBRWZ2fxQr0&wlN*VOzy)`n}TsUaT05;FhU-lST8F^4z~& zoJ2gajTYLURvXE|ys2vm5G&nN{vdyQ2X|LhIxK{5t2II>@tWVJ?^{$yBoNtVOs z&XX8qqwWpTZJ)!N1DyV3P*C(r54U!U9EP9#D0zS6tUfG;aeXz1x;kvv#n>&vHDzLo z5xMd$G4*(<--wHec}YDu*&xi$0DlRRoSsH!Qn$6y!GwsNoiy@1(|3@}p{^w?3Dy+m zn^I3gZd6c-nF$Xr{**`&20~}io41d<#@4dW#=Q^=D?UbnDeRTyXA`B7nJG3RLSk>a zgi*}d{RrWi+~2P+js@ra-)3gUMegG-UJxrMzD1{zeGd|r_L+M|FIYj8>Z{JDRlM?B#Tb~MH-hy8548zX` z0}xupArW(G{0w z_6xeJ;LV!9vhC^awc27{sIXb<{<oCsMa`T-x?yOs&o^-mL+w(TN()!nKF3ZK(-j zOJeFXwUPN_WwJB!1FkKUg}3BxaI8+uQoNMd5uvPjBU;h!RG&-Gq4Km+fD3rxX@O|T zX_EvjMjTA(Y59Mu0#FmY z7KoxDN5rd;(5@Rt5~|3pBG_YoY3l)ecbKzQ*I*;}LWmShQHvc^S-#gPfz}D6`Z>K= z@`cDR^9k=@OloT-1CAqu{_49{o&r+)gUpG7p-d1so{%w!P~5apd6HTC zWB6G0Kc(tgXp%(@wV4z;xrdjB8BpN#X%txSbZu2?k5!rJIq2E^P&hUA+-x(YQO#(^ z3d+c|TNT$9T6zrI+($o^rHrNKSNX@eO&W|OnSk%wqgSm)E>fQs#)=ixgt;9hiNV#D zHG?%lT<-h~C9RhSGL@X@SG%YNnh8e17td`jH7z6x2p7ej7Szf)Y7S^%j4LJ}G$83K z#YKqtc9=*UUhL4%ES*hZXe~)Y?YXd5tkzL!HggK^Rhzson(>=WtrecrA}cAdafdNc zg}vWB1Ub_iMl@nZu2!lkx6<7=$Jb`+Gl&1a%APa_&wzX9o@pQ8Xe7(+MDS|4Z*%Ce zKh`0C0u+~ToHst@CH18Qu_lI_)}RlW6UePMPX8H79KkqdLv1uJ&I;8-tzRJO?Ib{9 zuYjPk$iwQekwB!^e%I~1BP!WWgO3LoKihdMMx3QV)Y&xxB;>{~m;5j@%I@7~JxC~5 zy4to!rqTjEQ_@s-rW0hZqL0qIE$wRI93jQ+&5z{40U%Z&jnIYM1q3^z>J<>Ki zPT{+pffy3DLT1KzpMN-LrhU^w!2R!^lCm_XkD#r|8}JCF9vtBwFO_jMrJw10P0Z9E z5%n<|{yUKW@dNx9$YzjTmmN&ZjJ|V(w7(d!J2Cjtl6di7sXwlE-J&u5{v7?a8;&p5P-|!O-Ev16 zYeBc|KjEQb%blKI-`&?}l;Lry&_Me;9WG@Y2UkZ6_o5g`z^7Umfu-li6Bmz0U)n65 zX+E6kg7fzDn4>LOM>>|E+WkEDw~+QsuqP!?Yv><}N5Aw(#6qN*Fjl4mOL?W8rrpEj z2wQxvGO;^e7_}B;6=LGU-On&9Jul5iE5~<=Z1S)vF+hGes#6vUEnLAW#n}!2@96xG z%>0!_CMM>;lgRX8m-su0e_9;=6E*$;2tPu9qH{4ZRS_}q|5xao<)0z`e*@0{bgTT! zS@Lhk#eZetkBKY&jn0K7MLzQIQT-?1_%DS1KV;>-hmxD)J3)}NF|qv^?89lJXl!c! z4iMjIjNrRR$M_E(^8q41I~W-|d_ZyvNuzfHV(#SjPnD9hot>4j^#?8agLM4mAoB75_Bc{0oHtX$tz+W_+OfzmMc!Ae`yHd^!Io`wJr@GYiXq zpzsY154(h=q^>&;$y(zP_qBL}>}&N)>G&v`uBbR&Jsikmo=|u&qj6$Fd0YvQpkOc% z^Fy$r-1-C~4YoiQyuxqCkoB3tCZ-^ng)8H7O)(pCMJIf<@UVt9<&9{F9~!UzHT5AEIW+1;~WypZ?#%Z&HtQazwLx{Mn4?cNPsLXnY-?i ze?u%DoAYRr?J{{RjfpJ=x>*nbFbds&9>A1LWl8v2n_;B5xu>0pcVb;ZJ0hYjt5RIM zzLyvXqV!_neD&M4G)6vp8m8AVao5~m-tW5*G1RW>yAcLBE>9h)#;8VuL7Yf3D5>9y zO@GCxHqA&{ub~xIgrLaL?Sd%x25SMQ`BKl?^fsIzJ@m*663G46(|1v%T=Tz_pLF zsO49YN54!#TO$!@Sp>Dn0~6M+QyRa-YF z1;r?!M_gVnJkLfBB~e}_&{B>|vosCGf5}{GkaIq1NV|uXw3YMX#m~_y>5leg3sje1c zcwh8_E_`)=iuMR|#+5NB2zI0UDu!0XhZ2R1{opVy5OM2FO(C6*cvEfRdC(H>P8!}P zl87vC1z(S_PXkFSh+m3nB1y0zP9Kb5v--k71#S}uW5x<{ZPsd+1t*6AT5=n7g(hDd zKSk-kCu-tW&ss7I4qgELynSoOLP}-MKCuS5fPft@6Afc#X$}F&(ZZ~#clNM&>S5Z{ z=p>uH(RtvC>b(ABbW|%2@wq+cx{!IlKknSG;h>Ye3OB3M)1bVKHvz?)i7WBqwz2&# zpkxK!d3-2jpMS@PCY0gn6Rlj5NN?i&VxVE%mv37q^_c21dMQ*9`X~9AP5gnt=bBm4 z-LFOMV6Bm1SoH*-rRpP^|fSOoL3O`9Ano*-Yn(ai8l1WWN z6hNY%8=Q$Jm~0vrg;G~O7`5xVsTnFxRp3rFW*a9#Dlt^>b|O5zY?WQ|r1cK=nYU}T zt%GqupQS7qJ0HWwWA_7b059Z1hqxSP%c1<4iJ(^J*?MJb?e+1m;L%bk@z1iR{SAlG zXS^*}PrB{9BUSiEn>DQ%;~=XX(1QcFPFiu zAAX63Y=ckS*c$bbU%ALTqPn)JIl5F$k@(e7|2`ydO7Q@1 zRuI#B&U(=XfFXL2^=&UNtA$tEkdk{IaC8g|A9AD&=LPmK#Jo9zd<9 zej-S94o^h>O)rZqt?0tr1d;hB08l!;-z?)8!@nP%yT`j>n5u zRKX^^5HnznYpyb!&pi#^>P}0M>m4oM9fu^!@2GuTz1EbcL_P;hVKI!k=(=5J86NF^ z6X{FyLq`wM*=iUkIH9_=Ew!ln3hB`V?{uQRL{;u}ebuv?Gq%Xbw%X`?o7IA-B_;G4 zB>(MqRoMX)cReps=38tV$=3vccDjR-B0j}MYeuwzM;wWOX6#-Lm|5M35T&kU*sxZN zv_F*yRTy;WC0ZZ#r=UIbZU&lR4#!2UMlfoMIK?niWCP&uHM}-4Gv66s5k7I-1jXqk zDms9iV}Xqo(da#RnovoABmq*x{Ylt_P>?B(TS!GyZ=Q*Qi6#)L&m2|{4+}Hrk*~q< zY;4(v6&}BXiX&in=$7rZN8hj{KJ1?w8#kci*HtiJ@oPNK>)WJ^b&LwHom7sm5IfQr zwVuee?Pfh1(-h%?@A4Ar1g_iQI#8qrE3OmEBuh4zIB%^X8t=$b*fjwC9?q+xeRE=yPoOI-puqDo^~5;=&9 zlghJt!Bk1l=7LUy02$QFVTKoz;ONNXcqfY&6Y=hjptE`3vPjO$+Q{9RqupHtDI~kI z{jGcLGa@|i%dOu_N;ZDy*X?(w!^T~_W|n4`_g5zKpPaAR0`z>B;>r7-{gBOl!Jm|v z48FFg{j8Un+%Mo5X30nzqarm!+K63p5>i-_NoL@uwogrpovxJZ&(ugs8g^vQXAA&_ z6&&`l2Vlg0V$yT;-a$>4@)Y|uw!>prm!I_jw6@|t#pk-TdnZEAQ*C*Y!gXKvV+y{) zc7v_W<`n7ONV`kS>!`yPf!STvzGED`z^)szz>ClZt!IK!wgmZ1CxHN>q&Snet-d1& zDHknTHf>$)%91>Rt)mkUj@;Pb0Ib6X(4iShj;Rp@VOqgtl6-`Sp&JD|$2RYSo3a$l z4Hb`_t>xgHs*w+K)mcMbsX31m-qWKU5|TmCGbW_+`LPpThZspr;`96XmR8rIHkH^3 zPTp;oIG`?cbYRS}3QqQ#&+VK3W!!5NviI9fs^{y*I3))EMt-p3I=VioJPd17p9M-? z9Fak8j?y|B*P(N@JkTWK;i4R)PCPI7hJbcG8tbb3Oo_T4nCxbEp>+`|XAhBq^)i0g zfNSa{P8B(t9yhei#&B+o!B z_#ky=@{+I4uvuJV=Q@^#5K|*g zz8367H~#2wQC^BVK0Rw`s~A0?mbvb{YMC%age$*)4)WsG)$X}N(~zc?N^hEsw)M2B zEnxx~%C1?Uj?sPS6}IIEl~?HV-00&jn>3s}Ku6%fXtFX}&KYEcrJf<_ypDo6mleM^07-@HOtDuYfBEyNwom~ICq7sD zG-XlIBj1?>3&#qkKbT$C8atY_aeTAs>ZX#bZt0-hoIo>6D}g2o-ZpEM2iJ1_e!jKB zXQ8L+!X5M-2{{@&Gb!SiAGEqWjK18FL1;WiSQ+Qm3ba!zFKKAgEq%59ec)Fws0`Kmu5H;bZ(%wsfqvBYOPSJCPsxUmZO?HUEvMM^ zApxDCGn)B<;J`Hp^#W+&!gnaj%-6Lfm9>BtK#C}<#nA|2W$EG@3{y247?dUki`m#sEHo} zeu0lY^HudlQ*d}VBA3&mF&3ywwCZz=5-zf)=X`6rT-o@<28D%|e0`G(+Tz$h7(I?6 z%pZFRaV+F)(f^e%B6OE?WVPUzqRcTH>T#?P1S(S~89o=l=~5P`UgmMLzpw183SlaT zHdH4ygAyuV-Tnfu-0kBvJ0zDzqjpSs#A?<`^X>)HRCnu0|8K*)LHc!mbnUeQt>xO1 zyIct0nLEVDvrH>I*<==LHydLAgO?DhHZ~{ygKa;RvHkG+WwWu2UH-u3-=ramCRJ-R z(70yHiHE`<%szgJ)RgZO!4#%Tcs7v8!^cxD_Lhue-L+p<{iqJs zY@e*f8JO>({O2gYr9CFM0cZj!ujZ3bW)FA}kzd4s(w_z$$4H(@W42=TTyuGple1k{Qph^4amC?xq z~r481HGP(8C?r9(!oon2nW)H7s{6_iL6Ztdvn*|-x0LWDr zrYg^X?#=2xe&r!-&|_&c{o01j(|Y|Z0UUy=!xV-`C3IYwoC&KW*)B%IW;7Dns01aC4M2#MQ zKI7NB)S(LIPtjVL><|Fv-dJ^f>iD&R@@%%f;aqW~bWDbtFv>_AUah+>fl^*uq;}Gv zVh3J!4tf%X+d-Ax0Z8r}vrbLCqA~LX3=U5+xLzcKQOeDDowQpp9i7G>VKc7?exrLy z7Ynp|EE#UuT~!j-%^{8JPXkZb8>lvpl4Gt@UgDJI?&+)iO7ysdUeAtnHp%!{7witcq3j5bPzc3($^uBqNr!X46XLlQ?h9`?6fS4pIO{?QB~5mE)WGOD5`YAP(eEjDB&N|xcnweO`! z>QykHID-LF3=GV4W#7N=3bL{y$1bqZG|0*fmm2FilxYO0jQN(c8RQ~UZsOymG>tJc zC)8>%aAgH!U_HT9&-)i7I|1N-5aq-_hCzE-eaeU^B`Z)%+Lk_FE0Xdu+$}AsdIh%*+&75$-G~T96>dnRC^bvOuoauEH={TL0Leysj}BU`ze-?!=S41BV)5P?lj8 z4a9`ldUyv3MPPwyswYGQpa00YB=g)xhF{sH6Il^237W*pHLEdr9>RPRrHI z#3ZR!>f3srV!@pdV}`>9gD>j%4tPNy|6_B)*3P^1q z_N;KChAnRUc2eh=+UKk#q>JraN?1kt0w(AMKr}M1gcc&Ls^F+}V?=*jTuGqa`V_(*D^}(J6bv%6i~du! zO|EJm3Y*;Oh$~NI*Pw<}^DK$8tUKf~oFc@Ycw#Ub3j<8=_34@tv?P%Q1;94Ee1#rF zu8#YKMO}FE5#4i;it4$URFbAqA-FQQkO7jal&(NgrE*IjnkgEIN=Dg1o8Uz|NV%c2CdFn8viWpt-$A;>NH-21$`2Ggek1&V~(Bc$|9 z3z((lktj^lDv8z?xh{wm(YtK!880u zG-=)dl2JtAXI})pPVnm-YtY|Z*@p)V*L>lz3uanhMc02yk`n&4lEx}BvuoSGHk%fg z@k+#K(yHlud1=dMlf}9HjGvv84ft>20Nkv=7J2r9-Y=3 zk^hJ{ealbUpVf+~etxOyEYM`{Mv5A4!J;!e4Z^4`$Xob4g^KS&h%$1(nfH0cQ{0{f zVbt<3@+ttin1Q>>lmDIo2wlem0#ffj^7;He0CpNopMh|4tdrSg!BICOKLq zcYLwAoLr}=zPN6k1wC8OZJ}jy}2dcQC-x;;z4G zwcl_pNUO23{#$Z{Xeqx-6Tgcm|H!f_Y0HUe3jb8gAi9sMjgiw2ZR9}_`QL!qxY&J5KdW6o zHM|Gq>~{(3e}vh9+&^)#|9hB?ll|{&;^cm?%){>kp7zhb?97~`?CjhSknQJ|9U^bB zLLfHC``9^1S$?F4+s4ByE{Lr6koz0Nf6bnduA>t$78K-Kd(gbzcWHn{N$B-a#%pE@`Ve3!3btAy`~Ty_R7Ijr|F_yfOVxa^r9ob!8G-Q(OCyguvFS@eEW zw~F`jyZvVf;l_5#$G%#`1)hM>%llMxobqSf zm8=+pfZ_phtAF<-25$;#%4HIl0t&Y#SZbvrqX{+t3-BB^<=OE1Xm2&R z|BHhY0GCiIv;-&4zwG{mI%|eAZiQ7JnMyp+_{-L}Z#P@2WCF*_a|7ne!y^sD8N&9h zoIYQ_W)!V`-s}-{V!O(AJQ_aF{*q5DE|R`rawZ_QP?P$$R+NT^hlQ|hdUcLq|I1;0 z)yqtE7=~O^*_LtQKEvn%_JR2jYWV!tAbCai={k13qsg|A3{#AFJ9tZN=8w_&Oi${` zQREBpg{&qfC%hZ48crjy!6^ovF+buk*?OC^Cr{V~v&-+H z3(NyC@!UedBVa}=)`ep;Y^>fVm?pE2qe6^(!@To`mKT{XroWO{f%G$;oGA#60K;$>r)<4`f>r zL*xV5Mk`g}Qbl&4ybGnr;3pfz}CzQ`7t zg?+fDeF3Jh^6cGPt@N42RWiz9%wt&qQfOK7Fm&h&AS^X@LUpVhi#6y#(6(dWSKeD< z39CoO&rSE;JfT0^I(&Vds_=oBlT}gjL)1=X{;8bDqh;Tiw~o2%6#{sTSK3|OwCZBE zA}mULtwWlnB#2(XROZasnm4psRvoz***Lp`UwHMl@ce(EZT&?hlwB@WPNmL45~%0F zKvf28pj1h4!u6hfkH1f=k`8=I)0exQPpX;og$(Pi8;+4Wp$}iO09M$Y(+4gJ?JM@fH z)mo#8kDu{1X^Y9V*1tiCSB>m^1e#2Pe=c_&6TtF_V(Y!);-XsQQO*U$R?Z2<)=e~B zpY0r|=&ITP@?YjT#TL(}_W{j&mC8PI+y=_k59y52&QoKQGgo)>4bD1#`|WXzx4&Oe z$uT0zM}Ot*)i-xJ{P&mk86-K$ajR~`^Pu|}UF+{rWTW5Lu5_3|R9i`jXFU=;w-eEb z(gc^&-Z=bkZ(YgL#O)L12s|*yEuS1sAlKea-{9hVuf>aG5u(+xnA2aF-m{>(OfD*+ zZA>Lmu`3BYarvyhTYHw@j?mYtffilZ^i3F#Lqulc9x4L4@o&I3mfrw2$Rq~H_4_RI z$K2sh01SlW`LDkk#P>$_k)nql3G@>#iU5bXAEh2rgt?`e-$IEGR*pxr03l#oISRx6 z%eN!<{-?aBOa2#|WJC5BUOkUz6O44fy~j6tH=IyPz$`!5Mc0z6hR)L4*DRXtU9aEB zFr@P|ZHRzSAe!Wa8cNWwPRI=cw)qj~78s$d2e2v!OS=%^HhyV~LegL-AA))1mE?lc zqVjen_7wvuTT#ucghtNRM@0$GOa;^{MMEJX{!D|fNlR|mH)S7+mE*VE7=Ay_Ag}H54TbO>0%#@ z@MpB`pRV%vFBboiZTn&9zp!mTM)EK1@!QjYKYZ(F{W(Q{_$Koo**0c2Hm+Z$-&+v2 zO=l+K#m-U~;6rL<%XzI!5#4aJe%;n3Yb4PkApV`_A}YWs8wXvI6c3*m5mD|jS1nvB z!y+ScBN49VYuzM0x4a^i)MO*g_qD9IuVEJtn)?LIk&s8mT`X;mF7=k_hGU5mzjrtl zdK0f+<~VoHUFSH9P(LpWB%~rtptR5Ax>|Qt5R^g3U%1+|4WsB=Tq${+P#qFFk{jf0 z-|qTtmT;PcRP>w2(f7s6&aVf~@SnW{;y=pWx+(0pAKM!|!Blp~{rK@hDCpdM!~Qku z9iaWFxKHBrlHf(a&8_0t(F78*=2-Rz;77{ zyHt68?CVm2=mMmCBBqn_?ZZwiS%>vPn|j&%C!oZ!&gN zA_HPfhzj-?3r7W3!ViBQtNLDGiu*(yhHlnuo!kbWK!qnUR^Vec3QOA5Tb(jpoTE=V zIvY;!-fl|f6cojQaumVL_{|}U>$h)m&=gRKbQjSjO#aYbTmRAhbHgRQnM%jF8}abRo$#9#SGHJ`#&CI?g7syrt(63>ALM%FSJcbwBtVqwE)+~Z5 zII_5CH%f(Yg`QGNKvp_FiSC6~!GhBfh8e@HFnxN&G{UGY9(E`ABv>guv_X>2vVpOr z$kI3#n;r>M9aKflnZ}4S9KgF)j{SU4SQA$zZQjOuG}Mx$OW!Jjm-o`o`oqZci?xd`y#+jy$PliA$YV4^STs*07uoC! zB=TuPOQDv~G-d}`P0~TN5D)1xF;k*Al`L!# zBUI5zEeqh&vvVZ#w{gJ89*@VBl!it>QJiU_WWr~q8d4B4M}5{mVG+lP&Dd)vBo==3 zwTO&Kpf5C#Cq%MyhEigqtC7e_oKrNK&c?YttD#iOJzSgmZbt>}z?x>D;u>T0wamoo8Ra?B3`&ydi4+-;utX2LMz@FJp(%Jts{(A z3@Mk0IBlethdOq0_|2GJ#FMXvYVp|ZZb)sdhSA*=n~z^>384%niaMM+(J&%44C{3T zL)D^>cq<8s?_>0;QW#x5CO|Gm3d%@TLN4+4^9B})IA>)-TViK1WPkXwoy$);r_s7s zO^j!6;CR-qdi3hsMNt)jw|IMlko|Oa?suziy*c0pWxlzE-PQYJOPd%OZHgB0gVXWu z-fErZ^J9K*Dp;qU9nH(T!#$D(9_yAz_%KXJ-$f-n#1QE5T<)pAWn-)ajq}G>yu@ZV zaqt=_HC{VaLw@Q}Hlc0#?Gb)eIongU+v&zbGq0zHE=I;olmVN8nCCoWZo;kn7*mPa#m^Wl*x(F1@t1A zQ)VZruqlS;&ZaiI;oG4&b8?lNTYuKT`Cjo++@DtmtmY7Rn`y&3=vP12Jko#YuP68A z!;ZktcY=#T+~M(ET{*q%L{Skwm9(;R-h)}%hKP5TvN7lcBlZ13Jy33xk;5#=S1+AD zldbXKE{lSntj$HAd=}ud--$|a?C)gkPS31A^9*>JE}EYopP%iS9iP?C6c>R&HeNa! zjmFQdL{t$Q3B49j#kiDcM9+Q_w0ILj+hV(|e^%iB)kbe(v#vcxgMFXdq4Z$j zO^5x#`;PmIq3^slUwsW0;a65JV{p(O88c2{#=(;XNS}FpifrFqXMOR)_pahHPIlF? z*3p?!{rHG_vzjSHLz2mp&f?3V?DQZhu`GOUM7sg|r82E#@=2@NCjoF-MjGsAc%l$C z6J!k62qn$@oRs>z$x2HUAvO7giT-IDe=WXp$}PwcQOqkm$-$IX{Q=z}nKaGtAXJ5QWN>W;ro`0hII28(t`Z1Se5NWIA zajob4zA?{d31bxJ`8G0+D5lplUu_u*+@8BK>XE6|ah2lYy_VE6!laJ>BqCv?VKq?l8(3aGKuZEtIYCK$`b z%?obnA>Ms{{?%plpele|v9@?c2aSRFofRSbE?&M=As^yZMo$L~X6x&KQy;|sK;GTl z$55f4%DJDlURA%GP#c{`j;=z+RpW|Y665Q9+>o365eLszN+26mdPmw5v_6mZ=y9?J z3Jkp=>QE&GFnrjVA4e{I2g$JG9sNr^xaI2jFYH=Ijy9Es+F^2X;{7Jrhi_du5CV^c zs{lw)I6Zi4W9Gr2XM!qGG@w#ZlbeZ_*&V2MBEJ#Z9l;RtrWm%mTieYqOoHKKTAp`Ds zk+<$lJ~0AFZV$<>QLK@UlGRwRe``-c2>0kEtKGxt;#v16C7=E*E2%>Yho2PhE>pgyr>UZOF zX|AtTXMVi7T7h~P&IX6zYiHNjFR+3vFb<k|+{aA5ipmzv z%UQI8!IO&^tMVXD-&;<7Bb&oo#t95|%!MO!KWNqRo3U%MSz&gDDLl$Is3(}us>OI0 z>8{2-wqXH-dij_OW#4#XL}MR(S&LcsU1e>R=Dky@DF6Pt;S^b}=$M^6bk7;cZ4#O| ze}{#y?NR3&bLaqV z{@(7FH5eseN-~ zF=*%csW)LJ2u>Ewkz!yRese0DKwB9wqll9d1Z^MF7s?sXzZmZ2@(S?M8oM%ZQ;W{( zWjHi^umdyxBUEJpjp(dt7%z59QQNp4lgfs71qh?U#H5&xu-h_21wAHetloQKB3SVs zlx@U@SdNOv4>J4>EVVqGH4l!6HIp*9@XOn*K8Q|jCw=-bYQKPMFoAV-H*j7xH*-es zE5}UWs`sYJQ*B_melKtDn8$IaumRa!C8!s1E?-Bb9$eaOy6JmtybDu^jp4suN2GqycCtdm@GJyPjFn|fq>QS^dOl1A3iG*s*iPWCnkC4{t}B-x5SYdD(igd`vlhlEdS^uMUNCfQS8!_Lj5F?#9lUtPU@)zg^ha?~ySDOt&SO z#q`~@sH}J=SzxvrANbP&AMwsmr`(5VUXpi}vG_2!!H?busv9esV0AX2PLj^?#DxM6 zP~cY9Fxmar5HXTo(^cxXhT8g=ZriEzo@FhJg;;zybDMfIn$6JJAlcK_N_?8)hM+`} z*4LdGbwnDN)*a-azkzD(I4iR%lDWgt}AQ}2P$D^TsQxQ}8R^g-QL8a2+I zghami;C9q>?{IGxju$KxSCVI|CSm0nM9$r5JJ`H#>b9{?#QCi@YOlN+vS`P5!GUKZ z4L>#RqDr8>Y5`;Ts%3G%`}No}sX0;B@s$0s8ePA=?d?4IyF2uE?J^xf4j5@&?d4C$ zh{bFrxHTJ8{Et*zhc5*ZSUx)0C_kg^I;VML>;jZdk(9Uy;S}8pDj?8XAs{0fN;A7Y z<42QA)<=OBzSr6re~TkRt=;8X^{AoUz>i@jzxlV645J|xFD{!WnMyA*DATa z5a(?|8@JC3ncRE=OX8b2t2@-sNAwp=cX`YM5??#MI^I0`#$R;T1?O)+)xtEauU>JV z^7x!wQl%dUyL%olFM!sc^`IV|UbKx_+-IYQ6c*}5Ca;pynVPy>YHH_=lhV8(Qo6co zeT!yCfbzlUe!8?UIyyQuWhvc2nWuId$U#X{+#8`M&95#OlsFk4B3^j)WvH}KA~Q2H zJ~=XBg0E3>UL&9g0#|L_d4`j{5l%EQ`8dzaB|`(>W`MCSbIkiX9ClCQS)bsknGa^d zk;YO7tl(hLYlXn)3u}eK2=3b&#n1HZ$)!BCH%&Z24$`?j{iP_XdmW&|~oB=LsUgN-wOm4fbD&pXCiPEO|aFehSP#34#U z-EczB$Ewsp+0Ppz90gP0$dK|)XC(3xC2|lyKN&*yAtfTjDRe$Y*56C(Gogqud`iVA z5s3Y~nnB_)K&^e+yr23~WHp{I`c&zIo$EPayO6E4TZa_HWRb+L1;^{lwY4tK< zX#}dRoJEjF6=H zia_zG|8cdRf!&idg4CvSt24%G=3qL)M?{h$@l9XFW{R*Zlmz+Jlkut9ODUL=pOzcH z-UyOrDQb{-qrr{G5{m#sda;{&E=&?fH(KgRqeRV45RfipAStx{{;Qo!3V*QQA`Qua5-}#Tf zduj>+9{%lxM^CxK4zkjJ>HHb~)bnHV=4_bI05UzkuL13MP-6MYuub$sqib)NXy9qa zd$+&1NIWg9SGixhbxc|C&IUtOxD>Qtq+a8r46*8pGes(YoTzM3xyh13NXYY?PkmOr z(xKH@H6n2Ar1go?UBOMDFDPEw+Ko0b#H!m&EI+LoLbD-=YuoOabcFy&zQsWG{ z^ci80zM(8%ItJPE(`tRs=-ze+KD9K-N411e`fhY$YMEE2J z*WEbIF&G+CQzY>mD;_%j)yZc48*LAMs_{!NqpMnplH3_`H}yT$%Unf$Ih{GYtG%RtT>2&wKy4hx127kDod0 z5RY9CuULP0+WKRuhu7>s#x>^;BI(cje(Z;UXY8C0zPlip6lBZ(!^Ds+2Nx*|=g*oS zwpn z?soGa-ux^HiC)6O!O8NANOO?k>4Q15GBB0_Sk@6PQx5-#XhNGFT95Sjqf66EL7i|) zGnsa!*Md!OLPC*|hUj9d92oI?-9W@rF;$1Yn0^#@I8|LCK{%Y(ZDSb*@^D=mt7Eq- zw=0)%oWt(9JKsiGEHYS=8lzbUo@!#U0-KzaUw;o8JIsEUUX}|i>U7dBXusdE)qt!Z zgO9Y@9hAeLZTkAyBmxvo@rt+FPF!xC=xGr5k zne^_lGB?*BfGhOgA58Nli5ky>sk_TzNFD?8#FLSzP`*apB zKCIksEP8jiH?%14BEXNY1&bhMn(^rYyjpjqU`{-G4ax#^P(0$<^jpfbeb;{<= zwj5lj>WFJ;YGId@QEJpY$j;^Wy4)?d)IQ4PA6520(qJp&3NT=%gBX zFUoGOTh~UreJZx2&o@!!iub0o-CL%iz}v~2ot?eFS6ce^Nz}odFZN=cu5*8#+ItN~ zg+uO&Sc5&iP$r}ZN&G0y0^y79O7++!;jlhzH#>2dGg>vuJihmE6e(vzyh?993B3kaH#jK_d|e zDKb~19gbep`y|(+`=%XQyl$v_V^*TPSy~o;;pc`RuH29_LL{mupQlEMj5IPR3q`aD zB!`p-oKV9#Te=%A54r}tf_3xPLU}Z_5Wvht@9Fa$T2YJYo@~VUami=u13#s{kvwB8 z!_y+-H8)zxf4}q1wUCnk!%B5d=1g8tUNx$J0ybvgFe|WbFEL#U4~aiY3q#7NK`R(- z2U;M<0E|PYlM**A+mz+ieN)?|0wBA9)#>}tN^;$he|-?YTEl+->J8WE35G$h#;KY| zO+3udB`6HZ=>Qwr*2>848R)N*L20iBHPj(u}dB zQ8Z19#x{@GdA^wJ|K{_d^l?;Kgi8dWvO`BILwb#IS@@kzS=~s}lD#MFr(NS{hZ+9@ z9T8>(6D{^_BbZW?^Km5`HiEwLd5=QIJ|DJvkPUoCqF%pw-S9rr=b3$cH-?T}utYmF z#`_%bHYrcsau0Sb!Gf?GW(Qt^HS?=8MJFLPC)ls%1(lW%=VTb{fmAbXAR_O$ZZdX;L>uWn*{WNf{#ta);~5Tt1ld57)!SYh9l zKKM$T{FHhz6-D)poM|#i0eH4QKLCA3&U<8!W(dpal!qEapZ{_9U%OK|L*N-G;YjMTmfd= zW#jgJ!AXrR!x@x#MFX?W7)R~k0w(Tt0j@W0+2Gny-)EBW(IN7o2_T*71+d`j9o1IV zt%x0~ccQJaLS0E+Qs#m6xvp=)yc~mHaFTI*KS0~BiO@76 zb%nmySj9~=_O!)Vz~Tf5+MXhlSSYI8sNos+dQ;x(+Nivg=iqL9c-j_&l4hX(!tAuL zJpyxJF1Dhqx*V6+{A+2#mOzcgyW{%FL?e+ov^J09*8%-F&z@m#?rgO?GjvGD?rE3J(2o zHrDYTt*}^0>V(=GC0YKaLIMhFKaEK_isdSbHZ`Ii_}2PqVY!(W9LuX4ZT;O41I^dh zs&_Bf!*Ex!L4d@B+C?Q12H6@0Mb|?O+|O;*TQzVMb7Kj&qMktqNa8!C5&E6Gtr!5W6$rqy}~fL7T58T?IyNvEQ-bPHAkLS zoK0Pe0jsO8yHl!x)B=?Bm$3wkUY9ZBW|y?M?&>wdbbUy9RbnzUIh30+W8<7((LXgTiWy+Db!fpo^-?KI zo20F%4awkL{4(|I&Cmjwv;0|jHf6??_HxZazw)!2f>)rXFJ3AkyYJub6ObSX3~9Kk z6u-%@SGRo4Q~C^Ryffx__Yelxos?(xtzwH!GTId|{KObvy34Csy`}`@sR{+HR@r;C zu3sSCn3@$3%$* zIcTLEpTEz(;_bOsftRh@@$KKQ0yHvh;+FH|lr7ld8-2gjHo~0ct(5h)6|0Om>TB=01%QOO;XuGc_;xx_RA6 zrP)jfXc2lXN!e;)SRP>J?7VV|k&Z%jRYI;nmfu~YiP6p{if+D$yDbZE3(v)6-+1SQ z{eeCSmKO&I2gZF~7x|@pR9dx0z3Q4xSLE<%4Q^JtwTUkR+8lgZs&~vxtUd*~yp)ok zf%KP=Sep#RQ%4kRZd)g9;cMQasB#hCC>ECttbe0_;%oV5qN>;&oN9=V-hCR~zzuf7LHzdEppa7yUi3x!Al>c&54`$Kt%_L? zHH%`L1oFuQwqpKJDY+Xdt=fh*vSp|kE@u+ut3&6=B(K5SXd;^#tvWXWMn9{mL>l?z z1LR^W{^&&vH~&DB+<1?#F({YH??M6c+XYgQa>8SD?51irYVG2oChk+8$a}>TtoYzY zGG^crn=L6Z`&hW2CSiWPs%?1=Bj*gI3&iJ9)Sgw9?pr^=Veppr9dgfU0Z zf)z73&IqfH%pZ$1m(J44;2`2unE(|OX`1d0#|A+29hLf#k!c%famz1ZV;0zU3QlMO zPXQymc8K7F+eW;LB>h(R@SVKY@9DPW9JFU}{v_iPJbGG=Z{jEk)(x2%uSqmsUq{N= zsgaj*(e^vT&@ys;@=`(W?^7YbQV!)o*$Rb60C^Nsqe6Gzq}TfHTcs!@3#Z9MdoR3T zc&9)MAYKyxBxLQMu`z+gV6I5wZdKpRt_A3s6OqukQ4h!q_hzV0LFDK{)hMPXQx`=k zS3mHPBaD6zX$sB4S=)dQBpNyMY;q~(EigE|`1Gd;QGpvW)@GzUwiB#hl5$GeB7A!t zPhLL<5_Xk|p5Lmz=&n3fu35J0vkNX~zuGQ`dspx}ctAzj&c_q+Q9s4#@p8Y#l*vhH zIijky8=s1cBSQWHjxU=;3rYiAkhUV{g)IhB!L{tk2u7enmkP3K6w?g*2Cr+AQp@9I zc5{hFbGqr;vIg#4xnr)Y?^xSig8cKO-**hB zS_rMpmhhP)rWd&#@1sOwcrlUP!a7Ve9&sa6~Qh_@rJITR}pq12Z2GC0KHVQ~T&Afz-w{U?G%mLC*+ExSUCv2O6 z^b^f1*qEfx0@=o7&a&CYq#MLC!IOOC;HgPI3NR%oH`%;^4U+L3{ArSp5)2QTmBCD< z1jI0cc|gPo+eRSb#BCuEas0L)=rCzpA9R?oZ3mi_c1~lnF4STG=S)^6Z=-_LB^z`z z%_iwn%p=%ZWSq0uTBI8^Gj$5J0ATRs5&$d=3Y=Ub1^a-urJTFioQvAzGp&m<$iX(C z>QwVoHrApx=}f#y*|cpWP_fh$wOkNenhdL0=J2F!VzGQ?#H4Jzc_&-bWOP!oc%~Jo zys#{}SUB?~n;&REnpHe=Vlq0>ypT-*)H$h33#OR#D8zd%m%&yvnJmdFo0&KHB*8qM z4YmjpzfmDm7qm3FDd`-*c31R;O3nauBk7#W)>ZU{LQVnXB{`*+X*X#qGv(UF+~%9S zrsP}w}B!igXJerA11azK2G*37(>wm{Aza8Fk!mryMS^3tF=5q4scheMP0w`|SNZ#Ahh^`#_&7}>X*f;l6SIIT zYI}G)VAHmxk;DEB-Ks2AaTk)Yyo%`%6k#APq83UpS6jZ7$+q_b{)S7n|LX;LhhNiNtCDu zuutlIb~sLQN;ymbs7cxfEC|)-m=X+I18NdGg-=P0z#~$y|yB;rfhC zyu=czT(SkxQ^2rz;we!c=gipK%oJm$wWym-Y4L1Si;zyo?$TH-d*oWy8GXNS1x>77b zraZ&X08^61iEN2%@w)PmSs(u}76dR&lH8S~Oq3 zG?^2)ELktrp4j>p)LL{#|J0&22iA#ED$Sw1mCBuxI}DKYPRNNPLVPmfoqT@i;zg)w ze72aA`Mfq1chEcW$%H~!DIO{B#3?Cxaw&3O1MlP(TPE13ZuR(MZj3E#hOYj&OUTUFV&jKF~$|T57Y|6QGk0duc3yt*1+N#!!e}? zY$vK)Adw;OJiI<^O`t~4hc$}YXPuArp~ixCdK!AVY>;EKdRf;%$5``^avu>0v3kJp zp%TLq^mw7e=WRwlA*zIp3zX>TTg%%_j7G>qdwJXIyQc4e)=KaVr4r6PfZdQ?6YlU4 zT`$pVt&F2;sb-5x$;`w$LV8%4-lq<{ttd-K*g-lyWDcyYG{E#a^rgp@M4rBb#j(=9 zVa2G@ZeH>?wJs{+xBC~|2XYmQHP$t)i;FCg+nKrB1F!sXccSJW@2qXlOD-7lSDO}& z?(D^`Z+vECm^0bl8&MIMB-r>y`RZg9xvT0bEIA>!!{IqEt9`BCwuAkAqd7Rkv%A+T z>DaDio%}5;)(LmlA;&pL4pdWeAjQhr^vSxile^xmi@ibzV|sZwbE2%Y8)bDVbTzx4 z$4oC0r1yRs%gp)xX!0@*rThC3PqER^fie`8oYBxw_^dcyf?^I1Ha3=>`#YR_1^2bp zv$xZnq712p7!}Y{^IS6vk3rx-PNHIZXz50XxV6}Y39dTX}F*dCaj(2$U8y{NTBHn24T8ohLDX2(zGk@O!iz1-<_ zUel_DvwH)Qcb_jbJ=-sny z$k2*h30={fV@S~oTM6}{m!%eq9=auHv8T!q*a}Av!x@y-BaHrt^X0W6agV)`0FExg zu}u0HbVKiqA$EEOU93g`o*_>w%$f$3{Uek$CuTTV_#{E1^v9GhPhMk3!?M4|R}GL> zgof!s8wdm%vf;vs!;l;Y%Jfq7z>2?QQACpNVP?j90v*&NsQBnqkkKKC&;j~0+D$mZ0o%g4X0J3p0bAl{Kuz|!17d3l`mGR5-Y(2#`yLY7M z2iK5~uYM$1J4CHWj+iLI$RxBLW-dwKYarvd%U+MB1Z61JGgSW2SM58DS5Lm>7VzV~@Jmxdn)cV0R5Y?M``K4z zK&C&udOx)Ozxtery(^?c0UvG_zujs6xZp|A$==k}1afDgHZcM@*^>epfgFs?q|}x! zE)LE-OiVv=Eu4%TEI}sDjP_0zG(Y>kh?5y4;*%38pSYN~I5RUFH#0Mb7$gZZGjp;+ zwhsvidCd&jvNJQYK(?%q|5+eqAiI8UAO3%M%>^j~Nt|r|N|3yVI#`7u`yh2cB#!@7 z|HH8%6n33%*-@@ zw9Rk0!|w{Qh!G^>mA%D}>V8<7^yj+I-wo>FWM+=^5DN(ja_0Z~A?09ahpZwqC;gen z3i-5#nEm00)b3w-%&c4w!Hxcu$MLXa?$6%=PEvvaoPIxa|1l7|d*t=KpgZ7dQK#-^-^% zd909u@#kxUK1#zVR1@y9j6aWAK literal 0 HcmV?d00001 diff --git a/doc/crypto/figure/interruptible_operation.pdf.license b/doc/crypto/figure/interruptible_operation.pdf.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation.puml new file mode 100644 index 00000000..a50a2f4d --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.puml @@ -0,0 +1,44 @@ +' SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml +!include atg-spec.pumh + +skinparam LegendFontSize 12 + +legend bottom + --""———""-- Solid lines show successful operation + ""---"" Dashed lines show error flows + ""………"" Dotted lines show operation cancellation +end legend + +state inactive as "//inactive//" +state starting as "//starting//" +state active as "//active//" +state finishing as "//finishing//" +state error as "//error//" ##darkred + +[*] --> inactive: **Initialize** +note as N1 + Operation object starts as + uninitialised memory +end note +inactive --> starting: **Setup** +starting --> starting: **Setup-complete**\n//incomplete// +starting --> active: **Setup-complete** +active --> active: **Update** +active --> finishing: **Finish**\n//incomplete// +active --> inactive: **Finish** +finishing --> finishing: **Finish**\n//incomplete// +finishing --> inactive: **Finish** +error -[#darkred,dashed]-> inactive: **Abort** +inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +starting -[#darkred,dashed]-> error: **Setup-complete**\n//fails// +active -[#darkred,dashed]-> error: **Update**\n//fails// +active -[#darkred,dashed]-> error: **Finish**\n//fails// +finishing -[#darkred,dashed]-> error: **Finish**\n//fails// +starting -[#blue,dotted]-> inactive: **Abort** +active -[#blue,dotted]-> inactive: **Abort** +finishing -[#blue,dotted]-> inactive: **Abort** + +@enduml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg new file mode 100644 index 00000000..575421bc --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg @@ -0,0 +1,19 @@ +inactivestartingactivefinishingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateFinishincompleteFinishAbortFinishincompleteFinishAbortAbortSetupfailsSetup-completefailsUpdatefailsFinishfailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/interruptible_operation.svg.license b/doc/crypto/figure/interruptible_operation.svg.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 90f084a2..e9610063 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -276,14 +276,100 @@ This specification defines interfaces for the following types of asymmetric cryp For asymmetric encryption, the API provides *single-part* functions. -For asymmetric signature, the API provides single-part functions. +For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). -For key agreement, the API provides single-part functions and an additional input method for a key-derivation operation. +For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. For key encapsulation, the API provides single-part functions. For PAKE, the API provides a *multi-part* operation. +.. _interruptible-operations: + +Interruptible operations +^^^^^^^^^^^^^^^^^^^^^^^^ + +Interruptible operations are APIs which split a single computationally-expensive operation into a sequence of separate function calls, each of which has a bounded execution time. Interruptible operations are useful in application contexts where responsiveness is critical, and techniques such a multi-threading are not available. This is achieved by limiting the amount of computational progress that is made for each function call. + +For some use cases, this can be achieved by controlling the amount of data processed by the function. For example, a hash can be computed using a multi-part operation to break up the computation into smaller blocks. For other use cases, the execution time is a consequence of the computational complexity of the algorithm, and a multi-part operation does not enable a caller to bound the runtime of each function. For example, verifying an asymmetric signature. + +.. note:: + + Unlike multi-part operations, an interruptible operation does not provide additional control over the inputs to an algorithm. If an application does not need to interrupt the operation, it is recommended that the appropriate single-part function is used. + +There are three components in an interruptible operation: + +* A specific object type to maintain the state of the operation, in a similar way to multi-part operations. These types are implementation-defined. +* A non-error status code, :code:`PSA_OPERATION_INCOMPLETE`, that is returned by some interruptible operation functions to indicate that the computation is incomplete. The same function must be called repeatedly until it returns either a success or an error status. +* The concept of a unit of work --- called *ops* --- that can be carried out by an interruptible operation function. The amount of computation done, or time duration, for one *op* is implementation- and function- specific, and can depend on the algorithm inputs, for example, the key size. + + An application can set an overall *max ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *max ops* value can also be queried. + + Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation object is set up for a new operation, which permits the value to be queried after an operation has finished. + +All interruptible operations follow the same pattern of use, which is shown in :numref:`fig-interruptible`. + +.. figure:: /figure/interruptible_operation.* + :name: fig-interruptible + + General state model for an interruptible operation + +The typical sequence of actions with a interruptible operation is as follows: + +1. **Allocate:** Allocate memory for an operation object of the appropriate type. The application can use any allocation strategy: stack, heap, static, etc. + +#. **Initialize:** Initialize or assign the operation object by one of the following methods: + + - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_INIT`` macro as the type is implementation-defined. + - Set it to all-bits zero. This is automatic if the object was allocated with ``calloc()``. + - Assign the value of the associated macro ``PSA_xxx_INIT``. + - Assign the result of calling the associated function ``psa_xxx_init()``. + + The resulting object is now *inactive*. + It is an error to initialize an operation object that is in *active* or *error* states. This can leak memory or other resources. + +#. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. + + On success, an operation object enters a *starting* state. On failure, the operation object will remain *inactive*. + +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. + + If the setup computation is interrupted, a the operation remains in *starting* state. If setup completes successfully, the operation enters an *active* state. On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the setup completes with success or an error status. + +#. **Update:** Update an *active* interruptible operation object. The update function can provide additional parameters, supply data for processing or generate outputs. + + On success, the operation object remains *active*. On failure, the operation object will enter an *error* state. + +#. **Finish:** To end an interruptible operation, call the applicable finishing function. This will take any final inputs, produce any final outputs, and then release any resources associated with the operation. + + If the finishing computation is interrupted, a the operation is left in *finishing* state. If finishing completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the finishing function completes with success or an error status. + +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. + + Any error that occurs to an operation while it is in an *active* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_abort()`` function to release the operation resources and return the object to the *inactive* state. + + ``psa_xxx_abort()`` can be called on an *inactive* operation, and this has no effect. + +Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the applicable setup functions again. + +If an interruptible operation object is not initialized before use, the behavior is undefined. + +If an interruptible operation function determines that the operation object is not in any valid state, it can return :code:`PSA_ERROR_CORRUPTION_DETECTED`. + +If an interruptible operation function is called with an operation object in the wrong state, the function will return :code:`PSA_ERROR_BAD_STATE` and the operation object will enter the *error* state. + +It is safe to move an interruptible operation object to a different memory location, for example, using a bitwise copy, and then to use the object in the new location. For example, an application can allocate an operation object on the stack and return it, or the operation object can be allocated within memory managed by a garbage collector. However, this does not permit the following behaviors: + +* Moving the object while a function is being called on the object. See also :secref:`concurrency`. +* Working with both the original and the copied operation objects. + +Each type of interruptible operation can have multiple *starting*, *active*, and *finishing* states. Documentation for the specific operation describes the setup, update and finishing functions, and any requirements about their usage and ordering. + +See :secref:`interruptible_example` for an example of using an interruptible operation. Randomness and key generation ----------------------------- From 2e35fe49f665514ccea0ea3a96c8a2fd22eaab89 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 25 Sep 2023 17:29:17 +0100 Subject: [PATCH 02/22] Add PSA_OPERATION_INCOMPLETE --- doc/crypto/api/library/status.rst | 2 ++ 1 file changed, 2 insertions(+) diff --git a/doc/crypto/api/library/status.rst b/doc/crypto/api/library/status.rst index a23e58e8..d6cf48f3 100644 --- a/doc/crypto/api/library/status.rst +++ b/doc/crypto/api/library/status.rst @@ -36,6 +36,8 @@ The following elements are defined in :file:`psa/error.h` from :cite-title:`PSA- #define PSA_ERROR_DATA_CORRUPT ((psa_status_t)-152) #define PSA_ERROR_DATA_INVALID ((psa_status_t)-153) + #define PSA_OPERATION_INCOMPLETE ((psa_status_t)-248) + These definitions must be available to an application that includes the :file:`psa/crypto.h` header file. .. admonition:: Implementation note From 799d17d2690324b4c28508bb7061f0ccf2e9a590 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 12 Oct 2023 17:18:13 +0100 Subject: [PATCH 03/22] crypto: Add interruptible asymmetric signature API Provide interruptible operations for message and hash signature calculation and verification. Reworked information in this section of the API relating to the behavior of different types of signature algorithm, consolidating this to the section introduction. --- doc/crypto/api.db/psa/crypto.h | 42 + doc/crypto/api/library/library.rst | 52 ++ doc/crypto/api/ops/signature.rst | 719 +++++++++++++++++- doc/crypto/appendix/history.rst | 1 + doc/crypto/figure/interruptible_operation.pdf | Bin 37853 -> 34550 bytes .../figure/interruptible_operation.puml | 36 +- doc/crypto/figure/interruptible_operation.svg | 38 +- doc/crypto/overview/functionality.rst | 24 +- 8 files changed, 857 insertions(+), 55 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index bd6ba5e5..19b92d77 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -24,6 +24,8 @@ typedef uint32_t psa_pake_primitive_t; typedef uint8_t psa_pake_primitive_type_t; typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; +typedef /* implementation-defined type */ psa_sign_interruptible_operation_t; +typedef /* implementation-defined type */ psa_verify_interruptible_operation_t; typedef struct psa_custom_key_parameters_t { uint32_t flags; } psa_custom_key_parameters_t; @@ -246,6 +248,7 @@ typedef struct psa_custom_key_parameters_t { /* specification-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_MAX_SIZE /* implementation-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_SIZE(alg) /* specification-defined value */ +#define PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED UINT32_MAX #define PSA_KEY_ATTRIBUTES_INIT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_CONTEXT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_COST /* implementation-defined value */ @@ -376,10 +379,14 @@ typedef struct psa_custom_key_parameters_t { #define PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ #define PSA_SIGNATURE_MAX_SIZE /* implementation-defined value */ +#define PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT \ + /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ #define PSA_TLS12_ECJPAKE_TO_PMS_OUTPUT_SIZE 32 #define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE /* implementation-defined value */ +#define PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT \ + /* implementation-defined value */ psa_status_t psa_aead_abort(psa_aead_operation_t * operation); psa_status_t psa_aead_decrypt(psa_key_id_t key, psa_algorithm_t alg, @@ -580,6 +587,8 @@ psa_status_t psa_import_key(const psa_key_attributes_t * attributes, const uint8_t * data, size_t data_length, psa_key_id_t * key); +uint32_t psa_interruptible_get_max_ops(void); +void psa_interruptible_set_max_ops(uint32_t max_ops); psa_status_t psa_key_agreement(psa_key_id_t private_key, const uint8_t * peer_key, size_t peer_key_length, @@ -724,6 +733,23 @@ psa_status_t psa_sign_hash(psa_key_id_t key, uint8_t * signature, size_t signature_size, size_t * signature_length); +psa_status_t psa_sign_interruptible_abort(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_complete(psa_sign_interruptible_operation_t * operation, + uint8_t * signature, + size_t signature_size, + size_t * signature_length); +uint32_t psa_sign_interruptible_get_num_ops(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_hash(psa_sign_interruptible_operation_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_sign_interruptible_operation_t psa_sign_interruptible_operation_init(void); +psa_status_t psa_sign_interruptible_setup(psa_sign_interruptible_operation_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); +psa_status_t psa_sign_interruptible_setup_complete(psa_sign_interruptible_operation_t * operation); +psa_status_t psa_sign_interruptible_update(psa_sign_interruptible_operation_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_sign_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, @@ -737,6 +763,22 @@ psa_status_t psa_verify_hash(psa_key_id_t key, size_t hash_length, const uint8_t * signature, size_t signature_length); +psa_status_t psa_verify_interruptible_abort(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_complete(psa_verify_interruptible_operation_t * operation); +uint32_t psa_verify_interruptible_get_num_ops(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_hash(psa_verify_interruptible_operation_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_verify_interruptible_operation_t psa_verify_interruptible_operation_init(void); +psa_status_t psa_verify_interruptible_setup(psa_verify_interruptible_operation_t * operation, + psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t * signature, + size_t signature_length); +psa_status_t psa_verify_interruptible_setup_complete(psa_verify_interruptible_operation_t * operation); +psa_status_t psa_verify_interruptible_update(psa_verify_interruptible_operation_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_verify_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index 2d734f67..b021b392 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -73,3 +73,55 @@ Library initialization .. warning:: The set of functions that depend on successful initialization of the library is :scterm:`IMPLEMENTATION DEFINED`. Applications that rely on calling functions before initializing the library might not be portable to other implementations. + + +Interruptible operation limit +----------------------------- + +.. todo:: Provide simple intro to these support functions + +See :secref:`interruptible-operations` + +.. function:: psa_interruptible_set_max_ops + + .. summary:: + Set the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. param:: uint32_t max_ops + The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. + + .. return:: void + + Interruptible functions use this value to limit the computation that is done in any single call to the function. If this limit is reached, the function will return :code:`PSA_OPERATION_INCOMPLETE`, and the caller must repeat the function call until a different status code is returned, or abort the operation. + + After initialization of the implementation, the maximum *ops* defaults to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_interruptible_set_max_ops()` to set a different limit. + + .. note:: + + The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_interruptible_get_num_ops()` and `psa_verify_interruptible_get_num_ops()` are provided to help with tuning this value. + + .. admonition:: Implementation note + + The interpretation of this maximum number is obviously also implementation defined. On a hard real-time system, this can indicate a hard deadline, which is good, as a real-time system needs a guarantee of not spending more than X time, however care must be taken to avoid the situation whereby calls just return, not being able to do any actual work within the allotted time. On a non-real-time system, the implementation can be more relaxed, but again whether this number should be interpreted as as hard or soft limit or even whether a less than or equals as regards to ops executed in a single call is implementation defined. + + .. warning:: + With implementations that interpret this number as a hard limit, setting this number too small can result in an infinite loop, whereby each call results in immediate return with no computation done. + +.. function:: psa_interruptible_get_max_ops + + .. summary:: + Get the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + .. return:: uint32_t + Maximum number of *ops* allowed to be executed by an interruptible function in a single call. + + This returns the value last set in a call to `psa_interruptible_set_max_ops()`. + +.. macro:: PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED + :definition: UINT32_MAX + + .. summary:: + + Maximum value for use with `psa_interruptible_set_max_ops()`. + + Using this value in a call to `psa_interruptible_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 4a74d74d..a51a9b51 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -78,7 +78,7 @@ The |API| provides several functions for calculating and verifying signatures: These functions can also be used on the specialized signature algorithms, with a hash or encoded-hash as input. See also `PSA_ALG_IS_SIGN_HASH()`. -See :secref:`single-part-signature`. +* The pair of `interruptible operations `, `psa_sign_interruptible_operation_t` and `psa_verify_interruptible_operation_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible_sign` and :secref:`interruptible_verify` for details on how to use these operations. .. _rsa-sign-algorithms: @@ -116,7 +116,7 @@ RSA signature algorithms .. summary:: The raw RSA PKCS#1 v1.5 signature algorithm, without hashing. - This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This signature scheme is defined by :RFC-title:`8017#8.2` under the name RSASSA-PKCS1-v1_5. @@ -334,7 +334,7 @@ ECDSA signature algorithms .. summary:: The randomized ECDSA signature scheme, without hashing. - This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions. + This specialized signature algorithm can be only used with the `psa_sign_hash()` and `psa_verify_hash()` functions, or with the interruptible asymmetric signature and verification operations. This algorithm is randomized: each invocation returns a different, equally valid signature. @@ -457,7 +457,7 @@ EdDSA signature algorithms .. versionadded:: 1.1 - This message signature algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions. + This message signature algorithm can be only used with the `psa_sign_message()` and `psa_verify_message()` functions, or with the interruptible asymmetric signature and verification operations. This is the PureEdDSA digital signature algorithm defined by :RFC-title:`8032`, using standard parameters. @@ -467,6 +467,11 @@ EdDSA signature algorithms * Edwards448: the Ed448 algorithm is computed with an empty string as the context. The output signature is a 114-byte string: the concatenation of :math:`R` and :math:`S` as defined by :RFC:`8032#5.2.6`. + .. note:: + When using an interruptible asymmetric signature operation with this algorithm, it is not possible to fragment the message data when calculating the signature. The message must be passed in a single call to `psa_sign_interruptible_update()`. + + However, it is possible to fragment the message data when verifying a signature using an interruptible asymmetric verification operation. + .. note:: To sign or verify the pre-computed hash of a message using EdDSA, the HashEdDSA algorithms (`PSA_ALG_ED25519PH` and `PSA_ALG_ED448PH`) can be used. @@ -565,8 +570,8 @@ EdDSA signature algorithms .. _single-part-signature: -Asymmetric signature functions ------------------------------- +Single-part asymmetric signature functions +------------------------------------------ .. function:: psa_sign_message @@ -801,6 +806,708 @@ Asymmetric signature functions Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. +.. _interruptible_sign: + +Interruptible asymmetric signature operations +--------------------------------------------- + +.. todo:: + + Decide how to calculate the signature of the zero-length message using the interruptible API. Either: + + * Implicitly, if neither `psa_sign_interruptible_hash()`, nor `psa_sign_interruptible_update()`, is called; OR + * Require that `psa_sign_interruptible_update()` is called with a zero-length input. + + In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_interruptible_complete()`. + + :issue:`Current preference for the latter` + +The interruptible asymmetric signature operation calculates the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. + +An interruptible asymmetric signature operation is used as follows: + +1. Allocate an interruptible asymmetric signature operation object, of type `psa_sign_interruptible_operation_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_sign_interruptible_operation_t`, for example, `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`. +#. Call `psa_sign_interruptible_setup()` to specify the algorithm and key. +#. Call `psa_sign_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_sign_interruptible_hash()` with a pre-computed hash of the message to sign; or + 2. Call `psa_sign_interruptible_update()` one or more times, passing a fragment of the message each time. The signature that is calculated will that be of the concatenation of these fragments, in order. +#. Call `psa_sign_interruptible_complete()` to finish calculating the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_sign_interruptible_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_sign_interruptible_operation_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric signature operation. + + Before calling any function on an interruptible asymmetric signature operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_sign_interruptible_operation_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_sign_interruptible_operation_t operation; + + * Initialize the object to the initializer `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`, for example: + + .. code-block:: xref + + psa_sign_interruptible_operation_t operation = PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT; + + * Assign the result of the function `psa_sign_interruptible_operation_init()` to the object, for example: + + .. code-block:: xref + + psa_sign_interruptible_operation_t operation; + operation = psa_sign_interruptible_operation_init(); + + This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_interruptible_operation_t`. + +.. function:: psa_sign_interruptible_operation_init + + .. summary:: + Return an initial value for an interruptible asymmetric signature operation object. + + .. return:: psa_sign_interruptible_operation_t + +.. function:: psa_sign_interruptible_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_sign_interruptible_setup()` or `psa_sign_interruptible_abort()`. + + This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_sign_interruptible_setup + + .. summary:: + Begin the setup of an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_interruptible_operation_t` and not yet in use. + .. param:: psa_key_id_t key + Identifier of the key to use for the operation. It must be an asymmetric key pair. The key must either permit the usage `PSA_KEY_USAGE_SIGN_HASH` or `PSA_KEY_USAGE_SIGN_MESSAGE`. + .. param:: psa_algorithm_t alg + An asymmetric signature algorithm: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN(alg)` is true. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation setup must now be completed by calling `psa_sign_interruptible_setup_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * The key has neither the `PSA_KEY_USAGE_SIGN_HASH` nor the `PSA_KEY_USAGE_SIGN_MESSAGE` usage flag. + * The key does not permit the requested algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``key`` is not supported for use with ``alg``. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not an asymmetric signature algorithm. + * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + + This function sets up the calculation of an asymmetric signature of a message or pre-computed hash. To verify an asymmetric signature against an expected value, use an interruptible asymmetric verification operation, see :secref:`interruptible_verify`. + + After a successful call to `psa_sign_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_interruptible_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_sign_interruptible_complete()`. + * A call to `psa_sign_interruptible_abort()`. + + If `psa_sign_interruptible_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_sign_interruptible_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to use. The operation must be in the process of being set up. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for input of data to sign. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must have started, but not yet finished. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation is ready for data input using a call to `psa_sign_interruptible_hash()` or `psa_sign_interruptible_update()`. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_sign_interruptible_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input. + .. param:: const uint8_t * hash + The input to sign. This is usually the hash of a message. + + See the description of this function, or the description of individual signature algorithms, for details of the acceptable inputs. + .. param:: size_t hash_length + Size of the ``hash`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for completion. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no data input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_SIGN_HASH` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow signing of a pre-computed hash. + * ``hash_length`` is not valid for the algorithm and key type. + * ``hash`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The implementation does not support signing of a pre-computed hash. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + :issue:`I don't think is necessary in this phase of the operation?` + + The application must complete the setup of the operation before calling this function. + + For hash-and-sign signature algorithms, the ``hash`` input to this function is the hash of the message to sign. The algorithm used to calculate this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output: :code:`hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg))`. + + Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. + + After input of the hash, the signature operation can be completed by calling `psa_sign_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + +.. function:: psa_sign_interruptible_update + + .. summary:: + Add a message fragment to an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to use. The operation must have been set up, with no hash value input. + .. param:: const uint8_t * input + Buffer containing the message fragment to add to the signature calculation. + .. param:: size_t input_length + Size of the ``input`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no hash value input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_SIGN_MESSAGE` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow signing of a message. + * The total input for the operation is too large for the signature algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The implementation does not support signing of a message. + * The total input for the operation is too large for the implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + :issue:`I don't think is necessary in this phase of the operation?` + + The application must complete the setup of the operation before calling this function. + + For message-signature algorithms that process the message data multiple times when computing a signature, `psa_sign_interruptible_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_sign_interruptible_update()`. + + After input of the message, the signature operation can be completed by calling `psa_sign_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + +.. function:: psa_sign_interruptible_complete + + .. summary:: + Attempt to finish the interruptible calculation of an asymmetric signature. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible asymmetric signature operation to use. The operation must have hash or message data input, or be in the process of finishing. + .. param:: uint8_t * signature + Buffer where the signature is to be written. + .. param:: size_t signature_size + Size of the ``signature`` buffer in bytes. This must be appropriate for the selected algorithm and key: + + * The required signature size is :code:`PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg)` where ``key_type`` and ``key_bits`` are attributes of the key, and ``alg`` is the algorithm used to calculate the signature. + * `PSA_SIGNATURE_MAX_SIZE` evaluates to the maximum signature size of any supported signature algorithm. + .. param:: size_t * signature_length + On success, the number of bytes that make up the returned signature value. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The first ``(*signature_length)`` bytes of ``signature`` contain the signature value. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_sign_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_BUFFER_TOO_SMALL + The size of the ``signature`` buffer is too small. + `PSA_SIGN_OUTPUT_SIZE()` or `PSA_SIGNATURE_MAX_SIZE` can be used to determine a sufficient buffer size. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the signature is returned in ``signature``, and the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no signature is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_sign_interruptible_abort + + .. summary:: + Abort an interruptible asymmetric signature operation. + + .. param:: psa_sign_interruptible_operation_t * operation + The interruptible signature operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_sign_interruptible_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_sign_interruptible_operation_t`. + + In particular, it is valid to call `psa_sign_interruptible_abort()` twice, or to call `psa_sign_interruptible_abort()` on an operation that has not been set up. + + +.. _interruptible_verify: + +Interruptible asymmetric verification operations +------------------------------------------------ + +The interruptible asymmetric verification operation verifies the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. + +An interruptible asymmetric verification operation is used as follows: + +1. Allocate an interruptible asymmetric verification operation object, of type `psa_verify_interruptible_operation_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_verify_interruptible_operation_t`, for example, `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`. +#. Call `psa_verify_interruptible_setup()` to specify the algorithm, key, and the signature to verify. +#. Call `psa_verify_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. Either: + + 1. Call `psa_verify_interruptible_hash()` with a pre-computed hash of the message to verify; or + 2. Call `psa_verify_interruptible_update()` one or more times, passing a fragment of the message each time. The signature is verified against the concatenation of these fragments, in order. +#. Call `psa_verify_interruptible_complete()` to finish verifying the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_interruptible_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_verify_interruptible_operation_t + + .. summary:: + The type of the state data structure for an interruptible asymmetric verification operation. + + Before calling any function on an interruptible asymmetric verification operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_verify_interruptible_operation_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_verify_interruptible_operation_t operation; + + * Initialize the object to the initializer `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`, for example: + + .. code-block:: xref + + psa_verify_interruptible_operation_t operation = PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT; + + * Assign the result of the function `psa_verify_interruptible_operation_init()` to the object, for example: + + .. code-block:: xref + + psa_verify_interruptible_operation_t operation; + operation = psa_verify_interruptible_operation_init(); + + This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_interruptible_operation_t`. + +.. function:: psa_verify_interruptible_operation_init + + .. summary:: + Return an initial value for an interruptible asymmetric verification operation object. + + .. return:: psa_verify_interruptible_operation_t + +.. function:: psa_verify_interruptible_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible asymmetric verification operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_verify_interruptible_setup()` or `psa_verify_interruptible_abort()`. + + This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_verify_interruptible_setup + + .. summary:: + Begin the setup of an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_interruptible_operation_t` and not yet in use. + .. param:: psa_key_id_t key + Identifier of the key to use for the operation. It must be an asymmetric key pair or asymmetric public key. The key must either permit the usage `PSA_KEY_USAGE_VERIFY_HASH` or `PSA_KEY_USAGE_VERIFY_MESSAGE`. + .. param:: psa_algorithm_t alg + An asymmetric signature algorithm: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_SIGN(alg)` is true. + .. param:: const uint8_t * signature + Buffer containing the signature to verify. + .. param:: size_t signature_length + Size of the ``signature`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation setup must now be completed by calling `psa_verify_interruptible_setup_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * The key has neither the `PSA_KEY_USAGE_VERIFY_HASH` nor the `PSA_KEY_USAGE_VERIFY_MESSAGE` usage flag. + * The key does not permit the requested algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not an asymmetric signature algorithm. + * ``key`` is not supported for use with ``alg``. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not an asymmetric signature algorithm. + * ``key`` is not an asymmetric key pair, or asymmetric public key, that is compatible with ``alg``. + * ``signature`` is not a valid signature for the algorithm and key. + + .. todo:: Decision required on handling invalid signatures. + + If the signature is clearly invalid (for the algorithm/key combination), an implementation could: + + 1. report ``PSA_ERROR_INVALID_ARGUMENT`` now, + 2. ``PSA_ERROR_INVALID_SIGNATURE`` now, or + 3. defer ``PSA_ERROR_INVALID_SIGNATURE`` until the operation is completed. + + Although (3) might appear to reduce information leakage (early return), it will still short cut all the calculations (as would happen in the single-part function). + + My preference is permitting an implementation to do (1)-or-(3), but we could instead permit (2)-or-(3) + + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + This function sets up the verification of an asymmetric signature of a message or pre-computed hash. To calculate an asymmetric signature, use an interruptible asymmetric signature operation, see :secref:`interruptible_sign`. + + After a successful call to `psa_verify_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_interruptible_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_verify_interruptible_complete()`. + * A call to `psa_verify_interruptible_abort()`. + + If `psa_verify_interruptible_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_verify_interruptible_setup_complete + + .. summary:: + Finish setting up an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to use. The operation must be in the process of being set up. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for input of data to verify. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must have started, but not yet finished. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation is ready for data input using a call to `psa_verify_interruptible_hash()` or `psa_verify_interruptible_update()`. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_verify_interruptible_hash + + .. summary:: + Input a pre-computed hash to an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to use. The operation must have been set up, with no data input. + .. param:: const uint8_t * hash + The input whose signature is to be verified. This is usually the hash of a message. + + See the description of this function, or the description of individual signature algorithms, for details of the acceptable inputs. + .. param:: size_t hash_length + Size of the ``hash`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation is now ready for completion. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no data input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_VERIFY_HASH` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow verification of a pre-computed hash. + * ``hash_length`` is not valid for the algorithm and key type. + * ``hash`` is not a valid input value for the algorithm and key type. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The implementation does not support verification of a pre-computed hash. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + The application must complete the setup of the operation before calling this function. + + For hash-and-sign signature algorithms, the ``hash`` input to this function is the hash of the message to verify. The algorithm used to calculate this hash is encoded in the signature algorithm. For such algorithms, ``hash_length`` must equal the length of the hash output: :code:`hash_length == PSA_HASH_LENGTH(PSA_ALG_GET_HASH(alg))`. + + Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. + + After input of the hash, the verification operation can be completed by calling `psa_verify_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + + +.. function:: psa_verify_interruptible_update + + .. summary:: + Add a message fragment to an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to use. The operation must have been set up, with no hash value input. + .. param:: const uint8_t * input + Buffer containing the message fragment to add to the verification. + .. param:: size_t input_length + Size of the ``input`` buffer in bytes. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation must be set up, with no hash value input. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_NOT_PERMITTED + The key does not have the `PSA_KEY_USAGE_VERIFY_MESSAGE` flag. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The algorithm does not allow verification of a message. + * The total input for the operation is too large for the signature algorithm. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The implementation does not support signing of a message. + * The total input for the operation is too large for the implementation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + The application must complete the setup of the operation before calling this function. + + For message-signature algorithms that process the message data multiple times when verifying a signature, `psa_verify_interruptible_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_verify_interruptible_update()`. + + After input of the message, the verification operation can be completed by calling `psa_verify_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + +.. function:: psa_verify_interruptible_complete + + .. summary:: + Attempt to finish the interruptible verification of an asymmetric signature. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to use. The operation must have hash or message data input, or be in the process of finishing. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The signature is valid. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_verify_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + The signature is not the result of signing the input message, or hash value, with the requested algorithm, using the private key corresponding to the key provided to the operation. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + +.. function:: psa_verify_interruptible_abort + + .. summary:: + Abort an interruptible asymmetric verification operation. + + .. param:: psa_verify_interruptible_operation_t * operation + The interruptible verification operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_verify_interruptible_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_verify_interruptible_operation_t`. + + In particular, it is valid to call `psa_verify_interruptible_abort()` twice, or to call `psa_verify_interruptible_abort()` on an operation that has not been set up. + Support macros -------------- diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index 187b0747..f820b31c 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -20,6 +20,7 @@ Changes to the API ~~~~~~~~~~~~~~~~~~ * Added `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` to evaluate the export buffer size for any asymmetric key pair or public key. +* Added interruptible operations for asymmetric sign and verify. See :secref:`sign` and :secref:`interruptible-operations`. * Add extended key-generation and key-derivation functions, `psa_generate_key_custom()` and `psa_key_derivation_output_key_custom()`, that accept additional parameters to control the key creation process. * Define a key production parameter to select a non-default exponent for RSA key generation. diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf index 81472000390439f607d973bdad5919eeb9a4b5c6..c5c9b415051e10e2d3a258069072815c8ca084d6 100644 GIT binary patch delta 16027 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 VFBfwQlvjIPKon|faYYG~{|ClRHJ|_h delta 18774 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 QW`D#Hni3tIl4//error//" ##darkred [*] --> inactive: **Initialize** @@ -23,22 +23,22 @@ note as N1 Operation object starts as uninitialised memory end note -inactive --> starting: **Setup** -starting --> starting: **Setup-complete**\n//incomplete// -starting --> active: **Setup-complete** -active --> active: **Update** -active --> finishing: **Finish**\n//incomplete// -active --> inactive: **Finish** -finishing --> finishing: **Finish**\n//incomplete// -finishing --> inactive: **Finish** +inactive --> setup: **Setup** +setup --> setup: **Setup-complete**\n//incomplete// +setup --> input: **Setup-complete** +input --> input: **Update** +input --> completing: **Complete**\n//incomplete// +input --> inactive: **Complete** +completing --> completing: **Complete**\n//incomplete// +completing --> inactive: **Complete** error -[#darkred,dashed]-> inactive: **Abort** inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// -starting -[#darkred,dashed]-> error: **Setup-complete**\n//fails// -active -[#darkred,dashed]-> error: **Update**\n//fails// -active -[#darkred,dashed]-> error: **Finish**\n//fails// -finishing -[#darkred,dashed]-> error: **Finish**\n//fails// -starting -[#blue,dotted]-> inactive: **Abort** -active -[#blue,dotted]-> inactive: **Abort** -finishing -[#blue,dotted]-> inactive: **Abort** +setup -[#darkred,dashed]-> error: **Setup-complete**\n//fails// +input -[#darkred,dashed]-> error: **Update**\n//fails// +input -[#darkred,dashed]-> error: **Complete**\n//fails// +completing -[#darkred,dashed]-> error: **Complete**\n//fails// +setup -[#blue,dotted]-> inactive: **Abort** +input -[#blue,dotted]-> inactive: **Abort** +completing -[#blue,dotted]-> inactive: **Abort** @enduml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg index 575421bc..ce2ed7c2 100644 --- a/doc/crypto/figure/interruptible_operation.svg +++ b/doc/crypto/figure/interruptible_operation.svg @@ -1,19 +1,19 @@ -inactivestartingactivefinishingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateFinishincompleteFinishAbortFinishincompleteFinishAbortAbortSetupfailsSetup-completefailsUpdatefailsFinishfailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file +inactivesetupinputcompletingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateCompleteincompleteCompleteAbortCompleteincompleteCompleteAbortAbortSetupfailsSetup-completefailsUpdatefailsCompletefailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index e9610063..00d16cce 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -303,7 +303,7 @@ There are three components in an interruptible operation: * A non-error status code, :code:`PSA_OPERATION_INCOMPLETE`, that is returned by some interruptible operation functions to indicate that the computation is incomplete. The same function must be called repeatedly until it returns either a success or an error status. * The concept of a unit of work --- called *ops* --- that can be carried out by an interruptible operation function. The amount of computation done, or time duration, for one *op* is implementation- and function- specific, and can depend on the algorithm inputs, for example, the key size. - An application can set an overall *max ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *max ops* value can also be queried. + An application can set an overall *maximum ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *maximum ops* value can also be queried. If the *maximum ops* is not set by an application, interruptible functions will not return until the operation is complete. Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation object is set up for a new operation, which permits the value to be queried after an operation has finished. @@ -334,25 +334,25 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. - If the setup computation is interrupted, a the operation remains in *starting* state. If setup completes successfully, the operation enters an *active* state. On failure, the operation object will enter an *error* state. + If the setup computation is interrupted, a the operation remains in *setup* state. If setup completes successfully, the operation enters an *input* state. On failure, the operation object will enter an *error* state. An application needs to repeat this step until the setup completes with success or an error status. -#. **Update:** Update an *active* interruptible operation object. The update function can provide additional parameters, supply data for processing or generate outputs. +#. **Update:** Update an interruptible operation object in *input* state. The update function can provide additional parameters, supply data for processing or generate outputs. - On success, the operation object remains *active*. On failure, the operation object will enter an *error* state. + On success, the operation object remains in *input* state. On failure, the operation object will enter an *error* state. -#. **Finish:** To end an interruptible operation, call the applicable finishing function. This will take any final inputs, produce any final outputs, and then release any resources associated with the operation. +#. **Complete:** To end an interruptible operation, call the applicable completion function. This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. - If the finishing computation is interrupted, a the operation is left in *finishing* state. If finishing completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. + If the finishing computation is interrupted, a the operation is left in *completing* state. If the operation completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. - An application needs to repeat this step until the finishing function completes with success or an error status. + An application needs to repeat this step until the completion function completes with success or an error status. -#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_interruptible_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. - Any error that occurs to an operation while it is in an *active* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_abort()`` function to release the operation resources and return the object to the *inactive* state. + Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_interruptible_abort()`` function to release the operation resources and return the object to the *inactive* state. - ``psa_xxx_abort()`` can be called on an *inactive* operation, and this has no effect. + ``psa_xxx_interruptible_abort()`` can be called on an *inactive* operation, and this has no effect. Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the applicable setup functions again. @@ -367,9 +367,9 @@ It is safe to move an interruptible operation object to a different memory locat * Moving the object while a function is being called on the object. See also :secref:`concurrency`. * Working with both the original and the copied operation objects. -Each type of interruptible operation can have multiple *starting*, *active*, and *finishing* states. Documentation for the specific operation describes the setup, update and finishing functions, and any requirements about their usage and ordering. +Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. -See :secref:`interruptible_example` for an example of using an interruptible operation. +See :secref:`interruptible_sign` for an example of using an interruptible operation. Randomness and key generation ----------------------------- From c05ae6c74ec1b2e93f1b21022098719e194413c7 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 9 Nov 2023 09:47:54 +0000 Subject: [PATCH 04/22] Xref interruptible operations from key usage flags --- doc/crypto/api/keys/policy.rst | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/doc/crypto/api/keys/policy.rst b/doc/crypto/api/keys/policy.rst index 274acf34..530b878e 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -192,6 +192,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_compute()` * `psa_mac_sign_setup()` * `psa_sign_message()` + * `psa_sign_interruptible_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -206,6 +207,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_verify()` * `psa_mac_verify_setup()` * `psa_verify_message()` + * `psa_verify_interruptible_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -218,6 +220,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to sign a pre-computed message hash in an asymmetric signature operation. The flag must be present on keys used with the following APIs: * `psa_sign_hash()` + * `psa_sign_interruptible_setup()` when signing a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_SIGN_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_SIGN_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_SIGN_MESSAGE`, and the flag `PSA_KEY_USAGE_SIGN_MESSAGE` will also be present when the application queries the usage flags of the key. @@ -232,6 +235,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to verify a pre-computed message hash in an asymmetric signature verification operation. The flag must be present on keys used with the following APIs: * `psa_verify_hash()` + * `psa_verify_interruptible_setup()`, when verifying the signature of a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_VERIFY_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_VERIFY_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_VERIFY_MESSAGE`, and the flag `PSA_KEY_USAGE_VERIFY_MESSAGE` will also be present when the application queries the usage flags of the key. From 322817121c83dfc1f53ef21139af6edb3e26bbef Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 17:13:22 +0100 Subject: [PATCH 05/22] Rework the addition of Interruptible operations to the functionality chapter --- doc/crypto/overview/functionality.rst | 130 +++++++++++++------------- 1 file changed, 66 insertions(+), 64 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 00d16cce..dcb2afc0 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -120,7 +120,7 @@ The API supports cryptographic operations through two kinds of interfaces: * A *multi-part operation* is a set of functions that work with a stored operation state. This provides more control over operation configuration, piecewise processing of large input data, or handling for multi-step processes. See :secref:`multi-part-operations`. -Depending on the mechanism, one or both kind of interfaces may be provided. +* An *interruptible operation* is also a set of functions that work with a stored operation state. However, these operations are for computationally expensive algorithms (for example, digital signatures), and enable the application to limit the computation performed in a single function call. See :secref:`interruptible-operations`. .. _single-part-functions: @@ -133,6 +133,7 @@ Single-part functions do not meet the needs of all use cases: * Some use cases involve messages that are too large to be assembled in memory, or require non-default configuration of the algorithm. These use cases require the use of a `multi-part operation `. +* Some use cases require that the time spent in a single function call is bounded. For some algorithms, this result can be achieved using a multi-part operation. For algorithms that involve computationally expensive steps, the use case requires the use of an `interruptible operation `. .. _multi-part-operations: @@ -221,69 +222,6 @@ It is safe to move a multi-part operation object to a different memory location, Each type of multi-part operation can have multiple *active* states. Documentation for the specific operation describes the configuration and update functions, and any requirements about their usage and ordering. -Symmetric cryptography -~~~~~~~~~~~~~~~~~~~~~~ - -This specification defines interfaces for the following types of symmetric -cryptographic operation: - -* Message digests, commonly known as hash functions. See :secref:`hashes`. -* Message authentication codes (MAC). See :secref:`macs`. -* Symmetric ciphers. See :secref:`ciphers`. -* Authenticated encryption with associated data (AEAD). See :secref:`aead`. -* Key derivation. See :secref:`kdf`. - -Key derivation only provides multi-part operation, to support the flexibility required by these type of algorithms. - -.. _symmetric-crypto-example: - -Example of the symmetric cryptography API -^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ - -Here is an example of a use case where a master key is used to generate both a message encryption key and an IV for the encryption, and the derived key and IV are then used to encrypt a message. - -1. Derive the message encryption material from the master key. - - a. Initialize a `psa_key_derivation_operation_t` object to zero or to `PSA_KEY_DERIVATION_OPERATION_INIT`. - #. Call `psa_key_derivation_setup()` with `PSA_ALG_HKDF` as the algorithm. - #. Call `psa_key_derivation_input_key()` with the step `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. - #. Call `psa_key_derivation_input_bytes()` with the step `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely identifies the message. - #. Populate a `psa_key_attributes_t` object with the derived message encryption key’s attributes. - #. Call `psa_key_derivation_output_key()` to create the derived message key. - #. Call `psa_key_derivation_output_bytes()` to generate the derived IV. - #. Call `psa_key_derivation_abort()` to release the key-derivation operation memory. - -#. Encrypt the message with the derived material. - - a. Initialize a `psa_cipher_operation_t` object to zero or to `PSA_CIPHER_OPERATION_INIT`. - #. Call `psa_cipher_encrypt_setup()` with the derived message encryption key. - #. Call `psa_cipher_set_iv()` using the derived IV retrieved above. - #. Call `psa_cipher_update()` one or more times to encrypt the message. - #. Call `psa_cipher_finish()` at the end of the message. - -#. Call `psa_destroy_key()` to clear the generated key. - -Asymmetric cryptography -~~~~~~~~~~~~~~~~~~~~~~~ - -This specification defines interfaces for the following types of asymmetric cryptographic operation: - -* Asymmetric encryption (also known as public-key encryption). See :secref:`pke`. -* Asymmetric signature. See :secref:`sign`. -* Two-way key agreement (also known as key establishment). See :secref:`key-agreement`. -* Key encapsulation. See :secref:`key-encapsulation`. -* Password-authenticated key exchange (PAKE). See :secref:`pake`. - -For asymmetric encryption, the API provides *single-part* functions. - -For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). - -For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. - -For key encapsulation, the API provides single-part functions. - -For PAKE, the API provides a *multi-part* operation. - .. _interruptible-operations: Interruptible operations @@ -371,6 +309,70 @@ Each type of interruptible operation can have multiple *setup*, *input*, and *co See :secref:`interruptible_sign` for an example of using an interruptible operation. +Symmetric cryptography +~~~~~~~~~~~~~~~~~~~~~~ + +This specification defines interfaces for the following types of symmetric +cryptographic operation: + +* Message digests, commonly known as hash functions. See :secref:`hashes`. +* Message authentication codes (MAC). See :secref:`macs`. +* Symmetric ciphers. See :secref:`ciphers`. +* Authenticated encryption with associated data (AEAD). See :secref:`aead`. +* Key derivation. See :secref:`kdf`. + +Key derivation only provides multi-part operation, to support the flexibility required by these type of algorithms. + +.. _symmetric-crypto-example: + +Example of the symmetric cryptography API +^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^ + +Here is an example of a use case where a master key is used to generate both a message encryption key and an IV for the encryption, and the derived key and IV are then used to encrypt a message. + +1. Derive the message encryption material from the master key. + + a. Initialize a `psa_key_derivation_operation_t` object to zero or to `PSA_KEY_DERIVATION_OPERATION_INIT`. + #. Call `psa_key_derivation_setup()` with `PSA_ALG_HKDF` as the algorithm. + #. Call `psa_key_derivation_input_key()` with the step `PSA_KEY_DERIVATION_INPUT_SECRET` and the master key. + #. Call `psa_key_derivation_input_bytes()` with the step `PSA_KEY_DERIVATION_INPUT_INFO` and a public value that uniquely identifies the message. + #. Populate a `psa_key_attributes_t` object with the derived message encryption key’s attributes. + #. Call `psa_key_derivation_output_key()` to create the derived message key. + #. Call `psa_key_derivation_output_bytes()` to generate the derived IV. + #. Call `psa_key_derivation_abort()` to release the key derivation operation memory. + +#. Encrypt the message with the derived material. + + a. Initialize a `psa_cipher_operation_t` object to zero or to `PSA_CIPHER_OPERATION_INIT`. + #. Call `psa_cipher_encrypt_setup()` with the derived message encryption key. + #. Call `psa_cipher_set_iv()` using the derived IV retrieved above. + #. Call `psa_cipher_update()` one or more times to encrypt the message. + #. Call `psa_cipher_finish()` at the end of the message. + +#. Call `psa_destroy_key()` to clear the generated key. + +Asymmetric cryptography +~~~~~~~~~~~~~~~~~~~~~~~ + +This specification defines interfaces for the following types of asymmetric cryptographic operation: + +* Asymmetric encryption (also known as public-key encryption). See :secref:`pke`. +* Asymmetric signature. See :secref:`sign`. +* Two-way key agreement (also known as key establishment). See :secref:`key-agreement`. +* Key encapsulation. See :secref:`key-encapsulation`. +* Password-authenticated key exchange (PAKE). See :secref:`pake`. + +For asymmetric encryption, the API provides *single-part* functions. + +For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). + +For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. + +For key encapsulation, the API provides single-part functions. + +For PAKE, the API provides a *multi-part* operation. + + Randomness and key generation ----------------------------- From 0f391ea4e483c9646ba0342cb33c3fd47accb01e Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 22:41:14 +0100 Subject: [PATCH 06/22] fixup: Incorrect section level --- doc/crypto/overview/functionality.rst | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index dcb2afc0..aca5989f 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -225,7 +225,7 @@ Each type of multi-part operation can have multiple *active* states. Documentati .. _interruptible-operations: Interruptible operations -^^^^^^^^^^^^^^^^^^^^^^^^ +~~~~~~~~~~~~~~~~~~~~~~~~ Interruptible operations are APIs which split a single computationally-expensive operation into a sequence of separate function calls, each of which has a bounded execution time. Interruptible operations are useful in application contexts where responsiveness is critical, and techniques such a multi-threading are not available. This is achieved by limiting the amount of computational progress that is made for each function call. From 4b92a270c2bcda23dc6478419feb8af2cf4d4016 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 13 May 2024 22:51:18 +0100 Subject: [PATCH 07/22] fixup: Fix residual uses of 'starting' state --- doc/crypto/overview/functionality.rst | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index aca5989f..618e5784 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -268,9 +268,9 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. - On success, an operation object enters a *starting* state. On failure, the operation object will remain *inactive*. + On success, an operation object enters a *setup* state. On failure, the operation object will remain *inactive*. -#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is *starting*. +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is in *setup* state. If the setup computation is interrupted, a the operation remains in *setup* state. If setup completes successfully, the operation enters an *input* state. On failure, the operation object will enter an *error* state. From 79f7de39cb3c64dff81f5a7746f86594bc85bf92 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 14 May 2024 14:00:11 +0100 Subject: [PATCH 08/22] Resolve some issues and todos * Align permitted errors with the single-part functions * Introduce the max_ops APIs --- doc/crypto/api/library/library.rst | 4 ++-- doc/crypto/api/ops/signature.rst | 19 ++++--------------- 2 files changed, 6 insertions(+), 17 deletions(-) diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index b021b392..00f548a9 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -78,9 +78,9 @@ Library initialization Interruptible operation limit ----------------------------- -.. todo:: Provide simple intro to these support functions +Using an interruptible operation, an application can perform an expensive cryptographic computation while limiting the execution time of each function call. The execution limit is controlled via the *maximum ops* value. -See :secref:`interruptible-operations` +See :secref:`interruptible-operations`. .. function:: psa_interruptible_set_max_ops diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index a51a9b51..067fe308 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -1029,7 +1029,6 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - :issue:`I don't think is necessary in this phase of the operation?` The application must complete the setup of the operation before calling this function. @@ -1080,7 +1079,6 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - :issue:`I don't think is necessary in this phase of the operation?` The application must complete the setup of the operation before calling this function. @@ -1286,19 +1284,8 @@ An interruptible asymmetric verification operation is used as follows: * ``alg`` is not an asymmetric signature algorithm. * ``key`` is not an asymmetric key pair, or asymmetric public key, that is compatible with ``alg``. * ``signature`` is not a valid signature for the algorithm and key. - - .. todo:: Decision required on handling invalid signatures. - - If the signature is clearly invalid (for the algorithm/key combination), an implementation could: - - 1. report ``PSA_ERROR_INVALID_ARGUMENT`` now, - 2. ``PSA_ERROR_INVALID_SIGNATURE`` now, or - 3. defer ``PSA_ERROR_INVALID_SIGNATURE`` until the operation is completed. - - Although (3) might appear to reduce information leakage (early return), it will still short cut all the calculations (as would happen in the single-part function). - - My preference is permitting an implementation to do (1)-or-(3), but we could instead permit (2)-or-(3) - + .. retval:: PSA_ERROR_INVALID_SIGNATURE + ``signature`` is not a valid signature for the algorithm and key. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED @@ -1334,6 +1321,8 @@ An interruptible asymmetric verification operation is used as follows: * The operation state is not valid: the operation setup must have started, but not yet finished. * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INVALID_SIGNATURE + The signature is not a valid signature for the algorithm and key. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED From f910088d28a7bbcdffafd154c85a81790fd87d89 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Jun 2024 14:03:40 +0100 Subject: [PATCH 09/22] Updated naming pattern for interruptible APIs: * Operation objects are now psa_xxx_iop_t, initialization PSA_XXX_IOP_INIT etc. * Operation and support functions use 'iop' as an infix instead of 'interruptible' --- doc/crypto/api.db/psa/crypto.h | 82 +++++---- doc/crypto/api/keys/policy.rst | 8 +- doc/crypto/api/library/library.rst | 18 +- doc/crypto/api/ops/signature.rst | 242 +++++++++++++------------- doc/crypto/overview/functionality.rst | 14 +- 5 files changed, 181 insertions(+), 183 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 19b92d77..8b283269 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -24,8 +24,8 @@ typedef uint32_t psa_pake_primitive_t; typedef uint8_t psa_pake_primitive_type_t; typedef uint8_t psa_pake_role_t; typedef uint8_t psa_pake_step_t; -typedef /* implementation-defined type */ psa_sign_interruptible_operation_t; -typedef /* implementation-defined type */ psa_verify_interruptible_operation_t; +typedef /* implementation-defined type */ psa_sign_iop_t; +typedef /* implementation-defined type */ psa_verify_iop_t; typedef struct psa_custom_key_parameters_t { uint32_t flags; } psa_custom_key_parameters_t; @@ -248,7 +248,7 @@ typedef struct psa_custom_key_parameters_t { /* specification-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_MAX_SIZE /* implementation-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_SIZE(alg) /* specification-defined value */ -#define PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED UINT32_MAX +#define PSA_IOP_MAX_OPS_UNLIMITED UINT32_MAX #define PSA_KEY_ATTRIBUTES_INIT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_CONTEXT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_COST /* implementation-defined value */ @@ -379,14 +379,12 @@ typedef struct psa_custom_key_parameters_t { #define PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ #define PSA_SIGNATURE_MAX_SIZE /* implementation-defined value */ -#define PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT \ - /* implementation-defined value */ +#define PSA_SIGN_IOP_INIT /* implementation-defined value */ #define PSA_SIGN_OUTPUT_SIZE(key_type, key_bits, alg) \ /* implementation-defined value */ #define PSA_TLS12_ECJPAKE_TO_PMS_OUTPUT_SIZE 32 #define PSA_TLS12_PSK_TO_MS_PSK_MAX_SIZE /* implementation-defined value */ -#define PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT \ - /* implementation-defined value */ +#define PSA_VERIFY_IOP_INIT /* implementation-defined value */ psa_status_t psa_aead_abort(psa_aead_operation_t * operation); psa_status_t psa_aead_decrypt(psa_key_id_t key, psa_algorithm_t alg, @@ -587,8 +585,8 @@ psa_status_t psa_import_key(const psa_key_attributes_t * attributes, const uint8_t * data, size_t data_length, psa_key_id_t * key); -uint32_t psa_interruptible_get_max_ops(void); -void psa_interruptible_set_max_ops(uint32_t max_ops); +uint32_t psa_iop_get_max_ops(void); +void psa_iop_set_max_ops(uint32_t max_ops); psa_status_t psa_key_agreement(psa_key_id_t private_key, const uint8_t * peer_key, size_t peer_key_length, @@ -733,23 +731,23 @@ psa_status_t psa_sign_hash(psa_key_id_t key, uint8_t * signature, size_t signature_size, size_t * signature_length); -psa_status_t psa_sign_interruptible_abort(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_complete(psa_sign_interruptible_operation_t * operation, - uint8_t * signature, - size_t signature_size, - size_t * signature_length); -uint32_t psa_sign_interruptible_get_num_ops(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_hash(psa_sign_interruptible_operation_t * operation, - const uint8_t * hash, - size_t hash_length); -psa_sign_interruptible_operation_t psa_sign_interruptible_operation_init(void); -psa_status_t psa_sign_interruptible_setup(psa_sign_interruptible_operation_t * operation, - psa_key_id_t key, - psa_algorithm_t alg); -psa_status_t psa_sign_interruptible_setup_complete(psa_sign_interruptible_operation_t * operation); -psa_status_t psa_sign_interruptible_update(psa_sign_interruptible_operation_t * operation, - const uint8_t * input, - size_t input_length); +psa_status_t psa_sign_iop_abort(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_complete(psa_sign_iop_t * operation, + uint8_t * signature, + size_t signature_size, + size_t * signature_length); +uint32_t psa_sign_iop_get_num_ops(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_hash(psa_sign_iop_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_sign_iop_t psa_sign_iop_init(void); +psa_status_t psa_sign_iop_setup(psa_sign_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg); +psa_status_t psa_sign_iop_setup_complete(psa_sign_iop_t * operation); +psa_status_t psa_sign_iop_update(psa_sign_iop_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_sign_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, @@ -763,22 +761,22 @@ psa_status_t psa_verify_hash(psa_key_id_t key, size_t hash_length, const uint8_t * signature, size_t signature_length); -psa_status_t psa_verify_interruptible_abort(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_complete(psa_verify_interruptible_operation_t * operation); -uint32_t psa_verify_interruptible_get_num_ops(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_hash(psa_verify_interruptible_operation_t * operation, - const uint8_t * hash, - size_t hash_length); -psa_verify_interruptible_operation_t psa_verify_interruptible_operation_init(void); -psa_status_t psa_verify_interruptible_setup(psa_verify_interruptible_operation_t * operation, - psa_key_id_t key, - psa_algorithm_t alg, - const uint8_t * signature, - size_t signature_length); -psa_status_t psa_verify_interruptible_setup_complete(psa_verify_interruptible_operation_t * operation); -psa_status_t psa_verify_interruptible_update(psa_verify_interruptible_operation_t * operation, - const uint8_t * input, - size_t input_length); +psa_status_t psa_verify_iop_abort(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_complete(psa_verify_iop_t * operation); +uint32_t psa_verify_iop_get_num_ops(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_hash(psa_verify_iop_t * operation, + const uint8_t * hash, + size_t hash_length); +psa_verify_iop_t psa_verify_iop_init(void); +psa_status_t psa_verify_iop_setup(psa_verify_iop_t * operation, + psa_key_id_t key, + psa_algorithm_t alg, + const uint8_t * signature, + size_t signature_length); +psa_status_t psa_verify_iop_setup_complete(psa_verify_iop_t * operation); +psa_status_t psa_verify_iop_update(psa_verify_iop_t * operation, + const uint8_t * input, + size_t input_length); psa_status_t psa_verify_message(psa_key_id_t key, psa_algorithm_t alg, const uint8_t * input, diff --git a/doc/crypto/api/keys/policy.rst b/doc/crypto/api/keys/policy.rst index 530b878e..1b6b6061 100644 --- a/doc/crypto/api/keys/policy.rst +++ b/doc/crypto/api/keys/policy.rst @@ -192,7 +192,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_compute()` * `psa_mac_sign_setup()` * `psa_sign_message()` - * `psa_sign_interruptible_setup()`, when signing a message. + * `psa_sign_iop_setup()`, when signing a message. For a key pair, this concerns the private key. @@ -207,7 +207,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. * `psa_mac_verify()` * `psa_mac_verify_setup()` * `psa_verify_message()` - * `psa_verify_interruptible_setup()`, when verifying the signature of a message. + * `psa_verify_iop_setup()`, when verifying the signature of a message. For a key pair, this concerns the public key. @@ -220,7 +220,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to sign a pre-computed message hash in an asymmetric signature operation. The flag must be present on keys used with the following APIs: * `psa_sign_hash()` - * `psa_sign_interruptible_setup()` when signing a pre-computed hash. + * `psa_sign_iop_setup()` when signing a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_SIGN_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_SIGN_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_SIGN_MESSAGE`, and the flag `PSA_KEY_USAGE_SIGN_MESSAGE` will also be present when the application queries the usage flags of the key. @@ -235,7 +235,7 @@ The usage flags are encoded in a bitmask, which has the type `psa_key_usage_t`. This flag is required to use the key to verify a pre-computed message hash in an asymmetric signature verification operation. The flag must be present on keys used with the following APIs: * `psa_verify_hash()` - * `psa_verify_interruptible_setup()`, when verifying the signature of a pre-computed hash. + * `psa_verify_iop_setup()`, when verifying the signature of a pre-computed hash. This flag automatically sets `PSA_KEY_USAGE_VERIFY_MESSAGE`: if an application sets the flag `PSA_KEY_USAGE_VERIFY_HASH` when creating a key, then the key always has the permissions conveyed by `PSA_KEY_USAGE_VERIFY_MESSAGE`, and the flag `PSA_KEY_USAGE_VERIFY_MESSAGE` will also be present when the application queries the usage flags of the key. diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index 00f548a9..c01b81e8 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -82,23 +82,23 @@ Using an interruptible operation, an application can perform an expensive crypto See :secref:`interruptible-operations`. -.. function:: psa_interruptible_set_max_ops +.. function:: psa_iop_set_max_ops .. summary:: Set the maximum number of *ops* allowed to be executed by an interruptible function in a single call. .. param:: uint32_t max_ops - The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. + The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_IOP_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. .. return:: void Interruptible functions use this value to limit the computation that is done in any single call to the function. If this limit is reached, the function will return :code:`PSA_OPERATION_INCOMPLETE`, and the caller must repeat the function call until a different status code is returned, or abort the operation. - After initialization of the implementation, the maximum *ops* defaults to `PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_interruptible_set_max_ops()` to set a different limit. + After initialization of the implementation, the maximum *ops* defaults to `PSA_IOP_MAX_OPS_UNLIMITED`. This means that the whole operation will complete in a single call, regardless of the number of *ops* required. An application must call `psa_iop_set_max_ops()` to set a different limit. .. note:: - The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_interruptible_get_num_ops()` and `psa_verify_interruptible_get_num_ops()` are provided to help with tuning this value. + The time taken to execute a single *op* is implementation specific and depends on software, hardware, the algorithm, key type and curve chosen. Even within a single operation, successive ops can take differing amounts of time. The only guarantee is that lower values for ``max_ops`` means functions will block for a lesser maximum amount of time and conversely larger values will mean blocking for a larger maximum amount of time. The functions `psa_sign_iop_get_num_ops()` and `psa_verify_iop_get_num_ops()` are provided to help with tuning this value. .. admonition:: Implementation note @@ -107,7 +107,7 @@ See :secref:`interruptible-operations`. .. warning:: With implementations that interpret this number as a hard limit, setting this number too small can result in an infinite loop, whereby each call results in immediate return with no computation done. -.. function:: psa_interruptible_get_max_ops +.. function:: psa_iop_get_max_ops .. summary:: Get the maximum number of *ops* allowed to be executed by an interruptible function in a single call. @@ -115,13 +115,13 @@ See :secref:`interruptible-operations`. .. return:: uint32_t Maximum number of *ops* allowed to be executed by an interruptible function in a single call. - This returns the value last set in a call to `psa_interruptible_set_max_ops()`. + This returns the value last set in a call to `psa_iop_set_max_ops()`. -.. macro:: PSA_INTERRUPTIBLE_MAX_OPS_UNLIMITED +.. macro:: PSA_IOP_MAX_OPS_UNLIMITED :definition: UINT32_MAX .. summary:: - Maximum value for use with `psa_interruptible_set_max_ops()`. + Maximum value for use with `psa_iop_set_max_ops()`. - Using this value in a call to `psa_interruptible_set_max_ops()` will cause interruptible functions to complete their calculation before returning. + Using this value in a call to `psa_iop_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 067fe308..cbf6073a 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -78,7 +78,7 @@ The |API| provides several functions for calculating and verifying signatures: These functions can also be used on the specialized signature algorithms, with a hash or encoded-hash as input. See also `PSA_ALG_IS_SIGN_HASH()`. -* The pair of `interruptible operations `, `psa_sign_interruptible_operation_t` and `psa_verify_interruptible_operation_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible_sign` and :secref:`interruptible_verify` for details on how to use these operations. +* The pair of `interruptible operations `, `psa_sign_iop_t` and `psa_verify_iop_t`, enable the signature of a message, or pre-computed hash, to be calculated and verified in an interruptible manner. See :secref:`interruptible-sign` and :secref:`interruptible-verify` for details on how to use these operations. .. _rsa-sign-algorithms: @@ -468,7 +468,7 @@ EdDSA signature algorithms * Edwards448: the Ed448 algorithm is computed with an empty string as the context. The output signature is a 114-byte string: the concatenation of :math:`R` and :math:`S` as defined by :RFC:`8032#5.2.6`. .. note:: - When using an interruptible asymmetric signature operation with this algorithm, it is not possible to fragment the message data when calculating the signature. The message must be passed in a single call to `psa_sign_interruptible_update()`. + When using an interruptible asymmetric signature operation with this algorithm, it is not possible to fragment the message data when calculating the signature. The message must be passed in a single call to `psa_sign_iop_update()`. However, it is possible to fragment the message data when verifying a signature using an interruptible asymmetric verification operation. @@ -806,7 +806,7 @@ Single-part asymmetric signature functions Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. -.. _interruptible_sign: +.. _interruptible-sign: Interruptible asymmetric signature operations --------------------------------------------- @@ -815,10 +815,10 @@ Interruptible asymmetric signature operations Decide how to calculate the signature of the zero-length message using the interruptible API. Either: - * Implicitly, if neither `psa_sign_interruptible_hash()`, nor `psa_sign_interruptible_update()`, is called; OR - * Require that `psa_sign_interruptible_update()` is called with a zero-length input. + * Implicitly, if neither `psa_sign_iop_hash()`, nor `psa_sign_iop_update()`, is called; OR + * Require that `psa_sign_iop_update()` is called with a zero-length input. - In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_interruptible_complete()`. + In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_iop_complete()`. :issue:`Current preference for the latter` @@ -826,19 +826,19 @@ The interruptible asymmetric signature operation calculates the signature of a m An interruptible asymmetric signature operation is used as follows: -1. Allocate an interruptible asymmetric signature operation object, of type `psa_sign_interruptible_operation_t`, which will be passed to all the functions listed here. -#. Initialize the operation object with one of the methods described in the documentation for `psa_sign_interruptible_operation_t`, for example, `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`. -#. Call `psa_sign_interruptible_setup()` to specify the algorithm and key. -#. Call `psa_sign_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +1. Allocate an interruptible asymmetric signature operation object, of type `psa_sign_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_sign_iop_t`, for example, `PSA_SIGN_IOP_INIT`. +#. Call `psa_sign_iop_setup()` to specify the algorithm and key. +#. Call `psa_sign_iop_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. #. Either: - 1. Call `psa_sign_interruptible_hash()` with a pre-computed hash of the message to sign; or - 2. Call `psa_sign_interruptible_update()` one or more times, passing a fragment of the message each time. The signature that is calculated will that be of the concatenation of these fragments, in order. -#. Call `psa_sign_interruptible_complete()` to finish calculating the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. -#. If an error occurs at any stage, or to terminate the operation early, call `psa_sign_interruptible_abort()`. + 1. Call `psa_sign_iop_hash()` with a pre-computed hash of the message to sign; or + 2. Call `psa_sign_iop_update()` one or more times, passing a fragment of the message each time. The signature that is calculated will that be of the concatenation of these fragments, in order. +#. Call `psa_sign_iop_complete()` to finish calculating the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_sign_iop_abort()`. -.. typedef:: /* implementation-defined type */ psa_sign_interruptible_operation_t +.. typedef:: /* implementation-defined type */ psa_sign_iop_t .. summary:: The type of the state data structure for an interruptible asymmetric signature operation. @@ -849,67 +849,67 @@ An interruptible asymmetric signature operation is used as follows: .. code-block:: xref - psa_sign_interruptible_operation_t operation; + psa_sign_iop_t operation; memset(&operation, 0, sizeof(operation)); * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: .. code-block:: xref - static psa_sign_interruptible_operation_t operation; + static psa_sign_iop_t operation; - * Initialize the object to the initializer `PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT`, for example: + * Initialize the object to the initializer `PSA_SIGN_IOP_INIT`, for example: .. code-block:: xref - psa_sign_interruptible_operation_t operation = PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT; + psa_sign_iop_t operation = PSA_SIGN_IOP_INIT; - * Assign the result of the function `psa_sign_interruptible_operation_init()` to the object, for example: + * Assign the result of the function `psa_sign_iop_init()` to the object, for example: .. code-block:: xref - psa_sign_interruptible_operation_t operation; - operation = psa_sign_interruptible_operation_init(); + psa_sign_iop_t operation; + operation = psa_sign_iop_init(); This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. -.. macro:: PSA_SIGN_INTERRUPTIBLE_OPERATION_INIT +.. macro:: PSA_SIGN_IOP_INIT :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_interruptible_operation_t`. + This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_iop_t`. -.. function:: psa_sign_interruptible_operation_init +.. function:: psa_sign_iop_init .. summary:: Return an initial value for an interruptible asymmetric signature operation object. - .. return:: psa_sign_interruptible_operation_t + .. return:: psa_sign_iop_t -.. function:: psa_sign_interruptible_get_num_ops +.. function:: psa_sign_iop_get_num_ops .. summary:: Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. - After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_sign_interruptible_setup()` or `psa_sign_interruptible_abort()`. + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_sign_iop_setup()` or `psa_sign_iop_abort()`. - This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. The value is undefined if the operation object has not been initialized. -.. function:: psa_sign_interruptible_setup +.. function:: psa_sign_iop_setup .. summary:: Begin the setup of an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation - The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_interruptible_operation_t` and not yet in use. + .. param:: psa_sign_iop_t * operation + The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_iop_t` and not yet in use. .. param:: psa_key_id_t key Identifier of the key to use for the operation. It must be an asymmetric key pair. The key must either permit the usage `PSA_KEY_USAGE_SIGN_HASH` or `PSA_KEY_USAGE_SIGN_MESSAGE`. .. param:: psa_algorithm_t alg @@ -918,7 +918,7 @@ An interruptible asymmetric signature operation is used as follows: .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The operation setup must now be completed by calling `psa_sign_interruptible_setup_complete()`. + The operation setup must now be completed by calling `psa_sign_iop_setup_complete()`. .. retval:: PSA_ERROR_INVALID_HANDLE ``key`` is not a valid key identifier. .. retval:: PSA_ERROR_NOT_PERMITTED @@ -944,21 +944,21 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY - This function sets up the calculation of an asymmetric signature of a message or pre-computed hash. To verify an asymmetric signature against an expected value, use an interruptible asymmetric verification operation, see :secref:`interruptible_verify`. + This function sets up the calculation of an asymmetric signature of a message or pre-computed hash. To verify an asymmetric signature against an expected value, use an interruptible asymmetric verification operation, see :secref:`interruptible-verify`. - After a successful call to `psa_sign_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_interruptible_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + After a successful call to `psa_sign_iop_setup()`, the operation is in setup state. Setup can be completed by calling `psa_sign_iop_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: - * A successful call to `psa_sign_interruptible_complete()`. - * A call to `psa_sign_interruptible_abort()`. + * A successful call to `psa_sign_iop_complete()`. + * A call to `psa_sign_iop_abort()`. - If `psa_sign_interruptible_setup()` returns an error, the operation object is unchanged. + If `psa_sign_iop_setup()` returns an error, the operation object is unchanged. -.. function:: psa_sign_interruptible_setup_complete +.. function:: psa_sign_iop_setup_complete .. summary:: Finish setting up an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must be in the process of being set up. .. return:: psa_status_t @@ -983,18 +983,18 @@ An interruptible asymmetric signature operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - When this function returns successfully, the operation is ready for data input using a call to `psa_sign_interruptible_hash()` or `psa_sign_interruptible_update()`. + When this function returns successfully, the operation is ready for data input using a call to `psa_sign_iop_hash()` or `psa_sign_iop_update()`. If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. - The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. -.. function:: psa_sign_interruptible_hash +.. function:: psa_sign_iop_hash .. summary:: Input a pre-computed hash to an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input. .. param:: const uint8_t * hash The input to sign. This is usually the hash of a message. @@ -1036,16 +1036,16 @@ An interruptible asymmetric signature operation is used as follows: Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. - After input of the hash, the signature operation can be completed by calling `psa_sign_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + After input of the hash, the signature operation can be completed by calling `psa_sign_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. -.. function:: psa_sign_interruptible_update +.. function:: psa_sign_iop_update .. summary:: Add a message fragment to an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have been set up, with no hash value input. .. param:: const uint8_t * input Buffer containing the message fragment to add to the signature calculation. @@ -1082,18 +1082,18 @@ An interruptible asymmetric signature operation is used as follows: The application must complete the setup of the operation before calling this function. - For message-signature algorithms that process the message data multiple times when computing a signature, `psa_sign_interruptible_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_sign_interruptible_update()`. + For message-signature algorithms that process the message data multiple times when computing a signature, `psa_sign_iop_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_sign_iop_update()`. - After input of the message, the signature operation can be completed by calling `psa_sign_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + After input of the message, the signature operation can be completed by calling `psa_sign_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. -.. function:: psa_sign_interruptible_complete +.. function:: psa_sign_iop_complete .. summary:: Attempt to finish the interruptible calculation of an asymmetric signature. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have hash or message data input, or be in the process of finishing. .. param:: uint8_t * signature Buffer where the signature is to be written. @@ -1114,7 +1114,7 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_sign_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_sign_iop_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_BUFFER_TOO_SMALL The size of the ``signature`` buffer is too small. @@ -1132,16 +1132,16 @@ An interruptible asymmetric signature operation is used as follows: When this function returns successfully, the signature is returned in ``signature``, and the operation becomes inactive. If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no signature is returned, and this function must be called again to continue the operation. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. - The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. -.. function:: psa_sign_interruptible_abort +.. function:: psa_sign_iop_abort .. summary:: Abort an interruptible asymmetric signature operation. - .. param:: psa_sign_interruptible_operation_t * operation + .. param:: psa_sign_iop_t * operation The interruptible signature operation to abort. .. return:: psa_status_t @@ -1153,14 +1153,14 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The library requires initializing by a call to `psa_crypto_init()`. - Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_sign_interruptible_setup()` again. + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_sign_iop_setup()` again. - This function can be called at any time after the operation object has been initialized as described in `psa_sign_interruptible_operation_t`. + This function can be called at any time after the operation object has been initialized as described in `psa_sign_iop_t`. - In particular, it is valid to call `psa_sign_interruptible_abort()` twice, or to call `psa_sign_interruptible_abort()` on an operation that has not been set up. + In particular, it is valid to call `psa_sign_iop_abort()` twice, or to call `psa_sign_iop_abort()` on an operation that has not been set up. -.. _interruptible_verify: +.. _interruptible-verify: Interruptible asymmetric verification operations ------------------------------------------------ @@ -1169,19 +1169,19 @@ The interruptible asymmetric verification operation verifies the signature of a An interruptible asymmetric verification operation is used as follows: -1. Allocate an interruptible asymmetric verification operation object, of type `psa_verify_interruptible_operation_t`, which will be passed to all the functions listed here. -#. Initialize the operation object with one of the methods described in the documentation for `psa_verify_interruptible_operation_t`, for example, `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`. -#. Call `psa_verify_interruptible_setup()` to specify the algorithm, key, and the signature to verify. -#. Call `psa_verify_interruptible_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +1. Allocate an interruptible asymmetric verification operation object, of type `psa_verify_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_verify_iop_t`, for example, `PSA_VERIFY_IOP_INIT`. +#. Call `psa_verify_iop_setup()` to specify the algorithm, key, and the signature to verify. +#. Call `psa_verify_iop_setup_complete()` to complete the setup, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. #. Either: - 1. Call `psa_verify_interruptible_hash()` with a pre-computed hash of the message to verify; or - 2. Call `psa_verify_interruptible_update()` one or more times, passing a fragment of the message each time. The signature is verified against the concatenation of these fragments, in order. -#. Call `psa_verify_interruptible_complete()` to finish verifying the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. -#. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_interruptible_abort()`. + 1. Call `psa_verify_iop_hash()` with a pre-computed hash of the message to verify; or + 2. Call `psa_verify_iop_update()` one or more times, passing a fragment of the message each time. The signature is verified against the concatenation of these fragments, in order. +#. Call `psa_verify_iop_complete()` to finish verifying the signature value, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_verify_iop_abort()`. -.. typedef:: /* implementation-defined type */ psa_verify_interruptible_operation_t +.. typedef:: /* implementation-defined type */ psa_verify_iop_t .. summary:: The type of the state data structure for an interruptible asymmetric verification operation. @@ -1192,67 +1192,67 @@ An interruptible asymmetric verification operation is used as follows: .. code-block:: xref - psa_verify_interruptible_operation_t operation; + psa_verify_iop_t operation; memset(&operation, 0, sizeof(operation)); * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: .. code-block:: xref - static psa_verify_interruptible_operation_t operation; + static psa_verify_iop_t operation; - * Initialize the object to the initializer `PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT`, for example: + * Initialize the object to the initializer `PSA_VERIFY_IOP_INIT`, for example: .. code-block:: xref - psa_verify_interruptible_operation_t operation = PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT; + psa_verify_iop_t operation = PSA_VERIFY_IOP_INIT; - * Assign the result of the function `psa_verify_interruptible_operation_init()` to the object, for example: + * Assign the result of the function `psa_verify_iop_init()` to the object, for example: .. code-block:: xref - psa_verify_interruptible_operation_t operation; - operation = psa_verify_interruptible_operation_init(); + psa_verify_iop_t operation; + operation = psa_verify_iop_init(); This is an implementation-defined type. Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. -.. macro:: PSA_VERIFY_INTERRUPTIBLE_OPERATION_INIT +.. macro:: PSA_VERIFY_IOP_INIT :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_interruptible_operation_t`. + This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_iop_t`. -.. function:: psa_verify_interruptible_operation_init +.. function:: psa_verify_iop_init .. summary:: Return an initial value for an interruptible asymmetric verification operation object. - .. return:: psa_verify_interruptible_operation_t + .. return:: psa_verify_iop_t -.. function:: psa_verify_interruptible_get_num_ops +.. function:: psa_verify_iop_get_num_ops .. summary:: Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible asymmetric verification operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. - After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_verify_interruptible_setup()` or `psa_verify_interruptible_abort()`. + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. The value is reset to zero by a call to either `psa_verify_iop_setup()` or `psa_verify_iop_abort()`. - This function can be used to tune the value passed to `psa_interruptible_set_max_ops()`. + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. The value is undefined if the operation object has not been initialized. -.. function:: psa_verify_interruptible_setup +.. function:: psa_verify_iop_setup .. summary:: Begin the setup of an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation - The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_interruptible_operation_t` and not yet in use. + .. param:: psa_verify_iop_t * operation + The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_iop_t` and not yet in use. .. param:: psa_key_id_t key Identifier of the key to use for the operation. It must be an asymmetric key pair or asymmetric public key. The key must either permit the usage `PSA_KEY_USAGE_VERIFY_HASH` or `PSA_KEY_USAGE_VERIFY_MESSAGE`. .. param:: psa_algorithm_t alg @@ -1265,7 +1265,7 @@ An interruptible asymmetric verification operation is used as follows: .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The operation setup must now be completed by calling `psa_verify_interruptible_setup_complete()`. + The operation setup must now be completed by calling `psa_verify_iop_setup_complete()`. .. retval:: PSA_ERROR_INVALID_HANDLE ``key`` is not a valid key identifier. .. retval:: PSA_ERROR_NOT_PERMITTED @@ -1293,21 +1293,21 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_DATA_CORRUPT .. retval:: PSA_ERROR_DATA_INVALID - This function sets up the verification of an asymmetric signature of a message or pre-computed hash. To calculate an asymmetric signature, use an interruptible asymmetric signature operation, see :secref:`interruptible_sign`. + This function sets up the verification of an asymmetric signature of a message or pre-computed hash. To calculate an asymmetric signature, use an interruptible asymmetric signature operation, see :secref:`interruptible-sign`. - After a successful call to `psa_verify_interruptible_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_interruptible_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: + After a successful call to `psa_verify_iop_setup()`, the operation is in setup state. Setup can be completed by calling `psa_verify_iop_setup_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once setup has begun, the application must eventually terminate the operation. The following events terminate an operation: - * A successful call to `psa_verify_interruptible_complete()`. - * A call to `psa_verify_interruptible_abort()`. + * A successful call to `psa_verify_iop_complete()`. + * A call to `psa_verify_iop_abort()`. - If `psa_verify_interruptible_setup()` returns an error, the operation object is unchanged. + If `psa_verify_iop_setup()` returns an error, the operation object is unchanged. -.. function:: psa_verify_interruptible_setup_complete +.. function:: psa_verify_iop_setup_complete .. summary:: Finish setting up an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must be in the process of being set up. .. return:: psa_status_t @@ -1333,18 +1333,18 @@ An interruptible asymmetric verification operation is used as follows: .. note:: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - When this function returns successfully, the operation is ready for data input using a call to `psa_verify_interruptible_hash()` or `psa_verify_interruptible_update()`. + When this function returns successfully, the operation is ready for data input using a call to `psa_verify_iop_hash()` or `psa_verify_iop_update()`. If this function returns :code:`PSA_OPERATION_INCOMPLETE`, setup is not complete, and this function must be called again to continue the operation. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. - The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. -.. function:: psa_verify_interruptible_hash +.. function:: psa_verify_iop_hash .. summary:: Input a pre-computed hash to an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have been set up, with no data input. .. param:: const uint8_t * hash The input whose signature is to be verified. This is usually the hash of a message. @@ -1385,17 +1385,17 @@ An interruptible asymmetric verification operation is used as follows: Specialized signature algorithms can apply a padding or encoding to the hash. In such cases, the encoded hash must be passed to this function. For example, see `PSA_ALG_RSA_PKCS1V15_SIGN_RAW`. - After input of the hash, the verification operation can be completed by calling `psa_verify_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + After input of the hash, the verification operation can be completed by calling `psa_verify_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. -.. function:: psa_verify_interruptible_update +.. function:: psa_verify_iop_update .. summary:: Add a message fragment to an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have been set up, with no hash value input. .. param:: const uint8_t * input Buffer containing the message fragment to add to the verification. @@ -1431,18 +1431,18 @@ An interruptible asymmetric verification operation is used as follows: The application must complete the setup of the operation before calling this function. - For message-signature algorithms that process the message data multiple times when verifying a signature, `psa_verify_interruptible_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_verify_interruptible_update()`. + For message-signature algorithms that process the message data multiple times when verifying a signature, `psa_verify_iop_update()` must be called exactly once with the entire message content. For signature algorithms that only process the message data once, the message content can be passed in a series of calls to `psa_verify_iop_update()`. - After input of the message, the verification operation can be completed by calling `psa_verify_interruptible_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + After input of the message, the verification operation can be completed by calling `psa_verify_iop_complete()` until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. -.. function:: psa_verify_interruptible_complete +.. function:: psa_verify_iop_complete .. summary:: Attempt to finish the interruptible verification of an asymmetric signature. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have hash or message data input, or be in the process of finishing. .. return:: psa_status_t @@ -1454,7 +1454,7 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_verify_interruptible_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. + * The operation state is not valid: the operation setup must be complete, or a previous call to `psa_verify_iop_complete()` returned :code:`PSA_OPERATION_INCOMPLETE`. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_SIGNATURE The signature is not the result of signing the input message, or hash value, with the requested algorithm, using the private key corresponding to the key provided to the operation. @@ -1470,16 +1470,16 @@ An interruptible asymmetric verification operation is used as follows: When this function returns successfully, the operation becomes inactive. If this function returns :code:`PSA_OPERATION_INCOMPLETE`, this function must be called again to continue the operation. - If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_interruptible_abort()`. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. - The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_interruptible_set_max_ops()`. + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. -.. function:: psa_verify_interruptible_abort +.. function:: psa_verify_iop_abort .. summary:: Abort an interruptible asymmetric verification operation. - .. param:: psa_verify_interruptible_operation_t * operation + .. param:: psa_verify_iop_t * operation The interruptible verification operation to abort. .. return:: psa_status_t @@ -1491,11 +1491,11 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The library requires initializing by a call to `psa_crypto_init()`. - Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_verify_interruptible_setup()` again. + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_verify_iop_setup()` again. - This function can be called at any time after the operation object has been initialized as described in `psa_verify_interruptible_operation_t`. + This function can be called at any time after the operation object has been initialized as described in `psa_verify_iop_t`. - In particular, it is valid to call `psa_verify_interruptible_abort()` twice, or to call `psa_verify_interruptible_abort()` on an operation that has not been set up. + In particular, it is valid to call `psa_verify_iop_abort()` twice, or to call `psa_verify_iop_abort()` on an operation that has not been set up. Support macros -------------- diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index 618e5784..a3e8bb09 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -258,10 +258,10 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Initialize:** Initialize or assign the operation object by one of the following methods: - - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_INIT`` macro as the type is implementation-defined. + - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_IOP_INIT`` macro as the type is implementation-defined. - Set it to all-bits zero. This is automatic if the object was allocated with ``calloc()``. - - Assign the value of the associated macro ``PSA_xxx_INIT``. - - Assign the result of calling the associated function ``psa_xxx_init()``. + - Assign the value of the associated macro ``PSA_xxx_IOP_INIT``. + - Assign the result of calling the associated function ``psa_xxx_iop_init()``. The resulting object is now *inactive*. It is an error to initialize an operation object that is in *active* or *error* states. This can leak memory or other resources. @@ -286,11 +286,11 @@ The typical sequence of actions with a interruptible operation is as follows: An application needs to repeat this step until the completion function completes with success or an error status. -#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_interruptible_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_iop_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. - Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_interruptible_abort()`` function to release the operation resources and return the object to the *inactive* state. + Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_iop_abort()`` function to release the operation resources and return the object to the *inactive* state. - ``psa_xxx_interruptible_abort()`` can be called on an *inactive* operation, and this has no effect. + ``psa_xxx_iop_abort()`` can be called on an *inactive* operation, and this has no effect. Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the applicable setup functions again. @@ -307,7 +307,7 @@ It is safe to move an interruptible operation object to a different memory locat Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. -See :secref:`interruptible_sign` for an example of using an interruptible operation. +See :secref:`interruptible-sign` for an example of using an interruptible operation. Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ From 7f711dafb9b30b4d0011c8c0837434064af479d5 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 11 Jun 2024 14:23:10 +0100 Subject: [PATCH 10/22] Resolve the TODO for interruptible signature. Require that one of psa_xxx_iop_hash() or psa_xxx_iop_update() MUST be called in an interruptible signature operation. --- doc/crypto/api/ops/signature.rst | 23 ++++++++++------------- 1 file changed, 10 insertions(+), 13 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index cbf6073a..5fd1a25e 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -811,17 +811,6 @@ Single-part asymmetric signature functions Interruptible asymmetric signature operations --------------------------------------------- -.. todo:: - - Decide how to calculate the signature of the zero-length message using the interruptible API. Either: - - * Implicitly, if neither `psa_sign_iop_hash()`, nor `psa_sign_iop_update()`, is called; OR - * Require that `psa_sign_iop_update()` is called with a zero-length input. - - In the latter case, we can required that at least one those APIs must be called after finishing setup, before calling `psa_sign_iop_complete()`. - - :issue:`Current preference for the latter` - The interruptible asymmetric signature operation calculates the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. An interruptible asymmetric signature operation is used as follows: @@ -1058,7 +1047,7 @@ An interruptible asymmetric signature operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must be set up, with no hash value input. + * The operation state is not valid: the operation must be set up, with no pre-computed hash value input. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_NOT_PERMITTED The key does not have the `PSA_KEY_USAGE_SIGN_MESSAGE` flag. @@ -1088,6 +1077,10 @@ An interruptible asymmetric signature operation is used as follows: If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_sign_iop_abort()`. + .. note:: + + To sign the zero-length message using an interruptible operation, call `psa_sign_iop_update()` once with a zero-length message fragment before calling `psa_sign_iop_complete()`. + .. function:: psa_sign_iop_complete .. summary:: @@ -1408,7 +1401,7 @@ An interruptible asymmetric verification operation is used as follows: .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: - * The operation state is not valid: the operation must be set up, with no hash value input. + * The operation state is not valid: the operation must be set up, with no pre-computed hash value input. * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_NOT_PERMITTED The key does not have the `PSA_KEY_USAGE_VERIFY_MESSAGE` flag. @@ -1437,6 +1430,10 @@ An interruptible asymmetric verification operation is used as follows: If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_verify_iop_abort()`. + .. note:: + + To verify the signature of the zero-length message using an interruptible operation, call `psa_verify_iop_update()` once with a zero-length message fragment before calling `psa_verify_iop_complete()` + .. function:: psa_verify_iop_complete .. summary:: From 66ed971516488e39e96d3511b7e376775cd52501 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 18 Jun 2024 11:02:21 +0100 Subject: [PATCH 11/22] Fix: missing BAD_STATE errors in iop setup functions --- doc/crypto/api/ops/signature.rst | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 5fd1a25e..fee4df54 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -925,6 +925,11 @@ An interruptible asymmetric signature operation is used as follows: * ``alg`` is not an asymmetric signature algorithm. * ``key`` is not an asymmetric key pair, that is compatible with ``alg``. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY .. retval:: PSA_ERROR_COMMUNICATION_FAILURE .. retval:: PSA_ERROR_CORRUPTION_DETECTED @@ -1277,6 +1282,11 @@ An interruptible asymmetric verification operation is used as follows: * ``alg`` is not an asymmetric signature algorithm. * ``key`` is not an asymmetric key pair, or asymmetric public key, that is compatible with ``alg``. * ``signature`` is not a valid signature for the algorithm and key. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. .. retval:: PSA_ERROR_INVALID_SIGNATURE ``signature`` is not a valid signature for the algorithm and key. .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY From 2d995d8a241e5a97850b3a636aba2b9cfddb3786 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 16 Dec 2024 17:18:42 +0000 Subject: [PATCH 12/22] Add versionadded placeholders for the new APIs --- doc/crypto/api/library/library.rst | 7 +++++- doc/crypto/api/ops/signature.rst | 40 ++++++++++++++++++++++++++++++ 2 files changed, 46 insertions(+), 1 deletion(-) diff --git a/doc/crypto/api/library/library.rst b/doc/crypto/api/library/library.rst index c01b81e8..97e293da 100644 --- a/doc/crypto/api/library/library.rst +++ b/doc/crypto/api/library/library.rst @@ -87,6 +87,8 @@ See :secref:`interruptible-operations`. .. summary:: Set the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + .. versionadded:: 1.x + .. param:: uint32_t max_ops The maximum number of ops to be executed in a single call, this can be a number from ``0`` to `PSA_IOP_MAX_OPS_UNLIMITED`, where ``0`` is obviously the least amount of work done per call. @@ -112,6 +114,8 @@ See :secref:`interruptible-operations`. .. summary:: Get the maximum number of *ops* allowed to be executed by an interruptible function in a single call. + .. versionadded:: 1.x + .. return:: uint32_t Maximum number of *ops* allowed to be executed by an interruptible function in a single call. @@ -121,7 +125,8 @@ See :secref:`interruptible-operations`. :definition: UINT32_MAX .. summary:: - Maximum value for use with `psa_iop_set_max_ops()`. + .. versionadded:: 1.x + Using this value in a call to `psa_iop_set_max_ops()` will cause interruptible functions to complete their calculation before returning. diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index fee4df54..0407cce7 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -832,6 +832,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: The type of the state data structure for an interruptible asymmetric signature operation. + .. versionadded:: 1.x + Before calling any function on an interruptible asymmetric signature operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -868,11 +870,15 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: This macro evaluates to an initializer for an interruptible asymmetric signature operation object of type `psa_sign_iop_t`. + .. versionadded:: 1.x + .. function:: psa_sign_iop_init .. summary:: Return an initial value for an interruptible asymmetric signature operation object. + .. versionadded:: 1.x + .. return:: psa_sign_iop_t .. function:: psa_sign_iop_get_num_ops @@ -880,6 +886,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Get the number of *ops* that an interruptible asymmetric signature operation has taken so far. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to inspect. @@ -897,6 +905,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Begin the setup of an interruptible asymmetric signature operation. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to set up. It must have been initialized as per the documentation for `psa_sign_iop_t` and not yet in use. .. param:: psa_key_id_t key @@ -952,6 +962,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Finish setting up an interruptible asymmetric signature operation. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must be in the process of being set up. @@ -988,6 +1000,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Input a pre-computed hash to an interruptible asymmetric signature operation. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have been set up, with no data input. .. param:: const uint8_t * hash @@ -1039,6 +1053,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Add a message fragment to an interruptible asymmetric signature operation. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have been set up, with no hash value input. .. param:: const uint8_t * input @@ -1091,6 +1107,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Attempt to finish the interruptible calculation of an asymmetric signature. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible asymmetric signature operation to use. The operation must have hash or message data input, or be in the process of finishing. .. param:: uint8_t * signature @@ -1139,6 +1157,8 @@ An interruptible asymmetric signature operation is used as follows: .. summary:: Abort an interruptible asymmetric signature operation. + .. versionadded:: 1.x + .. param:: psa_sign_iop_t * operation The interruptible signature operation to abort. @@ -1184,6 +1204,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: The type of the state data structure for an interruptible asymmetric verification operation. + .. versionadded:: 1.x + Before calling any function on an interruptible asymmetric verification operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -1220,11 +1242,15 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: This macro evaluates to an initializer for an interruptible asymmetric verification operation object of type `psa_verify_iop_t`. + .. versionadded:: 1.x + .. function:: psa_verify_iop_init .. summary:: Return an initial value for an interruptible asymmetric verification operation object. + .. versionadded:: 1.x + .. return:: psa_verify_iop_t .. function:: psa_verify_iop_get_num_ops @@ -1232,6 +1258,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Get the number of *ops* that an interruptible asymmetric verification operation has taken so far. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible asymmetric verification operation to inspect. @@ -1249,6 +1277,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Begin the setup of an interruptible asymmetric verification operation. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to set up. It must have been initialized as per the documentation for `psa_verify_iop_t` and not yet in use. .. param:: psa_key_id_t key @@ -1310,6 +1340,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Finish setting up an interruptible asymmetric verification operation. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must be in the process of being set up. @@ -1347,6 +1379,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Input a pre-computed hash to an interruptible asymmetric verification operation. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have been set up, with no data input. .. param:: const uint8_t * hash @@ -1398,6 +1432,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Add a message fragment to an interruptible asymmetric verification operation. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have been set up, with no hash value input. .. param:: const uint8_t * input @@ -1449,6 +1485,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Attempt to finish the interruptible verification of an asymmetric signature. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to use. The operation must have hash or message data input, or be in the process of finishing. @@ -1486,6 +1524,8 @@ An interruptible asymmetric verification operation is used as follows: .. summary:: Abort an interruptible asymmetric verification operation. + .. versionadded:: 1.x + .. param:: psa_verify_iop_t * operation The interruptible verification operation to abort. From 31b2559e20e9974eb01d37957f389e8c041ca3a3 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Wed, 19 Jun 2024 15:17:20 +0100 Subject: [PATCH 13/22] crypto: Add interruptible operations for key agreement use cases Added three new interruptible operation APIs: * psa_generate_key_iop_*() for key generation * psa_export_public_key_iop_*() for public key export * psa_key_agreement_iop_*() for key agreement --- doc/crypto/api.db/psa/crypto.h | 33 ++ doc/crypto/api/keys/management.rst | 452 +++++++++++++++++++++++++++ doc/crypto/api/ops/key-agreement.rst | 268 +++++++++++++++- 3 files changed, 750 insertions(+), 3 deletions(-) diff --git a/doc/crypto/api.db/psa/crypto.h b/doc/crypto/api.db/psa/crypto.h index 8b283269..c22bf274 100644 --- a/doc/crypto/api.db/psa/crypto.h +++ b/doc/crypto/api.db/psa/crypto.h @@ -6,7 +6,10 @@ typedef uint32_t psa_algorithm_t; typedef /* implementation-defined type */ psa_cipher_operation_t; typedef uint8_t psa_dh_family_t; typedef uint8_t psa_ecc_family_t; +typedef /* implementation-defined type */ psa_export_public_key_iop_t; +typedef /* implementation-defined type */ psa_generate_key_iop_t; typedef /* implementation-defined type */ psa_hash_operation_t; +typedef /* implementation-defined type */ psa_key_agreement_iop_t; typedef /* implementation-defined type */ psa_key_attributes_t; typedef /* implementation-defined type */ psa_key_derivation_operation_t; typedef uint16_t psa_key_derivation_step_t; @@ -234,9 +237,11 @@ typedef struct psa_custom_key_parameters_t { #define PSA_EXPORT_KEY_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ #define PSA_EXPORT_KEY_PAIR_MAX_SIZE /* implementation-defined value */ +#define PSA_EXPORT_PUBLIC_KEY_IOP_INIT /* implementation-defined value */ #define PSA_EXPORT_PUBLIC_KEY_MAX_SIZE /* implementation-defined value */ #define PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(key_type, key_bits) \ /* implementation-defined value */ +#define PSA_GENERATE_KEY_IOP_INIT /* implementation-defined value */ #define PSA_HASH_BLOCK_LENGTH(alg) /* implementation-defined value */ #define PSA_HASH_LENGTH(alg) /* implementation-defined value */ #define PSA_HASH_MAX_SIZE /* implementation-defined value */ @@ -249,6 +254,7 @@ typedef struct psa_custom_key_parameters_t { #define PSA_HASH_SUSPEND_OUTPUT_MAX_SIZE /* implementation-defined value */ #define PSA_HASH_SUSPEND_OUTPUT_SIZE(alg) /* specification-defined value */ #define PSA_IOP_MAX_OPS_UNLIMITED UINT32_MAX +#define PSA_KEY_AGREEMENT_IOP_INIT /* implementation-defined value */ #define PSA_KEY_ATTRIBUTES_INIT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_CONTEXT /* implementation-defined value */ #define PSA_KEY_DERIVATION_INPUT_COST /* implementation-defined value */ @@ -530,6 +536,15 @@ psa_status_t psa_export_public_key(psa_key_id_t key, uint8_t * data, size_t data_size, size_t * data_length); +psa_status_t psa_export_public_key_iop_abort(psa_export_public_key_iop_t * operation); +psa_status_t psa_export_public_key_iop_complete(psa_export_public_key_iop_t * operation, + uint8_t * data, + size_t data_size, + size_t * data_length); +uint32_t psa_export_public_key_iop_get_num_ops(psa_export_public_key_iop_t * operation); +psa_export_public_key_iop_t psa_export_public_key_iop_init(void); +psa_status_t psa_export_public_key_iop_setup(psa_export_public_key_iop_t * operation, + psa_key_id_t key); psa_status_t psa_generate_key(const psa_key_attributes_t * attributes, psa_key_id_t * key); psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, @@ -537,6 +552,13 @@ psa_status_t psa_generate_key_custom(const psa_key_attributes_t * attributes, const uint8_t * custom_data, size_t custom_data_length, mbedtls_svc_key_id_t * key); +psa_status_t psa_generate_key_iop_abort(psa_generate_key_iop_t * operation); +psa_status_t psa_generate_key_iop_complete(psa_generate_key_iop_t * operation, + psa_key_id_t * key); +uint32_t psa_generate_key_iop_get_num_ops(psa_generate_key_iop_t * operation); +psa_generate_key_iop_t psa_generate_key_iop_init(void); +psa_status_t psa_generate_key_iop_setup(psa_generate_key_iop_t * operation, + const psa_key_attributes_t * attributes); psa_status_t psa_generate_random(uint8_t * output, size_t output_size); psa_algorithm_t psa_get_key_algorithm(const psa_key_attributes_t * attributes); @@ -593,6 +615,17 @@ psa_status_t psa_key_agreement(psa_key_id_t private_key, psa_algorithm_t alg, const psa_key_attributes_t * attributes, psa_key_id_t * key); +psa_status_t psa_key_agreement_iop_abort(psa_key_agreement_iop_t * operation); +psa_status_t psa_key_agreement_iop_complete(psa_key_agreement_iop_t * operation, + psa_key_id_t * key); +uint32_t psa_key_agreement_iop_get_num_ops(psa_key_agreement_iop_t * operation); +psa_key_agreement_iop_t psa_key_agreement_iop_init(void); +psa_status_t psa_key_agreement_iop_setup(psa_key_agreement_iop_t * operation, + psa_key_id_t private_key, + const uint8_t * peer_key, + size_t peer_key_length, + psa_algorithm_t alg, + const psa_key_attributes_t * attributes); psa_key_attributes_t psa_key_attributes_init(void); psa_status_t psa_key_derivation_abort(psa_key_derivation_operation_t * operation); psa_status_t psa_key_derivation_get_capacity(const psa_key_derivation_operation_t * operation, diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 202230e7..d00f3fde 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -322,6 +322,9 @@ When creating a key, the attributes for the new key are specified in a `psa_key_ See the documentation of `psa_custom_key_parameters_t` for a list of non-default production parameters. See the key type definitions in :secref:`key-types` for details of the custom production parameters used for key generation. + If an application requires bounded execution when generating a key, it can use an interruptible key generation operation. + See :secref:`interruptible-generate-key`. + .. function:: psa_copy_key .. summary:: @@ -602,6 +605,10 @@ Key export Exporting a public-key object or the public part of a key pair is always permitted, regardless of the key's usage flags. + If an application requires bounded execution when exporting a public key, it can use an interruptible public-key export operation. + See :secref:`interruptible-export-key`. + + .. macro:: PSA_EXPORT_KEY_OUTPUT_SIZE :definition: /* implementation-defined value */ @@ -709,3 +716,448 @@ Key export This value must be a sufficient buffer size when calling `psa_export_key()` or `psa_export_public_key()` to export any asymmetric key pair or public key that is supported by the implementation, regardless of the exact key type and key size. See also `PSA_EXPORT_KEY_PAIR_MAX_SIZE`, `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE`, and `PSA_EXPORT_KEY_OUTPUT_SIZE()`. + +.. _interruptible-generate-key: + +Interruptible key generation +---------------------------- + +Generation of some key types can be computationally expensive. +For example, RSA keys, and elliptic curve public keys. + +An interruptible key generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. + +An interruptible key generation operation is used as follows: + +1. Allocate an interruptible key generation operation object, of type `psa_generate_key_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_generate_key_iop_t`, for example, `PSA_GENERATE_KEY_IOP_INIT`. +#. Call `psa_generate_key_iop_setup()` to specify the key attributes. +#. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_generate_key_iop_abort()`. + +.. typedef:: /* implementation-defined type */ psa_generate_key_iop_t + + .. summary:: + The type of the state data structure for an interruptible key generation operation. + + Before calling any function on an interruptible key generation operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_generate_key_iop_t operation; + + * Initialize the object to the initializer `PSA_GENERATE_KEY_IOP_INIT`, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation = PSA_GENERATE_KEY_IOP_INIT; + + * Assign the result of the function `psa_generate_key_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_generate_key_iop_t operation; + operation = psa_generate_key_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_GENERATE_KEY_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible key generation operation object of type `psa_generate_key_iop_t`. + +.. function:: psa_generate_key_iop_init + + .. summary:: + Return an initial value for an interruptible key generation operation object. + + .. return:: psa_generate_key_iop_t + +.. function:: psa_generate_key_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible key generation operation has taken so far. + + .. param:: psa_generate_key_iop_t * operation + The interruptible key generation operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_generate_key_iop_setup()` or `psa_generate_key_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_generate_key_iop_setup + + .. summary:: + Start an interruptible operation to generate a key or key pair. + + .. param:: psa_generate_key_iop_t * operation + The interruptible key generation operation to set up. + It must have been initialized as per the documentation for `psa_generate_key_iop_t`, and be inactive. + .. param:: const psa_key_attributes_t * attributes + The attributes for the new key. + This function uses the attributes as follows: + + * The key type is required. + It cannot be an asymmetric public key. + * The key size is required. + It must be a valid size for the key type. + * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation, see :secref:`permitted-algorithms`. + * The key usage flags define what operations are permitted with the key, see :secref:`key-usage-flags`. + * The key lifetime and identifier are required for a persistent key. + + .. note:: + This is an input parameter: it is not updated with the final key attributes. + The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_generate_key_iop_complete()`. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * The key type is invalid, or is an asymmetric public key type. + * The key size is not valid for the key type. + * The key lifetime is invalid. + * The key identifier is not valid for the key lifetime. + * The key usage flags include invalid values. + * The key's permitted-usage algorithm is invalid. + * The key attributes, as a whole, are invalid. + .. retval:: PSA_ERROR_NOT_PERMITTED + The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + This function sets up the random generation of a new key. + The location, policy, type, and size of the key are taken from ``attributes``. + + Implementations must reject an attempt to generate a key of size ``0``. + + The following type-specific considerations apply: + + * For RSA keys (`PSA_KEY_TYPE_RSA_KEY_PAIR`), the public exponent is 65537. + The modulus is a product of two probabilistic primes between :math:`2^{n-1}` and :math:`2^n` where :math:`n` is the bit size specified in the attributes. + + After a successful call to `psa_generate_key_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_generate_key_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. The following events terminate an operation: + + * A successful call to `psa_generate_key_iop_complete()`. + * A call to `psa_generate_key_iop_abort()`. + + If `psa_generate_key_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_generate_key_iop_complete + + .. summary:: + Attempt to finish the interruptible generation of a key. + + .. param:: psa_generate_key_iop_t * operation + The interruptible key generation operation to use. + The operation must be active. + .. param:: psa_key_id_t * key + On success, an identifier for the newly created key. + `PSA_KEY_ID_NULL` on failure. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + If the key is persistent, the key material and the key's metadata have been saved to persistent storage. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_ENTROPY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the new key is returned in ``key``, and the operation becomes inactive. + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_generate_key_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_generate_key_iop_abort + + .. summary:: + Abort an interruptible key generation operation. + + .. param:: psa_generate_key_iop_t * operation + The interruptible key generation operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. + Once aborted, the operation object can be reused for another operation by calling `psa_generate_key_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_generate_key_iop_t`. + + In particular, it is valid to call `psa_generate_key_iop_abort()` twice, or to call `psa_generate_key_iop_abort()` on an operation that has not been set up. + +.. _interruptible-export-key: + +Interruptible public-key export +------------------------------- + +Extracting a public key from an asymmetric key-pair can be computationally expensive. +For example, computing an elliptic curve public key from the private key. + +An interruptible public-key export operation can be used instead of calling `psa_export_public_key()`, in applications that have bounded execution requirements for use cases that require public-key export. + +An interruptible public-key export operation is used as follows: + +1. Allocate an interruptible public-key export operation object, of type `psa_export_public_key_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_export_public_key_iop_t`, for example, `PSA_EXPORT_PUBLIC_KEY_IOP_INIT`. +#. Call `psa_export_public_key_iop_setup()` to specify the key to export. +#. Call `psa_export_public_key_iop_complete()` to finish exporting the key data, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_export_public_key_iop_abort()`. + +.. typedef:: /* implementation-defined type */ psa_export_public_key_iop_t + + .. summary:: + The type of the state data structure for an interruptible public-key export operation. + + Before calling any function on an interruptible public-key export operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_export_public_key_iop_t operation; + + * Initialize the object to the initializer `PSA_EXPORT_PUBLIC_KEY_IOP_INIT`, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation = PSA_EXPORT_PUBLIC_KEY_IOP_INIT; + + * Assign the result of the function `psa_export_public_key_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_export_public_key_iop_t operation; + operation = psa_export_public_key_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_EXPORT_PUBLIC_KEY_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible public-key export operation object of type `psa_export_public_key_iop_t`. + +.. function:: psa_export_public_key_iop_init + + .. summary:: + Return an initial value for an interruptible public-key export operation object. + + .. return:: psa_export_public_key_iop_t + +.. function:: psa_export_public_key_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible public-key export operation has taken so far. + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_export_public_key_iop_setup()` or `psa_export_public_key_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_export_public_key_iop_setup + + .. summary:: + Start an interruptible operation to export a public key or the public part of a key pair in binary format. + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to set up. + It must have been initialized as per the documentation for `psa_export_public_key_iop_t`, and be inactive. + .. param:: psa_key_id_t key + Identifier of the key to export. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_export_public_key_iop_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``key`` is not a valid key identifier. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The key is neither a public key nor a key pair. + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * The key's storage location does not support export of the key. + * The implementation does not support export of keys with this key type. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + + This function sets up the export of a public key in binary format. + For standard key types, the output format is defined in the relevant *Key format* section in :secref:`key-types`. + + Exporting a public key object or the public part of a key pair is always permitted, regardless of the key's usage flags. + + After a successful call to `psa_export_public_key_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_export_public_key_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. + The following events terminate an operation: + + * A successful call to `psa_export_public_key_iop_complete()`. + * A call to `psa_export_public_key_iop_abort()`. + + If `psa_export_public_key_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_export_public_key_iop_complete + + .. summary:: + Attempt to finish the interruptible export of a public key. + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to use. + The operation must be active. + .. param:: uint8_t * data + Buffer where the key data is to be written. + .. param:: size_t data_size + Size of the ``data`` buffer in bytes. + This must be appropriate for the key: + + * The required output size is :code:`PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE(type, bits)` where ``type`` is the key type and ``bits`` is the key size in bits. + * `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE` evaluates to the maximum output size of any supported public key or public part of a key pair. + * `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` evaluates to the maximum output size of any supported public key or key pair. + .. param:: size_t * data_length + On success, the number of bytes that make up the key data. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The first ``(*data_length)`` bytes of ``data`` contain the exported public key. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BUFFER_TOO_SMALL + The size of the ``data`` buffer is too small. + `PSA_EXPORT_PUBLIC_KEY_OUTPUT_SIZE()`, `PSA_EXPORT_PUBLIC_KEY_MAX_SIZE`, or `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` can be used to determine a sufficient buffer size. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the public key data is returned in ``data``, and the operation becomes inactive. + The output of this function can be passed to `psa_import_key()` to create a new key that is equivalent to the public key. + + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_export_public_key_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + + .. note:: + + If the implementation of `psa_import_key()` supports other formats beyond the format specified here, the output from `psa_export_public_key_iop_complete()` must use the representation specified in :secref:`key-types`, not the originally imported representation. + +.. function:: psa_export_public_key_iop_abort + + .. summary:: + Abort an interruptible public-key export operation. + + .. param:: psa_export_public_key_iop_t * operation + The interruptible public-key export operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. + Once aborted, the operation object can be reused for another operation by calling `psa_export_public_key_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_export_public_key_iop_t`. + + In particular, it is valid to call `psa_export_public_key_iop_abort()` twice, or to call `psa_export_public_key_iop_abort()` on an operation that has not been set up. diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 9f9e2e95..100becab 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -9,7 +9,7 @@ Key agreement ============= -Three functions are provided for a Diffie-Hellman-style key agreement where each party combines its own private key with the peer’s public key, to produce a shared secret value: +Three functions are provided for a Diffie-Hellman-style key agreement where each party combines its own private key with the peer's public key, to produce a shared secret value: * A call to `psa_key_agreement()` will compute the shared secret and store the result in a new derivation key. @@ -17,7 +17,10 @@ Three functions are provided for a Diffie-Hellman-style key agreement where each * Where an application needs direct access to the shared secret, it can call `psa_raw_key_agreement()` instead. -Using `psa_key_agreement()` or `psa_key_derivation_key_agreement()` is recommended, as these do not expose the shared secret to the application. +If an application requires bounded execution during a key agreement, it can use an interruptible key agreement operation. +See :secref:`interruptible-key-agreement`. + +Using `psa_key_agreement()`, `psa_key_derivation_key_agreement()`, or an interruptible key agreement operation is recommended, as these do not expose the shared secret to the application. .. note:: @@ -173,7 +176,7 @@ Standalone key agreement .. return:: psa_status_t .. retval:: PSA_SUCCESS Success. - The new key contains the share secret. + The new key contains the shared secret. If the key is persistent, the key material and the key's metadata have been saved to persistent storage. .. retval:: PSA_ERROR_INVALID_HANDLE ``private_key`` is not a valid key identifier. @@ -228,6 +231,9 @@ Standalone key agreement .. warning:: The shared secret resulting from a key-agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key-derivation algorithm is applied to the result, to derive unbiased cryptographic keys. + If an application requires bounded execution during key agreement, it can use an interruptible key agreement operation. + See :secref:`interruptible-key-agreement`. + .. function:: psa_raw_key_agreement .. summary:: @@ -352,6 +358,262 @@ Combining key agreement and key derivation Instead, the application can call `psa_key_agreement()` to obtain the shared secret as a derivation key. This key can be used as input to as many key-derivation operations as required. +.. _interruptible-key-agreement: + +Interruptible key agreement +--------------------------- + +Most key agreement algorithms are computationally expensive. + +An interruptible key agreement operation can be used instead of calling `psa_key_agreement()`, in applications that have bounded execution requirements for use cases involving key agreement. + +An interruptible key agreement operation is used as follows: + +1. Allocate an interruptible key agreement operation object, of type `psa_key_agreement_iop_t`, which will be passed to all the functions listed here. +#. Initialize the operation object with one of the methods described in the documentation for `psa_key_agreement_iop_t`, for example, `PSA_KEY_AGREEMENT_IOP_INIT`. +#. Call `psa_key_agreement_iop_setup()` to specify the algorithm, and provide the private key and the peer public key. +#. Call `psa_key_agreement_iop_complete()` to finish the key agreement and output the shared secret, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. +#. If an error occurs at any stage, or to terminate the operation early, call `psa_key_agreement_iop_abort()`. + + +.. typedef:: /* implementation-defined type */ psa_key_agreement_iop_t + + .. summary:: + The type of the state data structure for an interruptible key agreement operation. + + Before calling any function on an interruptible key agreement operation object, the application must initialize it by any of the following means: + + * Set the object to all-bits-zero, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation; + memset(&operation, 0, sizeof(operation)); + + * Initialize the object to logical zero values by declaring the object as static or global without an explicit initializer, for example: + + .. code-block:: xref + + static psa_key_agreement_iop_t operation; + + * Initialize the object to the initializer `PSA_KEY_AGREEMENT_IOP_INIT`, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation = PSA_KEY_AGREEMENT_IOP_INIT; + + * Assign the result of the function `psa_key_agreement_iop_init()` to the object, for example: + + .. code-block:: xref + + psa_key_agreement_iop_t operation; + operation = psa_key_agreement_iop_init(); + + This is an implementation-defined type. + Applications that make assumptions about the content of this object will result in implementation-specific behavior, and are non-portable. + +.. macro:: PSA_KEY_AGREEMENT_IOP_INIT + :definition: /* implementation-defined value */ + + .. summary:: + This macro evaluates to an initializer for an interruptible key agreement operation object of type `psa_key_agreement_iop_t`. + +.. function:: psa_key_agreement_iop_init + + .. summary:: + Return an initial value for an interruptible key agreement operation object. + + .. return:: psa_key_agreement_iop_t + +.. function:: psa_key_agreement_iop_get_num_ops + + .. summary:: + Get the number of *ops* that an interruptible key agreement operation has taken so far. + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key agreement operation to inspect. + + .. return:: uint32_t + Number of *ops* that the operation has taken so far. + + After the interruptible operation has completed, the returned value is the number of *ops* required for the entire operation. + The value is reset to zero by a call to either `psa_key_agreement_iop_setup()` or `psa_key_agreement_iop_abort()`. + + This function can be used to tune the value passed to `psa_iop_set_max_ops()`. + + The value is undefined if the operation object has not been initialized. + +.. function:: psa_key_agreement_iop_setup + + .. summary:: + Start an interruptible operation to perform a key agreement. + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key agreement operation to set up. + It must have been initialized as per the documentation for `psa_key_agreement_iop_t`, and be inactive. + .. param:: psa_key_id_t private_key + Identifier of the private key to use. + It must permit the usage `PSA_KEY_USAGE_DERIVE`. + .. param:: const uint8_t * peer_key + Public key of the peer. + The peer key data is parsed with the type :code:`PSA_KEY_TYPE_PUBLIC_KEY_OF_KEY_PAIR(type)` where ``type`` is the type of ``private_key``, and with the same bit-size as ``private_key``. + The peer key must be in the format that `psa_import_key()` accepts for this public key type. + These formats are described with the public key type in :secref:`key-types`. + .. param:: size_t peer_key_length + Size of ``peer_key`` in bytes. + .. param:: psa_algorithm_t alg + The standalone key agreement algorithm to compute: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_STANDALONE_KEY_AGREEMENT(alg)` is true. + .. param:: const psa_key_attributes_t * attributes + The attributes for the new key. + This function uses the attributes as follows: + + * The key type must be one of `PSA_KEY_TYPE_DERIVE`, `PSA_KEY_TYPE_RAW_DATA`, `PSA_KEY_TYPE_HMAC`, or `PSA_KEY_TYPE_PASSWORD`. + + Implementations must support the `PSA_KEY_TYPE_DERIVE` and `PSA_KEY_TYPE_RAW_DATA` key types. + + * The size of the returned key is always the bit-size of the shared secret, rounded up to a whole number of bytes. + The key size in ``attributes`` can be zero; if it is nonzero, it must be equal to the output size of the key agreement, in bits. + + The output size, in bits, of the key agreement is :code:`8 * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(type, bits)`, where ``type`` and ``bits`` are the type and bit-size of ``private_key``. + + * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation, see :secref:`permitted-algorithms`. + * The key usage flags define what operations are permitted with the key, see :secref:`key-usage-flags`. + * The key lifetime and identifier are required for a persistent key. + + .. note:: + This is an input parameter: it is not updated with the final key attributes. The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The interruptible operation must now be completed by calling `psa_key_agreement_iop_complete()`. + .. retval:: PSA_ERROR_INVALID_HANDLE + ``private_key`` is not a valid key identifier. + .. retval:: PSA_ERROR_NOT_PERMITTED + The following conditions can result in this error: + + * ``private_key`` does not have the `PSA_KEY_USAGE_DERIVE` flag, or it does not permit the requested algorithm. + * The implementation does not permit creating a key with the specified attributes due to some implementation-specific policy. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. + .. retval:: PSA_ERROR_INVALID_ARGUMENT + The following conditions can result in this error: + + * ``alg`` is not a key agreement algorithm. + * ``private_key`` is not compatible with ``alg``. + * ``peer_key`` is not a valid public key corresponding to ``private_key``. + * The output key attributes in ``attributes`` are not valid : + + - The key type is not valid for key agreement output. + - The key size is nonzero, and is not the size of the shared secret. + - The key lifetime is invalid. + - The key identifier is not valid for the key lifetime. + - The key usage flags include invalid values. + - The key's permitted-usage algorithm is invalid. + - The key attributes, as a whole, are invalid. + + .. retval:: PSA_ERROR_NOT_SUPPORTED + The following conditions can result in this error: + + * ``alg`` is not supported or is not a key agreement algorithm. + * ``private_key`` is not supported for use with ``alg``. + * The output key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be inactive. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + + This function sets up an interruptible operation to perform a key agreement. + A key agreement algorithm takes two inputs: a private key ``private_key``, and a public key ``peer_key``. + + After a successful call to `psa_key_agreement_iop_setup()`, the operation is active. + The operation can be completed by calling `psa_key_agreement_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + Once active, the application must eventually terminate the operation. + The following events terminate an operation: + + * A successful call to `psa_key_agreement_iop_complete()`. + * A call to `psa_key_agreement_iop_abort()`. + + If `psa_key_agreement_iop_setup()` returns an error, the operation object is unchanged. + +.. function:: psa_key_agreement_iop_complete + + .. summary:: + Attempt to finish a key agreement and return the shared secret. + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key agreement operation to use. + The operation must be active. + .. param:: psa_key_id_t * key + On success, an identifier for the newly created key. + `PSA_KEY_ID_NULL` on failure. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The new key contains the shared secret. + If the key is persistent, the key material and the key's metadata have been saved to persistent storage. + .. retval:: PSA_OPERATION_INCOMPLETE + The function was interrupted after exhausting the maximum *ops*. + The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_BAD_STATE + The following conditions can result in this error: + + * The operation state is not valid: it must be active. + * The library requires initializing by a call to `psa_crypto_init()`. + .. retval:: PSA_ERROR_INSUFFICIENT_MEMORY + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE + .. retval:: PSA_ERROR_STORAGE_FAILURE + .. retval:: PSA_ERROR_DATA_CORRUPT + .. retval:: PSA_ERROR_DATA_INVALID + + .. note:: + This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. + + When this function returns successfully, the shared secret is returned as a derivation key in ``key``, and the operation becomes inactive. + This key can be input to a key derivation operation using `psa_key_derivation_input_key()`. + + .. warning:: + The shared secret resulting from a key agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key derivation algorithm is applied to the result, to derive unbiased cryptographic keys. + + If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. + If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_key_agreement_iop_abort()`. + + The amount of calculation performed in a single call to this function is determined by the maximum *ops* setting. See `psa_iop_set_max_ops()`. + +.. function:: psa_key_agreement_iop_abort + + .. summary:: + Abort an interruptible key agreement operation. + + .. param:: psa_key_agreement_iop_t * operation + The interruptible key agreement operation to abort. + + .. return:: psa_status_t + .. retval:: PSA_SUCCESS + Success. + The operation object can now be discarded or reused. + .. retval:: PSA_ERROR_COMMUNICATION_FAILURE + .. retval:: PSA_ERROR_CORRUPTION_DETECTED + .. retval:: PSA_ERROR_BAD_STATE + The library requires initializing by a call to `psa_crypto_init()`. + + Aborting an operation frees all associated resources except for the ``operation`` structure itself. Once aborted, the operation object can be reused for another operation by calling `psa_key_agreement_iop_setup()` again. + + This function can be called at any time after the operation object has been initialized as described in `psa_key_agreement_iop_t`. + + In particular, it is valid to call `psa_key_agreement_iop_abort()` twice, or to call `psa_key_agreement_iop_abort()` on an operation that has not been set up. + Support macros -------------- From de875725b700e4c5882a41405d7ff99368f1071b Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 20 Jun 2024 11:07:22 +0100 Subject: [PATCH 14/22] Added new functionality to change log --- doc/crypto/api/ops/signature.rst | 8 ++++---- doc/crypto/appendix/history.rst | 9 ++++++++- 2 files changed, 12 insertions(+), 5 deletions(-) diff --git a/doc/crypto/api/ops/signature.rst b/doc/crypto/api/ops/signature.rst index 0407cce7..d316db0f 100644 --- a/doc/crypto/api/ops/signature.rst +++ b/doc/crypto/api/ops/signature.rst @@ -808,8 +808,8 @@ Single-part asymmetric signature functions .. _interruptible-sign: -Interruptible asymmetric signature operations ---------------------------------------------- +Interruptible asymmetric signature +---------------------------------- The interruptible asymmetric signature operation calculates the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. @@ -1180,8 +1180,8 @@ An interruptible asymmetric signature operation is used as follows: .. _interruptible-verify: -Interruptible asymmetric verification operations ------------------------------------------------- +Interruptible asymmetric verification +------------------------------------- The interruptible asymmetric verification operation verifies the signature of a message, or pre-computed hash, in an interruptible manner. For example, this can enable an application to remain responsive in an execution environment that does not provide multi-tasking. diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index f820b31c..e25dcd63 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -20,7 +20,14 @@ Changes to the API ~~~~~~~~~~~~~~~~~~ * Added `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` to evaluate the export buffer size for any asymmetric key pair or public key. -* Added interruptible operations for asymmetric sign and verify. See :secref:`sign` and :secref:`interruptible-operations`. +* Added interruptible operations for asymmetric signature and key agreement use cases. + See :secref:`interruptible-operations`. + + - :secref:`interruptible-sign` + - :secref:`interruptible-verify` + - :secref:`interruptible-generate-key` + - :secref:`interruptible-export-key` + - :secref:`interruptible-key-agreement` * Add extended key-generation and key-derivation functions, `psa_generate_key_custom()` and `psa_key_derivation_output_key_custom()`, that accept additional parameters to control the key creation process. * Define a key production parameter to select a non-default exponent for RSA key generation. From fe7c96ec4798f7a5b2710b9e52f261bb2f1bbfbd Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Thu, 20 Jun 2024 11:11:11 +0100 Subject: [PATCH 15/22] Rename interruptible operation graphic --- ...tion.pdf => interruptible_operation_complex.pdf} | Bin ... => interruptible_operation_complex.pdf.license} | 0 ...on.puml => interruptible_operation_complex.puml} | 0 ...tion.svg => interruptible_operation_complex.svg} | 0 ... => interruptible_operation_complex.svg.license} | 0 doc/crypto/overview/functionality.rst | 2 +- 6 files changed, 1 insertion(+), 1 deletion(-) rename doc/crypto/figure/{interruptible_operation.pdf => interruptible_operation_complex.pdf} (100%) rename doc/crypto/figure/{interruptible_operation.pdf.license => interruptible_operation_complex.pdf.license} (100%) rename doc/crypto/figure/{interruptible_operation.puml => interruptible_operation_complex.puml} (100%) rename doc/crypto/figure/{interruptible_operation.svg => interruptible_operation_complex.svg} (100%) rename doc/crypto/figure/{interruptible_operation.svg.license => interruptible_operation_complex.svg.license} (100%) diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation_complex.pdf similarity index 100% rename from doc/crypto/figure/interruptible_operation.pdf rename to doc/crypto/figure/interruptible_operation_complex.pdf diff --git a/doc/crypto/figure/interruptible_operation.pdf.license b/doc/crypto/figure/interruptible_operation_complex.pdf.license similarity index 100% rename from doc/crypto/figure/interruptible_operation.pdf.license rename to doc/crypto/figure/interruptible_operation_complex.pdf.license diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation_complex.puml similarity index 100% rename from doc/crypto/figure/interruptible_operation.puml rename to doc/crypto/figure/interruptible_operation_complex.puml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation_complex.svg similarity index 100% rename from doc/crypto/figure/interruptible_operation.svg rename to doc/crypto/figure/interruptible_operation_complex.svg diff --git a/doc/crypto/figure/interruptible_operation.svg.license b/doc/crypto/figure/interruptible_operation_complex.svg.license similarity index 100% rename from doc/crypto/figure/interruptible_operation.svg.license rename to doc/crypto/figure/interruptible_operation_complex.svg.license diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index a3e8bb09..bc267b0f 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -247,7 +247,7 @@ There are three components in an interruptible operation: All interruptible operations follow the same pattern of use, which is shown in :numref:`fig-interruptible`. -.. figure:: /figure/interruptible_operation.* +.. figure:: /figure/interruptible_operation_complex.* :name: fig-interruptible General state model for an interruptible operation From 91d4580c669c7f2e25754a8a9c7fdbfdb7b58b02 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Fri, 21 Jun 2024 12:25:52 +0100 Subject: [PATCH 16/22] Updated Functionality chapter to describe both basic and complex interruptible operations --- doc/crypto/appendix/history.rst | 2 +- doc/crypto/figure/interruptible_operation.pdf | Bin 0 -> 32413 bytes .../interruptible_operation.pdf.license | 2 + .../figure/interruptible_operation.puml | 32 +++++ doc/crypto/figure/interruptible_operation.svg | 9 ++ .../interruptible_operation.svg.license | 2 + .../interruptible_operation_complex.pdf | Bin 34550 -> 34722 bytes .../interruptible_operation_complex.puml | 18 +-- .../interruptible_operation_complex.svg | 38 ++--- doc/crypto/figure/multi_part_operation.pdf | Bin 32100 -> 32209 bytes doc/crypto/figure/multi_part_operation.puml | 4 +- doc/crypto/figure/multi_part_operation.svg | 20 +-- doc/crypto/overview/functionality.rst | 131 ++++++++++++++---- 13 files changed, 188 insertions(+), 70 deletions(-) create mode 100644 doc/crypto/figure/interruptible_operation.pdf create mode 100644 doc/crypto/figure/interruptible_operation.pdf.license create mode 100644 doc/crypto/figure/interruptible_operation.puml create mode 100644 doc/crypto/figure/interruptible_operation.svg create mode 100644 doc/crypto/figure/interruptible_operation.svg.license diff --git a/doc/crypto/appendix/history.rst b/doc/crypto/appendix/history.rst index e25dcd63..9c037c93 100644 --- a/doc/crypto/appendix/history.rst +++ b/doc/crypto/appendix/history.rst @@ -21,7 +21,7 @@ Changes to the API * Added `PSA_EXPORT_ASYMMETRIC_KEY_MAX_SIZE` to evaluate the export buffer size for any asymmetric key pair or public key. * Added interruptible operations for asymmetric signature and key agreement use cases. - See :secref:`interruptible-operations`. + See :secref:`interruptible-operations` and :secref:`interruptible-multi-part-operations`. - :secref:`interruptible-sign` - :secref:`interruptible-verify` diff --git a/doc/crypto/figure/interruptible_operation.pdf b/doc/crypto/figure/interruptible_operation.pdf new file mode 100644 index 0000000000000000000000000000000000000000..49bd453646c2223f87f312e8d7eb6ba5058019a9 GIT binary patch literal 32413 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 NPJu>CE2%7n_FtLw>QVpz literal 0 HcmV?d00001 diff --git a/doc/crypto/figure/interruptible_operation.pdf.license b/doc/crypto/figure/interruptible_operation.pdf.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.pdf.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation.puml b/doc/crypto/figure/interruptible_operation.puml new file mode 100644 index 00000000..2a49b993 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.puml @@ -0,0 +1,32 @@ +' SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +' SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license + +@startuml +!include atg-spec.pumh + +skinparam LegendFontSize 12 + +legend bottom + --""———""-- Solid lines show successful operation + ""---"" Dashed lines show error flows + ""………"" Dotted lines show operation cancellation +end legend + +state inactive as "//inactive//" +state active as "//active//" +state error as "//error//" ##darkred + +[*] --> inactive: **Initialize** +note as N1 + Operation object starts as + uninitialised memory +end note +inactive --> active: **Setup** +active --> active: **Complete**\n//incomplete// +active --> inactive: **Complete**\n//success// +error -[#darkred,dashed]r-> inactive: **Abort** +inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// +active -[#darkred,dashed]-> error: **Complete**\n//fails// +active -[#blue,dotted]l-> inactive: **Abort** + +@enduml diff --git a/doc/crypto/figure/interruptible_operation.svg b/doc/crypto/figure/interruptible_operation.svg new file mode 100644 index 00000000..1efea1a0 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg @@ -0,0 +1,9 @@ +inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupCompletesuccessAbortCompleteincompleteAbortSetupfailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/interruptible_operation.svg.license b/doc/crypto/figure/interruptible_operation.svg.license new file mode 100644 index 00000000..22ae5f88 --- /dev/null +++ b/doc/crypto/figure/interruptible_operation.svg.license @@ -0,0 +1,2 @@ +SPDX-FileCopyrightText: Copyright 2023-2024 Arm Limited and/or its affiliates +SPDX-License-Identifier: CC-BY-SA-4.0 AND LicenseRef-Patent-license diff --git a/doc/crypto/figure/interruptible_operation_complex.pdf b/doc/crypto/figure/interruptible_operation_complex.pdf index c5c9b415051e10e2d3a258069072815c8ca084d6..b2bd387ca619ed8adbc158be2858b2b5a85d3869 100644 GIT binary patch delta 4439 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 delta 4266 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 J>ze5S{{s%uW2OKA diff --git a/doc/crypto/figure/interruptible_operation_complex.puml b/doc/crypto/figure/interruptible_operation_complex.puml index 57c95408..00a2c339 100644 --- a/doc/crypto/figure/interruptible_operation_complex.puml +++ b/doc/crypto/figure/interruptible_operation_complex.puml @@ -23,22 +23,22 @@ note as N1 Operation object starts as uninitialised memory end note -inactive --> setup: **Setup** +inactive --> setup: **Setup**\n//success// setup --> setup: **Setup-complete**\n//incomplete// -setup --> input: **Setup-complete** -input --> input: **Update** +setup --> input: **Setup-complete**\n//success// +input --> input: **Update**\n//success// input --> completing: **Complete**\n//incomplete// -input --> inactive: **Complete** +input -u-> inactive: **Complete**\n//success// completing --> completing: **Complete**\n//incomplete// -completing --> inactive: **Complete** -error -[#darkred,dashed]-> inactive: **Abort** +completing -u-> inactive: **Complete**\n//success// +error -[#darkred,dashed]r-> inactive: **Abort** inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// setup -[#darkred,dashed]-> error: **Setup-complete**\n//fails// input -[#darkred,dashed]-> error: **Update**\n//fails// input -[#darkred,dashed]-> error: **Complete**\n//fails// completing -[#darkred,dashed]-> error: **Complete**\n//fails// -setup -[#blue,dotted]-> inactive: **Abort** -input -[#blue,dotted]-> inactive: **Abort** -completing -[#blue,dotted]-> inactive: **Abort** +setup -[#blue,dotted]u-> inactive: **Abort** +input -[#blue,dotted]u-> inactive: **Abort** +completing -[#blue,dotted]u-> inactive: **Abort** @enduml diff --git a/doc/crypto/figure/interruptible_operation_complex.svg b/doc/crypto/figure/interruptible_operation_complex.svg index ce2ed7c2..83d741c6 100644 --- a/doc/crypto/figure/interruptible_operation_complex.svg +++ b/doc/crypto/figure/interruptible_operation_complex.svg @@ -1,19 +1,19 @@ -inactivesetupinputcompletingerrorOperation object starts asuninitialised memoryInitializeSetupAbortSetup-completeincompleteSetup-completeUpdateCompleteincompleteCompleteAbortCompleteincompleteCompleteAbortAbortSetupfailsSetup-completefailsUpdatefailsCompletefailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file +inactivesetupinputcompletingerrorOperation object starts asuninitialised memoryInitializeSetupsuccessAbortSetup-completeincompleteSetup-completesuccessUpdatesuccessCompleteincompleteCompletesuccessAbortCompleteincompleteCompletesuccessAbortAbortSetupfailsSetup-completefailsUpdatefailsCompletefailsCompletefails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/figure/multi_part_operation.pdf b/doc/crypto/figure/multi_part_operation.pdf index 2d02cf0e576d2442f105cad318df2f3b3b59c635..72b0443d0076e82561f853ce1729690998e5d160 100644 GIT binary patch delta 3163 zcmZXSc|4SB8^^;d%h)PQgow$WnP(oe5Shu6eLF=cYGk6A3Wew?loS$2#=cBO5~J+X z6r&n@Lm68XMUrEi5Gh{goIl=Pp1b#qFR)!ocor(3@N?lr;(^%jp962y0r021=>{{3)-EG-k|6+W6Wa^Gv*Vmz8 z?gy@ZYjgDvaa>DcKSWCXv@^i*d+X@~j2DeD*RlS@m8h;_69-~k>_pP-{l_^v?AoIQ zFA-|TiF>DQv)uqexY z=-5@Bb>xR(!9n_au|oI42czudtR8Hfh_#|Tkh_b|&T6*8rx85g^_Z;K>C#fU1;160|IgKZ=4 zf)T~LYfIfqpI&|Lt;0N=!?nvf<1iWQRzh?3O8+zZeuP_jp)sBJxj0a?`#L@(mG-=F za&UBEM>gIxWS5Za|l1I<#!NEMM4;`QhGoT+ufyf zh9d3M5_`R%GHSqAnanF?D9zJM7!>?6w26|_-`NmCz*m}|lKeW z=)BTRv#72@CW#cTX)4`^Z|gb!!YXpD6FTo`H*4rTFB_{(@uTk(`k288jc)!i`o-nB z|2c|JHgth%!#zAX$nGjqx9^b!STDNo7+SD(bKkyN3O*eaQvPFaTOEAZVJ2|N%`R$=o_N9;ximiP2#9U8D@2Z=ygA6lQCee;% z;-KcN?t4>e_rM%BQ_@H{f|I-IPb1EHoRNn^58@T@{2^zOCfIN~d%^us< zbc%m=*|jG3siUny)kz~e#unb9>TO9wI4bhUs=htc{f(ST2klDtzpQQgSS|-gak))5 z&E(qML>Dd9U7~+6renK@-;PtK_%e}@bK|ojch-opa?~)pi{HT-ZLhAu5xhe=7lGop z*@gc}Xg%DlMy%b=r#PzcnT`>QmE5kbzXxV)Z8lt}qVPe-R7pf|&rVuEA|daATHk5A zPtUeD);I9-6Xl6!yr%u6tX`|?k#3+qN(?{2VatrwZV?%I-BN9ebZS?6CVdtXzJb^3 zpKKPC)^z_^^Mq<3Ii#KBIJ6{Fm{yLGd3rB|K zne(mORv)=pzRyZR{Eej+&5X5LF~lelEQaq;F=LKqHy-Cy_;rK31ziUqeAL(4EibJf zXy(G|rn!ELUkUZ?3Z&Yr%R(1}XO z&F`p^g;u|oVkOMFUPqOIbnY0I!s5|nsxV6WR=GBtV<)7~AEQcGOZsdW!TzSdg`waZ zpWr#O?McUlu)7MMf}Hce4n+%v@~lM+h)JFk&+Ou?#4n{KA3tl_0L-^d&tB;Mb&=c< z$L}et#jv1u3n&WvA5vs>gC;K6$b36-MqRXi%z(R3oMj77><|??Bg;>?mK=;rY}QA( za49n-c=qj#@DoIj2fXY@0gM`9cBs{LrtU+Qwy$B@{F7|nS&jL44aKGOJv2qL#A8BJ<{J$re~g%3X%9ChpR-Qx0{Q{2nkq}l5CFC>5sl)ulTm4 zREJ4?Gp!!!3cCv}WVC1MEz<52gb#L#@93oEKfJaZDn85-a=uZfxmDKwf&*`odTmLC zbzm-19nUk#mzNKB(>Y*ea`DJ{?J;=tk3-|9s)(-XSG*KV49MD_o0*^2e2*nWVPJk8F+%&4bw#A;xt!XZh6}Kw`2MWZ?CJa*ghx@KW+)Kl$rq!A-)n@VNFVI(~>D??`~%P$f=C-Iyms%+JG@^RqFpb+<6HYq-W zrNSvd`@En0>04oPdB?fbgud1hrI|DSZFC3My>+Xj;_hCL2WpgtkP**Pf^6?xz#z$U zsdw#TW#$Rc*|Vg4R%_yNRmi^YLK-@t$K69yuxvF0D(8>wtr{6Q7Cb-g=-_y%WJ}B{vb1}!AkQ*on~1Ui$^cyfhzRm+!(y-~0Ih;D0QR8`5C95WaY&Rz zhJgq)fIuQqXb?iY+NoB62Rh8#3SuQQ6L}+hnqWCi2fG@ CB~V#aC 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 diff --git a/doc/crypto/figure/multi_part_operation.puml b/doc/crypto/figure/multi_part_operation.puml index 28ea4b26..d7840140 100644 --- a/doc/crypto/figure/multi_part_operation.puml +++ b/doc/crypto/figure/multi_part_operation.puml @@ -24,10 +24,10 @@ end note inactive --> active: **Setup** active --> active: **Update** active --> inactive: **Finish** -error -[#darkred,dashed]-> inactive: **Abort** +error -[#darkred,dashed]r-> inactive: **Abort** inactive -[#darkred,dashed]-> inactive: **Setup**\n//fails// active -[#darkred,dashed]-> error: **Update**\n//fails// active -[#darkred,dashed]-> error: **Finish**\n//fails// -active -[#blue,dotted]-> inactive: **Abort** +active -[#blue,dotted]l-> inactive: **Abort** @enduml diff --git a/doc/crypto/figure/multi_part_operation.svg b/doc/crypto/figure/multi_part_operation.svg index cfb52f79..c3e57314 100644 --- a/doc/crypto/figure/multi_part_operation.svg +++ b/doc/crypto/figure/multi_part_operation.svg @@ -1,10 +1,10 @@ -inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupFinishAbortUpdateAbortSetupfailsUpdatefailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file +inactiveactiveerrorOperation object starts asuninitialised memoryInitializeSetupFinishAbortUpdateAbortSetupfailsUpdatefailsFinishfails———Solid lines show successful operation---Dashed lines show error flows………Dotted lines show operation cancellation \ No newline at end of file diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index bc267b0f..ab1511a5 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -222,6 +222,8 @@ It is safe to move a multi-part operation object to a different memory location, Each type of multi-part operation can have multiple *active* states. Documentation for the specific operation describes the configuration and update functions, and any requirements about their usage and ordering. +See :secref:`hash-mp` for an example of a multi-part operation. + .. _interruptible-operations: Interruptible operations @@ -235,6 +237,11 @@ For some use cases, this can be achieved by controlling the amount of data proce Unlike multi-part operations, an interruptible operation does not provide additional control over the inputs to an algorithm. If an application does not need to interrupt the operation, it is recommended that the appropriate single-part function is used. +Use cases for which the |API| defines interruptible operations include: + +* Asymmetric signature generation and verification. +* Key exchange protocols, including the use of ephemeral key-pairs. + There are three components in an interruptible operation: * A specific object type to maintain the state of the operation, in a similar way to multi-part operations. These types are implementation-defined. @@ -245,54 +252,56 @@ There are three components in an interruptible operation: Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation object is set up for a new operation, which permits the value to be queried after an operation has finished. -All interruptible operations follow the same pattern of use, which is shown in :numref:`fig-interruptible`. +Interruptible operations follow a common pattern of use, which is shown in :numref:`fig-interruptible`. -.. figure:: /figure/interruptible_operation_complex.* +.. figure:: /figure/interruptible_operation.* :name: fig-interruptible General state model for an interruptible operation The typical sequence of actions with a interruptible operation is as follows: -1. **Allocate:** Allocate memory for an operation object of the appropriate type. The application can use any allocation strategy: stack, heap, static, etc. +1. **Allocate:** Allocate memory for an operation object of the appropriate type. + The application can use any allocation strategy: stack, heap, static, etc. -#. **Initialize:** Initialize or assign the operation object by one of the following methods: +#. **Initialize:** Initialize or assign the interruptible operation object by one of the following methods: - - Set it to logical zero. This is automatic for static and global variables. Explicit initialization must use the associated ``PSA_xxx_IOP_INIT`` macro as the type is implementation-defined. - - Set it to all-bits zero. This is automatic if the object was allocated with ``calloc()``. + - Set it to logical zero. + This is automatic for static and global variables. + Explicit initialization must use the associated ``PSA_xxx_IOP_INIT`` macro as the type is implementation-defined. + - Set it to all-bits zero. + This is automatic if the object was allocated with ``calloc()``. - Assign the value of the associated macro ``PSA_xxx_IOP_INIT``. - Assign the result of calling the associated function ``psa_xxx_iop_init()``. The resulting object is now *inactive*. - It is an error to initialize an operation object that is in *active* or *error* states. This can leak memory or other resources. - -#. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. - - On success, an operation object enters a *setup* state. On failure, the operation object will remain *inactive*. - -#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is in *setup* state. - - If the setup computation is interrupted, a the operation remains in *setup* state. If setup completes successfully, the operation enters an *input* state. On failure, the operation object will enter an *error* state. - An application needs to repeat this step until the setup completes with success or an error status. + It is an error to initialize an interruptible operation object that is in *active* or *error* states. This can leak memory or other resources. -#. **Update:** Update an interruptible operation object in *input* state. The update function can provide additional parameters, supply data for processing or generate outputs. +#. **Setup:** Start a new interruptible operation on an *inactive* operation object. + Each interruptible operation object will define one or more setup functions to start a specific operation. - On success, the operation object remains in *input* state. On failure, the operation object will enter an *error* state. + On success, a setup function will put an interruptible operation object into an *active* state. + On failure, the operation object will remain *inactive*. -#. **Complete:** To end an interruptible operation, call the applicable completion function. This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. +#. **Complete:** To end an interruptible operation, call the applicable completion function. + This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. - If the finishing computation is interrupted, a the operation is left in *completing* state. If the operation completes successfully, the operation enters an *inactive* state. On failure, the operation object will enter an *error* state. + If the computation cannot be completed within the *maximum ops*, the interruptible operation is left in an *active* state. + If the operation completes successfully, the operation enters an *inactive* state. + On failure, the operation object will enter an *error* state. - An application needs to repeat this step until the completion function completes with success or an error status. + An application needs to repeat this step until the completion function returns with a success or an error status. -#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_iop_abort()`` function. This will release any resources associated with the operation and return the operation object to the *inactive* state. +#. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_iop_abort()`` function. + This will release any resources associated with the operation and return the operation object to the *inactive* state. - Any error that occurs to an operation while it is not in an *inactive* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_iop_abort()`` function to release the operation resources and return the object to the *inactive* state. + Any error that occurs to an operation while it is in an *active* state will result in the operation entering an *error* state. + The application must call the associated ``psa_xxx_iop_abort()`` function to release the operation resources and return the object to the *inactive* state. - ``psa_xxx_iop_abort()`` can be called on an *inactive* operation, and this has no effect. + ``psa_xxx_iop_abort()`` can be called on an *inactive* interruptible operation, and this has no effect. -Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the applicable setup functions again. +Once an interruptible operation object is returned to the *inactive* state, it can be reused by calling one of the setup functions again. If an interruptible operation object is not initialized before use, the behavior is undefined. @@ -300,14 +309,78 @@ If an interruptible operation function determines that the operation object is n If an interruptible operation function is called with an operation object in the wrong state, the function will return :code:`PSA_ERROR_BAD_STATE` and the operation object will enter the *error* state. -It is safe to move an interruptible operation object to a different memory location, for example, using a bitwise copy, and then to use the object in the new location. For example, an application can allocate an operation object on the stack and return it, or the operation object can be allocated within memory managed by a garbage collector. However, this does not permit the following behaviors: +It is safe to move an interruptible operation object to a different memory location, for example, using a bitwise copy, and then to use the object in the new location. +For example, an application can allocate an operation object on the stack and return it, or the operation object can be allocated within memory managed by a garbage collector. +However, this does not permit the following behaviors: * Moving the object while a function is being called on the object. See also :secref:`concurrency`. * Working with both the original and the copied operation objects. +Each type of interruptible operation can have multiple *active* states. +Documentation for the specific operation describes the setup and completion functions, and any requirements about their usage and ordering. + +See :secref:`interruptible-generate-key` for an example of an interruptible operation. + +.. _interruptible-multi-part-operations: + +Interruptible multi-part operations +~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ + +Some operations combine the behaviors of multi-part operations and interruptible operations. +They enable both the incremental provision of data input, and the interruption of computationally expensive function calls. + +In these interruptible operations, there can be more than one step at which the same function is called repeatedly until it no longer returns :code:`PSA_OPERATION_INCOMPLETE`. + +:numref:`fig-interruptible-mp` shows the interruptible multi-part operation flow that is used for asymmetric signature. + +.. figure:: /figure/interruptible_operation_complex.* + :name: fig-interruptible-mp + + State model for an interruptible multi-part operation + +The sequence of actions is an integration of the multi-part and interruptible flows. +In the following sequence, detail is only provided where actions behave differently: + +1. **Allocate** + +#. **Initialize** + +#. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. + Each operation object will define one or more setup functions to start a specific operation. + + On success, an operation object enters a *setup* state. + On failure, the operation object will remain *inactive*. + +#. **Complete-setup:** Complete the operation setup on an interruptible operation object that is in *setup* state. + + If the setup computation is interrupted, the operation remains in *setup* state. + If setup completes successfully, the operation enters an *input* state. + On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the setup completes with success or an error status. + +#. **Update:** Update an interruptible operation object in *input* state. + The update function can provide additional parameters, supply data for processing or generate outputs. + + On success, the operation object remains in *input* state. + On failure, the operation object will enter an *error* state. + +#. **Complete:** To end an interruptible operation, call the applicable completion function. + This will perform the final computation, produce any final outputs, and then release any resources associated with the operation. + + If the finishing computation is interrupted, a the operation is left in *completing* state. + If the operation completes successfully, the operation enters an *inactive* state. + On failure, the operation object will enter an *error* state. + + An application needs to repeat this step until the completion function completes with success or an error status. + +#. **Abort** + +The rules for use of an interruptible operation or multi-part operation also apply to an interruptible multi-part operation. See :secref:`multi-part-operations`. + Each type of interruptible operation can have multiple *setup*, *input*, and *completing* states. Documentation for the specific operation describes the setup, update and completion functions, and any requirements about their usage and ordering. -See :secref:`interruptible-sign` for an example of using an interruptible operation. +See :secref:`interruptible-sign` for an example of using an interruptible multi-part operation. Symmetric cryptography ~~~~~~~~~~~~~~~~~~~~~~ @@ -364,9 +437,9 @@ This specification defines interfaces for the following types of asymmetric cryp For asymmetric encryption, the API provides *single-part* functions. -For asymmetric signature, the API provides single-part functions and *interruptible operations* (see :secref:`interruptible-operations`). +For asymmetric signature, the API provides single-part functions and *interruptible multi-part operations* (see :secref:`interruptible-multi-part-operations`). -For key agreement, the API provides single-part functions and an additional input method for a key derivation operation. +For key agreement, the API provides single-part functions, an *interruptible operation* (see :secref:`interruptible-operations`), and an additional input method for a key derivation operation. For key encapsulation, the API provides single-part functions. From 960595e5e3df77dfcbedc1af1da83c953dd3ea3d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Wed, 26 Jun 2024 10:29:31 +0100 Subject: [PATCH 17/22] Deferred allocation of key ids Permit an implementation to defer reserving the persistent key id when performing an interruptible key creation operation. --- doc/crypto/api/keys/management.rst | 2 ++ doc/crypto/api/ops/key-agreement.rst | 2 ++ 2 files changed, 4 insertions(+) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index d00f3fde..aa428f4a 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -898,6 +898,8 @@ An interruptible key generation operation is used as follows: .. retval:: PSA_OPERATION_INCOMPLETE The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 100becab..ae59db57 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -564,6 +564,8 @@ An interruptible key agreement operation is used as follows: .. retval:: PSA_OPERATION_INCOMPLETE The function was interrupted after exhausting the maximum *ops*. The computation is incomplete, and this function must be called again with the same operation object to continue. + .. retval:: PSA_ERROR_ALREADY_EXISTS + This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. .. retval:: PSA_ERROR_BAD_STATE The following conditions can result in this error: From ca46ff2af080376ddc6630f9f9b2c357a33437e6 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 30 Jul 2024 15:07:08 +0100 Subject: [PATCH 18/22] Adopt the key attribute clarifications for key creation functions from #205 --- doc/crypto/api/keys/management.rst | 24 +++++++++++++-------- doc/crypto/api/ops/key-agreement.rst | 32 +++++++++++++++++++--------- 2 files changed, 37 insertions(+), 19 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index aa428f4a..8d5c8b9f 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -812,15 +812,21 @@ An interruptible key generation operation is used as follows: It must have been initialized as per the documentation for `psa_generate_key_iop_t`, and be inactive. .. param:: const psa_key_attributes_t * attributes The attributes for the new key. - This function uses the attributes as follows: - - * The key type is required. - It cannot be an asymmetric public key. - * The key size is required. - It must be a valid size for the key type. - * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation, see :secref:`permitted-algorithms`. - * The key usage flags define what operations are permitted with the key, see :secref:`key-usage-flags`. - * The key lifetime and identifier are required for a persistent key. + + The following attributes are required for all keys: + + * The key type. It must not be an asymmetric public key. + * The key size. It must be a valid size for the key type. + + The following attributes must be set for keys used in cryptographic operations: + + * The key permitted-algorithm policy, see :secref:`permitted-algorithms`. + * The key usage flags, see :secref:`key-usage-flags`. + + The following attributes must be set for keys that do not use the default volatile lifetime: + + * The key lifetime, see :secref:`key-lifetimes`. + * The key identifier is required for a key with a persistent lifetime, see :secref:`key-identifiers`. .. note:: This is an input parameter: it is not updated with the final key attributes. diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index ae59db57..5f3bd7f3 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -464,24 +464,33 @@ An interruptible key agreement operation is used as follows: .. param:: psa_algorithm_t alg The standalone key agreement algorithm to compute: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_STANDALONE_KEY_AGREEMENT(alg)` is true. .. param:: const psa_key_attributes_t * attributes - The attributes for the new key. - This function uses the attributes as follows: + The attributes for the key to be output on completion. + + The following attributes are required for all keys: - * The key type must be one of `PSA_KEY_TYPE_DERIVE`, `PSA_KEY_TYPE_RAW_DATA`, `PSA_KEY_TYPE_HMAC`, or `PSA_KEY_TYPE_PASSWORD`. + * The key type, which must be one of `PSA_KEY_TYPE_DERIVE`, `PSA_KEY_TYPE_RAW_DATA`, `PSA_KEY_TYPE_HMAC`, or `PSA_KEY_TYPE_PASSWORD`. Implementations must support the `PSA_KEY_TYPE_DERIVE` and `PSA_KEY_TYPE_RAW_DATA` key types. - * The size of the returned key is always the bit-size of the shared secret, rounded up to a whole number of bytes. - The key size in ``attributes`` can be zero; if it is nonzero, it must be equal to the output size of the key agreement, in bits. + The following attributes must be set for keys used in cryptographic operations: - The output size, in bits, of the key agreement is :code:`8 * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(type, bits)`, where ``type`` and ``bits`` are the type and bit-size of ``private_key``. + * The key permitted-algorithm policy, see :secref:`permitted-algorithms`. + * The key usage flags, see :secref:`key-usage-flags`. - * The key permitted-algorithm policy is required for keys that will be used for a cryptographic operation, see :secref:`permitted-algorithms`. - * The key usage flags define what operations are permitted with the key, see :secref:`key-usage-flags`. - * The key lifetime and identifier are required for a persistent key. + The following attributes must be set for keys that do not use the default volatile lifetime: + + * The key lifetime, see :secref:`key-lifetimes`. + * The key identifier is required for a key with a persistent lifetime, see :secref:`key-identifiers`. + + The following attributes are optional: + + * If the key size is nonzero, it must be equal to the output size of the key agreement, in bits. + + The output size, in bits, of the key agreement is :code:`8 * PSA_RAW_KEY_AGREEMENT_OUTPUT_SIZE(type, bits)`, where ``type`` and ``bits`` are the type and bit-size of ``private_key``. .. note:: - This is an input parameter: it is not updated with the final key attributes. The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. + This is an input parameter: it is not updated with the final key attributes. + The final attributes of the new key can be queried by calling `psa_get_key_attributes()` with the key's identifier. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -534,6 +543,8 @@ An interruptible key agreement operation is used as follows: This function sets up an interruptible operation to perform a key agreement. A key agreement algorithm takes two inputs: a private key ``private_key``, and a public key ``peer_key``. + When the interruptible operation completes, the shared secret is output in a key. The key's location, policy, and type are taken from ``attributes``. The size of the key is always the bit-size of the shared secret, rounded up to a whole number of bytes. + After a successful call to `psa_key_agreement_iop_setup()`, the operation is active. The operation can be completed by calling `psa_key_agreement_iop_complete()` repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. Once active, the application must eventually terminate the operation. @@ -583,6 +594,7 @@ An interruptible key agreement operation is used as follows: This is an interruptible function, and must be called repeatedly, until it returns a status code that is not :code:`PSA_OPERATION_INCOMPLETE`. When this function returns successfully, the shared secret is returned as a derivation key in ``key``, and the operation becomes inactive. + The attributes of the new key are specified in the call to `psa_key_agreement_iop_setup()` used to set up this operation. This key can be input to a key derivation operation using `psa_key_derivation_input_key()`. .. warning:: From 8c4bbcc82834ee4cfbd26f79197df4c10a17c7e8 Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 6 Aug 2024 14:24:05 +0100 Subject: [PATCH 19/22] Note that incremental key generation might only be available for some key types. --- doc/crypto/api/keys/management.rst | 13 ++++++++++--- 1 file changed, 10 insertions(+), 3 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 8d5c8b9f..295d7cc1 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -322,7 +322,7 @@ When creating a key, the attributes for the new key are specified in a `psa_key_ See the documentation of `psa_custom_key_parameters_t` for a list of non-default production parameters. See the key type definitions in :secref:`key-types` for details of the custom production parameters used for key generation. - If an application requires bounded execution when generating a key, it can use an interruptible key generation operation. + If an application requires bounded execution when generating a key, the implementation might provide support for interruptible key generation. See :secref:`interruptible-generate-key`. .. function:: psa_copy_key @@ -725,7 +725,11 @@ Interruptible key generation Generation of some key types can be computationally expensive. For example, RSA keys, and elliptic curve public keys. -An interruptible key generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. +For such keys, an interruptible key generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. + +.. note:: + An implementation of the |API| does not need to provide incremental generation for all key types supported by the implementation. + Use `psa_generate_key()` to create keys for types that do not need to be incrementally generated. An interruptible key generation operation is used as follows: @@ -839,7 +843,10 @@ An interruptible key generation operation is used as follows: .. retval:: PSA_ERROR_ALREADY_EXISTS This is an attempt to create a persistent key, and there is already a persistent key with the given identifier. .. retval:: PSA_ERROR_NOT_SUPPORTED - The key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. + The following conditions can result in this error: + + * The implementation does not support incremental generation of the requested key type. + * The key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. .. retval:: PSA_ERROR_INVALID_ARGUMENT The following conditions can result in this error: From 01196f5fc502ee3989ede33e03027f2c638410de Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 16 Dec 2024 17:36:42 +0000 Subject: [PATCH 20/22] Add versionadded placeholders for new API --- doc/crypto/api/keys/management.rst | 28 ++++++++++++++++++++++++++++ doc/crypto/api/ops/key-agreement.rst | 14 ++++++++++++++ 2 files changed, 42 insertions(+) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index 295d7cc1..e9783264 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -744,6 +744,8 @@ An interruptible key generation operation is used as follows: .. summary:: The type of the state data structure for an interruptible key generation operation. + .. versionadded:: 1.x + Before calling any function on an interruptible key generation operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -781,11 +783,15 @@ An interruptible key generation operation is used as follows: .. summary:: This macro evaluates to an initializer for an interruptible key generation operation object of type `psa_generate_key_iop_t`. + .. versionadded:: 1.x + .. function:: psa_generate_key_iop_init .. summary:: Return an initial value for an interruptible key generation operation object. + .. versionadded:: 1.x + .. return:: psa_generate_key_iop_t .. function:: psa_generate_key_iop_get_num_ops @@ -793,6 +799,8 @@ An interruptible key generation operation is used as follows: .. summary:: Get the number of *ops* that an interruptible key generation operation has taken so far. + .. versionadded:: 1.x + .. param:: psa_generate_key_iop_t * operation The interruptible key generation operation to inspect. @@ -811,6 +819,8 @@ An interruptible key generation operation is used as follows: .. summary:: Start an interruptible operation to generate a key or key pair. + .. versionadded:: 1.x + .. param:: psa_generate_key_iop_t * operation The interruptible key generation operation to set up. It must have been initialized as per the documentation for `psa_generate_key_iop_t`, and be inactive. @@ -897,6 +907,8 @@ An interruptible key generation operation is used as follows: .. summary:: Attempt to finish the interruptible generation of a key. + .. versionadded:: 1.x + .. param:: psa_generate_key_iop_t * operation The interruptible key generation operation to use. The operation must be active. @@ -941,6 +953,8 @@ An interruptible key generation operation is used as follows: .. summary:: Abort an interruptible key generation operation. + .. versionadded:: 1.x + .. param:: psa_generate_key_iop_t * operation The interruptible key generation operation to abort. @@ -983,6 +997,8 @@ An interruptible public-key export operation is used as follows: .. summary:: The type of the state data structure for an interruptible public-key export operation. + .. versionadded:: 1.x + Before calling any function on an interruptible public-key export operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -1020,11 +1036,15 @@ An interruptible public-key export operation is used as follows: .. summary:: This macro evaluates to an initializer for an interruptible public-key export operation object of type `psa_export_public_key_iop_t`. + .. versionadded:: 1.x + .. function:: psa_export_public_key_iop_init .. summary:: Return an initial value for an interruptible public-key export operation object. + .. versionadded:: 1.x + .. return:: psa_export_public_key_iop_t .. function:: psa_export_public_key_iop_get_num_ops @@ -1032,6 +1052,8 @@ An interruptible public-key export operation is used as follows: .. summary:: Get the number of *ops* that an interruptible public-key export operation has taken so far. + .. versionadded:: 1.x + .. param:: psa_export_public_key_iop_t * operation The interruptible public-key export operation to inspect. @@ -1050,6 +1072,8 @@ An interruptible public-key export operation is used as follows: .. summary:: Start an interruptible operation to export a public key or the public part of a key pair in binary format. + .. versionadded:: 1.x + .. param:: psa_export_public_key_iop_t * operation The interruptible public-key export operation to set up. It must have been initialized as per the documentation for `psa_export_public_key_iop_t`, and be inactive. @@ -1101,6 +1125,8 @@ An interruptible public-key export operation is used as follows: .. summary:: Attempt to finish the interruptible export of a public key. + .. versionadded:: 1.x + .. param:: psa_export_public_key_iop_t * operation The interruptible public-key export operation to use. The operation must be active. @@ -1158,6 +1184,8 @@ An interruptible public-key export operation is used as follows: .. summary:: Abort an interruptible public-key export operation. + .. versionadded:: 1.x + .. param:: psa_export_public_key_iop_t * operation The interruptible public-key export operation to abort. diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 5f3bd7f3..903b681e 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -381,6 +381,8 @@ An interruptible key agreement operation is used as follows: .. summary:: The type of the state data structure for an interruptible key agreement operation. + .. versionadded:: 1.x + Before calling any function on an interruptible key agreement operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -418,11 +420,15 @@ An interruptible key agreement operation is used as follows: .. summary:: This macro evaluates to an initializer for an interruptible key agreement operation object of type `psa_key_agreement_iop_t`. + .. versionadded:: 1.x + .. function:: psa_key_agreement_iop_init .. summary:: Return an initial value for an interruptible key agreement operation object. + .. versionadded:: 1.x + .. return:: psa_key_agreement_iop_t .. function:: psa_key_agreement_iop_get_num_ops @@ -430,6 +436,8 @@ An interruptible key agreement operation is used as follows: .. summary:: Get the number of *ops* that an interruptible key agreement operation has taken so far. + .. versionadded:: 1.x + .. param:: psa_key_agreement_iop_t * operation The interruptible key agreement operation to inspect. @@ -448,6 +456,8 @@ An interruptible key agreement operation is used as follows: .. summary:: Start an interruptible operation to perform a key agreement. + .. versionadded:: 1.x + .. param:: psa_key_agreement_iop_t * operation The interruptible key agreement operation to set up. It must have been initialized as per the documentation for `psa_key_agreement_iop_t`, and be inactive. @@ -560,6 +570,8 @@ An interruptible key agreement operation is used as follows: .. summary:: Attempt to finish a key agreement and return the shared secret. + .. versionadded:: 1.x + .. param:: psa_key_agreement_iop_t * operation The interruptible key agreement operation to use. The operation must be active. @@ -610,6 +622,8 @@ An interruptible key agreement operation is used as follows: .. summary:: Abort an interruptible key agreement operation. + .. versionadded:: 1.x + .. param:: psa_key_agreement_iop_t * operation The interruptible key agreement operation to abort. From 4c6cddaa8c6703b744539d3081e934707830da3d Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Mon, 16 Dec 2024 17:44:28 +0000 Subject: [PATCH 21/22] Fix hyphenation --- doc/crypto/api/keys/management.rst | 28 ++++++++-------- doc/crypto/api/ops/key-agreement.rst | 48 ++++++++++++++-------------- 2 files changed, 38 insertions(+), 38 deletions(-) diff --git a/doc/crypto/api/keys/management.rst b/doc/crypto/api/keys/management.rst index e9783264..3c1249f3 100644 --- a/doc/crypto/api/keys/management.rst +++ b/doc/crypto/api/keys/management.rst @@ -725,15 +725,15 @@ Interruptible key generation Generation of some key types can be computationally expensive. For example, RSA keys, and elliptic curve public keys. -For such keys, an interruptible key generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. +For such keys, an interruptible key-generation operation can be used instead of calling `psa_generate_key()`, in applications that have bounded execution requirements for use cases that require key generation. .. note:: An implementation of the |API| does not need to provide incremental generation for all key types supported by the implementation. Use `psa_generate_key()` to create keys for types that do not need to be incrementally generated. -An interruptible key generation operation is used as follows: +An interruptible key-generation operation is used as follows: -1. Allocate an interruptible key generation operation object, of type `psa_generate_key_iop_t`, which will be passed to all the functions listed here. +1. Allocate an interruptible key-generation operation object, of type `psa_generate_key_iop_t`, which will be passed to all the functions listed here. #. Initialize the operation object with one of the methods described in the documentation for `psa_generate_key_iop_t`, for example, `PSA_GENERATE_KEY_IOP_INIT`. #. Call `psa_generate_key_iop_setup()` to specify the key attributes. #. Call `psa_generate_key_iop_complete()` to finish generating the key, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. @@ -742,11 +742,11 @@ An interruptible key generation operation is used as follows: .. typedef:: /* implementation-defined type */ psa_generate_key_iop_t .. summary:: - The type of the state data structure for an interruptible key generation operation. + The type of the state data structure for an interruptible key-generation operation. .. versionadded:: 1.x - Before calling any function on an interruptible key generation operation object, the application must initialize it by any of the following means: + Before calling any function on an interruptible key-generation operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -781,14 +781,14 @@ An interruptible key generation operation is used as follows: :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible key generation operation object of type `psa_generate_key_iop_t`. + This macro evaluates to an initializer for an interruptible key-generation operation object of type `psa_generate_key_iop_t`. .. versionadded:: 1.x .. function:: psa_generate_key_iop_init .. summary:: - Return an initial value for an interruptible key generation operation object. + Return an initial value for an interruptible key-generation operation object. .. versionadded:: 1.x @@ -797,12 +797,12 @@ An interruptible key generation operation is used as follows: .. function:: psa_generate_key_iop_get_num_ops .. summary:: - Get the number of *ops* that an interruptible key generation operation has taken so far. + Get the number of *ops* that an interruptible key-generation operation has taken so far. .. versionadded:: 1.x .. param:: psa_generate_key_iop_t * operation - The interruptible key generation operation to inspect. + The interruptible key-generation operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. @@ -822,7 +822,7 @@ An interruptible key generation operation is used as follows: .. versionadded:: 1.x .. param:: psa_generate_key_iop_t * operation - The interruptible key generation operation to set up. + The interruptible key-generation operation to set up. It must have been initialized as per the documentation for `psa_generate_key_iop_t`, and be inactive. .. param:: const psa_key_attributes_t * attributes The attributes for the new key. @@ -910,7 +910,7 @@ An interruptible key generation operation is used as follows: .. versionadded:: 1.x .. param:: psa_generate_key_iop_t * operation - The interruptible key generation operation to use. + The interruptible key-generation operation to use. The operation must be active. .. param:: psa_key_id_t * key On success, an identifier for the newly created key. @@ -951,12 +951,12 @@ An interruptible key generation operation is used as follows: .. function:: psa_generate_key_iop_abort .. summary:: - Abort an interruptible key generation operation. + Abort an interruptible key-generation operation. .. versionadded:: 1.x .. param:: psa_generate_key_iop_t * operation - The interruptible key generation operation to abort. + The interruptible key-generation operation to abort. .. return:: psa_status_t .. retval:: PSA_SUCCESS @@ -979,7 +979,7 @@ An interruptible key generation operation is used as follows: Interruptible public-key export ------------------------------- -Extracting a public key from an asymmetric key-pair can be computationally expensive. +Extracting a public key from an asymmetric key pair can be computationally expensive. For example, computing an elliptic curve public key from the private key. An interruptible public-key export operation can be used instead of calling `psa_export_public_key()`, in applications that have bounded execution requirements for use cases that require public-key export. diff --git a/doc/crypto/api/ops/key-agreement.rst b/doc/crypto/api/ops/key-agreement.rst index 903b681e..c17941f2 100644 --- a/doc/crypto/api/ops/key-agreement.rst +++ b/doc/crypto/api/ops/key-agreement.rst @@ -17,10 +17,10 @@ Three functions are provided for a Diffie-Hellman-style key agreement where each * Where an application needs direct access to the shared secret, it can call `psa_raw_key_agreement()` instead. -If an application requires bounded execution during a key agreement, it can use an interruptible key agreement operation. +If an application requires bounded execution during a key agreement, it can use an interruptible key-agreement operation. See :secref:`interruptible-key-agreement`. -Using `psa_key_agreement()`, `psa_key_derivation_key_agreement()`, or an interruptible key agreement operation is recommended, as these do not expose the shared secret to the application. +Using `psa_key_agreement()`, `psa_key_derivation_key_agreement()`, or an interruptible key-agreement operation is recommended, as these do not expose the shared secret to the application. .. note:: @@ -231,7 +231,7 @@ Standalone key agreement .. warning:: The shared secret resulting from a key-agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key-derivation algorithm is applied to the result, to derive unbiased cryptographic keys. - If an application requires bounded execution during key agreement, it can use an interruptible key agreement operation. + If an application requires bounded execution during key agreement, it can use an interruptible key-agreement operation. See :secref:`interruptible-key-agreement`. .. function:: psa_raw_key_agreement @@ -363,13 +363,13 @@ Combining key agreement and key derivation Interruptible key agreement --------------------------- -Most key agreement algorithms are computationally expensive. +Most key-agreement algorithms are computationally expensive. -An interruptible key agreement operation can be used instead of calling `psa_key_agreement()`, in applications that have bounded execution requirements for use cases involving key agreement. +An interruptible key-agreement operation can be used instead of calling `psa_key_agreement()`, in applications that have bounded execution requirements for use cases involving key agreement. -An interruptible key agreement operation is used as follows: +An interruptible key-agreement operation is used as follows: -1. Allocate an interruptible key agreement operation object, of type `psa_key_agreement_iop_t`, which will be passed to all the functions listed here. +1. Allocate an interruptible key-agreement operation object, of type `psa_key_agreement_iop_t`, which will be passed to all the functions listed here. #. Initialize the operation object with one of the methods described in the documentation for `psa_key_agreement_iop_t`, for example, `PSA_KEY_AGREEMENT_IOP_INIT`. #. Call `psa_key_agreement_iop_setup()` to specify the algorithm, and provide the private key and the peer public key. #. Call `psa_key_agreement_iop_complete()` to finish the key agreement and output the shared secret, until this function does not return :code:`PSA_OPERATION_INCOMPLETE`. @@ -379,11 +379,11 @@ An interruptible key agreement operation is used as follows: .. typedef:: /* implementation-defined type */ psa_key_agreement_iop_t .. summary:: - The type of the state data structure for an interruptible key agreement operation. + The type of the state data structure for an interruptible key-agreement operation. .. versionadded:: 1.x - Before calling any function on an interruptible key agreement operation object, the application must initialize it by any of the following means: + Before calling any function on an interruptible key-agreement operation object, the application must initialize it by any of the following means: * Set the object to all-bits-zero, for example: @@ -418,14 +418,14 @@ An interruptible key agreement operation is used as follows: :definition: /* implementation-defined value */ .. summary:: - This macro evaluates to an initializer for an interruptible key agreement operation object of type `psa_key_agreement_iop_t`. + This macro evaluates to an initializer for an interruptible key-agreement operation object of type `psa_key_agreement_iop_t`. .. versionadded:: 1.x .. function:: psa_key_agreement_iop_init .. summary:: - Return an initial value for an interruptible key agreement operation object. + Return an initial value for an interruptible key-agreement operation object. .. versionadded:: 1.x @@ -434,12 +434,12 @@ An interruptible key agreement operation is used as follows: .. function:: psa_key_agreement_iop_get_num_ops .. summary:: - Get the number of *ops* that an interruptible key agreement operation has taken so far. + Get the number of *ops* that an interruptible key-agreement operation has taken so far. .. versionadded:: 1.x .. param:: psa_key_agreement_iop_t * operation - The interruptible key agreement operation to inspect. + The interruptible key-agreement operation to inspect. .. return:: uint32_t Number of *ops* that the operation has taken so far. @@ -459,7 +459,7 @@ An interruptible key agreement operation is used as follows: .. versionadded:: 1.x .. param:: psa_key_agreement_iop_t * operation - The interruptible key agreement operation to set up. + The interruptible key-agreement operation to set up. It must have been initialized as per the documentation for `psa_key_agreement_iop_t`, and be inactive. .. param:: psa_key_id_t private_key Identifier of the private key to use. @@ -472,7 +472,7 @@ An interruptible key agreement operation is used as follows: .. param:: size_t peer_key_length Size of ``peer_key`` in bytes. .. param:: psa_algorithm_t alg - The standalone key agreement algorithm to compute: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_STANDALONE_KEY_AGREEMENT(alg)` is true. + The standalone key-agreement algorithm to compute: a value of type `psa_algorithm_t` such that :code:`PSA_ALG_IS_STANDALONE_KEY_AGREEMENT(alg)` is true. .. param:: const psa_key_attributes_t * attributes The attributes for the key to be output on completion. @@ -518,12 +518,12 @@ An interruptible key agreement operation is used as follows: .. retval:: PSA_ERROR_INVALID_ARGUMENT The following conditions can result in this error: - * ``alg`` is not a key agreement algorithm. + * ``alg`` is not a key-agreement algorithm. * ``private_key`` is not compatible with ``alg``. * ``peer_key`` is not a valid public key corresponding to ``private_key``. * The output key attributes in ``attributes`` are not valid : - - The key type is not valid for key agreement output. + - The key type is not valid for key-agreement output. - The key size is nonzero, and is not the size of the shared secret. - The key lifetime is invalid. - The key identifier is not valid for the key lifetime. @@ -534,7 +534,7 @@ An interruptible key agreement operation is used as follows: .. retval:: PSA_ERROR_NOT_SUPPORTED The following conditions can result in this error: - * ``alg`` is not supported or is not a key agreement algorithm. + * ``alg`` is not supported or is not a key-agreement algorithm. * ``private_key`` is not supported for use with ``alg``. * The output key attributes, as a whole, are not supported, either by the implementation in general or in the specified storage location. .. retval:: PSA_ERROR_BAD_STATE @@ -550,8 +550,8 @@ An interruptible key agreement operation is used as follows: .. retval:: PSA_ERROR_DATA_INVALID .. retval:: PSA_ERROR_INSUFFICIENT_STORAGE - This function sets up an interruptible operation to perform a key agreement. - A key agreement algorithm takes two inputs: a private key ``private_key``, and a public key ``peer_key``. + This function sets up an interruptible operation to perform a key-agreement. + A key-agreement algorithm takes two inputs: a private key ``private_key``, and a public key ``peer_key``. When the interruptible operation completes, the shared secret is output in a key. The key's location, policy, and type are taken from ``attributes``. The size of the key is always the bit-size of the shared secret, rounded up to a whole number of bytes. @@ -573,7 +573,7 @@ An interruptible key agreement operation is used as follows: .. versionadded:: 1.x .. param:: psa_key_agreement_iop_t * operation - The interruptible key agreement operation to use. + The interruptible key-agreement operation to use. The operation must be active. .. param:: psa_key_id_t * key On success, an identifier for the newly created key. @@ -610,7 +610,7 @@ An interruptible key agreement operation is used as follows: This key can be input to a key derivation operation using `psa_key_derivation_input_key()`. .. warning:: - The shared secret resulting from a key agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key derivation algorithm is applied to the result, to derive unbiased cryptographic keys. + The shared secret resulting from a key-agreement algorithm such as finite-field Diffie-Hellman or elliptic curve Diffie-Hellman has biases. This makes it unsuitable for use as key material, for example, as an AES key. Instead, it is recommended that a key derivation algorithm is applied to the result, to derive unbiased cryptographic keys. If this function returns :code:`PSA_OPERATION_INCOMPLETE`, no key is returned, and this function must be called again to continue the operation. If this function returns an error status, the operation enters an error state and must be aborted by calling `psa_key_agreement_iop_abort()`. @@ -620,12 +620,12 @@ An interruptible key agreement operation is used as follows: .. function:: psa_key_agreement_iop_abort .. summary:: - Abort an interruptible key agreement operation. + Abort an interruptible key-agreement operation. .. versionadded:: 1.x .. param:: psa_key_agreement_iop_t * operation - The interruptible key agreement operation to abort. + The interruptible key-agreement operation to abort. .. return:: psa_status_t .. retval:: PSA_SUCCESS From 28344b7a01d8a9d50801ffb7acc05f2ff729df2a Mon Sep 17 00:00:00 2001 From: Andrew Thoelke Date: Tue, 17 Dec 2024 14:31:19 +0000 Subject: [PATCH 22/22] Clarify when the ops value is reset In the Functionality Overview, clarify the points at which the accumulated ops for an interruptible multi-part operation are reset. --- doc/crypto/overview/functionality.rst | 8 ++++++-- 1 file changed, 6 insertions(+), 2 deletions(-) diff --git a/doc/crypto/overview/functionality.rst b/doc/crypto/overview/functionality.rst index ab1511a5..9c7ede61 100644 --- a/doc/crypto/overview/functionality.rst +++ b/doc/crypto/overview/functionality.rst @@ -250,7 +250,7 @@ There are three components in an interruptible operation: An application can set an overall *maximum ops* value, that limits the *ops* performed within any interruptible function called by that application. The current *maximum ops* value can also be queried. If the *maximum ops* is not set by an application, interruptible functions will not return until the operation is complete. - Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation object is set up for a new operation, which permits the value to be queried after an operation has finished. + Each interruptible operation also provides a function to report the cumulative number of *ops* used by the operation. This value is only reset when the operation is aborted, or when an operation object is set up for a new operation. This permits the final value to be queried after an operation has finished successfully. Interruptible operations follow a common pattern of use, which is shown in :numref:`fig-interruptible`. @@ -281,6 +281,8 @@ The typical sequence of actions with a interruptible operation is as follows: #. **Setup:** Start a new interruptible operation on an *inactive* operation object. Each interruptible operation object will define one or more setup functions to start a specific operation. + The accumulated *ops* value for the operation is reset to zero. + On success, a setup function will put an interruptible operation object into an *active* state. On failure, the operation object will remain *inactive*. @@ -294,7 +296,7 @@ The typical sequence of actions with a interruptible operation is as follows: An application needs to repeat this step until the completion function returns with a success or an error status. #. **Abort:** An interruptible operation can be aborted at any stage during its use by calling the associated ``psa_xxx_iop_abort()`` function. - This will release any resources associated with the operation and return the operation object to the *inactive* state. + This will release any resources associated with the operation, return the operation object to the *inactive* state, and reset the accumulated *ops* value to zero. Any error that occurs to an operation while it is in an *active* state will result in the operation entering an *error* state. The application must call the associated ``psa_xxx_iop_abort()`` function to release the operation resources and return the object to the *inactive* state. @@ -348,6 +350,8 @@ In the following sequence, detail is only provided where actions behave differen #. **Begin-setup:** Start a new interruptible operation on an *inactive* operation object. Each operation object will define one or more setup functions to start a specific operation. + The accumulated *ops* value for the operation is reset to zero. + On success, an operation object enters a *setup* state. On failure, the operation object will remain *inactive*.