core: Use consistent functions to validate and convert address formats
This commit is contained in:
parent
91dbe5a261
commit
9c0110dca2
|
@ -0,0 +1,84 @@
|
||||||
|
package core
|
||||||
|
|
||||||
|
import (
|
||||||
|
"crypto/rsa"
|
||||||
|
"errors"
|
||||||
|
"github.com/yawning/bulb/utils/pkcs1"
|
||||||
|
"strings"
|
||||||
|
)
|
||||||
|
|
||||||
|
// Conversion functions between ricochet addresses, onion hostnames, and 80-bit base32 encoded fingerprints.
|
||||||
|
// As used in this file, these are referred to as 'address', 'onion', and 'plain host' respectively.
|
||||||
|
|
||||||
|
func isBase32Valid(str string) bool {
|
||||||
|
for _, c := range []byte(str) {
|
||||||
|
if (c < 'a' || c > 'z') && (c < '2' || c > '7') {
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsAddressValid(addr string) bool {
|
||||||
|
return len(addr) == 25 && strings.HasPrefix(addr, "ricochet:") && isBase32Valid(addr[9:])
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsOnionValid(onion string) bool {
|
||||||
|
return len(onion) == 22 && strings.HasSuffix(onion, ".onion") && isBase32Valid(onion[0:16])
|
||||||
|
}
|
||||||
|
|
||||||
|
func IsPlainHostValid(host string) bool {
|
||||||
|
return len(host) == 16 && isBase32Valid(host)
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddressFromOnion(onion string) (string, bool) {
|
||||||
|
if !IsOnionValid(onion) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return "ricochet:" + onion[0:16], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnionFromAddress(addr string) (string, bool) {
|
||||||
|
if !IsAddressValid(addr) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return addr[9:] + ".onion", true
|
||||||
|
}
|
||||||
|
|
||||||
|
func OnionFromPlainHost(host string) (string, bool) {
|
||||||
|
if !IsPlainHostValid(host) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return host + ".onion", true
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlainHostFromOnion(onion string) (string, bool) {
|
||||||
|
if !IsOnionValid(onion) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return onion[0:16], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddressFromPlainHost(host string) (string, bool) {
|
||||||
|
if !IsPlainHostValid(host) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return "ricochet:" + host, true
|
||||||
|
}
|
||||||
|
|
||||||
|
func PlainHostFromAddress(host string) (string, bool) {
|
||||||
|
if !IsAddressValid(host) {
|
||||||
|
return "", false
|
||||||
|
}
|
||||||
|
return host[9:], true
|
||||||
|
}
|
||||||
|
|
||||||
|
func AddressFromKey(key *rsa.PublicKey) (string, error) {
|
||||||
|
addr, err := pkcs1.OnionAddr(key)
|
||||||
|
if err != nil {
|
||||||
|
return "", err
|
||||||
|
} else if addr == "" {
|
||||||
|
return "", errors.New("Invalid key")
|
||||||
|
}
|
||||||
|
return "ricochet:" + addr, nil
|
||||||
|
}
|
|
@ -8,7 +8,6 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"log"
|
"log"
|
||||||
"strconv"
|
"strconv"
|
||||||
"strings"
|
|
||||||
"sync"
|
"sync"
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
@ -50,7 +49,7 @@ func ContactFromConfig(core *Ricochet, id int, data ConfigContact, events *utils
|
||||||
|
|
||||||
if id < 0 {
|
if id < 0 {
|
||||||
return nil, fmt.Errorf("Invalid contact ID '%d'", id)
|
return nil, fmt.Errorf("Invalid contact ID '%d'", id)
|
||||||
} else if len(data.Hostname) != 22 || !strings.HasSuffix(data.Hostname, ".onion") {
|
} else if !IsOnionValid(data.Hostname) {
|
||||||
return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname)
|
return nil, fmt.Errorf("Invalid contact hostname '%s", data.Hostname)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -78,7 +77,8 @@ func (c *Contact) Nickname() string {
|
||||||
func (c *Contact) Address() string {
|
func (c *Contact) Address() string {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
return "ricochet:" + c.data.Hostname[0:16]
|
address, _ := AddressFromOnion(c.data.Hostname)
|
||||||
|
return address
|
||||||
}
|
}
|
||||||
|
|
||||||
func (c *Contact) Hostname() string {
|
func (c *Contact) Hostname() string {
|
||||||
|
@ -110,9 +110,10 @@ func (c *Contact) Status() ricochet.Contact_Status {
|
||||||
func (c *Contact) Data() *ricochet.Contact {
|
func (c *Contact) Data() *ricochet.Contact {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
|
address, _ := AddressFromOnion(c.data.Hostname)
|
||||||
data := &ricochet.Contact{
|
data := &ricochet.Contact{
|
||||||
Id: int32(c.id),
|
Id: int32(c.id),
|
||||||
Address: "ricochet:" + c.data.Hostname[0:16],
|
Address: address,
|
||||||
Nickname: c.data.Nickname,
|
Nickname: c.data.Nickname,
|
||||||
WhenCreated: c.data.WhenCreated,
|
WhenCreated: c.data.WhenCreated,
|
||||||
LastConnected: c.data.LastConnected,
|
LastConnected: c.data.LastConnected,
|
||||||
|
@ -134,9 +135,10 @@ func (c *Contact) Conversation() *Conversation {
|
||||||
c.mutex.Lock()
|
c.mutex.Lock()
|
||||||
defer c.mutex.Unlock()
|
defer c.mutex.Unlock()
|
||||||
if c.conversation == nil {
|
if c.conversation == nil {
|
||||||
|
address, _ := AddressFromOnion(c.data.Hostname)
|
||||||
entity := &ricochet.Entity{
|
entity := &ricochet.Entity{
|
||||||
ContactId: int32(c.id),
|
ContactId: int32(c.id),
|
||||||
Address: "ricochet:" + c.data.Hostname[0:16],
|
Address: address,
|
||||||
}
|
}
|
||||||
c.conversation = NewConversation(c, entity, c.core.Identity.ConversationStream)
|
c.conversation = NewConversation(c, entity, c.core.Identity.ConversationStream)
|
||||||
}
|
}
|
||||||
|
|
|
@ -81,10 +81,13 @@ func (this *ContactList) ContactByAddress(address string) *Contact {
|
||||||
}
|
}
|
||||||
|
|
||||||
func (this *ContactList) AddContactRequest(address, name, fromName, text string) (*Contact, error) {
|
func (this *ContactList) AddContactRequest(address, name, fromName, text string) (*Contact, error) {
|
||||||
|
if !IsAddressValid(address) {
|
||||||
|
return nil, errors.New("Invalid ricochet address")
|
||||||
|
}
|
||||||
|
|
||||||
this.mutex.Lock()
|
this.mutex.Lock()
|
||||||
defer this.mutex.Unlock()
|
defer this.mutex.Unlock()
|
||||||
|
|
||||||
// XXX check that address is valid before relying on format below
|
|
||||||
// XXX validity checks on name/text also useful
|
// XXX validity checks on name/text also useful
|
||||||
|
|
||||||
for _, contact := range this.contacts {
|
for _, contact := range this.contacts {
|
||||||
|
@ -111,8 +114,9 @@ func (this *ContactList) AddContactRequest(address, name, fromName, text string)
|
||||||
}
|
}
|
||||||
|
|
||||||
contactId := maxContactId + 1
|
contactId := maxContactId + 1
|
||||||
|
onion, _ := OnionFromAddress(address)
|
||||||
configContact := ConfigContact{
|
configContact := ConfigContact{
|
||||||
Hostname: address[9:] + ".onion",
|
Hostname: onion,
|
||||||
Nickname: name,
|
Nickname: name,
|
||||||
WhenCreated: time.Now().Format(time.RFC3339),
|
WhenCreated: time.Now().Format(time.RFC3339),
|
||||||
Request: ConfigContactRequest{
|
Request: ConfigContactRequest{
|
||||||
|
|
|
@ -62,7 +62,7 @@ func (me *Identity) loadIdentity() error {
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
me.address, err = utils.RicochetAddressFromKey(&me.privateKey.PublicKey)
|
me.address, err = AddressFromKey(&me.privateKey.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -93,7 +93,7 @@ func (me *Identity) setPrivateKey(key *rsa.PrivateKey) error {
|
||||||
config.Save()
|
config.Save()
|
||||||
|
|
||||||
// Update Identity
|
// Update Identity
|
||||||
me.address, err = utils.RicochetAddressFromKey(&key.PublicKey)
|
me.address, err = AddressFromKey(&key.PublicKey)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return err
|
return err
|
||||||
}
|
}
|
||||||
|
@ -116,7 +116,11 @@ func (is *identityService) OnNewConnection(oc *protocol.OpenConnection) {
|
||||||
handler := &ProtocolConnection{
|
handler := &ProtocolConnection{
|
||||||
Conn: oc,
|
Conn: oc,
|
||||||
GetContactByHostname: func(hostname string) *Contact {
|
GetContactByHostname: func(hostname string) *Contact {
|
||||||
return identity.ContactList().ContactByAddress("ricochet:" + hostname)
|
address, ok := AddressFromPlainHost(hostname)
|
||||||
|
if !ok {
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
return identity.ContactList().ContactByAddress(address)
|
||||||
},
|
},
|
||||||
}
|
}
|
||||||
go oc.Process(handler)
|
go oc.Process(handler)
|
||||||
|
|
|
@ -5,7 +5,6 @@ import (
|
||||||
"golang.org/x/net/context"
|
"golang.org/x/net/context"
|
||||||
"log"
|
"log"
|
||||||
"net"
|
"net"
|
||||||
"strings"
|
|
||||||
"time"
|
"time"
|
||||||
)
|
)
|
||||||
|
|
||||||
|
@ -38,7 +37,7 @@ func (oc *OnionConnector) Connect(address string, c context.Context) (net.Conn,
|
||||||
}
|
}
|
||||||
|
|
||||||
host, _, err := net.SplitHostPort(address)
|
host, _, err := net.SplitHostPort(address)
|
||||||
if err != nil || !strings.HasSuffix(host, ".onion") {
|
if err != nil || !IsOnionValid(host) {
|
||||||
return nil, errors.New("Invalid address")
|
return nil, errors.New("Invalid address")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,5 @@
|
||||||
package utils
|
package utils
|
||||||
|
|
||||||
import (
|
|
||||||
"crypto/rsa"
|
|
||||||
"errors"
|
|
||||||
"github.com/yawning/bulb/utils/pkcs1"
|
|
||||||
"strings"
|
|
||||||
)
|
|
||||||
|
|
||||||
// Take a string containing substrings separated by sep, and
|
// Take a string containing substrings separated by sep, and
|
||||||
// return a slice of substrings as in strings.Split. Double quotes
|
// return a slice of substrings as in strings.Split. Double quotes
|
||||||
// are stripped from the output, and separator characters within
|
// are stripped from the output, and separator characters within
|
||||||
|
@ -35,27 +28,3 @@ func UnquoteStringSplit(s string, sep rune) []string {
|
||||||
|
|
||||||
return append(re, current)
|
return append(re, current)
|
||||||
}
|
}
|
||||||
|
|
||||||
func RicochetAddressFromKey(key *rsa.PublicKey) (string, error) {
|
|
||||||
addr, err := pkcs1.OnionAddr(key)
|
|
||||||
if err != nil {
|
|
||||||
return "", err
|
|
||||||
} else if addr == "" {
|
|
||||||
return "", errors.New("Invalid key")
|
|
||||||
}
|
|
||||||
return "ricochet:" + addr, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func RicochetAddressFromOnion(onion string) (string, error) {
|
|
||||||
if len(onion) != 23 || !strings.HasSuffix(onion, ".onion") {
|
|
||||||
return "", errors.New("Invalid onion address")
|
|
||||||
}
|
|
||||||
return "ricochet:" + onion[:16], nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func OnionFromRicochetAddress(address string) (string, error) {
|
|
||||||
if len(address) != 25 || !strings.HasPrefix(address, "ricochet:") {
|
|
||||||
return "", errors.New("Invalid ricochet address")
|
|
||||||
}
|
|
||||||
return address[9:] + ".onion", nil
|
|
||||||
}
|
|
||||||
|
|
Loading…
Reference in New Issue