Skip to content

Commit

Permalink
Use proquints for recovery codes (#41385)
Browse files Browse the repository at this point in the history
  • Loading branch information
espadolini authored May 9, 2024
1 parent 2af284d commit 9c1bf48
Show file tree
Hide file tree
Showing 4 changed files with 68 additions and 12 deletions.
1 change: 0 additions & 1 deletion go.mod
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,6 @@ require (
github.com/schollz/progressbar/v3 v3.14.1
github.com/scim2/filter-parser/v2 v2.2.0
github.com/segmentio/parquet-go v0.0.0-20230622230624-510764ae9e80
github.com/sethvargo/go-diceware v0.3.0
github.com/sigstore/cosign/v2 v2.2.4
github.com/sigstore/sigstore v1.8.3
github.com/sijms/go-ora/v2 v2.7.9
Expand Down
2 changes: 0 additions & 2 deletions go.sum
Original file line number Diff line number Diff line change
Expand Up @@ -1411,8 +1411,6 @@ github.com/segmentio/parquet-go v0.0.0-20230622230624-510764ae9e80 h1:d09YiLivaP
github.com/segmentio/parquet-go v0.0.0-20230622230624-510764ae9e80/go.mod h1:+J0xQnJjm8DuQUHBO7t57EnmPbstT6+b45+p3DC9k1Q=
github.com/sergi/go-diff v1.3.1 h1:xkr+Oxo4BOQKmkn/B9eMK0g5Kg/983T9DqqPHwYqD+8=
github.com/sergi/go-diff v1.3.1/go.mod h1:aMJSSKb2lpPvRNec0+w3fl7LP9IOFzdc9Pa4NFbPK1I=
github.com/sethvargo/go-diceware v0.3.0 h1:UVVEfmN/uF50JfWAN7nbY6CiAlp5xeSx+5U0lWKkMCQ=
github.com/sethvargo/go-diceware v0.3.0/go.mod h1:lH5Q/oSPMivseNdhMERAC7Ti5oOPqsaVddU1BcN1CY0=
github.com/shabbyrobe/gocovmerge v0.0.0-20180507124511-f6ea450bfb63/go.mod h1:n+VKSARF5y/tS9XFSP7vWDfS+GUC5vs/YT7M5XDTUEM=
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500 h1:WnNuhiq+FOY3jNj6JXFT+eLN3CQ/oPIsDPRanvwsmbI=
github.com/shabbyrobe/gocovmerge v0.0.0-20190829150210-3e036491d500/go.mod h1:+njLrG5wSeoG4Ds61rFgEzKvenR2UHbjMoDHsczxly0=
Expand Down
44 changes: 35 additions & 9 deletions lib/auth/accountrecovery.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,11 +20,12 @@ package auth

import (
"context"
"crypto/rand"
"encoding/binary"
"net/mail"
"strings"

"github.com/gravitational/trace"
"github.com/sethvargo/go-diceware/diceware"
"github.com/sirupsen/logrus"
"golang.org/x/crypto/bcrypt"

Expand Down Expand Up @@ -479,20 +480,45 @@ func (a *Server) isAccountRecoveryAllowed(ctx context.Context) error {
// generateRecoveryCodes returns an array of tokens where each token
// have 8 random words prefixed with tele and concanatenated with dashes.
func generateRecoveryCodes() ([]string, error) {
gen, err := diceware.NewGenerator(nil /* use default word list */)
if err != nil {
return nil, trace.Wrap(err)
}
tokenList := make([]string, 0, numOfRecoveryCodes)

tokenList := make([]string, numOfRecoveryCodes)
for i := 0; i < numOfRecoveryCodes; i++ {
list, err := gen.Generate(numWordsInRecoveryCode)
if err != nil {
wordIDs := make([]uint16, numWordsInRecoveryCode)
if err := binary.Read(rand.Reader, binary.NativeEndian, wordIDs); err != nil {
return nil, trace.Wrap(err)
}

tokenList[i] = "tele-" + strings.Join(list, "-")
words := make([]string, 0, 1+len(wordIDs))
words = append(words, "tele")
for _, id := range wordIDs {
words = append(words, encodeProquint(id))
}

tokenList = append(tokenList, strings.Join(words, "-"))
}

return tokenList, nil
}

// encodeProquint returns a five-letter word based on a uint16.
// This proquint implementation is adapted from upspin.io:
// https://github.com/upspin/upspin/blob/master/key/proquint/proquint.go
// For the algorithm, see https://arxiv.org/html/0901.4016
func encodeProquint(x uint16) string {
const consonants = "bdfghjklmnprstvz"
const vowels = "aiou"

cons3 := x & 0b1111
vow2 := (x >> 4) & 0b11
cons2 := (x >> 6) & 0b1111
vow1 := (x >> 10) & 0b11
cons1 := x >> 12

return string([]byte{
consonants[cons1],
vowels[vow1],
consonants[cons2],
vowels[vow2],
consonants[cons3],
})
}
33 changes: 33 additions & 0 deletions lib/auth/accountrecovery_test.go
Original file line number Diff line number Diff line change
Expand Up @@ -20,9 +20,11 @@ package auth

import (
"context"
"encoding/binary"
"encoding/hex"
"fmt"
"math/rand"
"net/netip"
"strings"
"testing"
"time"
Expand Down Expand Up @@ -1353,3 +1355,34 @@ func createUserWithSecondFactors(testServer *TestTLSServer) (*userAuthCreds, err
webDev: webDev,
}, nil
}

func TestProquint(t *testing.T) {
t.Parallel()

// source: https://arxiv.org/html/0901.4016
proquintTestCases := []struct{ address, proquint string }{
{"127.0.0.1", "lusab-babad"},
{"63.84.220.193", "gutih-tugad"},
{"63.118.7.35", "gutuk-bisog"},
{"140.98.193.141", "mudof-sakat"},
{"64.255.6.200", "haguz-biram"},
{"128.30.52.45", "mabiv-gibot"},
{"147.67.119.2", "natag-lisaf"},
{"212.58.253.68", "tibup-zujah"},
{"216.35.68.215", "tobog-higil"},
{"216.68.232.21", "todah-vobij"},
{"198.81.129.136", "sinid-makam"},
{"12.110.110.204", "budov-kuras"},
}

for _, tc := range proquintTestCases {
addr, err := netip.ParseAddr(tc.address)
require.NoError(t, err)
require.True(t, addr.Is4())
addr4 := addr.As4()

hi, lo := binary.BigEndian.Uint16(addr4[:2]), binary.BigEndian.Uint16(addr4[2:])
proquint := encodeProquint(hi) + "-" + encodeProquint(lo)
require.Equal(t, tc.proquint, proquint, "wrong encoding for address %v", addr)
}
}

0 comments on commit 9c1bf48

Please sign in to comment.