From 27126e3ca31fb4a743fb1d816ec5c4d5ebe574ce Mon Sep 17 00:00:00 2001 From: reugn Date: Tue, 8 Dec 2020 17:48:52 +0200 Subject: [PATCH] Initial commit --- .github/workflows/test.yml | 20 +++++++++ .gitignore | 3 ++ LICENSE | 21 +++++++++ README.md | 46 ++++++++++++++++++++ cmd/wifiqr/main.go | 87 +++++++++++++++++++++++++++++++++++++ config.go | 27 ++++++++++++ docs/images/qr.png | Bin 0 -> 459 bytes go.mod | 5 +++ go.sum | 2 + wifiqr.go | 46 ++++++++++++++++++++ wifiqr_test.go | 30 +++++++++++++ 11 files changed, 287 insertions(+) create mode 100644 .github/workflows/test.yml create mode 100644 .gitignore create mode 100644 LICENSE create mode 100644 README.md create mode 100644 cmd/wifiqr/main.go create mode 100644 config.go create mode 100644 docs/images/qr.png create mode 100644 go.mod create mode 100644 go.sum create mode 100644 wifiqr.go create mode 100644 wifiqr_test.go diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml new file mode 100644 index 0000000..92e8b0c --- /dev/null +++ b/.github/workflows/test.yml @@ -0,0 +1,20 @@ +name: Test + +on: [push, pull_request] + +jobs: + test: + runs-on: ubuntu-latest + strategy: + matrix: + go-version: [1.14.x, 1.15.x] + runs-on: ${{ matrix.os }} + steps: + - name: Setup Go + uses: actions/setup-go@v2 + with: + go-version: ${{ matrix.go-version }} + - name: Checkout code + uses: actions/checkout@v2 + - name: Test + run: go test ./... \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..cba4d2b --- /dev/null +++ b/.gitignore @@ -0,0 +1,3 @@ +.vscode/ +.idea/ +/wifiqr \ No newline at end of file diff --git a/LICENSE b/LICENSE new file mode 100644 index 0000000..acbc0fe --- /dev/null +++ b/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2020 reugn + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. diff --git a/README.md b/README.md new file mode 100644 index 0000000..aea550f --- /dev/null +++ b/README.md @@ -0,0 +1,46 @@ +# Wi-Fi QR Code generator + +Create a QR code with your Wi-Fi login details. + +Use Google Lens or other application to scan it and connect automatically. + +## Installation +Download and install Go https://golang.org/doc/install. + +Clone the repository: +```sh +git clone https://github.com/reugn/wifiqr.git +``` + +Build: +```sh +cd wifiqr +go build ./cmd/wifiqr +``` + +## Usage +```text +Usage of ./wifiqr: + -enc string + The wireless network encryption protocol (WEP, WPA, WPA2). (default "WPA2") + -file string + A png file to write the QR Code (prints to stdout if not set). + -hidden string + Hidden SSID true/false. (default "false") + -key string + A pre-shared key (PSK). You'll be prompted to enter the key if not set. + -size int + Size is both the image width and height in pixels. (default 256) + -ssid string + The name of the wireless network. You'll be prompted to enter the SSID if not set. + -version + Show version. +``` + +## Usage example +```sh +./wifiqr -ssid some_ssid -key 1234 -file qr.png -size 128 +``` + +## License +MIT \ No newline at end of file diff --git a/cmd/wifiqr/main.go b/cmd/wifiqr/main.go new file mode 100644 index 0000000..e9be1cd --- /dev/null +++ b/cmd/wifiqr/main.go @@ -0,0 +1,87 @@ +package main + +import ( + "flag" + "fmt" + "os" + "path/filepath" + "strconv" + + "github.com/reugn/wifiqr" +) + +const version = "0.1.0" + +var ( + versionParam = flag.Bool("version", false, "Show version.") + + ssidParam = flag.String("ssid", "", "The name of the wireless network. You'll be prompted to enter the SSID if not set.") + keyParam = flag.String("key", "", "A pre-shared key (PSK). You'll be prompted to enter the key if not set.") + encParam = flag.String("enc", "WPA2", "The wireless network encryption protocol (WEP, WPA, WPA2).") + hiddenParam = flag.String("hidden", "false", "Hidden SSID true/false.") + + fileNameParam = flag.String("file", "", "A png file to write the QR Code (prints to stdout if not set).") + sizeParam = flag.Int("size", 256, "Size is both the image width and height in pixels.") +) + +func main() { + flag.Parse() + + if *versionParam { + fmt.Println("Version: " + version) + return + } + + validateArguments() + + config := wifiqr.NewConfig(*ssidParam, *keyParam, *encParam, validateAndGetHidden()) + q, err := wifiqr.InitCode(config) + if err != nil { + fmt.Println(err) + return + } + + if *fileNameParam == "" { + fmt.Println(q.ToSmallString(false)) + } else { + fileName := validateAndGetFileName() + err := q.WriteFile(*sizeParam, fileName) + if err != nil { + fmt.Println(err) + } else { + fmt.Println("QR Code was successfully saved to " + fileName + ".") + } + } + +} + +func validateAndGetFileName() string { + if filepath.Ext(*fileNameParam) != ".png" { + return *fileNameParam + ".png" + } + return *fileNameParam +} + +func validateAndGetHidden() bool { + hidden, err := strconv.ParseBool(*hiddenParam) + if err != nil { + fmt.Println(err) + os.Exit(1) + } + return hidden +} + +func validateArguments() { + if *ssidParam == "" { + fmt.Println("Enter the name of the wireless network (SSID):") + fmt.Scan(ssidParam) + } + if *keyParam == "" { + fmt.Println("Enter the network key (password):") + fmt.Scan(keyParam) + } + if *ssidParam == "" || *keyParam == "" { + flag.Usage() + os.Exit(1) + } +} diff --git a/config.go b/config.go new file mode 100644 index 0000000..40089b4 --- /dev/null +++ b/config.go @@ -0,0 +1,27 @@ +package wifiqr + +// Config is the Wi-Fi network configuration parameters. +type Config struct { + // The Service Set Identifier (SSID) is the name of the wireless network. + // It can be contained in the beacons sent out by APs, or it can be ‘hidden’ so that clients + // who wish to associate must first know the name of the network. Early security guidance was + // to hide the SSID of your network, but modern networking tools can detect the SSID by simply + // watching for legitimate client association, as SSIDs are transmitted in cleartext. + SSID string + // A pre-shared key (PSK). + Key string + // The wireless network encryption protocol (WEP, WPA, WPA2). + Encryption string + // Defines if the SSID is ‘hidden’. + Hidden bool +} + +// NewConfig returns a new Config. +func NewConfig(ssid string, key string, enc string, hidden bool) *Config { + return &Config{ + SSID: ssid, + Key: key, + Encryption: enc, + Hidden: hidden, + } +} diff --git a/docs/images/qr.png b/docs/images/qr.png new file mode 100644 index 0000000000000000000000000000000000000000..5faca2bc2619daf37a9d97542c1d6c37c95e702c GIT binary patch literal 459 zcmV;+0W|)JP)E zZwEmgm`8i+>FJ|;MR^D)h%967w4#}-K*tA0GBCu~fsWz1p?Pz;Lpi^e9)ebLJ7Dsi zRWzv%49jpiQ*2B!{1mcTE{)$2pWAByE#&s}&<-Zmg4wu*hUPtnL(p=KcYo6YQq=(f zwfD|?eUkcmmJUd}Pqfyr@K8+VYOjr)BN^oy3>%9bD{fRv^{8;?n)D)>F~UjP6A|NnoX*i))})JgyV002ovPDHLkV1o4N B%Ps%_ literal 0 HcmV?d00001 diff --git a/go.mod b/go.mod new file mode 100644 index 0000000..402b631 --- /dev/null +++ b/go.mod @@ -0,0 +1,5 @@ +module github.com/reugn/wifiqr + +go 1.15 + +require github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e diff --git a/go.sum b/go.sum new file mode 100644 index 0000000..e99b5b9 --- /dev/null +++ b/go.sum @@ -0,0 +1,2 @@ +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e h1:MRM5ITcdelLK2j1vwZ3Je0FKVCfqOLp5zO6trqMLYs0= +github.com/skip2/go-qrcode v0.0.0-20200617195104-da1b6568686e/go.mod h1:XV66xRDqSt+GTGFMVlhk3ULuV0y9ZmzeVGR4mloJI3M= diff --git a/wifiqr.go b/wifiqr.go new file mode 100644 index 0000000..728a17e --- /dev/null +++ b/wifiqr.go @@ -0,0 +1,46 @@ +package wifiqr + +import ( + "strconv" + "strings" + + "github.com/skip2/go-qrcode" +) + +// There are several levels of error detection/recovery capacity. Higher levels +// of error recovery are able to correct more errors, with the trade-off of +// increased symbol size. +var defaultRecoveryLevel qrcode.RecoveryLevel = qrcode.High + +// InitCode returns the qrcode.QRCode based on the configuration. +func InitCode(config *Config) (*qrcode.QRCode, error) { + schema := buildSchema(config) + + q, err := qrcode.New(schema, defaultRecoveryLevel) + if err != nil { + return nil, err + } + + return q, nil +} + +// WIFI:S:My_SSID;T:WPA;P:key goes here;H:false; +// ^ ^ ^ ^ ^ +// | | | | +-- hidden SSID (true/false) +// | | | +-- WPA key +// | | +-- encryption type +// | +-- ESSID +// +-- code type +func buildSchema(config *Config) string { + var sb strings.Builder + sb.WriteString("WIFI:S:") + sb.WriteString(config.SSID) + sb.WriteString(";T:") + sb.WriteString(config.Encryption) + sb.WriteString(";P:") + sb.WriteString(config.Key) + sb.WriteString(";H:") + sb.WriteString(strconv.FormatBool(config.Hidden)) + sb.WriteString(";") + return sb.String() +} diff --git a/wifiqr_test.go b/wifiqr_test.go new file mode 100644 index 0000000..d5abcec --- /dev/null +++ b/wifiqr_test.go @@ -0,0 +1,30 @@ +package wifiqr_test + +import ( + "hash/fnv" + "reflect" + "testing" + + "github.com/reugn/wifiqr" +) + +func TestCode(t *testing.T) { + config := wifiqr.NewConfig("ssid1", "1234", "WPA2", false) + qrCode, err := wifiqr.InitCode(config) + if err != nil { + t.Fatal(err) + } + assertEqual(t, hashCode(qrCode.ToString(false)), 550955445) +} + +func hashCode(s string) int { + h := fnv.New32a() + h.Write([]byte(s)) + return int(h.Sum32()) +} + +func assertEqual(t *testing.T, a interface{}, b interface{}) { + if !reflect.DeepEqual(a, b) { + t.Fatalf("%v != %v", a, b) + } +}