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{})
|
||||
}
|
||||
|
||||
// SendResponse sends a contact request status response to the requester.
|
||||
func (crc *ContactRequestChannel) SendResponse(status string) {
|
||||
messageBuilder := new(utils.MessageBuilder)
|
||||
crc.channel.SendMessage(messageBuilder.ReplyToContactRequest(crc.channel.ID, status))
|
||||
|
|
|
@ -8,6 +8,7 @@ import (
|
|||
"crypto/sha256"
|
||||
"encoding/asn1"
|
||||
"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/wire/auth"
|
||||
"github.com/s-rah/go-ricochet/wire/control"
|
||||
|
@ -15,14 +16,14 @@ import (
|
|||
)
|
||||
|
||||
const (
|
||||
// InvalidClientCookieError - returned when the client provides a cookie with the wrong length
|
||||
InvalidClientCookieError = utils.Error("InvalidClientCookieError")
|
||||
)
|
||||
|
||||
// HiddenServiceAuthChannel wraps implementation of im.ricochet.auth.hidden-service"
|
||||
type HiddenServiceAuthChannel struct {
|
||||
// PrivateKey must be set for client-side authentication channels
|
||||
PrivateKey *rsa.PrivateKey
|
||||
// Server Hostname must be set for client-side authentication channels
|
||||
Identity identity.Identity
|
||||
ServerHostname string
|
||||
|
||||
// Callbacks
|
||||
|
@ -71,7 +72,7 @@ func (ah *HiddenServiceAuthChannel) Closed(err error) {
|
|||
// Remote -> [Open Authentication Channel] -> Local
|
||||
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
|
||||
}
|
||||
|
||||
|
@ -93,7 +94,7 @@ func (ah *HiddenServiceAuthChannel) OpenInbound(channel *Channel, oc *Protocol_D
|
|||
// Local -> [Open Authentication Channel] -> Remote
|
||||
func (ah *HiddenServiceAuthChannel) OpenOutbound(channel *Channel) ([]byte, error) {
|
||||
|
||||
if ah.PrivateKey == nil {
|
||||
if !ah.Identity.Initialized() {
|
||||
return nil, utils.PrivateKeyNotSetError
|
||||
}
|
||||
|
||||
|
@ -122,15 +123,9 @@ func (ah *HiddenServiceAuthChannel) OpenOutboundResult(err error, crm *Protocol_
|
|||
|
||||
ah.AddServerCookie(serverCookie.([]byte)[:])
|
||||
|
||||
publicKeyBytes, _ := asn1.Marshal(rsa.PublicKey{
|
||||
N: ah.PrivateKey.PublicKey.N,
|
||||
E: ah.PrivateKey.PublicKey.E,
|
||||
})
|
||||
challenge := ah.GenChallenge(ah.Identity.Hostname(), ah.ServerHostname)
|
||||
|
||||
clientHostname := utils.GetTorHostname(publicKeyBytes)
|
||||
challenge := ah.GenChallenge(clientHostname, ah.ServerHostname)
|
||||
|
||||
signature, err := rsa.SignPKCS1v15(nil, ah.PrivateKey, crypto.SHA256, challenge)
|
||||
signature, err := ah.Identity.Sign(challenge)
|
||||
|
||||
if err != nil {
|
||||
ah.channel.SendMessage([]byte{})
|
||||
|
@ -138,14 +133,14 @@ func (ah *HiddenServiceAuthChannel) OpenOutboundResult(err error, crm *Protocol_
|
|||
}
|
||||
|
||||
messageBuilder := new(utils.MessageBuilder)
|
||||
proof := messageBuilder.Proof(publicKeyBytes, signature)
|
||||
proof := messageBuilder.Proof(ah.Identity.PublicKeyBytes(), signature)
|
||||
ah.channel.SendMessage(proof)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Packet is called for each raw packet received on this channel.
|
||||
// Input: Remote -> [Proof] -> Client
|
||||
// Input: Client -> [Proof] -> Remote
|
||||
// OR
|
||||
// Input: Remote -> [Result] -> Client
|
||||
func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
|
||||
|
@ -160,18 +155,13 @@ func (ah *HiddenServiceAuthChannel) Packet(data []byte) {
|
|||
if res.GetProof() != nil && ah.channel.Direction == Inbound {
|
||||
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 {
|
||||
ah.ServerAuthInvalid(err)
|
||||
ah.channel.SendMessage([]byte{})
|
||||
return
|
||||
}
|
||||
|
||||
serverHostname := utils.GetTorHostname(publicKeyBytes)
|
||||
serverHostname := ah.Identity.Hostname()
|
||||
|
||||
publicKey := rsa.PublicKey{}
|
||||
_, err = asn1.Unmarshal(res.GetProof().GetPublicKey(), &publicKey)
|
||||
|
|
|
@ -4,6 +4,7 @@ import (
|
|||
"bytes"
|
||||
"crypto/rsa"
|
||||
"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/wire/control"
|
||||
"testing"
|
||||
|
@ -71,10 +72,10 @@ func GetOpenAuthenticationChannelMessage() *Protocol_Data_Control.OpenChannel {
|
|||
}
|
||||
|
||||
func TestAuthenticationOpenInbound(t *testing.T) {
|
||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
||||
id := identity.Init("../testing/private_key")
|
||||
opm := GetOpenAuthenticationChannelMessage()
|
||||
authHandler := new(HiddenServiceAuthChannel)
|
||||
authHandler.PrivateKey = privateKey
|
||||
authHandler.Identity = id
|
||||
channel := Channel{ID: 1}
|
||||
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)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("HiddenServiceAuthChannel OpenOutbound Failed: %v", err)
|
||||
t.Errorf("HiddenServiceAuthChannel OpenInbound Failed: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestAuthenticationOpenOutbound(t *testing.T) {
|
||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
||||
id := identity.Init("../testing/private_key")
|
||||
authHandler := new(HiddenServiceAuthChannel)
|
||||
authHandler.PrivateKey = privateKey
|
||||
authHandler.Identity = id
|
||||
authHandler.ServerHostname = "kwke2hntvyfqm7dr"
|
||||
channel := Channel{ID: 1}
|
||||
response, err := authHandler.OpenOutbound(&channel)
|
||||
|
||||
|
@ -105,20 +107,20 @@ func TestAuthenticationOpenOutbound(t *testing.T) {
|
|||
t.Errorf("Open Channel Packet not included %v", res)
|
||||
}
|
||||
} else {
|
||||
t.Errorf("HiddenServiceAuthChannel OpenInbound Failed: %v", err)
|
||||
t.Errorf("HiddenServiceAuthChannel OpenOutbound Failed: %v", err)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
func TestAuthenticationOpenOutboundResult(t *testing.T) {
|
||||
|
||||
privateKey, _ := utils.LoadPrivateKeyFromFile("../testing/private_key")
|
||||
id := identity.Init("../testing/private_key")
|
||||
|
||||
authHandlerA := new(HiddenServiceAuthChannel)
|
||||
authHandlerB := new(HiddenServiceAuthChannel)
|
||||
|
||||
authHandlerA.Identity = id
|
||||
authHandlerA.ServerHostname = "kwke2hntvyfqm7dr"
|
||||
authHandlerA.PrivateKey = privateKey
|
||||
authHandlerA.ClientAuthResult = func(accepted, known bool) {}
|
||||
channelA := Channel{ID: 1, Direction: Outbound}
|
||||
channelA.SendMessage = func(message []byte) {
|
||||
|
@ -130,8 +132,7 @@ func TestAuthenticationOpenOutboundResult(t *testing.T) {
|
|||
res := new(Protocol_Data_Control.Packet)
|
||||
proto.Unmarshal(response[:], res)
|
||||
|
||||
authHandlerB.ServerHostname = "kwke2hntvyfqm7dr"
|
||||
authHandlerB.PrivateKey = privateKey
|
||||
authHandlerB.Identity = id
|
||||
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") }
|
||||
channelB := Channel{ID: 1, Direction: Inbound}
|
||||
|
|
|
@ -84,6 +84,9 @@ func NewOutboundConnection(conn io.ReadWriteCloser, remoteHostname string) *Conn
|
|||
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) {
|
||||
rc.trace = enabled
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package connection
|
|||
import (
|
||||
"crypto/rsa"
|
||||
"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/utils"
|
||||
"sync"
|
||||
|
@ -63,7 +64,7 @@ func (ich *InboundConnectionHandler) ProcessAuthAsServer(privateKey *rsa.Private
|
|||
ach.RegisterChannelHandler("im.ricochet.auth.hidden-service",
|
||||
func() channels.Handler {
|
||||
return &channels.HiddenServiceAuthChannel{
|
||||
PrivateKey: privateKey,
|
||||
Identity: identity.Initialize("", privateKey),
|
||||
ServerAuthValid: onAuthValid,
|
||||
ServerAuthInvalid: onAuthInvalid,
|
||||
}
|
||||
|
|
|
@ -3,6 +3,7 @@ package connection
|
|||
import (
|
||||
"crypto/rsa"
|
||||
"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/utils"
|
||||
"sync"
|
||||
|
@ -68,7 +69,7 @@ func (och *OutboundConnectionHandler) ProcessAuthAsClient(privateKey *rsa.Privat
|
|||
err := och.connection.Do(func() error {
|
||||
_, err := och.connection.RequestOpenChannel("im.ricochet.auth.hidden-service",
|
||||
&channels.HiddenServiceAuthChannel{
|
||||
PrivateKey: privateKey,
|
||||
Identity: identity.Initialize("", privateKey),
|
||||
ServerHostname: och.connection.RemoteHostname,
|
||||
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
|
||||
}
|
||||
|
||||
// negotiate version takes an open network connection and executes
|
||||
// NegotiateVersionOutbound takes an open network connection and executes
|
||||
// the ricochet version negotiation procedure.
|
||||
func NegotiateVersionOutbound(conn net.Conn, remoteHostname string) (*connection.Connection, error) {
|
||||
versions := []byte{0x49, 0x4D, 0x01, 0x01}
|
||||
|
|
|
@ -5,15 +5,15 @@ import (
|
|||
)
|
||||
|
||||
func TestGeneratePrivateKey(t *testing.T) {
|
||||
_, err := GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Errorf("Error while generating private key: %v", err)
|
||||
}
|
||||
_, err := GeneratePrivateKey()
|
||||
if err != nil {
|
||||
t.Errorf("Error while generating private key: %v", err)
|
||||
}
|
||||
}
|
||||
|
||||
func TestLoadPrivateKey(t *testing.T) {
|
||||
_,err := LoadPrivateKeyFromFile("../testing/private_key")
|
||||
if err != nil {
|
||||
t.Errorf("Error while loading private key from file: %v", err)
|
||||
}
|
||||
_, err := LoadPrivateKeyFromFile("../testing/private_key")
|
||||
if err != nil {
|
||||
t.Errorf("Error while loading private key from file: %v", err)
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue