Files
opencloud/vendor/github.com/go-crypt/x/crypt/crypt.go
Pascal Bleser a8c2beac3a test(groupware): add testcontainers based jmap test
* adds pkg/jmap/jmap_integration_test.go

 * uses ghcr.io/stalwartlabs/stalwart:v0.13.2-alpine

 * can be disabled by setting one of the following environment
   variables, in the same fashion as ca0493b28
   - CI=woodpecker
   - CI_SYSTEM_NAME=woodpecker
   - USE_TESTCONTAINERS=false

 * dependencies:
   - bump github.com/go-test/deep from 1.1.0 to 1.1.1
   - add github.com/cention-sany/utf7
   - add github.com/dustinkirkland/golang-petname
   - add github.com/emersion/go-imap/v2
   - add github.com/emersion/go-message
   - add github.com/emersion/go-sasl
   - add github.com/go-crypt/crypt
   - add github.com/go-crypt/x
   - add github.com/gogs/chardet
   - add github.com/inbucket/html2text
   - add github.com/jhilleryerd/enmime/v2
   - add github.com/ssor/bom
   - add gopkg.in/loremipsum.v1
2025-12-09 09:15:37 +01:00

301 lines
5.2 KiB
Go

package crypt
import (
"crypto/hmac"
"crypto/md5"
"crypto/sha1"
"crypto/sha256"
"crypto/sha512"
"hash"
"strconv"
)
// KeySHACrypt calculates the shacrypt SHA256/SHA512 key given an appropriate hash.Hash, password, salt, and number of rounds.
func KeySHACrypt(hashFunc func() hash.Hash, password, salt []byte, rounds int) []byte {
// Step 1.
digest := hashFunc()
size := digest.Size()
switch size {
case sha1.Size:
return KeySHA1Crypt(password, salt, uint32(rounds))
case sha256.Size, sha512.Size:
break
default:
return nil
}
length := len(password)
// Step 2.
digest.Write(password)
// Step 3.
digest.Write(salt)
// Step 4.
digestB := hashFunc()
// Step 5.
digestB.Write(password)
// Step 6.
digestB.Write(salt)
// Step 7.
digestB.Write(password)
// Step 8.
sumB := digestB.Sum(nil)
digestB.Reset()
digestB = nil
// Step 9 and 10:
digest.Write(repeat(sumB, length))
// Step 11.
for i := length; i > 0; i >>= 1 {
if even(i) {
digest.Write(password)
} else {
digest.Write(sumB)
}
}
clean(sumB)
sumB = nil
// Step 12.
sumA := digest.Sum(nil)
digest.Reset()
// Step 13-14.
for i := 0; i < length; i++ {
digest.Write(password)
}
// Step 15.
sumDP := digest.Sum(nil)
digest.Reset()
// Step 16.
seqP := repeat(sumDP, length)
sumDP = nil
// Step 17-18.
for i := 0; i < 16+int(sumA[0]); i++ {
digest.Write(salt)
}
// Step 19.
sumDS := digest.Sum(nil)
digest.Reset()
// Step 20.
seqS := repeat(sumDS, len(salt))
// Step 21.
for i := 0; i < rounds; i++ {
digest.Reset()
// Step 21 Sub-Step B and C.
if i&1 != 0 {
// Step 21 Sub-Step B.
digest.Write(seqP)
} else {
// Step 21 Sub-Step C.
digest.Write(sumA)
}
// Step 21 Sub-Step D.
if i%3 != 0 {
digest.Write(seqS)
}
// Step 21 Sub-Step E.
if i%7 != 0 {
digest.Write(seqP)
}
// Step 21 Sub-Step F and G.
if i&1 != 0 {
// Step 21 Sub-Step F.
digest.Write(sumA)
} else {
// Step 21 Sub-Step G.
digest.Write(seqP)
}
// Sub-Step H.
copy(sumA, digest.Sum(nil))
}
digest.Reset()
digest = nil
seqP, seqS = nil, nil
switch size {
case sha256.Size:
// Step 22 Sub Step E.
return permute(sumA, permuteTableSHACryptSHA256[:])
case sha512.Size:
// Step 22 Sub Step E.
return permute(sumA, permuteTableSHACryptSHA512[:])
}
return nil
}
// KeySHA1Crypt calculates the sha1crypt key given a password, salt, and number of rounds.
func KeySHA1Crypt(password, salt []byte, rounds uint32) []byte {
digest := hmac.New(sha1.New, password)
digest.Write(salt)
digest.Write(prefixSHA1Crypt)
digest.Write([]byte(strconv.FormatUint(uint64(rounds), 10)))
sumA := digest.Sum(nil)
if rounds == 0 {
return permute(sumA, permuteTableSHA1Crypt[:])
}
for rounds--; rounds > 0; rounds-- {
digest.Reset()
digest.Write(sumA)
copy(sumA, digest.Sum(nil))
}
return permute(sumA, permuteTableSHA1Crypt[:])
}
// KeyMD5Crypt calculates the md5crypt key given a password and salt.
func KeyMD5Crypt(password, salt []byte) []byte {
length := len(password)
digest := md5.New()
digest.Write(password)
digest.Write(salt)
digest.Write(password)
sumB := digest.Sum(nil)
digest.Reset()
digest.Write(password)
digest.Write(prefixMD5Crypt)
digest.Write(salt)
digest.Write(repeat(sumB, length))
clean(sumB)
for i := length; i > 0; i >>= 1 {
if even(i) {
digest.Write(password[0:1])
} else {
digest.Write([]byte{0})
}
}
sumA := digest.Sum(nil)
for i := 0; i < 1000; i++ {
digest.Reset()
if even(i) {
digest.Write(sumA)
} else {
digest.Write(password)
}
if i%3 != 0 {
digest.Write(salt)
}
if i%7 != 0 {
digest.Write(password)
}
if i&1 == 0 {
digest.Write(password)
} else {
digest.Write(sumA)
}
copy(sumA, digest.Sum(nil))
}
return permute(sumA, permuteTableMD5Crypt[:])
}
// KeyMD5CryptSun calculates the md5crypt (Sun Version) key given a password, salt, and number rounds.
func KeyMD5CryptSun(password, salt []byte, rounds uint32) []byte {
digest := md5.New()
digest.Write(password)
if rounds == 0 {
digest.Write(prefixSunMD5Crypt)
digest.Write(salt)
digest.Write(sepCrypt)
} else {
digest.Write(prefixSunMD5CryptRounds)
digest.Write([]byte(strconv.FormatUint(uint64(rounds), 10)))
digest.Write(sepCrypt)
digest.Write(salt)
digest.Write(sepCrypt)
}
sumA := digest.Sum(nil)
iterations := uint32(rounds + 4096)
bit := func(off uint32) uint32 {
off %= 128
if (sumA[off/8] & (0x01 << (off % 8))) != 0 {
return 1
}
return 0
}
var ind7 [md5.Size]byte
for i := uint32(0); i < iterations; i++ {
digest.Reset()
digest.Write(sumA)
for j := 0; j < md5.Size; j++ {
off := (j + 3) % 16
ind4 := (sumA[j] >> (sumA[off] % 5)) & 0x0F
sh7 := (sumA[off] >> (sumA[j] % 8)) & 0x01
ind7[j] = (sumA[ind4] >> sh7) & 0x7F
}
var indA, indB uint32
for j := uint(0); j < 8; j++ {
indA |= bit(uint32(ind7[j])) << j
indB |= bit(uint32(ind7[j+8])) << j
}
indA = (indA >> bit(i)) & 0x7F
indB = (indB >> bit(i+64)) & 0x7F
if bit(indA)^bit(indB) == 1 {
digest.Write(magicTableMD5CryptSunHamlet[:])
}
digest.Write([]byte(strconv.FormatUint(uint64(i), 10)))
copy(sumA, digest.Sum(nil))
}
return permute(sumA, permuteTableMD5Crypt[:])
}