-
Notifications
You must be signed in to change notification settings - Fork 376
/
api_attachment.go
147 lines (127 loc) · 4.39 KB
/
api_attachment.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
package bertyprotocol
import (
"errors"
"fmt"
"strconv"
ipfscid "github.com/ipfs/go-cid"
ipfsfiles "github.com/ipfs/go-ipfs-files"
ipfsoptions "github.com/ipfs/interface-go-ipfs-core/options"
ipfspath "github.com/ipfs/interface-go-ipfs-core/path"
"berty.tech/berty/v2/go/internal/cryptoutil"
"berty.tech/berty/v2/go/internal/streamutil"
"berty.tech/berty/v2/go/pkg/errcode"
"berty.tech/berty/v2/go/pkg/protocoltypes"
"berty.tech/berty/v2/go/pkg/tyber"
)
func (s *service) AttachmentPrepare(stream protocoltypes.ProtocolService_AttachmentPrepareServer) (err error) {
tyberCtx, _, endSection := tyber.Section(stream.Context(), s.logger, "Preparing attachment")
defer func() { endSection(err, "") }()
// read header
headerMsg, err := stream.Recv()
if err != nil {
return errcode.ErrStreamHeaderRead.Wrap(err)
}
if len(headerMsg.GetBlock()) > 0 {
return errcode.ErrInvalidInput.Wrap(errors.New("unexpected non-empty block"))
}
if headerMsg.GetDisableEncryption() {
return errcode.ErrNotImplemented.Wrap(errors.New("disable_encryption not implemented"))
}
tyber.LogStep(tyberCtx, s.logger, "AttachmentPrepare header received", tyber.WithJSONDetail("Header", headerMsg))
// open requests reader
plaintext := streamutil.FuncReader(func() ([]byte, error) {
msg, err := stream.Recv()
return msg.GetBlock(), err
}, s.logger)
defer plaintext.Close()
// open stream cipher
sk, ciphertext, err := cryptoutil.AttachmentSealer(plaintext, s.logger)
if err != nil {
return errcode.ErrCryptoCipherInit.Wrap(err)
}
defer ciphertext.Close()
// sink ciphertext to ipfs
ipfsFile := ipfsfiles.NewReaderFile(ciphertext)
defer ipfsFile.Close()
ipfsPath, err := s.ipfsCoreAPI.Unixfs().Add(stream.Context(), ipfsFile, attachmentForcePin)
if err != nil {
return errcode.ErrIPFSAdd.Wrap(err)
}
cid := ipfsPath.Cid()
// log file info
{
sz, err := ipfsFile.Size()
if err != nil {
sz = -1
}
tyber.LogStep(tyberCtx, s.logger, "Sinked ciphertext to ipfs",
tyber.WithDetail("CID", cid.String()),
tyber.WithDetail("IPFSPath", ipfsPath.String()),
tyber.WithDetail("CiphertextSize", strconv.FormatInt(sz, 10)),
)
}
// store associated private key
cidBytes := cid.Bytes()
if err = s.deviceKeystore.AttachmentPrivKeyPut(cidBytes, sk); err != nil {
return errcode.ErrKeystorePut.Wrap(err)
}
// return cid to client
if err = stream.SendAndClose(&protocoltypes.AttachmentPrepare_Reply{AttachmentCID: cidBytes}); err != nil {
return errcode.ErrStreamSendAndClose.Wrap(err)
}
// success
return nil
}
func (s *service) AttachmentRetrieve(req *protocoltypes.AttachmentRetrieve_Request, stream protocoltypes.ProtocolService_AttachmentRetrieveServer) (err error) {
tyberCtx, _, endSection := tyber.Section(stream.Context(), s.logger, "Retrieving attachment")
defer func() {
if err != nil {
endSection(err, "")
}
}()
// deserialize cid. We could do it later but it's better to fail fast
cid, err := ipfscid.Cast(req.GetAttachmentCID())
if err != nil {
return errcode.ErrDeserialization.Wrap(err)
}
// get associated private key
sk, err := s.deviceKeystore.AttachmentPrivKey(req.GetAttachmentCID())
if err != nil {
return errcode.ErrKeystoreGet.Wrap(err)
}
// open ciphertext reader
ipfsNode, err := s.ipfsCoreAPI.Unixfs().Get(stream.Context(), ipfspath.IpfsPath(cid))
if err != nil {
return errcode.ErrIPFSGet.Wrap(err)
}
defer ipfsNode.Close()
if sz, err := ipfsNode.Size(); err == nil {
tyber.LogStep(tyberCtx, s.logger, fmt.Sprintf("Found attachment's ciphertext of %.2f MB", float64(sz)/1000/1000))
}
ciphertext := ipfsfiles.ToFile(ipfsNode)
defer ciphertext.Close()
// open stream cipher
plaintext, err := cryptoutil.AttachmentOpener(ciphertext, sk, s.logger)
if err != nil {
return errcode.ErrCryptoCipherInit.Wrap(err)
}
defer plaintext.Close()
// sink plaintext to client
plaintextSize := 0
if err := streamutil.FuncSink(make([]byte, 64*1024), plaintext, func(block []byte) error {
plaintextSize += len(block)
return stream.Send(&protocoltypes.AttachmentRetrieve_Reply{Block: block})
}); err != nil {
return errcode.ErrStreamSink.Wrap(err)
}
// success
endSection(nil, fmt.Sprintf("Decrypted attachment of %.2f MB", float64(plaintextSize)/1000/1000))
return nil
}
func attachmentForcePin(settings *ipfsoptions.UnixfsAddSettings) error {
if settings == nil {
return errcode.ErrInvalidInput.Wrap(errors.New("nil ipfs settings"))
}
settings.Pin = true
return nil
}