magic.go 3.85 KB
Newer Older
zauberstuhl's avatar
zauberstuhl committed
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22
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/base64"
  "encoding/xml"
23 24 25
  "encoding/json"
  "crypto/rsa"
  "crypto/rand"
zauberstuhl's avatar
zauberstuhl committed
26 27
)

zauberstuhl's avatar
zauberstuhl committed
28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44
type MagicEnvelopeMarshal 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"`
  }
}

45
func MagicEnvelope(privkey, handle string, plainXml []byte) (payload []byte, err error) {
46 47 48 49 50
  logger.Info(
    "MagicEnvelope with", string(plainXml),
    "private key length", len(privkey),
    "for", handle,
  )
zauberstuhl's avatar
zauberstuhl committed
51 52

  data := base64.URLEncoding.EncodeToString(plainXml)
53
  keyId := base64.URLEncoding.EncodeToString([]byte(handle))
zauberstuhl's avatar
zauberstuhl committed
54

zauberstuhl's avatar
zauberstuhl committed
55
  xmlBody := MagicEnvelopeMarshal{}
56
  xmlBody.Data.Type = APPLICATION_XML
zauberstuhl's avatar
zauberstuhl committed
57
  xmlBody.Data.Data = data
58 59 60
  xmlBody.Me = XMLNS_ME
  xmlBody.Encoding = BASE64_URL
  xmlBody.Alg = RSA_SHA256
zauberstuhl's avatar
zauberstuhl committed
61 62 63 64
  xmlBody.Sig.KeyId = keyId

  err = xmlBody.Sign(privkey)
  if err != nil {
65
    logger.Warn(err)
zauberstuhl's avatar
zauberstuhl committed
66 67 68 69 70
    return
  }

  payload, err = xml.Marshal(xmlBody)
  if err != nil {
71
    logger.Warn(err)
zauberstuhl's avatar
zauberstuhl committed
72 73
    return
  }
74 75

  logger.Info("MagicEnvelope payload", string(payload))
zauberstuhl's avatar
zauberstuhl committed
76 77
  return
}
78

79
func EncryptedMagicEnvelope(privkey, pubkey, handle string, serializedXml []byte) (payload []byte, err error) {
80 81 82 83 84 85
  logger.Info("EncryptedMagicEnvelope with", string(serializedXml),
    "private key length", len(privkey),
    "and public key length", len(pubkey),
    "for", handle,
  )

zauberstuhl's avatar
zauberstuhl committed
86 87
  var aesKeySet Aes
  var aesWrapper AesWrapper
88 89 90
  data := base64.URLEncoding.EncodeToString(serializedXml)
  keyId := base64.URLEncoding.EncodeToString([]byte(handle))

zauberstuhl's avatar
zauberstuhl committed
91
  envelope := MagicEnvelopeMarshal{
92 93 94 95
    Me: XMLNS_ME,
    Encoding: BASE64_URL,
    Alg: RSA_SHA256,
  }
zauberstuhl's avatar
zauberstuhl committed
96 97 98
  envelope.Data.Type = APPLICATION_XML
  envelope.Data.Data = data
  envelope.Sig.KeyId = keyId
99

zauberstuhl's avatar
zauberstuhl committed
100
  err = envelope.Sign(privkey)
101
  if err != nil {
102
    logger.Warn(err)
103 104 105 106 107 108
    return
  }

  // Generate a new AES key pair
  err = aesKeySet.Generate()
  if err != nil {
109
    logger.Warn(err)
110 111 112
    return
  }

zauberstuhl's avatar
zauberstuhl committed
113 114
  // payload with aes encryption
  payload, err = xml.Marshal(envelope)
115
  if err != nil {
116
    logger.Warn(err)
117 118 119
    return
  }

120 121 122 123
  logger.Info(
    "EncryptedMagicEnvelope payload with aes encryption",
    string(payload),
  )
zauberstuhl's avatar
zauberstuhl committed
124

125 126
  err = aesKeySet.Encrypt(payload)
  if err != nil {
127
    logger.Warn(err)
128 129 130 131
    return
  }
  aesWrapper.MagicEnvelope = aesKeySet.Data

zauberstuhl's avatar
zauberstuhl committed
132
  // aes with rsa encryption
133 134
  aesKeySetXml, err := json.Marshal(aesKeySet)
  if err != nil {
135
    logger.Warn(err)
136 137 138 139 140
    return
  }

  pubKey, err := ParseRSAPubKey([]byte(pubkey))
  if err != nil {
141
    logger.Warn(err)
142 143 144
    return
  }

145
  logger.Info("AES key-set XML", string(aesKeySetXml))
146 147 148

  aesKey, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, aesKeySetXml)
  if err != nil {
149
    logger.Warn(err)
150 151 152 153 154 155
    return
  }
  aesWrapper.AesKey = base64.StdEncoding.EncodeToString(aesKey)

  payload, err = json.Marshal(aesWrapper)
  if err != nil {
156
    logger.Warn(err)
157 158 159
    return
  }

160
  logger.Info("EncryptedMagicEnvelope payload", string(payload))
161 162
  return
}