Initial commit

This commit is contained in:
2026-05-31 01:09:34 +01:00
commit 1d51b29b92
31 changed files with 4874 additions and 0 deletions
+48
View File
@@ -0,0 +1,48 @@
package strenc
import (
"crypto/hmac"
"crypto/sha256"
"encoding/binary"
"os"
"runtime"
)
const buildSeed = 0xDEAD_BEEF_CAFE_BABE
func DeriveMasterKey(seed [32]byte) [32]byte {
var out [32]byte
mac := hmac.New(sha256.New, seed[:])
mac.Write([]byte("strenc.masterkey.v1"))
copy(out[:16], mac.Sum(nil))
mac.Reset()
mac.Write(seed[:])
mac.Write([]byte("strenc.masterkey.v2"))
copy(out[16:], mac.Sum(nil))
return out
}
func DeriveFromBinary() [32]byte {
exePath, err := os.Executable()
if err != nil {
exePath = "unknown"
}
mac := hmac.New(sha256.New, []byte("strenc.binary.seed"))
mac.Write([]byte(exePath))
var seedBuf [8]byte
binary.LittleEndian.PutUint64(seedBuf[:], buildSeed)
mac.Write(seedBuf[:])
mac.Write([]byte(runtime.GOOS))
mac.Write([]byte(runtime.GOARCH))
var seed [32]byte
copy(seed[:], mac.Sum(nil))
return DeriveMasterKey(seed)
}
+151
View File
@@ -0,0 +1,151 @@
package strenc
import (
"crypto/aes"
"crypto/cipher"
"crypto/hmac"
"crypto/sha256"
"fmt"
)
type Entry struct {
Ciphertext []byte
IV [16]byte
}
type Pool struct {
data []byte
offsets []int
lengths []int
entries int
masterKey [32]byte
}
func NewPool(masterKeySeed [32]byte, entries []Entry) *Pool {
p := &Pool{
masterKey: DeriveMasterKey(masterKeySeed),
}
p.entries = len(entries)
totalSize := 0
for _, e := range entries {
totalSize += 16 + len(e.Ciphertext)
}
p.data = make([]byte, 0, totalSize)
p.offsets = make([]int, len(entries))
p.lengths = make([]int, len(entries))
for i, e := range entries {
p.offsets[i] = len(p.data)
p.data = append(p.data, e.IV[:]...)
p.data = append(p.data, e.Ciphertext...)
p.lengths[i] = 16 + len(e.Ciphertext)
}
return p
}
func (p *Pool) StringCount() int {
return p.entries
}
func (p *Pool) deriveSubkey(iv [16]byte) [32]byte {
mac := hmac.New(sha256.New, p.masterKey[:])
mac.Write(iv[:])
var subkey [32]byte
copy(subkey[:], mac.Sum(nil))
return subkey
}
func (p *Pool) decryptAt(index int) ([]byte, error) {
if index < 0 || index >= p.entries {
return nil, fmt.Errorf("strenc: index %d out of range [0, %d)", index, p.entries)
}
start := p.offsets[index]
raw := p.data[start : start+p.lengths[index]]
var iv [16]byte
copy(iv[:], raw[:16])
ct := raw[16:]
subkey := p.deriveSubkey(iv)
block, err := aes.NewCipher(subkey[:])
if err != nil {
return nil, fmt.Errorf("strenc: aes.NewCipher: %w", err)
}
gcm, err := cipher.NewGCM(block)
if err != nil {
return nil, fmt.Errorf("strenc: cipher.NewGCM: %w", err)
}
nonceSize := gcm.NonceSize()
if len(ct) < nonceSize {
return nil, fmt.Errorf("strenc: ciphertext too short")
}
nonce := ct[:nonceSize]
ciphertext := ct[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
return nil, fmt.Errorf("strenc: gcm.Open: %w", err)
}
return plaintext, nil
}
func (p *Pool) GetString(index int) string {
pt, err := p.decryptAt(index)
if err != nil {
return ""
}
return string(pt)
}
func (p *Pool) GetStringZero(index int, fn func(string)) {
pt, err := p.decryptAt(index)
if err != nil {
fn("")
return
}
buf := make([]byte, len(pt))
copy(buf, pt)
s := string(buf)
for i := range pt {
pt[i] = 0
}
fn(s)
for i := range buf {
buf[i] = 0
}
}
func EncryptEntry(plaintext string, key [32]byte, iv [16]byte) Entry {
block, err := aes.NewCipher(key[:])
if err != nil {
panic(fmt.Sprintf("strenc: aes.NewCipher: %v", err))
}
gcm, err := cipher.NewGCM(block)
if err != nil {
panic(fmt.Sprintf("strenc: cipher.NewGCM: %v", err))
}
nonceSize := gcm.NonceSize()
nonce := make([]byte, nonceSize)
copy(nonce, iv[:nonceSize])
ct := gcm.Seal(nonce, nonce, []byte(plaintext), nil)
return Entry{
Ciphertext: ct,
IV: iv,
}
}