forked from openprivacy/libricochet-go
First cut of minimizing private_key exposure in the code base
Minor formatting
This commit is contained in:
parent
e1031861a2
commit
43b357fdb6
|
@ -128,6 +128,7 @@ func (crc *ContactRequestChannel) OpenOutboundResult(err error, crm *Protocol_Da
|
||||||
crc.channel.SendMessage([]byte{})
|
crc.channel.SendMessage([]byte{})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// SendResponse sends a contact request status response to the requester.
|
||||||
func (crc *ContactRequestChannel) SendResponse(status string) {
|
func (crc *ContactRequestChannel) SendResponse(status string) {
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
crc.channel.SendMessage(messageBuilder.ReplyToContactRequest(crc.channel.ID, status))
|
crc.channel.SendMessage(messageBuilder.ReplyToContactRequest(crc.channel.ID, status))
|
||||||
|
|
|
@ -8,6 +8,7 @@ import (
|
||||||
"crypto/sha256"
|
"crypto/sha256"
|
||||||
"encoding/asn1"
|
"encoding/asn1"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/s-rah/go-ricochet/identity"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"github.com/s-rah/go-ricochet/wire/auth"
|
"github.com/s-rah/go-ricochet/wire/auth"
|
||||||
"github.com/s-rah/go-ricochet/wire/control"
|
"github.com/s-rah/go-ricochet/wire/control"
|
||||||
|
@ -15,14 +16,14 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
const (
|
const (
|
||||||
|
// InvalidClientCookieError - returned when the client provides a cookie with the wrong length
|
||||||
InvalidClientCookieError = utils.Error("InvalidClientCookieError")
|
InvalidClientCookieError = utils.Error("InvalidClientCookieError")
|
||||||
)
|
)
|
||||||
|
|
||||||
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
|
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
|
||||||
type HiddenServiceAuthChannel struct {
|
type HiddenServiceAuthChannel struct {
|
||||||
// PrivateKey must be set for client-side authentication channels
|
// PrivateKey must be set for client-side authentication channels
|
||||||
PrivateKey *rsa.PrivateKey
|
Identity identity.Identity
|
||||||
// Server Hostname must be set for client-side authentication channels
|
|
||||||
ServerHostname string
|
ServerHostname string
|
||||||
|
|
||||||
// Callbacks
|
// Callbacks
|
||||||
|
@ -71,7 +72,7 @@ func (ah *HiddenServiceAuthChannel) Closed(err error) {
|
||||||
// Remote -> [Open Authentication Channel] -> Local
|
// Remote -> [Open Authentication Channel] -> Local
|
||||||
func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) {
|
func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_Data_Control.OpenChannel) ([]byte, error) {
|
||||||
|
|
||||||
if ah.PrivateKey == nil {
|
if !ah.Identity.Initialized() {
|
||||||
return nil, utils.PrivateKeyNotSetError
|
return nil, utils.PrivateKeyNotSetError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -93,7 +94,7 @@ func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_D
|
||||||
// Local -> [Open Authentication Channel] -> Remote
|
// Local -> [Open Authentication Channel] -> Remote
|
||||||
func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) {
|
func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) {
|
||||||
|
|
||||||
if ah.PrivateKey == nil {
|
if !ah.Identity.Initialized() {
|
||||||
return nil, utils.PrivateKeyNotSetError
|
return nil, utils.PrivateKeyNotSetError
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -122,15 +123,9 @@ func (ah *HiddenServiceAuthChannel) OpenOutboundResult(err error, crm *Protocol_
|
||||||
|
|
||||||
ah.AddServerCookie(serverCookie.([]byte)[:])
|
ah.AddServerCookie(serverCookie.([]byte)[:])
|
||||||
|
|
||||||
publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
challenge := ah.GenChallenge(ah.Identity.Hostname(), ah.ServerHostname)
|
||||||
N: ah.PrivateKey.PublicKey.N,
|
|
||||||
E: ah.PrivateKey.PublicKey.E,
|
|
||||||
})
|
|
||||||
|
|
||||||
clientHostname := utils.GetTorHostname(publicKeyBytes)
|
signature, err := ah.Identity.Sign(challenge)
|
||||||
challenge := ah.GenChallenge(clientHostname, ah.ServerHostname)
|
|
||||||
|
|
||||||
signature, err := rsa.SignPKCS1v15(nil, ah.PrivateKey, crypto.SHA256, challenge)
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ah.channel.SendMessage([]byte{})
|
ah.channel.SendMessage([]byte{})
|
||||||
|
@ -138,14 +133,14 @@ func (ah *HiddenServiceAuthChannel) OpenOutboundResult(err error, crm *Protocol_
|
||||||
}
|
}
|
||||||
|
|
||||||
messageBuilder := new(utils.MessageBuilder)
|
messageBuilder := new(utils.MessageBuilder)
|
||||||
proof := messageBuilder.Proof(publicKeyBytes, signature)
|
proof := messageBuilder.Proof(ah.Identity.PublicKeyBytes(), signature)
|
||||||
ah.channel.SendMessage(proof)
|
ah.channel.SendMessage(proof)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// Packet is called for each raw packet received on this channel.
|
// Packet is called for each raw packet received on this channel.
|
||||||
// Input: Remote -> [Proof] -> Client
|
// Input: Client -> [Proof] -> Remote
|
||||||
// OR
|
// OR
|
||||||
// Input: Remote -> [Result] -> Client
|
// Input: Remote -> [Result] -> Client
|
||||||
func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
|
func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
|
||||||
|
@ -160,18 +155,13 @@ func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
|
||||||
if res.GetProof() != nil && ah.channel.Direction == Inbound {
|
if res.GetProof() != nil && ah.channel.Direction == Inbound {
|
||||||
provisionalClientHostname := utils.GetTorHostname(res.GetProof().GetPublicKey())
|
provisionalClientHostname := utils.GetTorHostname(res.GetProof().GetPublicKey())
|
||||||
|
|
||||||
publicKeyBytes, err := asn1.Marshal(rsa.PublicKey{
|
|
||||||
N: ah.PrivateKey.PublicKey.N,
|
|
||||||
E: ah.PrivateKey.PublicKey.E,
|
|
||||||
})
|
|
||||||
|
|
||||||
if err != nil {
|
if err != nil {
|
||||||
ah.ServerAuthInvalid(err)
|
ah.ServerAuthInvalid(err)
|
||||||
ah.channel.SendMessage([]byte{})
|
ah.channel.SendMessage([]byte{})
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
|
|
||||||
serverHostname := utils.GetTorHostname(publicKeyBytes)
|
serverHostname := ah.Identity.Hostname()
|
||||||
|
|
||||||
publicKey := rsa.PublicKey{}
|
publicKey := rsa.PublicKey{}
|
||||||
_, err = asn1.Unmarshal(res.GetProof().GetPublicKey(), &publicKey)
|
_, err = asn1.Unmarshal(res.GetProof().GetPublicKey(), &publicKey)
|
||||||
|
|
|
@ -4,6 +4,7 @@ import (
|
||||||
"bytes"
|
"bytes"
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"github.com/golang/protobuf/proto"
|
"github.com/golang/protobuf/proto"
|
||||||
|
"github.com/s-rah/go-ricochet/identity"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"github.com/s-rah/go-ricochet/wire/control"
|
"github.com/s-rah/go-ricochet/wire/control"
|
||||||
"testing"
|
"testing"
|
||||||
|
@ -71,10 +72,10 @@ func GetOpenAuthenticationChannelMessage() *Protocol_Data_Control.OpenChannel {
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthenticationOpenInbound(t *testing.T) {
|
func TestAuthenticationOpenInbound(t *testing.T) {
|
||||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
id := identity.Init("../testing/private_key")
|
||||||
opm := GetOpenAuthenticationChannelMessage()
|
opm := GetOpenAuthenticationChannelMessage()
|
||||||
authHandler := new(HiddenServiceAuthChannel)
|
authHandler := new(HiddenServiceAuthChannel)
|
||||||
authHandler.PrivateKey = privateKey
|
authHandler.Identity = id
|
||||||
channel := Channel{ID: 1}
|
channel := Channel{ID: 1}
|
||||||
response, err := authHandler.OpenInbound(&channel, opm)
|
response, err := authHandler.OpenInbound(&channel, opm)
|
||||||
|
|
||||||
|
@ -86,14 +87,15 @@ func TestAuthenticationOpenInbound(t *testing.T) {
|
||||||
t.Errorf("Response not a Open Channel Result %v", res)
|
t.Errorf("Response not a Open Channel Result %v", res)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("HiddenServiceAuthChannel OpenOutbound Failed: %v", err)
|
t.Errorf("HiddenServiceAuthChannel OpenInbound Failed: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthenticationOpenOutbound(t *testing.T) {
|
func TestAuthenticationOpenOutbound(t *testing.T) {
|
||||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
id := identity.Init("../testing/private_key")
|
||||||
authHandler := new(HiddenServiceAuthChannel)
|
authHandler := new(HiddenServiceAuthChannel)
|
||||||
authHandler.PrivateKey = privateKey
|
authHandler.Identity = id
|
||||||
|
authHandler.ServerHostname = "kwke2hntvyfqm7dr"
|
||||||
channel := Channel{ID: 1}
|
channel := Channel{ID: 1}
|
||||||
response, err := authHandler.OpenOutbound(&channel)
|
response, err := authHandler.OpenOutbound(&channel)
|
||||||
|
|
||||||
|
@ -105,20 +107,20 @@ func TestAuthenticationOpenOutbound(t *testing.T) {
|
||||||
t.Errorf("Open Channel Packet not included %v", res)
|
t.Errorf("Open Channel Packet not included %v", res)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
t.Errorf("HiddenServiceAuthChannel OpenInbound Failed: %v", err)
|
t.Errorf("HiddenServiceAuthChannel OpenOutbound Failed: %v", err)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestAuthenticationOpenOutboundResult(t *testing.T) {
|
func TestAuthenticationOpenOutboundResult(t *testing.T) {
|
||||||
|
|
||||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
id := identity.Init("../testing/private_key")
|
||||||
|
|
||||||
authHandlerA := new(HiddenServiceAuthChannel)
|
authHandlerA := new(HiddenServiceAuthChannel)
|
||||||
authHandlerB := new(HiddenServiceAuthChannel)
|
authHandlerB := new(HiddenServiceAuthChannel)
|
||||||
|
|
||||||
|
authHandlerA.Identity = id
|
||||||
authHandlerA.ServerHostname = "kwke2hntvyfqm7dr"
|
authHandlerA.ServerHostname = "kwke2hntvyfqm7dr"
|
||||||
authHandlerA.PrivateKey = privateKey
|
|
||||||
authHandlerA.ClientAuthResult = func(accepted, known bool) {}
|
authHandlerA.ClientAuthResult = func(accepted, known bool) {}
|
||||||
channelA := Channel{ID: 1, Direction: Outbound}
|
channelA := Channel{ID: 1, Direction: Outbound}
|
||||||
channelA.SendMessage = func(message []byte) {
|
channelA.SendMessage = func(message []byte) {
|
||||||
|
@ -130,8 +132,7 @@ func TestAuthenticationOpenOutboundResult(t *testing.T) {
|
||||||
res := new(Protocol_Data_Control.Packet)
|
res := new(Protocol_Data_Control.Packet)
|
||||||
proto.Unmarshal(response[:], res)
|
proto.Unmarshal(response[:], res)
|
||||||
|
|
||||||
authHandlerB.ServerHostname = "kwke2hntvyfqm7dr"
|
authHandlerB.Identity = id
|
||||||
authHandlerB.PrivateKey = privateKey
|
|
||||||
authHandlerB.ServerAuthValid = func(hostname string, publicKey rsa.PublicKey) (allowed, known bool) { return true, true }
|
authHandlerB.ServerAuthValid = func(hostname string, publicKey rsa.PublicKey) (allowed, known bool) { return true, true }
|
||||||
authHandlerB.ServerAuthInvalid = func(err error) { t.Error("server received invalid auth") }
|
authHandlerB.ServerAuthInvalid = func(err error) { t.Error("server received invalid auth") }
|
||||||
channelB := Channel{ID: 1, Direction: Inbound}
|
channelB := Channel{ID: 1, Direction: Inbound}
|
||||||
|
|
|
@ -84,6 +84,9 @@ func NewOutboundConnection(conn io.ReadWriteCloser, remoteHostname string) *Conn
|
||||||
return rc
|
return rc
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// TraceLog turns on debug logging, you shouldn't need to do this but if for some
|
||||||
|
// reason ricochet isn't working, you can use this to see at what point in the
|
||||||
|
// protcol trace ricochet is failing.
|
||||||
func (rc *Connection) TraceLog(enabled bool) {
|
func (rc *Connection) TraceLog(enabled bool) {
|
||||||
rc.trace = enabled
|
rc.trace = enabled
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package connection
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
|
"github.com/s-rah/go-ricochet/identity"
|
||||||
"github.com/s-rah/go-ricochet/policies"
|
"github.com/s-rah/go-ricochet/policies"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -63,7 +64,7 @@ func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.Private
|
||||||
ach.RegisterChannelHandler("im.ricochet.auth.hidden-service",
|
ach.RegisterChannelHandler("im.ricochet.auth.hidden-service",
|
||||||
func() channels.Handler {
|
func() channels.Handler {
|
||||||
return &channels.HiddenServiceAuthChannel{
|
return &channels.HiddenServiceAuthChannel{
|
||||||
PrivateKey: privateKey,
|
Identity: identity.Initialize("", privateKey),
|
||||||
ServerAuthValid: onAuthValid,
|
ServerAuthValid: onAuthValid,
|
||||||
ServerAuthInvalid: onAuthInvalid,
|
ServerAuthInvalid: onAuthInvalid,
|
||||||
}
|
}
|
||||||
|
|
|
@ -3,6 +3,7 @@ package connection
|
||||||
import (
|
import (
|
||||||
"crypto/rsa"
|
"crypto/rsa"
|
||||||
"github.com/s-rah/go-ricochet/channels"
|
"github.com/s-rah/go-ricochet/channels"
|
||||||
|
"github.com/s-rah/go-ricochet/identity"
|
||||||
"github.com/s-rah/go-ricochet/policies"
|
"github.com/s-rah/go-ricochet/policies"
|
||||||
"github.com/s-rah/go-ricochet/utils"
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
"sync"
|
"sync"
|
||||||
|
@ -68,7 +69,7 @@ func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.Privat
|
||||||
err := och.connection.Do(func() error {
|
err := och.connection.Do(func() error {
|
||||||
_, err := och.connection.RequestOpenChannel("im.ricochet.auth.hidden-service",
|
_, err := och.connection.RequestOpenChannel("im.ricochet.auth.hidden-service",
|
||||||
&channels.HiddenServiceAuthChannel{
|
&channels.HiddenServiceAuthChannel{
|
||||||
PrivateKey: privateKey,
|
Identity: identity.Initialize("", privateKey),
|
||||||
ServerHostname: och.connection.RemoteHostname,
|
ServerHostname: och.connection.RemoteHostname,
|
||||||
ClientAuthResult: authCallback,
|
ClientAuthResult: authCallback,
|
||||||
})
|
})
|
||||||
|
|
|
@ -0,0 +1,62 @@
|
||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto"
|
||||||
|
"crypto/rsa"
|
||||||
|
"encoding/asn1"
|
||||||
|
"github.com/s-rah/go-ricochet/utils"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Identity is an encapsulation of Name, PrivateKey and other features
|
||||||
|
// that make up a Ricochet client.
|
||||||
|
// The purpose of Identity is to prevent other classes directly accessing private key
|
||||||
|
// and to ensure the integrity of security-critical functions.
|
||||||
|
type Identity struct {
|
||||||
|
Name string
|
||||||
|
pk *rsa.PrivateKey
|
||||||
|
}
|
||||||
|
|
||||||
|
// Init loads an identity from a file. Currently file should be a private_key
|
||||||
|
// but this may change in the future. //XXX
|
||||||
|
func Init(filename string) Identity {
|
||||||
|
pk, err := utils.LoadPrivateKeyFromFile(filename)
|
||||||
|
if err == nil {
|
||||||
|
return Identity{"", pk}
|
||||||
|
}
|
||||||
|
return Identity{}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialize is a courtesy function for initializing an Identity in-code.
|
||||||
|
func Initialize(name string, pk *rsa.PrivateKey) Identity {
|
||||||
|
return Identity{name, pk}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Initialized ensures that an Identity has been assigned a private_key and
|
||||||
|
// is ready to perform operations.
|
||||||
|
func (i *Identity) Initialized() bool {
|
||||||
|
if i.pk == nil {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
// PublicKeyBytes returns the public key associated with this Identity in serializable-friendly
|
||||||
|
// format. //TODO Not sure I like this.
|
||||||
|
func (i *Identity) PublicKeyBytes() []byte {
|
||||||
|
publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
||||||
|
N: i.pk.PublicKey.N,
|
||||||
|
E: i.pk.PublicKey.E,
|
||||||
|
})
|
||||||
|
|
||||||
|
return publicKeyBytes
|
||||||
|
}
|
||||||
|
|
||||||
|
// Hostname provides the onion address associated with this Identity.
|
||||||
|
func (i *Identity) Hostname() string {
|
||||||
|
return utils.GetTorHostname(i.PublicKeyBytes())
|
||||||
|
}
|
||||||
|
|
||||||
|
// Sign produces a cryptographic signature using this Identities private key.
|
||||||
|
func (i *Identity) Sign(challenge []byte) ([]byte, error) {
|
||||||
|
return rsa.SignPKCS1v15(nil, i.pk, crypto.SHA256, challenge)
|
||||||
|
}
|
|
@ -0,0 +1,13 @@
|
||||||
|
package identity
|
||||||
|
|
||||||
|
import (
|
||||||
|
"github.com/s-rah/go-ricochet/identity"
|
||||||
|
"testing"
|
||||||
|
)
|
||||||
|
|
||||||
|
func TestIdentity(t *testing.T) {
|
||||||
|
id := identity.Init("../testing/private_key")
|
||||||
|
if id.Hostname() != "kwke2hntvyfqm7dr" {
|
||||||
|
t.Errorf("Expected %v as Hostname() got: ", "kwke2hntvyfqm7dr", id.Hostname())
|
||||||
|
}
|
||||||
|
}
|
|
@ -28,7 +28,7 @@ func Open(remoteHostname string) (*connection.Connection, error) {
|
||||||
return rc, nil
|
return rc, nil
|
||||||
}
|
}
|
||||||
|
|
||||||
// negotiate version takes an open network connection and executes
|
// NegotiateVersionOutbound takes an open network connection and executes
|
||||||
// the ricochet version negotiation procedure.
|
// the ricochet version negotiation procedure.
|
||||||
func NegotiateVersionOutbound(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
|
func NegotiateVersionOutbound(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
|
||||||
versions := []byte{0x49, 0x4D, 0x01, 0x01}
|
versions := []byte{0x49, 0x4D, 0x01, 0x01}
|
||||||
|
|
|
@ -5,15 +5,15 @@ import (
|
||||||
)
|
)
|
||||||
|
|
||||||
func TestGeneratePrivateKey(t *testing.T) {
|
func TestGeneratePrivateKey(t *testing.T) {
|
||||||
_, err := GeneratePrivateKey()
|
_, err := GeneratePrivateKey()
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while generating private key: %v", err)
|
t.Errorf("Error while generating private key: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func TestLoadPrivateKey(t *testing.T) {
|
func TestLoadPrivateKey(t *testing.T) {
|
||||||
_,err := LoadPrivateKeyFromFile("../testing/private_key")
|
_, err := LoadPrivateKeyFromFile("../testing/private_key")
|
||||||
if err != nil {
|
if err != nil {
|
||||||
t.Errorf("Error while loading private key from file: %v", err)
|
t.Errorf("Error while loading private key from file: %v", err)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue