PNG  IHDRX cHRMz&u0`:pQ<bKGD pHYsodtIME MeqIDATxw]Wug^Qd˶ 6`!N:!@xI~)%7%@Bh&`lnjVF29gΨ4E$|>cɚ{gk= %,a KX%,a KX%,a KX%,a KX%,a KX%,a KX%, b` ǟzeאfp]<!SJmɤY޲ڿ,%c ~ع9VH.!Ͳz&QynֺTkRR.BLHi٪:l;@(!MԴ=žI,:o&N'Kù\vRmJ雵֫AWic H@" !: Cé||]k-Ha oݜ:y F())u]aG7*JV@J415p=sZH!=!DRʯvɱh~V\}v/GKY$n]"X"}t@ xS76^[bw4dsce)2dU0 CkMa-U5tvLƀ~mlMwfGE/-]7XAƟ`׮g ewxwC4\[~7@O-Q( a*XGƒ{ ՟}$_y3tĐƤatgvێi|K=uVyrŲlLӪuܿzwk$m87k( `múcE)"@rK( z4$D; 2kW=Xb$V[Ru819קR~qloѱDyįݎ*mxw]y5e4K@ЃI0A D@"BDk_)N\8͜9dz"fK0zɿvM /.:2O{ Nb=M=7>??Zuo32 DLD@D| &+֎C #B8ַ`bOb $D#ͮҪtx]%`ES`Ru[=¾!@Od37LJ0!OIR4m]GZRJu$‡c=%~s@6SKy?CeIh:[vR@Lh | (BhAMy=݃  G"'wzn޺~8ԽSh ~T*A:xR[ܹ?X[uKL_=fDȊ؂p0}7=D$Ekq!/t.*2ʼnDbŞ}DijYaȲ(""6HA;:LzxQ‘(SQQ}*PL*fc\s `/d'QXW, e`#kPGZuŞuO{{wm[&NBTiiI0bukcA9<4@SӊH*؎4U/'2U5.(9JuDfrޱtycU%j(:RUbArLֺN)udA':uGQN"-"Is.*+k@ `Ojs@yU/ H:l;@yyTn}_yw!VkRJ4P)~y#)r,D =ě"Q]ci'%HI4ZL0"MJy 8A{ aN<8D"1#IJi >XjX֔#@>-{vN!8tRݻ^)N_╗FJEk]CT՟ YP:_|H1@ CBk]yKYp|og?*dGvzنzӴzjֺNkC~AbZƷ`.H)=!QͷVTT(| u78y֮}|[8-Vjp%2JPk[}ԉaH8Wpqhwr:vWª<}l77_~{s۴V+RCģ%WRZ\AqHifɤL36: #F:p]Bq/z{0CU6ݳEv_^k7'>sq*+kH%a`0ԣisqにtү04gVgW΂iJiS'3w.w}l6MC2uԯ|>JF5`fV5m`Y**Db1FKNttu]4ccsQNnex/87+}xaUW9y>ͯ骵G{䩓Գ3+vU}~jJ.NFRD7<aJDB1#ҳgSb,+CS?/ VG J?|?,2#M9}B)MiE+G`-wo߫V`fio(}S^4e~V4bHOYb"b#E)dda:'?}׮4繏`{7Z"uny-?ǹ;0MKx{:_pÚmFמ:F " .LFQLG)Q8qN q¯¯3wOvxDb\. BKD9_NN &L:4D{mm o^tֽ:q!ƥ}K+<"m78N< ywsard5+вz~mnG)=}lYݧNj'QJS{S :UYS-952?&O-:W}(!6Mk4+>A>j+i|<<|;ر^߉=HE|V#F)Emm#}/"y GII웻Jі94+v뾧xu~5C95~ūH>c@덉pʃ1/4-A2G%7>m;–Y,cyyaln" ?ƻ!ʪ<{~h~i y.zZB̃/,雋SiC/JFMmBH&&FAbϓO^tubbb_hZ{_QZ-sύodFgO(6]TJA˯#`۶ɟ( %$&+V'~hiYy>922 Wp74Zkq+Ovn錄c>8~GqܲcWꂎz@"1A.}T)uiW4="jJ2W7mU/N0gcqܗOO}?9/wìXžΏ0 >֩(V^Rh32!Hj5`;O28؇2#ݕf3 ?sJd8NJ@7O0 b־?lldщ̡&|9C.8RTWwxWy46ah嘦mh٤&l zCy!PY?: CJyв]dm4ǜҐR޻RլhX{FƯanшQI@x' ao(kUUuxW_Ñ줮[w8 FRJ(8˼)_mQ _!RJhm=!cVmm ?sFOnll6Qk}alY}; "baӌ~M0w,Ggw2W:G/k2%R,_=u`WU R.9T"v,<\Ik޽/2110Ӿxc0gyC&Ny޽JҢrV6N ``یeA16"J³+Rj*;BϜkZPJaÍ<Jyw:NP8/D$ 011z֊Ⱳ3ι֘k1V_"h!JPIΣ'ɜ* aEAd:ݺ>y<}Lp&PlRfTb1]o .2EW\ͮ]38؋rTJsǏP@芎sF\> P^+dYJLbJ C-xϐn> ι$nj,;Ǖa FU *择|h ~izť3ᤓ`K'-f tL7JK+vf2)V'-sFuB4i+m+@My=O҈0"|Yxoj,3]:cо3 $#uŘ%Y"y죯LebqtҢVzq¼X)~>4L׶m~[1_k?kxֺQ`\ |ٛY4Ѯr!)N9{56(iNq}O()Em]=F&u?$HypWUeB\k]JɩSع9 Zqg4ZĊo oMcjZBU]B\TUd34ݝ~:7ڶSUsB0Z3srx 7`:5xcx !qZA!;%͚7&P H<WL!džOb5kF)xor^aujƍ7 Ǡ8/p^(L>ὴ-B,{ۇWzֺ^k]3\EE@7>lYBȝR.oHnXO/}sB|.i@ɥDB4tcm,@ӣgdtJ!lH$_vN166L__'Z)y&kH;:,Y7=J 9cG) V\hjiE;gya~%ks_nC~Er er)muuMg2;֫R)Md) ,¶ 2-wr#F7<-BBn~_(o=KO㭇[Xv eN_SMgSҐ BS헃D%g_N:/pe -wkG*9yYSZS.9cREL !k}<4_Xs#FmҶ:7R$i,fi!~' # !6/S6y@kZkZcX)%5V4P]VGYq%H1!;e1MV<!ϐHO021Dp= HMs~~a)ަu7G^];git!Frl]H/L$=AeUvZE4P\.,xi {-~p?2b#amXAHq)MWǾI_r`S Hz&|{ +ʖ_= (YS(_g0a03M`I&'9vl?MM+m~}*xT۲(fY*V4x@29s{DaY"toGNTO+xCAO~4Ϳ;p`Ѫ:>Ҵ7K 3}+0 387x\)a"/E>qpWB=1 ¨"MP(\xp߫́A3+J] n[ʼnӼaTbZUWb={~2ooKױӰp(CS\S筐R*JغV&&"FA}J>G֐p1ٸbk7 ŘH$JoN <8s^yk_[;gy-;߉DV{c B yce% aJhDȶ 2IdйIB/^n0tNtџdcKj4϶v~- CBcgqx9= PJ) dMsjpYB] GD4RDWX +h{y`,3ꊕ$`zj*N^TP4L:Iz9~6s) Ga:?y*J~?OrMwP\](21sZUD ?ܟQ5Q%ggW6QdO+\@ ̪X'GxN @'4=ˋ+*VwN ne_|(/BDfj5(Dq<*tNt1х!MV.C0 32b#?n0pzj#!38}޴o1KovCJ`8ŗ_"]] rDUy޲@ Ȗ-;xџ'^Y`zEd?0„ DAL18IS]VGq\4o !swV7ˣι%4FѮ~}6)OgS[~Q vcYbL!wG3 7띸*E Pql8=jT\꘿I(z<[6OrR8ºC~ډ]=rNl[g|v TMTղb-o}OrP^Q]<98S¤!k)G(Vkwyqyr޽Nv`N/e p/~NAOk \I:G6]4+K;j$R:Mi #*[AȚT,ʰ,;N{HZTGMoּy) ]%dHء9Պ䠬|<45,\=[bƟ8QXeB3- &dҩ^{>/86bXmZ]]yޚN[(WAHL$YAgDKp=5GHjU&99v簪C0vygln*P)9^͞}lMuiH!̍#DoRBn9l@ xA/_v=ȺT{7Yt2N"4!YN`ae >Q<XMydEB`VU}u]嫇.%e^ánE87Mu\t`cP=AD/G)sI"@MP;)]%fH9'FNsj1pVhY&9=0pfuJ&gޤx+k:!r˭wkl03׼Ku C &ѓYt{.O.zҏ z}/tf_wEp2gvX)GN#I ݭ߽v/ .& и(ZF{e"=V!{zW`, ]+LGz"(UJp|j( #V4, 8B 0 9OkRrlɱl94)'VH9=9W|>PS['G(*I1==C<5"Pg+x'K5EMd؞Af8lG ?D FtoB[je?{k3zQ vZ;%Ɠ,]E>KZ+T/ EJxOZ1i #T<@ I}q9/t'zi(EMqw`mYkU6;[t4DPeckeM;H}_g pMww}k6#H㶏+b8雡Sxp)&C $@'b,fPߑt$RbJ'vznuS ~8='72_`{q纶|Q)Xk}cPz9p7O:'|G~8wx(a 0QCko|0ASD>Ip=4Q, d|F8RcU"/KM opKle M3#i0c%<7׿p&pZq[TR"BpqauIp$ 8~Ĩ!8Սx\ւdT>>Z40ks7 z2IQ}ItԀ<-%S⍤};zIb$I 5K}Q͙D8UguWE$Jh )cu4N tZl+[]M4k8֦Zeq֮M7uIqG 1==tLtR,ƜSrHYt&QP윯Lg' I,3@P'}'R˪e/%-Auv·ñ\> vDJzlӾNv5:|K/Jb6KI9)Zh*ZAi`?S {aiVDԲuy5W7pWeQJk֤#5&V<̺@/GH?^τZL|IJNvI:'P=Ϛt"¨=cud S Q.Ki0 !cJy;LJR;G{BJy޺[^8fK6)=yʊ+(k|&xQ2`L?Ȓ2@Mf 0C`6-%pKpm')c$׻K5[J*U[/#hH!6acB JA _|uMvDyk y)6OPYjœ50VT K}cǻP[ $:]4MEA.y)|B)cf-A?(e|lɉ#P9V)[9t.EiQPDѠ3ϴ;E:+Օ t ȥ~|_N2,ZJLt4! %ա]u {+=p.GhNcŞQI?Nd'yeh n7zi1DB)1S | S#ًZs2|Ɛy$F SxeX{7Vl.Src3E℃Q>b6G ўYCmtկ~=K0f(=LrAS GN'ɹ9<\!a`)֕y[uՍ[09` 9 +57ts6}b4{oqd+J5fa/,97J#6yν99mRWxJyѡyu_TJc`~W>l^q#Ts#2"nD1%fS)FU w{ܯ R{ ˎ󅃏џDsZSQS;LV;7 Od1&1n$ N /.q3~eNɪ]E#oM~}v֯FڦwyZ=<<>Xo稯lfMFV6p02|*=tV!c~]fa5Y^Q_WN|Vs 0ҘދU97OI'N2'8N֭fgg-}V%y]U4 峧p*91#9U kCac_AFңĪy뚇Y_AiuYyTTYЗ-(!JFLt›17uTozc. S;7A&&<ԋ5y;Ro+:' *eYJkWR[@F %SHWP 72k4 qLd'J "zB6{AC0ƁA6U.'F3:Ȅ(9ΜL;D]m8ڥ9}dU "v!;*13Rg^fJyShyy5auA?ɩGHRjo^]׽S)Fm\toy 4WQS@mE#%5ʈfFYDX ~D5Ϡ9tE9So_aU4?Ѽm%&c{n>.KW1Tlb}:j uGi(JgcYj0qn+>) %\!4{LaJso d||u//P_y7iRJ߬nHOy) l+@$($VFIQ9%EeKʈU. ia&FY̒mZ=)+qqoQn >L!qCiDB;Y<%} OgBxB!ØuG)WG9y(Ą{_yesuZmZZey'Wg#C~1Cev@0D $a@˲(.._GimA:uyw֬%;@!JkQVM_Ow:P.s\)ot- ˹"`B,e CRtaEUP<0'}r3[>?G8xU~Nqu;Wm8\RIkբ^5@k+5(By'L&'gBJ3ݶ!/㮻w҅ yqPWUg<e"Qy*167΃sJ\oz]T*UQ<\FԎ`HaNmڜ6DysCask8wP8y9``GJ9lF\G g's Nn͵MLN֪u$| /|7=]O)6s !ĴAKh]q_ap $HH'\1jB^s\|- W1:=6lJBqjY^LsPk""`]w)󭃈,(HC ?䔨Y$Sʣ{4Z+0NvQkhol6C.婧/u]FwiVjZka&%6\F*Ny#8O,22+|Db~d ~Çwc N:FuuCe&oZ(l;@ee-+Wn`44AMK➝2BRՈt7g*1gph9N) *"TF*R(#'88pm=}X]u[i7bEc|\~EMn}P瘊J)K.0i1M6=7'_\kaZ(Th{K*GJyytw"IO-PWJk)..axӝ47"89Cc7ĐBiZx 7m!fy|ϿF9CbȩV 9V-՛^pV̌ɄS#Bv4-@]Vxt-Z, &ֺ*diؠ2^VXbs֔Ìl.jQ]Y[47gj=幽ex)A0ip׳ W2[ᎇhuE^~q흙L} #-b۸oFJ_QP3r6jr+"nfzRJTUqoaۍ /$d8Mx'ݓ= OՃ| )$2mcM*cЙj}f };n YG w0Ia!1Q.oYfr]DyISaP}"dIӗթO67jqR ҊƐƈaɤGG|h;t]䗖oSv|iZqX)oalv;۩meEJ\!8=$4QU4Xo&VEĊ YS^E#d,yX_> ۘ-e\ "Wa6uLĜZi`aD9.% w~mB(02G[6y.773a7 /=o7D)$Z 66 $bY^\CuP. (x'"J60׿Y:Oi;F{w佩b+\Yi`TDWa~|VH)8q/=9!g߆2Y)?ND)%?Ǐ`k/sn:;O299yB=a[Ng 3˲N}vLNy;*?x?~L&=xyӴ~}q{qE*IQ^^ͧvü{Huu=R|>JyUlZV, B~/YF!Y\u_ݼF{_C)LD]m {H 0ihhadd nUkf3oٺCvE\)QJi+֥@tDJkB$1!Đr0XQ|q?d2) Ӣ_}qv-< FŊ߫%roppVBwü~JidY4:}L6M7f٬F "?71<2#?Jyy4뷢<_a7_=Q E=S1И/9{+93֮E{ǂw{))?maÆm(uLE#lïZ  ~d];+]h j?!|$F}*"4(v'8s<ŏUkm7^7no1w2ؗ}TrͿEk>p'8OB7d7R(A 9.*Mi^ͳ; eeUwS+C)uO@ =Sy]` }l8^ZzRXj[^iUɺ$tj))<sbDJfg=Pk_{xaKo1:-uyG0M ԃ\0Lvuy'ȱc2Ji AdyVgVh!{]/&}}ċJ#%d !+87<;qN޼Nفl|1N:8ya  8}k¾+-$4FiZYÔXk*I&'@iI99)HSh4+2G:tGhS^繿 Kتm0 вDk}֚+QT4;sC}rՅE,8CX-e~>G&'9xpW,%Fh,Ry56Y–hW-(v_,? ; qrBk4-V7HQ;ˇ^Gv1JVV%,ik;D_W!))+BoS4QsTM;gt+ndS-~:11Sgv!0qRVh!"Ȋ(̦Yl.]PQWgٳE'`%W1{ndΗBk|Ž7ʒR~,lnoa&:ü$ 3<a[CBݮwt"o\ePJ=Hz"_c^Z.#ˆ*x z̝grY]tdkP*:97YľXyBkD4N.C_[;F9`8& !AMO c `@BA& Ost\-\NX+Xp < !bj3C&QL+*&kAQ=04}cC!9~820G'PC9xa!w&bo_1 Sw"ܱ V )Yl3+ס2KoXOx]"`^WOy :3GO0g;%Yv㐫(R/r (s } u B &FeYZh0y> =2<Ϟc/ -u= c&׭,.0"g"7 6T!vl#sc>{u/Oh Bᾈ)۴74]x7 gMӒ"d]U)}" v4co[ ɡs 5Gg=XR14?5A}D "b{0$L .\4y{_fe:kVS\\O]c^W52LSBDM! C3Dhr̦RtArx4&agaN3Cf<Ԉp4~ B'"1@.b_/xQ} _߃҉/gٓ2Qkqp0շpZ2fԫYz< 4L.Cyυι1t@鎫Fe sYfsF}^ V}N<_`p)alٶ "(XEAVZ<)2},:Ir*#m_YӼ R%a||EƼIJ,,+f"96r/}0jE/)s)cjW#w'Sʯ5<66lj$a~3Kʛy 2:cZ:Yh))+a߭K::N,Q F'qB]={.]h85C9cr=}*rk?vwV렵ٸW Rs%}rNAkDv|uFLBkWY YkX מ|)1!$#3%y?pF<@<Rr0}: }\J [5FRxY<9"SQdE(Q*Qʻ)q1E0B_O24[U'],lOb ]~WjHޏTQ5Syu wq)xnw8~)c 쫬gٲߠ H% k5dƝk> kEj,0% b"vi2Wس_CuK)K{n|>t{P1򨾜j>'kEkƗBg*H%'_aY6Bn!TL&ɌOb{c`'d^{t\i^[uɐ[}q0lM˕G:‚4kb祔c^:?bpg… +37stH:0}en6x˟%/<]BL&* 5&fK9Mq)/iyqtA%kUe[ڛKN]Ě^,"`/ s[EQQm?|XJ߅92m]G.E΃ח U*Cn.j_)Tѧj̿30ڇ!A0=͜ar I3$C^-9#|pk!)?7.x9 @OO;WƝZBFU keZ75F6Tc6"ZȚs2y/1 ʵ:u4xa`C>6Rb/Yм)^=+~uRd`/|_8xbB0?Ft||Z\##|K 0>>zxv8۴吅q 8ĥ)"6>~\8:qM}#͚'ĉ#p\׶ l#bA?)|g g9|8jP(cr,BwV (WliVxxᡁ@0Okn;ɥh$_ckCgriv}>=wGzβ KkBɛ[˪ !J)h&k2%07δt}!d<9;I&0wV/ v 0<H}L&8ob%Hi|޶o&h1L|u֦y~󛱢8fٲUsւ)0oiFx2}X[zVYr_;N(w]_4B@OanC?gĦx>мgx>ΛToZoOMp>40>V Oy V9iq!4 LN,ˢu{jsz]|"R޻&'ƚ{53ўFu(<٪9:΋]B;)B>1::8;~)Yt|0(pw2N%&X,URBK)3\zz&}ax4;ǟ(tLNg{N|Ǽ\G#C9g$^\}p?556]/RP.90 k,U8/u776s ʪ_01چ|\N 0VV*3H鴃J7iI!wG_^ypl}r*jɤSR 5QN@ iZ#1ٰy;_\3\BQQ x:WJv츟ٯ$"@6 S#qe딇(/P( Dy~TOϻ<4:-+F`0||;Xl-"uw$Цi󼕝mKʩorz"mϺ$F:~E'ҐvD\y?Rr8_He@ e~O,T.(ފR*cY^m|cVR[8 JҡSm!ΆԨb)RHG{?MpqrmN>߶Y)\p,d#xۆWY*,l6]v0h15M˙MS8+EdI='LBJIH7_9{Caз*Lq,dt >+~ّeʏ?xԕ4bBAŚjﵫ!'\Ը$WNvKO}ӽmSşذqsOy?\[,d@'73'j%kOe`1.g2"e =YIzS2|zŐƄa\U,dP;jhhhaxǶ?КZ՚.q SE+XrbOu%\GتX(H,N^~]JyEZQKceTQ]VGYqnah;y$cQahT&QPZ*iZ8UQQM.qo/T\7X"u?Mttl2Xq(IoW{R^ ux*SYJ! 4S.Jy~ BROS[V|žKNɛP(L6V^|cR7i7nZW1Fd@ Ara{詑|(T*dN]Ko?s=@ |_EvF]׍kR)eBJc" MUUbY6`~V޴dJKß&~'d3i WWWWWW
Current Directory: /opt/saltstack/salt/lib/python3.10/site-packages/Cryptodome/PublicKey
Viewing File: /opt/saltstack/salt/lib/python3.10/site-packages/Cryptodome/PublicKey/ECC.py
# =================================================================== # # Copyright (c) 2015, Legrandin <helderijs@gmail.com> # All rights reserved. # # Redistribution and use in source and binary forms, with or without # modification, are permitted provided that the following conditions # are met: # # 1. Redistributions of source code must retain the above copyright # notice, this list of conditions and the following disclaimer. # 2. Redistributions in binary form must reproduce the above copyright # notice, this list of conditions and the following disclaimer in # the documentation and/or other materials provided with the # distribution. # # THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS # "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT # LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS # FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE # COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, # INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, # BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; # LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER # CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT # LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN # ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE # POSSIBILITY OF SUCH DAMAGE. # =================================================================== from __future__ import print_function import re import struct import binascii from collections import namedtuple from Cryptodome.Util.py3compat import bord, tobytes, tostr, bchr, is_string from Cryptodome.Util.number import bytes_to_long, long_to_bytes from Cryptodome.Math.Numbers import Integer from Cryptodome.Util.asn1 import (DerObjectId, DerOctetString, DerSequence, DerBitString) from Cryptodome.Util._raw_api import (load_pycryptodome_raw_lib, VoidPointer, SmartPointer, c_size_t, c_uint8_ptr, c_ulonglong, null_pointer) from Cryptodome.PublicKey import (_expand_subject_public_key_info, _create_subject_public_key_info, _extract_subject_public_key_info) from Cryptodome.Hash import SHA512, SHAKE256 from Cryptodome.Random import get_random_bytes from Cryptodome.Random.random import getrandbits _ec_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ec_ws", """ typedef void EcContext; typedef void EcPoint; int ec_ws_new_context(EcContext **pec_ctx, const uint8_t *modulus, const uint8_t *b, const uint8_t *order, size_t len, uint64_t seed); void ec_free_context(EcContext *ec_ctx); int ec_ws_new_point(EcPoint **pecp, const uint8_t *x, const uint8_t *y, size_t len, const EcContext *ec_ctx); void ec_ws_free_point(EcPoint *ecp); int ec_ws_get_xy(uint8_t *x, uint8_t *y, size_t len, const EcPoint *ecp); int ec_ws_double(EcPoint *p); int ec_ws_add(EcPoint *ecpa, EcPoint *ecpb); int ec_ws_scalar(EcPoint *ecp, const uint8_t *k, size_t len, uint64_t seed); int ec_ws_clone(EcPoint **pecp2, const EcPoint *ecp); int ec_ws_cmp(const EcPoint *ecp1, const EcPoint *ecp2); int ec_ws_neg(EcPoint *p); """) _ed25519_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed25519", """ typedef void Point; int ed25519_new_point(Point **out, const uint8_t x[32], const uint8_t y[32], size_t modsize, const void *context); int ed25519_clone(Point **P, const Point *Q); void ed25519_free_point(Point *p); int ed25519_cmp(const Point *p1, const Point *p2); int ed25519_neg(Point *p); int ed25519_get_xy(uint8_t *xb, uint8_t *yb, size_t modsize, Point *p); int ed25519_double(Point *p); int ed25519_add(Point *P1, const Point *P2); int ed25519_scalar(Point *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); """) _ed448_lib = load_pycryptodome_raw_lib("Cryptodome.PublicKey._ed448", """ typedef void EcContext; typedef void PointEd448; int ed448_new_context(EcContext **pec_ctx); void ed448_context(EcContext *ec_ctx); void ed448_free_context(EcContext *ec_ctx); int ed448_new_point(PointEd448 **out, const uint8_t x[56], const uint8_t y[56], size_t len, const EcContext *context); int ed448_clone(PointEd448 **P, const PointEd448 *Q); void ed448_free_point(PointEd448 *p); int ed448_cmp(const PointEd448 *p1, const PointEd448 *p2); int ed448_neg(PointEd448 *p); int ed448_get_xy(uint8_t *xb, uint8_t *yb, size_t len, const PointEd448 *p); int ed448_double(PointEd448 *p); int ed448_add(PointEd448 *P1, const PointEd448 *P2); int ed448_scalar(PointEd448 *P, const uint8_t *scalar, size_t scalar_len, uint64_t seed); """) def lib_func(ecc_obj, func_name): if ecc_obj._curve.desc == "Ed25519": result = getattr(_ed25519_lib, "ed25519_" + func_name) elif ecc_obj._curve.desc == "Ed448": result = getattr(_ed448_lib, "ed448_" + func_name) else: result = getattr(_ec_lib, "ec_ws_" + func_name) return result # # _curves is a database of curve parameters. Items are indexed by their # human-friendly name, suchas "P-256". Each item has the following fields: # - p: the prime number that defines the finite field for all modulo operations # - b: the constant in the Short Weierstrass curve equation # - order: the number of elements in the group with the generator below # - Gx the affine coordinate X of the generator point # - Gy the affine coordinate Y of the generator point # - G the generator, as an EccPoint object # - modulus_bits the minimum number of bits for encoding the modulus p # - oid an ASCII string with the registered ASN.1 Object ID # - context a raw pointer to memory holding a context for all curve operations (can be NULL) # - desc an ASCII string describing the curve # - openssh the ASCII string used in OpenSSH id files for public keys on this curve # - name the ASCII string which is also a valid key in _curves _Curve = namedtuple("_Curve", "p b order Gx Gy G modulus_bits oid context desc openssh name") _curves = {} p192_names = ["p192", "NIST P-192", "P-192", "prime192v1", "secp192r1", "nistp192"] def init_p192(): p = 0xfffffffffffffffffffffffffffffffeffffffffffffffff b = 0x64210519e59c80e70fa7e9ab72243049feb8deecc146b9b1 order = 0xffffffffffffffffffffffff99def836146bc9b1b4d22831 Gx = 0x188da80eb03090f67cbf20eb43a18800f4ff0afd82ff1012 Gy = 0x07192b95ffc8da78631011ed6b24cdd573f977a11e794811 p192_modulus = long_to_bytes(p, 24) p192_b = long_to_bytes(b, 24) p192_order = long_to_bytes(order, 24) ec_p192_context = VoidPointer() result = _ec_lib.ec_ws_new_context(ec_p192_context.address_of(), c_uint8_ptr(p192_modulus), c_uint8_ptr(p192_b), c_uint8_ptr(p192_order), c_size_t(len(p192_modulus)), c_ulonglong(getrandbits(64)) ) if result: raise ImportError("Error %d initializing P-192 context" % result) context = SmartPointer(ec_p192_context.get(), _ec_lib.ec_free_context) p192 = _Curve(Integer(p), Integer(b), Integer(order), Integer(Gx), Integer(Gy), None, 192, "1.2.840.10045.3.1.1", # ANSI X9.62 / SEC2 context, "NIST P-192", "ecdsa-sha2-nistp192", "p192") global p192_names _curves.update(dict.fromkeys(p192_names, p192)) init_p192() del init_p192 p224_names = ["p224", "NIST P-224", "P-224", "prime224v1", "secp224r1", "nistp224"] def init_p224(): p = 0xffffffffffffffffffffffffffffffff000000000000000000000001 b = 0xb4050a850c04b3abf54132565044b0b7d7bfd8ba270b39432355ffb4 order = 0xffffffffffffffffffffffffffff16a2e0b8f03e13dd29455c5c2a3d Gx = 0xb70e0cbd6bb4bf7f321390b94a03c1d356c21122343280d6115c1d21 Gy = 0xbd376388b5f723fb4c22dfe6cd4375a05a07476444d5819985007e34 p224_modulus = long_to_bytes(p, 28) p224_b = long_to_bytes(b, 28) p224_order = long_to_bytes(order, 28) ec_p224_context = VoidPointer() result = _ec_lib.ec_ws_new_context(ec_p224_context.address_of(), c_uint8_ptr(p224_modulus), c_uint8_ptr(p224_b), c_uint8_ptr(p224_order), c_size_t(len(p224_modulus)), c_ulonglong(getrandbits(64)) ) if result: raise ImportError("Error %d initializing P-224 context" % result) context = SmartPointer(ec_p224_context.get(), _ec_lib.ec_free_context) p224 = _Curve(Integer(p), Integer(b), Integer(order), Integer(Gx), Integer(Gy), None, 224, "1.3.132.0.33", # SEC 2 context, "NIST P-224", "ecdsa-sha2-nistp224", "p224") global p224_names _curves.update(dict.fromkeys(p224_names, p224)) init_p224() del init_p224 p256_names = ["p256", "NIST P-256", "P-256", "prime256v1", "secp256r1", "nistp256"] def init_p256(): p = 0xffffffff00000001000000000000000000000000ffffffffffffffffffffffff b = 0x5ac635d8aa3a93e7b3ebbd55769886bc651d06b0cc53b0f63bce3c3e27d2604b order = 0xffffffff00000000ffffffffffffffffbce6faada7179e84f3b9cac2fc632551 Gx = 0x6b17d1f2e12c4247f8bce6e563a440f277037d812deb33a0f4a13945d898c296 Gy = 0x4fe342e2fe1a7f9b8ee7eb4a7c0f9e162bce33576b315ececbb6406837bf51f5 p256_modulus = long_to_bytes(p, 32) p256_b = long_to_bytes(b, 32) p256_order = long_to_bytes(order, 32) ec_p256_context = VoidPointer() result = _ec_lib.ec_ws_new_context(ec_p256_context.address_of(), c_uint8_ptr(p256_modulus), c_uint8_ptr(p256_b), c_uint8_ptr(p256_order), c_size_t(len(p256_modulus)), c_ulonglong(getrandbits(64)) ) if result: raise ImportError("Error %d initializing P-256 context" % result) context = SmartPointer(ec_p256_context.get(), _ec_lib.ec_free_context) p256 = _Curve(Integer(p), Integer(b), Integer(order), Integer(Gx), Integer(Gy), None, 256, "1.2.840.10045.3.1.7", # ANSI X9.62 / SEC2 context, "NIST P-256", "ecdsa-sha2-nistp256", "p256") global p256_names _curves.update(dict.fromkeys(p256_names, p256)) init_p256() del init_p256 p384_names = ["p384", "NIST P-384", "P-384", "prime384v1", "secp384r1", "nistp384"] def init_p384(): p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffff0000000000000000ffffffff b = 0xb3312fa7e23ee7e4988e056be3f82d19181d9c6efe8141120314088f5013875ac656398d8a2ed19d2a85c8edd3ec2aef order = 0xffffffffffffffffffffffffffffffffffffffffffffffffc7634d81f4372ddf581a0db248b0a77aecec196accc52973 Gx = 0xaa87ca22be8b05378eb1c71ef320ad746e1d3b628ba79b9859f741e082542a385502f25dbf55296c3a545e3872760aB7 Gy = 0x3617de4a96262c6f5d9e98bf9292dc29f8f41dbd289a147ce9da3113b5f0b8c00a60b1ce1d7e819d7a431d7c90ea0e5F p384_modulus = long_to_bytes(p, 48) p384_b = long_to_bytes(b, 48) p384_order = long_to_bytes(order, 48) ec_p384_context = VoidPointer() result = _ec_lib.ec_ws_new_context(ec_p384_context.address_of(), c_uint8_ptr(p384_modulus), c_uint8_ptr(p384_b), c_uint8_ptr(p384_order), c_size_t(len(p384_modulus)), c_ulonglong(getrandbits(64)) ) if result: raise ImportError("Error %d initializing P-384 context" % result) context = SmartPointer(ec_p384_context.get(), _ec_lib.ec_free_context) p384 = _Curve(Integer(p), Integer(b), Integer(order), Integer(Gx), Integer(Gy), None, 384, "1.3.132.0.34", # SEC 2 context, "NIST P-384", "ecdsa-sha2-nistp384", "p384") global p384_names _curves.update(dict.fromkeys(p384_names, p384)) init_p384() del init_p384 p521_names = ["p521", "NIST P-521", "P-521", "prime521v1", "secp521r1", "nistp521"] def init_p521(): p = 0x000001ffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffff b = 0x00000051953eb9618e1c9a1f929a21a0b68540eea2da725b99b315f3b8b489918ef109e156193951ec7e937b1652c0bd3bb1bf073573df883d2c34f1ef451fd46b503f00 order = 0x000001fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffa51868783bf2f966b7fcc0148f709a5d03bb5c9b8899c47aebb6fb71e91386409 Gx = 0x000000c6858e06b70404e9cd9e3ecb662395b4429c648139053fb521f828af606b4d3dbaa14b5e77efe75928fe1dc127a2ffa8de3348b3c1856a429bf97e7e31c2e5bd66 Gy = 0x0000011839296a789a3bc0045c8a5fb42c7d1bd998f54449579b446817afbd17273e662c97ee72995ef42640c550b9013fad0761353c7086a272c24088be94769fd16650 p521_modulus = long_to_bytes(p, 66) p521_b = long_to_bytes(b, 66) p521_order = long_to_bytes(order, 66) ec_p521_context = VoidPointer() result = _ec_lib.ec_ws_new_context(ec_p521_context.address_of(), c_uint8_ptr(p521_modulus), c_uint8_ptr(p521_b), c_uint8_ptr(p521_order), c_size_t(len(p521_modulus)), c_ulonglong(getrandbits(64)) ) if result: raise ImportError("Error %d initializing P-521 context" % result) context = SmartPointer(ec_p521_context.get(), _ec_lib.ec_free_context) p521 = _Curve(Integer(p), Integer(b), Integer(order), Integer(Gx), Integer(Gy), None, 521, "1.3.132.0.35", # SEC 2 context, "NIST P-521", "ecdsa-sha2-nistp521", "p521") global p521_names _curves.update(dict.fromkeys(p521_names, p521)) init_p521() del init_p521 ed25519_names = ["ed25519", "Ed25519"] def init_ed25519(): p = 0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed # 2**255 - 19 order = 0x1000000000000000000000000000000014def9dea2f79cd65812631a5cf5d3ed Gx = 0x216936d3cd6e53fec0a4e231fdd6dc5c692cc7609525a7b2c9562d608f25d51a Gy = 0x6666666666666666666666666666666666666666666666666666666666666658 ed25519 = _Curve(Integer(p), None, Integer(order), Integer(Gx), Integer(Gy), None, 255, "1.3.101.112", # RFC8410 None, "Ed25519", # Used throughout; do not change "ssh-ed25519", "ed25519") global ed25519_names _curves.update(dict.fromkeys(ed25519_names, ed25519)) init_ed25519() del init_ed25519 ed448_names = ["ed448", "Ed448"] def init_ed448(): p = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff # 2**448 - 2**224 - 1 order = 0x3fffffffffffffffffffffffffffffffffffffffffffffffffffffff7cca23e9c44edb49aed63690216cc2728dc58f552378c292ab5844f3 Gx = 0x4f1970c66bed0ded221d15a622bf36da9e146570470f1767ea6de324a3d3a46412ae1af72ab66511433b80e18b00938e2626a82bc70cc05e Gy = 0x693f46716eb6bc248876203756c9c7624bea73736ca3984087789c1e05a0c2d73ad3ff1ce67c39c4fdbd132c4ed7c8ad9808795bf230fa14 ed448_context = VoidPointer() result = _ed448_lib.ed448_new_context(ed448_context.address_of()) if result: raise ImportError("Error %d initializing Ed448 context" % result) context = SmartPointer(ed448_context.get(), _ed448_lib.ed448_free_context) ed448 = _Curve(Integer(p), None, Integer(order), Integer(Gx), Integer(Gy), None, 448, "1.3.101.113", # RFC8410 context, "Ed448", # Used throughout; do not change None, "ed448") global ed448_names _curves.update(dict.fromkeys(ed448_names, ed448)) init_ed448() del init_ed448 class UnsupportedEccFeature(ValueError): pass class EccPoint(object): """A class to model a point on an Elliptic Curve. The class supports operators for: * Adding two points: ``R = S + T`` * In-place addition: ``S += T`` * Negating a point: ``R = -T`` * Comparing two points: ``if S == T: ...`` or ``if S != T: ...`` * Multiplying a point by a scalar: ``R = S*k`` * In-place multiplication by a scalar: ``T *= k`` :ivar x: The affine X-coordinate of the ECC point :vartype x: integer :ivar y: The affine Y-coordinate of the ECC point :vartype y: integer :ivar xy: The tuple with affine X- and Y- coordinates """ def __init__(self, x, y, curve="p256"): try: self._curve = _curves[curve] except KeyError: raise ValueError("Unknown curve name %s" % str(curve)) self._curve_name = curve modulus_bytes = self.size_in_bytes() xb = long_to_bytes(x, modulus_bytes) yb = long_to_bytes(y, modulus_bytes) if len(xb) != modulus_bytes or len(yb) != modulus_bytes: raise ValueError("Incorrect coordinate length") new_point = lib_func(self, "new_point") free_func = lib_func(self, "free_point") self._point = VoidPointer() try: context = self._curve.context.get() except AttributeError: context = null_pointer result = new_point(self._point.address_of(), c_uint8_ptr(xb), c_uint8_ptr(yb), c_size_t(modulus_bytes), context) if result: if result == 15: raise ValueError("The EC point does not belong to the curve") raise ValueError("Error %d while instantiating an EC point" % result) # Ensure that object disposal of this Python object will (eventually) # free the memory allocated by the raw library for the EC point self._point = SmartPointer(self._point.get(), free_func) def set(self, point): clone = lib_func(self, "clone") free_func = lib_func(self, "free_point") self._point = VoidPointer() result = clone(self._point.address_of(), point._point.get()) if result: raise ValueError("Error %d while cloning an EC point" % result) self._point = SmartPointer(self._point.get(), free_func) return self def __eq__(self, point): if not isinstance(point, EccPoint): return False cmp_func = lib_func(self, "cmp") return 0 == cmp_func(self._point.get(), point._point.get()) # Only needed for Python 2 def __ne__(self, point): return not self == point def __neg__(self): neg_func = lib_func(self, "neg") np = self.copy() result = neg_func(np._point.get()) if result: raise ValueError("Error %d while inverting an EC point" % result) return np def copy(self): """Return a copy of this point.""" x, y = self.xy np = EccPoint(x, y, self._curve_name) return np def _is_eddsa(self): return self._curve.name in ("ed25519", "ed448") def is_point_at_infinity(self): """``True`` if this is the *point-at-infinity*.""" if self._is_eddsa(): return self.x == 0 else: return self.xy == (0, 0) def point_at_infinity(self): """Return the *point-at-infinity* for the curve.""" if self._is_eddsa(): return EccPoint(0, 1, self._curve_name) else: return EccPoint(0, 0, self._curve_name) @property def x(self): return self.xy[0] @property def y(self): return self.xy[1] @property def xy(self): modulus_bytes = self.size_in_bytes() xb = bytearray(modulus_bytes) yb = bytearray(modulus_bytes) get_xy = lib_func(self, "get_xy") result = get_xy(c_uint8_ptr(xb), c_uint8_ptr(yb), c_size_t(modulus_bytes), self._point.get()) if result: raise ValueError("Error %d while encoding an EC point" % result) return (Integer(bytes_to_long(xb)), Integer(bytes_to_long(yb))) def size_in_bytes(self): """Size of each coordinate, in bytes.""" return (self.size_in_bits() + 7) // 8 def size_in_bits(self): """Size of each coordinate, in bits.""" return self._curve.modulus_bits def double(self): """Double this point (in-place operation). Returns: This same object (to enable chaining). """ double_func = lib_func(self, "double") result = double_func(self._point.get()) if result: raise ValueError("Error %d while doubling an EC point" % result) return self def __iadd__(self, point): """Add a second point to this one""" add_func = lib_func(self, "add") result = add_func(self._point.get(), point._point.get()) if result: if result == 16: raise ValueError("EC points are not on the same curve") raise ValueError("Error %d while adding two EC points" % result) return self def __add__(self, point): """Return a new point, the addition of this one and another""" np = self.copy() np += point return np def __imul__(self, scalar): """Multiply this point by a scalar""" scalar_func = lib_func(self, "scalar") if scalar < 0: raise ValueError("Scalar multiplication is only defined for non-negative integers") sb = long_to_bytes(scalar) result = scalar_func(self._point.get(), c_uint8_ptr(sb), c_size_t(len(sb)), c_ulonglong(getrandbits(64))) if result: raise ValueError("Error %d during scalar multiplication" % result) return self def __mul__(self, scalar): """Return a new point, the scalar product of this one""" np = self.copy() np *= scalar return np def __rmul__(self, left_hand): return self.__mul__(left_hand) # Last piece of initialization p192_G = EccPoint(_curves['p192'].Gx, _curves['p192'].Gy, "p192") p192 = _curves['p192']._replace(G=p192_G) _curves.update(dict.fromkeys(p192_names, p192)) del p192_G, p192, p192_names p224_G = EccPoint(_curves['p224'].Gx, _curves['p224'].Gy, "p224") p224 = _curves['p224']._replace(G=p224_G) _curves.update(dict.fromkeys(p224_names, p224)) del p224_G, p224, p224_names p256_G = EccPoint(_curves['p256'].Gx, _curves['p256'].Gy, "p256") p256 = _curves['p256']._replace(G=p256_G) _curves.update(dict.fromkeys(p256_names, p256)) del p256_G, p256, p256_names p384_G = EccPoint(_curves['p384'].Gx, _curves['p384'].Gy, "p384") p384 = _curves['p384']._replace(G=p384_G) _curves.update(dict.fromkeys(p384_names, p384)) del p384_G, p384, p384_names p521_G = EccPoint(_curves['p521'].Gx, _curves['p521'].Gy, "p521") p521 = _curves['p521']._replace(G=p521_G) _curves.update(dict.fromkeys(p521_names, p521)) del p521_G, p521, p521_names ed25519_G = EccPoint(_curves['Ed25519'].Gx, _curves['Ed25519'].Gy, "Ed25519") ed25519 = _curves['Ed25519']._replace(G=ed25519_G) _curves.update(dict.fromkeys(ed25519_names, ed25519)) del ed25519_G, ed25519, ed25519_names ed448_G = EccPoint(_curves['Ed448'].Gx, _curves['Ed448'].Gy, "Ed448") ed448 = _curves['Ed448']._replace(G=ed448_G) _curves.update(dict.fromkeys(ed448_names, ed448)) del ed448_G, ed448, ed448_names class EccKey(object): r"""Class defining an ECC key. Do not instantiate directly. Use :func:`generate`, :func:`construct` or :func:`import_key` instead. :ivar curve: The name of the curve as defined in the `ECC table`_. :vartype curve: string :ivar pointQ: an ECC point representating the public component. :vartype pointQ: :class:`EccPoint` :ivar d: A scalar that represents the private component in NIST P curves. It is smaller than the order of the generator point. :vartype d: integer :ivar seed: A seed that representats the private component in EdDSA curves (Ed25519, 32 bytes; Ed448, 57 bytes). :vartype seed: bytes """ def __init__(self, **kwargs): """Create a new ECC key Keywords: curve : string The name of the curve. d : integer Mandatory for a private key one NIST P curves. It must be in the range ``[1..order-1]``. seed : bytes Mandatory for a private key on the Ed25519 (32 bytes) or Ed448 (57 bytes) curve. point : EccPoint Mandatory for a public key. If provided for a private key, the implementation will NOT check whether it matches ``d``. Only one parameter among ``d``, ``seed`` or ``point`` may be used. """ kwargs_ = dict(kwargs) curve_name = kwargs_.pop("curve", None) self._d = kwargs_.pop("d", None) self._seed = kwargs_.pop("seed", None) self._point = kwargs_.pop("point", None) if curve_name is None and self._point: curve_name = self._point._curve_name if kwargs_: raise TypeError("Unknown parameters: " + str(kwargs_)) if curve_name not in _curves: raise ValueError("Unsupported curve (%s)" % curve_name) self._curve = _curves[curve_name] self.curve = self._curve.desc count = int(self._d is not None) + int(self._seed is not None) if count == 0: if self._point is None: raise ValueError("At lest one between parameters 'point', 'd' or 'seed' must be specified") return if count == 2: raise ValueError("Parameters d and seed are mutually exclusive") # NIST P curves work with d, EdDSA works with seed if not self._is_eddsa(): if self._seed is not None: raise ValueError("Parameter 'seed' can only be used with Ed25519 or Ed448") self._d = Integer(self._d) if not 1 <= self._d < self._curve.order: raise ValueError("Parameter d must be an integer smaller than the curve order") else: if self._d is not None: raise ValueError("Parameter d can only be used with NIST P curves") # RFC 8032, 5.1.5 if self._curve.name == "ed25519": if len(self._seed) != 32: raise ValueError("Parameter seed must be 32 bytes long for Ed25519") seed_hash = SHA512.new(self._seed).digest() # h self._prefix = seed_hash[32:] tmp = bytearray(seed_hash[:32]) tmp[0] &= 0xF8 tmp[31] = (tmp[31] & 0x7F) | 0x40 # RFC 8032, 5.2.5 elif self._curve.name == "ed448": if len(self._seed) != 57: raise ValueError("Parameter seed must be 57 bytes long for Ed448") seed_hash = SHAKE256.new(self._seed).read(114) # h self._prefix = seed_hash[57:] tmp = bytearray(seed_hash[:57]) tmp[0] &= 0xFC tmp[55] |= 0x80 tmp[56] = 0 self._d = Integer.from_bytes(tmp, byteorder='little') def _is_eddsa(self): return self._curve.desc in ("Ed25519", "Ed448") def __eq__(self, other): if not isinstance(other, EccKey): return False if other.has_private() != self.has_private(): return False return other.pointQ == self.pointQ def __repr__(self): if self.has_private(): if self._is_eddsa(): extra = ", seed=%s" % tostr(binascii.hexlify(self._seed)) else: extra = ", d=%d" % int(self._d) else: extra = "" x, y = self.pointQ.xy return "EccKey(curve='%s', point_x=%d, point_y=%d%s)" % (self._curve.desc, x, y, extra) def has_private(self): """``True`` if this key can be used for making signatures or decrypting data.""" return self._d is not None # ECDSA def _sign(self, z, k): assert 0 < k < self._curve.order order = self._curve.order blind = Integer.random_range(min_inclusive=1, max_exclusive=order) blind_d = self._d * blind inv_blind_k = (blind * k).inverse(order) r = (self._curve.G * k).x % order s = inv_blind_k * (blind * z + blind_d * r) % order return (r, s) # ECDSA def _verify(self, z, rs): order = self._curve.order sinv = rs[1].inverse(order) point1 = self._curve.G * ((sinv * z) % order) point2 = self.pointQ * ((sinv * rs[0]) % order) return (point1 + point2).x == rs[0] @property def d(self): if not self.has_private(): raise ValueError("This is not a private ECC key") return self._d @property def seed(self): if not self.has_private(): raise ValueError("This is not a private ECC key") return self._seed @property def pointQ(self): if self._point is None: self._point = self._curve.G * self._d return self._point def public_key(self): """A matching ECC public key. Returns: a new :class:`EccKey` object """ return EccKey(curve=self._curve.desc, point=self.pointQ) def _export_SEC1(self, compress): if self._is_eddsa(): raise ValueError("SEC1 format is unsupported for EdDSA curves") # See 2.2 in RFC5480 and 2.3.3 in SEC1 # # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. modulus_bytes = self.pointQ.size_in_bytes() if compress: if self.pointQ.y.is_odd(): first_byte = b'\x03' else: first_byte = b'\x02' public_key = (first_byte + self.pointQ.x.to_bytes(modulus_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(modulus_bytes) + self.pointQ.y.to_bytes(modulus_bytes)) return public_key def _export_eddsa(self): x, y = self.pointQ.xy if self._curve.name == "ed25519": result = bytearray(y.to_bytes(32, byteorder='little')) result[31] = ((x & 1) << 7) | result[31] elif self._curve.name == "ed448": result = bytearray(y.to_bytes(57, byteorder='little')) result[56] = (x & 1) << 7 else: raise ValueError("Not an EdDSA key to export") return bytes(result) def _export_subjectPublicKeyInfo(self, compress): if self._is_eddsa(): oid = self._curve.oid public_key = self._export_eddsa() params = None else: oid = "1.2.840.10045.2.1" # unrestricted public_key = self._export_SEC1(compress) params = DerObjectId(self._curve.oid) return _create_subject_public_key_info(oid, public_key, params) def _export_rfc5915_private_der(self, include_ec_params=True): assert self.has_private() # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } # Public key - uncompressed form modulus_bytes = self.pointQ.size_in_bytes() public_key = (b'\x04' + self.pointQ.x.to_bytes(modulus_bytes) + self.pointQ.y.to_bytes(modulus_bytes)) seq = [1, DerOctetString(self.d.to_bytes(modulus_bytes)), DerObjectId(self._curve.oid, explicit=0), DerBitString(public_key, explicit=1)] if not include_ec_params: del seq[2] return DerSequence(seq).encode() def _export_pkcs8(self, **kwargs): from Cryptodome.IO import PKCS8 if kwargs.get('passphrase', None) is not None and 'protection' not in kwargs: raise ValueError("At least the 'protection' parameter should be present") if self._is_eddsa(): oid = self._curve.oid private_key = DerOctetString(self._seed).encode() params = None else: oid = "1.2.840.10045.2.1" # unrestricted private_key = self._export_rfc5915_private_der(include_ec_params=False) params = DerObjectId(self._curve.oid) result = PKCS8.wrap(private_key, oid, key_params=params, **kwargs) return result def _export_public_pem(self, compress): from Cryptodome.IO import PEM encoded_der = self._export_subjectPublicKeyInfo(compress) return PEM.encode(encoded_der, "PUBLIC KEY") def _export_private_pem(self, passphrase, **kwargs): from Cryptodome.IO import PEM encoded_der = self._export_rfc5915_private_der() return PEM.encode(encoded_der, "EC PRIVATE KEY", passphrase, **kwargs) def _export_private_clear_pkcs8_in_clear_pem(self): from Cryptodome.IO import PEM encoded_der = self._export_pkcs8() return PEM.encode(encoded_der, "PRIVATE KEY") def _export_private_encrypted_pkcs8_in_clear_pem(self, passphrase, **kwargs): from Cryptodome.IO import PEM assert passphrase if 'protection' not in kwargs: raise ValueError("At least the 'protection' parameter should be present") encoded_der = self._export_pkcs8(passphrase=passphrase, **kwargs) return PEM.encode(encoded_der, "ENCRYPTED PRIVATE KEY") def _export_openssh(self, compress): if self.has_private(): raise ValueError("Cannot export OpenSSH private keys") desc = self._curve.openssh if desc is None: raise ValueError("Cannot export %s keys as OpenSSH" % self._curve.name) elif desc == "ssh-ed25519": public_key = self._export_eddsa() comps = (tobytes(desc), tobytes(public_key)) else: modulus_bytes = self.pointQ.size_in_bytes() if compress: first_byte = 2 + self.pointQ.y.is_odd() public_key = (bchr(first_byte) + self.pointQ.x.to_bytes(modulus_bytes)) else: public_key = (b'\x04' + self.pointQ.x.to_bytes(modulus_bytes) + self.pointQ.y.to_bytes(modulus_bytes)) middle = desc.split("-")[2] comps = (tobytes(desc), tobytes(middle), public_key) blob = b"".join([struct.pack(">I", len(x)) + x for x in comps]) return desc + " " + tostr(binascii.b2a_base64(blob)) def export_key(self, **kwargs): """Export this ECC key. Args: format (string): The format to use for encoding the key: - ``'DER'``. The key will be encoded in ASN.1 DER format (binary). For a public key, the ASN.1 ``subjectPublicKeyInfo`` structure defined in `RFC5480`_ will be used. For a private key, the ASN.1 ``ECPrivateKey`` structure defined in `RFC5915`_ is used instead (possibly within a PKCS#8 envelope, see the ``use_pkcs8`` flag below). - ``'PEM'``. The key will be encoded in a PEM_ envelope (ASCII). - ``'OpenSSH'``. The key will be encoded in the OpenSSH_ format (ASCII, public keys only). - ``'SEC1'``. The public key (i.e., the EC point) will be encoded into ``bytes`` according to Section 2.3.3 of `SEC1`_ (which is a subset of the older X9.62 ITU standard). Only for NIST P-curves. - ``'raw'``. The public key will be encoded as ``bytes``, without any metadata. * For NIST P-curves: equivalent to ``'SEC1'``. * For EdDSA curves: ``bytes`` in the format defined in `RFC8032`_. passphrase (byte string or string): The passphrase to use for protecting the private key. use_pkcs8 (boolean): Only relevant for private keys. If ``True`` (default and recommended), the `PKCS#8`_ representation will be used. It must be ``True`` for EdDSA curves. protection (string): When a private key is exported with password-protection and PKCS#8 (both ``DER`` and ``PEM`` formats), this parameter MUST be present and be a valid algorithm supported by :mod:`Cryptodome.IO.PKCS8`. It is recommended to use ``PBKDF2WithHMAC-SHA1AndAES128-CBC``. compress (boolean): If ``True``, the method returns a more compact representation of the public key, with the X-coordinate only. If ``False`` (default), the method returns the full public key. This parameter is ignored for EdDSA curves, as compression is mandatory. .. warning:: If you don't provide a passphrase, the private key will be exported in the clear! .. note:: When exporting a private key with password-protection and `PKCS#8`_ (both ``DER`` and ``PEM`` formats), any extra parameters to ``export_key()`` will be passed to :mod:`Cryptodome.IO.PKCS8`. .. _PEM: http://www.ietf.org/rfc/rfc1421.txt .. _`PEM encryption`: http://www.ietf.org/rfc/rfc1423.txt .. _OpenSSH: http://www.openssh.com/txt/rfc5656.txt .. _RFC5480: https://tools.ietf.org/html/rfc5480 .. _SEC1: https://www.secg.org/sec1-v2.pdf Returns: A multi-line string (for ``'PEM'`` and ``'OpenSSH'``) or ``bytes`` (for ``'DER'``, ``'SEC1'``, and ``'raw'``) with the encoded key. """ args = kwargs.copy() ext_format = args.pop("format") if ext_format not in ("PEM", "DER", "OpenSSH", "SEC1", "raw"): raise ValueError("Unknown format '%s'" % ext_format) compress = args.pop("compress", False) if self.has_private(): passphrase = args.pop("passphrase", None) if is_string(passphrase): passphrase = tobytes(passphrase) if not passphrase: raise ValueError("Empty passphrase") use_pkcs8 = args.pop("use_pkcs8", True) if not use_pkcs8 and self._is_eddsa(): raise ValueError("'pkcs8' must be True for EdDSA curves") if ext_format == "PEM": if use_pkcs8: if passphrase: return self._export_private_encrypted_pkcs8_in_clear_pem(passphrase, **args) else: return self._export_private_clear_pkcs8_in_clear_pem() else: return self._export_private_pem(passphrase, **args) elif ext_format == "DER": # DER if passphrase and not use_pkcs8: raise ValueError("Private keys can only be encrpyted with DER using PKCS#8") if use_pkcs8: return self._export_pkcs8(passphrase=passphrase, **args) else: return self._export_rfc5915_private_der() else: raise ValueError("Private keys cannot be exported " "in the '%s' format" % ext_format) else: # Public key if args: raise ValueError("Unexpected parameters: '%s'" % args) if ext_format == "PEM": return self._export_public_pem(compress) elif ext_format == "DER": return self._export_subjectPublicKeyInfo(compress) elif ext_format == "SEC1": return self._export_SEC1(compress) elif ext_format == "raw": if self._curve.name in ('ed25519', 'ed448'): return self._export_eddsa() else: return self._export_SEC1(compress) else: return self._export_openssh(compress) def generate(**kwargs): """Generate a new private key on the given curve. Args: curve (string): Mandatory. It must be a curve name defined in the `ECC table`_. randfunc (callable): Optional. The RNG to read randomness from. If ``None``, :func:`Cryptodome.Random.get_random_bytes` is used. """ curve_name = kwargs.pop("curve") curve = _curves[curve_name] randfunc = kwargs.pop("randfunc", get_random_bytes) if kwargs: raise TypeError("Unknown parameters: " + str(kwargs)) if _curves[curve_name].name == "ed25519": seed = randfunc(32) new_key = EccKey(curve=curve_name, seed=seed) elif _curves[curve_name].name == "ed448": seed = randfunc(57) new_key = EccKey(curve=curve_name, seed=seed) else: d = Integer.random_range(min_inclusive=1, max_exclusive=curve.order, randfunc=randfunc) new_key = EccKey(curve=curve_name, d=d) return new_key def construct(**kwargs): """Build a new ECC key (private or public) starting from some base components. In most cases, you will already have an existing key which you can read in with :func:`import_key` instead of this function. Args: curve (string): Mandatory. The name of the elliptic curve, as defined in the `ECC table`_. d (integer): Mandatory for a private key and a NIST P-curve (e.g., P-256): the integer in the range ``[1..order-1]`` that represents the key. seed (bytes): Mandatory for a private key and an EdDSA curve. It must be 32 bytes for Ed25519, and 57 bytes for Ed448. point_x (integer): Mandatory for a public key: the X coordinate (affine) of the ECC point. point_y (integer): Mandatory for a public key: the Y coordinate (affine) of the ECC point. Returns: :class:`EccKey` : a new ECC key object """ curve_name = kwargs["curve"] curve = _curves[curve_name] point_x = kwargs.pop("point_x", None) point_y = kwargs.pop("point_y", None) if "point" in kwargs: raise TypeError("Unknown keyword: point") if None not in (point_x, point_y): # ValueError is raised if the point is not on the curve kwargs["point"] = EccPoint(point_x, point_y, curve_name) new_key = EccKey(**kwargs) # Validate that the private key matches the public one # because EccKey will not do that automatically if new_key.has_private() and 'point' in kwargs: pub_key = curve.G * new_key.d if pub_key.xy != (point_x, point_y): raise ValueError("Private and public ECC keys do not match") return new_key def _import_public_der(ec_point, curve_oid=None, curve_name=None): """Convert an encoded EC point into an EccKey object ec_point: byte string with the EC point (SEC1-encoded) curve_oid: string with the name the curve curve_name: string with the OID of the curve Either curve_id or curve_name must be specified """ for _curve_name, curve in _curves.items(): if curve_oid and curve.oid == curve_oid: break if curve_name == _curve_name: break else: if curve_oid: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) else: raise UnsupportedEccFeature("Unsupported ECC curve (%s)" % curve_name) # See 2.2 in RFC5480 and 2.3.3 in SEC1 # The first byte is: # - 0x02: compressed, only X-coordinate, Y-coordinate is even # - 0x03: compressed, only X-coordinate, Y-coordinate is odd # - 0x04: uncompressed, X-coordinate is followed by Y-coordinate # # PAI is in theory encoded as 0x00. modulus_bytes = curve.p.size_in_bytes() point_type = bord(ec_point[0]) # Uncompressed point if point_type == 0x04: if len(ec_point) != (1 + 2 * modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:modulus_bytes+1]) y = Integer.from_bytes(ec_point[modulus_bytes+1:]) # Compressed point elif point_type in (0x02, 0x03): if len(ec_point) != (1 + modulus_bytes): raise ValueError("Incorrect EC point length") x = Integer.from_bytes(ec_point[1:]) # Right now, we only support Short Weierstrass curves y = (x**3 - x*3 + curve.b).sqrt(curve.p) if point_type == 0x02 and y.is_odd(): y = curve.p - y if point_type == 0x03 and y.is_even(): y = curve.p - y else: raise ValueError("Incorrect EC point encoding") return construct(curve=_curve_name, point_x=x, point_y=y) def _import_subjectPublicKeyInfo(encoded, *kwargs): """Convert a subjectPublicKeyInfo into an EccKey object""" # See RFC5480 # Parse the generic subjectPublicKeyInfo structure oid, ec_point, params = _expand_subject_public_key_info(encoded) nist_p_oids = ( "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) "1.3.132.1.12", # id-ecDH "1.3.132.1.13" # id-ecMQV ) eddsa_oids = { "1.3.101.112": ("Ed25519", _import_ed25519_public_key), # id-Ed25519 "1.3.101.113": ("Ed448", _import_ed448_public_key) # id-Ed448 } if oid in nist_p_oids: # See RFC5480 # Parameters are mandatory and encoded as ECParameters # ECParameters ::= CHOICE { # namedCurve OBJECT IDENTIFIER # -- implicitCurve NULL # -- specifiedCurve SpecifiedECDomain # } # implicitCurve and specifiedCurve are not supported (as per RFC) if not params: raise ValueError("Missing ECC parameters for ECC OID %s" % oid) try: curve_oid = DerObjectId().decode(params).value except ValueError: raise ValueError("Error decoding namedCurve") # ECPoint ::= OCTET STRING return _import_public_der(ec_point, curve_oid=curve_oid) elif oid in eddsa_oids: # See RFC8410 curve_name, import_eddsa_public_key = eddsa_oids[oid] # Parameters must be absent if params: raise ValueError("Unexpected ECC parameters for ECC OID %s" % oid) x, y = import_eddsa_public_key(ec_point) return construct(point_x=x, point_y=y, curve=curve_name) else: raise UnsupportedEccFeature("Unsupported ECC OID: %s" % oid) def _import_rfc5915_der(encoded, passphrase, curve_oid=None): # See RFC5915 https://tools.ietf.org/html/rfc5915 # # ECPrivateKey ::= SEQUENCE { # version INTEGER { ecPrivkeyVer1(1) } (ecPrivkeyVer1), # privateKey OCTET STRING, # parameters [0] ECParameters {{ NamedCurve }} OPTIONAL, # publicKey [1] BIT STRING OPTIONAL # } private_key = DerSequence().decode(encoded, nr_elements=(3, 4)) if private_key[0] != 1: raise ValueError("Incorrect ECC private key version") try: parameters = DerObjectId(explicit=0).decode(private_key[2]).value if curve_oid is not None and parameters != curve_oid: raise ValueError("Curve mismatch") curve_oid = parameters except ValueError: pass if curve_oid is None: raise ValueError("No curve found") for curve_name, curve in _curves.items(): if curve.oid == curve_oid: break else: raise UnsupportedEccFeature("Unsupported ECC curve (OID: %s)" % curve_oid) scalar_bytes = DerOctetString().decode(private_key[1]).payload modulus_bytes = curve.p.size_in_bytes() if len(scalar_bytes) != modulus_bytes: raise ValueError("Private key is too small") d = Integer.from_bytes(scalar_bytes) # Decode public key (if any) if len(private_key) > 2: public_key_enc = DerBitString(explicit=1).decode(private_key[-1]).value public_key = _import_public_der(public_key_enc, curve_oid=curve_oid) point_x = public_key.pointQ.x point_y = public_key.pointQ.y else: point_x = point_y = None return construct(curve=curve_name, d=d, point_x=point_x, point_y=point_y) def _import_pkcs8(encoded, passphrase): from Cryptodome.IO import PKCS8 algo_oid, private_key, params = PKCS8.unwrap(encoded, passphrase) nist_p_oids = ( "1.2.840.10045.2.1", # id-ecPublicKey (unrestricted) "1.3.132.1.12", # id-ecDH "1.3.132.1.13" # id-ecMQV ) eddsa_oids = { "1.3.101.112": "Ed25519", # id-Ed25519 "1.3.101.113": "Ed448", # id-Ed448 } if algo_oid in nist_p_oids: curve_oid = DerObjectId().decode(params).value return _import_rfc5915_der(private_key, passphrase, curve_oid) elif algo_oid in eddsa_oids: if params is not None: raise ValueError("EdDSA ECC private key must not have parameters") curve_oid = None seed = DerOctetString().decode(private_key).payload return construct(curve=eddsa_oids[algo_oid], seed=seed) else: raise UnsupportedEccFeature("Unsupported ECC purpose (OID: %s)" % algo_oid) def _import_x509_cert(encoded, *kwargs): sp_info = _extract_subject_public_key_info(encoded) return _import_subjectPublicKeyInfo(sp_info) def _import_der(encoded, passphrase): try: return _import_subjectPublicKeyInfo(encoded, passphrase) except UnsupportedEccFeature as err: raise err except (ValueError, TypeError, IndexError): pass try: return _import_x509_cert(encoded, passphrase) except UnsupportedEccFeature as err: raise err except (ValueError, TypeError, IndexError): pass try: return _import_rfc5915_der(encoded, passphrase) except UnsupportedEccFeature as err: raise err except (ValueError, TypeError, IndexError): pass try: return _import_pkcs8(encoded, passphrase) except UnsupportedEccFeature as err: raise err except (ValueError, TypeError, IndexError): pass raise ValueError("Not an ECC DER key") def _import_openssh_public(encoded): parts = encoded.split(b' ') if len(parts) not in (2, 3): raise ValueError("Not an openssh public key") try: keystring = binascii.a2b_base64(parts[1]) keyparts = [] while len(keystring) > 4: lk = struct.unpack(">I", keystring[:4])[0] keyparts.append(keystring[4:4 + lk]) keystring = keystring[4 + lk:] if parts[0] != keyparts[0]: raise ValueError("Mismatch in openssh public key") # NIST P curves if parts[0].startswith(b"ecdsa-sha2-"): for curve_name, curve in _curves.items(): if curve.openssh is None: continue if not curve.openssh.startswith("ecdsa-sha2"): continue middle = tobytes(curve.openssh.split("-")[2]) if keyparts[1] == middle: break else: raise ValueError("Unsupported ECC curve: " + middle) ecc_key = _import_public_der(keyparts[2], curve_oid=curve.oid) # EdDSA elif parts[0] == b"ssh-ed25519": x, y = _import_ed25519_public_key(keyparts[1]) ecc_key = construct(curve="Ed25519", point_x=x, point_y=y) else: raise ValueError("Unsupported SSH key type: " + parts[0]) except (IndexError, TypeError, binascii.Error): raise ValueError("Error parsing SSH key type: " + parts[0]) return ecc_key def _import_openssh_private_ecc(data, password): from ._openssh import (import_openssh_private_generic, read_bytes, read_string, check_padding) key_type, decrypted = import_openssh_private_generic(data, password) eddsa_keys = { "ssh-ed25519": ("Ed25519", _import_ed25519_public_key, 32), } # https://datatracker.ietf.org/doc/html/draft-miller-ssh-agent-04 if key_type.startswith("ecdsa-sha2"): ecdsa_curve_name, decrypted = read_string(decrypted) if ecdsa_curve_name not in _curves: raise UnsupportedEccFeature("Unsupported ECC curve %s" % ecdsa_curve_name) curve = _curves[ecdsa_curve_name] modulus_bytes = (curve.modulus_bits + 7) // 8 public_key, decrypted = read_bytes(decrypted) if bord(public_key[0]) != 4: raise ValueError("Only uncompressed OpenSSH EC keys are supported") if len(public_key) != 2 * modulus_bytes + 1: raise ValueError("Incorrect public key length") point_x = Integer.from_bytes(public_key[1:1+modulus_bytes]) point_y = Integer.from_bytes(public_key[1+modulus_bytes:]) private_key, decrypted = read_bytes(decrypted) d = Integer.from_bytes(private_key) params = {'d': d, 'curve': ecdsa_curve_name} elif key_type in eddsa_keys: curve_name, import_eddsa_public_key, seed_len = eddsa_keys[key_type] public_key, decrypted = read_bytes(decrypted) point_x, point_y = import_eddsa_public_key(public_key) private_public_key, decrypted = read_bytes(decrypted) seed = private_public_key[:seed_len] params = {'seed': seed, 'curve': curve_name} else: raise ValueError("Unsupport SSH agent key type:" + key_type) _, padded = read_string(decrypted) # Comment check_padding(padded) return construct(point_x=point_x, point_y=point_y, **params) def _import_ed25519_public_key(encoded): """Import an Ed25519 ECC public key, encoded as raw bytes as described in RFC8032_. Args: encoded (bytes): The Ed25519 public key to import. It must be 32 bytes long. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed. .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 """ if len(encoded) != 32: raise ValueError("Incorrect length. Only Ed25519 public keys are supported.") p = Integer(0x7fffffffffffffffffffffffffffffffffffffffffffffffffffffffffffffed) # 2**255 - 19 d = 37095705934669439343138083508754565189542113879843219016388785533085940283555 y = bytearray(encoded) x_lsb = y[31] >> 7 y[31] &= 0x7F point_y = Integer.from_bytes(y, byteorder='little') if point_y >= p: raise ValueError("Invalid Ed25519 key (y)") if point_y == 1: return 0, 1 u = (point_y**2 - 1) % p v = ((point_y**2 % p) * d + 1) % p try: v_inv = v.inverse(p) x2 = (u * v_inv) % p point_x = Integer._tonelli_shanks(x2, p) if (point_x & 1) != x_lsb: point_x = p - point_x except ValueError: raise ValueError("Invalid Ed25519 public key") return point_x, point_y def _import_ed448_public_key(encoded): """Import an Ed448 ECC public key, encoded as raw bytes as described in RFC8032_. Args: encoded (bytes): The Ed448 public key to import. It must be 57 bytes long. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed. .. _RFC8032: https://datatracker.ietf.org/doc/html/rfc8032 """ if len(encoded) != 57: raise ValueError("Incorrect length. Only Ed448 public keys are supported.") p = Integer(0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffffffff) # 2**448 - 2**224 - 1 d = 0xfffffffffffffffffffffffffffffffffffffffffffffffffffffffeffffffffffffffffffffffffffffffffffffffffffffffffffff6756 y = encoded[:56] x_lsb = bord(encoded[56]) >> 7 point_y = Integer.from_bytes(y, byteorder='little') if point_y >= p: raise ValueError("Invalid Ed448 key (y)") if point_y == 1: return 0, 1 u = (point_y**2 - 1) % p v = ((point_y**2 % p) * d - 1) % p try: v_inv = v.inverse(p) x2 = (u * v_inv) % p point_x = Integer._tonelli_shanks(x2, p) if (point_x & 1) != x_lsb: point_x = p - point_x except ValueError: raise ValueError("Invalid Ed448 public key") return point_x, point_y def import_key(encoded, passphrase=None, curve_name=None): """Import an ECC key (public or private). Args: encoded (bytes or multi-line string): The ECC key to import. The function will try to automatically detect the right format. Supported formats for an ECC **public** key: * X.509 certificate: binary (DER) or ASCII (PEM). * X.509 ``subjectPublicKeyInfo``: binary (DER) or ASCII (PEM). * SEC1_ (or X9.62), as ``bytes``. NIST P curves only. You must also provide the ``curve_name`` (with a value from the `ECC table`_) * OpenSSH line, defined in RFC5656_ and RFC8709_ (ASCII). This is normally the content of files like ``~/.ssh/id_ecdsa.pub``. Supported formats for an ECC **private** key: * A binary ``ECPrivateKey`` structure, as defined in `RFC5915`_ (DER). NIST P curves only. * A `PKCS#8`_ structure (or the more recent Asymmetric Key Package, RFC5958_): binary (DER) or ASCII (PEM). * `OpenSSH 6.5`_ and newer versions (ASCII). Private keys can be in the clear or password-protected. For details about the PEM encoding, see `RFC1421`_/`RFC1423`_. passphrase (byte string): The passphrase to use for decrypting a private key. Encryption may be applied protected at the PEM level (not recommended) or at the PKCS#8 level (recommended). This parameter is ignored if the key in input is not encrypted. curve_name (string): For a SEC1 encoding only. This is the name of the curve, as defined in the `ECC table`_. .. note:: To import EdDSA private and public keys, when encoded as raw ``bytes``, use: * :func:`Cryptodome.Signature.eddsa.import_public_key`, or * :func:`Cryptodome.Signature.eddsa.import_private_key`. Returns: :class:`EccKey` : a new ECC key object Raises: ValueError: when the given key cannot be parsed (possibly because the pass phrase is wrong). .. _RFC1421: https://datatracker.ietf.org/doc/html/rfc1421 .. _RFC1423: https://datatracker.ietf.org/doc/html/rfc1423 .. _RFC5915: https://datatracker.ietf.org/doc/html/rfc5915 .. _RFC5656: https://datatracker.ietf.org/doc/html/rfc5656 .. _RFC8709: https://datatracker.ietf.org/doc/html/rfc8709 .. _RFC5958: https://datatracker.ietf.org/doc/html/rfc5958 .. _`PKCS#8`: https://datatracker.ietf.org/doc/html/rfc5208 .. _`OpenSSH 6.5`: https://flak.tedunangst.com/post/new-openssh-key-format-and-bcrypt-pbkdf .. _SEC1: https://www.secg.org/sec1-v2.pdf """ from Cryptodome.IO import PEM encoded = tobytes(encoded) if passphrase is not None: passphrase = tobytes(passphrase) # PEM if encoded.startswith(b'-----BEGIN OPENSSH PRIVATE KEY'): text_encoded = tostr(encoded) openssh_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) result = _import_openssh_private_ecc(openssh_encoded, passphrase) return result elif encoded.startswith(b'-----'): text_encoded = tostr(encoded) # Remove any EC PARAMETERS section # Ignore its content because the curve type must be already given in the key ecparams_start = "-----BEGIN EC PARAMETERS-----" ecparams_end = "-----END EC PARAMETERS-----" text_encoded = re.sub(ecparams_start + ".*?" + ecparams_end, "", text_encoded, flags=re.DOTALL) der_encoded, marker, enc_flag = PEM.decode(text_encoded, passphrase) if enc_flag: passphrase = None try: result = _import_der(der_encoded, passphrase) except UnsupportedEccFeature as uef: raise uef except ValueError: raise ValueError("Invalid DER encoding inside the PEM file") return result # OpenSSH if encoded.startswith((b'ecdsa-sha2-', b'ssh-ed25519')): return _import_openssh_public(encoded) # DER if len(encoded) > 0 and bord(encoded[0]) == 0x30: return _import_der(encoded, passphrase) # SEC1 if len(encoded) > 0 and bord(encoded[0]) in (0x02, 0x03, 0x04): if curve_name is None: raise ValueError("No curve name was provided") return _import_public_der(encoded, curve_name=curve_name) raise ValueError("ECC key format is not supported") if __name__ == "__main__": import time d = 0xc51e4753afdec1e6b6c6a5b992f43f8dd0c7a8933072708b6522468b2ffb06fd point = _curves['p256'].G.copy() count = 3000 start = time.time() for x in range(count): pointX = point * d print("(P-256 G)", (time.time() - start) / count * 1000, "ms") start = time.time() for x in range(count): pointX = pointX * d print("(P-256 arbitrary point)", (time.time() - start) / count * 1000, "ms")