Skip to content

Commit

Permalink
first commit
Browse files Browse the repository at this point in the history
  • Loading branch information
Mobilpadde committed Apr 28, 2022
0 parents commit 5fe638c
Show file tree
Hide file tree
Showing 7 changed files with 207 additions and 0 deletions.
41 changes: 41 additions & 0 deletions README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
# moths

> e**mo**ji a**ths**
# what

Emojies as TOTP

# why

Becasue why not?

# how

Runnig this is quite easy, just run the command:

Download the dependencies:

```sh
go mod download
```

Next-up start the program

```sh
go run .
```

As I said, easy-peasy!

# sample

A sample out-put might be `😻🙀😺🙀🙀` which would equal the `920811` TOTP-token. Using the super secret secret specified in [`./secret.pem`](./secret.pem) - genereated at `2022/04/28 02:05:42` and a `5`-second interval.

# shoutout

I couldn't have done it without the lovely OSS, listed below:

- https://github.com/aidarkhanov/nanoid
- https://github.com/enescakir/emoji
- https://github.com/tilaklodha/google-authenticator
65 changes: 65 additions & 0 deletions generator.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,65 @@
package main

import (
"encoding/binary"
"fmt"
"math"
"math/bits"
"math/rand"
"strings"
)

// randomBufferFromHOTP generates a random buffer from a HOTP token.
//
// /!\ Modified /!\
//
// Proudly stolen from https://stackoverflow.com/a/48307199/754471
func randomBufferFromHOTP(otp string) func(step int) ([]byte, error) {
buf := []byte(fmt.Sprintf("%08s", otp))
seed := int64(binary.BigEndian.Uint64(buf))
r := rand.New(rand.NewSource(seed))

return func(step int) ([]byte, error) {
buffer := make([]byte, step)
if _, err := r.Read(buffer); err != nil {
return nil, err
}
return buffer, nil
}
}

// MothsGenerator is essentially the nanoid generator with a few modifications.
//
// /!\ Modified /!\
//
// https://github.com/aidarkhanov/nanoid/blob/master/nanoid.go
func MothsGenerator(token string, numEmojies int, alphabet ...string) (string, error) {
emojiBytes := 4
size := emojiBytes * numEmojies

mask := 2<<uint32(31-bits.LeadingZeros32(uint32(len(alphabet)-1|1))) - 1
step := int(math.Ceil(1.6 * float64(mask*size) / float64(len(alphabet))))

id := new(strings.Builder)
id.Grow(size)

buffer := randomBufferFromHOTP(token)
for {
randomBuffer, err := buffer(step)
if err != nil {
return "", err
}

for i := 0; i < step; i++ {
currentIndex := int(randomBuffer[i]) & mask

if currentIndex < len(alphabet) {
if _, err := id.WriteString(alphabet[currentIndex]); err != nil {
return "", err
} else if id.Len() == size {
return id.String(), nil
}
}
}
}
}
5 changes: 5 additions & 0 deletions go.mod
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module moths

go 1.18

require github.com/enescakir/emoji v1.0.0
2 changes: 2 additions & 0 deletions go.sum
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
github.com/enescakir/emoji v1.0.0 h1:W+HsNql8swfCQFtioDGDHCHri8nudlK1n5p2rHCJoog=
github.com/enescakir/emoji v1.0.0/go.mod h1:Bt1EKuLnKDTYpLALApstIkAjdDrS/8IAgTkKp+WKFD0=
52 changes: 52 additions & 0 deletions hotp.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package main

import (
"bytes"
"crypto/hmac"
"crypto/sha1"
"encoding/binary"
"fmt"
"log"
"strconv"
)

// GetHOTPToken returns a HOTP token for the given secret and interval.
//
// /!\ Modified /!\
//
// https://github.com/tilaklodha/google-authenticator/blob/master/google_authenticator.go
func GetHOTPToken(secret string, interval int64) string {
key := []byte(fmt.Sprintf("%08s", secret))

bs := make([]byte, 8)
binary.BigEndian.PutUint64(bs, uint64(interval))

// Signing the value using HMAC-SHA1 Algorithm
hash := hmac.New(sha1.New, key)
hash.Write(bs)
h := hash.Sum(nil)

// We're going to use a subset of the generated hash.
// Using the last nibble (half-byte) to choose the index to start from.
// This number is always appropriate as it's maximum decimal 15, the hash will
// have the maximum index 19 (20 bytes of SHA1) and we need 4 bytes.
o := (h[19] & 15)

var header uint32
// Get 32 bit chunk from hash starting at the o
r := bytes.NewReader(h[o : o+4])
err := binary.Read(r, binary.BigEndian, &header)
if err != nil {
log.Fatalln(err)
}

// Ignore most significant bits as per RFC 4226.
// Takes division from one million to generate a remainder less than < 7 digits
h12 := (int(header) & 0x7fffffff) % 1000000

// Converts number as a string
otp := strconv.Itoa(int(h12))

// Padding with 0s to the left if the OTP is less than 8 digits
return fmt.Sprintf("%06s", otp)
}
41 changes: 41 additions & 0 deletions main.go
Original file line number Diff line number Diff line change
@@ -0,0 +1,41 @@
package main

import (
"io/ioutil"
"log"
"time"

"github.com/enescakir/emoji"
)

func main() {
log.SetFlags(log.Llongfile | log.LstdFlags)

data, err := ioutil.ReadFile("secret.pem")
if err != nil {
log.Fatalln(err)
}

secret := string(data)
interval := time.Now().Unix() / 5

otp := GetHOTPToken(secret, interval)
moth, err := MothsGenerator(
otp,
5,
emoji.GrinningCat.String(),
emoji.GrinningCatWithSmilingEyes.String(),
emoji.CatWithTearsOfJoy.String(),
emoji.SmilingCatWithHeartEyes.String(),
emoji.CatWithWrySmile.String(),
emoji.KissingCat.String(),
emoji.WearyCat.String(),
emoji.CryingCat.String(),
emoji.PoutingCat.String(),
)
if err != nil {
log.Fatalln(err)
}

log.Printf("moth-otp: %s => %s\n", moth, otp)
}
1 change: 1 addition & 0 deletions secret.pem
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
😻

0 comments on commit 5fe638c

Please sign in to comment.