| 72 | } |
| 73 | |
| 74 | func hashCommand(ctx *cli.Context) error { |
| 75 | hashFunc := ctx.String("hash") |
| 76 | if hashFunc == "" { |
| 77 | hashFunc = pass_table.DefaultHash |
| 78 | } |
| 79 | |
| 80 | hashCompute := pass_table.HashCompute[hashFunc] |
| 81 | if hashCompute == nil { |
| 82 | funcs := make([]string, 0, len(pass_table.HashCompute)) |
| 83 | for k := range pass_table.HashCompute { |
| 84 | funcs = append(funcs, k) |
| 85 | } |
| 86 | |
| 87 | return cli.Exit(fmt.Sprintf("Error: Unknown hash function, available: %s", strings.Join(funcs, ", ")), 2) |
| 88 | } |
| 89 | |
| 90 | opts := pass_table.HashOpts{ |
| 91 | BcryptCost: bcrypt.DefaultCost, |
| 92 | Argon2Memory: 1024, |
| 93 | Argon2Time: 2, |
| 94 | Argon2Threads: 1, |
| 95 | } |
| 96 | if ctx.IsSet("bcrypt-cost") { |
| 97 | if ctx.Int("bcrypt-cost") > bcrypt.MaxCost { |
| 98 | return cli.Exit("Error: too big bcrypt cost", 2) |
| 99 | } |
| 100 | if ctx.Int("bcrypt-cost") < bcrypt.MinCost { |
| 101 | return cli.Exit("Error: too small bcrypt cost", 2) |
| 102 | } |
| 103 | opts.BcryptCost = ctx.Int("bcrypt-cost") |
| 104 | } |
| 105 | if ctx.IsSet("argon2-memory") { |
| 106 | opts.Argon2Memory = uint32(ctx.Int("argon2-memory")) |
| 107 | } |
| 108 | if ctx.IsSet("argon2-time") { |
| 109 | opts.Argon2Time = uint32(ctx.Int("argon2-time")) |
| 110 | } |
| 111 | if ctx.IsSet("argon2-threads") { |
| 112 | opts.Argon2Threads = uint8(ctx.Int("argon2-threads")) |
| 113 | } |
| 114 | |
| 115 | var pass string |
| 116 | if ctx.IsSet("password") { |
| 117 | pass = ctx.String("password") |
| 118 | } else { |
| 119 | var err error |
| 120 | pass, err = clitools2.ReadPassword("Password") |
| 121 | if err != nil { |
| 122 | return err |
| 123 | } |
| 124 | } |
| 125 | |
| 126 | if pass == "" { |
| 127 | fmt.Fprintln(os.Stderr, "WARNING: This is the hash of an empty string") |
| 128 | } |
| 129 | if strings.TrimSpace(pass) != pass { |
| 130 | fmt.Fprintln(os.Stderr, "WARNING: There is leading/trailing whitespace in the string") |
| 131 | } |