From f0097d6c248f301f1f9ff8cc31355662ddbf36fd Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jan 2020 14:16:20 +0300 Subject: [PATCH 1/5] dep: Update neofs-crypto lib to v0.2.3 --- go.mod | 2 +- go.sum | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/go.mod b/go.mod index bb8c4ff..688905e 100644 --- a/go.mod +++ b/go.mod @@ -7,7 +7,7 @@ require ( github.com/golang/protobuf v1.3.2 github.com/google/uuid v1.1.1 github.com/mr-tron/base58 v1.1.3 - github.com/nspcc-dev/neofs-crypto v0.2.2 + github.com/nspcc-dev/neofs-crypto v0.2.3 github.com/nspcc-dev/netmap v1.6.1 github.com/nspcc-dev/tzhash v1.3.0 github.com/pkg/errors v0.8.1 diff --git a/go.sum b/go.sum index db7763e..531a682 100644 --- a/go.sum +++ b/go.sum @@ -106,12 +106,12 @@ github.com/mr-tron/base58 v1.1.3/go.mod h1:BinMc/sQntlIE1frQmRFPUoPA1Zkr8VRgBdjW github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/nspcc-dev/hrw v1.0.8 h1:vwRuJXZXgkMvf473vFzeWGCfY1WBVeSHAEHvR4u3/Cg= github.com/nspcc-dev/hrw v1.0.8/go.mod h1:l/W2vx83vMQo6aStyx2AuZrJ+07lGv2JQGlVkPG06MU= -github.com/nspcc-dev/neofs-crypto v0.2.2 h1:jLc5O+Wdpaq7L4lNYFX7li+OP4I1FsvvcPW1NXm3erY= -github.com/nspcc-dev/neofs-crypto v0.2.2/go.mod h1:F/96fUzPM3wR+UGsPi3faVNmFlA9KAEAUQR7dMxZmNA= +github.com/nspcc-dev/neofs-crypto v0.2.3 h1:aca3X2aly92ENRbFK+kH6Hd+J9EQ4Eu6XMVoITSIKtc= +github.com/nspcc-dev/neofs-crypto v0.2.3/go.mod h1:8w16GEJbH6791ktVqHN9YRNH3s9BEEKYxGhlFnp0cDw= github.com/nspcc-dev/netmap v1.6.1 h1:Pigqpqi6QSdRiusbq5XlO20A18k6Eyu7j9MzOfAE3CM= github.com/nspcc-dev/netmap v1.6.1/go.mod h1:mhV3UOg9ljQmu0teQShD6+JYX09XY5gu2I4hIByCH9M= -github.com/nspcc-dev/rfc6979 v0.1.0 h1:Lwg7esRRoyK1Up/IN1vAef1EmvrBeMHeeEkek2fAJ6c= -github.com/nspcc-dev/rfc6979 v0.1.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= +github.com/nspcc-dev/rfc6979 v0.2.0 h1:3e1WNxrN60/6N0DW7+UYisLeZJyfqZTNOjeV/toYvOE= +github.com/nspcc-dev/rfc6979 v0.2.0/go.mod h1:exhIh1PdpDC5vQmyEsGvc4YDM/lyQp/452QxGq/UEso= github.com/nspcc-dev/tzhash v1.3.0 h1:n6FTHsfPYbMi5Jmo6SwGVVRQD8i2w1P2ScCaW6rz69Q= github.com/nspcc-dev/tzhash v1.3.0/go.mod h1:Lc4DersKS8MNIrunTmsAzANO56qnG+LZ4GOE/WYGVzU= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= From 50d3649acf2143125b1eb1fece68b43c091596c5 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jan 2020 14:17:34 +0300 Subject: [PATCH 2/5] object: Add public key header type Object can contain public key header. It will be used for object verification. This header can contain owner's public key or be the part of x509 chain verification in couple with verification header. --- object/types.go | 6 ++++++ object/types.pb.go | Bin 79138 -> 85248 bytes object/types.proto | 7 +++++++ 3 files changed, 13 insertions(+) diff --git a/object/types.go b/object/types.go index f433b9c..79cef65 100644 --- a/object/types.go +++ b/object/types.go @@ -67,6 +67,8 @@ const ( IntegrityHdr // StorageGroupHdr is a storage group header type. StorageGroupHdr + // PublicKeyHdr is a public key header type. + PublicKeyHdr ) var ( @@ -140,6 +142,8 @@ func (m Header) typeOf(t isHeader_Value) (ok bool) { _, ok = m.Value.(*Header_Integrity) case *Header_StorageGroup: _, ok = m.Value.(*Header_StorageGroup) + case *Header_PublicKey: + _, ok = m.Value.(*Header_PublicKey) } return } @@ -168,6 +172,8 @@ func HeaderType(t headerType) Pred { return func(h *Header) bool { _, ok := h.Value.(*Header_Integrity); return ok } case StorageGroupHdr: return func(h *Header) bool { _, ok := h.Value.(*Header_StorageGroup); return ok } + case PublicKeyHdr: + return func(h *Header) bool { _, ok := h.Value.(*Header_PublicKey); return ok } default: return nil } diff --git a/object/types.pb.go b/object/types.pb.go index b5a1e785e86633310ad63204e2a52d2b49da629d..525b39b3b8b633290cdfab4649036dd57a343266 100644 GIT binary patch delta 6481 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 delta 5255 zcmY+IJ*yp66o$FMsL2J(K(q;h5GBH#`JT>33&F}p&}Kft&U8mXMmxJC+|JU%#wKtg zDa1k){1vtqmUbedYd_Bp9Gkgw_w2pC-nHJf_W9!L>)-tG+~fCeoj$(x^NrJ!JO5ss zet-47>!%;?etU8H{EaVPKmGc_-`B4G`S8)Twz_)w(WiUA-n&yA*D5{h`rgeO#qm(5 zf2Jlq4}4g=^xm=J)?r91Tgx8y&+KfAv{JBhIHdO$1r7VF83n_bKJ@AHe(aFZPig7E z-US(bpH`IQd(GDZTUKPdj-Y3H8Z=ZJ;t;*g>3!fiA#^SIn#|sh!;=1K@S(=;M(cfI z_snxZf4QMSM`tz}!v&oNrewXwsS45AK`d(Vze?ivV~@8Dhil*UVKTUcbwW~$>lNz_ z&jsT6$SevYzgN+bnXEws z&}lwZe63@D=y$6*Iby|(50PR-iNFQwDH{oKgE3~qrdO`;G>1p_j|eJxR_ruat%iug z%{_3~(4eg)mYFgg!JUa%<7u91sHuProgES?VlkL{tZV}=Sf(0moL9^Xm3w#;^~ zi4W3|lp zVtq!>$VyKR3B8rM%Bwv=68l1N<(^!Zf=uGl0^wPq$Mi_W5~PgX6*g*%RT{yAxshsh z3A)9Vc@@J7YmzMKEA-FAL}D+Ej2d{?w^`0`>j4=;JmR|i8IFdL=7&@hA&!*;+71Zu|8(bwph9VSa<~te9(9uwWSssiAe>|Vb?wq*E%qL#ckoM8V)qq2*y9k36*zvAEGgb(O9Y=#@Yx|BJ zDw7raw~RBQcWWm14$?OaNTJ6EQ>_QbxeY%!yN-IJ3V3uw5q31;UbW zNA@HQHRtSUnpa}#h7a~5bI(qow>EJ3v&AlJGP65&1(k$?{b)mwOj|5-sBuMk&RJG~ zl->YCApqDinQnM!uaC(uxnMAQqAI&OaZrb$Ut~Df0)PiZEVm2U01b8s8+A~6(%>eW zpdpqN%vcdFG1#yj2*8>GxgS@8KBHgFrnvnr#sXj<8!Nz&abE`$$tagrxlyD-IL`+Ffwa#LTaX#A`?KjOo>CdyF zza_*%EZpY1Lw7^m@@y>_L}ZpSB)s#2uZ$|s+=&RJLuEoT^jqncgBa#PpRLYri7z|fTdY0^c&A;$Y{V;!PtNyM%fe|96eYcfMnfB<972P zw%BRaOJd#|ge}N2DUeZ|%%f|k`OB%we7ABZ^ys=$y_uN4l9jRr&x(!7B59C)9j7$L zQtmL@*42;1n7KPqMnIsK)R<-46fJ8_J+Lq;MhXXH}rTe8uP6^m%-2zX{P6SD+UztDTcg?V7 zTVmx{U~nBIoVkprqr9~1uqDYZ(wzF&n)m znjQ`$xlc$j0At3+*qjtp&ObFWv{I_Ekz3n3x<6(7zG%BIJaDa)B=XrHyKSAENgF?a@@9a*h#WMXYS*G z5jWSQ!H_r-J49#rpb43Cuyx{&6NJ}Z>mwLC(ohN3o^7<>)gX0Bb&k?TDc|TMHq1M} zV>znJDBd3Om;q(3R-+2+3XXXWug>$kH=2Am+;9xQ9eEaF~or|xY{|~)NG8F&- diff --git a/object/types.proto b/object/types.proto index 8f50f76..293f347 100644 --- a/object/types.proto +++ b/object/types.proto @@ -45,6 +45,8 @@ message Header { IntegrityHeader Integrity = 9; // StorageGroup contains meta information for the data audit storagegroup.StorageGroup StorageGroup = 10; + // PublicKey of owner of the object. Key is used for verification and can be based on NeoID or x509 cert. + PublicKey PublicKey = 11; } } @@ -122,3 +124,8 @@ message Object { // Payload is an object's payload bytes Payload = 3; } + +message PublicKey { + // Value contains marshaled ecdsa public key + bytes Value = 1; +} From c2f63107d0c20ad0c1edfa1745ba6b2d4b50f644 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jan 2020 14:21:34 +0300 Subject: [PATCH 3/5] docs: Add doc about new public key header in object --- docs/object.md | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/docs/object.md b/docs/object.md index e9248ba..7e86325 100644 --- a/docs/object.md +++ b/docs/object.md @@ -33,6 +33,7 @@ - [IntegrityHeader](#object.IntegrityHeader) - [Link](#object.Link) - [Object](#object.Object) + - [PublicKey](#object.PublicKey) - [Range](#object.Range) - [SystemHeader](#object.SystemHeader) - [Tombstone](#object.Tombstone) @@ -368,6 +369,7 @@ in distributed system. | PayloadChecksum | [bytes](#bytes) | | PayloadChecksum of actual object's payload | | Integrity | [IntegrityHeader](#object.IntegrityHeader) | | Integrity header with checksum of all above headers in the object | | StorageGroup | [storagegroup.StorageGroup](#storagegroup.StorageGroup) | | StorageGroup contains meta information for the data audit | +| PublicKey | [PublicKey](#object.PublicKey) | | PublicKey of owner of the object. Key is used for verification and can be based on NeoID or x509 cert. | @@ -407,6 +409,17 @@ in distributed system. | Payload | [bytes](#bytes) | | Payload is an object's payload | + + +### Message PublicKey + + + +| Field | Type | Label | Description | +| ----- | ---- | ----- | ----------- | +| Value | [bytes](#bytes) | | Value contains marshaled ecdsa public key | + + ### Message Range From f1418ef79dd7e998954a9f02ba9733f39124ed39 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jan 2020 14:38:08 +0300 Subject: [PATCH 4/5] object: Add `CreateIntegrityHeader` function CreateIntegrityHeader function allow to optimize object put pipeline in neofs nodes. --- object/verification.go | 27 +++++++++++++++++++-------- 1 file changed, 19 insertions(+), 8 deletions(-) diff --git a/object/verification.go b/object/verification.go index 761c04e..cbf12ab 100644 --- a/object/verification.go +++ b/object/verification.go @@ -63,6 +63,7 @@ func (m Object) verifySignature(key []byte, ih *IntegrityHeader) error { // Verify performs local integrity check by finding verification header and // integrity header. If header integrity is passed, function verifies // checksum of the object payload. +// todo: move this verification logic into separate library func (m Object) Verify() error { var ( err error @@ -111,22 +112,32 @@ func (m Object) Verify() error { return nil } -// Sign creates new integrity header and adds it to the end of the list of -// extended headers. -func (m *Object) Sign(key *ecdsa.PrivateKey) error { - headerChecksum, err := m.headersChecksum(false) +// CreateIntegrityHeader returns signed integrity header for the object +func CreateIntegrityHeader(obj *Object, key *ecdsa.PrivateKey) (*Header, error) { + headerChecksum, err := obj.headersChecksum(false) if err != nil { - return err + return nil, err } headerChecksumSignature, err := crypto.Sign(key, headerChecksum) if err != nil { - return err + return nil, err } - m.AddHeader(&Header{Value: &Header_Integrity{ + + return &Header{Value: &Header_Integrity{ Integrity: &IntegrityHeader{ HeadersChecksum: headerChecksum, ChecksumSignature: headerChecksumSignature, }, - }}) + }}, nil +} + +// Sign creates new integrity header and adds it to the end of the list of +// extended headers. +func (m *Object) Sign(key *ecdsa.PrivateKey) error { + ih, err := CreateIntegrityHeader(m, key) + if err != nil { + return err + } + m.AddHeader(ih) return nil } From 81cdfcc502a47ddc4f781903585f7ab264fa45f3 Mon Sep 17 00:00:00 2001 From: alexvanin Date: Thu, 16 Jan 2020 17:32:57 +0300 Subject: [PATCH 5/5] object: Update verify routine Verify routine now supports public key field to check if integrity header contains correct signature of object header. Verify also have strict check that integrity header is last header in the list of headers. --- object/extensions.go | 1 + object/verification.go | 28 +++++++++++++-------- object/verification_test.go | 50 ++++++++++++++++++++++++++++++++++--- 3 files changed, 64 insertions(+), 15 deletions(-) diff --git a/object/extensions.go b/object/extensions.go index 7427079..f6b10ab 100644 --- a/object/extensions.go +++ b/object/extensions.go @@ -1,4 +1,5 @@ package object +// todo: all extensions must be transferred to the separate util library import "github.com/nspcc-dev/neofs-proto/storagegroup" diff --git a/object/verification.go b/object/verification.go index cbf12ab..742375a 100644 --- a/object/verification.go +++ b/object/verification.go @@ -68,24 +68,30 @@ func (m Object) Verify() error { var ( err error checksum []byte + pubkey []byte ) - // Prepare structures - _, vh := m.LastHeader(HeaderType(VerifyHdr)) - if vh == nil { - return ErrHeaderNotFound - } - verify := vh.Value.(*Header_Verify).Verify - - _, ih := m.LastHeader(HeaderType(IntegrityHdr)) - if ih == nil { + ind, ih := m.LastHeader(HeaderType(IntegrityHdr)) + if ih == nil || ind != len(m.Headers) - 1{ return ErrHeaderNotFound } integrity := ih.Value.(*Header_Integrity).Integrity + // Prepare structures + _, vh := m.LastHeader(HeaderType(VerifyHdr)) + if vh == nil { + _, pkh := m.LastHeader(HeaderType(PublicKeyHdr)) + if pkh == nil { + return ErrHeaderNotFound + } + pubkey = pkh.Value.(*Header_PublicKey).PublicKey.Value + } else { + pubkey = vh.Value.(*Header_Verify).Verify.PublicKey + } + // Verify signature - err = m.verifySignature(verify.PublicKey, integrity) + err = m.verifySignature(pubkey, integrity) if err != nil { - return errors.Wrapf(err, "public key: %x", verify.PublicKey) + return errors.Wrapf(err, "public key: %x", pubkey) } // Verify checksum of header diff --git a/object/verification_test.go b/object/verification_test.go index f91e051..b538023 100644 --- a/object/verification_test.go +++ b/object/verification_test.go @@ -56,7 +56,7 @@ func TestObject_Verify(t *testing.T) { obj.SetPayload(payload) obj.SetHeader(&Header{Value: &Header_PayloadChecksum{[]byte("incorrect checksum")}}) - t.Run("error no integrity header", func(t *testing.T) { + t.Run("error no integrity header and pubkey", func(t *testing.T) { err = obj.Verify() require.EqualError(t, err, ErrHeaderNotFound.Error()) }) @@ -83,12 +83,25 @@ func TestObject_Verify(t *testing.T) { } obj.SetVerificationHeader(vh) + // validation header is not last + t.Run("error validation header is not last", func(t *testing.T) { + err = obj.Verify() + require.EqualError(t, err, ErrHeaderNotFound.Error()) + }) + + obj.Headers = obj.Headers[:len(obj.Headers)-2] + obj.SetVerificationHeader(vh) + obj.SetHeader(&Header{Value: &Header_Integrity{ih}}) + t.Run("error invalid header checksum", func(t *testing.T) { err = obj.Verify() require.EqualError(t, err, ErrVerifyHeader.Error()) }) - require.NoError(t, obj.Sign(sessionkey)) + obj.Headers = obj.Headers[:len(obj.Headers)-1] + genIH, err := CreateIntegrityHeader(obj, sessionkey) + require.NoError(t, err) + obj.SetHeader(genIH) t.Run("error invalid payload checksum", func(t *testing.T) { err = obj.Verify() @@ -96,10 +109,39 @@ func TestObject_Verify(t *testing.T) { }) obj.SetHeader(&Header{Value: &Header_PayloadChecksum{obj.PayloadChecksum()}}) - require.NoError(t, obj.Sign(sessionkey)) - t.Run("correct", func(t *testing.T) { + obj.Headers = obj.Headers[:len(obj.Headers)-1] + genIH, err = CreateIntegrityHeader(obj, sessionkey) + require.NoError(t, err) + obj.SetHeader(genIH) + + t.Run("correct with vh", func(t *testing.T) { err = obj.Verify() require.NoError(t, err) }) + + pkh := Header{Value: &Header_PublicKey{&PublicKey{ + Value: crypto.MarshalPublicKey(&key.PublicKey), + }}} + // replace vh with pkh + obj.Headers[len(obj.Headers)-2] = pkh + // re-sign object + obj.Sign(sessionkey) + + + t.Run("incorrect with bad public key", func(t *testing.T) { + err = obj.Verify() + require.Error(t, err) + }) + + obj.SetHeader(&Header{Value: &Header_PublicKey{&PublicKey{ + Value: dataPK, + }}}) + obj.Sign(sessionkey) + + t.Run("correct with good public key", func(t *testing.T) { + err = obj.Verify() + require.NoError(t, err) + }) + }