tapir/auth_app.go

93 lines
3.0 KiB
Go
Raw Normal View History

2019-05-15 19:45:45 +00:00
package tapir
import (
"crypto/rand"
"crypto/subtle"
"encoding/json"
"git.openprivacy.ca/openprivacy/libricochet-go/identity"
"git.openprivacy.ca/openprivacy/libricochet-go/log"
"git.openprivacy.ca/openprivacy/libricochet-go/utils"
"golang.org/x/crypto/ed25519"
"golang.org/x/crypto/sha3"
"time"
)
type AuthApp struct {
}
type AuthMessage struct {
LongTermPublicKey ed25519.PublicKey
EphemeralPublicKey ed25519.PublicKey
}
func (ea AuthApp) NewInstance() Application {
return new(AuthApp)
}
func (ea AuthApp) Init(connection *Connection) {
longTermPubKey := ed25519.PublicKey(connection.ID.PublicKeyBytes())
epk, esk, _ := ed25519.GenerateKey(rand.Reader)
ephemeralPublicKey := ed25519.PublicKey(epk)
ephemeralPrivateKey := ed25519.PrivateKey(esk)
ephemeralIdentity := identity.InitializeV3("", &ephemeralPrivateKey, &ephemeralPublicKey)
authMessage := AuthMessage{LongTermPublicKey: longTermPubKey, EphemeralPublicKey: ephemeralPublicKey}
serialized, _ := json.Marshal(authMessage)
connection.Send(serialized)
message := connection.Expect()
var remoteAuthMessage AuthMessage
err := json.Unmarshal(message, &remoteAuthMessage)
if err != nil {
connection.conn.Close()
return
}
// 3DH Handshake
l2e := connection.ID.EDH(remoteAuthMessage.EphemeralPublicKey)
e2l := ephemeralIdentity.EDH(remoteAuthMessage.LongTermPublicKey)
e2e := ephemeralIdentity.EDH(remoteAuthMessage.EphemeralPublicKey)
// We need to define an order for the result concatenation so that both sides derive the same key.
var result [96]byte
if connection.Outbound {
copy(result[0:32], l2e)
copy(result[32:64], e2l)
copy(result[64:96], e2e)
} else {
copy(result[0:32], e2l)
copy(result[32:64], l2e)
copy(result[64:96], e2e)
}
connection.SetEncryptionKey(sha3.Sum256(result[:]))
// Wait to Sync
time.Sleep(time.Second)
// TODO: Replace this with proper transcript
challengeRemote, err := json.Marshal(remoteAuthMessage)
challengeLocal, err := json.Marshal(authMessage)
challenge := sha3.New512()
if connection.Outbound {
challenge.Write(challengeLocal)
challenge.Write(challengeRemote)
} else {
challenge.Write(challengeRemote)
challenge.Write(challengeLocal)
}
// Since we have set the encryption key on the connection the connection will encrypt any messages we send with that key
// To test that the remote peer has done the same we calculate a challenge hash based on the transcript so far and send it to them
// We expect the remote to do the same, and compare the two.
// If successful we extend our auth capability to the connection and reassert the hostname.
challengeBytes := challenge.Sum([]byte{})
connection.Send(challengeBytes)
remoteChallenge := connection.Expect()
if subtle.ConstantTimeCompare(challengeBytes, remoteChallenge) == 1 {
connection.SetHostname(utils.GetTorV3Hostname(remoteAuthMessage.LongTermPublicKey))
connection.SetCapability("AUTH")
} else {
log.Errorf("Failed Decrypt Challenge: [%x] [%x]\n", remoteChallenge, challengeBytes)
}
}