Curve25519
This commit is contained in:
		@@ -19,7 +19,7 @@ type KeysArray []KeyPair
 | 
			
		||||
 | 
			
		||||
func NewKeyPair() KeyPair {
 | 
			
		||||
	var kp KeyPair
 | 
			
		||||
	keys, err := crypto.GenerateKey("name", "mail", "rsa", 4096)
 | 
			
		||||
	keys, err := crypto.GenerateKey("name", "mail", "x25519", 0)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().Msg("Key generation failed")
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
@@ -4,16 +4,25 @@
 | 
			
		||||
\title{
 | 
			
		||||
    \textffm{Meow} messaging protocol}
 | 
			
		||||
\author{Author
 | 
			
		||||
   \texttt{author@address.net}}
 | 
			
		||||
   \texttt{meow@redroom.link}}
 | 
			
		||||
\date{\today}
 | 
			
		||||
 | 
			
		||||
\maketitle
 | 
			
		||||
 | 
			
		||||
\begin{abstract}
 | 
			
		||||
The \textffm{Meow} protocol is a privacy driven instant messaging protocol. 
 | 
			
		||||
 | 
			
		||||
    
 | 
			
		||||
    The \textffm{Meow} protocol is a privacy driven instant messaging protocol. 
 | 
			
		||||
That protocol might be used for creating secure and distributed chat services or allowing machine to machine communication.
 | 
			
		||||
This document describes the services provided by the protocol, the messaging structures and the transport protocols that might be used.
 | 
			
		||||
\begin{quote} 
 | 
			
		||||
 | 
			
		||||
    \centering 
 | 
			
		||||
    \emph{"Nous ne vivrons pas d'utopie collective, nous arrivons trop tard, le grand marché est déjà là.
 | 
			
		||||
Nous devons élaborer des stratégies de survie et de contamination, par la prolifération d'utopies privées, cryptées, qui se substitueront à l'ancien ordre social. 
 | 
			
		||||
Tout ce que je sais, c'est que nous vivons dans un monde dont on ne s'évade pas"}\\
 | 
			
		||||
\footnotesize{Maurice G. Dantec for NOII (1997)}
 | 
			
		||||
\end{quote} 
 | 
			
		||||
\end{abstract}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -60,23 +69,21 @@ In return your contact will provide the exact same data, encrypted with your pub
 | 
			
		||||
 | 
			
		||||
\subsection{Contacts forwarding}
 | 
			
		||||
Using the \textffm{Meow} protocol a user won't be able to forward your contact information without your consent.
 | 
			
		||||
Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless, any message to that identity signed by another user would be discarded.
 | 
			
		||||
Each user knows you as a different identity, thus forwarding a known identity to another user is meaningless. Any message to that identity signed by another user thna you would be discarded.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\subsection{Group conversation}
 | 
			
		||||
A very basic group messaging service is available. It allows to exchange group information between users. After that, a message to a group will send a copy of the message to each member.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\subsection{Emergency broadcast}
 | 
			
		||||
The
 | 
			
		||||
A local (server based) emergency broadcast service will be provided. It will provide the ability to send/receive broadcast messages to all users connected to the current server.
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\subsection{Public networks shortage resilience}
 | 
			
		||||
\textffm{Meow} may run without Internet connection, either on an isolated wifi access point, either on a meshed network of wifi routers or even via serial IOT transport layers (LoRa,...)
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
\subsection{User directory service}
 | 
			
		||||
This service allows to restore a lost functionality of Internet historic chat services (like ICQ). You could simply set a "Free for chat" status that allowed other people to contact you, either randomly or based on a short description that you might provide.
 | 
			
		||||
@@ -98,6 +105,8 @@ Let's call that one the User Key Pair (Ukp)
 | 
			
		||||
 | 
			
		||||
\subsection{Contact identity}
 | 
			
		||||
Each of your contacts will know you as a different identity, we'll call that one the Contact Key Pair (Ckp)
 | 
			
		||||
That contact Key Pair will not change once it's agreed between the two peerr : An initial key will be exchanged as part of the peer invitation process.
 | 
			
		||||
As other people myth have seen your key
 | 
			
		||||
This means that :
 | 
			
		||||
\begin{itemize}
 | 
			
		||||
    \item none of your contacts will be able to forward your id to another person without your consent
 | 
			
		||||
@@ -176,7 +185,10 @@ TODO
 | 
			
		||||
 | 
			
		||||
\section{Server Features}
 | 
			
		||||
\subsection{Server catalog}
 | 
			
		||||
Each server will cache a list of all the servers that it is aware of.
 | 
			
		||||
 | 
			
		||||
\subsection{Antispam}
 | 
			
		||||
 | 
			
		||||
\subsection{Self defense}
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
@@ -188,6 +200,10 @@ TODO
 | 
			
		||||
\section{Very secure devices}
 | 
			
		||||
You don't trust your phone ?
 | 
			
		||||
 | 
			
		||||
\section{Roadmap}
 | 
			
		||||
\subsection{Nations}
 | 
			
		||||
Beyond the scope of user directories, we plan to implement the concept of virtual Nations.
 | 
			
		||||
Today still, most people don't really choose the nation they live in. 
 | 
			
		||||
 | 
			
		||||
 | 
			
		||||
\end{document}
 | 
			
		||||
@@ -31,7 +31,7 @@ func TestEndToEnd(t *testing.T) {
 | 
			
		||||
		a, _ := json.Marshal(invitation)
 | 
			
		||||
		fmt.Println(string(a))
 | 
			
		||||
		// TODO : Convert invitation to QR Code
 | 
			
		||||
 | 
			
		||||
		invitation.WritePng("invitation.png")
 | 
			
		||||
		//
 | 
			
		||||
		// Simulate peer invitation response : generate the friend's keypair
 | 
			
		||||
		fmt.Println("Simulating first friend answer...")
 | 
			
		||||
@@ -64,6 +64,8 @@ func TestEndToEnd(t *testing.T) {
 | 
			
		||||
		ioutil.WriteFile("id.json", a, 0644)
 | 
			
		||||
		fmt.Println(string(a))
 | 
			
		||||
	}
 | 
			
		||||
	// go me.CheckMessages()
 | 
			
		||||
 | 
			
		||||
	//myFirstFriend.SetComMethod()
 | 
			
		||||
	//msg := myFirstFriend.SendText()
 | 
			
		||||
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										2
									
								
								go.mod
									
									
									
									
									
								
							
							
						
						
									
										2
									
								
								go.mod
									
									
									
									
									
								
							@@ -6,8 +6,8 @@ require (
 | 
			
		||||
	github.com/ProtonMail/gopenpgp/v2 v2.2.4
 | 
			
		||||
	github.com/go-resty/resty/v2 v2.6.0
 | 
			
		||||
	github.com/google/uuid v1.3.0
 | 
			
		||||
	github.com/makiuchi-d/gozxing v0.1.1
 | 
			
		||||
	github.com/rs/zerolog v1.25.0
 | 
			
		||||
	github.com/stretchr/testify v1.4.0
 | 
			
		||||
	github.com/tidwall/gjson v1.10.2
 | 
			
		||||
	google.golang.org/protobuf v1.27.1
 | 
			
		||||
)
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										11
									
								
								go.sum
									
									
									
									
									
								
							
							
						
						
									
										11
									
								
								go.sum
									
									
									
									
									
								
							@@ -19,6 +19,8 @@ github.com/google/uuid v1.3.0 h1:t6JiXgmwXMjEs8VusXIJk2BXHsn+wx8BZdTaoZ5fu7I=
 | 
			
		||||
github.com/google/uuid v1.3.0/go.mod h1:TIyPZe4MgqvfeYDBFedMoGGpEw/LqOeaOT+nhxU+yHo=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1 h1:mweAR1A6xJ3oS2pRaGiHgQ4OO8tzTaLawm8vnODuwDk=
 | 
			
		||||
github.com/konsorten/go-windows-terminal-sequences v1.0.1/go.mod h1:T0+1ngSBFLxvqU3pZ+m/2kptfBszLMUkC4ZK/EgS/cQ=
 | 
			
		||||
github.com/makiuchi-d/gozxing v0.1.1 h1:xxqijhoedi+/lZlhINteGbywIrewVdVv2wl9r5O9S1I=
 | 
			
		||||
github.com/makiuchi-d/gozxing v0.1.1/go.mod h1:eRIHbOjX7QWxLIDJoQuMLhuXg9LAuw6znsUtRkNw9DU=
 | 
			
		||||
github.com/pkg/errors v0.9.1 h1:FEBLx1zS214owpjy7qsBeixbURkuhQAwrK5UwLGTwt4=
 | 
			
		||||
github.com/pkg/errors v0.9.1/go.mod h1:bwawxfHBFNV+L2hUp1rHADufV3IMtnDRdf1r5NINEl0=
 | 
			
		||||
github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
 | 
			
		||||
@@ -33,12 +35,6 @@ github.com/stretchr/objx v0.1.1/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+
 | 
			
		||||
github.com/stretchr/testify v1.2.2/go.mod h1:a8OnRcib4nhh0OaRAV+Yts87kKdq0PP7pXfy6kDkUVs=
 | 
			
		||||
github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
 | 
			
		||||
github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
 | 
			
		||||
github.com/tidwall/gjson v1.10.2 h1:APbLGOM0rrEkd8WBw9C24nllro4ajFuJu0Sc9hRz8Bo=
 | 
			
		||||
github.com/tidwall/gjson v1.10.2/go.mod h1:/wbyibRr2FHMks5tjHJ5F8dMZh3AcwJEMf5vlfC0lxk=
 | 
			
		||||
github.com/tidwall/match v1.1.1 h1:+Ho715JplO36QYgwN9PGYNhgZvoUSc9X2c80KVTi+GA=
 | 
			
		||||
github.com/tidwall/match v1.1.1/go.mod h1:eRSPERbgtNPcGhD8UCthc6PmLEQXEWd3PRB5JTxsfmM=
 | 
			
		||||
github.com/tidwall/pretty v1.2.0 h1:RWIZEg2iJ8/g6fDDYzMpobmaoGh5OLl4AXtGUGPcqCs=
 | 
			
		||||
github.com/tidwall/pretty v1.2.0/go.mod h1:ITEVvHYasfjBbM0u2Pg8T2nJnzm8xPwvNhhsoaGGjNU=
 | 
			
		||||
github.com/yuin/goldmark v1.3.5/go.mod h1:mwnBkeHKe2W/ZEtQ+71ViKU8L12m81fl3OWwC1Zlc8k=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190308221718-c2843e01d9a2/go.mod h1:djNgcEr1/C05ACkg1iLfiJU5Ep61QUkGW8qpdssI0+w=
 | 
			
		||||
golang.org/x/crypto v0.0.0-20190510104115-cbcb75029529/go.mod h1:yigFU9vqHzYiE8UmvKecakEJjdnWj3jj499lnFckfCI=
 | 
			
		||||
@@ -72,8 +68,9 @@ golang.org/x/sys v0.0.0-20210510120138-977fb7262007/go.mod h1:oPkhp1MJrh7nUepCBc
 | 
			
		||||
golang.org/x/term v0.0.0-20201126162022-7de9c90e9dd1/go.mod h1:bj7SfCRtBDWHUb9snDiAeCFNEtKQo2Wmx5Cou7ajbmo=
 | 
			
		||||
golang.org/x/text v0.3.0/go.mod h1:NqM8EUOU14njkJ3fqMW+pc6Ldnwhi/IjpwHt7yyuwOQ=
 | 
			
		||||
golang.org/x/text v0.3.2/go.mod h1:bEr9sfX3Q8Zfm5fL9x+3itogRgK3+ptLWKqgva+5dAk=
 | 
			
		||||
golang.org/x/text v0.3.3 h1:cokOdA+Jmi5PJGXLlLllQSgYigAEfHXJAERHVMaCc2k=
 | 
			
		||||
golang.org/x/text v0.3.3/go.mod h1:5Zoc/QRtKVWzQhOtBMvqHzDpF6irO9z98xDceosuGiQ=
 | 
			
		||||
golang.org/x/text v0.3.7 h1:olpwvP2KacW1ZWvsR7uQhoyTYvKAupfQrRGBFM352Gk=
 | 
			
		||||
golang.org/x/text v0.3.7/go.mod h1:u+2+/6zg+i71rQMx5EYifcz6MCKuco9NR6JIITiCfzQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20180917221912-90fa682c2a6e/go.mod h1:n7NCudcB/nEzxVGmLbDWY5pfWTLqBcC2KZ6jyYvM4mQ=
 | 
			
		||||
golang.org/x/tools v0.0.0-20190312151545-0bb0c0a6e846/go.mod h1:LCzVGOaR6xXOjkQ3onu1FJEFr0SW1gC7cKk1uF8kGRs=
 | 
			
		||||
golang.org/x/tools v0.0.0-20191119224855-298f0cb1881e/go.mod h1:b+2E5dAYhXwXZwtnZ6UAqBI28+e2cm9otk0dWdXHAEo=
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										37
									
								
								https.go
									
									
									
									
									
								
							
							
						
						
									
										37
									
								
								https.go
									
									
									
									
									
								
							@@ -2,48 +2,35 @@ package meowlib
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"crypto/tls"
 | 
			
		||||
	"fmt"
 | 
			
		||||
 | 
			
		||||
	"github.com/tidwall/gjson"
 | 
			
		||||
 | 
			
		||||
	"github.com/go-resty/resty/v2"
 | 
			
		||||
	"github.com/rs/zerolog/log"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
var Address string
 | 
			
		||||
 | 
			
		||||
func Configure(url string) {
 | 
			
		||||
	Address = url
 | 
			
		||||
type Https struct {
 | 
			
		||||
	url string
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Send(msg []byte) (string, error) {
 | 
			
		||||
func (https *Https) Send(msg []byte) ([]byte, error) {
 | 
			
		||||
	client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
 | 
			
		||||
 | 
			
		||||
	resp, err := client.R().
 | 
			
		||||
		SetHeader("Content-Type", "application/json").
 | 
			
		||||
		SetBody(msg).
 | 
			
		||||
		Post(Address + "/message/add/")
 | 
			
		||||
		Post(https.url + "/message/add/")
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Error().Msg(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	serverUuid := gjson.Get(resp.String(), "serveruuid").String()
 | 
			
		||||
	return serverUuid, err
 | 
			
		||||
	return resp.Body(), err
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Receive(key string) []byte {
 | 
			
		||||
	client := resty.New().SetTLSClientConfig(&tls.Config{InsecureSkipVerify: true})
 | 
			
		||||
 | 
			
		||||
	resp, err := client.R().
 | 
			
		||||
		SetHeader("Content-Type", "application/json").
 | 
			
		||||
		Get(Address + "/message/" + key)
 | 
			
		||||
	fmt.Println("  StatusCode     :", resp.StatusCode())
 | 
			
		||||
	fmt.Println("  Cookies     :", resp.Cookies())
 | 
			
		||||
	fmt.Println("  Error    :", err)
 | 
			
		||||
	msg := resp.Body()
 | 
			
		||||
	return msg
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func Start(callback *func() []InternalMessage) {
 | 
			
		||||
func (https *Https) Start(callback *func() []InternalMessage) {
 | 
			
		||||
	for {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (https *Https) Stop() {
 | 
			
		||||
	for {
 | 
			
		||||
 | 
			
		||||
	}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										1
									
								
								invitation.json
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										1
									
								
								invitation.json
									
									
									
									
									
										Normal file
									
								
							
										
											
												File diff suppressed because one or more lines are too long
											
										
									
								
							
							
								
								
									
										
											BIN
										
									
								
								invitation.png
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										
											BIN
										
									
								
								invitation.png
									
									
									
									
									
										Normal file
									
								
							
										
											Binary file not shown.
										
									
								
							| 
		 After Width: | Height: | Size: 530 B  | 
@@ -1,9 +1,7 @@
 | 
			
		||||
package meowlib
 | 
			
		||||
 | 
			
		||||
type Network interface {
 | 
			
		||||
	Configure(url string)
 | 
			
		||||
	Send(msg []byte)
 | 
			
		||||
	Receive(request []byte) []byte
 | 
			
		||||
	Start()
 | 
			
		||||
	Send(request []byte) ([]byte, error)
 | 
			
		||||
	Start(callback *func() []InternalMessage)
 | 
			
		||||
	Stop()
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
							
								
								
									
										94
									
								
								peer.go
									
									
									
									
									
								
							
							
						
						
									
										94
									
								
								peer.go
									
									
									
									
									
								
							@@ -1,8 +1,19 @@
 | 
			
		||||
package meowlib
 | 
			
		||||
 | 
			
		||||
import (
 | 
			
		||||
	"encoding/json"
 | 
			
		||||
	"fmt"
 | 
			
		||||
	"image"
 | 
			
		||||
	"image/color"
 | 
			
		||||
	"image/png"
 | 
			
		||||
	"log"
 | 
			
		||||
	"math"
 | 
			
		||||
	"os"
 | 
			
		||||
	"strings"
 | 
			
		||||
	"time"
 | 
			
		||||
 | 
			
		||||
	"github.com/makiuchi-d/gozxing"
 | 
			
		||||
	"github.com/makiuchi-d/gozxing/qrcode"
 | 
			
		||||
)
 | 
			
		||||
 | 
			
		||||
type Contact struct {
 | 
			
		||||
@@ -27,6 +38,8 @@ type Peer struct {
 | 
			
		||||
	LastMessage         time.Time         `json:"last_message,omitempty"`
 | 
			
		||||
	EncryptionKp        KeyPair           `json:"conversation_kp,omitempty"`
 | 
			
		||||
	LookupKp            KeyPair           `json:"lookup_kp,omitempty"`
 | 
			
		||||
	CommUrl             string            `json:"comm_url,omitempty"`
 | 
			
		||||
	net                 Network
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
type PeerList []Peer
 | 
			
		||||
@@ -54,7 +67,88 @@ func (pl *PeerList) GetFromName(name string) *Peer {
 | 
			
		||||
	return nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (peer *Peer) SetCommInterface(url string) {
 | 
			
		||||
	if strings.HasPrefix(url, "http://") {
 | 
			
		||||
		var https *Https
 | 
			
		||||
		https.url = url
 | 
			
		||||
		peer.net = Network(https)
 | 
			
		||||
	}
 | 
			
		||||
	peer.net = nil
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (peer *Peer) SendText(text string) {
 | 
			
		||||
	im := CreateText(*peer, text)
 | 
			
		||||
	fmt.Println(im.MessageData.Destination)
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (contact *Contact) WritePng(filename string) {
 | 
			
		||||
	jsonContact, _ := json.Marshal(contact)
 | 
			
		||||
	//imgdata := base64.StdEncoding.EncodeToString(jsonContact)
 | 
			
		||||
	size := int(math.Sqrt(float64(len(jsonContact))/3)) + 1
 | 
			
		||||
	println(size)
 | 
			
		||||
 | 
			
		||||
	// Create a colored i	mage of the given width and height.
 | 
			
		||||
	img := image.NewNRGBA(image.Rect(0, 0, size, size))
 | 
			
		||||
 | 
			
		||||
	for y := 0; y < size; y++ {
 | 
			
		||||
		for x := 0; x < size*3; x = x + 3 {
 | 
			
		||||
			p1 := uint8(jsonContact[x+y])
 | 
			
		||||
			p2 := uint8(jsonContact[x+y+1])
 | 
			
		||||
			p3 := uint8(jsonContact[x+y+2])
 | 
			
		||||
			img.Set(x/3, y, color.NRGBA{
 | 
			
		||||
				R: p1,
 | 
			
		||||
				G: p2,
 | 
			
		||||
				B: p3,
 | 
			
		||||
				A: 255,
 | 
			
		||||
			})
 | 
			
		||||
		}
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	f, err := os.Create(filename)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := png.Encode(f, img); err != nil {
 | 
			
		||||
		f.Close()
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
	if err := f.Close(); err != nil {
 | 
			
		||||
		log.Fatal(err)
 | 
			
		||||
	}
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func (contact *Contact) WriteQr(filename string) {
 | 
			
		||||
	jsonContact, _ := json.Marshal(contact)
 | 
			
		||||
	qwriter := qrcode.NewQRCodeWriter()
 | 
			
		||||
	code, err := qwriter.Encode(string(jsonContact), gozxing.BarcodeFormat_QR_CODE, 512, 512, nil)
 | 
			
		||||
	if err != nil {
 | 
			
		||||
		println(err.Error())
 | 
			
		||||
	}
 | 
			
		||||
	file, _ := os.Create("barcode.png")
 | 
			
		||||
	defer file.Close()
 | 
			
		||||
 | 
			
		||||
	// *BitMatrix implements the image.Image interface,
 | 
			
		||||
	// so it is able to be passed to png.Encode directly.
 | 
			
		||||
	_ = png.Encode(file, code)
 | 
			
		||||
 | 
			
		||||
}
 | 
			
		||||
 | 
			
		||||
func ReadQr(fielname string) Contact {
 | 
			
		||||
	var contact Contact
 | 
			
		||||
	// open and decode image file
 | 
			
		||||
	file, _ := os.Open("qrcode.jpg")
 | 
			
		||||
	img, _, _ := image.Decode(file)
 | 
			
		||||
 | 
			
		||||
	// prepare BinaryBitmap
 | 
			
		||||
	bmp, _ := gozxing.NewBinaryBitmapFromImage(img)
 | 
			
		||||
 | 
			
		||||
	// decode image
 | 
			
		||||
	qrReader := qrcode.NewQRCodeReader()
 | 
			
		||||
	result, _ := qrReader.Decode(bmp, nil)
 | 
			
		||||
 | 
			
		||||
	fmt.Println(result)
 | 
			
		||||
	return contact
 | 
			
		||||
}
 | 
			
		||||
 
 | 
			
		||||
		Reference in New Issue
	
	Block a user