From e63d112cb235daacce4fdc9432ad219eaaa10872 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 12:50:52 +0300 Subject: [PATCH 01/68] Update to neofs-api v0.7.2 --- Makefile | 2 +- object/service.pb.go | Bin 132963 -> 128417 bytes object/service.proto | 11 +------ object/types.pb.go | Bin 85366 -> 85297 bytes object/types.proto | 6 ++-- service/meta.pb.go | Bin 15017 -> 15863 bytes service/meta.proto | 2 ++ service/verify.pb.go | Bin 22022 -> 35203 bytes service/verify.proto | 73 ++++++++++++++++++++++++++++++++++++------ session/service.pb.go | Bin 27408 -> 27455 bytes session/service.proto | 9 +++--- session/types.pb.go | Bin 22959 -> 0 bytes session/types.proto | 35 -------------------- 13 files changed, 74 insertions(+), 64 deletions(-) delete mode 100644 session/types.pb.go delete mode 100644 session/types.proto diff --git a/Makefile b/Makefile index 29f41bc..9773c9b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PROTO_VERSION=v0.7.1 +PROTO_VERSION=v0.7.2 PROTO_URL=https://github.com/nspcc-dev/neofs-api/archive/$(PROTO_VERSION).tar.gz B=\033[0;1m diff --git a/object/service.pb.go b/object/service.pb.go index 4ac61bc6736e4c9516d7e350928d031a2f340765..f882f5e1cb399150b11220b6daf2ed9852ce990e 100644 GIT binary patch delta 5594 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 diff --git a/object/service.proto b/object/service.proto index b5042e2..91d0b99 100644 --- a/object/service.proto +++ b/object/service.proto @@ -5,7 +5,6 @@ option csharp_namespace = "NeoFS.API.Object"; import "refs/types.proto"; import "object/types.proto"; -import "session/types.proto"; import "service/meta.proto"; import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -58,8 +57,6 @@ service Service { message GetRequest { // Address of object (container id + object id) refs.Address Address = 1 [(gogoproto.nullable) = false]; - // Raw is the request flag of a physically stored representation of an object - bool Raw = 2; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) @@ -82,10 +79,8 @@ message PutRequest { message PutHeader { // Object with at least container id and owner id fields Object Object = 1; - // Token with session public key and user's signature - session.Token Token = 2; // Number of the object copies to store within the RPC call (zero is processed according to the placement rules) - uint32 CopiesNumber = 3; + uint32 CopiesNumber = 2; } oneof R { @@ -112,8 +107,6 @@ message DeleteRequest { refs.Address Address = 1 [(gogoproto.nullable) = false]; // OwnerID is a wallet address bytes OwnerID = 2 [(gogoproto.nullable) = false, (gogoproto.customtype) = "OwnerID"]; - // Token with session public key and user's signature - session.Token Token = 3; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) @@ -132,8 +125,6 @@ message HeadRequest { refs.Address Address = 1 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; // FullHeaders can be set true for extended headers in the object bool FullHeaders = 2; - // Raw is the request flag of a physically stored representation of an object - bool Raw = 3; // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) diff --git a/object/types.pb.go b/object/types.pb.go index fe474596824b7610a1a35de1aec97117967d1a3a..a24a2f1a798a88c173bc4125b21db0228126a486 100644 GIT binary patch delta 5783 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 delta 5833 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 QWkkzevR3PNcdyO<0eJi<1^@s6 delta 709 zcmYLGu}T9$5G5hOL`4ZA7*q_Vu?V`ix4XB8y_KX-xp!_FYhfYaeZ>BNU}YyP*5Xe@ z5NyO>P+K!^1e>|pH*emXnfqBh+&sQ_;^*c`{O*|a+?{sQ^~RvrRzVNX72@vb8y2*_l5Dg?e zejJOgBUKDzO1JWWSq)lc_&*^0C6z4Uf= Gxb+Jo@we0f diff --git a/service/meta.proto b/service/meta.proto index 99b37d3..093f118 100644 --- a/service/meta.proto +++ b/service/meta.proto @@ -17,6 +17,8 @@ message RequestMetaHeader { // Version defines protocol version // TODO: not used for now, should be implemented in future uint32 Version = 3; + // Raw determines whether the request is raw or not + bool Raw = 4; } // ResponseMetaHeader contains meta information based on request processing by server diff --git a/service/verify.pb.go b/service/verify.pb.go index 9dca855603caf3abe20ccf15390182e233b18232..023e6392bb2ee316a7b3ddcc8c6726604981a694 100644 GIT binary patch literal 35203 zcmeHQZF3sOvi=OeVy>!^f<(d~A@mYERk?9;j_aN{sn|}X%H`{`u!FS6u*+pZmh<+MCu%#q+luBWko$2oB?&;oXTHv2Yc6bRr_s#DrvJuFrr9hT97x;2 zMRt+3%){@^&bYMO=qT2uMNjDvnL;W_1a6bcah94=l#|o8{bnr_b|RcAL18&NI(3$)OQr67iDkX za}i>iPX>d7*k0XBZ8ppgqDdmNxi_`LT%abI*-J9{GLB}KW=}uekC**qmb2xae%-4# z>pJHj?a)qbI=9kf^Xcfk^zEMEKqDcvBJxYfl;pt@}C~^}SVc8@Z*@Ia!wkFC& z4znanl_Hzwb3s14jArs7olD6MWrWT|8g+a$OZp>gUW*WvXwqc7@RFJK>)Rx~@YrG# zT|`Nm&!7Wexn=<)m@$9jagLJIno;t~j^3EDjndq_5si@nlQ$*!*2|y|U>`kwNy7)U2pESbFp_jbuqbFym=Bht>{)bX04Zl~@auto{o*0tx z0yGIRpyb@}zqWt`J-(r%4WP=k9BP9j4NXVd;HX1GR~twm#~c?8AoASwK<99t4ou^E znQu+=1bk{V4JkE3PGz9**9(Z|93?yhU7<@ zA2A8ZFd4u)DHrXOWeXov-wDTyft>*w{a|wh3-s@be@aLZnkLh){`N;oeyR@PXYI&qyt`Q19=m5QgJ|K zy%vmq{^GN>C|?%+%)j9=BtSUdQ?BRxo&Bku?CXkoQ1r_8(d%f>{3p-S^S$6fHcn=C zJej@O`#0!^e976F8hPiN3TL3MF-gtq%S3FSdKAY>9g^F0=B&C~1jRp}5w2OK_i#U( z%t$Z#ERo8#1uB33`RA99vuEim`^Luq67r|fNEKX!@aym9-<;xs&PRLB)2SU~7isc~ z@LK<)ddipOp1E`xL<53!N^~T>B=Q!EC7u53fk~5*8WL}%e_O<6O^Rd$l+sLN_XZW3@LGtzW^-E{$5Yeqw($;r?GTt`=Xcunkd__NG zJq6xi++y<%vv`=~gJ>Gd(oeJ3sX!D~ZBPBu=rO#Sh8tUhaLjv@iYlbh$T3lw=H26F zwUzLsG`&LpmNs5yDH<}a&=2LI_j{p^P&u~JcA*J2R>*NidJ{Tyt-yp`!CJ)${MCxH zgyZm&@ZQEdm26r#9Jva3JDnE?YjTBbSlgKIPh;ea)~H3EfDqB_im*$clUGU*eyL?IiMn%y%FCR zVt6Mn7y`Iqf9S&Cw|lLShFf%w%V<6xH@Pyin{b#V(06d1<>01Kk-onZU52>i5O;H@ zC9u{KoOf`nC9wB%u_c_Qan2TQR<}kPENpb66>e>}g(n!*o^OXydsbi+pY%*pmy)*B z&6gzZ(wU|kr^&^oq+rz-Wk{t-G9*nV^AYyLK~m0bgmkh33hr9t6Yl6PrrCUg{jC6! zG^%FzqJAd1H=qkMa;qD)&eBFr!8&iY?|04*3WR>ESvGKgS;Q-(sBo;OQW0MI+*9PY^(%1Gy2dQ(MJzmcJBMvawO@flGL33Qo|Yg0_PvJ*Rr$&RJVaP&}dNzhK$L9ex$Zy2*o3g?)6Sk!uV{rnpOT{W$+` zKq-^1-I7q($I}`J)jjbP`k!Rw*Heq5W=$HW;`@?WB3Vs4$t2Axd?g1*$Eh`K+Di(8617i#_bW%XFDTy1Zj|3d*uO!weTUqw7X-BC;!gwPXD+ksR?k>fJ9~#@PV@X(O$?gjGgM) zb7OB^B(T3MFVqpNwqEIpzMR4=!hR}oSfjDw?5iOZHup_|er@)9Z)ey+8e?lL_z2PnU~bJa`azFYN0l6Z{NJ7EO$uH20yDl)eYvO3FyCJ45jx*Qb5`Y)dJEd}e}x zu6{p5OE=cbu7~*E#eiOUq4IrcOHf@4tK11*R6?2#f4JJSJed~$FfAkKi2#0}8WB?OWSm@yG_fw_Yj z*a2pM&pr@XAR`fNfH=X-EI^!K)G7L$;FI$hV3q?dmT6xffjWX105rRvVX$=4DRWNv zw4k@m8K6I9)(0F5rVY`4NCcdq1Gkib9Y)H@v(4T@V zfe9g#I&?k4uw&4|nVo=vB;uI50W_x|h0KXDSEoflKt|a!Ac*id0>=q-0D2uVAhysM zijg!Pq0ceVa5nr7+WS=E6CgOGkUb(}Yy(`6tOLEA*&&4wjXDMTGtfIgJLgS-ceS8Ov?!Q<7XyybLIfvNM*;%S zAzFmLB5DC4pAp%Dr(HblgTz?tu>=98LoITvl+=N0Dkb%rqzoagGpYmlPLUpQ-2egg zusx)Zf&T;p1{e@y2EBm$J<`jkpgJV$j3iRXfYSkq!d{)h2?qBujMk9dLpza zE6_rQ^dK|Jpi3cR#t}fD0NH>tI71Ve5MzS0FvmdS^aS9@3aT~js0CY&@fm?ostNma zLl&`V0A#Ynh2Ap^IKlTAG}7{L4fjFg5W=)T&r!|>1Lcq`qDeSHCsIU(i!lS*mOiFW z9pfzf;5)fbb3zq30}pyYe@q0lCv+ek3={Q|8PMWWyyQDqJEhfz*7pGJ6!W0`h?qu2 z*1rmw%bIqSlcge(YU+|D>wumDBunXVhM3Ai!e>{{g{T2T8~fhO2RHGB~_MkI;7$PG!37#X_$uv(~fDbPe>h_ zXz}T-s721q0S!gUB}N$#&=J;98?$KxT9;(eT+^ypg1}=gbh>-0Cfzz)xD1EE%HUHn ztq&g1PaM-H_rNb&K^kGsz(p8fpmY^RQ4+MK)Go4%lIVb4$CwSj3uM(IYRCXQrHkik zq=#b6<4iHwNM)sO;L|o28n{0IniG7A6;K4U_*By~faqWV{e;tc@K;u*%!3PxK%M15 zJ5`nu6eAV+t_Zqd5vNR`wCS|05l~8url9dj*TluhII0iY8E2e;nUq|I2%KSpa45h- zbmFS!BBGn58DPjo%Syx)xQizM=MX9`J#_}uVg@XZil7?QnKKBMAr}pzd@2GCcJVL<&=yfy zU8DwNjE>G=DQT8nxCk4?z>-htQ6$b#1YEr?zJwUbar)RH22;HB+l-Fr6%K)nHfo3! zKzBa`GsQwH0%xkI#h8Hu2W2IVTEZ*_9QxJ=5p?>D?;HfEhKou8M+~?kphkzA4*CXu z(u7bp)SMVpbN#m=k78`G8Eb4VNAp=MDP)YlntkPVPb^q0D;jet-edhDlREVpI%_%p zD_azMYcJX7zO~#@$38#T-Y<47eB{uw_OVQ$tlfGyjho}sES#cDSdSN2E{eRu>LguR z?V761g=zS`?{@57EdK;AxousgDp9_VYzo?iU3yI_sn@h-QL^(|Y^^OL)-|*=F}YCf z9MqF0=75mKE8PTcO>ZFq$@An~YD+6SNd>BwPw>DctVNXVcCDriq~@U16-kBaf>*LG zQSsL*DqoA*S6N1)gxX0kOHZAFV&RVma$9L2mHj{Aq)3u&a@h_ukXsA;E^*e-?s9p7 z5R!uf`3CIC3t^jVz~v$jXQez2Ybl%NOVieTQ%z5WlE?8k*IpEh-40CW3-|m>0qMRU z9AIn>oLOc~aT=LF2jCZ5u&I(yzDT%a6gjoXD*3e=uN=FPWMo;1Txg~_P^O0& zyNydBNN@S|DcQYnfqqOjsA9H%zBQ^O>WM5DUzYx%*(I)EEvhQlZeaBAK=gOT(D?^j zu95%^49j*#rIIAI18SRANb(PMA_;@mkR%It>qW~P@yD|)7w7YbExcv4AO6xR+}2uU z#`U|k8eKfyS*7Wfx}xJZjW6- zm%rAwWG;s0Wni3q-@-B#yRYUK%Xb02ktpJPnxdVe`YQ8tD5jJ0&{sgbgCu0w8vb;u z_~U8^x8jfQ`)Be;w}*=oe)MoF;-%Z4N%vN6WzFun$N^=;p z86$&EI28mX5S9hYAw!+3 zcshmyYOL8!{Jq>`Pz1#Rz&F$BqjdKDm7NYp*=ur9pNsm`bRg-Jfh4RLmt^|JxM!)o zp2$1LZ2Zml-xkCAN)O@)=H3BH=c^$`t%gbbMv8^meGN>$ZK^0hr9<2|_YtZ%a{F?1 zLJzxJynRCFw#T=W)a+Or>e&PaYn#ns1^_LcXf}4V~p!vSvk$I=S8_r}jvKP_F z%%ThPNo+Vd5b;Ue+|yc5@@5~@c>;2It&m)F@eje+fd-SEb0KkoC$djDT-1{a0$)!v zxinkyQAtsi1cKllO+t|Gq@+-_RGY{7g_tLvv2Cx-)#~gSxlW&nwNj(~&^0RUMph{` zS24A^=4K;`tt+ugs}=i_TGtA_CK|u&957h|i(SLacw=PZ2BS&qb9M zU^WjrFM>A{mG2*K~X3xji9_e)3aBeVC5X%#>zo7dbeoAuc-k3u8T&r z+?N3a8|GTAXf&TfQREF`-lm2N1J7E2|FPb+Nss{m6 z0~{YpP`lPM>n(MO%08BO)=%eV0$LAkSG!w;T-eCb=HbSfa9aJ0*!Hr^uD}-t?`X|c zb0sUf5X?S5Z%bicV7Uv@O0%tQony;1xO#O5&t0wyoVs_ee@N`9so6UPFY*vO1fL(B zf~zWo3a-DmRd9}7RB#7xD+RxcF7=#ihm_lDF>xrmzg%?b4qWb%rXO?C6hgkm*wMlN z5aLFVio>(D&h;)CTKiihLsybCJSko@g>K_thL7)?s64%mL}jsgTdUMQD3Xl02J71% zSZUa`dSc}>707{C99g+Uy^LkDCpfh7Lo0J2K7CeuYl`1b=Wa_>aq1Uj`NP5pjsN}lthk0M=3%A);r`VsuCio5go5BdCE-+y$AktD%? z2!W)$q;%bBx_3@@z7};Ni|6T{IlE>EiwNwmm3|C^NU5S*G=4W8Jl@JFw|~j)ojPj; zmqy2b-(+K`&c49&mUa55YD2Vith{s0lbmq(EA2C@N+oWUYQGPE8{}pqyPz_!fp>Mj zPt_CZX}VQXdEZJj{|@MNmgX+=^7qTU_?qrpwlc?6`ux6D#+8b%F*LsF{G+ioenMhy zT;2A2)K|@ zBw1Np#9qi&7Oh*SD~k~;`O4tQUBa@eT>bBqu+%cmpaQnYQ1Z?b3uUF;HIrBCyyd!3 zY$vlz%|}#T9lD)sLUJvB-LVTwl|+&&hc2$LREDlOcEQN1Ll+jjE?gFBc;DYNxW%*k zg&}_jrTRvYyBt$=(o0eZ7TWMeH*<2efs0!lUae=OAFQQf?ly`Dq^v`en-Gh)sj5y| zFuSHkt*bey_2XH0Z z=qI}V1`k(}lywt?=ReaGSosj};%=E1L9PibZ7ql{xTKfH-w*$xx6+pG4FLdNMhC!d z+v2sLD^!FPS>?j6$dLFfda;r|iI*ZSUG{~&C|Fbig`uh9kQ=x>TQ?G%;|ua!ExbSV zU9)i9A3SK(YtW^=j!7tiHSYF(JQ|EuZpL+}4pnNAPg pNcd3!U-Zyk=pP=t;>}D4X1y${!OV*bn(`{gSpP_azm(Xl|35$TRJ;HH delta 2880 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 APyhe` delta 2373 zcmYk7t8W!S6vlH)!sC`}NJ9e{QU%EknSIR8t^q|5kO~~C>{C>t7R(BKL$gR60!y-Z zG!oN)0mC1`oZq(sai`lkkMDfnIWzO~YV+x8^ZnO${=NMWf5*%D;fu}u?Cz8K&+Rw- z{q}sfeBAxG9<3*HeN>y*e13Si{5^VUo|p0Gm)-HxgH1WVy7zu6hW$e~cD1dU+i!2V zG0E+pHrVP)R&Q%x*jiM!=IjrdeGff5_+(g1GC02b&aADowax)ZajncejUAZ^ZGo96 z0<@adQJw?GliTCprIvNTrvt$^qJSYGpr(wDfP(!Rm?iL{7^=$!T zTY^CJD1%C>L1=IdAeSneRdVK}XaFlM^)yzEW$kD~&)N;YuW zrwJVKHC!br2F(Yh)-@c~=8`qXX>By64wS8Y%H6QDHi<;Cs$qes1{I=U+bOD~p;%88 z1vp4+8gn&(xE$(gZontk(DAL&8}v<7t<%tHCPe|Y2MSC4eu~{_)D4XR(Vl=5%=I*2 zO&N(Af1U1ldT2EHJZlDmoH_zBNhL7=Ps#6tFui(x=rdQB`$)jtp(Nq;s zNJ&>gq*Z^C$@0Z((PWU^=p%-@(mHaql852y@Jv{BeI>fN3!EI(J&8(oI>K~kN=TiO zkb<95Oo?s-S=Z~D6yg?rtPPSx*N!d)q3Ick17$>1BeIEO$5B;i zxI0vp3ZAo^AYmqvR*IAtx1uBsq*G3Y?zn-T`w5lw2KHoHUo#MDs*pUoIpRBE90GG4 z2v+y>6lSCdU}`_U=@EZmsuKaQqgOEqzeTZ%k}_R;I^K#47~->JOb}^+kJ__NJy~;b zG<32mLqUdKs{nMmAEiLA?bgWZbb%&J7sZ(OC-)zSfa=WYT%35*qYBI0lgInz@^o`; ezC0b5JFgFR@#EccarXJZKIV(F8_U<`%guijkzAJm diff --git a/session/service.proto b/session/service.proto index 524213a..5c22fc3 100644 --- a/session/service.proto +++ b/session/service.proto @@ -3,7 +3,6 @@ package session; option go_package = "github.com/nspcc-dev/neofs-api-go/session"; option csharp_namespace = "NeoFS.API.Session"; -import "session/types.proto"; import "service/meta.proto"; import "service/verify.proto"; import "github.com/gogo/protobuf/gogoproto/gogo.proto"; @@ -33,9 +32,9 @@ message CreateRequest { // owner of manipulation object; // ID of manipulation object; // token lifetime bounds. - session.Token Init = 1; + service.Token Init = 1; // Signed Init message response (Unsigned) from server with user private key - session.Token Signed = 2; + service.Token Signed = 2; } // RequestMetaHeader contains information about request meta headers (should be embedded into message) service.RequestMetaHeader Meta = 98 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; @@ -46,8 +45,8 @@ message CreateRequest { message CreateResponse { oneof Message { // Unsigned token with token ID and session public key generated on server side - session.Token Unsigned = 1; + service.Token Unsigned = 1; // Result is a resulting token which can be used for object placing through an trusted intermediary - session.Token Result = 2; + service.Token Result = 2; } } diff --git a/session/types.pb.go b/session/types.pb.go deleted file mode 100644 index 01458ddcbeeb3b8b0e1309cf040c52def5afa7b2..0000000000000000000000000000000000000000 GIT binary patch literal 0 HcmV?d00001 literal 22959 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 AApigX diff --git a/session/types.proto b/session/types.proto deleted file mode 100644 index 3ae49a3..0000000 --- a/session/types.proto +++ /dev/null @@ -1,35 +0,0 @@ -syntax = "proto3"; -package session; -option go_package = "github.com/nspcc-dev/neofs-api-go/session"; -option csharp_namespace = "NeoFS.API.Session"; - -import "github.com/gogo/protobuf/gogoproto/gogo.proto"; - -option (gogoproto.stable_marshaler_all) = true; - -message VerificationHeader { - // PublicKey is a session public key - bytes PublicKey = 1; - // KeySignature is a session public key signature. Signed by trusted side - bytes KeySignature = 2; -} - -// User token granting rights for object manipulation -message Token { - // Header carries verification data of session key - VerificationHeader Header = 1 [(gogoproto.nullable) = false]; - // OwnerID is an owner of manipulation object - bytes OwnerID = 2 [(gogoproto.customtype) = "OwnerID", (gogoproto.nullable) = false]; - // FirstEpoch is an initial epoch of token lifetime - uint64 FirstEpoch = 3; - // LastEpoch is a last epoch of token lifetime - uint64 LastEpoch = 4; - // ObjectID is an object identifier of manipulation object - repeated bytes ObjectID = 5 [(gogoproto.customtype) = "ObjectID", (gogoproto.nullable) = false]; - // Signature is a token signature, signed by owner of manipulation object - bytes Signature = 6; - // ID is a token identifier. valid UUIDv4 represented in bytes - bytes ID = 7 [(gogoproto.customtype) = "TokenID", (gogoproto.nullable) = false]; - // PublicKeys associated with owner - repeated bytes PublicKeys = 8; -} From 24108f42c3afe8b70358cb420ed88d6b74533314 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 12:51:27 +0300 Subject: [PATCH 02/68] Regenerate docs --- docs/object.md | 6 +---- docs/service.md | 67 ++++++++++++++++++++++++++++++++++++++----------- docs/session.md | 59 +++---------------------------------------- 3 files changed, 57 insertions(+), 75 deletions(-) diff --git a/docs/object.md b/docs/object.md index 4ec32fc..27e4bcf 100644 --- a/docs/object.md +++ b/docs/object.md @@ -149,7 +149,6 @@ calculated for XORed data. | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | OwnerID | [bytes](#bytes) | | OwnerID is a wallet address | -| Token | [session.Token](#session.Token) | | Token with session public key and user's signature | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -228,7 +227,6 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | -| Raw | [bool](#bool) | | Raw is the request flag of a physically stored representation of an object | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -256,7 +254,6 @@ in distributed system. | ----- | ---- | ----- | ----------- | | Address | [refs.Address](#refs.Address) | | Address of object (container id + object id) | | FullHeaders | [bool](#bool) | | FullHeaders can be set true for extended headers in the object | -| Raw | [bool](#bool) | | Raw is the request flag of a physically stored representation of an object | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -296,7 +293,6 @@ in distributed system. | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Object | [Object](#object.Object) | | Object with at least container id and owner id fields | -| Token | [session.Token](#session.Token) | | Token with session public key and user's signature | | CopiesNumber | [uint32](#uint32) | | Number of the object copies to store within the RPC call (zero is processed according to the placement rules) | @@ -378,7 +374,7 @@ in distributed system. | UserHeader | [UserHeader](#object.UserHeader) | | UserHeader is a set of KV headers defined by user | | Transform | [Transform](#object.Transform) | | Transform defines transform operation (e.g. payload split) | | Tombstone | [Tombstone](#object.Tombstone) | | Tombstone header that set up in deleted objects | -| Verify | [session.VerificationHeader](#session.VerificationHeader) | | Verify header that contains session public key and user's signature | +| Token | [service.Token](#service.Token) | | Token header contains token of the session within which the object was created | | HomoHash | [bytes](#bytes) | | HomoHash is a homomorphic hash of original object payload | | PayloadChecksum | [bytes](#bytes) | | PayloadChecksum of actual object's payload | | Integrity | [IntegrityHeader](#object.IntegrityHeader) | | Integrity header with checksum of all above headers in the object | diff --git a/docs/service.md b/docs/service.md index 90e1bd2..eef1e49 100644 --- a/docs/service.md +++ b/docs/service.md @@ -14,8 +14,9 @@ - Messages - [RequestVerificationHeader](#service.RequestVerificationHeader) - - [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) - [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) + - [Token](#service.Token) + - [Token.Info](#service.Token.Info) - [service/verify_test.proto](#service/verify_test.proto) @@ -49,6 +50,7 @@ RequestMetaHeader contains information about request meta headers | TTL | [uint32](#uint32) | | TTL must be larger than zero, it decreased in every NeoFS Node | | Epoch | [uint64](#uint64) | | Epoch for user can be empty, because node sets epoch to the actual value | | Version | [uint32](#uint32) | | Version defines protocol version TODO: not used for now, should be implemented in future | +| Raw | [bool](#bool) | | Raw determines whether the request is raw or not | @@ -88,18 +90,7 @@ RequestVerificationHeader is a set of signatures of every NeoFS Node that proces | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | | Signatures | [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) | repeated | Signatures is a set of signatures of every passed NeoFS Node | - - - - -### Message RequestVerificationHeader.Sign - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| Sign | [bytes](#bytes) | | Sign is signature of the request or session key. | -| Peer | [bytes](#bytes) | | Peer is compressed public key used for signature. | +| Token | [Token](#service.Token) | | Token is a token of the session within which the request is sent | @@ -110,11 +101,57 @@ RequestVerificationHeader is a set of signatures of every NeoFS Node that proces | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Sign | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Sign is a signature and public key of the request. | -| Origin | [RequestVerificationHeader.Sign](#service.RequestVerificationHeader.Sign) | | Origin used for requests, when trusted node changes it and re-sign with session key. If session key used for signature request, then Origin should contain public key of user and signed session key. | +| Sign | [bytes](#bytes) | | Sign is signature of the request or session key. | +| Peer | [bytes](#bytes) | | Peer is compressed public key used for signature. | + + + + +### Message Token +User token granting rights for object manipulation + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| TokenInfo | [Token.Info](#service.Token.Info) | | TokenInfo is a grouped information about token | +| Signature | [bytes](#bytes) | | Signature is a signature of session token information | + + + + +### Message Token.Info + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| ID | [bytes](#bytes) | | ID is a token identifier. valid UUIDv4 represented in bytes | +| OwnerID | [bytes](#bytes) | | OwnerID is an owner of manipulation object | +| verb | [Token.Info.Verb](#service.Token.Info.Verb) | | Verb is a type of request for which the token is issued | +| Address | [refs.Address](#refs.Address) | | Address is an object address for which token is issued | +| Created | [uint64](#uint64) | | Created is an initial epoch of token lifetime | +| ValidUntil | [uint64](#uint64) | | ValidUntil is a last epoch of token lifetime | +| SessionKey | [bytes](#bytes) | | SessionKey is a public key of session key | + + + +### Token.Info.Verb +Verb is an enumeration of session request types + +| Name | Number | Description | +| ---- | ------ | ----------- | +| Put | 0 | Put refers to object.Put RPC call | +| Get | 1 | Get refers to object.Get RPC call | +| Head | 2 | Head refers to object.Head RPC call | +| Search | 3 | Search refers to object.Search RPC call | +| Delete | 4 | Delete refers to object.Delete RPC call | +| Range | 5 | Range refers to object.GetRange RPC call | +| RangeHash | 6 | RangeHash refers to object.GetRangeHash RPC call | + + diff --git a/docs/session.md b/docs/session.md index ba615c3..4a537e6 100644 --- a/docs/session.md +++ b/docs/session.md @@ -12,13 +12,6 @@ - [CreateResponse](#session.CreateResponse) -- [session/types.proto](#session/types.proto) - - - Messages - - [Token](#session.Token) - - [VerificationHeader](#session.VerificationHeader) - - - [Scalar Value Types](#scalar-value-types) @@ -68,8 +61,8 @@ session key. Session is established during 4-step handshake in one gRPC stream | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Init | [Token](#session.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | -| Signed | [Token](#session.Token) | | Signed Init message response (Unsigned) from server with user private key | +| Init | [service.Token](#service.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | +| Signed | [service.Token](#service.Token) | | Signed Init message response (Unsigned) from server with user private key | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -82,52 +75,8 @@ session key. Session is established during 4-step handshake in one gRPC stream | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Unsigned | [Token](#session.Token) | | Unsigned token with token ID and session public key generated on server side | -| Result | [Token](#session.Token) | | Result is a resulting token which can be used for object placing through an trusted intermediary | - - - - - - - - -

Top

- -## session/types.proto - - - - - - - -### Message Token -User token granting rights for object manipulation - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| Header | [VerificationHeader](#session.VerificationHeader) | | Header carries verification data of session key | -| OwnerID | [bytes](#bytes) | | OwnerID is an owner of manipulation object | -| FirstEpoch | [uint64](#uint64) | | FirstEpoch is an initial epoch of token lifetime | -| LastEpoch | [uint64](#uint64) | | LastEpoch is a last epoch of token lifetime | -| ObjectID | [bytes](#bytes) | repeated | ObjectID is an object identifier of manipulation object | -| Signature | [bytes](#bytes) | | Signature is a token signature, signed by owner of manipulation object | -| ID | [bytes](#bytes) | | ID is a token identifier. valid UUIDv4 represented in bytes | -| PublicKeys | [bytes](#bytes) | repeated | PublicKeys associated with owner | - - - - -### Message VerificationHeader - - - -| Field | Type | Label | Description | -| ----- | ---- | ----- | ----------- | -| PublicKey | [bytes](#bytes) | | PublicKey is a session public key | -| KeySignature | [bytes](#bytes) | | KeySignature is a session public key signature. Signed by trusted side | +| Unsigned | [service.Token](#service.Token) | | Unsigned token with token ID and session public key generated on server side | +| Result | [service.Token](#service.Token) | | Result is a resulting token which can be used for object placing through an trusted intermediary | From 4ac17201b7c72e6621cad6c0955b738c28dba21b Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 13:09:18 +0300 Subject: [PATCH 03/68] Resolve update conflicts --- object/extensions.go | 15 ----- object/service_test.go | 4 +- object/types.go | 16 ++--- object/utils.go | 4 +- object/verification.go | 4 +- object/verification_test.go | 18 ++--- service/alias.go | 14 ++++ service/verify.go | 30 +-------- service/verify_test.go | 19 ++---- session/service.go | 15 +---- session/store.go | 17 +++-- session/store_test.go | 95 +------------------------- session/types.go | 130 ++---------------------------------- 13 files changed, 61 insertions(+), 320 deletions(-) create mode 100644 service/alias.go diff --git a/object/extensions.go b/object/extensions.go index 6e577bd..be755c6 100644 --- a/object/extensions.go +++ b/object/extensions.go @@ -19,21 +19,6 @@ func (m Object) IsLinking() bool { return false } -// VerificationHeader returns verification header if it is presented in extended headers. -func (m Object) VerificationHeader() (*VerificationHeader, error) { - _, vh := m.LastHeader(HeaderType(VerifyHdr)) - if vh == nil { - return nil, ErrHeaderNotFound - } - return vh.Value.(*Header_Verify).Verify, nil -} - -// SetVerificationHeader sets verification header in the object. -// It will replace existing verification header or add a new one. -func (m *Object) SetVerificationHeader(header *VerificationHeader) { - m.SetHeader(&Header{Value: &Header_Verify{Verify: header}}) -} - // Links returns slice of ids of specified link type func (m *Object) Links(t Link_Type) []ID { var res []ID diff --git a/object/service_test.go b/object/service_test.go index 4b02b37..5b7a358 100644 --- a/object/service_test.go +++ b/object/service_test.go @@ -16,8 +16,8 @@ func TestRequest(t *testing.T) { &DeleteRequest{}, &GetRangeRequest{}, &GetRangeHashRequest{}, - MakePutRequestHeader(nil, nil), - MakePutRequestHeader(&Object{}, nil), + MakePutRequestHeader(nil), + MakePutRequestHeader(&Object{}), } types := []RequestType{ diff --git a/object/types.go b/object/types.go index aebb2fc..83b03c7 100644 --- a/object/types.go +++ b/object/types.go @@ -7,7 +7,6 @@ import ( "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/session" ) type ( @@ -19,9 +18,6 @@ type ( // Address is a type alias of object Address. Address = refs.Address - // VerificationHeader is a type alias of session's verification header. - VerificationHeader = session.VerificationHeader - // PositionReader defines object reader that returns slice of bytes // for specified object and data range. PositionReader interface { @@ -60,8 +56,8 @@ const ( TransformHdr // TombstoneHdr is a tombstone header type. TombstoneHdr - // VerifyHdr is a verification header type. - VerifyHdr + // TokenHdr is a token header type. + TokenHdr // HomoHashHdr is a homomorphic hash header type. HomoHashHdr // PayloadChecksumHdr is a payload checksum header type. @@ -175,8 +171,8 @@ func (m Header) typeOf(t isHeader_Value) (ok bool) { _, ok = m.Value.(*Header_Transform) case *Header_Tombstone: _, ok = m.Value.(*Header_Tombstone) - case *Header_Verify: - _, ok = m.Value.(*Header_Verify) + case *Header_Token: + _, ok = m.Value.(*Header_Token) case *Header_HomoHash: _, ok = m.Value.(*Header_HomoHash) case *Header_PayloadChecksum: @@ -205,8 +201,8 @@ func HeaderType(t headerType) Pred { return func(h *Header) bool { _, ok := h.Value.(*Header_Transform); return ok } case TombstoneHdr: return func(h *Header) bool { _, ok := h.Value.(*Header_Tombstone); return ok } - case VerifyHdr: - return func(h *Header) bool { _, ok := h.Value.(*Header_Verify); return ok } + case TokenHdr: + return func(h *Header) bool { _, ok := h.Value.(*Header_Token); return ok } case HomoHashHdr: return func(h *Header) bool { _, ok := h.Value.(*Header_HomoHash); return ok } case PayloadChecksumHdr: diff --git a/object/utils.go b/object/utils.go index 07f0984..33423aa 100644 --- a/object/utils.go +++ b/object/utils.go @@ -4,7 +4,6 @@ import ( "io" "strconv" - "github.com/nspcc-dev/neofs-api-go/session" "github.com/pkg/errors" ) @@ -46,11 +45,10 @@ func (b ByteSize) String() string { // MakePutRequestHeader combines object and session token value // into header of object put request. -func MakePutRequestHeader(obj *Object, token *session.Token) *PutRequest { +func MakePutRequestHeader(obj *Object) *PutRequest { return &PutRequest{ R: &PutRequest_Header{Header: &PutRequest_PutHeader{ Object: obj, - Token: token, }}, } } diff --git a/object/verification.go b/object/verification.go index a00b30a..5694316 100644 --- a/object/verification.go +++ b/object/verification.go @@ -77,7 +77,7 @@ func (m Object) Verify() error { integrity := ih.Value.(*Header_Integrity).Integrity // Prepare structures - _, vh := m.LastHeader(HeaderType(VerifyHdr)) + _, vh := m.LastHeader(HeaderType(TokenHdr)) if vh == nil { _, pkh := m.LastHeader(HeaderType(PublicKeyHdr)) if pkh == nil { @@ -85,7 +85,7 @@ func (m Object) Verify() error { } pubkey = pkh.Value.(*Header_PublicKey).PublicKey.Value } else { - pubkey = vh.Value.(*Header_Verify).Verify.PublicKey + pubkey = vh.Value.(*Header_Token).Token.SessionKey } // Verify signature diff --git a/object/verification_test.go b/object/verification_test.go index b37ec70..004f969 100644 --- a/object/verification_test.go +++ b/object/verification_test.go @@ -6,7 +6,7 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/session" + "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" @@ -77,11 +77,13 @@ func TestObject_Verify(t *testing.T) { dataPK := crypto.MarshalPublicKey(&sessionkey.PublicKey) signature, err = crypto.Sign(key, dataPK) - vh := &session.VerificationHeader{ - PublicKey: dataPK, - KeySignature: signature, + tok := &service.Token{ + Token_Info: service.Token_Info{ + SessionKey: dataPK, + }, + Signature: signature, } - obj.SetVerificationHeader(vh) + obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) // validation header is not last t.Run("error validation header is not last", func(t *testing.T) { @@ -90,7 +92,7 @@ func TestObject_Verify(t *testing.T) { }) obj.Headers = obj.Headers[:len(obj.Headers)-2] - obj.SetVerificationHeader(vh) + obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) obj.SetHeader(&Header{Value: &Header_Integrity{ih}}) t.Run("error invalid header checksum", func(t *testing.T) { @@ -115,7 +117,7 @@ func TestObject_Verify(t *testing.T) { require.NoError(t, err) obj.SetHeader(genIH) - t.Run("correct with vh", func(t *testing.T) { + t.Run("correct with tok", func(t *testing.T) { err = obj.Verify() require.NoError(t, err) }) @@ -123,7 +125,7 @@ func TestObject_Verify(t *testing.T) { pkh := Header{Value: &Header_PublicKey{&PublicKey{ Value: crypto.MarshalPublicKey(&key.PublicKey), }}} - // replace vh with pkh + // replace tok with pkh obj.Headers[len(obj.Headers)-2] = pkh // re-sign object obj.Sign(sessionkey) diff --git a/service/alias.go b/service/alias.go new file mode 100644 index 0000000..6c22ece --- /dev/null +++ b/service/alias.go @@ -0,0 +1,14 @@ +package service + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" +) + +// TokenID is type alias of UUID ref. +type TokenID = refs.UUID + +// OwnerID is type alias of OwnerID ref. +type OwnerID = refs.OwnerID + +// Address is type alias of Address ref. +type Address = refs.Address diff --git a/service/verify.go b/service/verify.go index 9687032..ade13ef 100644 --- a/service/verify.go +++ b/service/verify.go @@ -53,18 +53,6 @@ func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_ m.Signatures = append(m.Signatures, sig) } -// SetOwner adds origin (sign and public key) of owner (client) into first signature. -func (m *RequestVerificationHeader) SetOwner(pub *ecdsa.PublicKey, sign []byte) { - if len(m.Signatures) == 0 || pub == nil { - return - } - - m.Signatures[0].Origin = &RequestVerificationHeader_Sign{ - Sign: sign, - Peer: crypto.MarshalPublicKey(pub), - } -} - // CheckOwner validates, that passed OwnerID is equal to present PublicKey of owner. func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error { if key, err := m.GetOwner(); err != nil { @@ -83,18 +71,6 @@ func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error { func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) { if len(m.Signatures) == 0 { return nil, ErrCannotFindOwner - } - - // if first signature contains origin, we should try to validate session key - if m.Signatures[0].Origin != nil { - owner := crypto.UnmarshalPublicKey(m.Signatures[0].Origin.Peer) - if owner == nil { - return nil, ErrCannotLoadPublicKey - } else if err := crypto.Verify(owner, m.Signatures[0].Peer, m.Signatures[0].Origin.Sign); err != nil { - return nil, errors.Wrap(err, "could not verify session token") - } - - return owner, nil } else if key := crypto.UnmarshalPublicKey(m.Signatures[0].Peer); key != nil { return key, nil } @@ -128,10 +104,8 @@ func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeade } return &RequestVerificationHeader_Signature{ - RequestVerificationHeader_Sign: RequestVerificationHeader_Sign{ - Sign: sign, - Peer: crypto.MarshalPublicKey(&key.PublicKey), - }, + Sign: sign, + Peer: crypto.MarshalPublicKey(&key.PublicKey), }, nil } diff --git a/service/verify_test.go b/service/verify_test.go index 27491da..ce333aa 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -119,15 +119,13 @@ func TestMaintainableRequest(t *testing.T) { req.TTL-- key := test.DecodeKey(i) - require.NoError(t, SignRequestHeader(key, req)) // sign first key (session key) by owner key if i == 0 { - sign, err := crypto.Sign(owner, crypto.MarshalPublicKey(&key.PublicKey)) - require.NoError(t, err) - - req.SetOwner(&owner.PublicKey, sign) + key = owner } + + require.NoError(t, SignRequestHeader(key, req)) } { // Validate owner @@ -150,17 +148,8 @@ func TestMaintainableRequest(t *testing.T) { require.Equal(t, &owner.PublicKey, pub) } - { // wrong owner: - req.Signatures[0].Origin = nil - - pub, err := req.GetOwner() - require.NoError(t, err) - - require.NotEqual(t, &owner.PublicKey, pub) - } - { // Wrong signatures: - copy(req.Signatures[count-1].Sign, req.Signatures[count-1].Peer) + copy(req.Signatures[count-1].Sign, req.Signatures[count-2].Sign) err := VerifyRequestHeader(req) require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error()) } diff --git a/session/service.go b/session/service.go index 182ff7d..367aeb1 100644 --- a/session/service.go +++ b/session/service.go @@ -5,7 +5,6 @@ import ( "crypto/ecdsa" "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" ) type ( @@ -31,9 +30,9 @@ type ( TokenParams struct { FirstEpoch uint64 LastEpoch uint64 - ObjectID []ObjectID + Address Address OwnerID OwnerID - PublicKeys [][]byte + Verb Verb } ) @@ -46,13 +45,3 @@ func NewInitRequest(t *Token) *CreateRequest { func NewSignedRequest(t *Token) *CreateRequest { return &CreateRequest{Message: &CreateRequest_Signed{Signed: t}} } - -// Sign signs contents of the header with the private key. -func (m *VerificationHeader) Sign(key *ecdsa.PrivateKey) error { - s, err := crypto.Sign(key, m.PublicKey) - if err != nil { - return err - } - m.KeySignature = s - return nil -} diff --git a/session/store.go b/session/store.go index f6a6655..e46afde 100644 --- a/session/store.go +++ b/session/store.go @@ -7,6 +7,7 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -48,13 +49,15 @@ func (s *simpleStore) New(p TokenParams) *PToken { t := &PToken{ mtx: new(sync.Mutex), Token: Token{ - ID: tid, - Header: VerificationHeader{PublicKey: crypto.MarshalPublicKey(&key.PublicKey)}, - FirstEpoch: p.FirstEpoch, - LastEpoch: p.LastEpoch, - ObjectID: p.ObjectID, - OwnerID: p.OwnerID, - PublicKeys: p.PublicKeys, + Token_Info: service.Token_Info{ + ID: tid, + OwnerID: p.OwnerID, + Verb: p.Verb, + Address: p.Address, + Created: p.FirstEpoch, + ValidUntil: p.LastEpoch, + SessionKey: crypto.MarshalPublicKey(&key.PublicKey), + }, }, PrivateKey: key, } diff --git a/session/store_test.go b/session/store_test.go index 9ad0e1d..f51fb18 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -1,96 +1,3 @@ package session -import ( - "crypto/ecdsa" - "crypto/rand" - "testing" - - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/stretchr/testify/require" -) - -type testClient struct { - *ecdsa.PrivateKey - OwnerID OwnerID -} - -func (c *testClient) Sign(data []byte) ([]byte, error) { - return crypto.Sign(c.PrivateKey, data) -} - -func newTestClient(t *testing.T) *testClient { - key, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader) - require.NoError(t, err) - - owner, err := refs.NewOwnerID(&key.PublicKey) - require.NoError(t, err) - - return &testClient{PrivateKey: key, OwnerID: owner} -} - -func signToken(t *testing.T, token *PToken, c *testClient) { - require.NotNil(t, token) - token.SetPublicKeys(&c.PublicKey) - - signH, err := c.Sign(token.Header.PublicKey) - require.NoError(t, err) - require.NotNil(t, signH) - - // data is not yet signed - keys := UnmarshalPublicKeys(&token.Token) - require.False(t, token.Verify(keys...)) - - signT, err := c.Sign(token.verificationData()) - require.NoError(t, err) - require.NotNil(t, signT) - - token.AddSignatures(signH, signT) - require.True(t, token.Verify(keys...)) -} - -func TestTokenStore(t *testing.T) { - s := NewSimpleStore() - - oid, err := refs.NewObjectID() - require.NoError(t, err) - - c := newTestClient(t) - require.NotNil(t, c) - pk := [][]byte{crypto.MarshalPublicKey(&c.PublicKey)} - - // create new token - token := s.New(TokenParams{ - ObjectID: []ObjectID{oid}, - OwnerID: c.OwnerID, - PublicKeys: pk, - }) - signToken(t, token, c) - - // check that it can be fetched - t1 := s.Fetch(token.ID) - require.NotNil(t, t1) - require.Equal(t, token, t1) - - // create and sign another token by the same client - t1 = s.New(TokenParams{ - ObjectID: []ObjectID{oid}, - OwnerID: c.OwnerID, - PublicKeys: pk, - }) - - signToken(t, t1, c) - - data := []byte{1, 2, 3} - sign, err := t1.SignData(data) - require.NoError(t, err) - require.Error(t, token.Header.VerifyData(data, sign)) - - sign, err = token.SignData(data) - require.NoError(t, err) - require.NoError(t, token.Header.VerifyData(data, sign)) - - s.Remove(token.ID) - require.Nil(t, s.Fetch(token.ID)) - require.NotNil(t, s.Fetch(t1.ID)) -} +// TODO: write unit tests diff --git a/session/types.go b/session/types.go index 4165291..e56373c 100644 --- a/session/types.go +++ b/session/types.go @@ -2,14 +2,12 @@ package session import ( "crypto/ecdsa" - "encoding/binary" "sync" - "github.com/nspcc-dev/neofs-api-go/chain" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/pkg/errors" ) type ( @@ -19,6 +17,12 @@ type ( OwnerID = refs.OwnerID // TokenID type alias. TokenID = refs.UUID + // Token type alias + Token = service.Token + // Address type alias + Address = refs.Address + // Verb is Token_Info_Verb type alias + Verb = service.Token_Info_Verb // PToken is a wrapper around Token that allows to sign data // and to do thread-safe manipulations. @@ -55,127 +59,7 @@ const ( ErrInvalidSignature = internal.Error("invalid signature") ) -// verificationData returns byte array to sign. -// Note: protobuf serialization is inconsistent as -// wire order is unspecified. -func (m *Token) verificationData() (data []byte) { - var size int - if l := len(m.ObjectID); l > 0 { - size = m.ObjectID[0].Size() - data = make([]byte, 16+l*size) - } else { - data = make([]byte, 16) - } - binary.BigEndian.PutUint64(data, m.FirstEpoch) - binary.BigEndian.PutUint64(data[8:], m.LastEpoch) - for i := range m.ObjectID { - copy(data[16+i*size:], m.ObjectID[i].Bytes()) - } - return -} - -// IsSame checks if the passed token is valid and equal to current token -func (m *Token) IsSame(t *Token) error { - switch { - case m.FirstEpoch != t.FirstEpoch: - return ErrWrongFirstEpoch - case m.LastEpoch != t.LastEpoch: - return ErrWrongLastEpoch - case !m.OwnerID.Equal(t.OwnerID): - return ErrWrongOwner - case m.Header.PublicKey == nil: - return ErrEmptyPublicKey - case len(m.ObjectID) != len(t.ObjectID): - return ErrWrongObjectsCount - default: - for i := range m.ObjectID { - if !m.ObjectID[i].Equal(t.ObjectID[i]) { - return errors.Wrapf(ErrWrongObjects, "expect %s, actual: %s", m.ObjectID[i], t.ObjectID[i]) - } - } - } - return nil -} - -// Sign tries to sign current Token data and stores signature inside it. -func (m *Token) Sign(key *ecdsa.PrivateKey) error { - if err := m.Header.Sign(key); err != nil { - return err - } - - s, err := crypto.Sign(key, m.verificationData()) - if err != nil { - return err - } - - m.Signature = s - return nil -} - -// SetPublicKeys sets owner's public keys to the token -func (m *Token) SetPublicKeys(keys ...*ecdsa.PublicKey) { - m.PublicKeys = m.PublicKeys[:0] - for i := range keys { - m.PublicKeys = append(m.PublicKeys, crypto.MarshalPublicKey(keys[i])) - } -} - -// Verify checks if token is correct and signed. -func (m *Token) Verify(keys ...*ecdsa.PublicKey) bool { - if m.FirstEpoch > m.LastEpoch { - return false - } - ownerFromKeys := chain.KeysToAddress(keys...) - if m.OwnerID.String() != ownerFromKeys { - return false - } - - for i := range keys { - if m.Header.Verify(keys[i]) && crypto.Verify(keys[i], m.verificationData(), m.Signature) == nil { - return true - } - } - return false -} - -// AddSignatures adds token signatures. -func (t *PToken) AddSignatures(signH, signT []byte) { - t.mtx.Lock() - - t.Header.KeySignature = signH - t.Signature = signT - - t.mtx.Unlock() -} - // SignData signs data with session private key. func (t *PToken) SignData(data []byte) ([]byte, error) { return crypto.Sign(t.PrivateKey, data) } - -// VerifyData checks if signature of data by token is equal to sign. -func (m *VerificationHeader) VerifyData(data, sign []byte) error { - if crypto.Verify(crypto.UnmarshalPublicKey(m.PublicKey), data, sign) != nil { - return ErrInvalidSignature - } - return nil -} - -// Verify checks if verification header was issued by id. -func (m *VerificationHeader) Verify(keys ...*ecdsa.PublicKey) bool { - for i := range keys { - if crypto.Verify(keys[i], m.PublicKey, m.KeySignature) == nil { - return true - } - } - return false -} - -// UnmarshalPublicKeys returns unmarshal public keys from the token -func UnmarshalPublicKeys(t *Token) []*ecdsa.PublicKey { - r := make([]*ecdsa.PublicKey, 0, len(t.PublicKeys)) - for i := range t.PublicKeys { - r = append(r, crypto.UnmarshalPublicKey(t.PublicKeys[i])) - } - return r -} From ba3239589443e4b908e13e448c748fbe470eb103 Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 28 Apr 2020 16:29:54 +0300 Subject: [PATCH 04/68] api: update to neofs-api v0.7.3 --- Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Makefile b/Makefile index 9773c9b..62a92ec 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PROTO_VERSION=v0.7.2 +PROTO_VERSION=v0.7.3 PROTO_URL=https://github.com/nspcc-dev/neofs-api/archive/$(PROTO_VERSION).tar.gz B=\033[0;1m From 7a7deb0b01a3fccff3647148ffa595c47f4c9548 Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 28 Apr 2020 16:30:47 +0300 Subject: [PATCH 05/68] proto: regenerate files --- object/types.pb.go | Bin 85297 -> 85256 bytes object/types.proto | 2 ++ 2 files changed, 2 insertions(+) diff --git a/object/types.pb.go b/object/types.pb.go index a24a2f1a798a88c173bc4125b21db0228126a486..97106c7bde6493f1526fbbcc1f09c27946b60cdc 100644 GIT binary patch delta 5451 zcmYkAJI>@(5Jm+O0T7T9Ak7F6(FA@!_JV}$wjCp)mI$1Z|JkepG)U}%4X_!i&Z(di ztNZ%idVcj)UH|p*i$6Yo@#o*m+ds>@w;$JcpZ)&!-TLjPU)FDa_`IFoe*O91dl)~w zZ=PM3&wbD5xbSQ~eD!6#IP%ZJrkUq3u;NyJKiXVrc!;j5Uin;DAA4MwSeh|L<7Y&K0q;Y9d*+EA z*&AVSWklXYFfQIUi14rxnKm>$*gAUv5v3D%77m}h-EsE7qBEcOv!7G{j1`yu@LLhx zgoXvxhsKy6j`e6;37&!8(KhmISwDGh= zK*q_QO0j{#>{gJVt#;13K<+QMB4Q7rcfbT&P4R||l}!t_EcmG>Mp7R}^y^h)8ZOgH z3_BgwY*sD|GlVPV?V%?0PrT^RGYJ6WW)u)~4frtgr#Fd6HS~?yGgSDwA!0_vf;Lwc zL&(`Sc7}d^o7K>`7a1oe8JLwIbp+->MvvLbe&G3p)W*PYR_h)}>n~jBIA-}o1>A`} z&h4JShm)s}co+brMxbN=i4}vP;eF^XSQx^HAx*Rqi|EdH8$a4!;1}V zWh#O!fbb+WSSIR`aKz`x)`O%!G!mg2e33TQ45TMQnEF>jVs0mWFvSZePGoFsQnt}+ zN!5W)%h$w6gc8xosRsXbZQ5~x;(N}=odFd-MX#iC!-pO#MP_SYu;mh~{O*C?!71ZX z1#RIN?t{ZRvXWcHb*=he?Zc}^pUK`xB!!Yy7f~S5qJ-x}sB9nu+FqR&$sJj}P|%8= z4gI2_XqOk^d1?^RGOpH}VxLhm5j^=*f3#vDYy!Y%kLH!2`i$C71?NqIYhihP=C-%dcu-W>VZ|HGS-FCMA8M3&j3dH z(378?1JZ|7XUS+}^#gV;?5sXJe_E;avr2u*^-$xA!mwS>qBrC3`lpB zRX8%6dVYuKSZQye)Jfewd80OiD;7uKT3Liq7je=MChW& z27=+?1*cl}3~9yv6x=JGIMtbGQ{ucZU{O>_YEZU=jL49ui!u=8o|E6Q^^PMlzzK%C zwCjHE{Mj)w$UHUJUrehLnUo@RT}ZV<`Sw*$0i8r@MbQeV4hW?J={=zw^T(@w9gxw! z6O#ngfKEq&8b63+O<_Q}#+(`g@Jc$cWNf^zQ9U97Ie$?sYKW0N(Abe5E4?}^8V% zNFvdMng2Lpua&cen6VX51$Wp$al0BEL!F^*f3m+tTvs;IP=-G=7htX60hyxpMEpFH6(fN6KHvmNkBr)1~#Bjvahe>2b^f-!0ht71`7!oo_=2Q6E_vaf=?062* zQu;tDZO*3NS?smfI@K1(nP^$eF6TUJL0N|p$uR!ag(c>nSTa_AiV+!`D3J&7Jccc> z$_sBaQjo#$Zth*d@na-B0wbv|3XMvV@0~T88x04HR%3=18+6mEJlmpeS=4(>MlsN~ zF)ynQ6HaX%&~LrtT5cBf?Ud-0s+Hcl>?~G;G;Y=Fp@V>x$f^S| zvY3rpYPucFlwn;B_5hh|mu>x>jZ*!3vDNk_yP){j7coG`scFb=;^vc!==V?Wqvq4N$vRQ#)Z6jt>)(F*?|*@hR09A2 delta 5394 zcmY+ItF9(h5QP(x5lm(bijc{KgapG8eZP>9@F1AJ`f-hFi~rpi9smOw9Pfa_;dl-N zwZE?d2eb2^?!9;2R;^lnet7@lm-jDz{&jl1e)aP4+w|$<`>#KF)UUpI{4u?K{P@-D z4}VW@zxb${A8)?<{a*H`&#SBJ$~q2ZUF)(g)ziz5tE*qi9~JAdmfzD<)|#!$Tz(%7 z&slfg4qJIrRb}nQvd$P74t~3*D+casYd)}G>MJ~}@v`H$&ljv^>9JPxw!*@uFW5QAZ6h#PdkvT#k8Nx%5#9kYcJ1bn z0vMZ#X(>j7g_=|iBxd8(YRne82&cfP^ zmcf8XfVggPV8_XY!`^*hrzMdSLDiOo=YxJl8N$AYE1^VDuHKSi=fHu9c#K4hx4dmh zTgU!}C$rvRv%%(RUN%C|TBhW9x&v0bC)g`+@;*u_a7F!OXAoycF-B6=@jPHqD5EC0 zgV#3K#P0(G6V4<8TkJ3TO0E`TAgY7AWs!ly&1o4(#=x=8LW^h)o9UFB~)SEn_ex7+s&86B#Bz3p;x% zoEL7cqwPzQ8W1)B#z<93(sYxhnQ)yZCctowDkm6sTVki8G?lXfPaE#u31l0vaEM9V zGpm_N5_4$(#LLrCvYN3Gk7w4(mACeI_M6Gjw6i90NYWEYIA*~d-Hzhp1{qFm%i#ET zjK$C>S#lJ)W-_>YBC4Ij$l@c{RXCaCN80QXQ@@fb_pi5{G1`3GYX3AmdESAuh2w51 z%g%5%Sq72p4HaFVIz}3o{A6N_ad!Y9=Zl@oImd`|d?yBhE}7<368g>t&8bw(o?Yia z)X9MZ8-X|pZ^=&@m!~Aw9lvCQV3AS=Daf=UUM|Pgj@u`x_lZcB(Om61sZk;prMHpH z+0i67J;nhwmqlNtQT8M?fXPU4FiMLT4pXrz$(qOrAafZ{dLgCbw?rtcq78}#sm2lMG_wo?q35NbMBJv61ba$K;a#y$_E$c-Ni~bT&LUs{*SWuTpvPtsXsxVD? z%uc7p#@kcxOj61@+^}MjlFS*!DE4booTup}Cr;84P*yegWn_Ou*O4TUVS_j!{*`Lw zYLX#yc6hPiWC*!<8=q;#ehL{Z=l20NLy{K@R$c6i45va)kY}l~(27(?s)8igEzAg& z_+*RUDZjd>yI4>-`|;;+2!#{7dOVht?1q#xMCCM7)F^|^tsfeGLqt-G0bMnp`5{UI zh86wuzYjj*$uCE`-rhHpdS@@^2(aJeLtn`3Er)A6oz*4^0a|iS2Xqbgok4?@;f2-` zJ;sn?lq$K^KFGEQLyBvU4cK&Ai5{`7%YCYHSbV{ zI|FiWMO_l4U}!% zV7FpHy!)R3iPF>RMhoz-E(z!DOnu2OElMwa=B&f7WbPUTVfJpb`;_Vqq{jY?*XFr)* zwMLLwF@PkzFBPlACYGH9qxX?XX~)w0;oujLGcW}N` Date: Tue, 28 Apr 2020 16:31:46 +0300 Subject: [PATCH 06/68] object: implement Stringify for Object --- object/types.go | 118 ++++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 118 insertions(+) diff --git a/object/types.go b/object/types.go index 83b03c7..4acffbe 100644 --- a/object/types.go +++ b/object/types.go @@ -3,10 +3,14 @@ package object import ( "bytes" "context" + "fmt" + "io" + "reflect" "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/pkg/errors" ) type ( @@ -262,3 +266,117 @@ func (m Object) Address() *refs.Address { CID: m.SystemHeader.CID, } } + +func (m CreationPoint) String() string { + return fmt.Sprintf(`{UnixTime=%d Epoch=%d}`, m.UnixTime, m.Epoch) +} + +// Stringify converts object into string format. +func Stringify(dst io.Writer, obj *Object) error { + // put empty line + if _, err := fmt.Fprintln(dst); err != nil { + return err + } + + // put object line + if _, err := fmt.Fprintln(dst, "Object:"); err != nil { + return err + } + + // put system headers + if _, err := fmt.Fprintln(dst, "\tSystemHeader:"); err != nil { + return err + } + + sysHeaders := []string{"ID", "CID", "OwnerID", "Version", "PayloadLength", "CreatedAt"} + v := reflect.ValueOf(obj.SystemHeader) + for _, key := range sysHeaders { + if !v.FieldByName(key).IsValid() { + return errors.Errorf("invalid system header key: %q", key) + } + + val := v.FieldByName(key).Interface() + if _, err := fmt.Fprintf(dst, "\t\t- %s=%v\n", key, val); err != nil { + return err + } + } + + // put user headers + if _, err := fmt.Fprintln(dst, "\tUserHeaders:"); err != nil { + return err + } + + for _, header := range obj.Headers { + var ( + typ = reflect.ValueOf(header.Value) + key string + val interface{} + ) + + switch t := typ.Interface().(type) { + case *Header_Link: + key = "Link" + val = fmt.Sprintf(`{Type=%s ID=%s}`, t.Link.Type, t.Link.ID) + case *Header_Redirect: + key = "Redirect" + val = fmt.Sprintf(`{CID=%s OID=%s}`, t.Redirect.CID, t.Redirect.ObjectID) + case *Header_UserHeader: + key = "UserHeader" + val = fmt.Sprintf(`{Key=%s Val=%s}`, t.UserHeader.Key, t.UserHeader.Value) + case *Header_Transform: + key = "Transform" + val = t.Transform.Type.String() + case *Header_Tombstone: + key = "Tombstone" + val = "MARKED" + case *Header_Token: + key = "Token" + val = fmt.Sprintf("{"+ + "ID=%s OwnerID=%s Verb=%s Address=%s Created=%d ValidUntil=%d SessionKey=%02x Signature=%02x"+ + "}", + t.Token.Token_Info.ID, + t.Token.Token_Info.OwnerID, + t.Token.Token_Info.Verb, + t.Token.Token_Info.Address, + t.Token.Token_Info.Created, + t.Token.Token_Info.ValidUntil, + t.Token.Token_Info.SessionKey, + t.Token.Signature) + case *Header_HomoHash: + key = "HomoHash" + val = t.HomoHash + case *Header_PayloadChecksum: + key = "PayloadChecksum" + val = t.PayloadChecksum + case *Header_Integrity: + key = "Integrity" + val = fmt.Sprintf(`{Checksum=%02x Signature=%02x}`, + t.Integrity.HeadersChecksum, + t.Integrity.ChecksumSignature) + case *Header_StorageGroup: + key = "StorageGroup" + val = fmt.Sprintf(`{DataSize=%d Hash=%02x Lifetime={Unit=%s Value=%d}}`, + t.StorageGroup.ValidationDataSize, + t.StorageGroup.ValidationHash, + t.StorageGroup.Lifetime.Unit, + t.StorageGroup.Lifetime.Value) + case *Header_PublicKey: + key = "PublicKey" + val = t.PublicKey.Value + default: + key = "Unknown" + val = t + } + + if _, err := fmt.Fprintf(dst, "\t\t- Type=%s\n\t\t Value=%v\n", key, val); err != nil { + return err + } + } + + // put payload + if _, err := fmt.Fprintf(dst, "\tPayload: %#v\n", obj.Payload); err != nil { + return err + } + + return nil +} From 5ae4e14bfaf9b83019e6bb71783f6a6d36779f6a Mon Sep 17 00:00:00 2001 From: Evgeniy Kulikov Date: Tue, 28 Apr 2020 16:32:19 +0300 Subject: [PATCH 07/68] object: test coverage for stringify method --- object/types_test.go | 181 +++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 181 insertions(+) create mode 100644 object/types_test.go diff --git a/object/types_test.go b/object/types_test.go new file mode 100644 index 0000000..50e4e0b --- /dev/null +++ b/object/types_test.go @@ -0,0 +1,181 @@ +package object + +import ( + "bytes" + "testing" + + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-api-go/storagegroup" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestStringify(t *testing.T) { + res := ` +Object: + SystemHeader: + - ID=7e0b9c6c-aabc-4985-949e-2680e577b48b + - CID=11111111111111111111111111111111 + - OwnerID=ALYeYC41emF6MrmUMc4a8obEPdgFhq9ran + - Version=1 + - PayloadLength=1 + - CreatedAt={UnixTime=1 Epoch=1} + UserHeaders: + - Type=Link + Value={Type=Child ID=7e0b9c6c-aabc-4985-949e-2680e577b48b} + - Type=Redirect + Value={CID=11111111111111111111111111111111 OID=7e0b9c6c-aabc-4985-949e-2680e577b48b} + - Type=UserHeader + Value={Key=test_key Val=test_value} + - Type=Transform + Value=Split + - Type=Tombstone + Value=MARKED + - Type=Token + Value={ID=7e0b9c6c-aabc-4985-949e-2680e577b48b OwnerID=ALYeYC41emF6MrmUMc4a8obEPdgFhq9ran Verb=Search Address=11111111111111111111111111111111/7e0b9c6c-aabc-4985-949e-2680e577b48b Created=1 ValidUntil=2 SessionKey=010203040506 Signature=010203040506} + - Type=HomoHash + Value=1111111111111111111111111111111111111111111111111111111111111111 + - Type=PayloadChecksum + Value=[1 2 3 4 5 6] + - Type=Integrity + Value={Checksum=010203040506 Signature=010203040506} + - Type=StorageGroup + Value={DataSize=5 Hash=31313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131313131 Lifetime={Unit=UnixTime Value=555}} + - Type=PublicKey + Value=[1 2 3 4 5 6] + Payload: []byte{0x1, 0x2, 0x3, 0x4, 0x5, 0x6, 0x7} +` + + key := test.DecodeKey(0) + + uid, err := refs.NewOwnerID(&key.PublicKey) + require.NoError(t, err) + + var oid refs.UUID + + require.NoError(t, oid.Parse("7e0b9c6c-aabc-4985-949e-2680e577b48b")) + + obj := &Object{ + SystemHeader: SystemHeader{ + Version: 1, + PayloadLength: 1, + ID: oid, + OwnerID: uid, + CID: CID{}, + CreatedAt: CreationPoint{ + UnixTime: 1, + Epoch: 1, + }, + }, + Payload: []byte{1, 2, 3, 4, 5, 6, 7}, + } + + // *Header_Link + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Link{ + Link: &Link{ID: oid, Type: Link_Child}, + }, + }) + + // *Header_Redirect + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Redirect{ + Redirect: &Address{ObjectID: oid, CID: CID{}}, + }, + }) + + // *Header_UserHeader + obj.Headers = append(obj.Headers, Header{ + Value: &Header_UserHeader{ + UserHeader: &UserHeader{ + Key: "test_key", + Value: "test_value", + }, + }, + }) + + // *Header_Transform + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Transform{ + Transform: &Transform{ + Type: Transform_Split, + }, + }, + }) + + // *Header_Tombstone + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Tombstone{ + Tombstone: &Tombstone{}, + }, + }) + + // *Header_Token + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Token{ + Token: &Token{ + Signature: []byte{1, 2, 3, 4, 5, 6}, + Token_Info: service.Token_Info{ + ID: oid, + OwnerID: uid, + Verb: service.Token_Info_Search, + Address: service.Address{ObjectID: oid, CID: refs.CID{}}, + Created: 1, + ValidUntil: 2, + SessionKey: []byte{1, 2, 3, 4, 5, 6}, + }, + }, + }, + }) + + // *Header_HomoHash + obj.Headers = append(obj.Headers, Header{ + Value: &Header_HomoHash{ + HomoHash: Hash{}, + }, + }) + + // *Header_PayloadChecksum + obj.Headers = append(obj.Headers, Header{ + Value: &Header_PayloadChecksum{ + PayloadChecksum: []byte{1, 2, 3, 4, 5, 6}, + }, + }) + + // *Header_Integrity + obj.Headers = append(obj.Headers, Header{ + Value: &Header_Integrity{ + Integrity: &IntegrityHeader{ + HeadersChecksum: []byte{1, 2, 3, 4, 5, 6}, + ChecksumSignature: []byte{1, 2, 3, 4, 5, 6}, + }, + }, + }) + + // *Header_StorageGroup + obj.Headers = append(obj.Headers, Header{ + Value: &Header_StorageGroup{ + StorageGroup: &storagegroup.StorageGroup{ + ValidationDataSize: 5, + ValidationHash: storagegroup.Hash{}, + Lifetime: &storagegroup.StorageGroup_Lifetime{ + Unit: storagegroup.StorageGroup_Lifetime_UnixTime, + Value: 555, + }, + }, + }, + }) + + // *Header_PublicKey + obj.Headers = append(obj.Headers, Header{ + Value: &Header_PublicKey{ + PublicKey: &PublicKey{Value: []byte{1, 2, 3, 4, 5, 6}}, + }, + }) + + buf := new(bytes.Buffer) + + require.NoError(t, Stringify(buf, obj)) + require.Equal(t, res, buf.String()) +} From 09f8ee52d0b15e808126227d349947b4d7a9687f Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 15:39:09 +0300 Subject: [PATCH 08/68] service: implement Raw field setter on RequestMetaHeader struct After recent changes Raw field is presented in RequestMetaHeader. There is a need to provide an interface of field getter/setter. This commit: * defines RawHeader interface of raw value container; * embeds RawHeader into MetaHeader interface; * implements Raw field setter on RequestMetaHeader. --- service/meta.go | 14 ++++++++++++++ service/meta_test.go | 10 ++++++++++ 2 files changed, 24 insertions(+) diff --git a/service/meta.go b/service/meta.go index 5e9886d..8602dca 100644 --- a/service/meta.go +++ b/service/meta.go @@ -25,6 +25,9 @@ type ( // VersionHeader allows get or set version of protocol request VersionHeader + + // RawHeader allows to get and set raw option of request + RawHeader } // EpochHeader interface gives possibility to get or set epoch in RPC Requests. @@ -39,6 +42,12 @@ type ( SetVersion(uint32) } + // RawHeader is an interface of the container of a boolean Raw value + RawHeader interface { + GetRaw() bool + SetRaw(bool) + } + // TTLCondition is closure, that allows to validate request with ttl. TTLCondition func(ttl uint32) error ) @@ -77,6 +86,11 @@ func (m *RequestMetaHeader) SetTTL(v uint32) { m.TTL = v } // SetEpoch sets Epoch to RequestMetaHeader. func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v } +// SetRaw is a Raw field setter. +func (m *RequestMetaHeader) SetRaw(raw bool) { + m.Raw = raw +} + // ResetMeta returns current value and sets RequestMetaHeader to empty value. func (m *RequestMetaHeader) ResetMeta() RequestMetaHeader { cp := *m diff --git a/service/meta_test.go b/service/meta_test.go index 083ccd6..388b6ce 100644 --- a/service/meta_test.go +++ b/service/meta_test.go @@ -102,3 +102,13 @@ func TestRequestMetaHeader_SetVersion(t *testing.T) { m.SetVersion(version) require.Equal(t, version, m.GetVersion()) } + +func TestRequestMetaHeader_SetRaw(t *testing.T) { + m := new(RequestMetaHeader) + + m.SetRaw(true) + require.True(t, m.GetRaw()) + + m.SetRaw(false) + require.False(t, m.GetRaw()) +} From 942bedb8ed92fbb6587a19cbcbc1f79377260080 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 16:02:40 +0300 Subject: [PATCH 09/68] service: implement Token field setter on RequestVerificationHeader After recent changes Token field is presented in RequestVerificationHeader. There is a need to provide an interface of field getter/setter. This commit: * defines TokenHeader interface of token value container; * implements Token field setter on RequestVerificationHeader. --- service/verify.go | 11 +++++++++++ service/verify_test.go | 14 ++++++++++++++ 2 files changed, 25 insertions(+) diff --git a/service/verify.go b/service/verify.go index ade13ef..182685d 100644 --- a/service/verify.go +++ b/service/verify.go @@ -27,6 +27,12 @@ type ( SetOwner(*ecdsa.PublicKey, []byte) GetLastPeer() (*ecdsa.PublicKey, error) } + + // TokenHeader is an interface of the container of a Token pointer value + TokenHeader interface { + GetToken() *Token + SetToken(*Token) + } ) const ( @@ -97,6 +103,11 @@ func (m *RequestVerificationHeader) GetLastPeer() (*ecdsa.PublicKey, error) { } } +// SetToken is a Token field setter. +func (m *RequestVerificationHeader) SetToken(token *Token) { + m.Token = token +} + func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeader_Signature, error) { sign, err := crypto.Sign(key, data) if err != nil { diff --git a/service/verify_test.go b/service/verify_test.go index ce333aa..c192f0f 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -188,3 +188,17 @@ func TestVerifyAndSignRequestHeaderWithoutCloning(t *testing.T) { require.Contains(t, buf.String(), "proto: don't know how to copy") } + +func TestRequestVerificationHeader_SetToken(t *testing.T) { + id, err := refs.NewUUID() + require.NoError(t, err) + + token := new(Token) + token.ID = id + + h := new(RequestVerificationHeader) + + h.SetToken(token) + + require.Equal(t, token, h.GetToken()) +} From d327d836c417f7971bcb3d5f7867f73ff6e1e3cf Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 16:58:07 +0300 Subject: [PATCH 10/68] service: define and implement Token field composing interface This commit: * defines SessionToken interface of Token field getters/setters group; * implements SessionToken on Token message. --- refs/types.go | 12 ++++ service/token.go | 125 ++++++++++++++++++++++++++++++++++++++++++ service/token_test.go | 88 +++++++++++++++++++++++++++++ 3 files changed, 225 insertions(+) create mode 100644 service/token.go create mode 100644 service/token_test.go diff --git a/refs/types.go b/refs/types.go index 117aa03..a29424e 100644 --- a/refs/types.go +++ b/refs/types.go @@ -37,6 +37,18 @@ type ( OwnerID chain.WalletAddress ) +// OwnerIDContainer is an interface of the container of an OwnerID value. +type OwnerIDContainer interface { + GetOwnerID() OwnerID + SetOwnerID(OwnerID) +} + +// AddressContainer is an interface of the container of object address value. +type AddressContainer interface { + GetAddress() Address + SetAddress(Address) +} + const ( // UUIDSize contains size of UUID. UUIDSize = 16 diff --git a/service/token.go b/service/token.go new file mode 100644 index 0000000..71ea655 --- /dev/null +++ b/service/token.go @@ -0,0 +1,125 @@ +package service + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" +) + +// VerbContainer is an interface of the container of a token verb value. +type VerbContainer interface { + GetVerb() Token_Info_Verb + SetVerb(Token_Info_Verb) +} + +// TokenIDContainer is an interface of the container of a token ID value. +type TokenIDContainer interface { + GetID() TokenID + SetID(TokenID) +} + +// CreationEpochContainer is an interface of the container of a creation epoch number. +type CreationEpochContainer interface { + CreationEpoch() uint64 + SetCreationEpoch(uint64) +} + +// ExpirationEpochContainer is an interface of the container of an expiration epoch number. +type ExpirationEpochContainer interface { + ExpirationEpoch() uint64 + SetExpirationEpoch(uint64) +} + +// SessionKeyContainer is an interface of the container of session key bytes. +type SessionKeyContainer interface { + GetSessionKey() []byte + SetSessionKey([]byte) +} + +// SignatureContainer is an interface of the container of signature bytes. +type SignatureContainer interface { + GetSignature() []byte + SetSignature([]byte) +} + +// SessionTokenInfo is an interface that determines the information scope of session token. +type SessionTokenInfo interface { + TokenIDContainer + refs.OwnerIDContainer + VerbContainer + refs.AddressContainer + CreationEpochContainer + ExpirationEpochContainer + SessionKeyContainer +} + +// SessionToken is an interface of token information and signature pair. +type SessionToken interface { + SessionTokenInfo + SignatureContainer +} + +var _ SessionToken = (*Token)(nil) + +// GetID is an ID field getter. +func (m Token_Info) GetID() TokenID { + return m.ID +} + +// SetID is an ID field setter. +func (m *Token_Info) SetID(id TokenID) { + m.ID = id +} + +// GetOwnerID is an OwnerID field getter. +func (m Token_Info) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *Token_Info) SetOwnerID(id OwnerID) { + m.OwnerID = id +} + +// SetVerb is a Verb field setter. +func (m *Token_Info) SetVerb(verb Token_Info_Verb) { + m.Verb = verb +} + +// GetAddress is an Address field getter. +func (m Token_Info) GetAddress() Address { + return m.Address +} + +// SetAddress is an Address field setter. +func (m *Token_Info) SetAddress(addr Address) { + m.Address = addr +} + +// CreationEpoch is a Created field getter. +func (m Token_Info) CreationEpoch() uint64 { + return m.Created +} + +// SetCreationEpoch is a Created field setter. +func (m *Token_Info) SetCreationEpoch(e uint64) { + m.Created = e +} + +// ExpirationEpoch is a ValidUntil field getter. +func (m Token_Info) ExpirationEpoch() uint64 { + return m.ValidUntil +} + +// SetExpirationEpoch is a ValidUntil field setter. +func (m *Token_Info) SetExpirationEpoch(e uint64) { + m.ValidUntil = e +} + +// SetSessionKey is a SessionKey field setter. +func (m *Token_Info) SetSessionKey(key []byte) { + m.SessionKey = key +} + +// SetSignature is a Signature field setter. +func (m *Token) SetSignature(sig []byte) { + m.Signature = sig +} diff --git a/service/token_test.go b/service/token_test.go new file mode 100644 index 0000000..1a55406 --- /dev/null +++ b/service/token_test.go @@ -0,0 +1,88 @@ +package service + +import ( + "crypto/rand" + "testing" + + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/stretchr/testify/require" +) + +func TestTokenGettersSetters(t *testing.T) { + var tok SessionToken = new(Token) + + { // ID + id, err := refs.NewUUID() + require.NoError(t, err) + + tok.SetID(id) + + require.Equal(t, id, tok.GetID()) + } + + { // OwnerID + ownerID := OwnerID{} + _, err := rand.Read(ownerID[:]) + require.NoError(t, err) + + tok.SetOwnerID(ownerID) + + require.Equal(t, ownerID, tok.GetOwnerID()) + } + + { // Verb + verb := Token_Info_Verb(3) + + tok.SetVerb(verb) + + require.Equal(t, verb, tok.GetVerb()) + } + + { // Address + addr := Address{} + _, err := rand.Read(addr.CID[:]) + require.NoError(t, err) + _, err = rand.Read(addr.ObjectID[:]) + require.NoError(t, err) + + tok.SetAddress(addr) + + require.Equal(t, addr, tok.GetAddress()) + } + + { // Created + e := uint64(5) + + tok.SetCreationEpoch(e) + + require.Equal(t, e, tok.CreationEpoch()) + } + + { // ValidUntil + e := uint64(5) + + tok.SetExpirationEpoch(e) + + require.Equal(t, e, tok.ExpirationEpoch()) + } + + { // SessionKey + key := make([]byte, 10) + _, err := rand.Read(key) + require.NoError(t, err) + + tok.SetSessionKey(key) + + require.Equal(t, key, tok.GetSessionKey()) + } + + { // Signature + sig := make([]byte, 10) + _, err := rand.Read(sig) + require.NoError(t, err) + + tok.SetSignature(sig) + + require.Equal(t, sig, tok.GetSignature()) + } +} From c6971d20042aef04f302cb14befe60e14a8eeb31 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 18:40:21 +0300 Subject: [PATCH 11/68] service: access Token message fields through getters and setters --- object/types.go | 16 ++++++++-------- object/types_test.go | 23 +++++++++++------------ object/verification.go | 2 +- object/verification_test.go | 10 +++------- service/verify_test.go | 2 +- session/store.go | 26 ++++++++++++-------------- 6 files changed, 36 insertions(+), 43 deletions(-) diff --git a/object/types.go b/object/types.go index 4acffbe..cbc50f5 100644 --- a/object/types.go +++ b/object/types.go @@ -334,14 +334,14 @@ func Stringify(dst io.Writer, obj *Object) error { val = fmt.Sprintf("{"+ "ID=%s OwnerID=%s Verb=%s Address=%s Created=%d ValidUntil=%d SessionKey=%02x Signature=%02x"+ "}", - t.Token.Token_Info.ID, - t.Token.Token_Info.OwnerID, - t.Token.Token_Info.Verb, - t.Token.Token_Info.Address, - t.Token.Token_Info.Created, - t.Token.Token_Info.ValidUntil, - t.Token.Token_Info.SessionKey, - t.Token.Signature) + t.Token.GetID(), + t.Token.GetOwnerID(), + t.Token.GetVerb(), + t.Token.GetAddress(), + t.Token.CreationEpoch(), + t.Token.ExpirationEpoch(), + t.Token.GetSessionKey(), + t.Token.GetSignature()) case *Header_HomoHash: key = "HomoHash" val = t.HomoHash diff --git a/object/types_test.go b/object/types_test.go index 50e4e0b..95f328b 100644 --- a/object/types_test.go +++ b/object/types_test.go @@ -111,21 +111,20 @@ Object: }, }) + token := new(Token) + token.SetID(oid) + token.SetOwnerID(uid) + token.SetVerb(service.Token_Info_Search) + token.SetAddress(Address{ObjectID: oid, CID: refs.CID{}}) + token.SetCreationEpoch(1) + token.SetExpirationEpoch(2) + token.SetSessionKey([]byte{1, 2, 3, 4, 5, 6}) + token.SetSignature([]byte{1, 2, 3, 4, 5, 6}) + // *Header_Token obj.Headers = append(obj.Headers, Header{ Value: &Header_Token{ - Token: &Token{ - Signature: []byte{1, 2, 3, 4, 5, 6}, - Token_Info: service.Token_Info{ - ID: oid, - OwnerID: uid, - Verb: service.Token_Info_Search, - Address: service.Address{ObjectID: oid, CID: refs.CID{}}, - Created: 1, - ValidUntil: 2, - SessionKey: []byte{1, 2, 3, 4, 5, 6}, - }, - }, + Token: token, }, }) diff --git a/object/verification.go b/object/verification.go index 5694316..0bcbc7c 100644 --- a/object/verification.go +++ b/object/verification.go @@ -85,7 +85,7 @@ func (m Object) Verify() error { } pubkey = pkh.Value.(*Header_PublicKey).PublicKey.Value } else { - pubkey = vh.Value.(*Header_Token).Token.SessionKey + pubkey = vh.Value.(*Header_Token).Token.GetSessionKey() } // Verify signature diff --git a/object/verification_test.go b/object/verification_test.go index 004f969..95a6d32 100644 --- a/object/verification_test.go +++ b/object/verification_test.go @@ -6,7 +6,6 @@ import ( "github.com/google/uuid" "github.com/nspcc-dev/neofs-api-go/container" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" @@ -77,12 +76,9 @@ func TestObject_Verify(t *testing.T) { dataPK := crypto.MarshalPublicKey(&sessionkey.PublicKey) signature, err = crypto.Sign(key, dataPK) - tok := &service.Token{ - Token_Info: service.Token_Info{ - SessionKey: dataPK, - }, - Signature: signature, - } + tok := new(Token) + tok.SetSignature(signature) + tok.SetSessionKey(dataPK) obj.AddHeader(&Header{Value: &Header_Token{Token: tok}}) // validation header is not last diff --git a/service/verify_test.go b/service/verify_test.go index c192f0f..107416b 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -194,7 +194,7 @@ func TestRequestVerificationHeader_SetToken(t *testing.T) { require.NoError(t, err) token := new(Token) - token.ID = id + token.SetID(id) h := new(RequestVerificationHeader) diff --git a/session/store.go b/session/store.go index e46afde..7c56c39 100644 --- a/session/store.go +++ b/session/store.go @@ -7,7 +7,6 @@ import ( "sync" "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -46,24 +45,23 @@ func (s *simpleStore) New(p TokenParams) *PToken { return nil } + token := new(Token) + token.SetID(tid) + token.SetOwnerID(p.OwnerID) + token.SetVerb(p.Verb) + token.SetAddress(p.Address) + token.SetCreationEpoch(p.FirstEpoch) + token.SetExpirationEpoch(p.LastEpoch) + token.SetSessionKey(crypto.MarshalPublicKey(&key.PublicKey)) + t := &PToken{ - mtx: new(sync.Mutex), - Token: Token{ - Token_Info: service.Token_Info{ - ID: tid, - OwnerID: p.OwnerID, - Verb: p.Verb, - Address: p.Address, - Created: p.FirstEpoch, - ValidUntil: p.LastEpoch, - SessionKey: crypto.MarshalPublicKey(&key.PublicKey), - }, - }, + mtx: new(sync.Mutex), + Token: *token, PrivateKey: key, } s.Lock() - s.tokens[t.ID] = t + s.tokens[tid] = t s.Unlock() return t From 82ffde253b2f117969ce2b17775a37e1b3a689c5 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 28 Apr 2020 19:03:15 +0300 Subject: [PATCH 12/68] service: implement Sign/Verify functions for SessionToken --- service/token.go | 87 +++++++++++++++++++++++++ service/token_test.go | 144 ++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 231 insertions(+) diff --git a/service/token.go b/service/token.go index 71ea655..b6d6435 100644 --- a/service/token.go +++ b/service/token.go @@ -1,7 +1,12 @@ package service import ( + "crypto/ecdsa" + "encoding/binary" + + "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" + crypto "github.com/nspcc-dev/neofs-crypto" ) // VerbContainer is an interface of the container of a token verb value. @@ -57,8 +62,13 @@ type SessionToken interface { SignatureContainer } +// ErrEmptyToken is raised when passed Token is nil. +const ErrEmptyToken = internal.Error("token is empty") + var _ SessionToken = (*Token)(nil) +var tokenEndianness = binary.BigEndian + // GetID is an ID field getter. func (m Token_Info) GetID() TokenID { return m.ID @@ -123,3 +133,80 @@ func (m *Token_Info) SetSessionKey(key []byte) { func (m *Token) SetSignature(sig []byte) { m.Signature = sig } + +// Returns byte slice that is used for creation/verification of the token signature. +func verificationTokenData(token SessionToken) []byte { + var sz int + + id := token.GetID() + sz += id.Size() + + ownerID := token.GetOwnerID() + sz += ownerID.Size() + + verb := uint32(token.GetVerb()) + sz += 4 + + addr := token.GetAddress() + sz += addr.CID.Size() + addr.ObjectID.Size() + + cEpoch := token.CreationEpoch() + sz += 8 + + fEpoch := token.ExpirationEpoch() + sz += 8 + + key := token.GetSessionKey() + sz += len(key) + + data := make([]byte, sz) + + var off int + + tokenEndianness.PutUint32(data, verb) + off += 4 + + tokenEndianness.PutUint64(data[off:], cEpoch) + off += 8 + + tokenEndianness.PutUint64(data[off:], fEpoch) + off += 8 + + off += copy(data[off:], id.Bytes()) + off += copy(data[off:], ownerID.Bytes()) + off += copy(data[off:], addr.CID.Bytes()) + off += copy(data[off:], addr.ObjectID.Bytes()) + off += copy(data[off:], key) + + return data +} + +// SignToken calculates and stores the signature of token information. +// +// If passed token is nil, ErrEmptyToken returns. +// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. +func SignToken(token SessionToken, key *ecdsa.PrivateKey) error { + if token == nil { + return ErrEmptyToken + } else if key == nil { + return crypto.ErrEmptyPrivateKey + } + + sig, err := crypto.Sign(key, verificationTokenData(token)) + if err != nil { + return err + } + + token.SetSignature(sig) + + return nil +} + +// VerifyTokenSignature checks if token was signed correctly. +func VerifyTokenSignature(token SessionToken, key *ecdsa.PublicKey) error { + return crypto.Verify( + key, + verificationTokenData(token), + token.GetSignature(), + ) +} diff --git a/service/token_test.go b/service/token_test.go index 1a55406..bd9c0b0 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -5,6 +5,8 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/refs" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" ) @@ -86,3 +88,145 @@ func TestTokenGettersSetters(t *testing.T) { require.Equal(t, sig, tok.GetSignature()) } } + +func TestSignToken(t *testing.T) { + // nil token + require.EqualError(t, + SignToken(nil, nil), + ErrEmptyToken.Error(), + ) + + var token SessionToken = new(Token) + + // nil key + require.EqualError(t, + SignToken(token, nil), + crypto.ErrEmptyPrivateKey.Error(), + ) + + // create private key for signing + sk := test.DecodeKey(0) + pk := &sk.PublicKey + + id := TokenID{} + _, err := rand.Read(id[:]) + require.NoError(t, err) + token.SetID(id) + + ownerID := OwnerID{} + _, err = rand.Read(ownerID[:]) + require.NoError(t, err) + token.SetOwnerID(ownerID) + + verb := Token_Info_Verb(1) + token.SetVerb(verb) + + addr := Address{} + _, err = rand.Read(addr.ObjectID[:]) + require.NoError(t, err) + _, err = rand.Read(addr.CID[:]) + require.NoError(t, err) + token.SetAddress(addr) + + cEpoch := uint64(1) + token.SetCreationEpoch(cEpoch) + + fEpoch := uint64(2) + token.SetExpirationEpoch(fEpoch) + + sessionKey := make([]byte, 10) + _, err = rand.Read(sessionKey[:]) + require.NoError(t, err) + token.SetSessionKey(sessionKey) + + // sign and verify token + require.NoError(t, SignToken(token, sk)) + require.NoError(t, VerifyTokenSignature(token, pk)) + + items := []struct { + corrupt func() + restore func() + }{ + { // ID + corrupt: func() { + id[0]++ + token.SetID(id) + }, + restore: func() { + id[0]-- + token.SetID(id) + }, + }, + { // Owner ID + corrupt: func() { + ownerID[0]++ + token.SetOwnerID(ownerID) + }, + restore: func() { + ownerID[0]-- + token.SetOwnerID(ownerID) + }, + }, + { // Verb + corrupt: func() { + token.SetVerb(verb + 1) + }, + restore: func() { + token.SetVerb(verb) + }, + }, + { // ObjectID + corrupt: func() { + addr.ObjectID[0]++ + token.SetAddress(addr) + }, + restore: func() { + addr.ObjectID[0]-- + token.SetAddress(addr) + }, + }, + { // CID + corrupt: func() { + addr.CID[0]++ + token.SetAddress(addr) + }, + restore: func() { + addr.CID[0]-- + token.SetAddress(addr) + }, + }, + { // Creation epoch + corrupt: func() { + token.SetCreationEpoch(cEpoch + 1) + }, + restore: func() { + token.SetCreationEpoch(cEpoch) + }, + }, + { // Expiration epoch + corrupt: func() { + token.SetExpirationEpoch(fEpoch + 1) + }, + restore: func() { + token.SetExpirationEpoch(fEpoch) + }, + }, + { // Session key + corrupt: func() { + sessionKey[0]++ + token.SetSessionKey(sessionKey) + }, + restore: func() { + sessionKey[0]-- + token.SetSessionKey(sessionKey) + }, + }, + } + + for _, v := range items { + v.corrupt() + require.Error(t, VerifyTokenSignature(token, pk)) + v.restore() + require.NoError(t, VerifyTokenSignature(token, pk)) + } +} From cce6566f1e48a22f8c15c0fb348cb8c19516ac0a Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 10:57:07 +0300 Subject: [PATCH 13/68] service: prevent NPE in VerifyTokenSignature function This commit adds next changes to VerifyTokenSignature: * returns ErrEmptyToken on nil token argument; * returns ErrEmptyPublicKey on nil public key argument. --- service/token.go | 9 +++++++++ service/token_test.go | 10 ++++++++++ 2 files changed, 19 insertions(+) diff --git a/service/token.go b/service/token.go index b6d6435..077e672 100644 --- a/service/token.go +++ b/service/token.go @@ -203,7 +203,16 @@ func SignToken(token SessionToken, key *ecdsa.PrivateKey) error { } // VerifyTokenSignature checks if token was signed correctly. +// +// If passed token is nil, ErrEmptyToken returns. +// If passed public key is nil, crypto.ErrEmptyPublicKey returns. func VerifyTokenSignature(token SessionToken, key *ecdsa.PublicKey) error { + if token == nil { + return ErrEmptyToken + } else if key == nil { + return crypto.ErrEmptyPublicKey + } + return crypto.Verify( key, verificationTokenData(token), diff --git a/service/token_test.go b/service/token_test.go index bd9c0b0..0b28084 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -96,6 +96,11 @@ func TestSignToken(t *testing.T) { ErrEmptyToken.Error(), ) + require.EqualError(t, + VerifyTokenSignature(nil, nil), + ErrEmptyToken.Error(), + ) + var token SessionToken = new(Token) // nil key @@ -104,6 +109,11 @@ func TestSignToken(t *testing.T) { crypto.ErrEmptyPrivateKey.Error(), ) + require.EqualError(t, + VerifyTokenSignature(token, nil), + crypto.ErrEmptyPublicKey.Error(), + ) + // create private key for signing sk := test.DecodeKey(0) pk := &sk.PublicKey From dfc2dd8a78ee2b63b43fb5fab221c7acf5bf71fd Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 11:52:05 +0300 Subject: [PATCH 14/68] session: replace PToken structure with PrivateToken interface In previous implementation PToken contained the full Token structure. Since private token is used for data signature only, storing unused fields of a user token is impractical. To emphasize the purpose of the private part of the session, it makes sense to provide the user of the session package with its interface. The interface will only provide the functionality of data signing with private session key. This commit: * removes PToken structure from session package; * defines PrivateToken interface of private session part; * adds the implementation of PrivateToken on unexported struct; * provides the constructor that generates session key internally. --- session/private.go | 37 +++++++++++++++++++++++++++++++++++++ session/private_test.go | 33 +++++++++++++++++++++++++++++++++ session/service.go | 4 ++-- session/store.go | 14 ++++++-------- session/types.go | 30 ++++++++++++------------------ 5 files changed, 90 insertions(+), 28 deletions(-) create mode 100644 session/private.go create mode 100644 session/private_test.go diff --git a/session/private.go b/session/private.go new file mode 100644 index 0000000..4d4f3c2 --- /dev/null +++ b/session/private.go @@ -0,0 +1,37 @@ +package session + +import ( + "crypto/ecdsa" + "crypto/rand" + + crypto "github.com/nspcc-dev/neofs-crypto" +) + +type pToken struct { + // private session token + sessionKey *ecdsa.PrivateKey +} + +// NewSessionPrivateToken creates PrivateToken instance. +// +// Returns non-nil error on key generation error. +func NewPrivateToken() (PrivateToken, error) { + sk, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader) + if err != nil { + return nil, err + } + + return &pToken{ + sessionKey: sk, + }, nil +} + +// Sign signs data with session private key. +func (t *pToken) Sign(data []byte) ([]byte, error) { + return crypto.Sign(t.sessionKey, data) +} + +// PublicKey returns a binary representation of the session public key. +func (t *pToken) PublicKey() []byte { + return crypto.MarshalPublicKey(&t.sessionKey.PublicKey) +} diff --git a/session/private_test.go b/session/private_test.go new file mode 100644 index 0000000..f0fb9f4 --- /dev/null +++ b/session/private_test.go @@ -0,0 +1,33 @@ +package session + +import ( + "crypto/rand" + "testing" + + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/stretchr/testify/require" +) + +func TestPrivateToken(t *testing.T) { + // create new private token + pToken, err := NewPrivateToken() + require.NoError(t, err) + + // generate data to sign + data := make([]byte, 10) + _, err = rand.Read(data) + require.NoError(t, err) + + // sign data via private token + sig, err := pToken.Sign(data) + require.NoError(t, err) + + // check signature + require.NoError(t, + crypto.Verify( + crypto.UnmarshalPublicKey(pToken.PublicKey()), + data, + sig, + ), + ) +} diff --git a/session/service.go b/session/service.go index 367aeb1..915abb4 100644 --- a/session/service.go +++ b/session/service.go @@ -17,10 +17,10 @@ type ( // TokenStore is a PToken storage manipulation interface. TokenStore interface { // New returns new token with specified parameters. - New(p TokenParams) *PToken + New(p TokenParams) PrivateToken // Fetch tries to fetch a token with specified id. - Fetch(id TokenID) *PToken + Fetch(id TokenID) PrivateToken // Remove removes token with id from store. Remove(id TokenID) diff --git a/session/store.go b/session/store.go index 7c56c39..440fbb5 100644 --- a/session/store.go +++ b/session/store.go @@ -13,7 +13,7 @@ import ( type simpleStore struct { *sync.RWMutex - tokens map[TokenID]*PToken + tokens map[TokenID]PrivateToken } // TODO get curve from neofs-crypto @@ -25,12 +25,12 @@ func defaultCurve() elliptic.Curve { func NewSimpleStore() TokenStore { return &simpleStore{ RWMutex: new(sync.RWMutex), - tokens: make(map[TokenID]*PToken), + tokens: make(map[TokenID]PrivateToken), } } // New returns new token with specified parameters. -func (s *simpleStore) New(p TokenParams) *PToken { +func (s *simpleStore) New(p TokenParams) PrivateToken { tid, err := refs.NewUUID() if err != nil { return nil @@ -54,10 +54,8 @@ func (s *simpleStore) New(p TokenParams) *PToken { token.SetExpirationEpoch(p.LastEpoch) token.SetSessionKey(crypto.MarshalPublicKey(&key.PublicKey)) - t := &PToken{ - mtx: new(sync.Mutex), - Token: *token, - PrivateKey: key, + t := &pToken{ + sessionKey: key, } s.Lock() @@ -68,7 +66,7 @@ func (s *simpleStore) New(p TokenParams) *PToken { } // Fetch tries to fetch a token with specified id. -func (s *simpleStore) Fetch(id TokenID) *PToken { +func (s *simpleStore) Fetch(id TokenID) PrivateToken { s.RLock() defer s.RUnlock() diff --git a/session/types.go b/session/types.go index e56373c..e7e4b2a 100644 --- a/session/types.go +++ b/session/types.go @@ -1,13 +1,9 @@ package session import ( - "crypto/ecdsa" - "sync" - "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" - crypto "github.com/nspcc-dev/neofs-crypto" ) type ( @@ -23,17 +19,20 @@ type ( Address = refs.Address // Verb is Token_Info_Verb type alias Verb = service.Token_Info_Verb - - // PToken is a wrapper around Token that allows to sign data - // and to do thread-safe manipulations. - PToken struct { - Token - - mtx *sync.Mutex - PrivateKey *ecdsa.PrivateKey - } ) +// PrivateToken is an interface of session private part. +type PrivateToken interface { + // PublicKey must return a binary representation of session public key. + PublicKey() []byte + + // Sign must return the signature of passed data. + // + // Resulting signature must be verified by crypto.Verify function + // with the session public key. + Sign([]byte) ([]byte, error) +} + const ( // ErrWrongFirstEpoch is raised when passed Token contains wrong first epoch. // First epoch is an epoch since token is valid @@ -58,8 +57,3 @@ const ( // ErrInvalidSignature is raised when wrong signature is passed to VerificationHeader.VerifyData(). ErrInvalidSignature = internal.Error("invalid signature") ) - -// SignData signs data with session private key. -func (t *PToken) SignData(data []byte) ([]byte, error) { - return crypto.Sign(t.PrivateKey, data) -} From 22265a9f038d27ea283cdf753feb46094b38cfe4 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:39:41 +0300 Subject: [PATCH 15/68] session: refactor token store This commit: * changes the set and signatures of TokenStore interface methods; * adds unit test for map storage. --- session/service.go | 21 ------------- session/store.go | 70 +++++++++++++------------------------------ session/store_test.go | 34 ++++++++++++++++++++- session/types.go | 20 +++++++++++++ 4 files changed, 74 insertions(+), 71 deletions(-) diff --git a/session/service.go b/session/service.go index 915abb4..ecbd6f5 100644 --- a/session/service.go +++ b/session/service.go @@ -13,27 +13,6 @@ type ( KeyStore interface { Get(ctx context.Context, id refs.OwnerID) ([]*ecdsa.PublicKey, error) } - - // TokenStore is a PToken storage manipulation interface. - TokenStore interface { - // New returns new token with specified parameters. - New(p TokenParams) PrivateToken - - // Fetch tries to fetch a token with specified id. - Fetch(id TokenID) PrivateToken - - // Remove removes token with id from store. - Remove(id TokenID) - } - - // TokenParams contains params to create new PToken. - TokenParams struct { - FirstEpoch uint64 - LastEpoch uint64 - Address Address - OwnerID OwnerID - Verb Verb - } ) // NewInitRequest returns new initialization CreateRequest from passed Token. diff --git a/session/store.go b/session/store.go index 440fbb5..fa3ca69 100644 --- a/session/store.go +++ b/session/store.go @@ -1,16 +1,11 @@ package session import ( - "crypto/ecdsa" "crypto/elliptic" - "crypto/rand" "sync" - - "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" ) -type simpleStore struct { +type mapTokenStore struct { *sync.RWMutex tokens map[TokenID]PrivateToken @@ -21,61 +16,38 @@ func defaultCurve() elliptic.Curve { return elliptic.P256() } -// NewSimpleStore creates simple token storage -func NewSimpleStore() TokenStore { - return &simpleStore{ +// NewMapTokenStore creates new PrivateTokenStore instance. +// +// The elements of the instance are stored in the map. +func NewMapTokenStore() PrivateTokenStore { + return &mapTokenStore{ RWMutex: new(sync.RWMutex), tokens: make(map[TokenID]PrivateToken), } } -// New returns new token with specified parameters. -func (s *simpleStore) New(p TokenParams) PrivateToken { - tid, err := refs.NewUUID() - if err != nil { - return nil - } - - key, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader) - if err != nil { - return nil - } - - if p.FirstEpoch > p.LastEpoch || p.OwnerID.Empty() { - return nil - } - - token := new(Token) - token.SetID(tid) - token.SetOwnerID(p.OwnerID) - token.SetVerb(p.Verb) - token.SetAddress(p.Address) - token.SetCreationEpoch(p.FirstEpoch) - token.SetExpirationEpoch(p.LastEpoch) - token.SetSessionKey(crypto.MarshalPublicKey(&key.PublicKey)) - - t := &pToken{ - sessionKey: key, - } - +// Store adds passed token to the map. +// +// Resulting error is always nil. +func (s *mapTokenStore) Store(id TokenID, token PrivateToken) error { s.Lock() - s.tokens[tid] = t + s.tokens[id] = token s.Unlock() - return t + return nil } -// Fetch tries to fetch a token with specified id. -func (s *simpleStore) Fetch(id TokenID) PrivateToken { +// Fetch returns the map element corresponding to the given key. +// +// Returns ErrPrivateTokenNotFound is there is no element in map. +func (s *mapTokenStore) Fetch(id TokenID) (PrivateToken, error) { s.RLock() defer s.RUnlock() - return s.tokens[id] -} + t, ok := s.tokens[id] + if !ok { + return nil, ErrPrivateTokenNotFound + } -// Remove removes token with id from store. -func (s *simpleStore) Remove(id TokenID) { - s.Lock() - delete(s.tokens, id) - s.Unlock() + return t, nil } diff --git a/session/store_test.go b/session/store_test.go index f51fb18..37d742e 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -1,3 +1,35 @@ package session -// TODO: write unit tests +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/stretchr/testify/require" +) + +func TestMapTokenStore(t *testing.T) { + // create new private token + pToken, err := NewPrivateToken() + require.NoError(t, err) + + // create map token store + s := NewMapTokenStore() + + // create new storage key + id, err := refs.NewUUID() + require.NoError(t, err) + + // ascertain that there is no record for the key + _, err = s.Fetch(id) + require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) + + // save private token record + require.NoError(t, s.Store(id, pToken)) + + // fetch private token by the key + res, err := s.Fetch(id) + require.NoError(t, err) + + // ascertain that returned token equals to initial + require.Equal(t, pToken, res) +} diff --git a/session/types.go b/session/types.go index e7e4b2a..bacc770 100644 --- a/session/types.go +++ b/session/types.go @@ -33,6 +33,26 @@ type PrivateToken interface { Sign([]byte) ([]byte, error) } +// PrivateTokenSource is an interface of private token storage with read access. +type PrivateTokenSource interface { + // Fetch must return the storage record corresponding to the passed key. + // + // Resulting error must be ErrPrivateTokenNotFound if there is no corresponding record. + Fetch(TokenID) (PrivateToken, error) +} + +// PrivateTokenStore is an interface of the storage of private tokens addressable by TokenID. +type PrivateTokenStore interface { + PrivateTokenSource + + // Store must save passed private token in the storage under the given key. + // + // Resulting error must be nil if private token was stored successfully. + Store(TokenID, PrivateToken) error +} + +const ErrPrivateTokenNotFound = internal.Error("private token not found") + const ( // ErrWrongFirstEpoch is raised when passed Token contains wrong first epoch. // First epoch is an epoch since token is valid From 701bbafcf11724216078739990363167d8f85cc6 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:44:35 +0300 Subject: [PATCH 16/68] session: change KeyStore documentation --- session/service.go | 15 --------------- session/types.go | 11 +++++++++++ 2 files changed, 11 insertions(+), 15 deletions(-) diff --git a/session/service.go b/session/service.go index ecbd6f5..6e293d3 100644 --- a/session/service.go +++ b/session/service.go @@ -1,20 +1,5 @@ package session -import ( - "context" - "crypto/ecdsa" - - "github.com/nspcc-dev/neofs-api-go/refs" -) - -type ( - // KeyStore is an interface that describes storage, - // that allows to fetch public keys by OwnerID. - KeyStore interface { - Get(ctx context.Context, id refs.OwnerID) ([]*ecdsa.PublicKey, error) - } -) - // NewInitRequest returns new initialization CreateRequest from passed Token. func NewInitRequest(t *Token) *CreateRequest { return &CreateRequest{Message: &CreateRequest_Init{Init: t}} diff --git a/session/types.go b/session/types.go index bacc770..435616a 100644 --- a/session/types.go +++ b/session/types.go @@ -1,6 +1,9 @@ package session import ( + "context" + "crypto/ecdsa" + "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" @@ -51,6 +54,14 @@ type PrivateTokenStore interface { Store(TokenID, PrivateToken) error } +// KeyStore is an interface of the storage of public keys addressable by OwnerID, +type KeyStore interface { + // Get must return the storage record corresponding to the passed key. + // + // Resulting error must be ErrKeyNotFound if there is no corresponding record. + Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error) +} + const ErrPrivateTokenNotFound = internal.Error("private token not found") const ( From f0867036fbd3818a1c7d2d38b5692fe73244260c Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:46:05 +0300 Subject: [PATCH 17/68] session: remove trivial defaultCurve function --- session/private.go | 3 ++- session/store.go | 6 ------ 2 files changed, 2 insertions(+), 7 deletions(-) diff --git a/session/private.go b/session/private.go index 4d4f3c2..a159227 100644 --- a/session/private.go +++ b/session/private.go @@ -2,6 +2,7 @@ package session import ( "crypto/ecdsa" + "crypto/elliptic" "crypto/rand" crypto "github.com/nspcc-dev/neofs-crypto" @@ -16,7 +17,7 @@ type pToken struct { // // Returns non-nil error on key generation error. func NewPrivateToken() (PrivateToken, error) { - sk, err := ecdsa.GenerateKey(defaultCurve(), rand.Reader) + sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err } diff --git a/session/store.go b/session/store.go index fa3ca69..5cd6314 100644 --- a/session/store.go +++ b/session/store.go @@ -1,7 +1,6 @@ package session import ( - "crypto/elliptic" "sync" ) @@ -11,11 +10,6 @@ type mapTokenStore struct { tokens map[TokenID]PrivateToken } -// TODO get curve from neofs-crypto -func defaultCurve() elliptic.Curve { - return elliptic.P256() -} - // NewMapTokenStore creates new PrivateTokenStore instance. // // The elements of the instance are stored in the map. From 79142ada0484e8e5e77b6c5f7c3981d215ab116d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:49:27 +0300 Subject: [PATCH 18/68] session: replace used type aliases to separate file --- session/alias.go | 15 +++++++++++++++ session/types.go | 17 ----------------- 2 files changed, 15 insertions(+), 17 deletions(-) create mode 100644 session/alias.go diff --git a/session/alias.go b/session/alias.go new file mode 100644 index 0000000..aa49d55 --- /dev/null +++ b/session/alias.go @@ -0,0 +1,15 @@ +package session + +import ( + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" +) + +// OwnerID is a type alias of OwnerID ref. +type OwnerID = refs.OwnerID + +// TokenID is a type alias of TokenID ref. +type TokenID = service.TokenID + +// Token is a type alias of Token. +type Token = service.Token diff --git a/session/types.go b/session/types.go index 435616a..a4a1643 100644 --- a/session/types.go +++ b/session/types.go @@ -5,23 +5,6 @@ import ( "crypto/ecdsa" "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" - "github.com/nspcc-dev/neofs-api-go/service" -) - -type ( - // ObjectID type alias. - ObjectID = refs.ObjectID - // OwnerID type alias. - OwnerID = refs.OwnerID - // TokenID type alias. - TokenID = refs.UUID - // Token type alias - Token = service.Token - // Address type alias - Address = refs.Address - // Verb is Token_Info_Verb type alias - Verb = service.Token_Info_Verb ) // PrivateToken is an interface of session private part. From 608f5781055a72f392b9cb47bd644ee04b0b1a02 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:50:52 +0300 Subject: [PATCH 19/68] session: removes unused errors --- session/types.go | 26 +------------------------- 1 file changed, 1 insertion(+), 25 deletions(-) diff --git a/session/types.go b/session/types.go index a4a1643..0f209c7 100644 --- a/session/types.go +++ b/session/types.go @@ -45,29 +45,5 @@ type KeyStore interface { Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error) } +// ErrPrivateTokenNotFound is raised when addressed private token was not found in storage. const ErrPrivateTokenNotFound = internal.Error("private token not found") - -const ( - // ErrWrongFirstEpoch is raised when passed Token contains wrong first epoch. - // First epoch is an epoch since token is valid - ErrWrongFirstEpoch = internal.Error("wrong first epoch") - - // ErrWrongLastEpoch is raised when passed Token contains wrong last epoch. - // Last epoch is an epoch until token is valid - ErrWrongLastEpoch = internal.Error("wrong last epoch") - - // ErrWrongOwner is raised when passed Token contains wrong OwnerID. - ErrWrongOwner = internal.Error("wrong owner") - - // ErrEmptyPublicKey is raised when passed Token contains wrong public key. - ErrEmptyPublicKey = internal.Error("empty public key") - - // ErrWrongObjectsCount is raised when passed Token contains wrong objects count. - ErrWrongObjectsCount = internal.Error("wrong objects count") - - // ErrWrongObjects is raised when passed Token contains wrong object ids. - ErrWrongObjects = internal.Error("wrong objects") - - // ErrInvalidSignature is raised when wrong signature is passed to VerificationHeader.VerifyData(). - ErrInvalidSignature = internal.Error("invalid signature") -) From ffd4338eb38bce519115ca9be3370bbb6dfa3a15 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 12:59:58 +0300 Subject: [PATCH 20/68] session: fix NewPrivateToken function doc --- session/private.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/session/private.go b/session/private.go index a159227..5a0ca03 100644 --- a/session/private.go +++ b/session/private.go @@ -13,7 +13,7 @@ type pToken struct { sessionKey *ecdsa.PrivateKey } -// NewSessionPrivateToken creates PrivateToken instance. +// NewPrivateToken creates PrivateToken instance. // // Returns non-nil error on key generation error. func NewPrivateToken() (PrivateToken, error) { From 4fa7360cd1cb4abce7b8e02c86b0eac8a7395f05 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 29 Apr 2020 14:11:19 +0300 Subject: [PATCH 21/68] session: support the expiration of private tokens All sessions in NeoFS has limited in epochs lifetime. There is a need to limit the lifetime of private session tokens. This commmit: * extends PrivateToken interface with Expired method; * defines EpochLifetimeStore interface with RemoveExpired method and embeds it to PrivateTokenStore interface; * adds epoch value parameter to private token constructor. --- session/private.go | 11 +++++-- session/private_test.go | 19 ++++++++++++- session/store.go | 17 +++++++++++ session/store_test.go | 63 ++++++++++++++++++++++++++++++++++++++++- session/types.go | 10 +++++++ 5 files changed, 116 insertions(+), 4 deletions(-) diff --git a/session/private.go b/session/private.go index 5a0ca03..8ebce81 100644 --- a/session/private.go +++ b/session/private.go @@ -11,12 +11,14 @@ import ( type pToken struct { // private session token sessionKey *ecdsa.PrivateKey + // last epoch of the lifetime + validUntil uint64 } -// NewPrivateToken creates PrivateToken instance. +// NewPrivateToken creates PrivateToken instance that expires after passed epoch. // // Returns non-nil error on key generation error. -func NewPrivateToken() (PrivateToken, error) { +func NewPrivateToken(validUntil uint64) (PrivateToken, error) { sk, err := ecdsa.GenerateKey(elliptic.P256(), rand.Reader) if err != nil { return nil, err @@ -24,6 +26,7 @@ func NewPrivateToken() (PrivateToken, error) { return &pToken{ sessionKey: sk, + validUntil: validUntil, }, nil } @@ -36,3 +39,7 @@ func (t *pToken) Sign(data []byte) ([]byte, error) { func (t *pToken) PublicKey() []byte { return crypto.MarshalPublicKey(&t.sessionKey.PublicKey) } + +func (t *pToken) Expired(epoch uint64) bool { + return t.validUntil < epoch +} diff --git a/session/private_test.go b/session/private_test.go index f0fb9f4..7963afb 100644 --- a/session/private_test.go +++ b/session/private_test.go @@ -10,7 +10,7 @@ import ( func TestPrivateToken(t *testing.T) { // create new private token - pToken, err := NewPrivateToken() + pToken, err := NewPrivateToken(0) require.NoError(t, err) // generate data to sign @@ -31,3 +31,20 @@ func TestPrivateToken(t *testing.T) { ), ) } + +func TestPToken_Expired(t *testing.T) { + e := uint64(10) + + var token PrivateToken = &pToken{ + validUntil: e, + } + + // must not be expired in the epoch before last + require.False(t, token.Expired(e-1)) + + // must not be expired in the last epoch + require.False(t, token.Expired(e)) + + // must be expired in the epoch after last + require.True(t, token.Expired(e+1)) +} diff --git a/session/store.go b/session/store.go index 5cd6314..79998c7 100644 --- a/session/store.go +++ b/session/store.go @@ -45,3 +45,20 @@ func (s *mapTokenStore) Fetch(id TokenID) (PrivateToken, error) { return t, nil } + +// RemoveExpired removes all the map elements that are expired in the passed epoch. +// +// Resulting error is always nil. +func (s *mapTokenStore) RemoveExpired(epoch uint64) error { + s.Lock() + + for key, token := range s.tokens { + if token.Expired(epoch) { + delete(s.tokens, key) + } + } + + s.Unlock() + + return nil +} diff --git a/session/store_test.go b/session/store_test.go index 37d742e..123b103 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -9,7 +9,7 @@ import ( func TestMapTokenStore(t *testing.T) { // create new private token - pToken, err := NewPrivateToken() + pToken, err := NewPrivateToken(0) require.NoError(t, err) // create map token store @@ -33,3 +33,64 @@ func TestMapTokenStore(t *testing.T) { // ascertain that returned token equals to initial require.Equal(t, pToken, res) } + +func TestMapTokenStore_RemoveExpired(t *testing.T) { + // create some epoch number + e1 := uint64(1) + + // create private token that expires after e1 + tok1, err := NewPrivateToken(e1) + require.NoError(t, err) + + // create some greater than e1 epoch number + e2 := e1 + 1 + + // create private token that expires after e2 + tok2, err := NewPrivateToken(e2) + require.NoError(t, err) + + // create token store instance + s := NewMapTokenStore() + + // create storage keys for tokens + id1, err := refs.NewUUID() + require.NoError(t, err) + id2, err := refs.NewUUID() + require.NoError(t, err) + + assertPresence := func(ids ...TokenID) { + for i := range ids { + _, err = s.Fetch(ids[i]) + require.NoError(t, err) + } + } + + assertAbsence := func(ids ...TokenID) { + for i := range ids { + _, err = s.Fetch(ids[i]) + require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) + } + } + + // store both tokens + require.NoError(t, s.Store(id1, tok1)) + require.NoError(t, s.Store(id2, tok2)) + + // ascertain that both tokens are available + assertPresence(id1, id2) + + // perform cleaning for epoch in which both tokens are not expired + require.NoError(t, s.RemoveExpired(e1)) + + // ascertain that both tokens are still available + assertPresence(id1, id2) + + // perform cleaning for epoch greater than e1 and not greater than e2 + require.NoError(t, s.RemoveExpired(e1+1)) + + // ascertain that tok1 was removed + assertAbsence(id1) + + // ascertain that tok2 was not removed + assertPresence(id2) +} diff --git a/session/types.go b/session/types.go index 0f209c7..c890aaf 100644 --- a/session/types.go +++ b/session/types.go @@ -17,6 +17,9 @@ type PrivateToken interface { // Resulting signature must be verified by crypto.Verify function // with the session public key. Sign([]byte) ([]byte, error) + + // Expired must return true if and only if private token is expired in the given epoch number. + Expired(uint64) bool } // PrivateTokenSource is an interface of private token storage with read access. @@ -27,9 +30,16 @@ type PrivateTokenSource interface { Fetch(TokenID) (PrivateToken, error) } +// EpochLifetimeStore is an interface of the storage of elements that lifetime is limited by NeoFS epoch. +type EpochLifetimeStore interface { + // RemoveExpired must remove all elements that are expired in the given epoch. + RemoveExpired(uint64) error +} + // PrivateTokenStore is an interface of the storage of private tokens addressable by TokenID. type PrivateTokenStore interface { PrivateTokenSource + EpochLifetimeStore // Store must save passed private token in the storage under the given key. // From fc177c4ce3828b56bb7eb4f562629ccd12b4355d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 13:04:10 +0300 Subject: [PATCH 22/68] service: change constant errors This commit: * moves defined errors to a separate file; * renames ErrEmptyToken to ErrNilToken; * merges ErrZeroTTL and ErrIncorrectTTL into single ErrInvalidTTL. --- service/errors.go | 18 ++++++++++++++++++ service/meta.go | 13 ++----------- service/meta_test.go | 4 ++-- service/token.go | 12 ++++-------- service/token_test.go | 4 ++-- service/verify.go | 17 +++-------------- 6 files changed, 31 insertions(+), 37 deletions(-) create mode 100644 service/errors.go diff --git a/service/errors.go b/service/errors.go new file mode 100644 index 0000000..4aefb4e --- /dev/null +++ b/service/errors.go @@ -0,0 +1,18 @@ +package service + +import "github.com/nspcc-dev/neofs-api-go/internal" + +// ErrNilToken is returned by functions that expect a non-nil token argument, but received nil. +const ErrNilToken = internal.Error("token is nil") + +// ErrInvalidTTL means that the TTL value does not satisfy a specific criterion. +const ErrInvalidTTL = internal.Error("invalid TTL value") + +// ErrInvalidPublicKeyBytes means that the public key could not be unmarshaled. +const ErrInvalidPublicKeyBytes = internal.Error("cannot load public key") + +// ErrCannotFindOwner is raised when signatures empty in GetOwner. +const ErrCannotFindOwner = internal.Error("cannot find owner public key") + +// ErrWrongOwner is raised when passed OwnerID not equal to present PublicKey +const ErrWrongOwner = internal.Error("wrong owner") diff --git a/service/meta.go b/service/meta.go index 8602dca..ea1a83d 100644 --- a/service/meta.go +++ b/service/meta.go @@ -1,7 +1,6 @@ package service import ( - "github.com/nspcc-dev/neofs-api-go/internal" "github.com/pkg/errors" "google.golang.org/grpc/codes" "google.golang.org/grpc/status" @@ -63,14 +62,6 @@ const ( SingleForwardingTTL ) -const ( - // ErrZeroTTL is raised when zero ttl is passed. - ErrZeroTTL = internal.Error("zero ttl") - - // ErrIncorrectTTL is raised when NonForwardingTTL is passed and NodeRole != InnerRingNode. - ErrIncorrectTTL = internal.Error("incorrect ttl") -) - // SetVersion sets protocol version to ResponseMetaHeader. func (m *ResponseMetaHeader) SetVersion(v uint32) { m.Version = v } @@ -105,7 +96,7 @@ func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { *m = v } func IRNonForwarding(role NodeRole) TTLCondition { return func(ttl uint32) error { if ttl == NonForwardingTTL && role != InnerRingNode { - return ErrIncorrectTTL + return ErrInvalidTTL } return nil @@ -117,7 +108,7 @@ func ProcessRequestTTL(req MetaHeader, cond ...TTLCondition) error { ttl := req.GetTTL() if ttl == ZeroTTL { - return status.New(codes.InvalidArgument, ErrZeroTTL.Error()).Err() + return status.New(codes.InvalidArgument, ErrInvalidTTL.Error()).Err() } for i := range cond { diff --git a/service/meta_test.go b/service/meta_test.go index 388b6ce..de77ac8 100644 --- a/service/meta_test.go +++ b/service/meta_test.go @@ -26,13 +26,13 @@ func TestMetaRequest(t *testing.T) { }, { code: codes.InvalidArgument, - msg: ErrIncorrectTTL.Error(), + msg: ErrInvalidTTL.Error(), name: "direct to storage node", handler: IRNonForwarding(StorageNode), RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, }, { - msg: ErrZeroTTL.Error(), + msg: ErrInvalidTTL.Error(), code: codes.InvalidArgument, name: "zero ttl", handler: IRNonForwarding(StorageNode), diff --git a/service/token.go b/service/token.go index 077e672..ece44c2 100644 --- a/service/token.go +++ b/service/token.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "encoding/binary" - "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -62,9 +61,6 @@ type SessionToken interface { SignatureContainer } -// ErrEmptyToken is raised when passed Token is nil. -const ErrEmptyToken = internal.Error("token is empty") - var _ SessionToken = (*Token)(nil) var tokenEndianness = binary.BigEndian @@ -183,11 +179,11 @@ func verificationTokenData(token SessionToken) []byte { // SignToken calculates and stores the signature of token information. // -// If passed token is nil, ErrEmptyToken returns. +// If passed token is nil, ErrNilToken returns. // If passed private key is nil, crypto.ErrEmptyPrivateKey returns. func SignToken(token SessionToken, key *ecdsa.PrivateKey) error { if token == nil { - return ErrEmptyToken + return ErrNilToken } else if key == nil { return crypto.ErrEmptyPrivateKey } @@ -204,11 +200,11 @@ func SignToken(token SessionToken, key *ecdsa.PrivateKey) error { // VerifyTokenSignature checks if token was signed correctly. // -// If passed token is nil, ErrEmptyToken returns. +// If passed token is nil, ErrNilToken returns. // If passed public key is nil, crypto.ErrEmptyPublicKey returns. func VerifyTokenSignature(token SessionToken, key *ecdsa.PublicKey) error { if token == nil { - return ErrEmptyToken + return ErrNilToken } else if key == nil { return crypto.ErrEmptyPublicKey } diff --git a/service/token_test.go b/service/token_test.go index 0b28084..1e02f46 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -93,12 +93,12 @@ func TestSignToken(t *testing.T) { // nil token require.EqualError(t, SignToken(nil, nil), - ErrEmptyToken.Error(), + ErrNilToken.Error(), ) require.EqualError(t, VerifyTokenSignature(nil, nil), - ErrEmptyToken.Error(), + ErrNilToken.Error(), ) var token SessionToken = new(Token) diff --git a/service/verify.go b/service/verify.go index 182685d..7ac3cf3 100644 --- a/service/verify.go +++ b/service/verify.go @@ -35,17 +35,6 @@ type ( } ) -const ( - // ErrCannotLoadPublicKey is raised when cannot unmarshal public key from RequestVerificationHeader_Sign. - ErrCannotLoadPublicKey = internal.Error("cannot load public key") - - // ErrCannotFindOwner is raised when signatures empty in GetOwner. - ErrCannotFindOwner = internal.Error("cannot find owner public key") - - // ErrWrongOwner is raised when passed OwnerID not equal to present PublicKey - ErrWrongOwner = internal.Error("wrong owner") -) - // SetSignatures replaces signatures stored in RequestVerificationHeader. func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) { m.Signatures = signatures @@ -81,7 +70,7 @@ func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) { return key, nil } - return nil, ErrCannotLoadPublicKey + return nil, ErrInvalidPublicKeyBytes } // GetLastPeer tries to get last peer public key from signatures. @@ -99,7 +88,7 @@ func (m *RequestVerificationHeader) GetLastPeer() (*ecdsa.PublicKey, error) { return key, nil } - return nil, ErrCannotLoadPublicKey + return nil, ErrInvalidPublicKeyBytes } } @@ -190,7 +179,7 @@ func VerifyRequestHeader(msg VerifiableRequest) error { key := crypto.UnmarshalPublicKey(peer) if key == nil { - return errors.Wrapf(ErrCannotLoadPublicKey, "%d: %02x", i, peer) + return errors.Wrapf(ErrInvalidPublicKeyBytes, "%d: %02x", i, peer) } if size := msg.Size(); size <= cap(data) { From b785eb710a157cea40c85d7c993826c43e830584 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 13:38:27 +0300 Subject: [PATCH 23/68] service: transfer TTL code to a separate file --- service/meta.go | 67 +----------------------------- service/meta_test.go | 83 ------------------------------------- service/ttl.go | 73 ++++++++++++++++++++++++++++++++ service/ttl_test.go | 99 ++++++++++++++++++++++++++++++++++++++++++++ 4 files changed, 174 insertions(+), 148 deletions(-) create mode 100644 service/ttl.go create mode 100644 service/ttl_test.go diff --git a/service/meta.go b/service/meta.go index ea1a83d..2675b79 100644 --- a/service/meta.go +++ b/service/meta.go @@ -1,11 +1,5 @@ package service -import ( - "github.com/pkg/errors" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" -) - type ( // MetaHeader contains meta information of request. // It provides methods to get or set meta information meta header. @@ -15,9 +9,8 @@ type ( ResetMeta() RequestMetaHeader RestoreMeta(RequestMetaHeader) - // TTLRequest to verify and update ttl requests. - GetTTL() uint32 - SetTTL(uint32) + // TTLHeader allows to get and set TTL value of request. + TTLHeader // EpochHeader gives possibility to get or set epoch in RPC Requests. EpochHeader @@ -46,20 +39,6 @@ type ( GetRaw() bool SetRaw(bool) } - - // TTLCondition is closure, that allows to validate request with ttl. - TTLCondition func(ttl uint32) error -) - -const ( - // ZeroTTL is empty ttl, should produce ErrZeroTTL. - ZeroTTL = iota - - // NonForwardingTTL is a ttl that allows direct connections only. - NonForwardingTTL - - // SingleForwardingTTL is a ttl that allows connections through another node. - SingleForwardingTTL ) // SetVersion sets protocol version to ResponseMetaHeader. @@ -71,9 +50,6 @@ func (m *ResponseMetaHeader) SetEpoch(v uint64) { m.Epoch = v } // SetVersion sets protocol version to RequestMetaHeader. func (m *RequestMetaHeader) SetVersion(v uint32) { m.Version = v } -// SetTTL sets TTL to RequestMetaHeader. -func (m *RequestMetaHeader) SetTTL(v uint32) { m.TTL = v } - // SetEpoch sets Epoch to RequestMetaHeader. func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v } @@ -91,42 +67,3 @@ func (m *RequestMetaHeader) ResetMeta() RequestMetaHeader { // RestoreMeta sets current RequestMetaHeader to passed value. func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { *m = v } - -// IRNonForwarding condition that allows NonForwardingTTL only for IR -func IRNonForwarding(role NodeRole) TTLCondition { - return func(ttl uint32) error { - if ttl == NonForwardingTTL && role != InnerRingNode { - return ErrInvalidTTL - } - - return nil - } -} - -// ProcessRequestTTL validates and update ttl requests. -func ProcessRequestTTL(req MetaHeader, cond ...TTLCondition) error { - ttl := req.GetTTL() - - if ttl == ZeroTTL { - return status.New(codes.InvalidArgument, ErrInvalidTTL.Error()).Err() - } - - for i := range cond { - if cond[i] == nil { - continue - } - - // check specific condition: - if err := cond[i](ttl); err != nil { - if st, ok := status.FromError(errors.Cause(err)); ok { - return st.Err() - } - - return status.New(codes.InvalidArgument, err.Error()).Err() - } - } - - req.SetTTL(ttl - 1) - - return nil -} diff --git a/service/meta_test.go b/service/meta_test.go index de77ac8..fb7fb17 100644 --- a/service/meta_test.go +++ b/service/meta_test.go @@ -3,92 +3,9 @@ package service import ( "testing" - "github.com/pkg/errors" "github.com/stretchr/testify/require" - "google.golang.org/grpc/codes" - "google.golang.org/grpc/status" ) -type mockedRequest struct { - msg string - name string - code codes.Code - handler TTLCondition - RequestMetaHeader -} - -func TestMetaRequest(t *testing.T) { - tests := []mockedRequest{ - { - name: "direct to ir node", - handler: IRNonForwarding(InnerRingNode), - RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, - }, - { - code: codes.InvalidArgument, - msg: ErrInvalidTTL.Error(), - name: "direct to storage node", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, - }, - { - msg: ErrInvalidTTL.Error(), - code: codes.InvalidArgument, - name: "zero ttl", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: ZeroTTL}, - }, - { - name: "default to ir node", - handler: IRNonForwarding(InnerRingNode), - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - }, - { - name: "default to storage node", - handler: IRNonForwarding(StorageNode), - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - }, - { - msg: "not found", - code: codes.NotFound, - name: "custom status error", - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - handler: func(_ uint32) error { return status.Error(codes.NotFound, "not found") }, - }, - { - msg: "not found", - code: codes.NotFound, - name: "custom wrapped status error", - RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, - handler: func(_ uint32) error { - err := status.Error(codes.NotFound, "not found") - err = errors.Wrap(err, "some error context") - err = errors.Wrap(err, "another error context") - return err - }, - }, - } - - for i := range tests { - tt := tests[i] - t.Run(tt.name, func(t *testing.T) { - before := tt.GetTTL() - err := ProcessRequestTTL(&tt, tt.handler) - if tt.msg != "" { - require.Errorf(t, err, tt.msg) - - state, ok := status.FromError(err) - require.True(t, ok) - require.Equal(t, tt.code, state.Code()) - require.Equal(t, tt.msg, state.Message()) - } else { - require.NoError(t, err) - require.NotEqualf(t, before, tt.GetTTL(), "ttl should be changed: %d vs %d", before, tt.GetTTL()) - } - }) - } -} - func TestRequestMetaHeader_SetEpoch(t *testing.T) { m := new(ResponseMetaHeader) epoch := uint64(3) diff --git a/service/ttl.go b/service/ttl.go new file mode 100644 index 0000000..f069f54 --- /dev/null +++ b/service/ttl.go @@ -0,0 +1,73 @@ +package service + +import ( + "github.com/pkg/errors" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +// TTLHeader is an interface of the container of a numerical TTL value. +type TTLHeader interface { + GetTTL() uint32 + SetTTL(uint32) +} + +// TTLCondition is a function type that used to verify that TTL value match a specific criterion. +// Nil error indicates compliance with the criterion. +type TTLCondition func(ttl uint32) error + +// TTL constants. +const ( + // ZeroTTL is an upper bound of invalid TTL values. + ZeroTTL = iota + + // NonForwardingTTL is a TTL value that does not imply a request forwarding. + NonForwardingTTL + + // SingleForwardingTTL is a TTL value that imply potential forwarding with NonForwardingTTL. + SingleForwardingTTL +) + +// SetTTL is a TTL field setter. +func (m *RequestMetaHeader) SetTTL(v uint32) { + m.TTL = v +} + +// IRNonForwarding condition that allows NonForwardingTTL only for IR. +func IRNonForwarding(role NodeRole) TTLCondition { + return func(ttl uint32) error { + if ttl == NonForwardingTTL && role != InnerRingNode { + return ErrInvalidTTL + } + + return nil + } +} + +// ProcessRequestTTL validates and updates requests with TTL. +func ProcessRequestTTL(req TTLHeader, cond ...TTLCondition) error { + ttl := req.GetTTL() + + if ttl == ZeroTTL { + return status.New(codes.InvalidArgument, ErrInvalidTTL.Error()).Err() + } + + for i := range cond { + if cond[i] == nil { + continue + } + + // check specific condition: + if err := cond[i](ttl); err != nil { + if st, ok := status.FromError(errors.Cause(err)); ok { + return st.Err() + } + + return status.New(codes.InvalidArgument, err.Error()).Err() + } + } + + req.SetTTL(ttl - 1) + + return nil +} diff --git a/service/ttl_test.go b/service/ttl_test.go new file mode 100644 index 0000000..1c982f5 --- /dev/null +++ b/service/ttl_test.go @@ -0,0 +1,99 @@ +package service + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + "google.golang.org/grpc/codes" + "google.golang.org/grpc/status" +) + +type mockedRequest struct { + msg string + name string + code codes.Code + handler TTLCondition + RequestMetaHeader +} + +func TestMetaRequest(t *testing.T) { + tests := []mockedRequest{ + { + name: "direct to ir node", + handler: IRNonForwarding(InnerRingNode), + RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, + }, + { + code: codes.InvalidArgument, + msg: ErrInvalidTTL.Error(), + name: "direct to storage node", + handler: IRNonForwarding(StorageNode), + RequestMetaHeader: RequestMetaHeader{TTL: NonForwardingTTL}, + }, + { + msg: ErrInvalidTTL.Error(), + code: codes.InvalidArgument, + name: "zero ttl", + handler: IRNonForwarding(StorageNode), + RequestMetaHeader: RequestMetaHeader{TTL: ZeroTTL}, + }, + { + name: "default to ir node", + handler: IRNonForwarding(InnerRingNode), + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + }, + { + name: "default to storage node", + handler: IRNonForwarding(StorageNode), + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + }, + { + msg: "not found", + code: codes.NotFound, + name: "custom status error", + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + handler: func(_ uint32) error { return status.Error(codes.NotFound, "not found") }, + }, + { + msg: "not found", + code: codes.NotFound, + name: "custom wrapped status error", + RequestMetaHeader: RequestMetaHeader{TTL: SingleForwardingTTL}, + handler: func(_ uint32) error { + err := status.Error(codes.NotFound, "not found") + err = errors.Wrap(err, "some error context") + err = errors.Wrap(err, "another error context") + return err + }, + }, + } + + for i := range tests { + tt := tests[i] + t.Run(tt.name, func(t *testing.T) { + before := tt.GetTTL() + err := ProcessRequestTTL(&tt, tt.handler) + if tt.msg != "" { + require.Errorf(t, err, tt.msg) + + state, ok := status.FromError(err) + require.True(t, ok) + require.Equal(t, tt.code, state.Code()) + require.Equal(t, tt.msg, state.Message()) + } else { + require.NoError(t, err) + require.NotEqualf(t, before, tt.GetTTL(), "ttl should be changed: %d vs %d", before, tt.GetTTL()) + } + }) + } +} + +func TestRequestMetaHeader_SetTTL(t *testing.T) { + m := new(RequestMetaHeader) + ttl := uint32(3) + + m.SetTTL(ttl) + + require.Equal(t, ttl, m.GetTTL()) +} From 8270245455f013f55c4081988ca64408301e429d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 14:00:25 +0300 Subject: [PATCH 24/68] service: transfer public types to a separate file --- service/alias.go | 12 +++- service/role.go | 3 - service/token.go | 56 ----------------- service/ttl.go | 4 -- service/types.go | 161 +++++++++++++++++++++++++++++++++++++++++++++++ 5 files changed, 170 insertions(+), 66 deletions(-) create mode 100644 service/types.go diff --git a/service/alias.go b/service/alias.go index 6c22ece..9a40702 100644 --- a/service/alias.go +++ b/service/alias.go @@ -4,11 +4,17 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" ) -// TokenID is type alias of UUID ref. +// TokenID is a type alias of UUID ref. type TokenID = refs.UUID -// OwnerID is type alias of OwnerID ref. +// OwnerID is a type alias of OwnerID ref. type OwnerID = refs.OwnerID -// Address is type alias of Address ref. +// Address is a type alias of Address ref. type Address = refs.Address + +// AddressContainer is a type alias of refs.AddressContainer. +type AddressContainer = refs.AddressContainer + +// OwnerIDContainer is a type alias of refs.OwnerIDContainer. +type OwnerIDContainer = refs.OwnerIDContainer diff --git a/service/role.go b/service/role.go index 53bcdf5..4c405c1 100644 --- a/service/role.go +++ b/service/role.go @@ -1,8 +1,5 @@ package service -// NodeRole to identify in Bootstrap service. -type NodeRole int32 - const ( _ NodeRole = iota // InnerRingNode that work like IR node. diff --git a/service/token.go b/service/token.go index ece44c2..2aa159b 100644 --- a/service/token.go +++ b/service/token.go @@ -4,65 +4,9 @@ import ( "crypto/ecdsa" "encoding/binary" - "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" ) -// VerbContainer is an interface of the container of a token verb value. -type VerbContainer interface { - GetVerb() Token_Info_Verb - SetVerb(Token_Info_Verb) -} - -// TokenIDContainer is an interface of the container of a token ID value. -type TokenIDContainer interface { - GetID() TokenID - SetID(TokenID) -} - -// CreationEpochContainer is an interface of the container of a creation epoch number. -type CreationEpochContainer interface { - CreationEpoch() uint64 - SetCreationEpoch(uint64) -} - -// ExpirationEpochContainer is an interface of the container of an expiration epoch number. -type ExpirationEpochContainer interface { - ExpirationEpoch() uint64 - SetExpirationEpoch(uint64) -} - -// SessionKeyContainer is an interface of the container of session key bytes. -type SessionKeyContainer interface { - GetSessionKey() []byte - SetSessionKey([]byte) -} - -// SignatureContainer is an interface of the container of signature bytes. -type SignatureContainer interface { - GetSignature() []byte - SetSignature([]byte) -} - -// SessionTokenInfo is an interface that determines the information scope of session token. -type SessionTokenInfo interface { - TokenIDContainer - refs.OwnerIDContainer - VerbContainer - refs.AddressContainer - CreationEpochContainer - ExpirationEpochContainer - SessionKeyContainer -} - -// SessionToken is an interface of token information and signature pair. -type SessionToken interface { - SessionTokenInfo - SignatureContainer -} - -var _ SessionToken = (*Token)(nil) - var tokenEndianness = binary.BigEndian // GetID is an ID field getter. diff --git a/service/ttl.go b/service/ttl.go index f069f54..c79cc85 100644 --- a/service/ttl.go +++ b/service/ttl.go @@ -12,10 +12,6 @@ type TTLHeader interface { SetTTL(uint32) } -// TTLCondition is a function type that used to verify that TTL value match a specific criterion. -// Nil error indicates compliance with the criterion. -type TTLCondition func(ttl uint32) error - // TTL constants. const ( // ZeroTTL is an upper bound of invalid TTL values. diff --git a/service/types.go b/service/types.go new file mode 100644 index 0000000..cad4b3c --- /dev/null +++ b/service/types.go @@ -0,0 +1,161 @@ +package service + +// NodeRole to identify in Bootstrap service. +type NodeRole int32 + +// TTLCondition is a function type that used to verify that TTL values match a specific criterion. +// Nil error indicates compliance with the criterion. +type TTLCondition func(uint32) error + +// RawSource is an interface of the container of a boolean Raw value with read access. +type RawSource interface { + GetRaw() bool +} + +// RawContainer is an interface of the container of a boolean Raw value. +type RawContainer interface { + RawSource + SetRaw(bool) +} + +// VersionContainer is an interface of the container of a numerical Version value with read access. +type VersionSource interface { + GetVersion() uint32 +} + +// VersionContainer is an interface of the container of a numerical Version value. +type VersionContainer interface { + VersionSource + SetVersion(uint32) +} + +// EpochSource is an interface of the container of a NeoFS epoch number with read access. +type EpochSource interface { + GetEpoch() uint64 +} + +// EpochContainer is an interface of the container of a NeoFS epoch number. +type EpochContainer interface { + EpochSource + SetEpoch(uint64) +} + +// TTLSource is an interface of the container of a numerical TTL value with read access. +type TTLSource interface { + GetTTL() uint32 +} + +// TTLContainer is an interface of the container of a numerical TTL value. +type TTLContainer interface { + TTLSource + SetTTL(uint32) +} + +// RequestMetaContainer is an interface of a fixed set of request meta value containers. +// Contains: +// - TTL value; +// - NeoFS epoch number; +// - Protocol version; +// - Raw toggle option. +type RequestMetaContainer interface { + TTLContainer + EpochContainer + VersionContainer + RawContainer +} + +// VerbSource is an interface of the container of a token verb value with read access. +type VerbSource interface { + GetVerb() Token_Info_Verb +} + +// VerbContainer is an interface of the container of a token verb value. +type VerbContainer interface { + VerbSource + SetVerb(Token_Info_Verb) +} + +// TokenIDSource is an interface of the container of a token ID value with read access. +type TokenIDSource interface { + GetID() TokenID +} + +// TokenIDContainer is an interface of the container of a token ID value. +type TokenIDContainer interface { + TokenIDSource + SetID(TokenID) +} + +// CreationEpochSource is an interface of the container of a creation epoch number with read access. +type CreationEpochSource interface { + CreationEpoch() uint64 +} + +// CreationEpochContainer is an interface of the container of a creation epoch number. +type CreationEpochContainer interface { + CreationEpochSource + SetCreationEpoch(uint64) +} + +// ExpirationEpochSource is an interface of the container of an expiration epoch number with read access. +type ExpirationEpochSource interface { + ExpirationEpoch() uint64 +} + +// ExpirationEpochContainer is an interface of the container of an expiration epoch number. +type ExpirationEpochContainer interface { + ExpirationEpochSource + SetExpirationEpoch(uint64) +} + +// SessionKeySource is an interface of the container of session key bytes with read access. +type SessionKeySource interface { + GetSessionKey() []byte +} + +// SessionKeyContainer is an interface of the container of public session key bytes. +type SessionKeyContainer interface { + SessionKeySource + SetSessionKey([]byte) +} + +// SignatureSource is an interface of the container of signature bytes with read access. +type SignatureSource interface { + GetSignature() []byte +} + +// SignatureContainer is an interface of the container of signature bytes. +type SignatureContainer interface { + SignatureSource + SetSignature([]byte) +} + +// SessionTokenSource is an interface of the container of a SessionToken with read access. +type SessionTokenSource interface { + GetSessionToken() SessionToken +} + +// SessionTokenInfo is an interface of a fixed set of token information value containers. +// Contains: +// - ID of the token; +// - ID of the token's owner; +// - verb of the session; +// - address of the session object; +// - creation epoch number of the token; +// - expiration epoch number of the token; +// - public session key bytes. +type SessionTokenInfo interface { + TokenIDContainer + OwnerIDContainer + VerbContainer + AddressContainer + CreationEpochContainer + ExpirationEpochContainer + SessionKeyContainer +} + +// SessionToken is an interface of token information and signature pair. +type SessionToken interface { + SessionTokenInfo + SignatureContainer +} From c38a8eddc898059e2ba948c64b710c6a4bb47c4c Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 14:03:11 +0300 Subject: [PATCH 25/68] service: use value container interfaces --- service/meta.go | 33 ++++----------------------------- service/ttl.go | 8 +------- 2 files changed, 5 insertions(+), 36 deletions(-) diff --git a/service/meta.go b/service/meta.go index 2675b79..2714f08 100644 --- a/service/meta.go +++ b/service/meta.go @@ -9,35 +9,10 @@ type ( ResetMeta() RequestMetaHeader RestoreMeta(RequestMetaHeader) - // TTLHeader allows to get and set TTL value of request. - TTLHeader - - // EpochHeader gives possibility to get or set epoch in RPC Requests. - EpochHeader - - // VersionHeader allows get or set version of protocol request - VersionHeader - - // RawHeader allows to get and set raw option of request - RawHeader - } - - // EpochHeader interface gives possibility to get or set epoch in RPC Requests. - EpochHeader interface { - GetEpoch() uint64 - SetEpoch(v uint64) - } - - // VersionHeader allows get or set version of protocol request - VersionHeader interface { - GetVersion() uint32 - SetVersion(uint32) - } - - // RawHeader is an interface of the container of a boolean Raw value - RawHeader interface { - GetRaw() bool - SetRaw(bool) + TTLContainer + EpochContainer + VersionContainer + RawContainer } ) diff --git a/service/ttl.go b/service/ttl.go index c79cc85..28a5092 100644 --- a/service/ttl.go +++ b/service/ttl.go @@ -6,12 +6,6 @@ import ( "google.golang.org/grpc/status" ) -// TTLHeader is an interface of the container of a numerical TTL value. -type TTLHeader interface { - GetTTL() uint32 - SetTTL(uint32) -} - // TTL constants. const ( // ZeroTTL is an upper bound of invalid TTL values. @@ -41,7 +35,7 @@ func IRNonForwarding(role NodeRole) TTLCondition { } // ProcessRequestTTL validates and updates requests with TTL. -func ProcessRequestTTL(req TTLHeader, cond ...TTLCondition) error { +func ProcessRequestTTL(req TTLContainer, cond ...TTLCondition) error { ttl := req.GetTTL() if ttl == ZeroTTL { From eb94cf754964c6f3fd23ea2e45dce82ca9d64f66 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 14:37:14 +0300 Subject: [PATCH 26/68] service: refactor MetaHeader interface --- object/service.go | 2 +- service/epoch.go | 11 +++++++++++ service/epoch_test.go | 21 +++++++++++++++++++++ service/meta.go | 39 ++++----------------------------------- service/meta_test.go | 38 ++++++++++++++++---------------------- service/raw.go | 6 ++++++ service/raw_test.go | 24 ++++++++++++++++++++++++ service/types.go | 12 ++++++++++++ service/verify.go | 8 ++++---- service/version.go | 11 +++++++++++ service/version_test.go | 21 +++++++++++++++++++++ 11 files changed, 131 insertions(+), 62 deletions(-) create mode 100644 service/epoch.go create mode 100644 service/epoch_test.go create mode 100644 service/raw.go create mode 100644 service/raw_test.go create mode 100644 service/version.go create mode 100644 service/version_test.go diff --git a/object/service.go b/object/service.go index 45a8d4b..0e38d70 100644 --- a/object/service.go +++ b/object/service.go @@ -31,7 +31,7 @@ type ( // All object operations must have TTL, Epoch, Type, Container ID and // permission of usage previous network map. Request interface { - service.MetaHeader + service.SeizedRequestMetaContainer CID() CID Type() RequestType diff --git a/service/epoch.go b/service/epoch.go new file mode 100644 index 0000000..7a7a556 --- /dev/null +++ b/service/epoch.go @@ -0,0 +1,11 @@ +package service + +// SetEpoch is an Epoch field setter. +func (m *ResponseMetaHeader) SetEpoch(v uint64) { + m.Epoch = v +} + +// SetEpoch is an Epoch field setter. +func (m *RequestMetaHeader) SetEpoch(v uint64) { + m.Epoch = v +} diff --git a/service/epoch_test.go b/service/epoch_test.go new file mode 100644 index 0000000..47316c0 --- /dev/null +++ b/service/epoch_test.go @@ -0,0 +1,21 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetSetEpoch(t *testing.T) { + v := uint64(5) + + items := []EpochContainer{ + new(ResponseMetaHeader), + new(RequestMetaHeader), + } + + for _, item := range items { + item.SetEpoch(v) + require.Equal(t, v, item.GetEpoch()) + } +} diff --git a/service/meta.go b/service/meta.go index 2714f08..3a945ac 100644 --- a/service/meta.go +++ b/service/meta.go @@ -1,44 +1,13 @@ package service -type ( - // MetaHeader contains meta information of request. - // It provides methods to get or set meta information meta header. - // Also contains methods to reset and restore meta header. - // Also contains methods to get or set request protocol version - MetaHeader interface { - ResetMeta() RequestMetaHeader - RestoreMeta(RequestMetaHeader) - - TTLContainer - EpochContainer - VersionContainer - RawContainer - } -) - -// SetVersion sets protocol version to ResponseMetaHeader. -func (m *ResponseMetaHeader) SetVersion(v uint32) { m.Version = v } - -// SetEpoch sets Epoch to ResponseMetaHeader. -func (m *ResponseMetaHeader) SetEpoch(v uint64) { m.Epoch = v } - -// SetVersion sets protocol version to RequestMetaHeader. -func (m *RequestMetaHeader) SetVersion(v uint32) { m.Version = v } - -// SetEpoch sets Epoch to RequestMetaHeader. -func (m *RequestMetaHeader) SetEpoch(v uint64) { m.Epoch = v } - -// SetRaw is a Raw field setter. -func (m *RequestMetaHeader) SetRaw(raw bool) { - m.Raw = raw -} - // ResetMeta returns current value and sets RequestMetaHeader to empty value. -func (m *RequestMetaHeader) ResetMeta() RequestMetaHeader { +func (m *RequestMetaHeader) CutMeta() RequestMetaHeader { cp := *m m.Reset() return cp } // RestoreMeta sets current RequestMetaHeader to passed value. -func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { *m = v } +func (m *RequestMetaHeader) RestoreMeta(v RequestMetaHeader) { + *m = v +} diff --git a/service/meta_test.go b/service/meta_test.go index fb7fb17..a0b85ef 100644 --- a/service/meta_test.go +++ b/service/meta_test.go @@ -6,26 +6,20 @@ import ( "github.com/stretchr/testify/require" ) -func TestRequestMetaHeader_SetEpoch(t *testing.T) { - m := new(ResponseMetaHeader) - epoch := uint64(3) - m.SetEpoch(epoch) - require.Equal(t, epoch, m.GetEpoch()) -} - -func TestRequestMetaHeader_SetVersion(t *testing.T) { - m := new(ResponseMetaHeader) - version := uint32(3) - m.SetVersion(version) - require.Equal(t, version, m.GetVersion()) -} - -func TestRequestMetaHeader_SetRaw(t *testing.T) { - m := new(RequestMetaHeader) - - m.SetRaw(true) - require.True(t, m.GetRaw()) - - m.SetRaw(false) - require.False(t, m.GetRaw()) +func TestCutRestoreMeta(t *testing.T) { + items := []func() SeizedMetaHeaderContainer{ + func() SeizedMetaHeaderContainer { + m := new(RequestMetaHeader) + m.SetEpoch(1) + return m + }, + } + + for _, item := range items { + v1 := item() + m1 := v1.CutMeta() + v1.RestoreMeta(m1) + + require.Equal(t, item(), v1) + } } diff --git a/service/raw.go b/service/raw.go new file mode 100644 index 0000000..0bb4b27 --- /dev/null +++ b/service/raw.go @@ -0,0 +1,6 @@ +package service + +// SetRaw is a Raw field setter. +func (m *RequestMetaHeader) SetRaw(raw bool) { + m.Raw = raw +} diff --git a/service/raw_test.go b/service/raw_test.go new file mode 100644 index 0000000..ad595ed --- /dev/null +++ b/service/raw_test.go @@ -0,0 +1,24 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetSetRaw(t *testing.T) { + items := []RawContainer{ + new(RequestMetaHeader), + } + + for _, item := range items { + // init with false + item.SetRaw(false) + + item.SetRaw(true) + require.True(t, item.GetRaw()) + + item.SetRaw(false) + require.False(t, item.GetRaw()) + } +} diff --git a/service/types.go b/service/types.go index cad4b3c..e7bdcd4 100644 --- a/service/types.go +++ b/service/types.go @@ -51,6 +51,12 @@ type TTLContainer interface { SetTTL(uint32) } +// SeizedMetaHeaderContainer is an interface of container of RequestMetaHeader that can be cut and restored. +type SeizedMetaHeaderContainer interface { + CutMeta() RequestMetaHeader + RestoreMeta(RequestMetaHeader) +} + // RequestMetaContainer is an interface of a fixed set of request meta value containers. // Contains: // - TTL value; @@ -64,6 +70,12 @@ type RequestMetaContainer interface { RawContainer } +// SeizedRequestMetaContainer is a RequestMetaContainer with seized meta. +type SeizedRequestMetaContainer interface { + RequestMetaContainer + SeizedMetaHeaderContainer +} + // VerbSource is an interface of the container of a token verb value with read access. type VerbSource interface { GetVerb() Token_Info_Verb diff --git a/service/verify.go b/service/verify.go index 7ac3cf3..3ed8402 100644 --- a/service/verify.go +++ b/service/verify.go @@ -118,8 +118,8 @@ var bytesPool = sync.Pool{New: func() interface{} { // new signature to headers. If something went wrong, returns error. func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error { // ignore meta header - if meta, ok := msg.(MetaHeader); ok { - h := meta.ResetMeta() + if meta, ok := msg.(SeizedRequestMetaContainer); ok { + h := meta.CutMeta() defer func() { meta.RestoreMeta(h) @@ -157,8 +157,8 @@ func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error { // If something went wrong, returns error. func VerifyRequestHeader(msg VerifiableRequest) error { // ignore meta header - if meta, ok := msg.(MetaHeader); ok { - h := meta.ResetMeta() + if meta, ok := msg.(SeizedRequestMetaContainer); ok { + h := meta.CutMeta() defer func() { meta.RestoreMeta(h) diff --git a/service/version.go b/service/version.go new file mode 100644 index 0000000..6f4839c --- /dev/null +++ b/service/version.go @@ -0,0 +1,11 @@ +package service + +// SetVersion is a Version field setter. +func (m *ResponseMetaHeader) SetVersion(v uint32) { + m.Version = v +} + +// SetVersion is a Version field setter. +func (m *RequestMetaHeader) SetVersion(v uint32) { + m.Version = v +} diff --git a/service/version_test.go b/service/version_test.go new file mode 100644 index 0000000..d102d30 --- /dev/null +++ b/service/version_test.go @@ -0,0 +1,21 @@ +package service + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestGetSetVersion(t *testing.T) { + v := uint32(7) + + items := []VersionContainer{ + new(ResponseMetaHeader), + new(RequestMetaHeader), + } + + for _, item := range items { + item.SetVersion(v) + require.Equal(t, v, item.GetVersion()) + } +} From 0ffb1bd61df9a8420fd71001af629a1e45d056c1 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 18:52:56 +0300 Subject: [PATCH 27/68] service: implement a function for signing data --- service/errors.go | 3 ++ service/sign.go | 58 ++++++++++++++++++++++ service/sign_test.go | 112 +++++++++++++++++++++++++++++++++++++++++++ service/types.go | 16 +++++++ 4 files changed, 189 insertions(+) create mode 100644 service/sign.go create mode 100644 service/sign_test.go diff --git a/service/errors.go b/service/errors.go index 4aefb4e..1154a28 100644 --- a/service/errors.go +++ b/service/errors.go @@ -16,3 +16,6 @@ const ErrCannotFindOwner = internal.Error("cannot find owner public key") // ErrWrongOwner is raised when passed OwnerID not equal to present PublicKey const ErrWrongOwner = internal.Error("wrong owner") + +// ErrNilSignedDataSource returned by functions that expect a non-nil SignedDataSource argument, but received nil. +const ErrNilSignedDataSource = internal.Error("signed data source is nil") diff --git a/service/sign.go b/service/sign.go new file mode 100644 index 0000000..f371483 --- /dev/null +++ b/service/sign.go @@ -0,0 +1,58 @@ +package service + +import ( + "crypto/ecdsa" + + crypto "github.com/nspcc-dev/neofs-crypto" +) + +// Returns data from DataSignatureAccumulator for signature creation/verification. +// +// If passed DataSignatureAccumulator provides a SignedDataReader interface, data for signature is obtained +// using this interface for optimization. In this case, it is understood that reading into the slice D +// that the method DataForSignature returns does not change D. +func dataForSignature(src SignedDataSource) ([]byte, error) { + r, ok := src.(SignedDataReader) + if !ok { + return src.SignedData() + } + + buf := bytesPool.Get().([]byte) + defer func() { + bytesPool.Put(buf) + }() + + if size := r.SignedDataSize(); size <= cap(buf) { + buf = buf[:size] + } else { + buf = make([]byte, size) + } + + n, err := r.ReadSignedData(buf) + if err != nil { + return nil, err + } + + return buf[:n], nil + +} + +// DataSignature returns the signature of data obtained using the private key. +// +// If passed data container is nil, ErrNilSignedDataSource returns. +// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. +// If the data container or the signature function returns an error, it is returned directly. +func DataSignature(src SignedDataSource, key *ecdsa.PrivateKey) ([]byte, error) { + if src == nil { + return nil, ErrNilSignedDataSource + } else if key == nil { + return nil, crypto.ErrEmptyPrivateKey + } + + data, err := dataForSignature(src) + if err != nil { + return nil, err + } + + return crypto.Sign(key, data) +} diff --git a/service/sign_test.go b/service/sign_test.go new file mode 100644 index 0000000..be5f4b7 --- /dev/null +++ b/service/sign_test.go @@ -0,0 +1,112 @@ +package service + +import ( + "crypto/rand" + "errors" + "io" + "testing" + + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +type testSignedDataSrc struct { + e error + d []byte +} + +type testSignedDataReader struct { + SignedDataSource + + e error + d []byte +} + +func testData(t *testing.T, sz int) []byte { + d := make([]byte, sz) + _, err := rand.Read(d) + require.NoError(t, err) + return d +} + +func (s testSignedDataReader) SignedDataSize() int { + return len(s.d) +} + +func (s testSignedDataReader) ReadSignedData(buf []byte) (int, error) { + if s.e != nil { + return 0, s.e + } + + var err error + if len(buf) < len(s.d) { + err = io.ErrUnexpectedEOF + } + return copy(buf, s.d), err +} + +func (s testSignedDataSrc) SignedData() ([]byte, error) { + return s.d, s.e +} + +func TestDataSignature(t *testing.T) { + var err error + + // nil data source + _, err = DataSignature(nil, nil) + require.EqualError(t, err, ErrNilSignedDataSource.Error()) + + // nil private key + _, err = DataSignature(new(testSignedDataSrc), nil) + require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) + + // create test private key + sk := test.DecodeKey(0) + + t.Run("common signed data source", func(t *testing.T) { + // create test data source + src := &testSignedDataSrc{ + d: testData(t, 10), + } + + // create custom error for data source + src.e = errors.New("test error for data source") + + _, err = DataSignature(src, sk) + require.EqualError(t, err, src.e.Error()) + + // reset error to nil + src.e = nil + + // calculate data signature + sig, err := DataSignature(src, sk) + require.NoError(t, err) + + // ascertain that the signature passes verification + require.NoError(t, crypto.Verify(&sk.PublicKey, src.d, sig)) + }) + + t.Run("signed data reader", func(t *testing.T) { + // create test signed data reader + src := &testSignedDataReader{ + d: testData(t, 10), + } + + // create custom error for signed data reader + src.e = errors.New("test error for signed data reader") + + sig, err := DataSignature(src, sk) + require.EqualError(t, err, src.e.Error()) + + // reset error to nil + src.e = nil + + // calculate data signature + sig, err = DataSignature(src, sk) + require.NoError(t, err) + + // ascertain that the signature passes verification + require.NoError(t, crypto.Verify(&sk.PublicKey, src.d, sig)) + }) +} diff --git a/service/types.go b/service/types.go index e7bdcd4..06ea4e2 100644 --- a/service/types.go +++ b/service/types.go @@ -171,3 +171,19 @@ type SessionToken interface { SessionTokenInfo SignatureContainer } + +// SignedDataSource is an interface of the container of a data for signing. +type SignedDataSource interface { + // Must return the required for signature byte slice. + // A non-nil error indicates that the data is not ready for signature. + SignedData() ([]byte, error) +} + +// SignedDataReader is an interface of signed data reader. +type SignedDataReader interface { + // Must return the minimum length of the slice for full reading. + SignedDataSize() int + + // Must behave like Read method of io.Reader and differ only in the reading of the signed data. + ReadSignedData([]byte) (int, error) +} From f3e6caf7e75cf378da1c279d72ae6cd46d9d1fc0 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 4 May 2020 19:33:18 +0300 Subject: [PATCH 28/68] service: implement a function for creating and storing a signature --- service/sign.go | 14 ++++++++++++++ service/sign_test.go | 35 +++++++++++++++++++++++++++++++++++ service/types.go | 10 ++++++++++ 3 files changed, 59 insertions(+) diff --git a/service/sign.go b/service/sign.go index f371483..654c4a9 100644 --- a/service/sign.go +++ b/service/sign.go @@ -56,3 +56,17 @@ func DataSignature(src SignedDataSource, key *ecdsa.PrivateKey) ([]byte, error) return crypto.Sign(key, data) } + +// AddSignatureWithKey calculates the data signature and adds it to accumulator with public key. +// +// Returns signing errors only. +func AddSignatureWithKey(v SignatureKeyAccumulator, key *ecdsa.PrivateKey) error { + sign, err := DataSignature(v, key) + if err != nil { + return err + } + + v.AddSignKey(sign, &key.PublicKey) + + return nil +} diff --git a/service/sign_test.go b/service/sign_test.go index be5f4b7..5ac7c6c 100644 --- a/service/sign_test.go +++ b/service/sign_test.go @@ -1,6 +1,7 @@ package service import ( + "crypto/ecdsa" "crypto/rand" "errors" "io" @@ -23,6 +24,21 @@ type testSignedDataReader struct { d []byte } +type testKeySigAccum struct { + d []byte + f func([]byte, *ecdsa.PublicKey) +} + +func (s testKeySigAccum) SignedData() ([]byte, error) { + return s.d, nil +} + +func (s testKeySigAccum) AddSignKey(sig []byte, key *ecdsa.PublicKey) { + if s.f != nil { + s.f(sig, key) + } +} + func testData(t *testing.T, sz int) []byte { d := make([]byte, sz) _, err := rand.Read(d) @@ -110,3 +126,22 @@ func TestDataSignature(t *testing.T) { require.NoError(t, crypto.Verify(&sk.PublicKey, src.d, sig)) }) } + +func TestAddSignatureWithKey(t *testing.T) { + // create test data + data := testData(t, 10) + + // create test private key + sk := test.DecodeKey(0) + + // create test signature accumulator + var s SignatureKeyAccumulator = &testKeySigAccum{ + d: data, + f: func(sig []byte, key *ecdsa.PublicKey) { + require.Equal(t, &sk.PublicKey, key) + require.NoError(t, crypto.Verify(key, data, sig)) + }, + } + + require.NoError(t, AddSignatureWithKey(s, sk)) +} diff --git a/service/types.go b/service/types.go index 06ea4e2..80a4a49 100644 --- a/service/types.go +++ b/service/types.go @@ -1,5 +1,9 @@ package service +import ( + "crypto/ecdsa" +) + // NodeRole to identify in Bootstrap service. type NodeRole int32 @@ -187,3 +191,9 @@ type SignedDataReader interface { // Must behave like Read method of io.Reader and differ only in the reading of the signed data. ReadSignedData([]byte) (int, error) } + +// SignatureKeyAccumulator is an interface of the accumulator of data signatures with keys. +type SignatureKeyAccumulator interface { + SignedDataSource + AddSignKey([]byte, *ecdsa.PublicKey) +} From 74144f207aa1eaf88f1e55286a7e4264c2090d09 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 5 May 2020 13:16:21 +0300 Subject: [PATCH 29/68] service: implement functions for verification of signatures --- service/errors.go | 8 ++- service/sign.go | 103 ++++++++++++++++++++++++++-- service/sign_test.go | 160 +++++++++++++++++++++++++++++++++++++------ service/types.go | 20 +++++- 4 files changed, 264 insertions(+), 27 deletions(-) diff --git a/service/errors.go b/service/errors.go index 1154a28..6b0ed24 100644 --- a/service/errors.go +++ b/service/errors.go @@ -17,5 +17,11 @@ const ErrCannotFindOwner = internal.Error("cannot find owner public key") // ErrWrongOwner is raised when passed OwnerID not equal to present PublicKey const ErrWrongOwner = internal.Error("wrong owner") -// ErrNilSignedDataSource returned by functions that expect a non-nil SignedDataSource argument, but received nil. +// ErrNilSignedDataSource returned by functions that expect a non-nil SignedDataSource, but received nil. const ErrNilSignedDataSource = internal.Error("signed data source is nil") + +// ErrNilSignatureKeySource is returned by functions that expect a non-nil SignatureKeySource, but received nil. +const ErrNilSignatureKeySource = internal.Error("empty key-signature source") + +// ErrEmptyDataWithSignature is returned by functions that expect a non-nil DataWithSignature, but received nil. +const ErrEmptyDataWithSignature = internal.Error("empty data with signature") diff --git a/service/sign.go b/service/sign.go index 654c4a9..9a6eba9 100644 --- a/service/sign.go +++ b/service/sign.go @@ -6,12 +6,39 @@ import ( crypto "github.com/nspcc-dev/neofs-crypto" ) +type keySign struct { + key *ecdsa.PublicKey + sign []byte +} + +// GetSignature is a sign field getter. +func (s keySign) GetSignature() []byte { + return s.sign +} + +// GetPublicKey is a key field getter, +func (s keySign) GetPublicKey() *ecdsa.PublicKey { + return s.key +} + +// Unites passed key with signature and returns SignKeyPair interface. +func newSignatureKeyPair(key *ecdsa.PublicKey, sign []byte) SignKeyPair { + return &keySign{ + key: key, + sign: sign, + } +} + // Returns data from DataSignatureAccumulator for signature creation/verification. // // If passed DataSignatureAccumulator provides a SignedDataReader interface, data for signature is obtained // using this interface for optimization. In this case, it is understood that reading into the slice D // that the method DataForSignature returns does not change D. func dataForSignature(src SignedDataSource) ([]byte, error) { + if src == nil { + return nil, ErrNilSignedDataSource + } + r, ok := src.(SignedDataReader) if !ok { return src.SignedData() @@ -42,10 +69,8 @@ func dataForSignature(src SignedDataSource) ([]byte, error) { // If passed data container is nil, ErrNilSignedDataSource returns. // If passed private key is nil, crypto.ErrEmptyPrivateKey returns. // If the data container or the signature function returns an error, it is returned directly. -func DataSignature(src SignedDataSource, key *ecdsa.PrivateKey) ([]byte, error) { - if src == nil { - return nil, ErrNilSignedDataSource - } else if key == nil { +func DataSignature(key *ecdsa.PrivateKey, src SignedDataSource) ([]byte, error) { + if key == nil { return nil, crypto.ErrEmptyPrivateKey } @@ -61,7 +86,7 @@ func DataSignature(src SignedDataSource, key *ecdsa.PrivateKey) ([]byte, error) // // Returns signing errors only. func AddSignatureWithKey(v SignatureKeyAccumulator, key *ecdsa.PrivateKey) error { - sign, err := DataSignature(v, key) + sign, err := DataSignature(key, v) if err != nil { return err } @@ -70,3 +95,71 @@ func AddSignatureWithKey(v SignatureKeyAccumulator, key *ecdsa.PrivateKey) error return nil } + +// Checks passed key-signature pairs for data from the passed container. +// +// If passed key-signatures pair set is empty, nil returns immediately. +func verifySignatures(src SignedDataSource, items ...SignKeyPair) error { + if len(items) <= 0 { + return nil + } + + data, err := dataForSignature(src) + if err != nil { + return err + } + + for _, signKey := range items { + if err := crypto.Verify( + signKey.GetPublicKey(), + data, + signKey.GetSignature(), + ); err != nil { + return err + } + } + + return nil +} + +// VerifySignatures checks passed key-signature pairs for data from the passed container. +// +// If passed data source is nil, ErrNilSignedDataSource returns. +// If check data is not ready, corresponding error returns. +// If at least one of the pairs is invalid, an error returns. +func VerifySignatures(src SignedDataSource, items ...SignKeyPair) error { + return verifySignatures(src, items...) +} + +// VerifyAccumulatedSignatures checks if accumulated key-signature pairs are valid. +// +// Behaves like VerifySignatures. +// If passed key-signature source is empty, ErrNilSignatureKeySource returns. +func VerifyAccumulatedSignatures(src SignatureKeySource) error { + if src == nil { + return ErrNilSignatureKeySource + } + + return verifySignatures(src, src.GetSignKeyPairs()...) +} + +// VerifySignatureWithKey checks data signature from the passed container with passed key. +// +// If passed data with signature is nil, ErrEmptyDataWithSignature returns. +// If passed key is nil, crypto.ErrEmptyPublicKey returns. +// A non-nil error returns if and only if the signature does not pass verification. +func VerifySignatureWithKey(src DataWithSignature, key *ecdsa.PublicKey) error { + if src == nil { + return ErrEmptyDataWithSignature + } else if key == nil { + return crypto.ErrEmptyPublicKey + } + + return verifySignatures( + src, + newSignatureKeyPair( + key, + src.GetSignature(), + ), + ) +} diff --git a/service/sign_test.go b/service/sign_test.go index 5ac7c6c..8fba968 100644 --- a/service/sign_test.go +++ b/service/sign_test.go @@ -25,18 +25,28 @@ type testSignedDataReader struct { } type testKeySigAccum struct { - d []byte - f func([]byte, *ecdsa.PublicKey) + data []byte + sig []byte + key *ecdsa.PublicKey +} + +func (s testKeySigAccum) GetSignature() []byte { + return s.sig +} + +func (s testKeySigAccum) GetSignKeyPairs() []SignKeyPair { + return []SignKeyPair{ + newSignatureKeyPair(s.key, s.sig), + } } func (s testKeySigAccum) SignedData() ([]byte, error) { - return s.d, nil + return s.data, nil } func (s testKeySigAccum) AddSignKey(sig []byte, key *ecdsa.PublicKey) { - if s.f != nil { - s.f(sig, key) - } + s.key = key + s.sig = sig } func testData(t *testing.T, sz int) []byte { @@ -69,17 +79,17 @@ func (s testSignedDataSrc) SignedData() ([]byte, error) { func TestDataSignature(t *testing.T) { var err error - // nil data source - _, err = DataSignature(nil, nil) - require.EqualError(t, err, ErrNilSignedDataSource.Error()) - // nil private key - _, err = DataSignature(new(testSignedDataSrc), nil) + _, err = DataSignature(nil, nil) require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) // create test private key sk := test.DecodeKey(0) + // nil private key + _, err = DataSignature(sk, nil) + require.EqualError(t, err, ErrNilSignedDataSource.Error()) + t.Run("common signed data source", func(t *testing.T) { // create test data source src := &testSignedDataSrc{ @@ -89,14 +99,14 @@ func TestDataSignature(t *testing.T) { // create custom error for data source src.e = errors.New("test error for data source") - _, err = DataSignature(src, sk) + _, err = DataSignature(sk, src) require.EqualError(t, err, src.e.Error()) // reset error to nil src.e = nil // calculate data signature - sig, err := DataSignature(src, sk) + sig, err := DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification @@ -112,14 +122,14 @@ func TestDataSignature(t *testing.T) { // create custom error for signed data reader src.e = errors.New("test error for signed data reader") - sig, err := DataSignature(src, sk) + sig, err := DataSignature(sk, src) require.EqualError(t, err, src.e.Error()) // reset error to nil src.e = nil // calculate data signature - sig, err = DataSignature(src, sk) + sig, err = DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification @@ -136,12 +146,122 @@ func TestAddSignatureWithKey(t *testing.T) { // create test signature accumulator var s SignatureKeyAccumulator = &testKeySigAccum{ - d: data, - f: func(sig []byte, key *ecdsa.PublicKey) { - require.Equal(t, &sk.PublicKey, key) - require.NoError(t, crypto.Verify(key, data, sig)) - }, + data: data, } require.NoError(t, AddSignatureWithKey(s, sk)) } + +func TestVerifySignatures(t *testing.T) { + // empty signatures + require.NoError(t, VerifySignatures(nil)) + + // create test signature source + src := &testSignedDataSrc{ + d: testData(t, 10), + } + + // create private key for test + sk := test.DecodeKey(0) + + // calculate a signature of the data + sig, err := crypto.Sign(sk, src.d) + require.NoError(t, err) + + // ascertain that verification is passed + require.NoError(t, + VerifySignatures( + src, + newSignatureKeyPair(&sk.PublicKey, sig), + ), + ) + + // break the signature + sig[0]++ + + require.Error(t, + VerifySignatures( + src, + newSignatureKeyPair(&sk.PublicKey, sig), + ), + ) + + // restore the signature + sig[0]-- + + // empty data source + require.EqualError(t, + VerifySignatures(nil, nil), + ErrNilSignedDataSource.Error(), + ) + +} + +func TestVerifyAccumulatedSignatures(t *testing.T) { + // nil signature source + require.EqualError(t, + VerifyAccumulatedSignatures(nil), + ErrNilSignatureKeySource.Error(), + ) + + // create test private key + sk := test.DecodeKey(0) + + // create signature source + src := &testKeySigAccum{ + data: testData(t, 10), + key: &sk.PublicKey, + } + + var err error + + // calculate a signature + src.sig, err = crypto.Sign(sk, src.data) + require.NoError(t, err) + + // ascertain that verification is passed + require.NoError(t, VerifyAccumulatedSignatures(src)) + + // break the signature + src.sig[0]++ + + // ascertain that verification is failed + require.Error(t, VerifyAccumulatedSignatures(src)) +} + +func TestVerifySignatureWithKey(t *testing.T) { + // nil signature source + require.EqualError(t, + VerifySignatureWithKey(nil, nil), + ErrEmptyDataWithSignature.Error(), + ) + + // create test signature source + src := &testKeySigAccum{ + data: testData(t, 10), + } + + // nil public key + require.EqualError(t, + VerifySignatureWithKey(src, nil), + crypto.ErrEmptyPublicKey.Error(), + ) + + // create test private key + sk := test.DecodeKey(0) + + var err error + + // calculate a signature + src.sig, err = crypto.Sign(sk, src.data) + require.NoError(t, err) + + // ascertain that verification is passed + require.NoError(t, VerifySignatureWithKey(src, &sk.PublicKey)) + + // break the signature + src.sig[0]++ + + // ascertain that verification is failed + require.Error(t, VerifySignatureWithKey(src, &sk.PublicKey)) +} diff --git a/service/types.go b/service/types.go index 80a4a49..3bf8c3a 100644 --- a/service/types.go +++ b/service/types.go @@ -192,8 +192,26 @@ type SignedDataReader interface { ReadSignedData([]byte) (int, error) } -// SignatureKeyAccumulator is an interface of the accumulator of data signatures with keys. +// SignatureKeyAccumulator is an interface of the container of a data and signatures. type SignatureKeyAccumulator interface { SignedDataSource AddSignKey([]byte, *ecdsa.PublicKey) } + +// SignKeyPair is an interface of key-signature pair with read access. +type SignKeyPair interface { + SignatureSource + GetPublicKey() *ecdsa.PublicKey +} + +// SignatureKeyAccumulator is an interface of the container of a data and signatures with read access. +type SignatureKeySource interface { + SignedDataSource + GetSignKeyPairs() []SignKeyPair +} + +// DataWithSignature is an interface of data-signature pair with read access. +type DataWithSignature interface { + SignedDataSource + SignatureSource +} From fc2c78ae896515ba6efe4a5a01b99fc5fa3bdffa Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 5 May 2020 14:49:35 +0300 Subject: [PATCH 30/68] service: use new function for token signing and verification --- service/token.go | 147 ++++++++++++++++++++---------------------- service/token_test.go | 33 ++-------- 2 files changed, 76 insertions(+), 104 deletions(-) diff --git a/service/token.go b/service/token.go index 2aa159b..5786a40 100644 --- a/service/token.go +++ b/service/token.go @@ -3,10 +3,22 @@ package service import ( "crypto/ecdsa" "encoding/binary" + "io" - crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-api-go/refs" ) +const verbSize = 4 + +const fixedTokenDataSize = 0 + + refs.UUIDSize + + refs.OwnerIDSize + + verbSize + + refs.UUIDSize + + refs.CIDSize + + 8 + + 8 + var tokenEndianness = binary.BigEndian // GetID is an ID field getter. @@ -74,88 +86,71 @@ func (m *Token) SetSignature(sig []byte) { m.Signature = sig } -// Returns byte slice that is used for creation/verification of the token signature. -func verificationTokenData(token SessionToken) []byte { - var sz int - - id := token.GetID() - sz += id.Size() - - ownerID := token.GetOwnerID() - sz += ownerID.Size() - - verb := uint32(token.GetVerb()) - sz += 4 - - addr := token.GetAddress() - sz += addr.CID.Size() + addr.ObjectID.Size() - - cEpoch := token.CreationEpoch() - sz += 8 - - fEpoch := token.ExpirationEpoch() - sz += 8 - - key := token.GetSessionKey() - sz += len(key) - - data := make([]byte, sz) - - var off int - - tokenEndianness.PutUint32(data, verb) - off += 4 - - tokenEndianness.PutUint64(data[off:], cEpoch) - off += 8 - - tokenEndianness.PutUint64(data[off:], fEpoch) - off += 8 - - off += copy(data[off:], id.Bytes()) - off += copy(data[off:], ownerID.Bytes()) - off += copy(data[off:], addr.CID.Bytes()) - off += copy(data[off:], addr.ObjectID.Bytes()) - off += copy(data[off:], key) +// Size returns the size of a binary representation of the verb. +func (x Token_Info_Verb) Size() int { + return verbSize +} +// Bytes returns a binary representation of the verb. +func (x Token_Info_Verb) Bytes() []byte { + data := make([]byte, verbSize) + tokenEndianness.PutUint32(data, uint32(x)) return data } -// SignToken calculates and stores the signature of token information. -// -// If passed token is nil, ErrNilToken returns. -// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. -func SignToken(token SessionToken, key *ecdsa.PrivateKey) error { - if token == nil { - return ErrNilToken - } else if key == nil { - return crypto.ErrEmptyPrivateKey - } - - sig, err := crypto.Sign(key, verificationTokenData(token)) - if err != nil { - return err - } - - token.SetSignature(sig) - - return nil +// AddSignKey calls a Signature field setter with passed signature. +func (m *Token) AddSignKey(sig []byte, _ *ecdsa.PublicKey) { + m.SetSignature(sig) } -// VerifyTokenSignature checks if token was signed correctly. +// SignedData returns token information in a binary representation. +func (m *Token) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + copyTokenSignedData(data, m) + + return data, nil +} + +// ReadSignedData copies a binary representation of the token information to passed buffer. // -// If passed token is nil, ErrNilToken returns. -// If passed public key is nil, crypto.ErrEmptyPublicKey returns. -func VerifyTokenSignature(token SessionToken, key *ecdsa.PublicKey) error { - if token == nil { - return ErrNilToken - } else if key == nil { - return crypto.ErrEmptyPublicKey +// If buffer length is less than required, io.ErrUnexpectedEOF returns. +func (m *Token_Info) ReadSignedData(p []byte) (int, error) { + sz := m.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF } - return crypto.Verify( - key, - verificationTokenData(token), - token.GetSignature(), - ) + copyTokenSignedData(p, m) + + return sz, nil +} + +// SignedDataSize returns the length of signed token information slice. +func (m Token_Info) SignedDataSize() int { + return fixedTokenDataSize + len(m.GetSessionKey()) +} + +// Fills passed buffer with signing token information bytes. +// Does not check buffer length, it is understood that enough space is allocated in it. +func copyTokenSignedData(buf []byte, token SessionTokenInfo) { + var off int + + off += copy(buf[off:], token.GetID().Bytes()) + + off += copy(buf[off:], token.GetOwnerID().Bytes()) + + off += copy(buf[off:], token.GetVerb().Bytes()) + + addr := token.GetAddress() + off += copy(buf[off:], addr.CID.Bytes()) + off += copy(buf[off:], addr.ObjectID.Bytes()) + + tokenEndianness.PutUint64(buf[off:], token.CreationEpoch()) + off += 8 + + tokenEndianness.PutUint64(buf[off:], token.ExpirationEpoch()) + off += 8 + + copy(buf[off:], token.GetSessionKey()) } diff --git a/service/token_test.go b/service/token_test.go index 1e02f46..968364c 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -5,7 +5,6 @@ import ( "testing" "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" ) @@ -90,29 +89,7 @@ func TestTokenGettersSetters(t *testing.T) { } func TestSignToken(t *testing.T) { - // nil token - require.EqualError(t, - SignToken(nil, nil), - ErrNilToken.Error(), - ) - - require.EqualError(t, - VerifyTokenSignature(nil, nil), - ErrNilToken.Error(), - ) - - var token SessionToken = new(Token) - - // nil key - require.EqualError(t, - SignToken(token, nil), - crypto.ErrEmptyPrivateKey.Error(), - ) - - require.EqualError(t, - VerifyTokenSignature(token, nil), - crypto.ErrEmptyPublicKey.Error(), - ) + token := new(Token) // create private key for signing sk := test.DecodeKey(0) @@ -150,8 +127,8 @@ func TestSignToken(t *testing.T) { token.SetSessionKey(sessionKey) // sign and verify token - require.NoError(t, SignToken(token, sk)) - require.NoError(t, VerifyTokenSignature(token, pk)) + require.NoError(t, AddSignatureWithKey(token, sk)) + require.NoError(t, VerifySignatureWithKey(token, pk)) items := []struct { corrupt func() @@ -235,8 +212,8 @@ func TestSignToken(t *testing.T) { for _, v := range items { v.corrupt() - require.Error(t, VerifyTokenSignature(token, pk)) + require.Error(t, VerifySignatureWithKey(token, pk)) v.restore() - require.NoError(t, VerifyTokenSignature(token, pk)) + require.NoError(t, VerifySignatureWithKey(token, pk)) } } From 52d3c827763d28b3d8324d1e408b3b5f314c6ec0 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 11:44:55 +0300 Subject: [PATCH 31/68] service: implement sign/verify function for data with session token --- service/errors.go | 30 ++++++-- service/sign.go | 71 ++++++++++++++++-- service/sign_test.go | 171 ++++++++++++++++++++++++++++-------------- service/token.go | 83 +++++++++++++++++++- service/token_test.go | 8 +- service/types.go | 47 +++++++++--- 6 files changed, 326 insertions(+), 84 deletions(-) diff --git a/service/errors.go b/service/errors.go index 6b0ed24..6241ad2 100644 --- a/service/errors.go +++ b/service/errors.go @@ -2,10 +2,12 @@ package service import "github.com/nspcc-dev/neofs-api-go/internal" -// ErrNilToken is returned by functions that expect a non-nil token argument, but received nil. +// ErrNilToken is returned by functions that expect +// a non-nil token argument, but received nil. const ErrNilToken = internal.Error("token is nil") -// ErrInvalidTTL means that the TTL value does not satisfy a specific criterion. +// ErrInvalidTTL means that the TTL value does not +// satisfy a specific criterion. const ErrInvalidTTL = internal.Error("invalid TTL value") // ErrInvalidPublicKeyBytes means that the public key could not be unmarshaled. @@ -14,14 +16,30 @@ const ErrInvalidPublicKeyBytes = internal.Error("cannot load public key") // ErrCannotFindOwner is raised when signatures empty in GetOwner. const ErrCannotFindOwner = internal.Error("cannot find owner public key") -// ErrWrongOwner is raised when passed OwnerID not equal to present PublicKey +// ErrWrongOwner is raised when passed OwnerID +// not equal to present PublicKey const ErrWrongOwner = internal.Error("wrong owner") -// ErrNilSignedDataSource returned by functions that expect a non-nil SignedDataSource, but received nil. +// ErrNilSignedDataSource returned by functions that expect a non-nil +// SignedDataSource, but received nil. const ErrNilSignedDataSource = internal.Error("signed data source is nil") -// ErrNilSignatureKeySource is returned by functions that expect a non-nil SignatureKeySource, but received nil. +// ErrNilSignatureKeySource is returned by functions that expect a non-nil +// SignatureKeySource, but received nil. const ErrNilSignatureKeySource = internal.Error("empty key-signature source") -// ErrEmptyDataWithSignature is returned by functions that expect a non-nil DataWithSignature, but received nil. +// ErrEmptyDataWithSignature is returned by functions that expect +// a non-nil DataWithSignature, but received nil. const ErrEmptyDataWithSignature = internal.Error("empty data with signature") + +// ErrNegativeLength is returned by functions that received +// negative length for slice allocation. +const ErrNegativeLength = internal.Error("negative slice length") + +// ErrNilDataWithTokenSignAccumulator is returned by functions that expect +// a non-nil DataWithTokenSignAccumulator, but received nil. +const ErrNilDataWithTokenSignAccumulator = internal.Error("signed data with token is nil") + +// ErrNilSignatureKeySourceWithToken is returned by functions that expect +// a non-nil SignatureKeySourceWithToken, but received nil. +const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil") diff --git a/service/sign.go b/service/sign.go index 9a6eba9..f5cdc0b 100644 --- a/service/sign.go +++ b/service/sign.go @@ -34,6 +34,8 @@ func newSignatureKeyPair(key *ecdsa.PublicKey, sign []byte) SignKeyPair { // If passed DataSignatureAccumulator provides a SignedDataReader interface, data for signature is obtained // using this interface for optimization. In this case, it is understood that reading into the slice D // that the method DataForSignature returns does not change D. +// +// If returned length of data is negative, ErrNegativeLength returns. func dataForSignature(src SignedDataSource) ([]byte, error) { if src == nil { return nil, ErrNilSignedDataSource @@ -45,11 +47,10 @@ func dataForSignature(src SignedDataSource) ([]byte, error) { } buf := bytesPool.Get().([]byte) - defer func() { - bytesPool.Put(buf) - }() - if size := r.SignedDataSize(); size <= cap(buf) { + if size := r.SignedDataSize(); size < 0 { + return nil, ErrNegativeLength + } else if size <= cap(buf) { buf = buf[:size] } else { buf = make([]byte, size) @@ -78,14 +79,17 @@ func DataSignature(key *ecdsa.PrivateKey, src SignedDataSource) ([]byte, error) if err != nil { return nil, err } + defer bytesPool.Put(data) return crypto.Sign(key, data) } // AddSignatureWithKey calculates the data signature and adds it to accumulator with public key. // +// Any change of data provoke signature breakdown. +// // Returns signing errors only. -func AddSignatureWithKey(v SignatureKeyAccumulator, key *ecdsa.PrivateKey) error { +func AddSignatureWithKey(key *ecdsa.PrivateKey, v DataWithSignKeyAccumulator) error { sign, err := DataSignature(key, v) if err != nil { return err @@ -108,6 +112,7 @@ func verifySignatures(src SignedDataSource, items ...SignKeyPair) error { if err != nil { return err } + defer bytesPool.Put(data) for _, signKey := range items { if err := crypto.Verify( @@ -135,7 +140,7 @@ func VerifySignatures(src SignedDataSource, items ...SignKeyPair) error { // // Behaves like VerifySignatures. // If passed key-signature source is empty, ErrNilSignatureKeySource returns. -func VerifyAccumulatedSignatures(src SignatureKeySource) error { +func VerifyAccumulatedSignatures(src DataWithSignKeySource) error { if src == nil { return ErrNilSignatureKeySource } @@ -148,7 +153,7 @@ func VerifyAccumulatedSignatures(src SignatureKeySource) error { // If passed data with signature is nil, ErrEmptyDataWithSignature returns. // If passed key is nil, crypto.ErrEmptyPublicKey returns. // A non-nil error returns if and only if the signature does not pass verification. -func VerifySignatureWithKey(src DataWithSignature, key *ecdsa.PublicKey) error { +func VerifySignatureWithKey(key *ecdsa.PublicKey, src DataWithSignature) error { if src == nil { return ErrEmptyDataWithSignature } else if key == nil { @@ -163,3 +168,55 @@ func VerifySignatureWithKey(src DataWithSignature, key *ecdsa.PublicKey) error { ), ) } + +// SignDataWithSessionToken calculates data with token signature and adds it to accumulator. +// +// Any change of data or session token info provoke signature breakdown. +// +// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. +// If passed DataWithTokenSignAccumulator is nil, ErrNilDataWithTokenSignAccumulator returns. +func SignDataWithSessionToken(key *ecdsa.PrivateKey, src DataWithTokenSignAccumulator) error { + if src == nil { + return ErrNilDataWithTokenSignAccumulator + } else if r, ok := src.(SignedDataReader); ok { + return AddSignatureWithKey(key, &signDataReaderWithToken{ + SignedDataSource: src, + SignKeyPairAccumulator: src, + + rdr: r, + token: src.GetSessionToken(), + }, + ) + } + + return AddSignatureWithKey(key, &signAccumWithToken{ + SignedDataSource: src, + SignKeyPairAccumulator: src, + + token: src.GetSessionToken(), + }) +} + +// VerifyAccumulatedSignaturesWithToken checks if accumulated key-signature pairs of data with token are valid. +// +// If passed DataWithTokenSignSource is nil, ErrNilSignatureKeySourceWithToken returns. +func VerifyAccumulatedSignaturesWithToken(src DataWithTokenSignSource) error { + if src == nil { + return ErrNilSignatureKeySourceWithToken + } else if r, ok := src.(SignedDataReader); ok { + return VerifyAccumulatedSignatures(&signDataReaderWithToken{ + SignedDataSource: src, + SignKeyPairSource: src, + + rdr: r, + token: src.GetSessionToken(), + }) + } + + return VerifyAccumulatedSignatures(&signAccumWithToken{ + SignedDataSource: src, + SignKeyPairSource: src, + + token: src.GetSessionToken(), + }) +} diff --git a/service/sign_test.go b/service/sign_test.go index 8fba968..5cb7c40 100644 --- a/service/sign_test.go +++ b/service/sign_test.go @@ -13,38 +13,32 @@ import ( ) type testSignedDataSrc struct { - e error - d []byte + err error + data []byte + sig []byte + key *ecdsa.PublicKey + token SessionToken } type testSignedDataReader struct { - SignedDataSource - - e error - d []byte + *testSignedDataSrc } -type testKeySigAccum struct { - data []byte - sig []byte - key *ecdsa.PublicKey -} - -func (s testKeySigAccum) GetSignature() []byte { +func (s testSignedDataSrc) GetSignature() []byte { return s.sig } -func (s testKeySigAccum) GetSignKeyPairs() []SignKeyPair { +func (s testSignedDataSrc) GetSignKeyPairs() []SignKeyPair { return []SignKeyPair{ newSignatureKeyPair(s.key, s.sig), } } -func (s testKeySigAccum) SignedData() ([]byte, error) { - return s.data, nil +func (s testSignedDataSrc) SignedData() ([]byte, error) { + return s.data, s.err } -func (s testKeySigAccum) AddSignKey(sig []byte, key *ecdsa.PublicKey) { +func (s *testSignedDataSrc) AddSignKey(sig []byte, key *ecdsa.PublicKey) { s.key = key s.sig = sig } @@ -56,24 +50,24 @@ func testData(t *testing.T, sz int) []byte { return d } +func (s testSignedDataSrc) GetSessionToken() SessionToken { + return s.token +} + func (s testSignedDataReader) SignedDataSize() int { - return len(s.d) + return len(s.data) } func (s testSignedDataReader) ReadSignedData(buf []byte) (int, error) { - if s.e != nil { - return 0, s.e + if s.err != nil { + return 0, s.err } var err error - if len(buf) < len(s.d) { + if len(buf) < len(s.data) { err = io.ErrUnexpectedEOF } - return copy(buf, s.d), err -} - -func (s testSignedDataSrc) SignedData() ([]byte, error) { - return s.d, s.e + return copy(buf, s.data), err } func TestDataSignature(t *testing.T) { @@ -93,63 +87,59 @@ func TestDataSignature(t *testing.T) { t.Run("common signed data source", func(t *testing.T) { // create test data source src := &testSignedDataSrc{ - d: testData(t, 10), + data: testData(t, 10), } // create custom error for data source - src.e = errors.New("test error for data source") + src.err = errors.New("test error for data source") _, err = DataSignature(sk, src) - require.EqualError(t, err, src.e.Error()) + require.EqualError(t, err, src.err.Error()) // reset error to nil - src.e = nil + src.err = nil // calculate data signature sig, err := DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification - require.NoError(t, crypto.Verify(&sk.PublicKey, src.d, sig)) + require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) }) t.Run("signed data reader", func(t *testing.T) { // create test signed data reader - src := &testSignedDataReader{ - d: testData(t, 10), + src := &testSignedDataSrc{ + data: testData(t, 10), } // create custom error for signed data reader - src.e = errors.New("test error for signed data reader") + src.err = errors.New("test error for signed data reader") sig, err := DataSignature(sk, src) - require.EqualError(t, err, src.e.Error()) + require.EqualError(t, err, src.err.Error()) // reset error to nil - src.e = nil + src.err = nil // calculate data signature sig, err = DataSignature(sk, src) require.NoError(t, err) // ascertain that the signature passes verification - require.NoError(t, crypto.Verify(&sk.PublicKey, src.d, sig)) + require.NoError(t, crypto.Verify(&sk.PublicKey, src.data, sig)) }) } func TestAddSignatureWithKey(t *testing.T) { - // create test data - data := testData(t, 10) - - // create test private key - sk := test.DecodeKey(0) - - // create test signature accumulator - var s SignatureKeyAccumulator = &testKeySigAccum{ - data: data, - } - - require.NoError(t, AddSignatureWithKey(s, sk)) + require.NoError(t, + AddSignatureWithKey( + test.DecodeKey(0), + &testSignedDataSrc{ + data: testData(t, 10), + }, + ), + ) } func TestVerifySignatures(t *testing.T) { @@ -158,14 +148,14 @@ func TestVerifySignatures(t *testing.T) { // create test signature source src := &testSignedDataSrc{ - d: testData(t, 10), + data: testData(t, 10), } // create private key for test sk := test.DecodeKey(0) // calculate a signature of the data - sig, err := crypto.Sign(sk, src.d) + sig, err := crypto.Sign(sk, src.data) require.NoError(t, err) // ascertain that verification is passed @@ -208,7 +198,7 @@ func TestVerifyAccumulatedSignatures(t *testing.T) { sk := test.DecodeKey(0) // create signature source - src := &testKeySigAccum{ + src := &testSignedDataSrc{ data: testData(t, 10), key: &sk.PublicKey, } @@ -237,13 +227,13 @@ func TestVerifySignatureWithKey(t *testing.T) { ) // create test signature source - src := &testKeySigAccum{ + src := &testSignedDataSrc{ data: testData(t, 10), } // nil public key require.EqualError(t, - VerifySignatureWithKey(src, nil), + VerifySignatureWithKey(nil, src), crypto.ErrEmptyPublicKey.Error(), ) @@ -257,11 +247,80 @@ func TestVerifySignatureWithKey(t *testing.T) { require.NoError(t, err) // ascertain that verification is passed - require.NoError(t, VerifySignatureWithKey(src, &sk.PublicKey)) + require.NoError(t, VerifySignatureWithKey(&sk.PublicKey, src)) // break the signature src.sig[0]++ // ascertain that verification is failed - require.Error(t, VerifySignatureWithKey(src, &sk.PublicKey)) + require.Error(t, VerifySignatureWithKey(&sk.PublicKey, src)) +} + +func TestSignVerifyDataWithSessionToken(t *testing.T) { + // sign with empty DataWithTokenSignAccumulator + require.EqualError(t, + SignDataWithSessionToken(nil, nil), + ErrNilDataWithTokenSignAccumulator.Error(), + ) + + // verify with empty DataWithTokenSignSource + require.EqualError(t, + VerifyAccumulatedSignaturesWithToken(nil), + ErrNilSignatureKeySourceWithToken.Error(), + ) + + // create test session token + var ( + token = new(Token) + initVerb = Token_Info_Verb(1) + ) + + token.SetVerb(initVerb) + + // create test data with token + src := &testSignedDataSrc{ + data: testData(t, 10), + token: token, + } + + // create test private key + sk := test.DecodeKey(0) + + // sign with private key + require.NoError(t, SignDataWithSessionToken(sk, src)) + + // ascertain that verification is passed + require.NoError(t, VerifyAccumulatedSignaturesWithToken(src)) + + // break the data + src.data[0]++ + + // ascertain that verification is failed + require.Error(t, VerifyAccumulatedSignaturesWithToken(src)) + + // restore the data + src.data[0]-- + + // break the token + token.SetVerb(initVerb + 1) + + // ascertain that verification is failed + require.Error(t, VerifyAccumulatedSignaturesWithToken(src)) + + // restore the token + token.SetVerb(initVerb) + + // ascertain that verification is passed + require.NoError(t, VerifyAccumulatedSignaturesWithToken(src)) + + // wrap to data reader + rdr := &testSignedDataReader{ + testSignedDataSrc: src, + } + + // sign with private key + require.NoError(t, SignDataWithSessionToken(sk, rdr)) + + // ascertain that verification is passed + require.NoError(t, VerifyAccumulatedSignaturesWithToken(rdr)) } diff --git a/service/token.go b/service/token.go index 5786a40..f431427 100644 --- a/service/token.go +++ b/service/token.go @@ -8,6 +8,24 @@ import ( "github.com/nspcc-dev/neofs-api-go/refs" ) +type signAccumWithToken struct { + SignedDataSource + SignKeyPairAccumulator + SignKeyPairSource + + token SessionToken +} + +type signDataReaderWithToken struct { + SignedDataSource + SignKeyPairAccumulator + SignKeyPairSource + + rdr SignedDataReader + + token SessionToken +} + const verbSize = 4 const fixedTokenDataSize = 0 + @@ -127,13 +145,26 @@ func (m *Token_Info) ReadSignedData(p []byte) (int, error) { } // SignedDataSize returns the length of signed token information slice. -func (m Token_Info) SignedDataSize() int { - return fixedTokenDataSize + len(m.GetSessionKey()) +func (m *Token_Info) SignedDataSize() int { + return tokenInfoSize(m) +} + +func tokenInfoSize(v SessionKeySource) int { + if v == nil { + return 0 + } + return fixedTokenDataSize + len(v.GetSessionKey()) } // Fills passed buffer with signing token information bytes. // Does not check buffer length, it is understood that enough space is allocated in it. +// +// If passed SessionTokenInfo, buffer remains unchanged. func copyTokenSignedData(buf []byte, token SessionTokenInfo) { + if token == nil { + return + } + var off int off += copy(buf[off:], token.GetID().Bytes()) @@ -154,3 +185,51 @@ func copyTokenSignedData(buf []byte, token SessionTokenInfo) { copy(buf[off:], token.GetSessionKey()) } + +// SignedData concatenates signed data with session token information. Returns concatenation result. +// +// Token bytes are added if and only if token is not nil. +func (s signAccumWithToken) SignedData() ([]byte, error) { + data, err := s.SignedDataSource.SignedData() + if err != nil { + return nil, err + } + + tokenData := make([]byte, tokenInfoSize(s.token)) + + copyTokenSignedData(tokenData, s.token) + + return append(data, tokenData...), nil +} + +func (s signDataReaderWithToken) SignedDataSize() int { + sz := s.rdr.SignedDataSize() + if sz < 0 { + return -1 + } + + sz += tokenInfoSize(s.token) + + return sz +} + +func (s signDataReaderWithToken) ReadSignedData(p []byte) (int, error) { + dataSize := s.rdr.SignedDataSize() + if dataSize < 0 { + return 0, ErrNegativeLength + } + + sumSize := dataSize + tokenInfoSize(s.token) + + if len(p) < sumSize { + return 0, io.ErrUnexpectedEOF + } + + if n, err := s.rdr.ReadSignedData(p); err != nil { + return n, err + } + + copyTokenSignedData(p[dataSize:], s.token) + + return sumSize, nil +} diff --git a/service/token_test.go b/service/token_test.go index 968364c..ce3d2c8 100644 --- a/service/token_test.go +++ b/service/token_test.go @@ -127,8 +127,8 @@ func TestSignToken(t *testing.T) { token.SetSessionKey(sessionKey) // sign and verify token - require.NoError(t, AddSignatureWithKey(token, sk)) - require.NoError(t, VerifySignatureWithKey(token, pk)) + require.NoError(t, AddSignatureWithKey(sk, token)) + require.NoError(t, VerifySignatureWithKey(pk, token)) items := []struct { corrupt func() @@ -212,8 +212,8 @@ func TestSignToken(t *testing.T) { for _, v := range items { v.corrupt() - require.Error(t, VerifySignatureWithKey(token, pk)) + require.Error(t, VerifySignatureWithKey(pk, token)) v.restore() - require.NoError(t, VerifySignatureWithKey(token, pk)) + require.NoError(t, VerifySignatureWithKey(pk, token)) } } diff --git a/service/types.go b/service/types.go index 3bf8c3a..020cba0 100644 --- a/service/types.go +++ b/service/types.go @@ -186,32 +186,61 @@ type SignedDataSource interface { // SignedDataReader is an interface of signed data reader. type SignedDataReader interface { // Must return the minimum length of the slice for full reading. + // Must return a negative value if the length cannot be calculated. SignedDataSize() int // Must behave like Read method of io.Reader and differ only in the reading of the signed data. ReadSignedData([]byte) (int, error) } -// SignatureKeyAccumulator is an interface of the container of a data and signatures. -type SignatureKeyAccumulator interface { - SignedDataSource +// SignKeyPairAccumulator is an interface of a set of key-signature pairs with append access. +type SignKeyPairAccumulator interface { AddSignKey([]byte, *ecdsa.PublicKey) } +// SignKeyPairSource is an interface of a set of key-signature pairs with read access. +type SignKeyPairSource interface { + GetSignKeyPairs() []SignKeyPair +} + // SignKeyPair is an interface of key-signature pair with read access. type SignKeyPair interface { SignatureSource GetPublicKey() *ecdsa.PublicKey } -// SignatureKeyAccumulator is an interface of the container of a data and signatures with read access. -type SignatureKeySource interface { - SignedDataSource - GetSignKeyPairs() []SignKeyPair -} - // DataWithSignature is an interface of data-signature pair with read access. type DataWithSignature interface { SignedDataSource SignatureSource } + +// DataWithSignKeyAccumulator is an interface of data and key-signature accumulator pair. +type DataWithSignKeyAccumulator interface { + SignedDataSource + SignKeyPairAccumulator +} + +// DataWithSignKeySource is an interface of data and key-signature source pair. +type DataWithSignKeySource interface { + SignedDataSource + SignKeyPairSource +} + +// SignedDataWithToken is an interface of data-token pair with read access. +type SignedDataWithToken interface { + SignedDataSource + SessionTokenSource +} + +// DataWithTokenSignAccumulator is an interface of data-token pair with signature write access. +type DataWithTokenSignAccumulator interface { + SignedDataWithToken + SignKeyPairAccumulator +} + +// DataWithTokenSignSource is an interface of data-token pair with signature read access. +type DataWithTokenSignSource interface { + SignedDataWithToken + SignKeyPairSource +} From 082edf745626b0731c300ee7b7731d88aebc01c2 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 12:50:15 +0300 Subject: [PATCH 32/68] service: implement sign-verify methods on RequestVerificationHeader --- service/verify.go | 50 +++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 50 insertions(+) diff --git a/service/verify.go b/service/verify.go index 3ed8402..beca992 100644 --- a/service/verify.go +++ b/service/verify.go @@ -35,6 +35,56 @@ type ( } ) +// GetSessionToken returns SessionToken interface of Token field. +// +// If token field value is nil, nil returns. +func (m RequestVerificationHeader) GetSessionToken() SessionToken { + if t := m.GetToken(); t != nil { + return t + } + + return nil +} + +// AddSignKey adds new element to Signatures field. +// +// Sets Sign field to passed sign. Set Peer field to marshaled passed key. +func (m *RequestVerificationHeader) AddSignKey(sign []byte, key *ecdsa.PublicKey) { + m.SetSignatures( + append( + m.GetSignatures(), + &RequestVerificationHeader_Signature{ + Sign: sign, + Peer: crypto.MarshalPublicKey(key), + }, + ), + ) +} + +// GetSignKeyPairs returns the elements of Signatures field as SignKeyPair slice. +func (m RequestVerificationHeader) GetSignKeyPairs() []SignKeyPair { + var ( + signs = m.GetSignatures() + res = make([]SignKeyPair, len(signs)) + ) + + for i := range signs { + res[i] = signs[i] + } + + return res +} + +// GetSignature returns the result of a Sign field getter. +func (m RequestVerificationHeader_Signature) GetSignature() []byte { + return m.GetSign() +} + +// GetPublicKey unmarshals and returns the result of a Peer field getter. +func (m RequestVerificationHeader_Signature) GetPublicKey() *ecdsa.PublicKey { + return crypto.UnmarshalPublicKey(m.GetPeer()) +} + // SetSignatures replaces signatures stored in RequestVerificationHeader. func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificationHeader_Signature) { m.Signatures = signatures From 78f435a9051f0cdcdf9ec6faf81bc8cbd8cb2e44 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 13:16:15 +0300 Subject: [PATCH 33/68] object: implement signing payload methods on PutRequest message --- object/sign.go | 45 ++++++++++++++++++++++++++ object/sign_test.go | 79 +++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 124 insertions(+) create mode 100644 object/sign.go create mode 100644 object/sign_test.go diff --git a/object/sign.go b/object/sign.go new file mode 100644 index 0000000..1dc5bd0 --- /dev/null +++ b/object/sign.go @@ -0,0 +1,45 @@ +package object + +// SignedData returns marshaled payload of the Put request. +// +// If payload is nil, ErrHeaderNotFound returns. +func (m PutRequest) SignedData() ([]byte, error) { + r := m.GetR() + if r == nil { + return nil, ErrHeaderNotFound + } + + data := make([]byte, r.Size()) + + if _, err := r.MarshalTo(data); err != nil { + return nil, err + } + + return data, nil +} + +// ReadSignedData copies marshaled payload of the Put request to passed buffer. +// +// If payload is nil, ErrHeaderNotFound returns. +func (m PutRequest) ReadSignedData(p []byte) error { + r := m.GetR() + if r == nil { + return ErrHeaderNotFound + } + + _, err := r.MarshalTo(p) + + return err +} + +// SignedDataSize returns the size of payload of the Put request. +// +// If payload is nil, -1 returns. +func (m PutRequest) SignedDataSize() int { + r := m.GetR() + if r == nil { + return -1 + } + + return r.Size() +} diff --git a/object/sign_test.go b/object/sign_test.go new file mode 100644 index 0000000..2574d9c --- /dev/null +++ b/object/sign_test.go @@ -0,0 +1,79 @@ +package object + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestSignVerifyRequests(t *testing.T) { + sk := test.DecodeKey(0) + + type sigType interface { + service.SignedDataWithToken + service.SignKeyPairAccumulator + service.SignKeyPairSource + SetToken(*Token) + } + + items := []struct { + constructor func() sigType + bodyCorrupt []func(sigType) + }{ + { // PutRequest.PutHeader + constructor: func() sigType { + return MakePutRequestHeader(new(Object)) + }, + bodyCorrupt: []func(sigType){ + func(s sigType) { + obj := s.(*PutRequest).GetR().(*PutRequest_Header).Header.GetObject() + obj.SystemHeader.PayloadLength++ + }, + }, + }, + { // PutRequest.Chunk + constructor: func() sigType { + return MakePutRequestChunk(make([]byte, 10)) + }, + bodyCorrupt: []func(sigType){ + func(s sigType) { + h := s.(*PutRequest).GetR().(*PutRequest_Chunk) + h.Chunk[0]++ + }, + }, + }, + } + + for _, item := range items { + { // token corruptions + v := item.constructor() + + token := new(Token) + v.SetToken(token) + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + token.SetSessionKey(append(token.GetSessionKey(), 1)) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + + { // body corruptions + for _, corruption := range item.bodyCorrupt { + v := item.constructor() + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + corruption(v) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + } + } +} From 439221cea824f2b5c1568160db37344fac219a8f Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 13:33:03 +0300 Subject: [PATCH 34/68] object: implement signing payload methods on GetRequest message --- object/sign.go | 40 ++++++++++++++++++++++++++++++++++++++++ object/sign_test.go | 25 +++++++++++++++++++------ 2 files changed, 59 insertions(+), 6 deletions(-) diff --git a/object/sign.go b/object/sign.go index 1dc5bd0..63687a5 100644 --- a/object/sign.go +++ b/object/sign.go @@ -1,5 +1,9 @@ package object +import ( + "io" +) + // SignedData returns marshaled payload of the Put request. // // If payload is nil, ErrHeaderNotFound returns. @@ -43,3 +47,39 @@ func (m PutRequest) SignedDataSize() int { return r.Size() } + +// SignedData returns marshaled Address field. +// +// Resulting error is always nil. +func (m GetRequest) SignedData() ([]byte, error) { + addr := m.GetAddress() + + return append( + addr.CID.Bytes(), + addr.ObjectID.Bytes()..., + ), nil +} + +// ReadSignedData copies marshaled Address field to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m GetRequest) ReadSignedData(p []byte) error { + addr := m.GetAddress() + + if len(p) < addr.CID.Size()+addr.ObjectID.Size() { + return io.ErrUnexpectedEOF + } + + off := copy(p, addr.CID.Bytes()) + + copy(p[off:], addr.ObjectID.Bytes()) + + return nil +} + +// SignedDataSize returns the size of object address. +func (m GetRequest) SignedDataSize() int { + addr := m.GetAddress() + + return addr.CID.Size() + addr.ObjectID.Size() +} diff --git a/object/sign_test.go b/object/sign_test.go index 2574d9c..8cf5627 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -19,14 +19,14 @@ func TestSignVerifyRequests(t *testing.T) { } items := []struct { - constructor func() sigType - bodyCorrupt []func(sigType) + constructor func() sigType + payloadCorrupt []func(sigType) }{ { // PutRequest.PutHeader constructor: func() sigType { return MakePutRequestHeader(new(Object)) }, - bodyCorrupt: []func(sigType){ + payloadCorrupt: []func(sigType){ func(s sigType) { obj := s.(*PutRequest).GetR().(*PutRequest_Header).Header.GetObject() obj.SystemHeader.PayloadLength++ @@ -37,13 +37,26 @@ func TestSignVerifyRequests(t *testing.T) { constructor: func() sigType { return MakePutRequestChunk(make([]byte, 10)) }, - bodyCorrupt: []func(sigType){ + payloadCorrupt: []func(sigType){ func(s sigType) { h := s.(*PutRequest).GetR().(*PutRequest_Chunk) h.Chunk[0]++ }, }, }, + { // GetRequest + constructor: func() sigType { + return new(GetRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*GetRequest).Address.CID[0]++ + }, + func(s sigType) { + s.(*GetRequest).Address.ObjectID[0]++ + }, + }, + }, } for _, item := range items { @@ -62,8 +75,8 @@ func TestSignVerifyRequests(t *testing.T) { require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) } - { // body corruptions - for _, corruption := range item.bodyCorrupt { + { // payload corruptions + for _, corruption := range item.payloadCorrupt { v := item.constructor() require.NoError(t, service.SignDataWithSessionToken(sk, v)) From 68f83f547043edaba1e075414cd511f5163ebacc Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 13:54:13 +0300 Subject: [PATCH 35/68] object: implement signing payload methods on HeadRequest message --- object/sign.go | 56 ++++++++++++++++++++++++++++++++++++++++----- object/sign_test.go | 16 +++++++++++++ 2 files changed, 66 insertions(+), 6 deletions(-) diff --git a/object/sign.go b/object/sign.go index 63687a5..6cece1b 100644 --- a/object/sign.go +++ b/object/sign.go @@ -54,10 +54,7 @@ func (m PutRequest) SignedDataSize() int { func (m GetRequest) SignedData() ([]byte, error) { addr := m.GetAddress() - return append( - addr.CID.Bytes(), - addr.ObjectID.Bytes()..., - ), nil + return addressBytes(addr), nil } // ReadSignedData copies marshaled Address field to passed buffer. @@ -66,7 +63,7 @@ func (m GetRequest) SignedData() ([]byte, error) { func (m GetRequest) ReadSignedData(p []byte) error { addr := m.GetAddress() - if len(p) < addr.CID.Size()+addr.ObjectID.Size() { + if len(p) < m.SignedDataSize() { return io.ErrUnexpectedEOF } @@ -79,7 +76,54 @@ func (m GetRequest) ReadSignedData(p []byte) error { // SignedDataSize returns the size of object address. func (m GetRequest) SignedDataSize() int { - addr := m.GetAddress() + return addressSize(m.GetAddress()) +} +// SignedData returns marshaled Address field. +// +// Resulting error is always nil. +func (m HeadRequest) SignedData() ([]byte, error) { + sz := addressSize(m.Address) + + data := make([]byte, sz+1) + + if m.GetFullHeaders() { + data[0] = 1 + } + + copy(data[1:], addressBytes(m.Address)) + + return data, nil +} + +// ReadSignedData copies marshaled Address field to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m HeadRequest) ReadSignedData(p []byte) error { + if len(p) < m.SignedDataSize() { + return io.ErrUnexpectedEOF + } + + if m.GetFullHeaders() { + p[0] = 1 + } + + off := 1 + copy(p[1:], m.Address.CID.Bytes()) + + copy(p[off:], m.Address.ObjectID.Bytes()) + + return nil +} + +// SignedDataSize returns the size of object address. +func (m HeadRequest) SignedDataSize() int { + return addressSize(m.Address) + 1 +} + +func addressSize(addr Address) int { return addr.CID.Size() + addr.ObjectID.Size() } + +func addressBytes(addr Address) []byte { + return append(addr.CID.Bytes(), addr.ObjectID.Bytes()...) +} diff --git a/object/sign_test.go b/object/sign_test.go index 8cf5627..c5159c6 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -57,6 +57,22 @@ func TestSignVerifyRequests(t *testing.T) { }, }, }, + { // HeadRequest + constructor: func() sigType { + return new(HeadRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*HeadRequest).Address.CID[0]++ + }, + func(s sigType) { + s.(*HeadRequest).Address.ObjectID[0]++ + }, + func(s sigType) { + s.(*HeadRequest).FullHeaders = true + }, + }, + }, } for _, item := range items { From fc0da3c8fcb69e49a9e26cea3b482ff3102381d1 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 14:15:07 +0300 Subject: [PATCH 36/68] object: implement signing payload methods on DeleteRequest message --- object/sign.go | 77 ++++++++++++++++++++++++++------------------- object/sign_test.go | 16 ++++++++++ 2 files changed, 60 insertions(+), 33 deletions(-) diff --git a/object/sign.go b/object/sign.go index 6cece1b..acb9901 100644 --- a/object/sign.go +++ b/object/sign.go @@ -4,27 +4,23 @@ import ( "io" ) -// SignedData returns marshaled payload of the Put request. +// SignedData returns payload bytes of the request. // // If payload is nil, ErrHeaderNotFound returns. func (m PutRequest) SignedData() ([]byte, error) { - r := m.GetR() - if r == nil { + sz := m.SignedDataSize() + if sz < 0 { return nil, ErrHeaderNotFound } - data := make([]byte, r.Size()) + data := make([]byte, sz) - if _, err := r.MarshalTo(data); err != nil { - return nil, err - } - - return data, nil + return data, m.ReadSignedData(data) } -// ReadSignedData copies marshaled payload of the Put request to passed buffer. +// ReadSignedData copies payload bytes to passed buffer. // -// If payload is nil, ErrHeaderNotFound returns. +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. func (m PutRequest) ReadSignedData(p []byte) error { r := m.GetR() if r == nil { @@ -48,16 +44,14 @@ func (m PutRequest) SignedDataSize() int { return r.Size() } -// SignedData returns marshaled Address field. -// -// Resulting error is always nil. +// SignedData returns payload bytes of the request. func (m GetRequest) SignedData() ([]byte, error) { - addr := m.GetAddress() + data := make([]byte, m.SignedDataSize()) - return addressBytes(addr), nil + return data, m.ReadSignedData(data) } -// ReadSignedData copies marshaled Address field to passed buffer. +// ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. func (m GetRequest) ReadSignedData(p []byte) error { @@ -74,29 +68,19 @@ func (m GetRequest) ReadSignedData(p []byte) error { return nil } -// SignedDataSize returns the size of object address. +// SignedDataSize returns payload size of the request. func (m GetRequest) SignedDataSize() int { return addressSize(m.GetAddress()) } -// SignedData returns marshaled Address field. -// -// Resulting error is always nil. +// SignedData returns payload bytes of the request. func (m HeadRequest) SignedData() ([]byte, error) { - sz := addressSize(m.Address) + data := make([]byte, m.SignedDataSize()) - data := make([]byte, sz+1) - - if m.GetFullHeaders() { - data[0] = 1 - } - - copy(data[1:], addressBytes(m.Address)) - - return data, nil + return data, m.ReadSignedData(data) } -// ReadSignedData copies marshaled Address field to passed buffer. +// ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. func (m HeadRequest) ReadSignedData(p []byte) error { @@ -115,11 +99,38 @@ func (m HeadRequest) ReadSignedData(p []byte) error { return nil } -// SignedDataSize returns the size of object address. +// SignedDataSize returns payload size of the request. func (m HeadRequest) SignedDataSize() int { return addressSize(m.Address) + 1 } +// SignedData returns payload bytes of the request. +func (m DeleteRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + return data, m.ReadSignedData(data) +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m DeleteRequest) ReadSignedData(p []byte) error { + if len(p) < m.SignedDataSize() { + return io.ErrUnexpectedEOF + } + + off := copy(p, m.OwnerID.Bytes()) + + copy(p[off:], addressBytes(m.Address)) + + return nil +} + +// SignedDataSize returns payload size of the request. +func (m DeleteRequest) SignedDataSize() int { + return m.OwnerID.Size() + addressSize(m.Address) +} + func addressSize(addr Address) int { return addr.CID.Size() + addr.ObjectID.Size() } diff --git a/object/sign_test.go b/object/sign_test.go index c5159c6..d6357b9 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -73,6 +73,22 @@ func TestSignVerifyRequests(t *testing.T) { }, }, }, + { // DeleteRequest + constructor: func() sigType { + return new(DeleteRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*DeleteRequest).OwnerID[0]++ + }, + func(s sigType) { + s.(*DeleteRequest).Address.CID[0]++ + }, + func(s sigType) { + s.(*DeleteRequest).Address.ObjectID[0]++ + }, + }, + }, } for _, item := range items { From e784206032922a82fc5180d68d08e69572701406 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 14:38:39 +0300 Subject: [PATCH 37/68] object: implement signing payload methods on GetRangeRequest message --- object/sign.go | 30 ++++++++++++++++++++++++++++++ object/sign_test.go | 19 +++++++++++++++++++ 2 files changed, 49 insertions(+) diff --git a/object/sign.go b/object/sign.go index acb9901..506fc95 100644 --- a/object/sign.go +++ b/object/sign.go @@ -131,6 +131,36 @@ func (m DeleteRequest) SignedDataSize() int { return m.OwnerID.Size() + addressSize(m.Address) } +// SignedData returns payload bytes of the request. +func (m GetRangeRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + return data, m.ReadSignedData(data) +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m GetRangeRequest) ReadSignedData(p []byte) error { + if len(p) < m.SignedDataSize() { + return io.ErrUnexpectedEOF + } + + n, err := (&m.Range).MarshalTo(p) + if err != nil { + return err + } + + copy(p[n:], addressBytes(m.GetAddress())) + + return nil +} + +// SignedDataSize returns payload size of the request. +func (m GetRangeRequest) SignedDataSize() int { + return (&m.Range).Size() + addressSize(m.GetAddress()) +} + func addressSize(addr Address) int { return addr.CID.Size() + addr.ObjectID.Size() } diff --git a/object/sign_test.go b/object/sign_test.go index d6357b9..f8450ef 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -89,6 +89,25 @@ func TestSignVerifyRequests(t *testing.T) { }, }, }, + { // GetRangeRequest + constructor: func() sigType { + return new(GetRangeRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*GetRangeRequest).Range.Length++ + }, + func(s sigType) { + s.(*GetRangeRequest).Range.Offset++ + }, + func(s sigType) { + s.(*GetRangeRequest).Address.CID[0]++ + }, + func(s sigType) { + s.(*GetRangeRequest).Address.ObjectID[0]++ + }, + }, + }, } for _, item := range items { From 84671cd4aa49eaa0947a2774d8d1ac0857535806 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 14:58:29 +0300 Subject: [PATCH 38/68] object: implement signing payload methods on GetRangeHashRequest message --- object/sign.go | 76 +++++++++++++++++++++++++++++++++++++++++++++ object/sign_test.go | 28 +++++++++++++++++ 2 files changed, 104 insertions(+) diff --git a/object/sign.go b/object/sign.go index 506fc95..3b97d62 100644 --- a/object/sign.go +++ b/object/sign.go @@ -1,6 +1,7 @@ package object import ( + "encoding/binary" "io" ) @@ -161,6 +162,81 @@ func (m GetRangeRequest) SignedDataSize() int { return (&m.Range).Size() + addressSize(m.GetAddress()) } +// SignedData returns payload bytes of the request. +func (m GetRangeHashRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + return data, m.ReadSignedData(data) +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m GetRangeHashRequest) ReadSignedData(p []byte) error { + if len(p) < m.SignedDataSize() { + return io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], addressBytes(m.GetAddress())) + + off += copy(p[off:], sliceBytes(m.GetSalt())) + + copy(p[off:], rangeSetBytes(m.GetRanges())) + + return nil +} + +// SignedDataSize returns payload size of the request. +func (m GetRangeHashRequest) SignedDataSize() int { + var sz int + + sz += addressSize(m.GetAddress()) + + sz += rangeSetSize(m.GetRanges()) + + sz += sliceSize(m.GetSalt()) + + return sz +} + +func sliceSize(v []byte) int { + return 4 + len(v) +} + +func sliceBytes(v []byte) []byte { + data := make([]byte, sliceSize(v)) + + binary.BigEndian.PutUint32(data, uint32(len(v))) + + copy(data[4:], v) + + return data +} + +func rangeSetSize(rs []Range) int { + return 4 + len(rs)*16 // two uint64 fields +} + +func rangeSetBytes(rs []Range) []byte { + data := make([]byte, rangeSetSize(rs)) + + binary.BigEndian.PutUint32(data, uint32(len(rs))) + + off := 4 + + for i := range rs { + binary.BigEndian.PutUint64(data[off:], rs[i].Offset) + off += 8 + + binary.BigEndian.PutUint64(data[off:], rs[i].Length) + off += 8 + } + + return data +} + func addressSize(addr Address) int { return addr.CID.Size() + addr.ObjectID.Size() } diff --git a/object/sign_test.go b/object/sign_test.go index f8450ef..8b9a011 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -108,6 +108,34 @@ func TestSignVerifyRequests(t *testing.T) { }, }, }, + { // GetRangeHashRequest + constructor: func() sigType { + return &GetRangeHashRequest{ + Ranges: []Range{{}}, + Salt: []byte{1, 2, 3}, + } + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*GetRangeHashRequest).Address.CID[0]++ + }, + func(s sigType) { + s.(*GetRangeHashRequest).Address.ObjectID[0]++ + }, + func(s sigType) { + s.(*GetRangeHashRequest).Salt[0]++ + }, + func(s sigType) { + s.(*GetRangeHashRequest).Ranges[0].Length++ + }, + func(s sigType) { + s.(*GetRangeHashRequest).Ranges[0].Offset++ + }, + func(s sigType) { + s.(*GetRangeHashRequest).Ranges = nil + }, + }, + }, } for _, item := range items { From 4aac4d093de8b20a2f58acf9d982266c285ad29d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 15:09:31 +0300 Subject: [PATCH 39/68] object: implement signing payload methods on SearchRequest message --- object/sign.go | 46 +++++++++++++++++++++++++++++++++++---------- object/sign_test.go | 18 ++++++++++++++++++ 2 files changed, 54 insertions(+), 10 deletions(-) diff --git a/object/sign.go b/object/sign.go index 3b97d62..25d0b2f 100644 --- a/object/sign.go +++ b/object/sign.go @@ -181,9 +181,9 @@ func (m GetRangeHashRequest) ReadSignedData(p []byte) error { off += copy(p[off:], addressBytes(m.GetAddress())) - off += copy(p[off:], sliceBytes(m.GetSalt())) + off += copy(p[off:], rangeSetBytes(m.GetRanges())) - copy(p[off:], rangeSetBytes(m.GetRanges())) + off += copy(p[off:], m.GetSalt()) return nil } @@ -196,23 +196,49 @@ func (m GetRangeHashRequest) SignedDataSize() int { sz += rangeSetSize(m.GetRanges()) - sz += sliceSize(m.GetSalt()) + sz += len(m.GetSalt()) return sz } -func sliceSize(v []byte) int { - return 4 + len(v) +// SignedData returns payload bytes of the request. +func (m SearchRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + return data, m.ReadSignedData(data) } -func sliceBytes(v []byte) []byte { - data := make([]byte, sliceSize(v)) +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m SearchRequest) ReadSignedData(p []byte) error { + if len(p) < m.SignedDataSize() { + return io.ErrUnexpectedEOF + } - binary.BigEndian.PutUint32(data, uint32(len(v))) + var off int - copy(data[4:], v) + off += copy(p[off:], m.CID().Bytes()) - return data + binary.BigEndian.PutUint32(p[off:], m.GetQueryVersion()) + off += 4 + + copy(p[off:], m.GetQuery()) + + return nil +} + +// SignedDataSize returns payload size of the request. +func (m SearchRequest) SignedDataSize() int { + var sz int + + sz += m.CID().Size() + + sz += 4 // uint32 Version + + sz += len(m.GetQuery()) + + return sz } func rangeSetSize(rs []Range) int { diff --git a/object/sign_test.go b/object/sign_test.go index 8b9a011..4df1c2b 100644 --- a/object/sign_test.go +++ b/object/sign_test.go @@ -136,6 +136,24 @@ func TestSignVerifyRequests(t *testing.T) { }, }, }, + { // GetRangeHashRequest + constructor: func() sigType { + return &SearchRequest{ + Query: []byte{1, 2, 3}, + } + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + s.(*SearchRequest).ContainerID[0]++ + }, + func(s sigType) { + s.(*SearchRequest).Query[0]++ + }, + func(s sigType) { + s.(*SearchRequest).QueryVersion++ + }, + }, + }, } for _, item := range items { From 65d7c39e1a54d488f341eef33bab801d47c870f2 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Wed, 6 May 2020 15:32:13 +0300 Subject: [PATCH 40/68] service: fix comments --- service/meta.go | 2 +- service/types.go | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/service/meta.go b/service/meta.go index 3a945ac..3f01758 100644 --- a/service/meta.go +++ b/service/meta.go @@ -1,6 +1,6 @@ package service -// ResetMeta returns current value and sets RequestMetaHeader to empty value. +// CutMeta returns current value and sets RequestMetaHeader to empty value. func (m *RequestMetaHeader) CutMeta() RequestMetaHeader { cp := *m m.Reset() diff --git a/service/types.go b/service/types.go index 020cba0..c3148a0 100644 --- a/service/types.go +++ b/service/types.go @@ -22,7 +22,7 @@ type RawContainer interface { SetRaw(bool) } -// VersionContainer is an interface of the container of a numerical Version value with read access. +// VersionSource is an interface of the container of a numerical Version value with read access. type VersionSource interface { GetVersion() uint32 } From d8cc00b54cc6a148021e051362b212b7750d846c Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 10:49:23 +0300 Subject: [PATCH 41/68] Update to neofs-api v0.7.4 --- Makefile | 2 +- docs/service.md | 16 ++++++++++++++-- docs/session.md | 25 ++++++++---------------- service/verify.pb.go | Bin 35203 -> 39615 bytes service/verify.proto | 18 ++++++++++++------ session/service.go | 10 ---------- session/service.pb.go | Bin 27455 -> 21636 bytes session/service.proto | 43 +++++++++++++++--------------------------- 8 files changed, 50 insertions(+), 64 deletions(-) diff --git a/Makefile b/Makefile index 62a92ec..b99682b 100644 --- a/Makefile +++ b/Makefile @@ -1,4 +1,4 @@ -PROTO_VERSION=v0.7.3 +PROTO_VERSION=v0.7.4 PROTO_URL=https://github.com/nspcc-dev/neofs-api/archive/$(PROTO_VERSION).tar.gz B=\033[0;1m diff --git a/docs/service.md b/docs/service.md index eef1e49..9ed548e 100644 --- a/docs/service.md +++ b/docs/service.md @@ -17,6 +17,7 @@ - [RequestVerificationHeader.Signature](#service.RequestVerificationHeader.Signature) - [Token](#service.Token) - [Token.Info](#service.Token.Info) + - [TokenLifetime](#service.TokenLifetime) - [service/verify_test.proto](#service/verify_test.proto) @@ -129,10 +130,21 @@ User token granting rights for object manipulation | OwnerID | [bytes](#bytes) | | OwnerID is an owner of manipulation object | | verb | [Token.Info.Verb](#service.Token.Info.Verb) | | Verb is a type of request for which the token is issued | | Address | [refs.Address](#refs.Address) | | Address is an object address for which token is issued | -| Created | [uint64](#uint64) | | Created is an initial epoch of token lifetime | -| ValidUntil | [uint64](#uint64) | | ValidUntil is a last epoch of token lifetime | +| Lifetime | [TokenLifetime](#service.TokenLifetime) | | Lifetime is a lifetime of the session | | SessionKey | [bytes](#bytes) | | SessionKey is a public key of session key | + + + +### Message TokenLifetime +TokenLifetime carries a group of lifetime parameters of the token + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Created | [uint64](#uint64) | | Created carries an initial epoch of token lifetime | +| ValidUntil | [uint64](#uint64) | | ValidUntil carries a last epoch of token lifetime | + diff --git a/docs/session.md b/docs/session.md index 4a537e6..5ec7402 100644 --- a/docs/session.md +++ b/docs/session.md @@ -30,22 +30,13 @@ ``` -rpc Create(stream CreateRequest) returns (stream CreateResponse); +rpc Create(CreateRequest) returns (CreateResponse); ``` #### Method Create -Create is a method that used to open a trusted session to manipulate -an object. In order to put or delete object client have to obtain session -token with trusted node. Trusted node will modify client's object -(add missing headers, checksums, homomorphic hash) and sign id with -session key. Session is established during 4-step handshake in one gRPC stream - -- First client stream message SHOULD BE type of `CreateRequest_Init`. -- First server stream message SHOULD BE type of `CreateResponse_Unsigned`. -- Second client stream message SHOULD BE type of `CreateRequest_Signed`. -- Second server stream message SHOULD BE type of `CreateResponse_Result`. +Create opens new session between the client and the server | Name | Input | Output | | ---- | ----- | ------ | @@ -56,13 +47,13 @@ session key. Session is established during 4-step handshake in one gRPC stream ### Message CreateRequest - +CreateRequest carries an information necessary for opening a session | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Init | [service.Token](#service.Token) | | Init is a message to initialize session opening. Carry: owner of manipulation object; ID of manipulation object; token lifetime bounds. | -| Signed | [service.Token](#service.Token) | | Signed Init message response (Unsigned) from server with user private key | +| OwnerID | [bytes](#bytes) | | OwnerID carries an identifier of a session initiator | +| Lifetime | [service.TokenLifetime](#service.TokenLifetime) | | Lifetime carries a lifetime of the session | | Meta | [service.RequestMetaHeader](#service.RequestMetaHeader) | | RequestMetaHeader contains information about request meta headers (should be embedded into message) | | Verify | [service.RequestVerificationHeader](#service.RequestVerificationHeader) | | RequestVerificationHeader is a set of signatures of every NeoFS Node that processed request (should be embedded into message) | @@ -70,13 +61,13 @@ session key. Session is established during 4-step handshake in one gRPC stream ### Message CreateResponse - +CreateResponse carries an information about the opened session | Field | Type | Label | Description | | ----- | ---- | ----- | ----------- | -| Unsigned | [service.Token](#service.Token) | | Unsigned token with token ID and session public key generated on server side | -| Result | [service.Token](#service.Token) | | Result is a resulting token which can be used for object placing through an trusted intermediary | +| ID | [bytes](#bytes) | | ID carries an identifier of session token | +| SessionKey | [bytes](#bytes) | | SessionKey carries a session public key | diff --git a/service/verify.pb.go b/service/verify.pb.go index 023e6392bb2ee316a7b3ddcc8c6726604981a694..3dadf0b559b9930f8b0dccd14d645b45c1b8f4d7 100644 GIT binary patch delta 4300 zcmZ{nO^6(28pr7|#+_D|fJThLnc9p-jjidfs;;gwNXAW8P!n-kML`d#Pi>v)8GCwS zYc{FX1yL6kHZRB?_99*cKM)#kipqkAJ*?*DLEQD~&8vd=zW-m(^o(L3UaH=DKL5}E zdEU4GYUV!vVDA0Rh5A&#Do6d*((7+{skgHAwpMwgr%G?5+}P-^4Vv>0uRT)^wq?(( z57$O(*=BXuq3rolxzXyj*49U@LAqMry(QF=OwcM_$PFI{|34%ar8LEcZQBE{Kj@G8>56%6;aH<%K3h1kmyf2oc*kLH z`?uL2jH1xK?m*#eI<&hj-~4g$n7Od;)vI?UVE%Uf!qOHl=gxcQo7LtZ_ikPFj_j8E z@cNU#*^;|>U}^i=9J%tLgZ}yEuPVNA<;kD9{QSU&S4>TrAFm0_zNO1&=5AP8oV|Q= zLpXbRZt3W@+R^>D?^gTY(vg|uxtkuFy?lAoBa@3azu7SVK74St`}N`LW-tCNug^t2 z^PgK*>u%8U+T*ZrYgD{`?HxXk27wlh5^T2p0G$#Vxaq99vA6$b(-)K;vm0H_p_zXOJjo-x3K&id-HFxz2J436elLl=2x#xdaS7S0n`tn3gX4V4*bYaY_z^QK~?R zGy=fTb12(w?Q~@|R}@&vwBsxDKo)FqZ8~7lE0jWjA%fAG*}~M8T+%M29V>N8QVEPg zesa*0TCSCxedTYVa$HU1x&%F`!GmB&@Kd78MB6&H{%sL$tc8RsnW)4V5N@Mf$wLRL zDn_EPQz@VvsU)#v=!r@@z>q6Jk#5{mF=CsP_1~4b7_ugE*V)0eSh77?g+x7-f!yfH zuA&GelM1S?GAyMVQTiSr39(EdB}uEcsb|uI8eM%(6f51(5g3^~B(jt!x`420E=8Rt z@=(b(l|+rEjPNN#43Sc`l83fLX;4QZTRp8^DY&v)RM}Br zfy@C61qdW#8Se7W<-tx1`)q6Cs08;zk`|zm?B(Z4&DwDe za&n7n=d=x!Dj6h>WDok#Bg`WNVH6()MFY~fiYvMTDED20GddYk;+8z6{F8|Q51|!O9NMiLI7l> zD`lo7v&R`BoJ0P3~h5}=P+057}6fnmxhbHXpC{GKq z^+`#9hvJ2DhRx9WcOELZuK>6nqPXG zEZin)J9llAoLbO?^MyG90l_U*w$wADNQ%yu+j-yy6KN~i0Sts@1pvkrg{dvX)<0?t zc7$jpd`>-(BE7_T%T@YN9D>)3MkY5vV2bDPZ3ucB2 z(-qv14tOXGqXY=KsTJ~UzdDehHRd!?>gnxqdU=AZDj$<8QAA`;(Wh4qRk9<=SkIOo z&I90bT>8S6{@_;If#Pt>6{#MBAq|=;tLc3~$~}paRTnB%VhqOx3``X^T#XeHM{y<^ z00uLgo}?Re38=?*Tss2DgzZC@1HHrzhuaAk26~1wfFlxq{8Exw82bQLsSNf=l>-Il z=w|O9*qa{kl0vq}DW?K(Tuo7xa;RBCiBoB%Tk_5W^OFEV;1+vm90r5~&-&-y$n*zK z<|Ox7dYK%!NLgY!58TD3LrLR+$u%8B^2P{=s4A%`7W}tuAiN`Z}|D;(q&|_LCF&p13)Ge)8$PH}1V=#k<>Ey>s_J(Br3Cb1R;?^!xqh-07Fi zyARB7{~dh#zLVzbQ!kt7{y$9`4}RY=?F-Ah|8!2gNA}E_eP`#*nMZg0+w{(!X+-A5 zv)4}A7e2Lr@dvZ`C;xr$|Bb75hG5=){E6LG%|F(w$qz5Q*f5_xyx;ur!t&(Opa0O9 O6f0jfCf~huaqmwCMVc=F delta 3378 zcmYk8y~`a%6vnwPN#sqTm53to1(U+?zOcJ@KkmjR5-tk1eEGu3{P6tKt8$(%yg2`N?%h?jyZPMrr{+IixISOI`q+H= z;`RCD;vw_DpMQP#^~L+E`P1Y1AW!EfPTrsD?D#0z=6xJDLmc}xE8X$g!(^K#Y?N_q zsyL<`&6wvHlucZ(n6DYDF|H(>Oj@jPnx%2B?WTzU)3(^ewK8Xiu{0#@>bRaTYTe?a z2Gi&|R&qTUFFtz^wmHx#DGXh7mol~yVA>9#8AXDRirfZ#RG4izQ!%Ev>&UZ9AZbl_ zy6Euj(Hi4$C?;)Ei~|8DF@v6I4Ooq|PWd_-((+TClAM=Dm^5>Rhh*Q|R>Y{q1? z&;(Fn&|%QysN5?+X}~@BJ#niX238y7UeQxx3=GFYS6H{0DLV}#mZ4@z8hS`nY6@_cUcsMa*xV91Kup2d5UH)=nS7t5rkrAw9cbaI zhk1=f#k?mq$Y4X&dluH1lks-U6sAwm}UG zCHRF13d;!5DQ2O^Vn11K5|Mguy#-6?{StSZHTc$0WYs0;KrD2NCneQ%?Z%c~wV?*d z1K%1-w6D*|$&oY4{)Wcvm5g*UdXP52*!-UzItCWyW zg1g+fly2o^&$~)N$XX8S7aM7a}Xrbcgj1N5q8VS z$75ST=VbL#5f+J}Ko&9Ml(*Zv)bZd%lpN^&)~~*uIvVf8MtDzR^iG1DQ|T65gy>qZ&}wn$cgF z(ip~wp!M=g#z0ZBW0%hxX#U!2G*g9MzL1C&pUsdz+dePh;}H?O#mlEtk@1N%B&;p5 z&!zt*Ux*BMiq(jf&;JOWL98RdqFI6Gn}IQMSp3nlM!AM7U!s;M`~>*tTU^i{%U2aY zK{z8qQq6B)diEJLLj>ETC%!V-eS7KtC?%x}E< z)b7t~zn`8T-S}!gyz#^Cx3?Z0&kyci-hJ}TORM?bohNq>?tZyC(={IsN7ek^J8$m( Oy7T#|-O0W0&ioJOLuSVS diff --git a/service/verify.proto b/service/verify.proto index b25cd47..ed360be 100644 --- a/service/verify.proto +++ b/service/verify.proto @@ -58,14 +58,11 @@ message Token { // Address is an object address for which token is issued refs.Address Address = 4 [(gogoproto.nullable) = false, (gogoproto.customtype) = "Address"]; - // Created is an initial epoch of token lifetime - uint64 Created = 5; - - // ValidUntil is a last epoch of token lifetime - uint64 ValidUntil = 6; + // Lifetime is a lifetime of the session + TokenLifetime Lifetime = 5 [(gogoproto.embed) = true, (gogoproto.nullable) = false]; // SessionKey is a public key of session key - bytes SessionKey = 7; + bytes SessionKey = 6; } // TokenInfo is a grouped information about token @@ -75,6 +72,15 @@ message Token { bytes Signature = 8; } +// TokenLifetime carries a group of lifetime parameters of the token +message TokenLifetime { + // Created carries an initial epoch of token lifetime + uint64 Created = 1; + + // ValidUntil carries a last epoch of token lifetime + uint64 ValidUntil = 2; +} + // TODO: for variable token types and version redefine message // Example: // message Token { diff --git a/session/service.go b/session/service.go index 6e293d3..ab87616 100644 --- a/session/service.go +++ b/session/service.go @@ -1,11 +1 @@ package session - -// NewInitRequest returns new initialization CreateRequest from passed Token. -func NewInitRequest(t *Token) *CreateRequest { - return &CreateRequest{Message: &CreateRequest_Init{Init: t}} -} - -// NewSignedRequest returns new signed CreateRequest from passed Token. -func NewSignedRequest(t *Token) *CreateRequest { - return &CreateRequest{Message: &CreateRequest_Signed{Signed: t}} -} diff --git a/session/service.pb.go b/session/service.pb.go index 1088308c46f56a8bbc670655cdb94f03a1fae586..e68c0fdd58e180f56fc7c5b5de292b9c1da029a1 100644 GIT binary patch delta 4753 zcmbVPO^jPt71ktahz)J?+ey+kdT~l)CyvLz&-Rdsm}Vw0Fm0nuQ>2apc|Z3hkJ!)7 z^P9v&6s$_H2i*liP!LOY6<)HSNZBA3u<8mSSiuIdM<6)&`<`Q)X)8*k+_}E@ob!F> z=bm@(uRdJ(&d(R#-`W4Ar6ucXteiyMQ1^G#AhA3rjzcxDoX85Jpci+YB@KvfIbbwgJys-yHO!j(b$%<4>p2mM|&m?1GAV#n?6B;aDgtNaNi{oUtsjZX?|ngc5_jI$XL z1^10L{bBaZXl+7wCR*wKgs+5+aoS=wzp8d;A~iAJ-*G#kXWdh~+02Zf_4MENEt4?$ zIh#WLhvSDYO~q3(6XcnVUN=lsx1a3lx1Z1H*1@wnICw$7cQCK57Y>cj9gg;ET{|K_ zKKtTSD_^iu)JNG&7+78X?&V{j>z1Yx`kyCGj?bOUFD%%4^VFS@T`yYYVY^~RFEFF6 z%;-C2te1<~OnF!-W7{@kqisgVHDkGIMi(ZW2G)J7Kv`j|z^F(0rrE7v-UQ^RRRuz; zW_E07w}8+9jRtHuSZPDM0ZigM*!6*|z^)B6T4+MjYXQK6=^BJ=f*Et8VTIaY)5P5M z43{!kRd>ctAA;b<#jA%e^V7&$$7MH@h zjj;koHOSW>*>-{8!giI0pv;g|F(=g;XxC*$@Oc85874h|GKD%b7sSZ zZKk^cN95p`;9+dQ=NeOjl{B;x9>HhK6-+1=YYjf|On7jY1e)OOlRXj|pgufs5oN*j zSd$DDiyo2+GJX>-G!VU(UmS@BaROl05lZ^Ta0d`=@&u3;W7BE2^Mz+yj_k!V*%Kh^BmT^86RxKjd?4BK!Ym`4>H-J>*Ru(WU9(7 z;0F+`BYIVO=mWt4Z#JAV<|L+Im7#AznK@)T^squqwy)GE0C@J&TD6-ald>A!MJOxa zYD#m`4i$JaxXfD0%j60fm6W6A-|VkTLGCCNWA5WyMkOpIwPvhlJD9&2IyVRdI@Z zW(hcT*ro!zWn<=Kq%{n}HrTN#v)}PUP;*D{A^rS;QxnOhyEf1iaCktO9(HSKY2pJL zz8oc)^9#jjr(dzO?Xf0cng_9tTvbLh@nNH`!YaEMxv*RDtI3wm6U$7>c{gdGiwtWE zN#|Ft$K*jp<`7?*Z$ht853eaCW|FZr`;iUfl`6nN@ zx%iGHS1xm7U0yMe`H2Buatqmviet0ql{~Ms5j~L0Y2HhQMJu;7;5Bn{OIxC9t|-rf zq9rEv-}2RLtdgBL!p|cY?`{7dzGX8socDTBqJ~N7D*qPey-oCw3NPyQ(paybKXEix z_xEm}e%?5nGK#`ZQJ-De*cQCe}6gii_44qu6I+k0M z)ns)$T)tHlR?}xuA*+AbzHn@hR-vHZdF`d9F*|i;W9@(PLmr$T<-#lDAG~HSywtHi zDlUlgH& zuWgNg`Q;Dxe$=KeI=Ge*gdg literal 27455 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 Date: Fri, 8 May 2020 11:08:09 +0300 Subject: [PATCH 42/68] session: implement getters and setters on messages --- service/token.go | 8 ++++---- session/request.go | 11 +++++++++++ session/request_test.go | 29 +++++++++++++++++++++++++++++ session/response.go | 16 ++++++++++++++++ session/response_test.go | 27 +++++++++++++++++++++++++++ session/service.go | 1 - 6 files changed, 87 insertions(+), 5 deletions(-) create mode 100644 session/request.go create mode 100644 session/request_test.go create mode 100644 session/response.go create mode 100644 session/response_test.go delete mode 100644 session/service.go diff --git a/service/token.go b/service/token.go index f431427..78fccfa 100644 --- a/service/token.go +++ b/service/token.go @@ -75,22 +75,22 @@ func (m *Token_Info) SetAddress(addr Address) { } // CreationEpoch is a Created field getter. -func (m Token_Info) CreationEpoch() uint64 { +func (m TokenLifetime) CreationEpoch() uint64 { return m.Created } // SetCreationEpoch is a Created field setter. -func (m *Token_Info) SetCreationEpoch(e uint64) { +func (m *TokenLifetime) SetCreationEpoch(e uint64) { m.Created = e } // ExpirationEpoch is a ValidUntil field getter. -func (m Token_Info) ExpirationEpoch() uint64 { +func (m TokenLifetime) ExpirationEpoch() uint64 { return m.ValidUntil } // SetExpirationEpoch is a ValidUntil field setter. -func (m *Token_Info) SetExpirationEpoch(e uint64) { +func (m *TokenLifetime) SetExpirationEpoch(e uint64) { m.ValidUntil = e } diff --git a/session/request.go b/session/request.go new file mode 100644 index 0000000..4a92c42 --- /dev/null +++ b/session/request.go @@ -0,0 +1,11 @@ +package session + +// GetOwnerID is an OwnerID field getter. +func (m CreateRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *CreateRequest) SetOwnerID(id OwnerID) { + m.OwnerID = id +} diff --git a/session/request_test.go b/session/request_test.go new file mode 100644 index 0000000..06c62fd --- /dev/null +++ b/session/request_test.go @@ -0,0 +1,29 @@ +package session + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCreateRequestGettersSetters(t *testing.T) { + t.Run("owner ID", func(t *testing.T) { + id := OwnerID{1, 2, 3} + m := new(CreateRequest) + + m.SetOwnerID(id) + + require.Equal(t, id, m.GetOwnerID()) + }) + + t.Run("lifetime", func(t *testing.T) { + e1, e2 := uint64(3), uint64(4) + m := new(CreateRequest) + + m.SetCreationEpoch(e1) + m.SetExpirationEpoch(e2) + + require.Equal(t, e1, m.CreationEpoch()) + require.Equal(t, e2, m.ExpirationEpoch()) + }) +} diff --git a/session/response.go b/session/response.go new file mode 100644 index 0000000..3426d7c --- /dev/null +++ b/session/response.go @@ -0,0 +1,16 @@ +package session + +// GetID is an ID field getter. +func (m CreateResponse) GetID() TokenID { + return m.ID +} + +// SetID is an ID field setter. +func (m *CreateResponse) SetID(id TokenID) { + m.ID = id +} + +// SetSessionKey is a SessionKey field setter. +func (m *CreateResponse) SetSessionKey(key []byte) { + m.SessionKey = key +} diff --git a/session/response_test.go b/session/response_test.go new file mode 100644 index 0000000..0e1de0b --- /dev/null +++ b/session/response_test.go @@ -0,0 +1,27 @@ +package session + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestCreateResponseGettersSetters(t *testing.T) { + t.Run("id", func(t *testing.T) { + id := TokenID{1, 2, 3} + m := new(CreateResponse) + + m.SetID(id) + + require.Equal(t, id, m.GetID()) + }) + + t.Run("session key", func(t *testing.T) { + key := []byte{1, 2, 3} + m := new(CreateResponse) + + m.SetSessionKey(key) + + require.Equal(t, key, m.GetSessionKey()) + }) +} diff --git a/session/service.go b/session/service.go deleted file mode 100644 index ab87616..0000000 --- a/session/service.go +++ /dev/null @@ -1 +0,0 @@ -package session From 6d71ea239bb729a8021a39311b1bd790b0331534 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 11:55:19 +0300 Subject: [PATCH 43/68] session: implement SignedDataSource on CreateRequest --- session/request.go | 47 ++++++++++++++++++++++++++++++ session/request_test.go | 63 +++++++++++++++++++++++++++++++++++++++++ 2 files changed, 110 insertions(+) diff --git a/session/request.go b/session/request.go index 4a92c42..85f563f 100644 --- a/session/request.go +++ b/session/request.go @@ -1,5 +1,19 @@ package session +import ( + "encoding/binary" + "io" + + "github.com/nspcc-dev/neofs-api-go/refs" +) + +const signedRequestDataSize = 0 + + refs.OwnerIDSize + + 8 + + 8 + +var requestEndianness = binary.BigEndian + // GetOwnerID is an OwnerID field getter. func (m CreateRequest) GetOwnerID() OwnerID { return m.OwnerID @@ -9,3 +23,36 @@ func (m CreateRequest) GetOwnerID() OwnerID { func (m *CreateRequest) SetOwnerID(id OwnerID) { m.OwnerID = id } + +func (m CreateRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + _, err := m.ReadSignedData(data) + if err != nil { + return nil, err + } + + return data, nil +} + +func (m CreateRequest) SignedDataSize() int { + return signedRequestDataSize +} + +func (m CreateRequest) ReadSignedData(p []byte) (int, error) { + sz := m.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetOwnerID().Bytes()) + + requestEndianness.PutUint64(p[off:], m.CreationEpoch()) + off += 8 + + requestEndianness.PutUint64(p[off:], m.ExpirationEpoch()) + + return sz, nil +} diff --git a/session/request_test.go b/session/request_test.go index 06c62fd..094ca66 100644 --- a/session/request_test.go +++ b/session/request_test.go @@ -27,3 +27,66 @@ func TestCreateRequestGettersSetters(t *testing.T) { require.Equal(t, e2, m.ExpirationEpoch()) }) } + +func TestCreateRequest_SignedData(t *testing.T) { + var ( + id = OwnerID{1, 2, 3} + e1 = uint64(1) + e2 = uint64(2) + ) + + // create new message + m := new(CreateRequest) + + // fill the fields + m.SetOwnerID(id) + m.SetCreationEpoch(e1) + m.SetExpirationEpoch(e2) + + // calculate initial signed data + d, err := m.SignedData() + require.NoError(t, err) + + items := []struct { + change func() + reset func() + }{ + { // OwnerID + change: func() { + id2 := id + id2[0]++ + m.SetOwnerID(id2) + }, + reset: func() { + m.SetOwnerID(id) + }, + }, + { // CreationEpoch + change: func() { + m.SetCreationEpoch(e1 + 1) + }, + reset: func() { + m.SetCreationEpoch(e1) + }, + }, + { // ExpirationEpoch + change: func() { + m.SetExpirationEpoch(e2 + 1) + }, + reset: func() { + m.SetExpirationEpoch(e2) + }, + }, + } + + for _, item := range items { + item.change() + + d2, err := m.SignedData() + require.NoError(t, err) + + require.NotEqual(t, d, d2) + + item.reset() + } +} From b079a7604f19ef5aa5fa4abed0cb7a394fb14d34 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 12:34:16 +0300 Subject: [PATCH 44/68] session: implement gRPC session creator --- refs/types.go | 7 ++- service/types.go | 18 +++++-- session/create.go | 67 +++++++++++++++++++++++++++ session/create_test.go | 103 +++++++++++++++++++++++++++++++++++++++++ session/request.go | 4 ++ session/types.go | 25 ++++++++++ 6 files changed, 219 insertions(+), 5 deletions(-) create mode 100644 session/create.go create mode 100644 session/create_test.go diff --git a/refs/types.go b/refs/types.go index a29424e..417eec3 100644 --- a/refs/types.go +++ b/refs/types.go @@ -37,9 +37,14 @@ type ( OwnerID chain.WalletAddress ) +// OwnerIDSource is an interface of the container of an OwnerID value with read access. +type OwnerIDSource interface { + GetOwnerID() OwnerID +} + // OwnerIDContainer is an interface of the container of an OwnerID value. type OwnerIDContainer interface { - GetOwnerID() OwnerID + OwnerIDSource SetOwnerID(OwnerID) } diff --git a/service/types.go b/service/types.go index c3148a0..52d68d2 100644 --- a/service/types.go +++ b/service/types.go @@ -124,6 +124,18 @@ type ExpirationEpochContainer interface { SetExpirationEpoch(uint64) } +// LifetimeSource is an interface of the container of creation-expiration epoch pair with read access. +type LifetimeSource interface { + CreationEpochSource + ExpirationEpochSource +} + +// LifetimeSource is an interface of the container of creation-expiration epoch pair. +type LifetimeContainer interface { + CreationEpochContainer + ExpirationEpochContainer +} + // SessionKeySource is an interface of the container of session key bytes with read access. type SessionKeySource interface { GetSessionKey() []byte @@ -157,16 +169,14 @@ type SessionTokenSource interface { // - ID of the token's owner; // - verb of the session; // - address of the session object; -// - creation epoch number of the token; -// - expiration epoch number of the token; +// - token lifetime; // - public session key bytes. type SessionTokenInfo interface { TokenIDContainer OwnerIDContainer VerbContainer AddressContainer - CreationEpochContainer - ExpirationEpochContainer + LifetimeContainer SessionKeyContainer } diff --git a/session/create.go b/session/create.go new file mode 100644 index 0000000..a2d2b99 --- /dev/null +++ b/session/create.go @@ -0,0 +1,67 @@ +package session + +import ( + "context" + "crypto/ecdsa" + + "github.com/nspcc-dev/neofs-api-go/internal" + "github.com/nspcc-dev/neofs-api-go/service" + crypto "github.com/nspcc-dev/neofs-crypto" + "google.golang.org/grpc" +) + +type gRPCCreator struct { + conn *grpc.ClientConn + + key *ecdsa.PrivateKey + + clientFunc func(*grpc.ClientConn) SessionClient +} + +const ErrNilCreateParamsSource = internal.Error("create params source is nil") + +const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil") + +// NewGRPCCreator unites virtual gRPC client with private ket and returns Creator interface. +// +// If passed ClientConn is nil, ErrNilGPRCClientConn returns. +// If passed private key is nil, crypto.ErrEmptyPrivateKey returns. +func NewGRPCCreator(conn *grpc.ClientConn, key *ecdsa.PrivateKey) (Creator, error) { + if conn == nil { + return nil, ErrNilGPRCClientConn + } else if key == nil { + return nil, crypto.ErrEmptyPrivateKey + } + + return &gRPCCreator{ + conn: conn, + + key: key, + + clientFunc: NewSessionClient, + }, nil +} + +// Create constructs message, signs it with private key and sends it to a gRPC client. +// +// If passed CreateParamsSource is nil, ErrNilCreateParamsSource returns. +// If message could not be signed, an error returns. +func (s gRPCCreator) Create(ctx context.Context, p CreateParamsSource) (CreateResult, error) { + if p == nil { + return nil, ErrNilCreateParamsSource + } + + // create and fill a message + req := new(CreateRequest) + req.SetOwnerID(p.GetOwnerID()) + req.SetCreationEpoch(p.CreationEpoch()) + req.SetExpirationEpoch(p.ExpirationEpoch()) + + // sign with private key + if err := service.SignDataWithSessionToken(s.key, req); err != nil { + return nil, err + } + + // make gRPC call + return s.clientFunc(s.conn).Create(ctx, req) +} diff --git a/session/create_test.go b/session/create_test.go new file mode 100644 index 0000000..732d4fd --- /dev/null +++ b/session/create_test.go @@ -0,0 +1,103 @@ +package session + +import ( + "context" + "crypto/ecdsa" + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + crypto "github.com/nspcc-dev/neofs-crypto" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/pkg/errors" + "github.com/stretchr/testify/require" + "google.golang.org/grpc" +) + +type testSessionClient struct { + fn func(*CreateRequest) + resp *CreateResponse + err error +} + +func (s testSessionClient) Create(ctx context.Context, in *CreateRequest, opts ...grpc.CallOption) (*CreateResponse, error) { + if s.fn != nil { + s.fn(in) + } + + return s.resp, s.err +} + +func TestNewGRPCCreator(t *testing.T) { + var ( + err error + conn = new(grpc.ClientConn) + sk = new(ecdsa.PrivateKey) + ) + + // nil client connection + _, err = NewGRPCCreator(nil, sk) + require.EqualError(t, err, ErrNilGPRCClientConn.Error()) + + // nil private key + _, err = NewGRPCCreator(conn, nil) + require.EqualError(t, err, crypto.ErrEmptyPrivateKey.Error()) + + // valid params + res, err := NewGRPCCreator(conn, sk) + require.NoError(t, err) + + v := res.(*gRPCCreator) + require.Equal(t, conn, v.conn) + require.Equal(t, sk, v.key) + require.NotNil(t, v.clientFunc) +} + +func TestGRPCCreator_Create(t *testing.T) { + ctx := context.TODO() + s := new(gRPCCreator) + + // nil CreateParamsSource + _, err := s.Create(ctx, nil) + require.EqualError(t, err, ErrNilCreateParamsSource.Error()) + + var ( + ownerID = OwnerID{1, 2, 3} + created = uint64(2) + expired = uint64(4) + ) + + p := NewParams() + p.SetOwnerID(ownerID) + p.SetCreationEpoch(created) + p.SetExpirationEpoch(expired) + + // nil private key + _, err = s.Create(ctx, p) + require.Error(t, err) + + // create test private key + s.key = test.DecodeKey(0) + + // create test client + c := &testSessionClient{ + fn: func(req *CreateRequest) { + require.Equal(t, ownerID, req.GetOwnerID()) + require.Equal(t, created, req.CreationEpoch()) + require.Equal(t, expired, req.ExpirationEpoch()) + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(req)) + }, + resp: &CreateResponse{ + ID: TokenID{1, 2, 3}, + SessionKey: []byte{1, 2, 3}, + }, + err: errors.New("test error"), + } + + s.clientFunc = func(*grpc.ClientConn) SessionClient { + return c + } + + res, err := s.Create(ctx, p) + require.EqualError(t, err, c.err.Error()) + require.Equal(t, c.resp, res) +} diff --git a/session/request.go b/session/request.go index 85f563f..161edca 100644 --- a/session/request.go +++ b/session/request.go @@ -14,6 +14,10 @@ const signedRequestDataSize = 0 + var requestEndianness = binary.BigEndian +func NewParams() CreateParamsContainer { + return new(CreateRequest) +} + // GetOwnerID is an OwnerID field getter. func (m CreateRequest) GetOwnerID() OwnerID { return m.OwnerID diff --git a/session/types.go b/session/types.go index c890aaf..9e8db48 100644 --- a/session/types.go +++ b/session/types.go @@ -5,6 +5,8 @@ import ( "crypto/ecdsa" "github.com/nspcc-dev/neofs-api-go/internal" + "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" ) // PrivateToken is an interface of session private part. @@ -55,5 +57,28 @@ type KeyStore interface { Get(context.Context, OwnerID) ([]*ecdsa.PublicKey, error) } +// CreateParamsSource is an interface of the container of session parameters with read access. +type CreateParamsSource interface { + refs.OwnerIDSource + service.LifetimeSource +} + +// CreateParamsContainer is an interface of the container of session parameters. +type CreateParamsContainer interface { + refs.OwnerIDContainer + service.LifetimeContainer +} + +// CreateResult is an interface of the container of an opened session info with read access. +type CreateResult interface { + service.TokenIDSource + service.SessionKeySource +} + +// Creator is an interface of the tool for a session opening. +type Creator interface { + Create(context.Context, CreateParamsSource) (CreateResult, error) +} + // ErrPrivateTokenNotFound is raised when addressed private token was not found in storage. const ErrPrivateTokenNotFound = internal.Error("private token not found") From 2c2150b1017c8368c20b0110215a67dd9b329d75 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 12:37:56 +0300 Subject: [PATCH 45/68] session: move errors to a separate file --- session/create.go | 5 ----- session/errors.go | 15 +++++++++++++++ session/types.go | 4 ---- 3 files changed, 15 insertions(+), 9 deletions(-) create mode 100644 session/errors.go diff --git a/session/create.go b/session/create.go index a2d2b99..35d0540 100644 --- a/session/create.go +++ b/session/create.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ecdsa" - "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/service" crypto "github.com/nspcc-dev/neofs-crypto" "google.golang.org/grpc" @@ -18,10 +17,6 @@ type gRPCCreator struct { clientFunc func(*grpc.ClientConn) SessionClient } -const ErrNilCreateParamsSource = internal.Error("create params source is nil") - -const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil") - // NewGRPCCreator unites virtual gRPC client with private ket and returns Creator interface. // // If passed ClientConn is nil, ErrNilGPRCClientConn returns. diff --git a/session/errors.go b/session/errors.go new file mode 100644 index 0000000..3a9c129 --- /dev/null +++ b/session/errors.go @@ -0,0 +1,15 @@ +package session + +import "github.com/nspcc-dev/neofs-api-go/internal" + +// ErrNilCreateParamsSource is returned by functions that expect a non-nil +// CreateParamsSource, but received nil. +const ErrNilCreateParamsSource = internal.Error("create params source is nil") + +// ErrNilGPRCClientConn is returned by functions that expect a non-nil +// grpc.ClientConn, but received nil. +const ErrNilGPRCClientConn = internal.Error("gRPC client connection is nil") + +// ErrPrivateTokenNotFound is returned when addressed private token was +// not found in storage. +const ErrPrivateTokenNotFound = internal.Error("private token not found") diff --git a/session/types.go b/session/types.go index 9e8db48..932fe38 100644 --- a/session/types.go +++ b/session/types.go @@ -4,7 +4,6 @@ import ( "context" "crypto/ecdsa" - "github.com/nspcc-dev/neofs-api-go/internal" "github.com/nspcc-dev/neofs-api-go/refs" "github.com/nspcc-dev/neofs-api-go/service" ) @@ -79,6 +78,3 @@ type CreateResult interface { type Creator interface { Create(context.Context, CreateParamsSource) (CreateResult, error) } - -// ErrPrivateTokenNotFound is raised when addressed private token was not found in storage. -const ErrPrivateTokenNotFound = internal.Error("private token not found") From 15a55d54a22a21ff6e044754937d87222754ad85 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 12:45:16 +0300 Subject: [PATCH 46/68] fix comments --- service/types.go | 2 +- session/request.go | 6 ++++++ 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/service/types.go b/service/types.go index 52d68d2..31f4507 100644 --- a/service/types.go +++ b/service/types.go @@ -130,7 +130,7 @@ type LifetimeSource interface { ExpirationEpochSource } -// LifetimeSource is an interface of the container of creation-expiration epoch pair. +// LifetimeContainer is an interface of the container of creation-expiration epoch pair. type LifetimeContainer interface { CreationEpochContainer ExpirationEpochContainer diff --git a/session/request.go b/session/request.go index 161edca..0bb5176 100644 --- a/session/request.go +++ b/session/request.go @@ -14,6 +14,7 @@ const signedRequestDataSize = 0 + var requestEndianness = binary.BigEndian +// NewParams creates a new CreateRequest message and returns CreateParamsContainer interface. func NewParams() CreateParamsContainer { return new(CreateRequest) } @@ -28,6 +29,7 @@ func (m *CreateRequest) SetOwnerID(id OwnerID) { m.OwnerID = id } +// SignedData returns payload bytes of the request. func (m CreateRequest) SignedData() ([]byte, error) { data := make([]byte, m.SignedDataSize()) @@ -39,10 +41,14 @@ func (m CreateRequest) SignedData() ([]byte, error) { return data, nil } +// SignedDataSize returns payload size of the request. func (m CreateRequest) SignedDataSize() int { return signedRequestDataSize } +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. func (m CreateRequest) ReadSignedData(p []byte) (int, error) { sz := m.SignedDataSize() if len(p) < sz { From af73d958a19d2a7390363690c7e1ca182f386eeb Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 13:20:12 +0300 Subject: [PATCH 47/68] session: add OwnerID to a private token storage key --- session/private.go | 10 ++++++++++ session/private_test.go | 20 ++++++++++++++++++++ session/store.go | 12 ++++++------ session/store_test.go | 35 +++++++++++++++++++++++++---------- session/types.go | 10 ++++++++-- 5 files changed, 69 insertions(+), 18 deletions(-) diff --git a/session/private.go b/session/private.go index 8ebce81..42bb205 100644 --- a/session/private.go +++ b/session/private.go @@ -43,3 +43,13 @@ func (t *pToken) PublicKey() []byte { func (t *pToken) Expired(epoch uint64) bool { return t.validUntil < epoch } + +// SetOwnerID is an owner ID field setter. +func (s *PrivateTokenKey) SetOwnerID(id OwnerID) { + s.owner = id +} + +// SetTokenID is a token ID field setter. +func (s *PrivateTokenKey) SetTokenID(id TokenID) { + s.token = id +} diff --git a/session/private_test.go b/session/private_test.go index 7963afb..8097b97 100644 --- a/session/private_test.go +++ b/session/private_test.go @@ -48,3 +48,23 @@ func TestPToken_Expired(t *testing.T) { // must be expired in the epoch after last require.True(t, token.Expired(e+1)) } + +func TestPrivateTokenKey_SetOwnerID(t *testing.T) { + ownerID := OwnerID{1, 2, 3} + + s := new(PrivateTokenKey) + + s.SetOwnerID(ownerID) + + require.Equal(t, ownerID, s.owner) +} + +func TestPrivateTokenKey_SetTokenID(t *testing.T) { + tokenID := TokenID{1, 2, 3} + + s := new(PrivateTokenKey) + + s.SetTokenID(tokenID) + + require.Equal(t, tokenID, s.token) +} diff --git a/session/store.go b/session/store.go index 79998c7..fa33b7e 100644 --- a/session/store.go +++ b/session/store.go @@ -7,7 +7,7 @@ import ( type mapTokenStore struct { *sync.RWMutex - tokens map[TokenID]PrivateToken + tokens map[PrivateTokenKey]PrivateToken } // NewMapTokenStore creates new PrivateTokenStore instance. @@ -16,16 +16,16 @@ type mapTokenStore struct { func NewMapTokenStore() PrivateTokenStore { return &mapTokenStore{ RWMutex: new(sync.RWMutex), - tokens: make(map[TokenID]PrivateToken), + tokens: make(map[PrivateTokenKey]PrivateToken), } } // Store adds passed token to the map. // // Resulting error is always nil. -func (s *mapTokenStore) Store(id TokenID, token PrivateToken) error { +func (s *mapTokenStore) Store(key PrivateTokenKey, token PrivateToken) error { s.Lock() - s.tokens[id] = token + s.tokens[key] = token s.Unlock() return nil @@ -34,11 +34,11 @@ func (s *mapTokenStore) Store(id TokenID, token PrivateToken) error { // Fetch returns the map element corresponding to the given key. // // Returns ErrPrivateTokenNotFound is there is no element in map. -func (s *mapTokenStore) Fetch(id TokenID) (PrivateToken, error) { +func (s *mapTokenStore) Fetch(key PrivateTokenKey) (PrivateToken, error) { s.RLock() defer s.RUnlock() - t, ok := s.tokens[id] + t, ok := s.tokens[key] if !ok { return nil, ErrPrivateTokenNotFound } diff --git a/session/store_test.go b/session/store_test.go index 123b103..74e0023 100644 --- a/session/store_test.go +++ b/session/store_test.go @@ -15,19 +15,26 @@ func TestMapTokenStore(t *testing.T) { // create map token store s := NewMapTokenStore() - // create new storage key - id, err := refs.NewUUID() + // create test TokenID + tid, err := refs.NewUUID() require.NoError(t, err) + // create test OwnerID + ownerID := OwnerID{1, 2, 3} + + key := PrivateTokenKey{} + key.SetOwnerID(ownerID) + key.SetTokenID(tid) + // ascertain that there is no record for the key - _, err = s.Fetch(id) + _, err = s.Fetch(key) require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) // save private token record - require.NoError(t, s.Store(id, pToken)) + require.NoError(t, s.Store(key, pToken)) // fetch private token by the key - res, err := s.Fetch(id) + res, err := s.Fetch(key) require.NoError(t, err) // ascertain that returned token equals to initial @@ -52,7 +59,11 @@ func TestMapTokenStore_RemoveExpired(t *testing.T) { // create token store instance s := NewMapTokenStore() - // create storage keys for tokens + // create test PrivateTokenKey + key := PrivateTokenKey{} + key.SetOwnerID(OwnerID{1, 2, 3}) + + // create IDs for tokens id1, err := refs.NewUUID() require.NoError(t, err) id2, err := refs.NewUUID() @@ -60,21 +71,25 @@ func TestMapTokenStore_RemoveExpired(t *testing.T) { assertPresence := func(ids ...TokenID) { for i := range ids { - _, err = s.Fetch(ids[i]) + key.SetTokenID(ids[i]) + _, err = s.Fetch(key) require.NoError(t, err) } } assertAbsence := func(ids ...TokenID) { for i := range ids { - _, err = s.Fetch(ids[i]) + key.SetTokenID(ids[i]) + _, err = s.Fetch(key) require.EqualError(t, err, ErrPrivateTokenNotFound.Error()) } } // store both tokens - require.NoError(t, s.Store(id1, tok1)) - require.NoError(t, s.Store(id2, tok2)) + key.SetTokenID(id1) + require.NoError(t, s.Store(key, tok1)) + key.SetTokenID(id2) + require.NoError(t, s.Store(key, tok2)) // ascertain that both tokens are available assertPresence(id1, id2) diff --git a/session/types.go b/session/types.go index 932fe38..ee13b92 100644 --- a/session/types.go +++ b/session/types.go @@ -23,12 +23,18 @@ type PrivateToken interface { Expired(uint64) bool } +// PrivateTokenKey is a structure of private token storage key. +type PrivateTokenKey struct { + owner OwnerID + token TokenID +} + // PrivateTokenSource is an interface of private token storage with read access. type PrivateTokenSource interface { // Fetch must return the storage record corresponding to the passed key. // // Resulting error must be ErrPrivateTokenNotFound if there is no corresponding record. - Fetch(TokenID) (PrivateToken, error) + Fetch(PrivateTokenKey) (PrivateToken, error) } // EpochLifetimeStore is an interface of the storage of elements that lifetime is limited by NeoFS epoch. @@ -45,7 +51,7 @@ type PrivateTokenStore interface { // Store must save passed private token in the storage under the given key. // // Resulting error must be nil if private token was stored successfully. - Store(TokenID, PrivateToken) error + Store(PrivateTokenKey, PrivateToken) error } // KeyStore is an interface of the storage of public keys addressable by OwnerID, From dd2f568347f1f60218dde3b79674877fdc8679fc Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Fri, 8 May 2020 15:05:33 +0300 Subject: [PATCH 48/68] object: add Token header case to CopyTo --- object/types.go | 6 ++++++ object/types_test.go | 21 +++++++++++++++++++++ 2 files changed, 27 insertions(+) diff --git a/object/types.go b/object/types.go index cbc50f5..c8d3f25 100644 --- a/object/types.go +++ b/object/types.go @@ -251,6 +251,12 @@ func (m *Object) CopyTo(o *Object) { HomoHash: v.HomoHash, }, } + case *Header_Token: + o.Headers[i] = Header{ + Value: &Header_Token{ + Token: v.Token, + }, + } default: o.Headers[i] = *proto.Clone(&m.Headers[i]).(*Header) } diff --git a/object/types_test.go b/object/types_test.go index 95f328b..3f9292d 100644 --- a/object/types_test.go +++ b/object/types_test.go @@ -178,3 +178,24 @@ Object: require.NoError(t, Stringify(buf, obj)) require.Equal(t, res, buf.String()) } + +func TestObject_Copy(t *testing.T) { + t.Run("token header", func(t *testing.T) { + token := new(Token) + token.SetID(service.TokenID{1, 2, 3}) + + obj := new(Object) + + obj.AddHeader(&Header{ + Value: &Header_Token{ + Token: token, + }, + }) + + cp := obj.Copy() + + _, h := cp.LastHeader(HeaderType(TokenHdr)) + require.NotNil(t, h) + require.Equal(t, token, h.GetValue().(*Header_Token).Token) + }) +} From b9d30d613831de5f06bea5ef9a415c9ba719ce6d Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 11:59:39 +0300 Subject: [PATCH 49/68] accounting: implement SignedDataSource on BalanceRequest message --- accounting/sign.go | 33 ++++++++++++++++++++++++ accounting/sign_test.go | 54 ++++++++++++++++++++++++++++++++++++++++ accounting/types.go | 10 ++++++++ accounting/types_test.go | 9 +++++++ 4 files changed, 106 insertions(+) create mode 100644 accounting/sign.go create mode 100644 accounting/sign_test.go diff --git a/accounting/sign.go b/accounting/sign.go new file mode 100644 index 0000000..a42375f --- /dev/null +++ b/accounting/sign.go @@ -0,0 +1,33 @@ +package accounting + +import "io" + +// SignedData returns payload bytes of the request. +func (m BalanceRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m BalanceRequest) SignedDataSize() int { + return m.GetOwnerID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m BalanceRequest) ReadSignedData(p []byte) (int, error) { + sz := m.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF + } + + copy(p, m.GetOwnerID().Bytes()) + + return sz, nil +} diff --git a/accounting/sign_test.go b/accounting/sign_test.go new file mode 100644 index 0000000..03eaf6c --- /dev/null +++ b/accounting/sign_test.go @@ -0,0 +1,54 @@ +package accounting + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestSignBalanceRequest(t *testing.T) { + // create test OwnerID + ownerID := OwnerID{1, 2, 3} + + // create test BalanceRequest + req := new(BalanceRequest) + req.SetOwnerID(ownerID) + + // create test private key + sk := test.DecodeKey(0) + + items := []struct { + corrupt func() + restore func() + }{ + { + corrupt: func() { + ownerID[0]++ + req.SetOwnerID(ownerID) + }, + restore: func() { + ownerID[0]-- + req.SetOwnerID(ownerID) + }, + }, + } + + for _, item := range items { + // sign with private key + require.NoError(t, service.AddSignatureWithKey(sk, req)) + + // ascertain that verification is passed + require.NoError(t, service.VerifyAccumulatedSignatures(req)) + + // corrupt the request + item.corrupt() + + // ascertain that verification is failed + require.Error(t, service.VerifyAccumulatedSignatures(req)) + + // ascertain that request + item.restore() + } +} diff --git a/accounting/types.go b/accounting/types.go index 6a3b2e2..3ac637d 100644 --- a/accounting/types.go +++ b/accounting/types.go @@ -351,3 +351,13 @@ func (m *Settlement) Equal(s *Settlement) bool { } return len(m.Transactions) == 0 || reflect.DeepEqual(m.Transactions, s.Transactions) } + +// GetOwnerID is an OwnerID field getter. +func (m BalanceRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *BalanceRequest) SetOwnerID(owner OwnerID) { + m.OwnerID = owner +} diff --git a/accounting/types_test.go b/accounting/types_test.go index df81b46..e89c08c 100644 --- a/accounting/types_test.go +++ b/accounting/types_test.go @@ -84,3 +84,12 @@ func TestCheque(t *testing.T) { require.Equal(t, cheque.Amount, decimal.NewGAS(42)) }) } + +func TestBalanceRequest_SetOwnerID(t *testing.T) { + ownerID := OwnerID{1, 2, 3} + m := new(BalanceRequest) + + m.SetOwnerID(ownerID) + + require.Equal(t, ownerID, m.GetOwnerID()) +} From 6832d71d4870a43b48953d2c2aef23a99f9ad2a8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 12:54:21 +0300 Subject: [PATCH 50/68] accounting: implement SignedDataSource on GetRequest message --- accounting/sign.go | 34 ++++++++++++++ accounting/sign_test.go | 95 ++++++++++++++++++++++++++++------------ accounting/types.go | 20 +++++++++ accounting/types_test.go | 20 +++++++++ 4 files changed, 142 insertions(+), 27 deletions(-) diff --git a/accounting/sign.go b/accounting/sign.go index a42375f..8faeb96 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -31,3 +31,37 @@ func (m BalanceRequest) ReadSignedData(p []byte) (int, error) { return sz, nil } + +// SignedData returns payload bytes of the request. +func (m GetRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m GetRequest) SignedDataSize() int { + return m.GetID().Size() + m.GetOwnerID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m GetRequest) ReadSignedData(p []byte) (int, error) { + sz := m.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetID().Bytes()) + + copy(p[off:], m.GetOwnerID().Bytes()) + + return sz, nil +} diff --git a/accounting/sign_test.go b/accounting/sign_test.go index 03eaf6c..d059ab4 100644 --- a/accounting/sign_test.go +++ b/accounting/sign_test.go @@ -9,46 +9,87 @@ import ( ) func TestSignBalanceRequest(t *testing.T) { - // create test OwnerID - ownerID := OwnerID{1, 2, 3} - - // create test BalanceRequest - req := new(BalanceRequest) - req.SetOwnerID(ownerID) - - // create test private key sk := test.DecodeKey(0) + type sigType interface { + service.SignedDataWithToken + service.SignKeyPairAccumulator + service.SignKeyPairSource + SetToken(*service.Token) + } + items := []struct { - corrupt func() - restore func() + constructor func() sigType + payloadCorrupt []func(sigType) }{ - { - corrupt: func() { - ownerID[0]++ - req.SetOwnerID(ownerID) + { // BalanceRequest + constructor: func() sigType { + return new(BalanceRequest) }, - restore: func() { - ownerID[0]-- - req.SetOwnerID(ownerID) + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*BalanceRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + }, + }, + { // GetRequest + constructor: func() sigType { + return new(GetRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*GetRequest) + + id, err := NewChequeID() + require.NoError(t, err) + + req.SetID(id) + }, + func(s sigType) { + req := s.(*GetRequest) + + id := req.GetOwnerID() + id[0]++ + + req.SetOwnerID(id) + }, }, }, } for _, item := range items { - // sign with private key - require.NoError(t, service.AddSignatureWithKey(sk, req)) + { // token corruptions + v := item.constructor() - // ascertain that verification is passed - require.NoError(t, service.VerifyAccumulatedSignatures(req)) + token := new(service.Token) + v.SetToken(token) - // corrupt the request - item.corrupt() + require.NoError(t, service.SignDataWithSessionToken(sk, v)) - // ascertain that verification is failed - require.Error(t, service.VerifyAccumulatedSignatures(req)) + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) - // ascertain that request - item.restore() + token.SetSessionKey(append(token.GetSessionKey(), 1)) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + + { // payload corruptions + for _, corruption := range item.payloadCorrupt { + v := item.constructor() + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + corruption(v) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + } } } diff --git a/accounting/types.go b/accounting/types.go index 3ac637d..1e4e80a 100644 --- a/accounting/types.go +++ b/accounting/types.go @@ -361,3 +361,23 @@ func (m BalanceRequest) GetOwnerID() OwnerID { func (m *BalanceRequest) SetOwnerID(owner OwnerID) { m.OwnerID = owner } + +// GetID is an ID field getter. +func (m GetRequest) GetID() ChequeID { + return m.ID +} + +// SetID is an ID field setter. +func (m *GetRequest) SetID(id ChequeID) { + m.ID = id +} + +// GetOwnerID is an OwnerID field getter. +func (m GetRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *GetRequest) SetOwnerID(id OwnerID) { + m.OwnerID = id +} diff --git a/accounting/types_test.go b/accounting/types_test.go index e89c08c..ea17a8a 100644 --- a/accounting/types_test.go +++ b/accounting/types_test.go @@ -93,3 +93,23 @@ func TestBalanceRequest_SetOwnerID(t *testing.T) { require.Equal(t, ownerID, m.GetOwnerID()) } + +func TestGetRequestGettersSetters(t *testing.T) { + t.Run("id", func(t *testing.T) { + id := ChequeID("test id") + m := new(GetRequest) + + m.SetID(id) + + require.Equal(t, id, m.GetID()) + }) + + t.Run("owner", func(t *testing.T) { + id := OwnerID{1, 2, 3} + m := new(GetRequest) + + m.SetOwnerID(id) + + require.Equal(t, id, m.GetOwnerID()) + }) +} From 8c492a7712b44f6fd49ceb5c977801697370e2b6 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 13:14:31 +0300 Subject: [PATCH 51/68] accounting: implement SignedDataSource on PutRequest message --- accounting/sign.go | 59 +++++++++++++++++++++++++++++++++++++++- accounting/sign_test.go | 44 ++++++++++++++++++++++++++++++ accounting/types.go | 30 ++++++++++++++++++++ accounting/types_test.go | 39 ++++++++++++++++++++++++++ 4 files changed, 171 insertions(+), 1 deletion(-) diff --git a/accounting/sign.go b/accounting/sign.go index 8faeb96..8da8cf6 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -1,6 +1,9 @@ package accounting -import "io" +import ( + "encoding/binary" + "io" +) // SignedData returns payload bytes of the request. func (m BalanceRequest) SignedData() ([]byte, error) { @@ -65,3 +68,57 @@ func (m GetRequest) ReadSignedData(p []byte) (int, error) { return sz, nil } + +// SignedData returns payload bytes of the request. +func (m PutRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m PutRequest) SignedDataSize() (sz int) { + sz += m.GetOwnerID().Size() + + sz += m.GetMessageID().Size() + + sz += 8 + + if amount := m.GetAmount(); amount != nil { + sz += amount.Size() + } + + return +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m PutRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetOwnerID().Bytes()) + + off += copy(p[off:], m.GetMessageID().Bytes()) + + binary.BigEndian.PutUint64(p[off:], m.GetHeight()) + off += 8 + + if amount := m.GetAmount(); amount != nil { + n, err := amount.MarshalTo(p[off:]) + off += n + if err != nil { + return off + n, err + } + } + + return off, nil +} diff --git a/accounting/sign_test.go b/accounting/sign_test.go index d059ab4..1f88dcf 100644 --- a/accounting/sign_test.go +++ b/accounting/sign_test.go @@ -3,6 +3,7 @@ package accounting import ( "testing" + "github.com/nspcc-dev/neofs-api-go/decimal" "github.com/nspcc-dev/neofs-api-go/service" "github.com/nspcc-dev/neofs-crypto/test" "github.com/stretchr/testify/require" @@ -60,6 +61,49 @@ func TestSignBalanceRequest(t *testing.T) { }, }, }, + { // PutRequest + constructor: func() sigType { + req := new(PutRequest) + + amount := decimal.New(1) + req.SetAmount(amount) + + return req + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*PutRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + func(s sigType) { + req := s.(*PutRequest) + + mid := req.GetMessageID() + mid[0]++ + + req.SetMessageID(mid) + }, + func(s sigType) { + req := s.(*PutRequest) + + req.SetHeight(req.GetHeight() + 1) + }, + func(s sigType) { + req := s.(*PutRequest) + + amount := req.GetAmount() + if amount == nil { + req.SetAmount(decimal.New(0)) + } else { + req.SetAmount(amount.Add(decimal.New(amount.GetValue()))) + } + }, + }, + }, } for _, item := range items { diff --git a/accounting/types.go b/accounting/types.go index 1e4e80a..3a4b15e 100644 --- a/accounting/types.go +++ b/accounting/types.go @@ -381,3 +381,33 @@ func (m GetRequest) GetOwnerID() OwnerID { func (m *GetRequest) SetOwnerID(id OwnerID) { m.OwnerID = id } + +// GetOwnerID is an OwnerID field getter. +func (m PutRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *PutRequest) SetOwnerID(id OwnerID) { + m.OwnerID = id +} + +// GetMessageID is a MessageID field getter. +func (m PutRequest) GetMessageID() MessageID { + return m.MessageID +} + +// SetMessageID is a MessageID field setter. +func (m *PutRequest) SetMessageID(id MessageID) { + m.MessageID = id +} + +// SetAmount is an Amount field setter. +func (m *PutRequest) SetAmount(amount *decimal.Decimal) { + m.Amount = amount +} + +// SetHeight is a Height field setter. +func (m *PutRequest) SetHeight(h uint64) { + m.Height = h +} diff --git a/accounting/types_test.go b/accounting/types_test.go index ea17a8a..844ea70 100644 --- a/accounting/types_test.go +++ b/accounting/types_test.go @@ -113,3 +113,42 @@ func TestGetRequestGettersSetters(t *testing.T) { require.Equal(t, id, m.GetOwnerID()) }) } + +func TestPutRequestGettersSetters(t *testing.T) { + t.Run("owner", func(t *testing.T) { + id := OwnerID{1, 2, 3} + m := new(PutRequest) + + m.SetOwnerID(id) + + require.Equal(t, id, m.GetOwnerID()) + }) + + t.Run("message ID", func(t *testing.T) { + id, err := refs.NewUUID() + require.NoError(t, err) + + m := new(PutRequest) + m.SetMessageID(id) + + require.Equal(t, id, m.GetMessageID()) + }) + + t.Run("amount", func(t *testing.T) { + amount := decimal.New(1) + m := new(PutRequest) + + m.SetAmount(amount) + + require.Equal(t, amount, m.GetAmount()) + }) + + t.Run("height", func(t *testing.T) { + h := uint64(3) + m := new(PutRequest) + + m.SetHeight(h) + + require.Equal(t, h, m.GetHeight()) + }) +} From 81f537cda88329272b2d1cfe71eb2e9a7fec11d8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 13:20:58 +0300 Subject: [PATCH 52/68] accounting: implement SignedDataSource on ListRequest message --- accounting/sign.go | 30 ++++++++++++++++++++++++++++++ accounting/sign_test.go | 15 +++++++++++++++ accounting/types.go | 10 ++++++++++ accounting/types_test.go | 9 +++++++++ 4 files changed, 64 insertions(+) diff --git a/accounting/sign.go b/accounting/sign.go index 8da8cf6..dc8f6d7 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -122,3 +122,33 @@ func (m PutRequest) ReadSignedData(p []byte) (int, error) { return off, nil } + +// SignedData returns payload bytes of the request. +func (m ListRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m ListRequest) SignedDataSize() int { + return m.GetOwnerID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m ListRequest) ReadSignedData(p []byte) (int, error) { + sz := m.SignedDataSize() + if len(p) < sz { + return 0, io.ErrUnexpectedEOF + } + + copy(p, m.GetOwnerID().Bytes()) + + return sz, nil +} diff --git a/accounting/sign_test.go b/accounting/sign_test.go index 1f88dcf..77ee014 100644 --- a/accounting/sign_test.go +++ b/accounting/sign_test.go @@ -104,6 +104,21 @@ func TestSignBalanceRequest(t *testing.T) { }, }, }, + { // ListRequest + constructor: func() sigType { + return new(ListRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*ListRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + }, + }, } for _, item := range items { diff --git a/accounting/types.go b/accounting/types.go index 3a4b15e..8ea52e3 100644 --- a/accounting/types.go +++ b/accounting/types.go @@ -411,3 +411,13 @@ func (m *PutRequest) SetAmount(amount *decimal.Decimal) { func (m *PutRequest) SetHeight(h uint64) { m.Height = h } + +// GetOwnerID is an OwnerID field getter. +func (m ListRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *ListRequest) SetOwnerID(id OwnerID) { + m.OwnerID = id +} diff --git a/accounting/types_test.go b/accounting/types_test.go index 844ea70..cdf5610 100644 --- a/accounting/types_test.go +++ b/accounting/types_test.go @@ -152,3 +152,12 @@ func TestPutRequestGettersSetters(t *testing.T) { require.Equal(t, h, m.GetHeight()) }) } + +func TestListRequestGettersSetters(t *testing.T) { + ownerID := OwnerID{1, 2, 3} + m := new(ListRequest) + + m.SetOwnerID(ownerID) + + require.Equal(t, ownerID, m.GetOwnerID()) +} From 3fb293543f0e61b91c2fa830c425032bb21e0953 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 13:29:04 +0300 Subject: [PATCH 53/68] accounting: implement SignedDataSource on DeleteRequest message --- accounting/sign.go | 38 ++++++++++++++++++++++++++++++++++++++ accounting/sign_test.go | 31 +++++++++++++++++++++++++++++++ accounting/types.go | 30 ++++++++++++++++++++++++++++++ accounting/types_test.go | 30 ++++++++++++++++++++++++++++++ 4 files changed, 129 insertions(+) diff --git a/accounting/sign.go b/accounting/sign.go index dc8f6d7..b52d229 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -152,3 +152,41 @@ func (m ListRequest) ReadSignedData(p []byte) (int, error) { return sz, nil } + +// SignedData returns payload bytes of the request. +func (m DeleteRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m DeleteRequest) SignedDataSize() int { + return 0 + + m.GetID().Size() + + m.GetOwnerID().Size() + + m.GetMessageID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the buffer size is insufficient, io.ErrUnexpectedEOF returns. +func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetID().Bytes()) + + off += copy(p[off:], m.GetOwnerID().Bytes()) + + off += copy(p[off:], m.GetMessageID().Bytes()) + + return off, nil +} diff --git a/accounting/sign_test.go b/accounting/sign_test.go index 77ee014..dd7a819 100644 --- a/accounting/sign_test.go +++ b/accounting/sign_test.go @@ -119,6 +119,37 @@ func TestSignBalanceRequest(t *testing.T) { }, }, }, + { + constructor: func() sigType { + return new(DeleteRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*DeleteRequest) + + id, err := NewChequeID() + require.NoError(t, err) + + req.SetID(id) + }, + func(s sigType) { + req := s.(*DeleteRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + func(s sigType) { + req := s.(*DeleteRequest) + + mid := req.GetMessageID() + mid[0]++ + + req.SetMessageID(mid) + }, + }, + }, } for _, item := range items { diff --git a/accounting/types.go b/accounting/types.go index 8ea52e3..e16fa99 100644 --- a/accounting/types.go +++ b/accounting/types.go @@ -421,3 +421,33 @@ func (m ListRequest) GetOwnerID() OwnerID { func (m *ListRequest) SetOwnerID(id OwnerID) { m.OwnerID = id } + +// GetID is an ID field getter. +func (m DeleteRequest) GetID() ChequeID { + return m.ID +} + +// SetID is an ID field setter. +func (m *DeleteRequest) SetID(id ChequeID) { + m.ID = id +} + +// GetOwnerID is an OwnerID field getter. +func (m DeleteRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *DeleteRequest) SetOwnerID(id OwnerID) { + m.OwnerID = id +} + +// GetMessageID is a MessageID field getter. +func (m DeleteRequest) GetMessageID() MessageID { + return m.MessageID +} + +// SetMessageID is a MessageID field setter. +func (m *DeleteRequest) SetMessageID(id MessageID) { + m.MessageID = id +} diff --git a/accounting/types_test.go b/accounting/types_test.go index cdf5610..a440028 100644 --- a/accounting/types_test.go +++ b/accounting/types_test.go @@ -161,3 +161,33 @@ func TestListRequestGettersSetters(t *testing.T) { require.Equal(t, ownerID, m.GetOwnerID()) } + +func TestDeleteRequestGettersSetters(t *testing.T) { + t.Run("id", func(t *testing.T) { + id := ChequeID("test id") + m := new(DeleteRequest) + + m.SetID(id) + + require.Equal(t, id, m.GetID()) + }) + + t.Run("owner", func(t *testing.T) { + id := OwnerID{1, 2, 3} + m := new(DeleteRequest) + + m.SetOwnerID(id) + + require.Equal(t, id, m.GetOwnerID()) + }) + + t.Run("message ID", func(t *testing.T) { + id, err := refs.NewUUID() + require.NoError(t, err) + + m := new(DeleteRequest) + m.SetMessageID(id) + + require.Equal(t, id, m.GetMessageID()) + }) +} From 9327c5f816f4624f7db1e9709d4a25a0f7464ac0 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 13:57:23 +0300 Subject: [PATCH 54/68] bootstrap: implement SignedDataSource on Request message --- accounting/sign.go | 13 ++++--- bootstrap/sign.go | 48 ++++++++++++++++++++++++ bootstrap/sign_test.go | 82 +++++++++++++++++++++++++++++++++++++++++ bootstrap/types.go | 35 ++++++++++++++++++ bootstrap/types_test.go | 39 ++++++++++++++++++++ service/role.go | 14 +++++++ 6 files changed, 226 insertions(+), 5 deletions(-) create mode 100644 bootstrap/sign.go create mode 100644 bootstrap/sign_test.go create mode 100644 bootstrap/types_test.go diff --git a/accounting/sign.go b/accounting/sign.go index b52d229..4c6452d 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -165,11 +165,14 @@ func (m DeleteRequest) SignedData() ([]byte, error) { } // SignedDataSize returns payload size of the request. -func (m DeleteRequest) SignedDataSize() int { - return 0 + - m.GetID().Size() + - m.GetOwnerID().Size() + - m.GetMessageID().Size() +func (m DeleteRequest) SignedDataSize() (sz int) { + sz += m.GetID().Size() + + sz += m.GetOwnerID().Size() + + sz += m.GetMessageID().Size() + + return } // ReadSignedData copies payload bytes to passed buffer. diff --git a/bootstrap/sign.go b/bootstrap/sign.go new file mode 100644 index 0000000..97d8640 --- /dev/null +++ b/bootstrap/sign.go @@ -0,0 +1,48 @@ +package bootstrap + +import "io" + +// SignedData returns payload bytes of the request. +func (m Request) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m Request) SignedDataSize() (sz int) { + sz += m.GetType().Size() + + sz += m.GetState().Size() + + info := m.GetInfo() + sz += info.Size() + + return +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m Request) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetType().Bytes()) + + off += copy(p[off:], m.GetState().Bytes()) + + info := m.GetInfo() + // FIXME: implement and use stable functions + n, err := info.MarshalTo(p[off:]) + off += n + + return off, err +} diff --git a/bootstrap/sign_test.go b/bootstrap/sign_test.go new file mode 100644 index 0000000..2c76117 --- /dev/null +++ b/bootstrap/sign_test.go @@ -0,0 +1,82 @@ +package bootstrap + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestRequestSign(t *testing.T) { + sk := test.DecodeKey(0) + + type sigType interface { + service.SignedDataWithToken + service.SignKeyPairAccumulator + service.SignKeyPairSource + SetToken(*service.Token) + } + + items := []struct { + constructor func() sigType + payloadCorrupt []func(sigType) + }{ + { // Request + constructor: func() sigType { + return new(Request) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*Request) + + req.SetType(req.GetType() + 1) + }, + func(s sigType) { + req := s.(*Request) + + req.SetState(req.GetState() + 1) + }, + func(s sigType) { + req := s.(*Request) + + info := req.GetInfo() + info.Address += "1" + + req.SetInfo(info) + }, + }, + }, + } + + for _, item := range items { + { // token corruptions + v := item.constructor() + + token := new(service.Token) + v.SetToken(token) + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + token.SetSessionKey(append(token.GetSessionKey(), 1)) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + + { // payload corruptions + for _, corruption := range item.payloadCorrupt { + v := item.constructor() + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + corruption(v) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + } + } +} diff --git a/bootstrap/types.go b/bootstrap/types.go index 690d81c..eacc41e 100644 --- a/bootstrap/types.go +++ b/bootstrap/types.go @@ -2,6 +2,7 @@ package bootstrap import ( "bytes" + "encoding/binary" "encoding/hex" "strconv" "strings" @@ -27,6 +28,8 @@ var ( _ proto.Message = (*SpreadMap)(nil) ) +var requestEndianness = binary.BigEndian + // Equals checks whether two NodeInfo has same address. func (m NodeInfo) Equals(n1 NodeInfo) bool { return m.Address == n1.Address && bytes.Equal(m.PubKey, n1.PubKey) @@ -98,3 +101,35 @@ func (m SpreadMap) String() string { ", " + "Netmap: [" + strings.Join(result, ",") + "]>" } + +// GetType is a Type field getter. +func (m Request) GetType() NodeType { + return m.Type +} + +// SetType is a Type field setter. +func (m *Request) SetType(t NodeType) { + m.Type = t +} + +// SetState is a State field setter. +func (m *Request) SetState(state Request_State) { + m.State = state +} + +// SetInfo is an Info field getter. +func (m *Request) SetInfo(info NodeInfo) { + m.Info = info +} + +func (x Request_State) Size() int { + return 4 +} + +func (x Request_State) Bytes() []byte { + data := make([]byte, x.Size()) + + requestEndianness.PutUint32(data, uint32(x)) + + return data +} diff --git a/bootstrap/types_test.go b/bootstrap/types_test.go new file mode 100644 index 0000000..20b1b1a --- /dev/null +++ b/bootstrap/types_test.go @@ -0,0 +1,39 @@ +package bootstrap + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestRequestGettersSetters(t *testing.T) { + t.Run("type", func(t *testing.T) { + rt := NodeType(1) + m := new(Request) + + m.SetType(rt) + + require.Equal(t, rt, m.GetType()) + }) + + t.Run("state", func(t *testing.T) { + st := Request_State(1) + m := new(Request) + + m.SetState(st) + + require.Equal(t, st, m.GetState()) + }) + + t.Run("info", func(t *testing.T) { + info := NodeInfo{ + Address: "some address", + } + + m := new(Request) + + m.SetInfo(info) + + require.Equal(t, info, m.GetInfo()) + }) +} diff --git a/service/role.go b/service/role.go index 4c405c1..3af09ab 100644 --- a/service/role.go +++ b/service/role.go @@ -1,5 +1,7 @@ package service +import "encoding/binary" + const ( _ NodeRole = iota // InnerRingNode that work like IR node. @@ -19,3 +21,15 @@ func (nt NodeRole) String() string { return "Unknown" } } + +func (nt NodeRole) Size() int { + return 4 +} + +func (nt NodeRole) Bytes() []byte { + data := make([]byte, nt.Size()) + + binary.BigEndian.PutUint32(data, uint32(nt)) + + return data +} From 2d53ebf9c42dcfc95c8675c1083570a06c631614 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 14:37:16 +0300 Subject: [PATCH 55/68] container: implement SignedDataSource on PutRequest message --- container/sign.go | 66 +++++++++++++++++++++++++++ container/sign_test.go | 98 +++++++++++++++++++++++++++++++++++++++++ container/types.go | 35 +++++++++++++++ container/types_test.go | 52 ++++++++++++++++++++++ 4 files changed, 251 insertions(+) create mode 100644 container/sign.go create mode 100644 container/sign_test.go diff --git a/container/sign.go b/container/sign.go new file mode 100644 index 0000000..f551989 --- /dev/null +++ b/container/sign.go @@ -0,0 +1,66 @@ +package container + +import ( + "encoding/binary" + "io" +) + +var requestEndianness = binary.BigEndian + +// SignedData returns payload bytes of the request. +func (m PutRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m PutRequest) SignedDataSize() (sz int) { + sz += m.GetMessageID().Size() + + sz += 8 + + sz += m.GetOwnerID().Size() + + rules := m.GetRules() + sz += rules.Size() + + sz += 4 + + return +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m PutRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetMessageID().Bytes()) + + requestEndianness.PutUint64(p[off:], m.GetCapacity()) + off += 8 + + off += copy(p[off:], m.GetOwnerID().Bytes()) + + rules := m.GetRules() + // FIXME: implement and use stable functions + n, err := rules.MarshalTo(p[off:]) + off += n + if err != nil { + return off, err + } + + requestEndianness.PutUint32(p[off:], m.GetBasicACL()) + off += 4 + + return off, nil +} diff --git a/container/sign_test.go b/container/sign_test.go new file mode 100644 index 0000000..f1476ed --- /dev/null +++ b/container/sign_test.go @@ -0,0 +1,98 @@ +package container + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestRequestSign(t *testing.T) { + sk := test.DecodeKey(0) + + type sigType interface { + service.SignedDataWithToken + service.SignKeyPairAccumulator + service.SignKeyPairSource + SetToken(*service.Token) + } + + items := []struct { + constructor func() sigType + payloadCorrupt []func(sigType) + }{ + { // Request + constructor: func() sigType { + return new(PutRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*PutRequest) + + id := req.GetMessageID() + id[0]++ + + req.SetMessageID(id) + }, + func(s sigType) { + req := s.(*PutRequest) + + req.SetCapacity(req.GetCapacity() + 1) + }, + func(s sigType) { + req := s.(*PutRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + func(s sigType) { + req := s.(*PutRequest) + + rules := req.GetRules() + rules.ReplFactor++ + + req.SetRules(rules) + }, + func(s sigType) { + req := s.(*PutRequest) + + req.SetBasicACL(req.GetBasicACL() + 1) + }, + }, + }, + } + + for _, item := range items { + { // token corruptions + v := item.constructor() + + token := new(service.Token) + v.SetToken(token) + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + token.SetSessionKey(append(token.GetSessionKey(), 1)) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + + { // payload corruptions + for _, corruption := range item.payloadCorrupt { + v := item.constructor() + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + corruption(v) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + } + } +} diff --git a/container/types.go b/container/types.go index e358e6d..39cef43 100644 --- a/container/types.go +++ b/container/types.go @@ -93,3 +93,38 @@ func NewTestContainer() (*Container, error) { }, }) } + +// GetMessageID is a MessageID field getter. +func (m PutRequest) GetMessageID() MessageID { + return m.MessageID +} + +// SetMessageID is a MessageID field getter. +func (m *PutRequest) SetMessageID(id MessageID) { + m.MessageID = id +} + +// SetCapacity is a Capacity field setter. +func (m *PutRequest) SetCapacity(c uint64) { + m.Capacity = c +} + +// GetOwnerID is an OwnerID field getter. +func (m PutRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *PutRequest) SetOwnerID(owner OwnerID) { + m.OwnerID = owner +} + +// SetRules is a Rules field setter. +func (m *PutRequest) SetRules(rules netmap.PlacementRule) { + m.Rules = rules +} + +// SetBasicACL is a BasicACL field setter. +func (m *PutRequest) SetBasicACL(acl uint32) { + m.BasicACL = acl +} diff --git a/container/types_test.go b/container/types_test.go index fddccb3..07298bc 100644 --- a/container/types_test.go +++ b/container/types_test.go @@ -55,3 +55,55 @@ func TestCID(t *testing.T) { require.Equal(t, cid1, cid2) }) } + +func TestPutRequestGettersSetters(t *testing.T) { + t.Run("owner", func(t *testing.T) { + owner := OwnerID{1, 2, 3} + m := new(PutRequest) + + m.SetOwnerID(owner) + + require.Equal(t, owner, m.GetOwnerID()) + }) + + t.Run("capacity", func(t *testing.T) { + cp := uint64(3) + m := new(PutRequest) + + m.SetCapacity(cp) + + require.Equal(t, cp, m.GetCapacity()) + }) + + t.Run("message ID", func(t *testing.T) { + id, err := refs.NewUUID() + require.NoError(t, err) + + m := new(PutRequest) + + m.SetMessageID(id) + + require.Equal(t, id, m.GetMessageID()) + }) + + t.Run("rules", func(t *testing.T) { + rules := netmap.PlacementRule{ + ReplFactor: 1, + } + + m := new(PutRequest) + + m.SetRules(rules) + + require.Equal(t, rules, m.GetRules()) + }) + + t.Run("basic ACL", func(t *testing.T) { + bACL := uint32(5) + m := new(PutRequest) + + m.SetBasicACL(bACL) + + require.Equal(t, bACL, m.GetBasicACL()) + }) +} From eedb97d1355437461b71c02d7aff8cafb43786d7 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 15:01:29 +0300 Subject: [PATCH 56/68] container: implement SignedDataSource on DeleteRequest message --- container/sign.go | 31 +++++++++++++++++++++++++++++++ container/sign_test.go | 17 ++++++++++++++++- container/types.go | 10 ++++++++++ container/types_test.go | 11 +++++++++++ 4 files changed, 68 insertions(+), 1 deletion(-) diff --git a/container/sign.go b/container/sign.go index f551989..d9405b8 100644 --- a/container/sign.go +++ b/container/sign.go @@ -64,3 +64,34 @@ func (m PutRequest) ReadSignedData(p []byte) (int, error) { return off, nil } + +// SignedData returns payload bytes of the request. +func (m DeleteRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m DeleteRequest) SignedDataSize() (sz int) { + return m.GetCID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetCID().Bytes()) + + return off, nil +} diff --git a/container/sign_test.go b/container/sign_test.go index f1476ed..caa3516 100644 --- a/container/sign_test.go +++ b/container/sign_test.go @@ -22,7 +22,7 @@ func TestRequestSign(t *testing.T) { constructor func() sigType payloadCorrupt []func(sigType) }{ - { // Request + { // PutRequest constructor: func() sigType { return new(PutRequest) }, @@ -63,6 +63,21 @@ func TestRequestSign(t *testing.T) { }, }, }, + { // DeleteRequest + constructor: func() sigType { + return new(DeleteRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*DeleteRequest) + + cid := req.GetCID() + cid[0]++ + + req.SetCID(cid) + }, + }, + }, } for _, item := range items { diff --git a/container/types.go b/container/types.go index 39cef43..eeabe35 100644 --- a/container/types.go +++ b/container/types.go @@ -128,3 +128,13 @@ func (m *PutRequest) SetRules(rules netmap.PlacementRule) { func (m *PutRequest) SetBasicACL(acl uint32) { m.BasicACL = acl } + +// GetCID is a CID field getter. +func (m DeleteRequest) GetCID() CID { + return m.CID +} + +// SetCID is a CID field setter. +func (m *DeleteRequest) SetCID(cid CID) { + m.CID = cid +} diff --git a/container/types_test.go b/container/types_test.go index 07298bc..c1dafcc 100644 --- a/container/types_test.go +++ b/container/types_test.go @@ -107,3 +107,14 @@ func TestPutRequestGettersSetters(t *testing.T) { require.Equal(t, bACL, m.GetBasicACL()) }) } + +func TestDeleteRequestGettersSetters(t *testing.T) { + t.Run("cid", func(t *testing.T) { + cid := CID{1, 2, 3} + m := new(DeleteRequest) + + m.SetCID(cid) + + require.Equal(t, cid, m.GetCID()) + }) +} From a41f22782b588ae4b250974ab2f5ed079f56ac5b Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 15:05:03 +0300 Subject: [PATCH 57/68] container: implement SignedDataSource on GetRequest message --- container/sign.go | 31 +++++++++++++++++++++++++++++++ container/sign_test.go | 15 +++++++++++++++ container/types.go | 10 ++++++++++ container/types_test.go | 11 +++++++++++ 4 files changed, 67 insertions(+) diff --git a/container/sign.go b/container/sign.go index d9405b8..89c4a4c 100644 --- a/container/sign.go +++ b/container/sign.go @@ -95,3 +95,34 @@ func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { return off, nil } + +// SignedData returns payload bytes of the request. +func (m GetRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m GetRequest) SignedDataSize() (sz int) { + return m.GetCID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m GetRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetCID().Bytes()) + + return off, nil +} diff --git a/container/sign_test.go b/container/sign_test.go index caa3516..fb11fab 100644 --- a/container/sign_test.go +++ b/container/sign_test.go @@ -74,6 +74,21 @@ func TestRequestSign(t *testing.T) { cid := req.GetCID() cid[0]++ + req.SetCID(cid) + }, + }, + }, + { // GetRequest + constructor: func() sigType { + return new(GetRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*GetRequest) + + cid := req.GetCID() + cid[0]++ + req.SetCID(cid) }, }, diff --git a/container/types.go b/container/types.go index eeabe35..b34c51b 100644 --- a/container/types.go +++ b/container/types.go @@ -138,3 +138,13 @@ func (m DeleteRequest) GetCID() CID { func (m *DeleteRequest) SetCID(cid CID) { m.CID = cid } + +// GetCID is a CID field getter. +func (m GetRequest) GetCID() CID { + return m.CID +} + +// SetCID is a CID field setter. +func (m *GetRequest) SetCID(cid CID) { + m.CID = cid +} diff --git a/container/types_test.go b/container/types_test.go index c1dafcc..3c12072 100644 --- a/container/types_test.go +++ b/container/types_test.go @@ -118,3 +118,14 @@ func TestDeleteRequestGettersSetters(t *testing.T) { require.Equal(t, cid, m.GetCID()) }) } + +func TestGetRequestGettersSetters(t *testing.T) { + t.Run("cid", func(t *testing.T) { + cid := CID{1, 2, 3} + m := new(GetRequest) + + m.SetCID(cid) + + require.Equal(t, cid, m.GetCID()) + }) +} From f91adcb5607f62095b4ca2f5343b9fe00af7a70e Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 15:17:11 +0300 Subject: [PATCH 58/68] container: implement SignedDataSource on ListRequest message --- container/sign.go | 35 +++++++++++++++++++++++++++++++++-- container/sign_test.go | 15 +++++++++++++++ container/types.go | 10 ++++++++++ container/types_test.go | 11 +++++++++++ 4 files changed, 69 insertions(+), 2 deletions(-) diff --git a/container/sign.go b/container/sign.go index 89c4a4c..0f5fc93 100644 --- a/container/sign.go +++ b/container/sign.go @@ -77,7 +77,7 @@ func (m DeleteRequest) SignedData() ([]byte, error) { } // SignedDataSize returns payload size of the request. -func (m DeleteRequest) SignedDataSize() (sz int) { +func (m DeleteRequest) SignedDataSize() int { return m.GetCID().Size() } @@ -108,7 +108,7 @@ func (m GetRequest) SignedData() ([]byte, error) { } // SignedDataSize returns payload size of the request. -func (m GetRequest) SignedDataSize() (sz int) { +func (m GetRequest) SignedDataSize() int { return m.GetCID().Size() } @@ -126,3 +126,34 @@ func (m GetRequest) ReadSignedData(p []byte) (int, error) { return off, nil } + +// SignedData returns payload bytes of the request. +func (m ListRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m ListRequest) SignedDataSize() int { + return m.GetOwnerID().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m ListRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetOwnerID().Bytes()) + + return off, nil +} diff --git a/container/sign_test.go b/container/sign_test.go index fb11fab..e469399 100644 --- a/container/sign_test.go +++ b/container/sign_test.go @@ -93,6 +93,21 @@ func TestRequestSign(t *testing.T) { }, }, }, + { // ListRequest + constructor: func() sigType { + return new(ListRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*ListRequest) + + owner := req.GetOwnerID() + owner[0]++ + + req.SetOwnerID(owner) + }, + }, + }, } for _, item := range items { diff --git a/container/types.go b/container/types.go index b34c51b..f340aa5 100644 --- a/container/types.go +++ b/container/types.go @@ -148,3 +148,13 @@ func (m GetRequest) GetCID() CID { func (m *GetRequest) SetCID(cid CID) { m.CID = cid } + +// GetOwnerID is an OwnerID field getter. +func (m ListRequest) GetOwnerID() OwnerID { + return m.OwnerID +} + +// SetOwnerID is an OwnerID field setter. +func (m *ListRequest) SetOwnerID(owner OwnerID) { + m.OwnerID = owner +} diff --git a/container/types_test.go b/container/types_test.go index 3c12072..cc171cb 100644 --- a/container/types_test.go +++ b/container/types_test.go @@ -129,3 +129,14 @@ func TestGetRequestGettersSetters(t *testing.T) { require.Equal(t, cid, m.GetCID()) }) } + +func TestListRequestGettersSetters(t *testing.T) { + t.Run("owner", func(t *testing.T) { + owner := OwnerID{1, 2, 3} + m := new(PutRequest) + + m.SetOwnerID(owner) + + require.Equal(t, owner, m.GetOwnerID()) + }) +} From 539e93e558a58c596f9fe8a4118f35d0a27760d3 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:05:41 +0300 Subject: [PATCH 59/68] state: implement SignedDataSource on NetmapRequest message --- state/sign.go | 8 ++++++ state/sign_test.go | 62 ++++++++++++++++++++++++++++++++++++++++++++++ 2 files changed, 70 insertions(+) create mode 100644 state/sign.go create mode 100644 state/sign_test.go diff --git a/state/sign.go b/state/sign.go new file mode 100644 index 0000000..c0d4c7f --- /dev/null +++ b/state/sign.go @@ -0,0 +1,8 @@ +package state + +// SignedData returns payload bytes of the request. +// +// Always returns empty slice. +func (m NetmapRequest) SignedData() ([]byte, error) { + return make([]byte, 0), nil +} diff --git a/state/sign_test.go b/state/sign_test.go new file mode 100644 index 0000000..8f9a98f --- /dev/null +++ b/state/sign_test.go @@ -0,0 +1,62 @@ +package state + +import ( + "testing" + + "github.com/nspcc-dev/neofs-api-go/service" + "github.com/nspcc-dev/neofs-crypto/test" + "github.com/stretchr/testify/require" +) + +func TestRequestSign(t *testing.T) { + sk := test.DecodeKey(0) + + type sigType interface { + service.SignedDataWithToken + service.SignKeyPairAccumulator + service.SignKeyPairSource + SetToken(*service.Token) + } + + items := []struct { + constructor func() sigType + payloadCorrupt []func(sigType) + }{ + { // NetmapRequest + constructor: func() sigType { + return new(NetmapRequest) + }, + }, + } + + for _, item := range items { + { // token corruptions + v := item.constructor() + + token := new(service.Token) + v.SetToken(token) + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + token.SetSessionKey(append(token.GetSessionKey(), 1)) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + + { // payload corruptions + for _, corruption := range item.payloadCorrupt { + v := item.constructor() + + require.NoError(t, service.SignDataWithSessionToken(sk, v)) + + require.NoError(t, service.VerifyAccumulatedSignaturesWithToken(v)) + + corruption(v) + + require.Error(t, service.VerifyAccumulatedSignaturesWithToken(v)) + } + } + } +} From df9a04e5427f2adbc8d1f04a57f21be0a14c1e3e Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:11:17 +0300 Subject: [PATCH 60/68] state: implement SignedDataSource on MetricsRequest message --- state/sign.go | 7 +++++++ state/sign_test.go | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/state/sign.go b/state/sign.go index c0d4c7f..47edada 100644 --- a/state/sign.go +++ b/state/sign.go @@ -6,3 +6,10 @@ package state func (m NetmapRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } + +// SignedData returns payload bytes of the request. +// +// Always returns empty slice. +func (m MetricsRequest) SignedData() ([]byte, error) { + return make([]byte, 0), nil +} diff --git a/state/sign_test.go b/state/sign_test.go index 8f9a98f..2e8da40 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -27,6 +27,11 @@ func TestRequestSign(t *testing.T) { return new(NetmapRequest) }, }, + { // MetricsRequest + constructor: func() sigType { + return new(MetricsRequest) + }, + }, } for _, item := range items { From ea12eabaf6ec0f2f081f927b0df56c5ba4883284 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:12:49 +0300 Subject: [PATCH 61/68] state: implement SignedDataSource on HealthRequest message --- state/sign.go | 11 +++++++++-- state/sign_test.go | 5 +++++ 2 files changed, 14 insertions(+), 2 deletions(-) diff --git a/state/sign.go b/state/sign.go index 47edada..f2ddd8e 100644 --- a/state/sign.go +++ b/state/sign.go @@ -2,14 +2,21 @@ package state // SignedData returns payload bytes of the request. // -// Always returns empty slice. +// Always returns an empty slice. func (m NetmapRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } // SignedData returns payload bytes of the request. // -// Always returns empty slice. +// Always returns an empty slice. func (m MetricsRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } + +// SignedData returns payload bytes of the request. +// +// Always returns an empty slice. +func (m HealthRequest) SignedData() ([]byte, error) { + return make([]byte, 0), nil +} diff --git a/state/sign_test.go b/state/sign_test.go index 2e8da40..b5b4912 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -32,6 +32,11 @@ func TestRequestSign(t *testing.T) { return new(MetricsRequest) }, }, + { // HealthRequest + constructor: func() sigType { + return new(HealthRequest) + }, + }, } for _, item := range items { From 603db9c3254808d679df1dfa39f50b43c5b46eab Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:15:31 +0300 Subject: [PATCH 62/68] state: implement SignedDataSource on DumpRequest message --- state/sign.go | 7 +++++++ state/sign_test.go | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/state/sign.go b/state/sign.go index f2ddd8e..aaf1a2d 100644 --- a/state/sign.go +++ b/state/sign.go @@ -20,3 +20,10 @@ func (m MetricsRequest) SignedData() ([]byte, error) { func (m HealthRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } + +// SignedData returns payload bytes of the request. +// +// Always returns an empty slice. +func (m DumpRequest) SignedData() ([]byte, error) { + return make([]byte, 0), nil +} diff --git a/state/sign_test.go b/state/sign_test.go index b5b4912..cc525bc 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -37,6 +37,11 @@ func TestRequestSign(t *testing.T) { return new(HealthRequest) }, }, + { // DumpRequest + constructor: func() sigType { + return new(DumpRequest) + }, + }, } for _, item := range items { From 5545b25a95de39ed790c471511cea420fa3d00ad Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:16:54 +0300 Subject: [PATCH 63/68] state: implement SignedDataSource on DumpVarsRequest message --- state/sign.go | 7 +++++++ state/sign_test.go | 5 +++++ 2 files changed, 12 insertions(+) diff --git a/state/sign.go b/state/sign.go index aaf1a2d..8316b52 100644 --- a/state/sign.go +++ b/state/sign.go @@ -27,3 +27,10 @@ func (m HealthRequest) SignedData() ([]byte, error) { func (m DumpRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } + +// SignedData returns payload bytes of the request. +// +// Always returns an empty slice. +func (m DumpVarsRequest) SignedData() ([]byte, error) { + return make([]byte, 0), nil +} diff --git a/state/sign_test.go b/state/sign_test.go index cc525bc..dd55d21 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -42,6 +42,11 @@ func TestRequestSign(t *testing.T) { return new(DumpRequest) }, }, + { // DumpVarsRequest + constructor: func() sigType { + return new(DumpVarsRequest) + }, + }, } for _, item := range items { From ab198b404966640a2c78a155d66cda816f639fe8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 16:31:39 +0300 Subject: [PATCH 64/68] state: implement SignedDataSource on ChangeStateRequest message --- state/sign.go | 35 +++++++++++++++++++++++++++++++++++ state/sign_test.go | 12 ++++++++++++ state/types.go | 24 ++++++++++++++++++++++++ state/types_test.go | 18 ++++++++++++++++++ 4 files changed, 89 insertions(+) create mode 100644 state/types.go create mode 100644 state/types_test.go diff --git a/state/sign.go b/state/sign.go index 8316b52..9193ebc 100644 --- a/state/sign.go +++ b/state/sign.go @@ -1,5 +1,9 @@ package state +import ( + "io" +) + // SignedData returns payload bytes of the request. // // Always returns an empty slice. @@ -34,3 +38,34 @@ func (m DumpRequest) SignedData() ([]byte, error) { func (m DumpVarsRequest) SignedData() ([]byte, error) { return make([]byte, 0), nil } + +// SignedData returns payload bytes of the request. +func (m ChangeStateRequest) SignedData() ([]byte, error) { + data := make([]byte, m.SignedDataSize()) + + if _, err := m.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} + +// SignedDataSize returns payload size of the request. +func (m ChangeStateRequest) SignedDataSize() int { + return m.GetState().Size() +} + +// ReadSignedData copies payload bytes to passed buffer. +// +// If the Request size is insufficient, io.ErrUnexpectedEOF returns. +func (m ChangeStateRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + off += copy(p[off:], m.GetState().Bytes()) + + return off, nil +} diff --git a/state/sign_test.go b/state/sign_test.go index dd55d21..9b2bca9 100644 --- a/state/sign_test.go +++ b/state/sign_test.go @@ -47,6 +47,18 @@ func TestRequestSign(t *testing.T) { return new(DumpVarsRequest) }, }, + { + constructor: func() sigType { + return new(ChangeStateRequest) + }, + payloadCorrupt: []func(sigType){ + func(s sigType) { + req := s.(*ChangeStateRequest) + + req.SetState(req.GetState() + 1) + }, + }, + }, } for _, item := range items { diff --git a/state/types.go b/state/types.go new file mode 100644 index 0000000..6b572db --- /dev/null +++ b/state/types.go @@ -0,0 +1,24 @@ +package state + +import ( + "encoding/binary" +) + +// SetState is a State field setter. +func (m *ChangeStateRequest) SetState(st ChangeStateRequest_State) { + m.State = st +} + +// Size returns the size of the state binary representation. +func (ChangeStateRequest_State) Size() int { + return 4 +} + +// Bytes returns the state binary representation. +func (x ChangeStateRequest_State) Bytes() []byte { + data := make([]byte, x.Size()) + + binary.BigEndian.PutUint32(data, uint32(x)) + + return data +} diff --git a/state/types_test.go b/state/types_test.go new file mode 100644 index 0000000..5d5f5de --- /dev/null +++ b/state/types_test.go @@ -0,0 +1,18 @@ +package state + +import ( + "testing" + + "github.com/stretchr/testify/require" +) + +func TestChangeStateRequestGettersSetters(t *testing.T) { + t.Run("state", func(t *testing.T) { + st := ChangeStateRequest_State(1) + m := new(ChangeStateRequest) + + m.SetState(st) + + require.Equal(t, st, m.GetState()) + }) +} From e01fb0cc625ab164b3d15cc951c49b5aeab50a9a Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 17:28:44 +0300 Subject: [PATCH 65/68] Implement signed data calculating function from SignedDataReader --- accounting/sign.go | 42 +++-------------- bootstrap/sign.go | 14 +++--- container/sign.go | 34 +++----------- object/sign.go | 103 ++++++++++++++++++------------------------ service/errors.go | 4 ++ service/token.go | 6 +-- service/utils.go | 18 ++++++++ service/utils_test.go | 34 ++++++++++++++ session/request.go | 10 +--- state/sign.go | 10 ++-- 10 files changed, 126 insertions(+), 149 deletions(-) create mode 100644 service/utils.go create mode 100644 service/utils_test.go diff --git a/accounting/sign.go b/accounting/sign.go index 4c6452d..1eabed4 100644 --- a/accounting/sign.go +++ b/accounting/sign.go @@ -3,17 +3,13 @@ package accounting import ( "encoding/binary" "io" + + "github.com/nspcc-dev/neofs-api-go/service" ) // SignedData returns payload bytes of the request. func (m BalanceRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -37,13 +33,7 @@ func (m BalanceRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m GetRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -71,13 +61,7 @@ func (m GetRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m PutRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -125,13 +109,7 @@ func (m PutRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m ListRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -155,13 +133,7 @@ func (m ListRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m DeleteRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. diff --git a/bootstrap/sign.go b/bootstrap/sign.go index 97d8640..34f7fc2 100644 --- a/bootstrap/sign.go +++ b/bootstrap/sign.go @@ -1,16 +1,14 @@ package bootstrap -import "io" +import ( + "io" + + "github.com/nspcc-dev/neofs-api-go/service" +) // SignedData returns payload bytes of the request. func (m Request) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. diff --git a/container/sign.go b/container/sign.go index 0f5fc93..eafd93c 100644 --- a/container/sign.go +++ b/container/sign.go @@ -3,19 +3,15 @@ package container import ( "encoding/binary" "io" + + service "github.com/nspcc-dev/neofs-api-go/service" ) var requestEndianness = binary.BigEndian // SignedData returns payload bytes of the request. func (m PutRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -67,13 +63,7 @@ func (m PutRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m DeleteRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -98,13 +88,7 @@ func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m GetRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. @@ -129,13 +113,7 @@ func (m GetRequest) ReadSignedData(p []byte) (int, error) { // SignedData returns payload bytes of the request. func (m ListRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. diff --git a/object/sign.go b/object/sign.go index 25d0b2f..1ed3efa 100644 --- a/object/sign.go +++ b/object/sign.go @@ -3,34 +3,27 @@ package object import ( "encoding/binary" "io" + + "github.com/nspcc-dev/neofs-api-go/service" ) // SignedData returns payload bytes of the request. // // If payload is nil, ErrHeaderNotFound returns. func (m PutRequest) SignedData() ([]byte, error) { - sz := m.SignedDataSize() - if sz < 0 { - return nil, ErrHeaderNotFound - } - - data := make([]byte, sz) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m PutRequest) ReadSignedData(p []byte) error { +func (m PutRequest) ReadSignedData(p []byte) (int, error) { r := m.GetR() if r == nil { - return ErrHeaderNotFound + return 0, ErrHeaderNotFound } - _, err := r.MarshalTo(p) - - return err + return r.MarshalTo(p) } // SignedDataSize returns the size of payload of the Put request. @@ -47,26 +40,26 @@ func (m PutRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m GetRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRequest) ReadSignedData(p []byte) error { +func (m GetRequest) ReadSignedData(p []byte) (int, error) { addr := m.GetAddress() if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } - off := copy(p, addr.CID.Bytes()) + var off int - copy(p[off:], addr.ObjectID.Bytes()) + off += copy(p[off:], addr.CID.Bytes()) - return nil + off += copy(p[off:], addr.ObjectID.Bytes()) + + return off, nil } // SignedDataSize returns payload size of the request. @@ -76,28 +69,28 @@ func (m GetRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m HeadRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m HeadRequest) ReadSignedData(p []byte) error { +func (m HeadRequest) ReadSignedData(p []byte) (int, error) { if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } if m.GetFullHeaders() { p[0] = 1 } - off := 1 + copy(p[1:], m.Address.CID.Bytes()) + off := 1 - copy(p[off:], m.Address.ObjectID.Bytes()) + off += copy(p[off:], m.Address.CID.Bytes()) - return nil + off += copy(p[off:], m.Address.ObjectID.Bytes()) + + return off, nil } // SignedDataSize returns payload size of the request. @@ -107,24 +100,24 @@ func (m HeadRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m DeleteRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m DeleteRequest) ReadSignedData(p []byte) error { +func (m DeleteRequest) ReadSignedData(p []byte) (int, error) { if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } - off := copy(p, m.OwnerID.Bytes()) + var off int - copy(p[off:], addressBytes(m.Address)) + off += copy(p[off:], m.OwnerID.Bytes()) - return nil + off += copy(p[off:], addressBytes(m.Address)) + + return off, nil } // SignedDataSize returns payload size of the request. @@ -134,27 +127,25 @@ func (m DeleteRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m GetRangeRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRangeRequest) ReadSignedData(p []byte) error { +func (m GetRangeRequest) ReadSignedData(p []byte) (int, error) { if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } n, err := (&m.Range).MarshalTo(p) if err != nil { - return err + return 0, err } - copy(p[n:], addressBytes(m.GetAddress())) + n += copy(p[n:], addressBytes(m.GetAddress())) - return nil + return n, nil } // SignedDataSize returns payload size of the request. @@ -164,17 +155,15 @@ func (m GetRangeRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m GetRangeHashRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m GetRangeHashRequest) ReadSignedData(p []byte) error { +func (m GetRangeHashRequest) ReadSignedData(p []byte) (int, error) { if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } var off int @@ -185,7 +174,7 @@ func (m GetRangeHashRequest) ReadSignedData(p []byte) error { off += copy(p[off:], m.GetSalt()) - return nil + return off, nil } // SignedDataSize returns payload size of the request. @@ -203,17 +192,15 @@ func (m GetRangeHashRequest) SignedDataSize() int { // SignedData returns payload bytes of the request. func (m SearchRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - return data, m.ReadSignedData(data) + return service.SignedDataFromReader(m) } // ReadSignedData copies payload bytes to passed buffer. // // If the buffer size is insufficient, io.ErrUnexpectedEOF returns. -func (m SearchRequest) ReadSignedData(p []byte) error { +func (m SearchRequest) ReadSignedData(p []byte) (int, error) { if len(p) < m.SignedDataSize() { - return io.ErrUnexpectedEOF + return 0, io.ErrUnexpectedEOF } var off int @@ -223,9 +210,9 @@ func (m SearchRequest) ReadSignedData(p []byte) error { binary.BigEndian.PutUint32(p[off:], m.GetQueryVersion()) off += 4 - copy(p[off:], m.GetQuery()) + off += copy(p[off:], m.GetQuery()) - return nil + return off, nil } // SignedDataSize returns payload size of the request. diff --git a/service/errors.go b/service/errors.go index 6241ad2..f3a0dfc 100644 --- a/service/errors.go +++ b/service/errors.go @@ -43,3 +43,7 @@ const ErrNilDataWithTokenSignAccumulator = internal.Error("signed data with toke // ErrNilSignatureKeySourceWithToken is returned by functions that expect // a non-nil SignatureKeySourceWithToken, but received nil. const ErrNilSignatureKeySourceWithToken = internal.Error("key-signature source with token is nil") + +// ErrNilSignedDataReader is returned by functions that expect +// a non-nil SignedDataReader, but received nil. +const ErrNilSignedDataReader = internal.Error("signed data reader is nil") diff --git a/service/token.go b/service/token.go index 78fccfa..32c390f 100644 --- a/service/token.go +++ b/service/token.go @@ -123,11 +123,7 @@ func (m *Token) AddSignKey(sig []byte, _ *ecdsa.PublicKey) { // SignedData returns token information in a binary representation. func (m *Token) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - copyTokenSignedData(data, m) - - return data, nil + return SignedDataFromReader(m) } // ReadSignedData copies a binary representation of the token information to passed buffer. diff --git a/service/utils.go b/service/utils.go new file mode 100644 index 0000000..17b23bb --- /dev/null +++ b/service/utils.go @@ -0,0 +1,18 @@ +package service + +// SignedDataFromReader allocates buffer and reads bytes from passed reader to it. +// +// If passed SignedDataReader is nil, ErrNilSignedDataReader returns. +func SignedDataFromReader(r SignedDataReader) ([]byte, error) { + if r == nil { + return nil, ErrNilSignedDataReader + } + + data := make([]byte, r.SignedDataSize()) + + if _, err := r.ReadSignedData(data); err != nil { + return nil, err + } + + return data, nil +} diff --git a/service/utils_test.go b/service/utils_test.go new file mode 100644 index 0000000..60a2352 --- /dev/null +++ b/service/utils_test.go @@ -0,0 +1,34 @@ +package service + +import ( + "testing" + + "github.com/pkg/errors" + "github.com/stretchr/testify/require" +) + +func TestSignedDataFromReader(t *testing.T) { + // nil SignedDataReader + _, err := SignedDataFromReader(nil) + require.EqualError(t, err, ErrNilSignedDataReader.Error()) + + rdr := &testSignedDataReader{ + testSignedDataSrc: new(testSignedDataSrc), + } + + // make reader to return an error + rdr.err = errors.New("test error") + + _, err = SignedDataFromReader(rdr) + require.EqualError(t, err, rdr.err.Error()) + + // remove the error + rdr.err = nil + + // fill the data + rdr.data = testData(t, 10) + + res, err := SignedDataFromReader(rdr) + require.NoError(t, err) + require.Equal(t, rdr.data, res) +} diff --git a/session/request.go b/session/request.go index 0bb5176..73c05e5 100644 --- a/session/request.go +++ b/session/request.go @@ -5,6 +5,7 @@ import ( "io" "github.com/nspcc-dev/neofs-api-go/refs" + "github.com/nspcc-dev/neofs-api-go/service" ) const signedRequestDataSize = 0 + @@ -31,14 +32,7 @@ func (m *CreateRequest) SetOwnerID(id OwnerID) { // SignedData returns payload bytes of the request. func (m CreateRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - _, err := m.ReadSignedData(data) - if err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. diff --git a/state/sign.go b/state/sign.go index 9193ebc..88f038c 100644 --- a/state/sign.go +++ b/state/sign.go @@ -2,6 +2,8 @@ package state import ( "io" + + "github.com/nspcc-dev/neofs-api-go/service" ) // SignedData returns payload bytes of the request. @@ -41,13 +43,7 @@ func (m DumpVarsRequest) SignedData() ([]byte, error) { // SignedData returns payload bytes of the request. func (m ChangeStateRequest) SignedData() ([]byte, error) { - data := make([]byte, m.SignedDataSize()) - - if _, err := m.ReadSignedData(data); err != nil { - return nil, err - } - - return data, nil + return service.SignedDataFromReader(m) } // SignedDataSize returns payload size of the request. From b2543c073977b63d8e73f5a2bef9865897fcdecd Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Mon, 11 May 2020 17:44:59 +0300 Subject: [PATCH 66/68] fix comments --- bootstrap/types.go | 2 ++ service/role.go | 2 ++ 2 files changed, 4 insertions(+) diff --git a/bootstrap/types.go b/bootstrap/types.go index eacc41e..7ad3ec2 100644 --- a/bootstrap/types.go +++ b/bootstrap/types.go @@ -122,10 +122,12 @@ func (m *Request) SetInfo(info NodeInfo) { m.Info = info } +// Size returns the size necessary for a binary representation of the state. func (x Request_State) Size() int { return 4 } +// Bytes returns a binary representation of the state. func (x Request_State) Bytes() []byte { data := make([]byte, x.Size()) diff --git a/service/role.go b/service/role.go index 3af09ab..64a0074 100644 --- a/service/role.go +++ b/service/role.go @@ -22,10 +22,12 @@ func (nt NodeRole) String() string { } } +// Size returns the size necessary for a binary representation of the NodeRole. func (nt NodeRole) Size() int { return 4 } +// Bytes returns a binary representation of the NodeRole. func (nt NodeRole) Bytes() []byte { data := make([]byte, nt.Size()) From 877db6be32edb6237f5505ccd42ed669f40db3e9 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 12 May 2020 10:40:48 +0300 Subject: [PATCH 67/68] service: remove no longer used Sign/Verify methods and functions --- service/sign.go | 7 ++ service/verify.go | 181 ------------------------------ service/verify_test.go | 243 +++++++++++++---------------------------- 3 files changed, 85 insertions(+), 346 deletions(-) diff --git a/service/sign.go b/service/sign.go index f5cdc0b..5b1548f 100644 --- a/service/sign.go +++ b/service/sign.go @@ -2,6 +2,7 @@ package service import ( "crypto/ecdsa" + "sync" crypto "github.com/nspcc-dev/neofs-crypto" ) @@ -11,6 +12,12 @@ type keySign struct { sign []byte } +var bytesPool = sync.Pool{ + New: func() interface{} { + return make([]byte, 5<<20) + }, +} + // GetSignature is a sign field getter. func (s keySign) GetSignature() []byte { return s.sign diff --git a/service/verify.go b/service/verify.go index beca992..62db2f5 100644 --- a/service/verify.go +++ b/service/verify.go @@ -2,37 +2,9 @@ package service import ( "crypto/ecdsa" - "sync" "github.com/nspcc-dev/neofs-api-go/internal" - "github.com/nspcc-dev/neofs-api-go/refs" crypto "github.com/nspcc-dev/neofs-crypto" - "github.com/pkg/errors" -) - -type ( - // VerifiableRequest adds possibility to sign and verify request header. - VerifiableRequest interface { - Size() int - MarshalTo([]byte) (int, error) - AddSignature(*RequestVerificationHeader_Signature) - GetSignatures() []*RequestVerificationHeader_Signature - SetSignatures([]*RequestVerificationHeader_Signature) - } - - // MaintainableRequest adds possibility to set and get (+validate) - // owner (client) public key from RequestVerificationHeader. - MaintainableRequest interface { - GetOwner() (*ecdsa.PublicKey, error) - SetOwner(*ecdsa.PublicKey, []byte) - GetLastPeer() (*ecdsa.PublicKey, error) - } - - // TokenHeader is an interface of the container of a Token pointer value - TokenHeader interface { - GetToken() *Token - SetToken(*Token) - } ) // GetSessionToken returns SessionToken interface of Token field. @@ -90,164 +62,11 @@ func (m *RequestVerificationHeader) SetSignatures(signatures []*RequestVerificat m.Signatures = signatures } -// AddSignature adds new Signature into RequestVerificationHeader. -func (m *RequestVerificationHeader) AddSignature(sig *RequestVerificationHeader_Signature) { - if sig == nil { - return - } - m.Signatures = append(m.Signatures, sig) -} - -// CheckOwner validates, that passed OwnerID is equal to present PublicKey of owner. -func (m *RequestVerificationHeader) CheckOwner(owner refs.OwnerID) error { - if key, err := m.GetOwner(); err != nil { - return err - } else if user, err := refs.NewOwnerID(key); err != nil { - return err - } else if !user.Equal(owner) { - return ErrWrongOwner - } - return nil -} - -// GetOwner tries to get owner (client) public key from signatures. -// If signatures contains not empty Origin, we should try to validate, -// that session key was signed by owner (client), otherwise return error. -func (m *RequestVerificationHeader) GetOwner() (*ecdsa.PublicKey, error) { - if len(m.Signatures) == 0 { - return nil, ErrCannotFindOwner - } else if key := crypto.UnmarshalPublicKey(m.Signatures[0].Peer); key != nil { - return key, nil - } - - return nil, ErrInvalidPublicKeyBytes -} - -// GetLastPeer tries to get last peer public key from signatures. -// If signatures has zero length, returns ErrCannotFindOwner. -// If signatures has length equal to one, uses GetOwner. -// Otherwise tries to unmarshal last peer public key. -func (m *RequestVerificationHeader) GetLastPeer() (*ecdsa.PublicKey, error) { - switch ln := len(m.Signatures); ln { - case 0: - return nil, ErrCannotFindOwner - case 1: - return m.GetOwner() - default: - if key := crypto.UnmarshalPublicKey(m.Signatures[ln-1].Peer); key != nil { - return key, nil - } - - return nil, ErrInvalidPublicKeyBytes - } -} - // SetToken is a Token field setter. func (m *RequestVerificationHeader) SetToken(token *Token) { m.Token = token } -func newSignature(key *ecdsa.PrivateKey, data []byte) (*RequestVerificationHeader_Signature, error) { - sign, err := crypto.Sign(key, data) - if err != nil { - return nil, err - } - - return &RequestVerificationHeader_Signature{ - Sign: sign, - Peer: crypto.MarshalPublicKey(&key.PublicKey), - }, nil -} - -var bytesPool = sync.Pool{New: func() interface{} { - return make([]byte, 4.5*1024*1024) // 4.5MB -}} - -// SignRequestHeader receives private key and request with RequestVerificationHeader, -// tries to marshal and sign request with passed PrivateKey, after that adds -// new signature to headers. If something went wrong, returns error. -func SignRequestHeader(key *ecdsa.PrivateKey, msg VerifiableRequest) error { - // ignore meta header - if meta, ok := msg.(SeizedRequestMetaContainer); ok { - h := meta.CutMeta() - - defer func() { - meta.RestoreMeta(h) - }() - } - - data := bytesPool.Get().([]byte) - defer func() { - bytesPool.Put(data) - }() - - if size := msg.Size(); size <= cap(data) { - data = data[:size] - } else { - data = make([]byte, size) - } - - size, err := msg.MarshalTo(data) - if err != nil { - return err - } - - signature, err := newSignature(key, data[:size]) - if err != nil { - return err - } - - msg.AddSignature(signature) - - return nil -} - -// VerifyRequestHeader receives request with RequestVerificationHeader, -// tries to marshal and verify each signature from request. -// If something went wrong, returns error. -func VerifyRequestHeader(msg VerifiableRequest) error { - // ignore meta header - if meta, ok := msg.(SeizedRequestMetaContainer); ok { - h := meta.CutMeta() - - defer func() { - meta.RestoreMeta(h) - }() - } - - data := bytesPool.Get().([]byte) - signatures := msg.GetSignatures() - defer func() { - bytesPool.Put(data) - msg.SetSignatures(signatures) - }() - - for i := range signatures { - msg.SetSignatures(signatures[:i]) - peer := signatures[i].GetPeer() - sign := signatures[i].GetSign() - - key := crypto.UnmarshalPublicKey(peer) - if key == nil { - return errors.Wrapf(ErrInvalidPublicKeyBytes, "%d: %02x", i, peer) - } - - if size := msg.Size(); size <= cap(data) { - data = data[:size] - } else { - data = make([]byte, size) - } - - if size, err := msg.MarshalTo(data); err != nil { - return errors.Wrapf(err, "%d: %02x", i, peer) - } else if err := crypto.Verify(key, data[:size], sign); err != nil { - return errors.Wrapf(err, "%d: %02x", i, peer) - } - } - - return nil -} - // testCustomField for test usage only. type testCustomField [8]uint32 diff --git a/service/verify_test.go b/service/verify_test.go index 107416b..c6e4d61 100644 --- a/service/verify_test.go +++ b/service/verify_test.go @@ -1,194 +1,107 @@ package service import ( - "bytes" - "log" + "encoding/binary" + "io" "math" "testing" - "github.com/gogo/protobuf/proto" "github.com/nspcc-dev/neofs-api-go/refs" - crypto "github.com/nspcc-dev/neofs-crypto" "github.com/nspcc-dev/neofs-crypto/test" - "github.com/pkg/errors" "github.com/stretchr/testify/require" ) -func BenchmarkSignRequestHeader(b *testing.B) { +func (m TestRequest) SignedData() ([]byte, error) { + return SignedDataFromReader(m) +} + +func (m TestRequest) SignedDataSize() (sz int) { + sz += 4 + + sz += len(m.StringField) + + sz += len(m.BytesField) + + sz += m.CustomField.Size() + + return +} + +func (m TestRequest) ReadSignedData(p []byte) (int, error) { + if len(p) < m.SignedDataSize() { + return 0, io.ErrUnexpectedEOF + } + + var off int + + binary.BigEndian.PutUint32(p[off:], uint32(m.IntField)) + off += 4 + + off += copy(p[off:], []byte(m.StringField)) + + off += copy(p[off:], m.BytesField) + + n, err := m.CustomField.MarshalTo(p[off:]) + off += n + + return off, err +} + +func BenchmarkSignDataWithSessionToken(b *testing.B) { key := test.DecodeKey(0) - custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} + customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} - some := &TestRequest{ + token := new(Token) + + req := &TestRequest{ IntField: math.MaxInt32, StringField: "TestRequestStringField", BytesField: make([]byte, 1<<22), - CustomField: &custom, - RequestMetaHeader: RequestMetaHeader{ - TTL: math.MaxInt32 - 8, - Epoch: math.MaxInt64 - 12, - }, + CustomField: &customField, + } + + req.SetTTL(math.MaxInt32 - 8) + req.SetEpoch(math.MaxInt64 - 12) + req.SetToken(token) + + b.ResetTimer() + b.ReportAllocs() + + for i := 0; i < b.N; i++ { + require.NoError(b, SignDataWithSessionToken(key, req)) + } +} + +func BenchmarkVerifyAccumulatedSignaturesWithToken(b *testing.B) { + customField := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} + + token := new(Token) + + req := &TestRequest{ + IntField: math.MaxInt32, + StringField: "TestRequestStringField", + BytesField: make([]byte, 1<<22), + CustomField: &customField, + } + + req.SetTTL(math.MaxInt32 - 8) + req.SetEpoch(math.MaxInt64 - 12) + req.SetToken(token) + + for i := 0; i < 10; i++ { + key := test.DecodeKey(i) + require.NoError(b, SignDataWithSessionToken(key, req)) } b.ResetTimer() b.ReportAllocs() for i := 0; i < b.N; i++ { - require.NoError(b, SignRequestHeader(key, some)) + require.NoError(b, VerifyAccumulatedSignaturesWithToken(req)) } } -func BenchmarkVerifyRequestHeader(b *testing.B) { - custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} - - some := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: make([]byte, 1<<22), - CustomField: &custom, - RequestMetaHeader: RequestMetaHeader{ - TTL: math.MaxInt32 - 8, - Epoch: math.MaxInt64 - 12, - }, - } - - for i := 0; i < 10; i++ { - key := test.DecodeKey(i) - require.NoError(b, SignRequestHeader(key, some)) - } - - b.ResetTimer() - b.ReportAllocs() - - for i := 0; i < b.N; i++ { - require.NoError(b, VerifyRequestHeader(some)) - } -} - -func TestSignRequestHeader(t *testing.T) { - req := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: []byte("TestRequestBytesField"), - } - - key := test.DecodeKey(0) - peer := crypto.MarshalPublicKey(&key.PublicKey) - - data, err := req.Marshal() - require.NoError(t, err) - - require.NoError(t, SignRequestHeader(key, req)) - - require.Len(t, req.Signatures, 1) - for i := range req.Signatures { - sign := req.Signatures[i].GetSign() - require.Equal(t, peer, req.Signatures[i].GetPeer()) - require.NoError(t, crypto.Verify(&key.PublicKey, data, sign)) - } -} - -func TestVerifyRequestHeader(t *testing.T) { - req := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: []byte("TestRequestBytesField"), - RequestMetaHeader: RequestMetaHeader{TTL: 10}, - } - - for i := 0; i < 10; i++ { - req.TTL-- - require.NoError(t, SignRequestHeader(test.DecodeKey(i), req)) - } - - require.NoError(t, VerifyRequestHeader(req)) -} - -func TestMaintainableRequest(t *testing.T) { - req := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: []byte("TestRequestBytesField"), - RequestMetaHeader: RequestMetaHeader{TTL: 10}, - } - - count := 10 - owner := test.DecodeKey(count + 1) - - for i := 0; i < count; i++ { - req.TTL-- - - key := test.DecodeKey(i) - - // sign first key (session key) by owner key - if i == 0 { - key = owner - } - - require.NoError(t, SignRequestHeader(key, req)) - } - - { // Validate owner - user, err := refs.NewOwnerID(&owner.PublicKey) - require.NoError(t, err) - require.NoError(t, req.CheckOwner(user)) - } - - { // Good case: - require.NoError(t, VerifyRequestHeader(req)) - - // validate, that first key (session key) was signed with owner - signatures := req.GetSignatures() - - require.Len(t, signatures, count) - - pub, err := req.GetOwner() - require.NoError(t, err) - - require.Equal(t, &owner.PublicKey, pub) - } - - { // Wrong signatures: - copy(req.Signatures[count-1].Sign, req.Signatures[count-2].Sign) - err := VerifyRequestHeader(req) - require.EqualError(t, errors.Cause(err), crypto.ErrInvalidSignature.Error()) - } -} - -func TestVerifyAndSignRequestHeaderWithoutCloning(t *testing.T) { - key := test.DecodeKey(0) - - custom := testCustomField{1, 2, 3, 4, 5, 6, 7, 8} - - b := &TestRequest{ - IntField: math.MaxInt32, - StringField: "TestRequestStringField", - BytesField: []byte("TestRequestBytesField"), - CustomField: &custom, - RequestMetaHeader: RequestMetaHeader{ - TTL: math.MaxInt32 - 8, - Epoch: math.MaxInt64 - 12, - }, - } - - require.NoError(t, SignRequestHeader(key, b)) - require.NoError(t, VerifyRequestHeader(b)) - - require.Len(t, b.Signatures, 1) - require.Equal(t, custom, *b.CustomField) - require.Equal(t, uint32(math.MaxInt32-8), b.GetTTL()) - require.Equal(t, uint64(math.MaxInt64-12), b.GetEpoch()) - - buf := bytes.NewBuffer(nil) - log.SetOutput(buf) - - cp, ok := proto.Clone(b).(*TestRequest) - require.True(t, ok) - require.NotEqual(t, b, cp) - - require.Contains(t, buf.String(), "proto: don't know how to copy") -} - func TestRequestVerificationHeader_SetToken(t *testing.T) { id, err := refs.NewUUID() require.NoError(t, err) From 798c3ae658e60bd994939be2f5008904746ef8d8 Mon Sep 17 00:00:00 2001 From: Leonard Lyubich Date: Tue, 12 May 2020 11:08:03 +0300 Subject: [PATCH 68/68] Update changelog for v0.7.4 --- CHANGELOG.md | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/CHANGELOG.md b/CHANGELOG.md index 14b7a11..8abbc14 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -1,6 +1,22 @@ # Changelog This is the changelog for NeoFS-API-Go +## [0.7.4] - 2020-05-12 + +### Added + +- Stringify for `object.Object`. + +### Changed + +- Mechanism for creating and verifying request message signatures. +- Implementation and interface of private token storage. +- File structure of packages. + +### Updated + +- NeoFS API v0.7.4 + ## [0.7.1] - 2020-04-20 ### Added @@ -273,3 +289,4 @@ Initial public release [0.6.2]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.6.1...v0.6.2 [0.7.0]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.6.2...v0.7.0 [0.7.1]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.0...v0.7.1 +[0.7.4]: https://github.com/nspcc-dev/neofs-api-go/compare/v0.7.1...v0.7.4