60 lines
1.3 KiB
Go
60 lines
1.3 KiB
Go
package sbproxy
|
|
|
|
import (
|
|
"crypto/aes"
|
|
"crypto/cipher"
|
|
"crypto/sha256"
|
|
)
|
|
|
|
type cfb8 struct {
|
|
block cipher.Block
|
|
iv []byte
|
|
tmp []byte
|
|
decrypt bool
|
|
}
|
|
|
|
func NewCFB8(block cipher.Block, iv []byte, decrypt bool) cipher.Stream {
|
|
bs := block.BlockSize()
|
|
if len(iv) != bs {
|
|
panic("cfb8: IV length must equal block size")
|
|
}
|
|
return &cfb8{
|
|
block: block,
|
|
iv: append([]byte(nil), iv...),
|
|
tmp: make([]byte, bs),
|
|
decrypt: decrypt,
|
|
}
|
|
}
|
|
|
|
func (x *cfb8) XORKeyStream(dst, src []byte) {
|
|
for i := range src {
|
|
x.block.Encrypt(x.tmp, x.iv)
|
|
val := src[i]
|
|
dst[i] = val ^ x.tmp[0]
|
|
if x.decrypt {
|
|
copy(x.iv, x.iv[1:])
|
|
x.iv[len(x.iv)-1] = val
|
|
} else {
|
|
copy(x.iv, x.iv[1:])
|
|
x.iv[len(x.iv)-1] = dst[i]
|
|
}
|
|
}
|
|
}
|
|
|
|
// NewCipherStream now takes a session salt (IV) to prevent IV reuse attacks.
|
|
func NewCipherStream(password string, salt []byte, decrypt bool) (cipher.Stream, error) {
|
|
// Derive session key and IV from password and per-session salt
|
|
h := sha256.New()
|
|
h.Write([]byte(password))
|
|
h.Write(salt)
|
|
sessionMaterial := h.Sum(nil)
|
|
|
|
block, err := aes.NewCipher(sessionMaterial)
|
|
if err != nil {
|
|
return nil, err
|
|
}
|
|
|
|
// Use the first 16 bytes of the derived material as IV
|
|
return NewCFB8(block, sessionMaterial[:16], decrypt), nil
|
|
}
|