diff --git a/server/plugin/plg_authenticate_htpasswd/index.go b/server/plugin/plg_authenticate_htpasswd/index.go index 12f84011..5849af32 100644 --- a/server/plugin/plg_authenticate_htpasswd/index.go +++ b/server/plugin/plg_authenticate_htpasswd/index.go @@ -6,11 +6,12 @@ import ( "encoding/base64" "fmt" . "github.com/mickael-kerjean/filestash/server/common" - "github.com/tredoe/osutil/user/crypt" - "github.com/tredoe/osutil/user/crypt/apr1_crypt" - "github.com/tredoe/osutil/user/crypt/md5_crypt" - "github.com/tredoe/osutil/user/crypt/sha256_crypt" - "github.com/tredoe/osutil/user/crypt/sha512_crypt" + + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/apr1_crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/md5_crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/sha256_crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/sha512_crypt" "net/http" "strings" ) diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/AUTHORS.md b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/AUTHORS.md new file mode 100644 index 00000000..8eb23dd0 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/AUTHORS.md @@ -0,0 +1,8 @@ +### Initial author + +[Jeramey Crawford](https://github.com/jeramey) + +### Other authors + +[Jonas mg](https://github.com/tredoe) + diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/LICENSE b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/LICENSE new file mode 100644 index 00000000..c39e0de5 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/LICENSE @@ -0,0 +1,27 @@ +Copyright (c) 2012, Jeramey Crawford +Copyright (c) 2013, Jonas mg +All rights reserved. + +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions are +met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + + * Redistributions in binary form must reproduce the above copyright + notice, this list of conditions and the following disclaimer in + the documentation and/or other materials provided with the + distribution. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +(INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/README.md b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/README.md new file mode 100644 index 00000000..cbcfb2e5 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/README.md @@ -0,0 +1,25 @@ +crypt +===== +A password hashing library. + +The goal of crypt is to bring a library of many common and popular password +hashing algorithms to Go and to provide a simple and consistent interface to +each of them. As every hashing method is implemented in pure Go, this library +should be as portable as Go itself. + +All hashing methods come with a test suite which verifies their operation +against itself as well as the output of other password hashing implementations +to ensure compatibility with them. + +I hope you find this library to be useful and easy to use! + +Note: forked from + +## Installation + + go get github.com/tredoe/osutil/user/crypt + +## License + +The source files are distributed under a BSD-style license that can be found +in the LICENSE file. diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/apr1_crypt/apr1_crypt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/apr1_crypt/apr1_crypt.go new file mode 100644 index 00000000..2fc221d6 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/apr1_crypt/apr1_crypt.go @@ -0,0 +1,62 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// Package apr1_crypt implements the standard Unix MD5-crypt algorithm created +// by Poul-Henning Kamp for FreeBSD, and modified by the Apache project. +// +// The only change from MD5-crypt is the use of the magic constant "$apr1$" +// instead of "$1$". The algorithms are otherwise identical. +package apr1_crypt + +import ( + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/common" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/md5_crypt" +) + +func init() { + crypt.RegisterCrypt(crypt.APR1, New, MagicPrefix) +} + +const ( + MagicPrefix = "$apr1$" + SaltLenMin = 1 + SaltLenMax = 8 + RoundsDefault = 1000 +) + +var md5Crypt = md5_crypt.New() + +func init() { + md5Crypt.SetSalt(GetSalt()) +} + +type crypter struct{ Salt common.Salt } + +// New returns a new crypt.Crypter computing the variant "apr1" of MD5-crypt +func New() crypt.Crypter { return &crypter{common.Salt{}} } + +func (c *crypter) Generate(key, salt []byte) (string, error) { + return md5Crypt.Generate(key, salt) +} + +func (c *crypter) Verify(hashedKey string, key []byte) error { + return md5Crypt.Verify(hashedKey, key) +} + +func (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil } + +func (c *crypter) SetSalt(salt common.Salt) {} + +func GetSalt() common.Salt { + return common.Salt{ + MagicPrefix: []byte(MagicPrefix), + SaltLenMin: SaltLenMin, + SaltLenMax: SaltLenMax, + RoundsDefault: RoundsDefault, + } +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/base64.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/base64.go new file mode 100644 index 00000000..ed057d5c --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/base64.go @@ -0,0 +1,60 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// 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 + +const alphabet = "./0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz" + +// Base64_24Bit is a variant of Base64 encoding, commonly used with password +// hashing algorithms to encode the result of their checksum output. +// +// The algorithm operates on up to 3 bytes at a time, encoding the following +// 6-bit sequences into up to 4 hash64 ASCII bytes. +// +// 1. Bottom 6 bits of the first byte +// 2. Top 2 bits of the first byte, and bottom 4 bits of the second byte. +// 3. Top 4 bits of the second byte, and bottom 2 bits of the third byte. +// 4. Top 6 bits of the third byte. +// +// This encoding method does not emit padding bytes as Base64 does. +func Base64_24Bit(src []byte) (hash []byte) { + if len(src) == 0 { + return []byte{} // TODO: return nil + } + + hashSize := (len(src) * 8) / 6 + if (len(src) % 6) != 0 { + hashSize += 1 + } + hash = make([]byte, hashSize) + + dst := hash + for len(src) > 0 { + switch len(src) { + default: + dst[0] = alphabet[src[0]&0x3f] + dst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f] + dst[2] = alphabet[((src[1]>>4)|(src[2]<<4))&0x3f] + dst[3] = alphabet[(src[2]>>2)&0x3f] + src = src[3:] + dst = dst[4:] + case 2: + dst[0] = alphabet[src[0]&0x3f] + dst[1] = alphabet[((src[0]>>6)|(src[1]<<2))&0x3f] + dst[2] = alphabet[(src[1]>>4)&0x3f] + src = src[2:] + dst = dst[3:] + case 1: + dst[0] = alphabet[src[0]&0x3f] + dst[1] = alphabet[(src[0]>>6)&0x3f] + src = src[1:] + dst = dst[2:] + } + } + + return +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/doc.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/doc.go new file mode 100644 index 00000000..8eb6aff2 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/doc.go @@ -0,0 +1,13 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// 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 contains routines used by multiple password hashing +// algorithms. +// +// Generally, you will never import this package directly. Many of the +// *_crypt packages will import this package if they require it. +package common diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/salt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/salt.go new file mode 100644 index 00000000..ee38851b --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/common/salt.go @@ -0,0 +1,105 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// 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 ( + "crypto/rand" + "errors" + "strconv" +) + +var ( + ErrSaltPrefix = errors.New("invalid magic prefix") + ErrSaltFormat = errors.New("invalid salt format") + ErrSaltRounds = errors.New("invalid 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++ + } + 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++ + } + salt := make([]byte, saltLen) + rand.Read(salt) + + roundsText := "" + if rounds != s.RoundsDefault { + roundsText = "rounds=" + 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 +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/crypt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/crypt.go new file mode 100644 index 00000000..98fb01ac --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/crypt.go @@ -0,0 +1,108 @@ +// Copyright 2013, Jonas mg +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// Package crypt provides interface for password crypt functions and collects +// common constants. +package crypt + +import ( + "errors" + "strings" + + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/common" +) + +var ErrKeyMismatch = errors.New("hashed value is not the hash of the given password") + +// Crypter is the common interface implemented by all crypt functions. +type Crypter interface { + // Generate performs the hashing algorithm, returning a full hash suitable + // for storage and later password verification. + // + // If the salt is empty, a randomly-generated salt will be generated with a + // length of SaltLenMax and number RoundsDefault of rounds. + // + // Any error only can be got when the salt argument is not empty. + Generate(key, salt []byte) (string, error) + + // Verify compares a hashed key with its possible key equivalent. + // Returns nil on success, or an error on failure; if the hashed key is + // diffrent, the error is "ErrKeyMismatch". + Verify(hashedKey string, key []byte) error + + // Cost returns the hashing cost (in rounds) used to create the given hashed + // key. + // + // When, in the future, the hashing cost of a key needs to be increased in + // order to adjust for greater computational power, this function allows one + // to establish which keys need to be updated. + // + // The algorithms based in MD5-crypt use a fixed value of rounds. + Cost(hashedKey string) (int, error) + + // SetSalt sets a different salt. It is used to easily create derivated + // algorithms, i.e. "apr1_crypt" from "md5_crypt". + SetSalt(salt common.Salt) +} + +// Crypt identifies a crypt function that is implemented in another package. +type Crypt uint + +const ( + APR1 Crypt = iota + 1 // import "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/apr1_crypt" + MD5 // import "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/md5_crypt" + SHA256 // import "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/sha256_crypt" + SHA512 // import "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/sha512_crypt" + maxCrypt +) + +var cryptPrefixes = make([]string, maxCrypt) + +var crypts = make([]func() Crypter, maxCrypt) + +// RegisterCrypt registers a function that returns a new instance of the given +// crypt function. This is intended to be called from the init function in +// packages that implement crypt functions. +func RegisterCrypt(c Crypt, f func() Crypter, prefix string) { + if c >= maxCrypt { + panic("crypt: RegisterHash of unknown crypt function") + } + crypts[c] = f + cryptPrefixes[c] = prefix +} + +// New returns a new crypter. +func New(c Crypt) Crypter { + f := crypts[c] + if f != nil { + return f() + } + panic("crypt: requested crypt function is unavailable") +} + +// NewFromHash returns a new Crypter using the prefix in the given hashed key. +func NewFromHash(hashedKey string) Crypter { + var f func() Crypter + + if strings.HasPrefix(hashedKey, cryptPrefixes[SHA512]) { + f = crypts[SHA512] + } else if strings.HasPrefix(hashedKey, cryptPrefixes[SHA256]) { + f = crypts[SHA256] + } else if strings.HasPrefix(hashedKey, cryptPrefixes[MD5]) { + f = crypts[MD5] + } else if strings.HasPrefix(hashedKey, cryptPrefixes[APR1]) { + f = crypts[APR1] + } else { + toks := strings.SplitN(hashedKey, "$", 3) + prefix := "$" + toks[1] + "$" + panic("crypt: unknown cryp function from prefix: " + prefix) + } + + if f != nil { + return f() + } + panic("crypt: requested cryp function is unavailable") +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/md5_crypt/md5_crypt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/md5_crypt/md5_crypt.go new file mode 100644 index 00000000..923575c5 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/md5_crypt/md5_crypt.go @@ -0,0 +1,166 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// Package md5_crypt implements the standard Unix MD5-crypt algorithm created by +// Poul-Henning Kamp for FreeBSD. +package md5_crypt + +import ( + "bytes" + "crypto/md5" + + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/common" +) + +func init() { + crypt.RegisterCrypt(crypt.MD5, New, MagicPrefix) +} + +// NOTE: Cisco IOS only allows salts of length 4. + +const ( + MagicPrefix = "$1$" + SaltLenMin = 1 // Real minimum is 0, but that isn't useful. + SaltLenMax = 8 + RoundsDefault = 1000 +) + +type crypter struct{ Salt common.Salt } + +// New returns a new crypt.Crypter computing the MD5-crypt password hashing. +func New() crypt.Crypter { + return &crypter{GetSalt()} +} + +func (c *crypter) Generate(key, salt []byte) (string, error) { + if len(salt) == 0 { + salt = c.Salt.Generate(SaltLenMax) + } + if !bytes.HasPrefix(salt, c.Salt.MagicPrefix) { + return "", common.ErrSaltPrefix + } + + saltToks := bytes.Split(salt, []byte{'$'}) + + if len(saltToks) < 3 { + return "", common.ErrSaltFormat + } else { + salt = saltToks[2] + } + if len(salt) > 8 { + salt = salt[0:8] + } + + // Compute alternate MD5 sum with input KEY, SALT, and KEY. + Alternate := md5.New() + Alternate.Write(key) + Alternate.Write(salt) + Alternate.Write(key) + AlternateSum := Alternate.Sum(nil) // 16 bytes + + A := md5.New() + A.Write(key) + A.Write(c.Salt.MagicPrefix) + A.Write(salt) + // Add for any character in the key one byte of the alternate sum. + i := len(key) + for ; i > 16; i -= 16 { + A.Write(AlternateSum) + } + A.Write(AlternateSum[0:i]) + + // The original implementation now does something weird: + // For every 1 bit in the key, the first 0 is added to the buffer + // For every 0 bit, the first character of the key + // This does not seem to be what was intended but we have to follow this to + // be compatible. + for i = len(key); i > 0; i >>= 1 { + if (i & 1) == 0 { + A.Write(key[0:1]) + } else { + A.Write([]byte{0}) + } + } + Csum := A.Sum(nil) + + // In fear of password crackers here comes a quite long loop which just + // processes the output of the previous round again. + // We cannot ignore this here. + for i = 0; i < RoundsDefault; i++ { + C := md5.New() + + // Add key or last result. + if (i & 1) != 0 { + C.Write(key) + } else { + C.Write(Csum) + } + // Add salt for numbers not divisible by 3. + if (i % 3) != 0 { + C.Write(salt) + } + // Add key for numbers not divisible by 7. + if (i % 7) != 0 { + C.Write(key) + } + // Add key or last result. + if (i & 1) == 0 { + C.Write(key) + } else { + C.Write(Csum) + } + + Csum = C.Sum(nil) + } + + out := make([]byte, 0, 23+len(c.Salt.MagicPrefix)+len(salt)) + out = append(out, c.Salt.MagicPrefix...) + out = append(out, salt...) + out = append(out, '$') + out = append(out, common.Base64_24Bit([]byte{ + Csum[12], Csum[6], Csum[0], + Csum[13], Csum[7], Csum[1], + Csum[14], Csum[8], Csum[2], + Csum[15], Csum[9], Csum[3], + Csum[5], Csum[10], Csum[4], + Csum[11], + })...) + + // Clean sensitive data. + A.Reset() + Alternate.Reset() + for i = 0; i < len(AlternateSum); i++ { + AlternateSum[i] = 0 + } + + return string(out), nil +} + +func (c *crypter) Verify(hashedKey string, key []byte) error { + newHash, err := c.Generate(key, []byte(hashedKey)) + if err != nil { + return err + } + if newHash != hashedKey { + return crypt.ErrKeyMismatch + } + return nil +} + +func (c *crypter) Cost(hashedKey string) (int, error) { return RoundsDefault, nil } + +func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt } + +func GetSalt() common.Salt { + return common.Salt{ + MagicPrefix: []byte(MagicPrefix), + SaltLenMin: SaltLenMin, + SaltLenMax: SaltLenMax, + RoundsDefault: RoundsDefault, + } +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha256_crypt/sha256_crypt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha256_crypt/sha256_crypt.go new file mode 100644 index 00000000..8ba47cb6 --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha256_crypt/sha256_crypt.go @@ -0,0 +1,243 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// Package sha256_crypt implements Ulrich Drepper's SHA256-crypt password +// hashing algorithm. +// +// The specification for this algorithm can be found here: +// http://www.akkadia.org/drepper/SHA-crypt.txt +package sha256_crypt + +import ( + "bytes" + "crypto/sha256" + "strconv" + + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/common" +) + +func init() { + crypt.RegisterCrypt(crypt.SHA256, New, MagicPrefix) +} + +const ( + MagicPrefix = "$5$" + SaltLenMin = 1 + SaltLenMax = 16 + RoundsMin = 1000 + RoundsMax = 999999999 + RoundsDefault = 5000 +) + +var _rounds = []byte("rounds=") + +type crypter struct{ Salt common.Salt } + +// New returns a new crypt.Crypter computing the SHA256-crypt password hashing. +func New() crypt.Crypter { + return &crypter{GetSalt()} +} + +func (c *crypter) Generate(key, salt []byte) (string, error) { + var rounds int + var isRoundsDef bool + + if len(salt) == 0 { + salt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault) + } + if !bytes.HasPrefix(salt, c.Salt.MagicPrefix) { + return "", common.ErrSaltPrefix + } + + saltToks := bytes.Split(salt, []byte{'$'}) + if len(saltToks) < 3 { + return "", common.ErrSaltFormat + } + + if bytes.HasPrefix(saltToks[2], _rounds) { + isRoundsDef = true + pr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32) + if err != nil { + return "", common.ErrSaltRounds + } + rounds = int(pr) + if rounds < RoundsMin { + rounds = RoundsMin + } else if rounds > RoundsMax { + rounds = RoundsMax + } + salt = saltToks[3] + } else { + rounds = RoundsDefault + salt = saltToks[2] + } + + if len(salt) > 16 { + salt = salt[0:16] + } + + // Compute alternate SHA256 sum with input KEY, SALT, and KEY. + Alternate := sha256.New() + Alternate.Write(key) + Alternate.Write(salt) + Alternate.Write(key) + AlternateSum := Alternate.Sum(nil) // 32 bytes + + A := sha256.New() + A.Write(key) + A.Write(salt) + // Add for any character in the key one byte of the alternate sum. + i := len(key) + for ; i > 32; i -= 32 { + A.Write(AlternateSum) + } + A.Write(AlternateSum[0:i]) + + // Take the binary representation of the length of the key and for every add + // the alternate sum, for every 0 the key. + for i = len(key); i > 0; i >>= 1 { + if (i & 1) != 0 { + A.Write(AlternateSum) + } else { + A.Write(key) + } + } + Asum := A.Sum(nil) + + // Start computation of P byte sequence. + P := sha256.New() + // For every character in the password add the entire password. + for i = 0; i < len(key); i++ { + P.Write(key) + } + Psum := P.Sum(nil) + // Create byte sequence P. + Pseq := make([]byte, 0, len(key)) + for i = len(key); i > 32; i -= 32 { + Pseq = append(Pseq, Psum...) + } + Pseq = append(Pseq, Psum[0:i]...) + + // Start computation of S byte sequence. + S := sha256.New() + for i = 0; i < (16 + int(Asum[0])); i++ { + S.Write(salt) + } + Ssum := S.Sum(nil) + // Create byte sequence S. + Sseq := make([]byte, 0, len(salt)) + for i = len(salt); i > 32; i -= 32 { + Sseq = append(Sseq, Ssum...) + } + Sseq = append(Sseq, Ssum[0:i]...) + + Csum := Asum + + // Repeatedly run the collected hash value through SHA256 to burn CPU cycles. + for i = 0; i < rounds; i++ { + C := sha256.New() + + // Add key or last result. + if (i & 1) != 0 { + C.Write(Pseq) + } else { + C.Write(Csum) + } + // Add salt for numbers not divisible by 3. + if (i % 3) != 0 { + C.Write(Sseq) + } + // Add key for numbers not divisible by 7. + if (i % 7) != 0 { + C.Write(Pseq) + } + // Add key or last result. + if (i & 1) != 0 { + C.Write(Csum) + } else { + C.Write(Pseq) + } + + Csum = C.Sum(nil) + } + + out := make([]byte, 0, 80) + out = append(out, c.Salt.MagicPrefix...) + if isRoundsDef { + out = append(out, []byte("rounds="+strconv.Itoa(rounds)+"$")...) + } + out = append(out, salt...) + out = append(out, '$') + out = append(out, common.Base64_24Bit([]byte{ + Csum[20], Csum[10], Csum[0], + Csum[11], Csum[1], Csum[21], + Csum[2], Csum[22], Csum[12], + Csum[23], Csum[13], Csum[3], + Csum[14], Csum[4], Csum[24], + Csum[5], Csum[25], Csum[15], + Csum[26], Csum[16], Csum[6], + Csum[17], Csum[7], Csum[27], + Csum[8], Csum[28], Csum[18], + Csum[29], Csum[19], Csum[9], + Csum[30], Csum[31], + })...) + + // Clean sensitive data. + A.Reset() + Alternate.Reset() + P.Reset() + for i = 0; i < len(Asum); i++ { + Asum[i] = 0 + } + for i = 0; i < len(AlternateSum); i++ { + AlternateSum[i] = 0 + } + for i = 0; i < len(Pseq); i++ { + Pseq[i] = 0 + } + + return string(out), nil +} + +func (c *crypter) Verify(hashedKey string, key []byte) error { + newHash, err := c.Generate(key, []byte(hashedKey)) + if err != nil { + return err + } + if newHash != hashedKey { + return crypt.ErrKeyMismatch + } + return nil +} + +func (c *crypter) Cost(hashedKey string) (int, error) { + saltToks := bytes.Split([]byte(hashedKey), []byte{'$'}) + if len(saltToks) < 3 { + return 0, common.ErrSaltFormat + } + + if !bytes.HasPrefix(saltToks[2], _rounds) { + return RoundsDefault, nil + } + roundToks := bytes.Split(saltToks[2], []byte{'='}) + cost, err := strconv.ParseInt(string(roundToks[1]), 10, 0) + return int(cost), err +} + +func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt } + +func GetSalt() common.Salt { + return common.Salt{ + MagicPrefix: []byte(MagicPrefix), + SaltLenMin: SaltLenMin, + SaltLenMax: SaltLenMax, + RoundsDefault: RoundsDefault, + RoundsMin: RoundsMin, + RoundsMax: RoundsMax, + } +} diff --git a/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha512_crypt/sha512_crypt.go b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha512_crypt/sha512_crypt.go new file mode 100644 index 00000000..911e7c9d --- /dev/null +++ b/server/plugin/plg_authenticate_htpasswd/vendor/crypt/sha512_crypt/sha512_crypt.go @@ -0,0 +1,254 @@ +// Copyright 2012, Jeramey Crawford +// Copyright 2013, Jonas mg +// All rights reserved. +// +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file. + +// Package sha512_crypt implements Ulrich Drepper's SHA512-crypt password +// hashing algorithm. +// +// The specification for this algorithm can be found here: +// http://www.akkadia.org/drepper/SHA-crypt.txt +package sha512_crypt + +import ( + "bytes" + "crypto/sha512" + "strconv" + + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt" + "github.com/mickael-kerjean/server/plg_authenticate_htpasswd/vendor/crypt/common" +) + +func init() { + crypt.RegisterCrypt(crypt.SHA512, New, MagicPrefix) +} + +const ( + MagicPrefix = "$6$" + SaltLenMin = 1 + SaltLenMax = 16 + RoundsMin = 1000 + RoundsMax = 999999999 + RoundsDefault = 5000 +) + +var _rounds = []byte("rounds=") + +type crypter struct{ Salt common.Salt } + +// New returns a new crypt.Crypter computing the SHA512-crypt password hashing. +func New() crypt.Crypter { + return &crypter{GetSalt()} +} + +func (c *crypter) Generate(key, salt []byte) (string, error) { + var rounds int + var isRoundsDef bool + + if len(salt) == 0 { + salt = c.Salt.GenerateWRounds(SaltLenMax, RoundsDefault) + } + if !bytes.HasPrefix(salt, c.Salt.MagicPrefix) { + return "", common.ErrSaltPrefix + } + + saltToks := bytes.Split(salt, []byte{'$'}) + if len(saltToks) < 3 { + return "", common.ErrSaltFormat + } + + if bytes.HasPrefix(saltToks[2], _rounds) { + isRoundsDef = true + pr, err := strconv.ParseInt(string(saltToks[2][7:]), 10, 32) + if err != nil { + return "", common.ErrSaltRounds + } + rounds = int(pr) + if rounds < RoundsMin { + rounds = RoundsMin + } else if rounds > RoundsMax { + rounds = RoundsMax + } + salt = saltToks[3] + } else { + rounds = RoundsDefault + salt = saltToks[2] + } + + if len(salt) > SaltLenMax { + salt = salt[0:SaltLenMax] + } + + // Compute alternate SHA512 sum with input KEY, SALT, and KEY. + Alternate := sha512.New() + Alternate.Write(key) + Alternate.Write(salt) + Alternate.Write(key) + AlternateSum := Alternate.Sum(nil) // 64 bytes + + A := sha512.New() + A.Write(key) + A.Write(salt) + // Add for any character in the key one byte of the alternate sum. + i := len(key) + for ; i > 64; i -= 64 { + A.Write(AlternateSum) + } + A.Write(AlternateSum[0:i]) + + // Take the binary representation of the length of the key and for every add + // the alternate sum, for every 0 the key. + for i = len(key); i > 0; i >>= 1 { + if (i & 1) != 0 { + A.Write(AlternateSum) + } else { + A.Write(key) + } + } + Asum := A.Sum(nil) + + // Start computation of P byte sequence. + P := sha512.New() + // For every character in the password add the entire password. + for i = 0; i < len(key); i++ { + P.Write(key) + } + Psum := P.Sum(nil) + // Create byte sequence P. + Pseq := make([]byte, 0, len(key)) + for i = len(key); i > 64; i -= 64 { + Pseq = append(Pseq, Psum...) + } + Pseq = append(Pseq, Psum[0:i]...) + + // Start computation of S byte sequence. + S := sha512.New() + for i = 0; i < (16 + int(Asum[0])); i++ { + S.Write(salt) + } + Ssum := S.Sum(nil) + // Create byte sequence S. + Sseq := make([]byte, 0, len(salt)) + for i = len(salt); i > 64; i -= 64 { + Sseq = append(Sseq, Ssum...) + } + Sseq = append(Sseq, Ssum[0:i]...) + + Csum := Asum + + // Repeatedly run the collected hash value through SHA512 to burn CPU cycles. + for i = 0; i < rounds; i++ { + C := sha512.New() + + // Add key or last result. + if (i & 1) != 0 { + C.Write(Pseq) + } else { + C.Write(Csum) + } + // Add salt for numbers not divisible by 3. + if (i % 3) != 0 { + C.Write(Sseq) + } + // Add key for numbers not divisible by 7. + if (i % 7) != 0 { + C.Write(Pseq) + } + // Add key or last result. + if (i & 1) != 0 { + C.Write(Csum) + } else { + C.Write(Pseq) + } + + Csum = C.Sum(nil) + } + + out := make([]byte, 0, 123) + out = append(out, c.Salt.MagicPrefix...) + if isRoundsDef { + out = append(out, []byte("rounds="+strconv.Itoa(rounds)+"$")...) + } + out = append(out, salt...) + out = append(out, '$') + out = append(out, common.Base64_24Bit([]byte{ + Csum[42], Csum[21], Csum[0], + Csum[1], Csum[43], Csum[22], + Csum[23], Csum[2], Csum[44], + Csum[45], Csum[24], Csum[3], + Csum[4], Csum[46], Csum[25], + Csum[26], Csum[5], Csum[47], + Csum[48], Csum[27], Csum[6], + Csum[7], Csum[49], Csum[28], + Csum[29], Csum[8], Csum[50], + Csum[51], Csum[30], Csum[9], + Csum[10], Csum[52], Csum[31], + Csum[32], Csum[11], Csum[53], + Csum[54], Csum[33], Csum[12], + Csum[13], Csum[55], Csum[34], + Csum[35], Csum[14], Csum[56], + Csum[57], Csum[36], Csum[15], + Csum[16], Csum[58], Csum[37], + Csum[38], Csum[17], Csum[59], + Csum[60], Csum[39], Csum[18], + Csum[19], Csum[61], Csum[40], + Csum[41], Csum[20], Csum[62], + Csum[63], + })...) + + // Clean sensitive data. + A.Reset() + Alternate.Reset() + P.Reset() + for i = 0; i < len(Asum); i++ { + Asum[i] = 0 + } + for i = 0; i < len(AlternateSum); i++ { + AlternateSum[i] = 0 + } + for i = 0; i < len(Pseq); i++ { + Pseq[i] = 0 + } + + return string(out), nil +} + +func (c *crypter) Verify(hashedKey string, key []byte) error { + newHash, err := c.Generate(key, []byte(hashedKey)) + if err != nil { + return err + } + if newHash != hashedKey { + return crypt.ErrKeyMismatch + } + return nil +} + +func (c *crypter) Cost(hashedKey string) (int, error) { + saltToks := bytes.Split([]byte(hashedKey), []byte{'$'}) + if len(saltToks) < 3 { + return 0, common.ErrSaltFormat + } + + if !bytes.HasPrefix(saltToks[2], _rounds) { + return RoundsDefault, nil + } + roundToks := bytes.Split(saltToks[2], []byte{'='}) + cost, err := strconv.ParseInt(string(roundToks[1]), 10, 0) + return int(cost), err +} + +func (c *crypter) SetSalt(salt common.Salt) { c.Salt = salt } + +func GetSalt() common.Salt { + return common.Salt{ + MagicPrefix: []byte(MagicPrefix), + SaltLenMin: SaltLenMin, + SaltLenMax: SaltLenMax, + RoundsDefault: RoundsDefault, + RoundsMin: RoundsMin, + RoundsMax: RoundsMax, + } +}