Initial Commit

This commit provides connection, authentication and the
start of contact requests.
This commit is contained in:
Sarah Jamie Lewis 2015-10-10 21:33:07 -07:00
commit dba6ae8097
5 changed files with 869 additions and 0 deletions

17
README.md Normal file
View File

@ -0,0 +1,17 @@
# GoRicochet
GoRicochet is an implementation of the [Ricochet Protocol](https://ricochet.im)
in Go.
**NOTE:** This project is in the very early stages and is in no way meant to be
a replacement for the core Ricochet implementation. This version exists for
the purpose of writing testing tools for Ricochet in Go.
## Current Features
* Connect to a Local Ricochet Client
* Issue an Authentication Request
* Issue a Contact Request
If you have questions or want to contribute please contact Sarah @
`ricochet:qn6uo4cmsrfv4kzq`

119
auth/auth_message.go Normal file
View File

@ -0,0 +1,119 @@
// Code generated by protoc-gen-go.
// source: AuthHiddenService.proto
// DO NOT EDIT!
/*
Package Protocol_Data_AuthHiddenService is a generated protocol buffer package.
It is generated from these files:
AuthHiddenService.proto
It has these top-level messages:
Packet
Proof
Result
*/
package Protocol_Data_AuthHiddenService
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import Protocol_Data_Control "github.com/s-rah/go-ricochet/control"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type Packet struct {
Proof *Proof `protobuf:"bytes,1,opt,name=proof" json:"proof,omitempty"`
Result *Result `protobuf:"bytes,2,opt,name=result" json:"result,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Packet) Reset() { *m = Packet{} }
func (m *Packet) String() string { return proto.CompactTextString(m) }
func (*Packet) ProtoMessage() {}
func (m *Packet) GetProof() *Proof {
if m != nil {
return m.Proof
}
return nil
}
func (m *Packet) GetResult() *Result {
if m != nil {
return m.Result
}
return nil
}
type Proof struct {
PublicKey []byte `protobuf:"bytes,1,opt,name=public_key" json:"public_key,omitempty"`
Signature []byte `protobuf:"bytes,2,opt,name=signature" json:"signature,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Proof) Reset() { *m = Proof{} }
func (m *Proof) String() string { return proto.CompactTextString(m) }
func (*Proof) ProtoMessage() {}
func (m *Proof) GetPublicKey() []byte {
if m != nil {
return m.PublicKey
}
return nil
}
func (m *Proof) GetSignature() []byte {
if m != nil {
return m.Signature
}
return nil
}
type Result struct {
Accepted *bool `protobuf:"varint,1,req,name=accepted" json:"accepted,omitempty"`
IsKnownContact *bool `protobuf:"varint,2,opt,name=is_known_contact" json:"is_known_contact,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Result) Reset() { *m = Result{} }
func (m *Result) String() string { return proto.CompactTextString(m) }
func (*Result) ProtoMessage() {}
func (m *Result) GetAccepted() bool {
if m != nil && m.Accepted != nil {
return *m.Accepted
}
return false
}
func (m *Result) GetIsKnownContact() bool {
if m != nil && m.IsKnownContact != nil {
return *m.IsKnownContact
}
return false
}
var E_ClientCookie = &proto.ExtensionDesc{
ExtendedType: (*Protocol_Data_Control.OpenChannel)(nil),
ExtensionType: ([]byte)(nil),
Field: 7200,
Name: "Protocol.Data.AuthHiddenService.client_cookie",
Tag: "bytes,7200,opt,name=client_cookie",
}
var E_ServerCookie = &proto.ExtensionDesc{
ExtendedType: (*Protocol_Data_Control.ChannelResult)(nil),
ExtensionType: ([]byte)(nil),
Field: 7200,
Name: "Protocol.Data.AuthHiddenService.server_cookie",
Tag: "bytes,7200,opt,name=server_cookie",
}
func init() {
proto.RegisterExtension(E_ClientCookie)
proto.RegisterExtension(E_ServerCookie)
}

165
contact/request.go Normal file
View File

@ -0,0 +1,165 @@
// Code generated by protoc-gen-go.
// source: ContactRequestChannel.proto
// DO NOT EDIT!
/*
Package Protocol_Data_ContactRequest is a generated protocol buffer package.
It is generated from these files:
ContactRequestChannel.proto
It has these top-level messages:
ContactRequest
Response
*/
package Protocol_Data_ContactRequest
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
import Protocol_Data_Control "github.com/s-rah/go-ricochet/control"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type Limits int32
const (
Limits_MessageMaxCharacters Limits = 2000
Limits_NicknameMaxCharacters Limits = 30
)
var Limits_name = map[int32]string{
2000: "MessageMaxCharacters",
30: "NicknameMaxCharacters",
}
var Limits_value = map[string]int32{
"MessageMaxCharacters": 2000,
"NicknameMaxCharacters": 30,
}
func (x Limits) Enum() *Limits {
p := new(Limits)
*p = x
return p
}
func (x Limits) String() string {
return proto.EnumName(Limits_name, int32(x))
}
func (x *Limits) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(Limits_value, data, "Limits")
if err != nil {
return err
}
*x = Limits(value)
return nil
}
type Response_Status int32
const (
Response_Undefined Response_Status = 0
Response_Pending Response_Status = 1
Response_Accepted Response_Status = 2
Response_Rejected Response_Status = 3
Response_Error Response_Status = 4
)
var Response_Status_name = map[int32]string{
0: "Undefined",
1: "Pending",
2: "Accepted",
3: "Rejected",
4: "Error",
}
var Response_Status_value = map[string]int32{
"Undefined": 0,
"Pending": 1,
"Accepted": 2,
"Rejected": 3,
"Error": 4,
}
func (x Response_Status) Enum() *Response_Status {
p := new(Response_Status)
*p = x
return p
}
func (x Response_Status) String() string {
return proto.EnumName(Response_Status_name, int32(x))
}
func (x *Response_Status) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(Response_Status_value, data, "Response_Status")
if err != nil {
return err
}
*x = Response_Status(value)
return nil
}
// Sent only as an attachment to OpenChannel
type ContactRequest struct {
Nickname *string `protobuf:"bytes,1,opt,name=nickname" json:"nickname,omitempty"`
MessageText *string `protobuf:"bytes,2,opt,name=message_text" json:"message_text,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ContactRequest) Reset() { *m = ContactRequest{} }
func (m *ContactRequest) String() string { return proto.CompactTextString(m) }
func (*ContactRequest) ProtoMessage() {}
func (m *ContactRequest) GetNickname() string {
if m != nil && m.Nickname != nil {
return *m.Nickname
}
return ""
}
func (m *ContactRequest) GetMessageText() string {
if m != nil && m.MessageText != nil {
return *m.MessageText
}
return ""
}
// Response is the only valid message to send on the channel
type Response struct {
Status *Response_Status `protobuf:"varint,1,req,name=status,enum=Protocol.Data.ContactRequest.Response_Status" json:"status,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Response) Reset() { *m = Response{} }
func (m *Response) String() string { return proto.CompactTextString(m) }
func (*Response) ProtoMessage() {}
func (m *Response) GetStatus() Response_Status {
if m != nil && m.Status != nil {
return *m.Status
}
return Response_Undefined
}
var E_ContactRequest = &proto.ExtensionDesc{
ExtendedType: (*Protocol_Data_Control.OpenChannel)(nil),
ExtensionType: (*ContactRequest)(nil),
Field: 200,
Name: "Protocol.Data.ContactRequest.contact_request",
Tag: "bytes,200,opt,name=contact_request",
}
var E_Response = &proto.ExtensionDesc{
ExtendedType: (*Protocol_Data_Control.ChannelResult)(nil),
ExtensionType: (*Response)(nil),
Field: 201,
Name: "Protocol.Data.ContactRequest.response",
Tag: "bytes,201,opt,name=response",
}
func init() {
proto.RegisterEnum("Protocol.Data.ContactRequest.Limits", Limits_name, Limits_value)
proto.RegisterEnum("Protocol.Data.ContactRequest.Response_Status", Response_Status_name, Response_Status_value)
proto.RegisterExtension(E_ContactRequest)
proto.RegisterExtension(E_Response)
}

287
control/control_message.go Normal file
View File

@ -0,0 +1,287 @@
// Code generated by protoc-gen-go.
// source: ControlChannel.proto
// DO NOT EDIT!
/*
Package Protocol_Data_Control is a generated protocol buffer package.
It is generated from these files:
ControlChannel.proto
It has these top-level messages:
Packet
OpenChannel
ChannelResult
KeepAlive
EnableFeatures
FeaturesEnabled
*/
package Protocol_Data_Control
import proto "github.com/golang/protobuf/proto"
import fmt "fmt"
import math "math"
// Reference imports to suppress errors if they are not otherwise used.
var _ = proto.Marshal
var _ = fmt.Errorf
var _ = math.Inf
type ChannelResult_CommonError int32
const (
ChannelResult_GenericError ChannelResult_CommonError = 0
ChannelResult_UnknownTypeError ChannelResult_CommonError = 1
ChannelResult_UnauthorizedError ChannelResult_CommonError = 2
ChannelResult_BadUsageError ChannelResult_CommonError = 3
ChannelResult_FailedError ChannelResult_CommonError = 4
)
var ChannelResult_CommonError_name = map[int32]string{
0: "GenericError",
1: "UnknownTypeError",
2: "UnauthorizedError",
3: "BadUsageError",
4: "FailedError",
}
var ChannelResult_CommonError_value = map[string]int32{
"GenericError": 0,
"UnknownTypeError": 1,
"UnauthorizedError": 2,
"BadUsageError": 3,
"FailedError": 4,
}
func (x ChannelResult_CommonError) Enum() *ChannelResult_CommonError {
p := new(ChannelResult_CommonError)
*p = x
return p
}
func (x ChannelResult_CommonError) String() string {
return proto.EnumName(ChannelResult_CommonError_name, int32(x))
}
func (x *ChannelResult_CommonError) UnmarshalJSON(data []byte) error {
value, err := proto.UnmarshalJSONEnum(ChannelResult_CommonError_value, data, "ChannelResult_CommonError")
if err != nil {
return err
}
*x = ChannelResult_CommonError(value)
return nil
}
type Packet struct {
// Must contain exactly one field
OpenChannel *OpenChannel `protobuf:"bytes,1,opt,name=open_channel" json:"open_channel,omitempty"`
ChannelResult *ChannelResult `protobuf:"bytes,2,opt,name=channel_result" json:"channel_result,omitempty"`
KeepAlive *KeepAlive `protobuf:"bytes,3,opt,name=keep_alive" json:"keep_alive,omitempty"`
EnableFeatures *EnableFeatures `protobuf:"bytes,4,opt,name=enable_features" json:"enable_features,omitempty"`
FeaturesEnabled *FeaturesEnabled `protobuf:"bytes,5,opt,name=features_enabled" json:"features_enabled,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *Packet) Reset() { *m = Packet{} }
func (m *Packet) String() string { return proto.CompactTextString(m) }
func (*Packet) ProtoMessage() {}
func (m *Packet) GetOpenChannel() *OpenChannel {
if m != nil {
return m.OpenChannel
}
return nil
}
func (m *Packet) GetChannelResult() *ChannelResult {
if m != nil {
return m.ChannelResult
}
return nil
}
func (m *Packet) GetKeepAlive() *KeepAlive {
if m != nil {
return m.KeepAlive
}
return nil
}
func (m *Packet) GetEnableFeatures() *EnableFeatures {
if m != nil {
return m.EnableFeatures
}
return nil
}
func (m *Packet) GetFeaturesEnabled() *FeaturesEnabled {
if m != nil {
return m.FeaturesEnabled
}
return nil
}
type OpenChannel struct {
ChannelIdentifier *int32 `protobuf:"varint,1,req,name=channel_identifier" json:"channel_identifier,omitempty"`
ChannelType *string `protobuf:"bytes,2,req,name=channel_type" json:"channel_type,omitempty"`
XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"`
}
func (m *OpenChannel) Reset() { *m = OpenChannel{} }
func (m *OpenChannel) String() string { return proto.CompactTextString(m) }
func (*OpenChannel) ProtoMessage() {}
var extRange_OpenChannel = []proto.ExtensionRange{
{100, 536870911},
}
func (*OpenChannel) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_OpenChannel
}
func (m *OpenChannel) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *OpenChannel) GetChannelIdentifier() int32 {
if m != nil && m.ChannelIdentifier != nil {
return *m.ChannelIdentifier
}
return 0
}
func (m *OpenChannel) GetChannelType() string {
if m != nil && m.ChannelType != nil {
return *m.ChannelType
}
return ""
}
type ChannelResult struct {
ChannelIdentifier *int32 `protobuf:"varint,1,req,name=channel_identifier" json:"channel_identifier,omitempty"`
Opened *bool `protobuf:"varint,2,req,name=opened" json:"opened,omitempty"`
CommonError *ChannelResult_CommonError `protobuf:"varint,3,opt,name=common_error,enum=Protocol.Data.Control.ChannelResult_CommonError" json:"common_error,omitempty"`
XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"`
}
func (m *ChannelResult) Reset() { *m = ChannelResult{} }
func (m *ChannelResult) String() string { return proto.CompactTextString(m) }
func (*ChannelResult) ProtoMessage() {}
var extRange_ChannelResult = []proto.ExtensionRange{
{100, 536870911},
}
func (*ChannelResult) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_ChannelResult
}
func (m *ChannelResult) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *ChannelResult) GetChannelIdentifier() int32 {
if m != nil && m.ChannelIdentifier != nil {
return *m.ChannelIdentifier
}
return 0
}
func (m *ChannelResult) GetOpened() bool {
if m != nil && m.Opened != nil {
return *m.Opened
}
return false
}
func (m *ChannelResult) GetCommonError() ChannelResult_CommonError {
if m != nil && m.CommonError != nil {
return *m.CommonError
}
return ChannelResult_GenericError
}
type KeepAlive struct {
ResponseRequested *bool `protobuf:"varint,1,req,name=response_requested" json:"response_requested,omitempty"`
XXX_unrecognized []byte `json:"-"`
}
func (m *KeepAlive) Reset() { *m = KeepAlive{} }
func (m *KeepAlive) String() string { return proto.CompactTextString(m) }
func (*KeepAlive) ProtoMessage() {}
func (m *KeepAlive) GetResponseRequested() bool {
if m != nil && m.ResponseRequested != nil {
return *m.ResponseRequested
}
return false
}
type EnableFeatures struct {
Feature []string `protobuf:"bytes,1,rep,name=feature" json:"feature,omitempty"`
XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"`
}
func (m *EnableFeatures) Reset() { *m = EnableFeatures{} }
func (m *EnableFeatures) String() string { return proto.CompactTextString(m) }
func (*EnableFeatures) ProtoMessage() {}
var extRange_EnableFeatures = []proto.ExtensionRange{
{100, 536870911},
}
func (*EnableFeatures) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_EnableFeatures
}
func (m *EnableFeatures) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *EnableFeatures) GetFeature() []string {
if m != nil {
return m.Feature
}
return nil
}
type FeaturesEnabled struct {
Feature []string `protobuf:"bytes,1,rep,name=feature" json:"feature,omitempty"`
XXX_extensions map[int32]proto.Extension `json:"-"`
XXX_unrecognized []byte `json:"-"`
}
func (m *FeaturesEnabled) Reset() { *m = FeaturesEnabled{} }
func (m *FeaturesEnabled) String() string { return proto.CompactTextString(m) }
func (*FeaturesEnabled) ProtoMessage() {}
var extRange_FeaturesEnabled = []proto.ExtensionRange{
{100, 536870911},
}
func (*FeaturesEnabled) ExtensionRangeArray() []proto.ExtensionRange {
return extRange_FeaturesEnabled
}
func (m *FeaturesEnabled) ExtensionMap() map[int32]proto.Extension {
if m.XXX_extensions == nil {
m.XXX_extensions = make(map[int32]proto.Extension)
}
return m.XXX_extensions
}
func (m *FeaturesEnabled) GetFeature() []string {
if m != nil {
return m.Feature
}
return nil
}
func init() {
proto.RegisterEnum("Protocol.Data.Control.ChannelResult_CommonError", ChannelResult_CommonError_name, ChannelResult_CommonError_value)
}

281
ricochet.go Normal file
View File

@ -0,0 +1,281 @@
package goricochet
import (
"crypto"
"crypto/hmac"
"crypto/rsa"
"crypto/sha256"
"crypto/x509"
"encoding/asn1"
"encoding/pem"
"fmt"
"github.com/golang/protobuf/proto"
"github.com/s-rah/go-ricochet/auth"
"github.com/s-rah/go-ricochet/contact"
"github.com/s-rah/go-ricochet/control"
"io/ioutil"
"log"
"net"
"os"
)
// Ricochet is a protocol to conducting anonymous IM.
type Ricochet struct {
conn net.Conn
privateKey *pem.Block
logger *log.Logger
}
// Init sets up the Ricochet object. It takes in a filename of a hidden service
// private_key file so it can successfully authenticate itself with other
// clients.
func (r *Ricochet) Init(filename string) {
r.logger = log.New(os.Stdout, "[Ricochet]: ", log.Ltime|log.Lmicroseconds)
pemData, err := ioutil.ReadFile(filename)
if err != nil {
r.logger.Print("Error Reading Private Key: ", err)
}
block, _ := pem.Decode(pemData)
if block == nil || block.Type != "RSA PRIVATE KEY" {
r.logger.Print("No valid PEM data found")
}
r.privateKey = block
}
func (r *Ricochet) send(data []byte) {
fmt.Fprintf(r.conn, "%s", data)
}
func (r *Ricochet) recv() ([]byte, error) {
buf := make([]byte, 4096)
n, err := r.conn.Read(buf)
r.logger.Print("Received Response From Service: ", n, err)
if err != nil {
return nil, err
}
ret := make([]byte, n)
copy(ret[:], buf[:])
return ret, nil
}
func (r *Ricochet) decodePacket(response []byte) *Protocol_Data_Control.Packet {
// TODO: Check Length and Channel are Sane
if len(response) < 4 {
r.logger.Fatal("Response is too short ", response)
return nil
}
res := new(Protocol_Data_Control.Packet)
err := proto.Unmarshal(response[4:], res)
if err != nil {
r.logger.Fatal("Error Unmarshalling Response", err)
panic(err)
}
return res
}
func (r *Ricochet) decodeResult(response []byte) *Protocol_Data_AuthHiddenService.Packet {
// TODO: Check Length and Channel are Sane
if len(response) < 4 {
r.logger.Fatal("Response is too short ", response)
return nil
}
length := response[1]
r.logger.Print(response)
res := new(Protocol_Data_AuthHiddenService.Packet)
err := proto.Unmarshal(response[4:length], res)
if err != nil {
r.logger.Fatal("Error Unmarshalling Response: ", err)
panic(err)
}
return res
}
func (r *Ricochet) constructProtocol(data []byte, channel byte) []byte {
header := make([]byte, 4+len(data))
r.logger.Print("Wrting Packet of Size: ", len(header))
header[0] = byte(len(header) >> 8)
header[1] = byte(len(header) & 0x00FF)
header[2] = 0x00
header[3] = channel
copy(header[4:], data[:])
return header
}
// Connect sets up a ricochet connection between from and to which are
// both ricochet formated hostnames e.g. qn6uo4cmsrfv4kzq.onion. If this
// function finished successfully then the connection can be assumed to
// be open and authenticated.
func (r *Ricochet) Connect(from string, to string) error {
// TODO: In the future we will want the ability to connect
// through Tor to a hidden service address. This works if
// you uncomment this, but is slower for testing purposes
// dialSocksProxy := socks.DialSocksProxy(socks.SOCKS5, "127.0.0.1:9050")
//return dialSocksProxy("", "qn6uo4cmsrfv4kzq.onion:9878")
// TODO: For now hardcoding port numbers, these change
// on startup so need to be reset every time.
tcpAddr, err := net.ResolveTCPAddr("tcp", "127.0.0.1:49952")
if err != nil {
r.logger.Fatal("Cannot Resolve TCP Address ", err)
return err
}
r.conn, err = net.DialTCP("tcp", nil, tcpAddr)
if err != nil {
r.logger.Fatal("Cannot Dial TCP Address ", err)
return err
}
r.negotiateVersion()
// Construct an Open Channel Message
oc := &Protocol_Data_Control.OpenChannel{
ChannelIdentifier: proto.Int32(1),
ChannelType: proto.String("im.ricochet.auth.hidden-service"),
}
err = proto.SetExtension(oc, Protocol_Data_AuthHiddenService.E_ClientCookie, []byte("0000000000000000"))
pc := &Protocol_Data_Control.Packet{
OpenChannel: oc,
}
data, err := proto.Marshal(pc)
if err != nil {
r.logger.Fatal("Cannot Marshal Open Channel Message: ", err)
panic("Cannot Marshal Open Channel Message")
}
openChannel := r.constructProtocol(data, 0)
r.logger.Print("Opening Channel: ", pc)
r.send(openChannel)
response, _ := r.recv()
openChannelResponse := r.decodePacket(response)
r.logger.Print("Received Response: ", openChannelResponse)
channelResult := openChannelResponse.GetChannelResult()
if channelResult.GetOpened() == true {
r.logger.Print("Channel Opened Successfully: ", channelResult.GetChannelIdentifier())
}
sCookie, _ := proto.GetExtension(channelResult, Protocol_Data_AuthHiddenService.E_ServerCookie)
serverCookie, _ := sCookie.([]byte)
r.logger.Print("Starting Authentication with Server Cookie: ", serverCookie)
key := make([]byte, 32)
copy(key[0:16], []byte("0000000000000000"))
copy(key[16:], serverCookie)
value := []byte(from + to)
r.logger.Print("Got Hmac Key: ", key)
r.logger.Print("Got Proof Value: ", string(value))
mac := hmac.New(sha256.New, key)
mac.Write(value)
hmac := mac.Sum(nil)
r.logger.Print("Got HMAC: ", hmac)
privateKey, err := x509.ParsePKCS1PrivateKey(r.privateKey.Bytes)
if err != nil {
r.logger.Fatalf("Private key can't be decoded: %s", err)
}
// DER Encode the Public Key
publickeybytes, err := asn1.Marshal(rsa.PublicKey{
N: privateKey.PublicKey.N,
E: privateKey.PublicKey.E,
})
signature, _ := rsa.SignPKCS1v15(nil, privateKey, crypto.SHA256, hmac)
signatureBytes := make([]byte, 128)
copy(signatureBytes[:], signature[:])
r.logger.Print("Signature Length: ", len(signatureBytes))
r.logger.Print("Public Key Length: ", len(publickeybytes), ", Bit Size: ", privateKey.PublicKey.N.BitLen())
// Construct a Proof Message
proof := &Protocol_Data_AuthHiddenService.Proof{
PublicKey: publickeybytes,
Signature: signatureBytes,
}
ahsPacket := &Protocol_Data_AuthHiddenService.Packet{
Proof: proof,
Result: nil,
}
data, err = proto.Marshal(ahsPacket)
sendProof := r.constructProtocol(data, 1)
r.logger.Print("Constructed Proof: ", ahsPacket)
r.send(sendProof)
response, _ = r.recv()
resultResponse := r.decodeResult(response)
r.logger.Print("Received Result: ", resultResponse)
return nil
}
// SendContactRequest initiates a contact request to the server.
// Prerequisites:
// * Must have Previously issued a successful Connect()
func (r *Ricochet) SendContactRequest(nick string, message string) {
// Construct a Contact Request Channel
oc := &Protocol_Data_Control.OpenChannel{
ChannelIdentifier: proto.Int32(3),
ChannelType: proto.String("im.ricochet.contact.request"),
}
contactRequest := &Protocol_Data_ContactRequest.ContactRequest{
Nickname: proto.String(nick),
MessageText: proto.String(message),
}
err := proto.SetExtension(oc, Protocol_Data_ContactRequest.E_ContactRequest, contactRequest)
pc := &Protocol_Data_Control.Packet{
OpenChannel: oc,
}
data, err := proto.Marshal(pc)
if err != nil {
r.logger.Fatal("Cannot Marshal Open Channel Message: ", err)
panic("Cannot Marshal Open Channel Message")
}
openChannel := r.constructProtocol(data, 0)
r.logger.Print("Opening Channel: ", pc)
r.send(openChannel)
response, _ := r.recv()
openChannelResponse := r.decodePacket(response)
r.logger.Print("Received Response: ", openChannelResponse)
}
// negotiateVersion Perform version negotiation with the connected host.
func (r *Ricochet) negotiateVersion() {
version := make([]byte, 4)
version[0] = 0x49
version[1] = 0x4D
version[2] = 0x01
version[3] = 0x01
r.send(version)
r.logger.Print("Negotiating Version ", version)
res, err := r.recv()
if len(res) != 1 || err != nil {
r.logger.Fatal("Failed Version Negotiating: ", res, err)
panic("Failed Version Negotiating")
}
if res[0] != 1 {
r.logger.Fatal("Failed Version Negotiating - Invalid Version ", res)
panic("Failed Version Negotiating")
}
r.logger.Print("Successfully Negotiated Version ", res[0])
}