-
Notifications
You must be signed in to change notification settings - Fork 22
/
Copy pathsalt.go
148 lines (130 loc) · 3.23 KB
/
salt.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
// (C) Copyright 2012, Jeramey Crawford <[email protected]>. All
// rights reserved. Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
package common
import (
"bytes"
"crypto/rand"
"errors"
"strconv"
)
var (
ErrSaltPrefix = errors.New("invalid magic prefix")
ErrSaltFormat = errors.New("invalid salt format")
ErrSaltRounds = errors.New("invalid rounds")
)
const (
roundsPrefix = "rounds="
)
// Salt represents a salt.
type Salt struct {
MagicPrefix []byte
SaltLenMin int
SaltLenMax int
RoundsMin int
RoundsMax int
RoundsDefault int
}
// Generate generates a random salt of a given length.
//
// The length is set thus:
//
// length > SaltLenMax: length = SaltLenMax
// length < SaltLenMin: length = SaltLenMin
func (s *Salt) Generate(length int) []byte {
if length > s.SaltLenMax {
length = s.SaltLenMax
} else if length < s.SaltLenMin {
length = s.SaltLenMin
}
saltLen := (length * 6 / 8)
if (length*6)%8 != 0 {
saltLen += 1
}
salt := make([]byte, saltLen)
rand.Read(salt)
out := make([]byte, len(s.MagicPrefix)+length)
copy(out, s.MagicPrefix)
copy(out[len(s.MagicPrefix):], Base64_24Bit(salt))
return out
}
// GenerateWRounds creates a random salt with the random bytes being of the
// length provided, and the rounds parameter set as specified.
//
// The parameters are set thus:
//
// length > SaltLenMax: length = SaltLenMax
// length < SaltLenMin: length = SaltLenMin
//
// rounds < 0: rounds = RoundsDefault
// rounds < RoundsMin: rounds = RoundsMin
// rounds > RoundsMax: rounds = RoundsMax
//
// If rounds is equal to RoundsDefault, then the "rounds=" part of the salt is
// removed.
func (s *Salt) GenerateWRounds(length, rounds int) []byte {
if length > s.SaltLenMax {
length = s.SaltLenMax
} else if length < s.SaltLenMin {
length = s.SaltLenMin
}
if rounds < 0 {
rounds = s.RoundsDefault
} else if rounds < s.RoundsMin {
rounds = s.RoundsMin
} else if rounds > s.RoundsMax {
rounds = s.RoundsMax
}
saltLen := (length * 6 / 8)
if (length*6)%8 != 0 {
saltLen += 1
}
salt := make([]byte, saltLen)
rand.Read(salt)
roundsText := ""
if rounds != s.RoundsDefault {
roundsText = roundsPrefix + strconv.Itoa(rounds) + "$"
}
out := make([]byte, len(s.MagicPrefix)+len(roundsText)+length)
copy(out, s.MagicPrefix)
copy(out[len(s.MagicPrefix):], []byte(roundsText))
copy(out[len(s.MagicPrefix)+len(roundsText):], Base64_24Bit(salt))
return out
}
func (s *Salt) Decode(raw []byte) (salt []byte, rounds int, isRoundsDef bool, rest []byte, err error) {
tokens := bytes.SplitN(raw, []byte{'$'}, 4)
if len(tokens) < 3 {
err = ErrSaltFormat
return
}
if !bytes.HasPrefix(raw, s.MagicPrefix) {
err = ErrSaltPrefix
return
}
if bytes.HasPrefix(tokens[2], []byte(roundsPrefix)) {
if len(tokens) < 4 {
err = ErrSaltFormat
return
}
salt = tokens[3]
rounds, err = strconv.Atoi(string(tokens[2][len(roundsPrefix):]))
if err != nil {
err = ErrSaltRounds
return
}
if rounds < s.RoundsMin {
rounds = s.RoundsMin
}
if rounds > s.RoundsMax {
rounds = s.RoundsMax
}
isRoundsDef = true
} else {
salt = tokens[2]
rounds = s.RoundsDefault
}
if len(salt) > s.SaltLenMax {
salt = salt[0:s.SaltLenMax]
}
return
}