diff --git a/README.md b/README.md index 5445763..76e22e1 100644 --- a/README.md +++ b/README.md @@ -90,3 +90,4 @@ See our [contributing guide](CONTRIBUTING.md) ### License Bitbot is available under the [MIT License](LICENSE) + diff --git a/bitbot/paste.go b/bitbot/paste.go new file mode 100644 index 0000000..7e0e934 --- /dev/null +++ b/bitbot/paste.go @@ -0,0 +1,301 @@ +package bitbot + +// welp; it turned out the key (the base58 encoded one) is used in a key-derivation function; that is, the key used in AES-GCM is the derived one; consequently, the salt we send is equally important; here're the specs +// key derivation function: pbkdf2 +// key: password of paste + our random key +// salt: the one we send +// hash algorithm: sha-256 (used at the end of derivation) +// iterations: 10,000 (can we instruct the server to lower it ?) +// ==================================================== +// will be using this: https://pkg.go.dev/golang.org/x/crypto/pbkdf2?tab=doc +import ( + "bytes" + "compress/zlib" + "crypto/aes" + "crypto/cipher" + "crypto/rand" + "crypto/sha256" + "crypto/tls" + "crypto/x509" + "golang.org/x/crypto/pbkdf2" + // "encoding/base64" + "encoding/json" + "fmt" + "io" + "io/ioutil" + // "net" + // "time" + "net/http" + // "os" + "strings" + + "github.com/btcsuite/btcutil/base58" + "github.com/whyrusleeping/hellabot" +) + +const ( + nonceSize = 16 // privatebin uses a nonce of 16 bytes by default + aesKeySize = 32 // using aes-256-gcm; for reference only + gcmTagSize = 16 // for reference + kdfSaltSize = 8 // for reference +) + +// Array1 : not used directly in the paste request +type Array1 struct { // TODO: more descriptive name + // TODO: the following fields (till EOF) should be a (json ?) array + Nonce []byte // base64(cipher_iv); getRandomBytes(16) default + Kdfsalt []byte // base64(kdf_salt); getRandomBytes(8) default + KdfIterations int // pbkdf_iterations; default + KdfKeySize int // pbkdf_keysize; default + CipherTagSize int // cipher_tag_size (wtf ?); default + CipherAlgo string // cipher_algo; default + CipherMode string // cipher_mode; default + CompressionType string // compression_type; default + // EOF +} + +// AuthData : Format is the paste's format. +type AuthData struct { //TODO: should be type "json" ? + // + EncryptionDetails []interface{} // TODO: more descriptive name (type was: Array1) + Format string // format of the paste + OpenDiscussion int // open-discussion flag (TODO: not sure if bool works) + BurnAfterReading int // burn-after-reading flag (TODO: not sure if bool works) + // +} + +// PasteData : !shrug (see https://github.com/PrivateBin/PrivateBin/wiki/Encryption-format#data-passed-in) +type PasteData struct { + Paste string `json:"paste"` // ciphertext (encrypted zlib'd plaintext) + Attachment string `json:"attachment"` + AttachementName string `json:"attachment_name"` + Children []interface{} `json:"children"` +} + +// ============================================================================================================================================================================================================================ + +// PasteMeta : https://raw.githubusercontent.com/PrivateBin/PrivateBin/master/js/types.jsonld +type PasteMeta struct { + Expire string `json:"expire"` // ["5min", "10min", "1hour", "1day", "1week", "1month", "1year", "never"] +} + +// PasteResponse : A request's response, parsed +type PasteResponse struct { + Status int `json:"status"` + Id string `json:"id"` + Url string `json:"url"` + Deletetoken string `json:"deletetoken"` +} + +// PasteRequest : A paste request +type PasteRequest struct { + AuthData []interface{} `json:"adata"` + Meta PasteMeta `json:"meta"` + Version int `json:"v"` + CipherText []byte `json:"ct"` +} + +// NewRequest : Forges a new request to be posted. +func NewRequest(aData []interface{}, cipherText []byte, expiryDate string) *PasteRequest { + + var ( + req PasteRequest + ) + + meta := PasteMeta{expiryDate} + + req.AuthData = aData + req.Meta = meta + req.Version = 2 + req.CipherText = cipherText + return &req + +} + +// PasteTrigger : The trigger that takes care of the pasting. +var PasteTrigger = NamedTrigger{ //nolint + ID: "paste", + Help: "returns a pastebin link for a PRIVMSG to bitbot" + + "\nUsage: !paste ", + Condition: func(irc *hbot.Bot, m *hbot.Message) bool { + return m.Command == "PRIVMSG" && // This is a message + m.Params[0] == irc.Nick && // Private to the bot + strings.HasPrefix(m.Content, "!paste ") // beginning with !paste + }, + Action: func(irc *hbot.Bot, m *hbot.Message) bool { + + var ( + pasteResp PasteResponse + pasteReq *PasteRequest + err error + ) + plaintext := []byte(m.Content) // TODO: only fetch paste content and options + key, nonce, kdfsalt := generateEncryptionParameters() + adata := generateAuthenticationData(nonce, kdfsalt, "plaintext", 0, 0) + aesKey := pbkdf2.Key(key, kdfsalt, 100000, aesKeySize, sha256.New) + ciphertext := encrypt(plaintext, aesKey, nonce, adata) // auth tag is appended to ciphertext + pasteReq = NewRequest(adata, ciphertext, "1week") + if pasteResp, err = recvPaste(pasteReq); err != nil { + fmt.Println("could not receive paste") + return false + } + + if pasteResp.Status != 0 { + irc.Reply(m, "This is impossible to see unless the same key AND message were used") + return false + } + url := fmt.Sprintf("https://bin.fraq.io%s#%s", pasteResp.Url, base58.Encode(key)) + deleteURL := fmt.Sprintf("https://bin.fraq.io/?pasteid=%s&deletetoken=%s", pasteResp.Id, pasteResp.Deletetoken) + fmt.Printf("response: %v\n", pasteResp) // TODO: use logging + fmt.Printf("url: %s\n delete url: %s\n", url, deleteURL) // TODO: use logging + irc.Reply(m, fmt.Sprintf("Link: %s | Delete paste: %s", url, deleteURL)) + + return true + }, +} + +func generateAuthenticationData(iv []byte, dummyKDFsalt []byte, format string, openDiscussion int, burnAfterReading int) []interface{} { + // encryptionInfo := Array1{iv, dummyKDFsalt, 10000, 265, 128, "aes", "gcm", "zlib"} + // encryptionInfo := make([]interface{}, 0) + var ( + encryptionInfo []interface{} + aData []interface{} // or aData := make([]interface{}, 0); then append + ) + encryptionInfo = append(encryptionInfo, iv, dummyKDFsalt, 100000, 256, 128, "aes", "gcm", "none") // TODO: rget back zlib support + aData = append(aData, encryptionInfo, format, openDiscussion, burnAfterReading) + return aData +} +func generateEncryptionParameters() (key, iv, kdfSalt []byte) { + + // since we'll be using a different random key for each paste, + // a fixed nonce should be OK (but we won't do it anyway) + totalSize := aesKeySize + nonceSize + kdfSaltSize + keyWithNonceAndKdfSalt := make([]byte, totalSize) + if _, err := io.ReadFull(rand.Reader, keyWithNonceAndKdfSalt); err != nil { + panic(err.Error()) + } + + key = keyWithNonceAndKdfSalt[:aesKeySize] + iv = keyWithNonceAndKdfSalt[aesKeySize : aesKeySize+nonceSize] + kdfSalt = keyWithNonceAndKdfSalt[totalSize-kdfSaltSize:] // dummy value for PBKDF as we don't use it + + return key, iv, kdfSalt + +} + +func recvPaste(pasteReq *PasteRequest) (resp PasteResponse, err error) { + var ( + jsonForm []byte + req *http.Request + r *http.Response + ) + + if jsonForm, err = json.Marshal(pasteReq); err != nil { // Marshal, not NewEncoder + fmt.Println(err) + } + fmt.Printf("marhsalled json: %s\n", jsonForm) + // ==== cert ==== // + // TODO: self-signed certificates workaround (https://groups.google.com/d/msg/golang-nuts/v5ShM8R7Tdc/I2wyTy1o118J) + cert, err := ioutil.ReadFile("/tmp/burp.pem") // TODO: to debug w/ burp, install its cert here + if err != nil { + fmt.Println("error in importing cert: ", err) + } + caCertPool := x509.NewCertPool() + if ok := caCertPool.AppendCertsFromPEM(cert); ok != true { + fmt.Println("error in appending cert") + } + transportOptions := &http.Transport{ + TLSClientConfig: &tls.Config{ + InsecureSkipVerify: true, + RootCAs: caCertPool, + }, + Proxy: http.ProxyFromEnvironment, + } + + httpClient := &http.Client{Transport: transportOptions} + if req, err = http.NewRequest("POST", "https://bin.fraq.io", bytes.NewReader(jsonForm)); err != nil { // TODO: don't hardcode url + fmt.Println(err) + } + req.Header.Set("Content-Type", "application/x-www-form-urlencoded; charset=UTF-8") + req.Header.Add("X-Requested-With", "JSONHttpRequest") // reason we used http.NewRequest w/ Client.Do() + // req.Header.Add("Origin", "https://bin.fraq.io") + + // fmt.Println("debugging body: ") + // if b, err := req.GetBody(); err == nil{ + // fmt.Println(ioutil.ReadAll(b)) + // fmt.Println(ioutil.ReadAll(req.Body)) + // } + // resp, err := http.Post(, "application/json", &jsonForm) + url, err := http.ProxyFromEnvironment(req) + fmt.Printf("proxy: %s %s\n", url, err) + // req.WriteProxy(os.Stdout) + + if r, err = httpClient.Do(req); err != nil { + fmt.Printf("post to paste site error: %s\n", err) // TODO: use log + return resp, err + } + // fmt.Println(r.Body) + defer r.Body.Close() + // body, err := ioutil.ReadAll(r.Body) + // fmt.Printf("received body: %s, err %s\n", body, err) + // err = json.Unmarshal(body, &resp) + if err = json.NewDecoder(r.Body).Decode(&resp); err != nil { + fmt.Printf("json decoding error: %s\n", err) // TODO: use logging + } + + return resp, err +} + +func encrypt(plaintext, key, iv []byte, authenticationData []interface{}) (ciphertext []byte) { + // compresses the message with zlib and encrypts it with a random key + + block, err := aes.NewCipher(key) // will auto-pick aes-256 because key size + if err != nil { + panic(err.Error()) + } + + aesgcm, err := cipher.NewGCMWithNonceSize(block, nonceSize) //TODO: should instruct privatebin to use standard nonce size instead + if err != nil { + panic(err.Error()) + } + + // compress and encrypt message, then encode key and return + var ( + compressedCiphertext bytes.Buffer + // pasteData PasteData + // encodedCompressedPlaintext bytes.Buffer + cipherJson, authenticatedDataJson []byte + ) + // pasteData = PasteData{Paste: string(plaintext)} // TODO: support file attachement and paste linking + pasteData := struct { + Paste string `json:"paste"` + }{ + string(plaintext), + } + if cipherJson, err = json.Marshal(pasteData); err != nil { // Marshal, not NewEncoder + panic(err.Error()) + } + if authenticatedDataJson, err = json.Marshal(authenticationData); err != nil { // Marshal, not NewEncoder + panic(err.Error()) + } + fmt.Printf("marshalled cipher: %s\n", cipherJson) + fmt.Printf("marshalled adata: %s\n", authenticatedDataJson) + + // TODO: add check for compression support (for now, assuming defaults); get back zlib + compressedCiphertextWriter := zlib.NewWriter(&compressedCiphertext) + compressedCiphertextWriter.Write(cipherJson) + compressedCiphertextWriter.Close() + // encoder := base64.NewEncoder(base64.StdEncoding, &encodedCompressedPlaintext) + // encoder.Write(compressedCiphertext.Bytes()) + // encoder.Close() + + // authData is authenticated as well(https://github.com/r4sas/PBinCLI/blob/682b47fbd3e24a8a53c3b484ba896a5dbc85cda2/pbincli/format.py#L122) + // kudos to filo for hinting about the tag location (https://github.com/golang/go/issues/32742) + // look for function " decryptOrPromptPassword" in privatebin.js; start debugging there + // TODO: fully support the API (https://github.com/PrivateBin/PrivateBin/wiki/API) + ciphertext = aesgcm.Seal(nil, iv, cipherJson, authenticatedDataJson) // TODO: zzzzlib + // encodedNonce := base64.StdEncoding.EncodeToString(nonce) + // encodedCipherText := base64.StdEncoding.EncodeToString(ciphertext) + fmt.Printf("pt: %s\n key: %s\n", plaintext, base58.Encode(key)) + return ciphertext +} diff --git a/cmd/root.go b/cmd/root.go index ddbcfb3..69f822f 100644 --- a/cmd/root.go +++ b/cmd/root.go @@ -75,6 +75,7 @@ var pluginMap = map[string]bitbot.NamedTrigger{ "markovTrainer": bitbot.MarkovTrainerTrigger, "epeen": bitbot.EpeenTrigger, "ipinfo": bitbot.IPinfoTrigger, + "paste": bitbot.PasteTrigger, "urbd": bitbot.UrbanDictionaryTrigger, "reminder": bitbot.ReminderTrigger, "lennyface": bitbot.LennyTrigger, diff --git a/docker-compose.yml b/docker-compose.yml index 1d9ee71..009e0af 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -6,7 +6,7 @@ services: environment: SERVER: "irc.example.com:6697" CHANNELS: "#bots" - NICK: "bitbot-fraq" + NICK: "bitbot-test" SSL: "true" PROM: "true" PROMADDR: "0.0.0.0:8080" diff --git a/go.mod b/go.mod index e17119f..102eec8 100644 --- a/go.mod +++ b/go.mod @@ -4,16 +4,19 @@ go 1.12 require ( github.com/bbriggs/quotes v0.0.0-20190225011706-ef48dc0c0ec7 - github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 + github.com/btcsuite/btcutil v1.0.1 + github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec // indirect github.com/jinzhu/gorm v1.9.12 github.com/justinian/dice v0.0.0-20170728002755-6a18b51d929c github.com/leanovate/gopter v0.2.4 github.com/mb-14/gomarkov v0.0.0-20190125094512-044dd0dcb5e7 + github.com/mudler/sendfd v0.0.0-20150620134918-f0fc74c13877 // indirect github.com/prometheus/client_golang v1.2.1 github.com/spf13/cobra v0.0.5 github.com/spf13/viper v1.4.0 github.com/whyrusleeping/hellabot v0.0.0-20200524171630-1ea75c7dc208 go.etcd.io/bbolt v1.3.3 + golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d golang.org/x/net v0.0.0-20191014212845-da9a3fd4c582 gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1 gopkg.in/sorcix/irc.v1 v1.1.4 diff --git a/go.sum b/go.sum index 2b1e854..3ef265a 100644 --- a/go.sum +++ b/go.sum @@ -1,6 +1,7 @@ cloud.google.com/go v0.26.0/go.mod h1:aQUYkXzVsufM+DwF1aE+0xfcU+56JwCaLick0ClmMTw= github.com/BurntSushi/toml v0.3.1/go.mod h1:xHWCNGjB5oqiDr8zfno3MHue2Ht5sIBksp03qcyfWMU= github.com/OneOfOne/xxhash v1.2.2/go.mod h1:HSdplMjZKSmBqAxg5vPj2TmRDmfkzw+cTzAElWljhcU= +github.com/aead/siphash v1.0.1/go.mod h1:Nywa3cDsYNNK3gaciGTWPwHt0wlpNV15vwmswBAUSII= github.com/alecthomas/template v0.0.0-20160405071501-a0175ee3bccc/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/template v0.0.0-20190718012654-fb15b899a751/go.mod h1:LOuyumcjzFXgccqObfd/Ljyb9UuFJ6TxHnclSeseNhc= github.com/alecthomas/units v0.0.0-20151022065526-2efee857e7cf/go.mod h1:ybxpYRFXyAe+OPACYpWeL0wqObRcbAqCMya13uyzqw0= @@ -12,6 +13,16 @@ github.com/beorn7/perks v0.0.0-20180321164747-3a771d992973/go.mod h1:Dwedo/Wpr24 github.com/beorn7/perks v1.0.0/go.mod h1:KWe93zE9D1o94FZ5RNwFwVgaQK1VOXiVxmqh+CedLV8= github.com/beorn7/perks v1.0.1 h1:VlbKKnNfV8bJzeqoa4cOKqO6bYr3WgKZxO8Z16+hsOM= github.com/beorn7/perks v1.0.1/go.mod h1:G2ZrVWU2WbWT9wwq4/hrbKbnv/1ERSJQ0ibhJ6rlkpw= +github.com/btcsuite/btcd v0.20.1-beta/go.mod h1:wVuoA8VJLEcwgqHBwHmzLRazpKxTv13Px/pDuV7OomQ= +github.com/btcsuite/btclog v0.0.0-20170628155309-84c8d2346e9f/go.mod h1:TdznJufoqS23FtqVCzL0ZqgP5MqXbb4fg/WgDys70nA= +github.com/btcsuite/btcutil v0.0.0-20190425235716-9e5f4b9a998d/go.mod h1:+5NJ2+qvTyV9exUAL/rxXi3DcLg2Ts+ymUAY5y4NvMg= +github.com/btcsuite/btcutil v1.0.1 h1:GKOz8BnRjYrb/JTKgaOk+zh26NWNdSNvdvv0xoAZMSA= +github.com/btcsuite/btcutil v1.0.1/go.mod h1:j9HUFwoQRsZL3V4n+qG+CUnEGHOarIxfC3Le2Yhbcts= +github.com/btcsuite/go-socks v0.0.0-20170105172521-4720035b7bfd/go.mod h1:HHNXQzUsZCxOoE+CPiyCTO6x34Zs86zZUiwtpXoGdtg= +github.com/btcsuite/goleveldb v0.0.0-20160330041536-7834afc9e8cd/go.mod h1:F+uVaaLLH7j4eDXPRvw78tMflu7Ie2bzYOH4Y8rRKBY= +github.com/btcsuite/snappy-go v0.0.0-20151229074030-0bdef8d06723/go.mod h1:8woku9dyThutzjeg+3xrA5iCpBRH8XEEg3lh6TiUghc= +github.com/btcsuite/websocket v0.0.0-20150119174127-31079b680792/go.mod h1:ghJtEyQwv5/p4Mg4C0fgbePVuGr935/5ddU9Z3TmDRY= +github.com/btcsuite/winsvc v1.0.0/go.mod h1:jsenWakMcC0zFBFurPLEAyrnc/teJEM1O46fmI40EZs= github.com/cespare/xxhash v1.1.0 h1:a6HrQnmkObjyL+Gs60czilIUGqrzKutQD6XZog3p+ko= github.com/cespare/xxhash v1.1.0/go.mod h1:XrSqR1VqqWfGrhpAt58auRo0WTKS1nRRg3ghfAqPWnc= github.com/cespare/xxhash/v2 v2.1.0 h1:yTUvW7Vhb89inJ+8irsUqiWjh8iT6sQPZiQzI6ReGkA= @@ -24,6 +35,7 @@ github.com/coreos/go-semver v0.2.0/go.mod h1:nnelYz7RCh+5ahJtPPxZlU+153eP4D4r3Ee github.com/coreos/go-systemd v0.0.0-20190321100706-95778dfbb74e/go.mod h1:F5haX7vjVVG0kc13fIWeqUViNPyEJxv/OmvnBo0Yme4= github.com/coreos/pkg v0.0.0-20180928190104-399ea9e2e55f/go.mod h1:E3G3o1h8I7cfcXa63jLwjI0eiQQMgzzUDFVpN/nH/eA= github.com/cpuguy83/go-md2man v1.0.10/go.mod h1:SmD6nW6nTyfqj6ABTjUi3V3JVMnlJmwcJI5acqYI6dE= +github.com/davecgh/go-spew v0.0.0-20171005155431-ecdeabc65495/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/davecgh/go-spew v1.1.1/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38= github.com/denisenkom/go-mssqldb v0.0.0-20191124224453-732737034ffd/go.mod h1:xbL0rPBG9cCiLr28tMa8zpbdarY27NDyej4t/EjAShU= @@ -60,17 +72,21 @@ github.com/grpc-ecosystem/go-grpc-prometheus v1.2.0/go.mod h1:8NvIoxWQoOIhqOTXgf github.com/grpc-ecosystem/grpc-gateway v1.9.0/go.mod h1:vNeuVxBJEsws4ogUvrchl83t/GYV9WGTSLVdBhOQFDY= github.com/hashicorp/hcl v1.0.0 h1:0Anlzjpi4vEasTeNFn2mLJgTSwt0+6sfsiTG8qcWGx4= github.com/hashicorp/hcl v1.0.0/go.mod h1:E5yfLk+7swimpb2L/Alb/PJmXilQ/rhwaUYs4T20WEQ= +github.com/hpcloud/tail v1.0.0/go.mod h1:ab1qPbhIpdTxEkNHXyeSf5vhxWSCs/tWer42PpOxQnU= +github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec h1:CGkYB1Q7DSsH/ku+to+foV4agt2F2miquaLUgF6L178= github.com/inconshreveable/log15 v0.0.0-20180818164646-67afb5ed74ec/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1 h1:KUDFlmBg2buRWNzIcwLlKvfcnujcHQRQ1As1LoaCLAM= github.com/inconshreveable/log15 v0.0.0-20200109203555-b30bc20e4fd1/go.mod h1:cOaXtrgN4ScfRrD9Bre7U1thNq5RtJ8ZoP4iXVGRj6o= github.com/inconshreveable/mousetrap v1.0.0 h1:Z8tu5sraLXCXIcARxBp/8cbvlwVa7Z1NHg9XEKhtSvM= github.com/inconshreveable/mousetrap v1.0.0/go.mod h1:PxqpIevigyE2G7u3NXJIT2ANytuPF1OarO4DADm73n8= +github.com/jessevdk/go-flags v0.0.0-20141203071132-1679536dcc89/go.mod h1:4FA24M0QyGHXBuZZK/XkWh8h0e1EYbRYJSGM75WSRxI= github.com/jinzhu/gorm v1.9.12 h1:Drgk1clyWT9t9ERbzHza6Mj/8FY/CqMyVzOiHviMo6Q= github.com/jinzhu/gorm v1.9.12/go.mod h1:vhTjlKSJUTWNtcbQtrMBFCxy7eXTzeCAzfL5fBZT/Qs= github.com/jinzhu/inflection v1.0.0 h1:K317FqzuhWc8YvSVlFMCCUb36O/S9MCKRDI7QkRKD/E= github.com/jinzhu/inflection v1.0.0/go.mod h1:h+uFLlag+Qp1Va5pdKtLDYj+kHp5pxUVkryuEj+Srlc= github.com/jinzhu/now v1.0.1/go.mod h1:d3SSVoowX0Lcu0IBviAWJpolVfI5UJVZZ7cO71lE/z8= github.com/jonboulle/clockwork v0.1.0/go.mod h1:Ii8DK3G1RaLaWxj9trq07+26W01tbo22gdxWY5EU2bo= +github.com/jrick/logrotate v1.0.0/go.mod h1:LNinyqDIJnpAur+b8yyulnQw/wDuN1+BYKlTRt3OuAQ= github.com/json-iterator/go v1.1.6/go.mod h1:+SdeFBvtyEkXs7REEP0seUULqWtbJapLOCVDaaPEHmU= github.com/json-iterator/go v1.1.7/go.mod h1:KdQUCv79m/52Kvf8AW2vK1V8akMuk1QjK/uOdHXbAo4= github.com/julienschmidt/httprouter v1.2.0/go.mod h1:SYymIcj16QtmaHHD7aYtjjsJG7VTCxuUUipMqKk8s4w= @@ -78,6 +94,7 @@ github.com/justinian/dice v0.0.0-20170728002755-6a18b51d929c h1:nuF3PF5z48DaqyVL github.com/justinian/dice v0.0.0-20170728002755-6a18b51d929c/go.mod h1:AyqU0eD51oSe8WchcqHezWZKoQpXZkT4MWqvaW7AnW8= github.com/kisielk/errcheck v1.1.0/go.mod h1:EZBBE59ingxPouuu3KfxchcWSUPOHkagtvWXihfKN4Q= github.com/kisielk/gotool v1.0.0/go.mod h1:XhKaO+MFFWcvkIS/tQcRk01m1F5IRFswLeQ+oQHNcck= +github.com/kkdai/bstream v0.0.0-20161212061736-f391b8402d23/go.mod h1:J+Gs4SYgM6CZQHDETBtE9HaSEkGmuNXF86RwHhHUvq4= github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ= github.com/kr/logfmt v0.0.0-20140226030751-b84e30acd515/go.mod h1:+0opPa2QZZtGFBFZlji/RkVcI2GknAs/DXo4wKdlNEc= github.com/kr/pretty v0.1.0/go.mod h1:dAy3ld7l9f0ibDNOQOHHMYYIIbhfbHSm3C4ZsoJORNo= @@ -114,6 +131,9 @@ github.com/mudler/sendfd v0.0.0-20150620134918-f0fc74c13877 h1:Xp2ntqpIKo5TC32/F github.com/mudler/sendfd v0.0.0-20150620134918-f0fc74c13877/go.mod h1:j4hnSAX0+AZQpILVkkVRwfQTeAFMH0crQMGe5apT9Yo= github.com/mwitkow/go-conntrack v0.0.0-20161129095857-cc309e4a2223/go.mod h1:qRWi+5nqEBWmkhHvq77mSJWrCKwh8bxhgT7d/eI7P4U= github.com/oklog/ulid v1.3.1/go.mod h1:CirwcVhetQ6Lv90oh/F+FBtV6XMibvdAFo93nm5qn4U= +github.com/onsi/ginkgo v1.6.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/ginkgo v1.7.0/go.mod h1:lLunBs/Ym6LB5Z9jYTR76FiuTmxDTDusOGeTQH+WWjE= +github.com/onsi/gomega v1.4.3/go.mod h1:ex+gbHU/CVuBBDIJjb2X0qEXbFg53c61hWP/1CpauHY= github.com/pelletier/go-toml v1.2.0 h1:T5zMGML61Wp+FlcbWjRDT7yAxhJNAiPPLOFECq181zc= github.com/pelletier/go-toml v1.2.0/go.mod h1:5z9KED0ma1S8pY6P1sdut58dfprrGBbd/94hg7ilaic= github.com/pkg/errors v0.8.0/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0= @@ -175,15 +195,19 @@ go.etcd.io/bbolt v1.3.3/go.mod h1:IbVyRI1SCnLcuJnV2u8VeU0CEYM7e686BmAb1XKL+uU= go.uber.org/atomic v1.4.0/go.mod h1:gD2HeocX3+yG+ygLZcrzQJaqmWj9AIm7n08wl/qW/PE= go.uber.org/multierr v1.1.0/go.mod h1:wR5kodmAFQ0UK8QlbwjlSNy0Z68gJhDJUG5sjR94q/0= go.uber.org/zap v1.10.0/go.mod h1:vwi/ZaCAaUcBkycHslxD9B2zi4UTXhF60s6SWpuDF0Q= +golang.org/x/crypto v0.0.0-20170930174604-9419663f5a44/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20180904163835-0709b304e793/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20181203042331-505ab145d0a9/go.mod h1:6SG95UA2DQfeDnfUPMdvaQW0Q7yPrPDi9nlGo2tz2b4= golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20190325154230-a5d413f7728c/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w= golang.org/x/crypto v0.0.0-20191205180655-e7c4368fe9dd/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d h1:2+ZP7EfsZV7Vvmx3TIqSlSzATMkTAKqM14YGFPoSKjI= +golang.org/x/crypto v0.0.0-20200115085410-6d4e4cb37c7d/go.mod h1:LzIPMQfyMNhhGPhUkYOs5KpL4U8rLKemX1yGLhDgUto= golang.org/x/lint v0.0.0-20181026193005-c67002cb31c3/go.mod h1:UVdnD1Gm6xHRNCYTkRU2/jEulfH38KcIWyp/GAMgvoE= golang.org/x/lint v0.0.0-20190313153728-d0100b6bd8b3/go.mod h1:6SW0HCj/g11FgYtHlgUYUwCkIfeOF89ocIRzGO/8vkc= golang.org/x/net v0.0.0-20180724234803-3673e40ba225/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20180826012351-8a410e7b638d/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= +golang.org/x/net v0.0.0-20180906233101-161cd47e91fd/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181114220301-adae6a3d119a/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20181220203305-927f97764cc3/go.mod h1:mL1N/T3taQHkDXs73rZJwtUhF3w3ftmwwsq0BUmARs4= golang.org/x/net v0.0.0-20190311183353-d8887717615a/go.mod h1:t9HGtf8HONx5eT2rtn7q6eTqICYqUVnKs3thJo3Qplg= @@ -200,6 +224,7 @@ golang.org/x/sync v0.0.0-20181108010431-42b317875d0f/go.mod h1:RxMgew5VJxzue5/jJ golang.org/x/sync v0.0.0-20181221193216-37e7f081c4d4/go.mod h1:RxMgew5VJxzue5/jJTE5uejpjVlOe/izrB70Jof72aM= golang.org/x/sys v0.0.0-20180830151530-49385e6e1522/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20180905080454-ebe1bf3edb33/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= +golang.org/x/sys v0.0.0-20180909124046-d0be0721c37e/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181107165924-66b7b1311ac8/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181116152217-5ac8a444bdc5/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= golang.org/x/sys v0.0.0-20181205085412-a5c9d58dba9a/go.mod h1:STP8DvDyc/dI5b8T5hshtkjS+E42TnysNCUPdjciGhY= @@ -227,6 +252,7 @@ google.golang.org/grpc v1.21.0/go.mod h1:oYelfM1adQP15Ek0mdvEgi9Df8B9CZIaU1084ij gopkg.in/alecthomas/kingpin.v2 v2.2.6/go.mod h1:FMv+mEhP44yOT+4EoQTLFTRgOQ1FBLkstjWtayDeSgw= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= gopkg.in/check.v1 v1.0.0-20180628173108-788fd7840127/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= +gopkg.in/fsnotify.v1 v1.4.7/go.mod h1:Tz8NjZHkW78fSQdbUxIjBTcgA1z1m8ZHf0WmKUhAMys= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec h1:RlWgLqCMMIYYEVcAR5MDsuHlVkaIPDAF+5Dehzg8L5A= gopkg.in/inconshreveable/log15.v2 v2.0.0-20180818164646-67afb5ed74ec/go.mod h1:aPpfJ7XW+gOuirDoZ8gHhLh3kZ1B08FtV2bbmy7Jv3s= gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1 h1:iiHuQZCNgYPmFQxd3BBN/Nc5+dAwzZuq5y40s20oQw0= @@ -234,8 +260,7 @@ gopkg.in/inconshreveable/log15.v2 v2.0.0-20200109203555-b30bc20e4fd1/go.mod h1:a gopkg.in/resty.v1 v1.12.0/go.mod h1:mDo4pnntr5jdWRML875a/NmxYqAlA73dVijT2AXvQQo= gopkg.in/sorcix/irc.v1 v1.1.4 h1:ejxu1vDyqn1d+I5WXzh93pMDq5NqdwBl3V/eN5c2we8= gopkg.in/sorcix/irc.v1 v1.1.4/go.mod h1:CHwY3DGuZpB6/OvF+fj4Jlvnj6QeS56EUTDoQkuAePM= -gopkg.in/sorcix/irc.v2 v2.0.0-20200812151606-3f15758ea8c7 h1:XS4tmz0w7EYviIrBpFVww8IyKJQiIX5SU/1ptPVtBWI= -gopkg.in/sorcix/irc.v2 v2.0.0-20200812151606-3f15758ea8c7/go.mod h1:PmJkUcwbuPi1FiZ9Rarr6wzVMvzkO7uWqH1jwrMkgW0= +gopkg.in/tomb.v1 v1.0.0-20141024135613-dd632973f1e7/go.mod h1:dt/ZhP58zS4L8KSrWDmTeBkI65Dw0HsyUHuEVlX15mw= gopkg.in/yaml.v2 v2.0.0-20170812160011-eb3733d160e7/go.mod h1:JAlM8MvJe8wmxCU4Bli9HhUf9+ttbYbLASfIpnQbh74= gopkg.in/yaml.v2 v2.2.1/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI= gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw= diff --git a/progress.md b/progress.md new file mode 100644 index 0000000..61bfb4f --- /dev/null +++ b/progress.md @@ -0,0 +1,92 @@ +## usage and scenario +create a new paste +`/msg bitbot !paste ` + +delete a paste +`/msg bitbot !paste --delete ` + +see [sample](https://github.com/PrivateBin/PrivateBin/blob/master/cfg/conf.sample.php) for sample server-side configuration file for privatebin (and for more ideas, like url-shortner) + +the following sequence diagram represents the creation of a new paste: + +![sd_create_paste](tmp/sd_create_paste.png) + +## encryption format +from [here](https://github.com/PrivateBin/PrivateBin/wiki/Encryption-format#encryption); the following was request was sent with key `7MqznSyqHVx9VwNWfHRi6PLsr322eBY4Fnkx45tFV1gR` + +```json +{ + "adata": [ // auth data + [ + "Yu5ECsseuJ07M2QBfdf3bA==", // base64(cipher_iv); getRandomBytes(16) default + "kvDZJC6IahU=", // base64(kdf_salt); getRandomBytes(8) default + 100000, // pbkdf_iterations; default + 256, // pbkdf_keysize; default + 128, // cipher_tag_size (wtf ?); default + "aes", // cipher_algo; default + "gcm", // cipher_mode; default + "zlib" // compression_type; default + ], + "plaintext", // format of the paste - "plaintext" or "syntaxhighlighting" or "markdown" + + 0, // open-discussion flag + 0 // burn-after-reading flag (0 or 1) + ], + "meta": { + "expire": "1week" + }, + "v": 2, // schema version + "ct": "aVKpZWtKmTJKis5S6nYEL1rdxyPbHYFclHV3E6Kq99Tb" // cipher text +} +``` +prompt for password on wrong encryption keys + +### Process data + +If paste_password is an empty string: + +``` +paste_passphrase = random(32) # 32 bytes +``` + +if a paste_password has been specified: + +``` +paste_passphrase = random(32) + paste_password +``` + +Processing of the paste_data, if compression is enabled (the default): + +``` +paste_blob = zlib.compress(paste_data) +``` + +### Key derivation (PBKDF2) + +Since passwords and keys are usually too short to be usable for encryption, it is common practice to use salted key derivation to turn such low entropy input into the actual key to use during en/decryption. + +```python +kdf_salt = random(8) # 8 bytes +kdf_iterations = 100000 # was 10000 before PrivateBin version 1.3 +kdf_keysize = 256 # bits of resulting kdf_key + +kdf_key = PBKDF2_HMAC_SHA256(kdf_keysize, kdf_salt, paste_password) +``` + +The encrypted text is then: + +```python +cipher_algo = "aes" +cipher_mode = "gcm" # was "ccm" before PrivateBin version 1.0 +cipher_iv = random(16) # 128 bit +cipher_tag_size = 128 + +cipher_text = cipher(AES(kdf_key), GCM(iv, paste_meta), paste_blob) +``` + +## todo + +* switch to a db instead of "file-less" configuration +* enable ability to delete +* enable paste cloning + diff --git a/sequence_pastebin.png b/sequence_pastebin.png new file mode 100644 index 0000000..8abbf64 Binary files /dev/null and b/sequence_pastebin.png differ