539 lines
12 KiB
Go
539 lines
12 KiB
Go
package passwd
|
|
|
|
import (
|
|
"encoding/base64"
|
|
"errors"
|
|
"hash"
|
|
)
|
|
|
|
// The non-standard alphabet for crypt base64 encoding.
|
|
const iota64Encoding = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
|
|
|
|
// Base64 to integer encoding table.
|
|
var atoi64Partial = [...]byte{
|
|
0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11,
|
|
64, 64, 64, 64, 64, 64, 64,
|
|
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,
|
|
64, 64, 64, 64, 64, 64,
|
|
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,
|
|
}
|
|
|
|
// Append base64 for provided uint.
|
|
func Base64Append(dst []byte, v uint, n int) []byte {
|
|
// Until we finish the number of rounds specified,
|
|
// loop and encode to base64.
|
|
for n > 0 {
|
|
// Append base64 of current bit.
|
|
dst = append(dst, iota64Encoding[v&0x3F])
|
|
// Jump to the next bit for encoding.
|
|
v >>= 6
|
|
n -= 1
|
|
}
|
|
// Return new byte array.
|
|
return dst
|
|
}
|
|
|
|
// Encode to crypt base64 with flawed logic that misses bytes.
|
|
// This is used for SHA1 crypt algorithm.
|
|
func SHA1Base64Encode(src []byte) []byte {
|
|
size := len(src)
|
|
var b64 []byte
|
|
var i int
|
|
for i = 0; i < size-3; i += 3 {
|
|
l := uint(src[i])<<16 |
|
|
uint(src[i+1])<<8 |
|
|
uint(src[i+2])
|
|
b64 = Base64Append(b64, l, 4)
|
|
}
|
|
var l uint
|
|
if size-i == 2 {
|
|
l = uint(src[i])<<16 |
|
|
uint(src[i+1])<<8 |
|
|
uint(src[0])
|
|
b64 = Base64Append(b64, l, 4)
|
|
}
|
|
return b64
|
|
}
|
|
|
|
// Encode to crypt base64.
|
|
func Base64Encode(src []byte) []byte {
|
|
size := len(src)
|
|
var b64 []byte
|
|
var i int
|
|
for i = 0; i <= size-3; i += 3 {
|
|
l := uint(src[i])<<16 |
|
|
uint(src[i+1])<<8 |
|
|
uint(src[i+2])
|
|
b64 = Base64Append(b64, l, 4)
|
|
}
|
|
remaining := size - i
|
|
if remaining == 2 {
|
|
l := uint(src[i])<<8 |
|
|
uint(src[i+1])
|
|
b64 = Base64Append(b64, l, 3)
|
|
} else if remaining == 1 {
|
|
l := uint(src[i])
|
|
b64 = Base64Append(b64, l, 2)
|
|
}
|
|
return b64
|
|
}
|
|
|
|
// Decode from crypt base64.
|
|
func Base64Decode(src []byte) ([]byte, error) {
|
|
size := len(src)
|
|
if size == 0 {
|
|
return []byte{}, nil
|
|
}
|
|
if size%4 == 1 {
|
|
return nil, errors.New("invalid base64 length")
|
|
}
|
|
|
|
// Output buffer guessing.
|
|
// We can't know exact size easily due to the legacy 4->2 behavior vs 4->3,
|
|
// but (size/4)*3 is a safe lower bound, capacity can be larger.
|
|
dst := make([]byte, 0, (size/4)*3+3)
|
|
var i int
|
|
for i = 0; i < size-4; i += 4 {
|
|
var v uint
|
|
for j := 0; j < 4; j++ {
|
|
c := AToI64(src[i+j])
|
|
if c > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
dst = append(dst, byte(v>>16), byte(v>>8), byte(v))
|
|
}
|
|
// Handle last block (can be partial)
|
|
remaining := size - i
|
|
var v uint
|
|
for j := 0; j < remaining; j++ {
|
|
c := AToI64(src[i+j])
|
|
if c > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
|
|
if remaining == 4 {
|
|
// 4 chars -> 3 bytes
|
|
dst = append(dst, byte(v>>16), byte(v>>8), byte(v))
|
|
} else if remaining == 3 {
|
|
// 3 chars -> 2 bytes
|
|
dst = append(dst, byte(v>>8), byte(v))
|
|
} else if remaining == 2 {
|
|
// 2 chars -> 1 byte
|
|
dst = append(dst, byte(v))
|
|
}
|
|
return dst, nil
|
|
}
|
|
|
|
// Takes a prior hash, and recycles bytes until the length provided is covered.
|
|
func HashBlockRecycle(h hash.Hash, block []byte, len int) {
|
|
size := h.BlockSize()
|
|
var cnt int
|
|
for cnt = len; cnt > size; cnt -= size {
|
|
h.Write(block)
|
|
}
|
|
// Remaining characters of the length, add sub slice here.
|
|
h.Write(block[:cnt])
|
|
}
|
|
|
|
// Convert base64 byte to integer value.
|
|
func AToI64(c byte) (val int) {
|
|
if c >= '.' && c <= 'z' {
|
|
val = int(atoi64Partial[c-'.'])
|
|
}
|
|
return
|
|
}
|
|
|
|
// Convert integer to bae64.
|
|
func IToA64(N int) (val byte, err error) {
|
|
if N > 64 {
|
|
err = errors.New("maximum itoa64 value is 64")
|
|
return
|
|
}
|
|
Nb := byte(N)
|
|
for i, b := range atoi64Partial {
|
|
if b == Nb {
|
|
val = '.' + byte(i)
|
|
}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Get the power of 2 value.
|
|
func N2log2(N uint64) (N_log2 int) {
|
|
if N < 2 {
|
|
return
|
|
}
|
|
|
|
// Find power by bit shifting until shifting results in 0.
|
|
N_log2 = 2
|
|
for N>>N_log2 != 0 {
|
|
N_log2++
|
|
}
|
|
N_log2--
|
|
|
|
// If the result of removing one power level ends up resulting in a shift of not 1, return 0.
|
|
if N>>N_log2 != 1 {
|
|
return 0
|
|
}
|
|
|
|
return
|
|
}
|
|
|
|
// Encode uint32 into base64 at a fixed length.
|
|
func Base64Uint32Encode(src, srcbits uint32) (b64 []byte) {
|
|
var bits uint32
|
|
|
|
for bits = 0; bits < srcbits; bits += 6 {
|
|
b64 = append(b64, iota64Encoding[src&0x3F])
|
|
src >>= 6
|
|
}
|
|
|
|
if src != 0 {
|
|
return []byte{}
|
|
}
|
|
return
|
|
}
|
|
|
|
// Decode uint32 from base64 at a fixed length.
|
|
func Base64Uint32Decode(src []byte, dstbits uint32) (dst uint32) {
|
|
var bits uint32
|
|
var i uint
|
|
for bits = 0; bits < dstbits; bits += 6 {
|
|
c := AToI64(src[i])
|
|
i++
|
|
if c > 63 {
|
|
return 0
|
|
}
|
|
dst |= uint32(c << bits)
|
|
}
|
|
return
|
|
}
|
|
|
|
// Encode base64 in the format used for SCrypt hashes.
|
|
func SCryptBase64Encode(src []byte) []byte {
|
|
dst := make([]byte, 0, (len(src)*8+5)/6)
|
|
for i := 0; i < len(src); {
|
|
var val uint32
|
|
var bits int32
|
|
for ; bits < 24 && i < len(src); bits += 8 {
|
|
val |= uint32(src[i]) << bits
|
|
i++
|
|
}
|
|
for ; bits > 0; bits -= 6 {
|
|
dst = append(dst, iota64Encoding[val&0x3F])
|
|
val >>= 6
|
|
}
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// Decode base64 in the format used for SCrypt hashes.
|
|
func SCryptBase64Decode(src []byte) []byte {
|
|
dst := make([]byte, 0, len(src)*3/4)
|
|
for i := 0; i < len(src); {
|
|
var val uint32
|
|
var bits int32
|
|
for ; bits < 24 && i < len(src); bits += 6 {
|
|
c := AToI64(src[i])
|
|
if c > 63 {
|
|
return nil
|
|
}
|
|
i++
|
|
val |= uint32(c) << bits
|
|
}
|
|
if bits < 12 {
|
|
return nil
|
|
}
|
|
for ; bits >= 8; bits -= 8 {
|
|
dst = append(dst, byte(val))
|
|
val >>= 8
|
|
}
|
|
if val != 0 {
|
|
return nil
|
|
}
|
|
}
|
|
return dst
|
|
}
|
|
|
|
// Encode MD5 result to MD5 crypt base64.
|
|
func MD5Base64Encode(src []byte) []byte {
|
|
// The way the crypt standards work with base64 encoding of MD5 is odd, because the
|
|
// last round rotates some of the hash bytes positions. So we must have this custom
|
|
// function just to encode MD5 hashes to base64.
|
|
var b64 []byte
|
|
l := uint(src[0])<<16 | uint(src[6])<<8 | uint(src[12])
|
|
b64 = Base64Append(b64, l, 4)
|
|
l = uint(src[1])<<16 | uint(src[7])<<8 | uint(src[13])
|
|
b64 = Base64Append(b64, l, 4)
|
|
l = uint(src[2])<<16 | uint(src[8])<<8 | uint(src[14])
|
|
b64 = Base64Append(b64, l, 4)
|
|
l = uint(src[3])<<16 | uint(src[9])<<8 | uint(src[15])
|
|
b64 = Base64Append(b64, l, 4)
|
|
l = uint(src[4])<<16 | uint(src[10])<<8 | uint(src[5])
|
|
b64 = Base64Append(b64, l, 4)
|
|
l = uint(src[11])
|
|
b64 = Base64Append(b64, l, 2)
|
|
return b64
|
|
}
|
|
|
|
// Decode MD5 result from MD5 crypt base64.
|
|
func MD5Base64Decode(src []byte) ([]byte, error) {
|
|
if len(src) != 22 {
|
|
return nil, errors.New("invalid MD5 base64 length")
|
|
}
|
|
|
|
dst := make([]byte, 16)
|
|
decode4 := func(b []byte) (uint, error) {
|
|
var v uint
|
|
for j := 0; j < 4; j++ {
|
|
c := AToI64(b[j])
|
|
if c > 63 {
|
|
return 0, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
return v, nil
|
|
}
|
|
|
|
var v uint
|
|
var err error
|
|
|
|
v, err = decode4(src[0:4])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dst[12], dst[6], dst[0] = byte(v), byte(v>>8), byte(v>>16)
|
|
|
|
v, err = decode4(src[4:8])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dst[13], dst[7], dst[1] = byte(v), byte(v>>8), byte(v>>16)
|
|
|
|
v, err = decode4(src[8:12])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dst[14], dst[8], dst[2] = byte(v), byte(v>>8), byte(v>>16)
|
|
|
|
v, err = decode4(src[12:16])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dst[15], dst[9], dst[3] = byte(v), byte(v>>8), byte(v>>16)
|
|
|
|
v, err = decode4(src[16:20])
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
dst[5], dst[10], dst[4] = byte(v), byte(v>>8), byte(v>>16)
|
|
|
|
// Last 2 characters
|
|
c0 := AToI64(src[20])
|
|
c1 := AToI64(src[21])
|
|
if c0 > 63 || c1 > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v = uint(c0) | uint(c1)<<6
|
|
dst[11] = byte(v)
|
|
|
|
return dst, nil
|
|
}
|
|
|
|
// The crypt standard likes to rotate bits in base64,
|
|
// although it doesn't really do anything for brute force protection.
|
|
// This performs the rotation algorithm.
|
|
func Base64RotateEncode(src []byte, order bool) []byte {
|
|
var b64 []byte
|
|
l := len(src)
|
|
// Setup indexes.
|
|
// Used for the loop.
|
|
i := 0
|
|
// Index A.
|
|
ia := 0
|
|
// Index C, should be byte length divided by 3 to ensure we start a the 3rd point.
|
|
ib := l / 3
|
|
// Index C is just B doubled.
|
|
ic := ib + ib
|
|
// Index D is used to determine which iteration we're on.
|
|
id := 0
|
|
// Loop until we reach the last index that fits all 3 values to b64.
|
|
for ; i < l-3; i += 3 {
|
|
var a, b, c int
|
|
// Depending on index D, rotate the A, B, and C indexes.
|
|
// I am not sure why we are rotating byte input, it doesn't do anything
|
|
// with regards to brute force protection. Someone can just reverse the
|
|
// byte order to decode the base64 back down to binary, then use the binary
|
|
// for brute force attacks.
|
|
if order {
|
|
switch id % 3 {
|
|
case 0:
|
|
a = ia
|
|
b = ib
|
|
c = ic
|
|
case 1:
|
|
a = ib
|
|
b = ic
|
|
c = ia
|
|
case 2:
|
|
a = ic
|
|
b = ia
|
|
c = ib
|
|
}
|
|
} else {
|
|
switch id % 3 {
|
|
case 0:
|
|
a = ia
|
|
b = ib
|
|
c = ic
|
|
case 1:
|
|
a = ic
|
|
b = ia
|
|
c = ib
|
|
case 2:
|
|
a = ib
|
|
b = ic
|
|
c = ia
|
|
}
|
|
}
|
|
|
|
// For this round, append the base64.
|
|
l := uint(src[a])<<16 | uint(src[b])<<8 | uint(src[c])
|
|
b64 = Base64Append(b64, l, 4)
|
|
|
|
// Increment the indexes.
|
|
ia++
|
|
ib++
|
|
ic++
|
|
id++
|
|
}
|
|
// For the remaining bytes, append as needed.
|
|
if l-i == 2 {
|
|
l := uint(0)<<16 | uint(src[l-1])<<8 | uint(src[l-2])
|
|
b64 = Base64Append(b64, l, 3)
|
|
} else {
|
|
l := uint(0)<<16 | uint(0)<<8 | uint(src[l-1])
|
|
b64 = Base64Append(b64, l, 2)
|
|
}
|
|
// Return the base64.
|
|
return b64
|
|
}
|
|
|
|
// Decode from rotated base64.
|
|
func Base64RotateDecode(src []byte, order bool) ([]byte, error) {
|
|
b64len := len(src)
|
|
var l int
|
|
if b64len%4 == 0 {
|
|
l = (b64len / 4) * 3
|
|
} else if b64len%4 == 3 {
|
|
l = (b64len/4)*3 + 2
|
|
} else if b64len%4 == 2 {
|
|
l = (b64len/4)*3 + 1
|
|
} else {
|
|
return nil, errors.New("invalid base64 length")
|
|
}
|
|
|
|
dst := make([]byte, l)
|
|
ia := 0
|
|
ib := l / 3
|
|
ic := ib + ib
|
|
id := 0
|
|
i := 0 // index in src (base64)
|
|
outIdx := 0 // index for original bytes loop (mimicking i in Encode)
|
|
|
|
// Process blocks of 4 -> 3
|
|
for ; outIdx < l-3; outIdx += 3 {
|
|
var v uint
|
|
for j := 0; j < 4; j++ {
|
|
c := AToI64(src[i+j])
|
|
if c > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
i += 4
|
|
|
|
var a, b, c int
|
|
if order {
|
|
switch id % 3 {
|
|
case 0:
|
|
a, b, c = ia, ib, ic
|
|
case 1:
|
|
a, b, c = ib, ic, ia
|
|
case 2:
|
|
a, b, c = ic, ia, ib
|
|
}
|
|
} else {
|
|
switch id % 3 {
|
|
case 0:
|
|
a, b, c = ia, ib, ic
|
|
case 1:
|
|
a, b, c = ic, ia, ib
|
|
case 2:
|
|
a, b, c = ib, ic, ia
|
|
}
|
|
}
|
|
dst[a] = byte(v >> 16)
|
|
dst[b] = byte(v >> 8)
|
|
dst[c] = byte(v)
|
|
ia++
|
|
ib++
|
|
ic++
|
|
id++
|
|
}
|
|
|
|
// Process remainder
|
|
if l-outIdx == 2 {
|
|
var v uint
|
|
for j := 0; j < 3; j++ {
|
|
c := AToI64(src[i+j])
|
|
if c > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
dst[l-1] = byte(v >> 8)
|
|
dst[l-2] = byte(v)
|
|
} else if l-outIdx == 1 {
|
|
var v uint
|
|
for j := 0; j < 2; j++ {
|
|
c := AToI64(src[i+j])
|
|
if c > 63 {
|
|
return nil, errors.New("invalid character in base64")
|
|
}
|
|
v |= uint(c) << (uint(j) * 6)
|
|
}
|
|
dst[l-1] = byte(v)
|
|
}
|
|
return dst, nil
|
|
}
|
|
|
|
// BCrypt specific alphabet.
|
|
const bcryptAlphabet = "./ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"
|
|
|
|
var bcryptEncoding = base64.NewEncoding(bcryptAlphabet).WithPadding(base64.NoPadding)
|
|
|
|
// Encode to bcrypt base64.
|
|
func BCryptBase64Encode(src []byte) []byte {
|
|
n := bcryptEncoding.EncodedLen(len(src))
|
|
dst := make([]byte, n)
|
|
bcryptEncoding.Encode(dst, src)
|
|
return dst
|
|
}
|
|
|
|
// Decode from bcrypt base64.
|
|
func BCryptBase64Decode(src []byte) ([]byte, error) {
|
|
dst := make([]byte, bcryptEncoding.DecodedLen(len(src)))
|
|
n, err := bcryptEncoding.Decode(dst, src)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
return dst[:n], nil
|
|
}
|