Initial Commit
This commit provides connection, authentication and the start of contact requests.
This commit is contained in:
commit
dba6ae8097
|
@ -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`
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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)
|
||||
}
|
|
@ -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])
|
||||
}
|
Loading…
Reference in New Issue