Lets build a password generator we can actually use
For our next beginner project, we will build a password generator that doesn't only generate passwords but encrypts and saves them - so that it is actually functional.
We will be splitting our code into different files so we don't end up with a large "main.go" file.
First, we initialize a go project and create a "profile.go" file that will contain the logic for encrypting and decrypting passwords.
package main import ( "crypto/aes" "crypto/cipher" "crypto/rand" "encoding/hex" "errors" "io" ) // must be 32 characters var key = "askdjasjdbreonfsdfibsdhfgsdfhboo" var ErrMalformedEncryption = errors.New("malformed encryption") // password in small letters so it is not stored type profile struct { Enc, Platform, password string } func (p *profile) encrypt() error { block, err := aes.NewCipher([]byte(key)) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } nonce := make([]byte, gcm.NonceSize()) if _, err := io.ReadFull(rand.Reader, nonce); err != nil { return err } enc := gcm.Seal(nonce, nonce, []byte(p.password), nil) p.Enc = hex.EncodeToString(enc) return nil } func (p *profile) decrypt() error { block, err := aes.NewCipher([]byte(key)) if err != nil { return err } gcm, err := cipher.NewGCM(block) if err != nil { return err } nsize := gcm.NonceSize() if len(p.Enc) < nsize { return ErrMalformedEncryption } enc, err := hex.DecodeString(p.Enc) if err != nil { return err } password, err := gcm.Open(nil, enc[:nsize], enc[nsize:], nil) if err != nil { return err } p.password = string(password) return nil }
Here we create a profile struct that has 3 fields - Enc, Platform and password. Enc will hold the encrypted password, the service we are generating the password for will be stored in Platform and password will hold the actual generated password. The profile struct has 2 methods "encrypt" and "decrypt". We use AES - a symmetric key encryption algorithm to encrypt and decrypt our password.
Next we create a "store.go" file that contains the logic for storing and retrieving passwords.
package main import ( "encoding/gob" "errors" "os" "sync" ) const filename = "profile.bin" var ( ErrInvalidArgs = errors.New("invalid args") ErrNotFound = errors.New("not found") ) type store struct { sync.RWMutex data map[string]*profile } func newStore() (*store, error) { s := &store{ data: make(map[string]*profile), } if err := s.load(); err != nil { return nil, err } return s, nil } func (s *store) load() error { flags := os.O_CREATE | os.O_RDONLY f, err := os.OpenFile(filename, flags, 0644) if err != nil { return err } defer f.Close() info, err := f.Stat() if err != nil { return err } if info.Size() == 0 { return nil } return gob.NewDecoder(f).Decode(&s.data) } func (s *store) save() error { f, err := os.OpenFile(filename, os.O_WRONLY, 0644) if err != nil { return err } defer f.Close() return gob.NewEncoder(f).Encode(s.data) } func (s *store) find(platform string) (string, error) { s.RLock() defer s.RUnlock() p, ok := s.data[platform] if !ok { return "", ErrNotFound } if err := p.decrypt(); err != nil { return "", err } return p.password, nil } func (s *store) add(platform, password string) error { if platform == "" { return ErrInvalidArgs } p := &profile{ Platform: platform, password: password, } if err := p.encrypt(); err != nil { return err } s.Lock() defer s.Unlock() s.data[platform] = p return s.save() }
We chose gob files for storage because they are not exactly human readable. If the file ever gets exposed, your passwords are safe because they will be encrypted and really difficult to read. The store struct contains methods for loading, finding and saving to the gob file. We save the passwords in a dictionary. We also use a mutex to make the dictionary concurrent safe. An important thing to note is that we will not be storing the plain generated password - we will store its encrypted value instead.
Now let's write a couple of functions that will actually generate the passwords. Create a "password.go" file and type the following
package main import ( "math" "math/rand" "slices" "strings" ) const ( half = .5 onethird = .3 onefourth = .25 ) var ( randlowers = randFromSeed(lowers()) randuppers = randFromSeed(uppers()) randdigits = randFromSeed(digits()) randsymbols = randFromSeed(symbols()) ) var basicPassword = randlowers func mediumPassword(n int) string { frac := math.Round(float64(n) * half) pwd := basicPassword(n) return pwd[:n-int(frac)] + randuppers(int(frac)) } func hardPassword(n int) string { pwd := mediumPassword(n) frac := math.Round(float64(n) * onethird) return pwd[:n-int(frac)] + randdigits(int(frac)) } func xhardPassword(n int) string { pwd := hardPassword(n) frac := math.Round(float64(n) * onefourth) return pwd[:n-int(frac)] + randsymbols(int(frac)) } func randFromSeed(seed string) func(int) string { return func(n int) string { var b strings.Builder for range n { b.WriteByte(seed[rand.Intn(len(seed))]) } return b.String() } } func lowers() string { var b strings.Builder for i := 'a'; i < 'a'+26; i++ { b.WriteRune(i) } return b.String() } func uppers() string { var b strings.Builder for i := 'A'; i < 'A'+26; i++ { b.WriteRune(i) } return b.String() } func symbols() string { var b strings.Builder for i := '!'; i < '!'+14; i++ { b.WriteRune(i) } for i := ':'; i < ':'+6; i++ { b.WriteRune(i) } for i := '['; i < '['+5; i++ { b.WriteRune(i) } for i := '{'; i < '{'+3; i++ { b.WriteRune(i) } return b.String() } func digits() string { var b strings.Builder for i := '0'; i < '0'+9; i++ { b.WriteRune(i) } return b.String() } func shuffle[T any](ts []T) []T { cloned := slices.Clone(ts) rand.Shuffle(len(cloned), func(i, j int) { cloned[i], cloned[j] = cloned[j], cloned[i] }) return cloned } func shuffleStr(s string) string { return strings.Join(shuffle(strings.Split(s, "")), "") }
Here we've written functions that generates passwords at different levels of difficulty. The basicPassword function generates random small letter strings. The mediumPassword function takes a fraction of the characters from basicPassword function and adds random capital letters to it. The hardPassword function does the same for mediumPassword but adds digits to it. The xhardPassword does the same and adds symbols. The shuffle function does exactly what you'd expect on slices while shuffleStr shuffles strings.
Now let's put everything together. Create a "main.go" file and type the following
package main import ( "errors" "flag" "fmt" "log" "regexp" "strconv" "strings" ) var usage = ` Usage ----- --get platform=[string] - Gets saved password for a platform --set platform=[string] len=[int] level=(basic|medium|hard|xhard) - Creates and saves a password ` var ErrUsage = errors.New(usage) var pattern = regexp.MustCompile(`\S+=\S+`) type level int const ( _ level = iota level_basic level_medium level_hard level_xhard ) var level_key = map[string]level{ "basic": level_basic, "medium": level_medium, "hard": level_hard, "xhard": level_xhard, } type commands struct { get, set bool } func createCommands() (c commands) { flag.BoolVar(&c.get, "get", false, "get password for platform") flag.BoolVar(&c.set, "set", false, "set password for platform") flag.Parse() return } func (c commands) exec(store *store) (string, error) { switch { case c.get: return c.getPassword(store) case c.set: return c.setPassword(store) default: return "", ErrUsage } } func (c commands) getPassword(store *store) (string, error) { params, err := c.parse() if err != nil { return "", err } return store.find(params["platform"]) } func (c commands) setPassword(store *store) (string, error) { params, err := c.parse() if err != nil { return "", err } var password string n, err := strconv.Atoi(params["len"]) if err != nil { return "", err } if n < 8 { return "", fmt.Errorf("password len cannot be less than 8") } switch level_key[params["level"]] { case level_basic: password = basicPassword(n) case level_medium: password = mediumPassword(n) case level_hard: password = hardPassword(n) case level_xhard: password = xhardPassword(n) default: return "", ErrUsage } password = shuffleStr(password) if err := store.add(params["platform"], password); err != nil { return "", err } return password, nil } func (c commands) parse() (map[string]string, error) { args := flag.Args() if len(args) == 0 { return nil, ErrUsage } params := make(map[string]string) for i := range args { if !pattern.MatchString(args[i]) { return nil, ErrUsage } parts := strings.Split(args[i], "=") params[parts[0]] = parts[1] } return params, nil } func main() { store, err := newStore() if err != nil { log.Fatalf("could not initialize store: %v", err) } c := createCommands() password, err := c.exec(store) if err != nil { log.Fatalf("could not execute flag commands: %v", err) } fmt.Printf("password: %s\n", password) }
We used flags to specify how we expect the application to behave. "--get" to get a password and "--set" to generate and save a password. To set a password, the user provides arguments with the flags to instruct the application on the type of password to generate and save. To get a password, the user also provides arguments to specify the password to retrieve.
You can now run "go build" to build a binary and test the application.
The above is the detailed content of Lets build a password generator we can actually use. For more information, please follow other related articles on the PHP Chinese website!

Hot AI Tools

Undresser.AI Undress
AI-powered app for creating realistic nude photos

AI Clothes Remover
Online AI tool for removing clothes from photos.

Undress AI Tool
Undress images for free

Clothoff.io
AI clothes remover

Video Face Swap
Swap faces in any video effortlessly with our completely free AI face swap tool!

Hot Article

Hot Tools

Notepad++7.3.1
Easy-to-use and free code editor

SublimeText3 Chinese version
Chinese version, very easy to use

Zend Studio 13.0.1
Powerful PHP integrated development environment

Dreamweaver CS6
Visual web development tools

SublimeText3 Mac version
God-level code editing software (SublimeText3)

Hot Topics





OpenSSL, as an open source library widely used in secure communications, provides encryption algorithms, keys and certificate management functions. However, there are some known security vulnerabilities in its historical version, some of which are extremely harmful. This article will focus on common vulnerabilities and response measures for OpenSSL in Debian systems. DebianOpenSSL known vulnerabilities: OpenSSL has experienced several serious vulnerabilities, such as: Heart Bleeding Vulnerability (CVE-2014-0160): This vulnerability affects OpenSSL 1.0.1 to 1.0.1f and 1.0.2 to 1.0.2 beta versions. An attacker can use this vulnerability to unauthorized read sensitive information on the server, including encryption keys, etc.

The library used for floating-point number operation in Go language introduces how to ensure the accuracy is...

Queue threading problem in Go crawler Colly explores the problem of using the Colly crawler library in Go language, developers often encounter problems with threads and request queues. �...

This article introduces a variety of methods and tools to monitor PostgreSQL databases under the Debian system, helping you to fully grasp database performance monitoring. 1. Use PostgreSQL to build-in monitoring view PostgreSQL itself provides multiple views for monitoring database activities: pg_stat_activity: displays database activities in real time, including connections, queries, transactions and other information. pg_stat_replication: Monitors replication status, especially suitable for stream replication clusters. pg_stat_database: Provides database statistics, such as database size, transaction commit/rollback times and other key indicators. 2. Use log analysis tool pgBadg

Backend learning path: The exploration journey from front-end to back-end As a back-end beginner who transforms from front-end development, you already have the foundation of nodejs,...

The difference between string printing in Go language: The difference in the effect of using Println and string() functions is in Go...

The problem of using RedisStream to implement message queues in Go language is using Go language and Redis...

Under the BeegoORM framework, how to specify the database associated with the model? Many Beego projects require multiple databases to be operated simultaneously. When using Beego...
