211 lines
7.7 KiB
Go
211 lines
7.7 KiB
Go
|
package goricochet
|
||
|
|
||
|
import (
|
||
|
"crypto/rsa"
|
||
|
"crypto/x509"
|
||
|
"encoding/asn1"
|
||
|
"encoding/pem"
|
||
|
"errors"
|
||
|
"github.com/s-rah/go-ricochet/utils"
|
||
|
"io/ioutil"
|
||
|
"log"
|
||
|
"net"
|
||
|
"strconv"
|
||
|
)
|
||
|
|
||
|
// StandardRicochetService implements all the necessary flows to implement a
|
||
|
// minimal, protocol compliant Ricochet Service. It can be built on by other
|
||
|
// applications to produce automated riochet applications, and is a useful
|
||
|
// example for other implementations.
|
||
|
type StandardRicochetService struct {
|
||
|
PrivateKey *rsa.PrivateKey
|
||
|
serverHostname string
|
||
|
}
|
||
|
|
||
|
// StandardRicochetConnection implements the ConnectionHandler interface
|
||
|
// to handle events on connections. An instance of StandardRicochetConnection
|
||
|
// is created for each OpenConnection by the HandleConnection method.
|
||
|
type StandardRicochetConnection struct {
|
||
|
Conn *OpenConnection
|
||
|
PrivateKey *rsa.PrivateKey
|
||
|
}
|
||
|
|
||
|
// Init initializes a StandardRicochetService with the cryptographic key given
|
||
|
// by filename.
|
||
|
func (srs *StandardRicochetService) Init(filename string) error {
|
||
|
pemData, err := ioutil.ReadFile(filename)
|
||
|
|
||
|
if err != nil {
|
||
|
return errors.New("Could not setup ricochet service: could not read private key")
|
||
|
}
|
||
|
|
||
|
block, _ := pem.Decode(pemData)
|
||
|
if block == nil || block.Type != "RSA PRIVATE KEY" {
|
||
|
return errors.New("Could not setup ricochet service: no valid PEM data found")
|
||
|
}
|
||
|
|
||
|
srs.PrivateKey, err = x509.ParsePKCS1PrivateKey(block.Bytes)
|
||
|
if err != nil {
|
||
|
return errors.New("Could not setup ricochet service: could not parse private key")
|
||
|
}
|
||
|
|
||
|
publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
||
|
N: srs.PrivateKey.PublicKey.N,
|
||
|
E: srs.PrivateKey.PublicKey.E,
|
||
|
})
|
||
|
|
||
|
srs.serverHostname = utils.GetTorHostname(publicKeyBytes)
|
||
|
log.Printf("Initialised ricochet service for %s", srs.serverHostname)
|
||
|
|
||
|
return nil
|
||
|
}
|
||
|
|
||
|
// Listen starts listening for service connections on localhost `port`.
|
||
|
func (srs *StandardRicochetService) Listen(handler ServiceHandler, port int) {
|
||
|
ln, err := net.Listen("tcp", "127.0.0.1:"+strconv.Itoa(port))
|
||
|
if err != nil {
|
||
|
log.Printf("Cannot Listen on Port %v", port)
|
||
|
return
|
||
|
}
|
||
|
|
||
|
Serve(ln, handler)
|
||
|
}
|
||
|
|
||
|
// Connect initiates a new client connection to `hostname`, which must be in one
|
||
|
// of the forms accepted by the goricochet.Connect() method.
|
||
|
func (srs *StandardRicochetService) Connect(hostname string) (*OpenConnection, error) {
|
||
|
log.Printf("Connecting to...%s", hostname)
|
||
|
oc, err := Connect(hostname)
|
||
|
if err != nil {
|
||
|
return nil, errors.New("Could not connect to: " + hostname + " " + err.Error())
|
||
|
}
|
||
|
oc.MyHostname = srs.serverHostname
|
||
|
return oc, nil
|
||
|
}
|
||
|
|
||
|
// OnNewConnection is called for new inbound connections to our service. This
|
||
|
// method implements the ServiceHandler interface.
|
||
|
func (srs *StandardRicochetService) OnNewConnection(oc *OpenConnection) {
|
||
|
oc.MyHostname = srs.serverHostname
|
||
|
}
|
||
|
|
||
|
// OnFailedConnection is called for inbound connections that fail to successfully
|
||
|
// complete version negotiation for any reason. This method implements the
|
||
|
// ServiceHandler interface.
|
||
|
func (srs *StandardRicochetService) OnFailedConnection(err error) {
|
||
|
log.Printf("Inbound connection failed: %s", err)
|
||
|
}
|
||
|
|
||
|
// ------
|
||
|
|
||
|
// OnReady is called when a client or server sucessfully passes Version Negotiation.
|
||
|
func (src *StandardRicochetConnection) OnReady(oc *OpenConnection) {
|
||
|
src.Conn = oc
|
||
|
if oc.Client {
|
||
|
log.Printf("Successfully connected to %s", oc.OtherHostname)
|
||
|
oc.IsAuthed = true // Connections to Servers are Considered Authenticated by Default
|
||
|
oc.Authenticate(1)
|
||
|
} else {
|
||
|
log.Printf("Inbound connection received")
|
||
|
}
|
||
|
}
|
||
|
|
||
|
// OnDisconnect is called when a connection is closed
|
||
|
func (src *StandardRicochetConnection) OnDisconnect() {
|
||
|
log.Printf("Disconnected from %s", src.Conn.OtherHostname)
|
||
|
}
|
||
|
|
||
|
// OnAuthenticationRequest is called when a client requests Authentication
|
||
|
func (src *StandardRicochetConnection) OnAuthenticationRequest(channelID int32, clientCookie [16]byte) {
|
||
|
src.Conn.ConfirmAuthChannel(channelID, clientCookie)
|
||
|
}
|
||
|
|
||
|
// OnAuthenticationChallenge constructs a valid authentication challenge to the serverCookie
|
||
|
func (src *StandardRicochetConnection) OnAuthenticationChallenge(channelID int32, serverCookie [16]byte) {
|
||
|
// DER Encode the Public Key
|
||
|
publickeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
||
|
N: src.PrivateKey.PublicKey.N,
|
||
|
E: src.PrivateKey.PublicKey.E,
|
||
|
})
|
||
|
src.Conn.SendProof(1, serverCookie, publickeyBytes, src.PrivateKey)
|
||
|
}
|
||
|
|
||
|
// OnAuthenticationProof is called when a client sends Proof for an existing authentication challenge
|
||
|
func (src *StandardRicochetConnection) OnAuthenticationProof(channelID int32, publicKey []byte, signature []byte) {
|
||
|
result := src.Conn.ValidateProof(channelID, publicKey, signature)
|
||
|
// This implementation always sends 'true', indicating that the contact is known
|
||
|
src.Conn.SendAuthenticationResult(channelID, result, true)
|
||
|
src.Conn.IsAuthed = result
|
||
|
src.Conn.CloseChannel(channelID)
|
||
|
}
|
||
|
|
||
|
// OnAuthenticationResult is called once a server has returned the result of the Proof Verification
|
||
|
func (src *StandardRicochetConnection) OnAuthenticationResult(channelID int32, result bool, isKnownContact bool) {
|
||
|
src.Conn.IsAuthed = result
|
||
|
}
|
||
|
|
||
|
// IsKnownContact allows a caller to determine if a hostname an authorized contact.
|
||
|
func (src *StandardRicochetConnection) IsKnownContact(hostname string) bool {
|
||
|
return false
|
||
|
}
|
||
|
|
||
|
// OnContactRequest is called when a client sends a new contact request
|
||
|
func (src *StandardRicochetConnection) OnContactRequest(channelID int32, nick string, message string) {
|
||
|
}
|
||
|
|
||
|
// OnContactRequestAck is called when a server sends a reply to an existing contact request
|
||
|
func (src *StandardRicochetConnection) OnContactRequestAck(channelID int32, status string) {
|
||
|
}
|
||
|
|
||
|
// OnOpenChannelRequest is called when a client or server requests to open a new channel
|
||
|
func (src *StandardRicochetConnection) OnOpenChannelRequest(channelID int32, channelType string) {
|
||
|
src.Conn.AckOpenChannel(channelID, channelType)
|
||
|
}
|
||
|
|
||
|
// OnOpenChannelRequestSuccess is called when a client or server responds to an open channel request
|
||
|
func (src *StandardRicochetConnection) OnOpenChannelRequestSuccess(channelID int32) {
|
||
|
}
|
||
|
|
||
|
// OnChannelClosed is called when a client or server closes an existing channel
|
||
|
func (src *StandardRicochetConnection) OnChannelClosed(channelID int32) {
|
||
|
}
|
||
|
|
||
|
// OnChatMessage is called when a new chat message is received.
|
||
|
func (src *StandardRicochetConnection) OnChatMessage(channelID int32, messageID int32, message string) {
|
||
|
src.Conn.AckChatMessage(channelID, messageID)
|
||
|
}
|
||
|
|
||
|
// OnChatMessageAck is called when a new chat message is ascknowledged.
|
||
|
func (src *StandardRicochetConnection) OnChatMessageAck(channelID int32, messageID int32) {
|
||
|
}
|
||
|
|
||
|
// OnFailedChannelOpen is called when a server fails to open a channel
|
||
|
func (src *StandardRicochetConnection) OnFailedChannelOpen(channelID int32, errorType string) {
|
||
|
src.Conn.UnsetChannel(channelID)
|
||
|
}
|
||
|
|
||
|
// OnGenericError is called when a generalized error is returned from the peer
|
||
|
func (src *StandardRicochetConnection) OnGenericError(channelID int32) {
|
||
|
src.Conn.RejectOpenChannel(channelID, "GenericError")
|
||
|
}
|
||
|
|
||
|
//OnUnknownTypeError is called when an unknown type error is returned from the peer
|
||
|
func (src *StandardRicochetConnection) OnUnknownTypeError(channelID int32) {
|
||
|
src.Conn.RejectOpenChannel(channelID, "UnknownTypeError")
|
||
|
}
|
||
|
|
||
|
// OnUnauthorizedError is called when an unathorized error is returned from the peer
|
||
|
func (src *StandardRicochetConnection) OnUnauthorizedError(channelID int32) {
|
||
|
src.Conn.RejectOpenChannel(channelID, "UnauthorizedError")
|
||
|
}
|
||
|
|
||
|
// OnBadUsageError is called when a bad usage error is returned from the peer
|
||
|
func (src *StandardRicochetConnection) OnBadUsageError(channelID int32) {
|
||
|
src.Conn.RejectOpenChannel(channelID, "BadUsageError")
|
||
|
}
|
||
|
|
||
|
// OnFailedError is called when a failed error is returned from the peer
|
||
|
func (src *StandardRicochetConnection) OnFailedError(channelID int32) {
|
||
|
src.Conn.RejectOpenChannel(channelID, "FailedError")
|
||
|
}
|