Commit 6919d70f authored by zauberstuhl's avatar zauberstuhl

Initial commit

parents
This diff is collapsed.
# GangGo Federation Library
Pretty raw federation library for golang to communicate with the diaspora protocol
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import (
"crypto"
"crypto/rsa"
"crypto/x509"
"crypto/rand"
"crypto/sha256"
"crypto/aes"
"crypto/cipher"
"encoding/base64"
"encoding/pem"
"encoding/xml"
"encoding/json"
"errors"
"github.com/revel/revel"
)
func ParseBase64RSAPubKey(encodedKey string) (pubkey *rsa.PublicKey, err error) {
decodedKey, err := base64.StdEncoding.DecodeString(encodedKey)
if err != nil {
return
}
return ParseRSAPubKey(decodedKey)
}
func ParseRSAPubKey(decodedKey []byte) (pubkey *rsa.PublicKey, err error) {
block, _ := pem.Decode(decodedKey)
if block == nil {
err = errors.New("Decode public key block is nil!")
return
}
data, err := x509.ParsePKIXPublicKey(block.Bytes)
if err != nil {
return
}
switch data := data.(type) {
case *rsa.PublicKey:
pubkey = data
default:
err = errors.New("Wasn't able to parse the public key!")
}
return
}
func ParseRSAPrivKey(decodedKey []byte) (privkey *rsa.PrivateKey, err error) {
block, _ := pem.Decode(decodedKey)
if block == nil {
err = errors.New("Decode private key block is nil!")
return
}
privkey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
if err != nil {
return
}
return
}
func (aes *XmlDecryptedHeader) DecryptAES(ciphertext *[]byte, data string) error {
key, err := base64.StdEncoding.DecodeString(aes.AesKey)
if err != nil {
return err
}
iv, err := base64.StdEncoding.DecodeString(aes.Iv)
if err != nil {
return err
}
*ciphertext, err = base64.URLEncoding.DecodeString(data)
if err != nil {
return err
}
// diaspora magic do it twice
*ciphertext, err = base64.StdEncoding.DecodeString(string(*ciphertext))
if err != nil {
return err
}
*ciphertext, err = DecryptAES(key, iv, *ciphertext)
if err != nil {
return err
}
return nil
}
func (aes *JsonAesKey) DecryptAES(ciphertext *[]byte, data string) error {
key, err := base64.StdEncoding.DecodeString(aes.Key)
if err != nil {
return err
}
iv, err := base64.StdEncoding.DecodeString(aes.Iv)
if err != nil {
return err
}
*ciphertext, err = base64.StdEncoding.DecodeString(data)
if err != nil {
return err
}
*ciphertext, err = DecryptAES(key, iv, *ciphertext)
if err != nil {
return err
}
return nil
}
func DecryptAES(key, iv, ciphertext []byte) ([]byte, error) {
block, err := aes.NewCipher(key)
if err != nil {
return ciphertext, err
}
mode := cipher.NewCBCDecrypter(block, iv)
mode.CryptBlocks(ciphertext, ciphertext)
return ciphertext, nil
}
func (request *DiasporaUnmarshal) VerifySignature(serialized []byte) error {
pubkey, err := ParseRSAPubKey(serialized)
if err != nil {
warn(err)
return err
}
type64 := base64.StdEncoding.EncodeToString(
[]byte(request.Env.Data.Type),
)
encoding64 := base64.StdEncoding.EncodeToString(
[]byte(request.Env.Encoding),
)
alg64 := base64.StdEncoding.EncodeToString(
[]byte(request.Env.Alg),
)
text := request.Env.Data.Data + "." + type64 + "." + encoding64 + "." + alg64
h := sha256.New()
h.Write([]byte(text))
digest := h.Sum(nil)
ds, err := base64.URLEncoding.DecodeString(request.Env.Sig.Sig)
if err != nil {
return err
}
return rsa.VerifyPKCS1v15(pubkey, crypto.SHA256, digest, ds)
}
func AuthorSignature(data interface{}, privKey string) (string, error) {
var text string
switch entity := data.(type) {
case *EntityComment:
text = entity.DiasporaHandle+";"+entity.Guid+";"+
entity.ParentGuid+";"+entity.Text
case *EntityStatusMessage:
// parent author signature
//timeLayout := "2006-01-02T15:04:05-07:00"
//timeLayout := "2006-01-02T15:04:05.000000000Z"
// e.g. 2017-02-20T00:44:37.548811913+01:00
public := "false"
if entity.Public {
public = "true"
}
// <XML><post><status_message><diaspora_handle>lukas@192.168.0.173:9000</diaspora_handle><guid>de7f4b0a17ad43b74627628ee5f956fc</guid><created_at>2017-02-20T14:41:44.545004637Z</created_at><provider_display_name>GangGo</provider_display_name><raw_message>app/models/entities/post</raw_message><public>true</public></status_message></post></XML>
//<comment><diaspora_handle>lukas@192.168.0.173:9000</diaspora_handle><guid>3cadba7a21004e2ddebb0c3d18cb1951</guid><parent_guid>de7f4b0a17ad43b74627628ee5f956fc</parent_guid><text>app/models/entities/post2</text><author_signature>nTOIXwLuxeyR3Df9mEmIt56CnEFrnEWTCBfilioKydE6oHcjGLZTZL2nONzgBWmkhBCrM7TMd2k6LJFWDBZE4gTDirbBneAEamWtXoNAsQRjUD4NNJpfVjqZXC3D9269nmZQ1eRojIjNfNWHB13LDBIWQC1yfKG0Hokg23745nlqMeKGWB3ntrNm5rOHPfpcRt/VoxqB80nUXYdSePsbagAPh5KxvIaf+rNQdNVa5r8d1bHXyk41Doh2a4JdyVnC1D+vPJkX5R9vtoVbbzpSRSFQ9zJnbkVqIjVtS7oi3zI3zU+liM/n0iXA424/HOZW+nmP6LRGLsj3y/Wn6HW32A==</author_signature><parent_author_signature>jxxB0YtDkxfy+aAuKXliefGdtfcEAV1BmisdzYTNiay+ua7jtuSHq3AAIECEAQVwMWpxZ36uB69d6ji4KBqdJFMaD8momPqd7WjZYrTKUSObFl8mq2REAL1pBBFmECgmjyiOixwBNFE6r2dqD6Uk85GsY+IhNCuhvLsM7sM5ZYVZdoqPZv3lC2j6m/fvStG3EtYh7ruu/XxR1+mucRU5M4UaS48MD86F55+pDS7qj7QDMk/bronBz9SkDPJkpYGerrRTP4njKkNMhWjPxy8wpYURW8DRFqZLjOcYLLnmhppej3wJXrrx4PwufVW/zU6eZVdll8jsiaK7cmFWfjGKsQ==</parent_author_signature></comment>
//lukas@192.168.0.173:9000;de7f4b0a17ad43b74627628ee5f956fc;2017-02-20T15:41:44+00:00;GangGo;app/models/entities/post;true
text = entity.DiasporaHandle+";"+entity.Guid+";"+
//entity.CreatedAt.UTC().Format(timeLayout)+";"+
"2017-02-20T14:41:44.545004637Z;"+
entity.ProviderName+";"+entity.RawMessage+";"+public
// author guid parent_guid text
//lukas@192.168.0.173:9000;de7f4b0a17ad43b74627628ee5f956fc;2017-02-20T14:41:44.545004637Z;GangGo;app/models/entities/post;true
revel.WARN.Println(text)
}
return Sign(text, privKey)
}
func (d *DiasporaMarshal) Sign(privKey string) (err error) {
type64 := base64.StdEncoding.EncodeToString(
[]byte(d.Data.Type),
)
encoding64 := base64.StdEncoding.EncodeToString(
[]byte(d.Encoding),
)
alg64 := base64.StdEncoding.EncodeToString(
[]byte(d.Alg),
)
text := d.Data.Data + "." + type64 +
"." + encoding64 + "." + alg64
(*d).Sig.Sig, err = Sign(text, privKey)
return
}
func Sign(text, privKey string) (sig string, err error) {
privkey, err := ParseRSAPrivKey([]byte(privKey))
if err != nil {
return "", err
}
h := sha256.New()
h.Write([]byte(text))
digest := h.Sum(nil)
rng := rand.Reader
sigInByte, err := rsa.SignPKCS1v15(rng, privkey, crypto.SHA256, digest[:])
if err != nil {
return "", err
}
return base64.StdEncoding.EncodeToString(sigInByte), nil
}
func (request *DiasporaUnmarshal) DecryptHeader(serialized []byte) error {
header := JsonEnvHeader{}
decryptedHeader := XmlDecryptedHeader{}
decoded, err := base64.StdEncoding.DecodeString(request.EncryptedHeader)
if err != nil {
return err
}
err = json.Unmarshal(decoded, &header)
if err != nil {
return err
}
privkey, err := ParseRSAPrivKey(serialized)
if err != nil {
return err
}
decoded, err = base64.StdEncoding.DecodeString(header.AesKey)
if err != nil {
return err
}
aesKeyJson, err := rsa.DecryptPKCS1v15(rand.Reader, privkey, decoded)
if err != nil {
return err
}
var aesKey JsonAesKey
err = json.Unmarshal(aesKeyJson, &aesKey)
if err != nil {
return err
}
var ciphertext []byte
err = aesKey.DecryptAES(&ciphertext, header.Ciphertext)
if err != nil {
return err
}
err = xml.Unmarshal(ciphertext, &decryptedHeader)
if err != nil {
return err
}
(*request).DecryptedHeader = &decryptedHeader
return nil
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import "encoding/xml"
/* Encrypted Header
<?xml version="1.0" encoding="UTF-8"?>
<decrypted_header>
<iv>...</iv>
<aes_key>...</aes_key>
<author_id>one@two.tld</author_id>
</decrypted_header>
*/
type XmlDecryptedHeader struct {
XMLName xml.Name `xml:"decrypted_header"`
Iv string `xml:"iv"`
AesKey string `xml:"aes_key"`
AuthorId string `xml:"author_id"`
}
type JsonAesKey struct {
Key string `json:"key,omitempty"`
Iv string `json:"iv,omitempty"`
}
type JsonEnvHeader struct {
AesKey string `json:"aes_key"`
Ciphertext string `json:"ciphertext"`
}
// Marshal Requests Non-Legacy
type DiasporaMarshal struct {
XMLName xml.Name `xml:"me:env"`
Me string `xml:"xmlns:me,attr"`
Data struct {
XMLName xml.Name `xml:"me:data"`
Type string `xml:"type,attr"`
Data string `xml:",chardata"`
}
Encoding string `xml:"me:encoding"`
Alg string `xml:"me:alg"`
Sig struct {
XMLName xml.Name `xml:"me:sig"`
Sig string `xml:",chardata"`
KeyId string `xml:"key_id,attr,omitempty"`
}
}
// NOTE I had huge problems with marshal
// and unmarshal with the same structs
// apperently namespaces in tags are a problem
// for the go xml implementation
type DiasporaUnmarshal struct {
XMLName xml.Name `xml:"diaspora"`
Xmlns string `xml:"xmlns,attr"`
XmlnsMe string `xml:"me,attr"`
Header struct {
XMLName xml.Name `xml:"header"`
AuthorId string `xml:"author_id"`
}
EncryptedHeader string `xml:"encrypted_header,omitempty"`
DecryptedHeader *XmlDecryptedHeader `xml:",omitempty"`
Env struct {
XMLName xml.Name `xml:"env"`
Me string `xml:"me,attr"`
Data struct {
XMLName xml.Name `xml:"data"`
Type string `xml:"type,attr"`
Data string `xml:",chardata"`
}
Encoding string `xml:"encoding"`
Alg string `xml:"alg"`
Sig struct {
XMLName xml.Name `xml:"sig"`
Sig string `xml:",chardata"`
KeyId string `xml:"key_id,attr,omitempty"`
}
}
}
type Entity struct {
XMLName xml.Name `xml:"XML"`
Post EntityPost `xml:"post"`
}
type EntityPost struct {
XMLName xml.Name `xml:"post,omitempty"`
Request *EntityRequest `xml:"request,omitempty"`
Retraction *EntityRetraction `xml:"retraction,omitempty"`
Profile *EntityProfile `xml:"profile,omitempty"`
StatusMessage *EntityStatusMessage `xml:"status_message,omitempty"`
Comment *EntityComment `xml:"comment,omitempty"`
Like *EntityLike `xml:"like,omitempty"`
SignedRetraction *EntityRelayableSignedRetraction `xml:"signed_retraction,omitempty"`
RelayableRetraction *EntityRelayableSignedRetraction `xml:"relayable_retraction,omitempty"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import "encoding/xml"
type EntityComment struct {
XMLName xml.Name `xml:"comment"`
DiasporaHandle string `xml:"diaspora_handle"`
Guid string `xml:"guid"`
ParentGuid string `xml:"parent_guid"`
Text string `xml:"text"`
AuthorSignature string `xml:"author_signature"`
ParentAuthorSignature string `xml:"parent_author_signature"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
type EntityLike struct {
Positive bool `xml:"positive"`
Guid string `xml:"guid"`
ParentGuid string `xml:"parent_guid"`
TargetType string `xml:"target_type"`
DiasporaHandle string `xml:"diaspora_handle"`
AuthorSignature string `xml:"author_signature"`
ParentAuthorSignature string `xml:"parent_author_signature"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
import "time"
type EntityStatusMessage struct {
DiasporaHandle string `xml:"diaspora_handle"`
Guid string `xml:"guid"`
CreatedAt time.Time `xml:"created_at"`
ProviderName string `xml:"provider_display_name"`
RawMessage string `xml:"raw_message"`
Photo *EntityPhotos `xml:"photo,omitempty"`
Location *EntityLocation `xml:"location,omitempty"`
Poll *EntityPoll `xml:"poll,omitempty"`
Public bool `xml:"public"`
}
type EntityPhoto struct {
Guid string `xml:"guid"`
Author string `xml:"author"`
Public bool `xml:"public"`
CreatedAt time.Time `xml:"created_at"`
RemotePhotoPath string `xml:"remote_photo_path"`
RemotePhotoName string `xml:"remote_photo_name"`
Text string `xml:"text"`
StatusMessageGuid string `xml:"status_message_guid"`
Height int `xml:"height"`
Width int `xml:"width"`
}
type EntityPhotos []EntityPhoto
type EntityLocation struct {
Address string `xml:"address"`
Lat string `xml:"lat"`
Lng string `xml:"lng"`
}
type EntityPoll struct {
Guid string `xml:"guid"`
Question string `xml:"question"`
PollAnswers []EntityPollAnswer `xml:"poll_answers"`
}
type EntityPollAnswer struct {
Guid string `xml:"guid"`
Answer string `xml:"answer"`
}
type PollParticipation struct {
PollAnswerGuid string `xml:"poll_answer_guid"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
type EntityProfile struct {
DiasporaHandle string `xml:"diaspora_handle"`
FirstName string `xml:"first_name"`
LastName string `xml:"last_name"`
ImageUrl string `xml:"image_url"`
ImageUrlMedium string `xml:"image_url_medium"`
ImageUrlSmall string `xml:"image_url_small"`
Birthday string `xml:"birthday"`
Gender string `xml:"gender"`
Bio string `xml:"bio"`
Location string `xml:"location"`
Searchable bool `xml:"searchable"`
Nsfw bool `xml:"nsfw"`
TagString string `xml:"tag_string"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
type EntityRequest struct {
Sender string `xml:"sender_handle"`
Recipient string `xml:"recipient_handle"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//
// You should have received a copy of the GNU General Public License
// along with this program. If not, see <http://www.gnu.org/licenses/>.
//
type EntityRelayableSignedRetraction struct {
TargetGuid string `xml:"target_guid"`
TargetType string `xml:"target_type"`
SenderHandle string `xml:"sender_handle"`
TargetAuthorSignature string `xml:"target_author_signature"`
ParentAuthorSignature string `xml:"parent_author_signature"`
}
type EntityRetraction struct {
DiasporaHandle string `xml:"diaspora_handle"`
PostGuid string `xml:"post_guid"`
Type string `xml:"type"`
}
package federation
//
// GangGo Diaspora Federation Library
// Copyright (C) 2017 Lukas Matt <lukas@zauberstuhl.de>
//
// This program is free software: you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation, either version 3 of the License, or
// any later version.
//
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
// GNU General Public License for more details.
//