2025-10-27 03:14:50 +00:00
|
|
|
package crypt
|
|
|
|
|
|
|
|
|
|
import (
|
2025-10-30 14:18:37 +00:00
|
|
|
"bytes"
|
2025-10-27 03:14:50 +00:00
|
|
|
"crypto/md5"
|
|
|
|
|
"crypto/sha1"
|
|
|
|
|
"crypto/sha256"
|
|
|
|
|
"crypto/sha512"
|
|
|
|
|
"encoding/binary"
|
|
|
|
|
"encoding/hex"
|
2026-01-13 23:33:01 +00:00
|
|
|
"fmt"
|
2025-10-27 03:14:50 +00:00
|
|
|
"io"
|
|
|
|
|
"strconv"
|
|
|
|
|
"strings"
|
|
|
|
|
|
|
|
|
|
"github.com/Snider/Core/pkg/core"
|
|
|
|
|
"github.com/Snider/Core/pkg/crypt/lthn"
|
|
|
|
|
"github.com/Snider/Core/pkg/crypt/openpgp"
|
|
|
|
|
)
|
|
|
|
|
|
2026-01-13 23:33:01 +00:00
|
|
|
// HandleIPCEvents processes IPC messages for the crypt service.
|
|
|
|
|
func (s *Service) HandleIPCEvents(c *core.Core, msg core.Message) error {
|
|
|
|
|
switch msg.(type) {
|
|
|
|
|
case core.ActionServiceStartup:
|
|
|
|
|
// Crypt is stateless, no startup needed.
|
|
|
|
|
return nil
|
|
|
|
|
default:
|
|
|
|
|
if c.App != nil && c.App.Logger != nil {
|
|
|
|
|
c.App.Logger.Debug("Crypt: Unhandled message type", "type", fmt.Sprintf("%T", msg))
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
return nil
|
|
|
|
|
}
|
|
|
|
|
|
2025-10-27 03:14:50 +00:00
|
|
|
// Options holds configuration for the crypt service.
|
|
|
|
|
type Options struct{}
|
|
|
|
|
|
|
|
|
|
// Service provides cryptographic functions to the application.
|
|
|
|
|
type Service struct {
|
|
|
|
|
*core.Runtime[Options]
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// HashType defines the supported hashing algorithms.
|
|
|
|
|
type HashType string
|
|
|
|
|
|
|
|
|
|
const (
|
|
|
|
|
LTHN HashType = "lthn"
|
|
|
|
|
SHA512 HashType = "sha512"
|
|
|
|
|
SHA256 HashType = "sha256"
|
|
|
|
|
SHA1 HashType = "sha1"
|
|
|
|
|
MD5 HashType = "md5"
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
// newCryptService contains the common logic for initializing a Service struct.
|
|
|
|
|
func newCryptService() (*Service, error) {
|
|
|
|
|
return &Service{}, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// New is the constructor for static dependency injection.
|
|
|
|
|
// It creates a Service instance without initializing the core.Runtime field.
|
|
|
|
|
func New() (*Service, error) {
|
|
|
|
|
return newCryptService()
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Register is the constructor for dynamic dependency injection (used with core.WithService).
|
|
|
|
|
// It creates a Service instance and initializes its core.Runtime field.
|
|
|
|
|
func Register(c *core.Core) (any, error) {
|
|
|
|
|
s, err := newCryptService()
|
|
|
|
|
if err != nil {
|
|
|
|
|
return nil, err
|
|
|
|
|
}
|
|
|
|
|
s.Runtime = core.NewRuntime(c, Options{})
|
|
|
|
|
return s, nil
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Hashing ---
|
|
|
|
|
|
|
|
|
|
// Hash computes a hash of the payload using the specified algorithm.
|
|
|
|
|
func (s *Service) Hash(lib HashType, payload string) string {
|
|
|
|
|
switch lib {
|
|
|
|
|
case LTHN:
|
|
|
|
|
return lthn.Hash(payload)
|
|
|
|
|
case SHA512:
|
|
|
|
|
hash := sha512.Sum512([]byte(payload))
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
case SHA1:
|
|
|
|
|
hash := sha1.Sum([]byte(payload))
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
case MD5:
|
|
|
|
|
hash := md5.Sum([]byte(payload))
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
case SHA256:
|
|
|
|
|
fallthrough
|
|
|
|
|
default:
|
|
|
|
|
hash := sha256.Sum256([]byte(payload))
|
|
|
|
|
return hex.EncodeToString(hash[:])
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- Checksums ---
|
|
|
|
|
|
|
|
|
|
// Luhn validates a number using the Luhn algorithm.
|
|
|
|
|
func (s *Service) Luhn(payload string) bool {
|
|
|
|
|
payload = strings.ReplaceAll(payload, " ", "")
|
|
|
|
|
sum := 0
|
|
|
|
|
isSecond := false
|
|
|
|
|
for i := len(payload) - 1; i >= 0; i-- {
|
|
|
|
|
digit, err := strconv.Atoi(string(payload[i]))
|
|
|
|
|
if err != nil {
|
|
|
|
|
return false // Contains non-digit
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
if isSecond {
|
|
|
|
|
digit = digit * 2
|
|
|
|
|
if digit > 9 {
|
|
|
|
|
digit = digit - 9
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
sum += digit
|
|
|
|
|
isSecond = !isSecond
|
|
|
|
|
}
|
|
|
|
|
return sum%10 == 0
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fletcher16 computes the Fletcher-16 checksum.
|
|
|
|
|
func (s *Service) Fletcher16(payload string) uint16 {
|
|
|
|
|
data := []byte(payload)
|
|
|
|
|
var sum1, sum2 uint16
|
|
|
|
|
for _, b := range data {
|
|
|
|
|
sum1 = (sum1 + uint16(b)) % 255
|
|
|
|
|
sum2 = (sum2 + sum1) % 255
|
|
|
|
|
}
|
|
|
|
|
return (sum2 << 8) | sum1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fletcher32 computes the Fletcher-32 checksum.
|
|
|
|
|
func (s *Service) Fletcher32(payload string) uint32 {
|
|
|
|
|
data := []byte(payload)
|
|
|
|
|
if len(data)%2 != 0 {
|
|
|
|
|
data = append(data, 0)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sum1, sum2 uint32
|
|
|
|
|
for i := 0; i < len(data); i += 2 {
|
|
|
|
|
val := binary.LittleEndian.Uint16(data[i : i+2])
|
|
|
|
|
sum1 = (sum1 + uint32(val)) % 65535
|
|
|
|
|
sum2 = (sum2 + sum1) % 65535
|
|
|
|
|
}
|
|
|
|
|
return (sum2 << 16) | sum1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Fletcher64 computes the Fletcher-64 checksum.
|
|
|
|
|
func (s *Service) Fletcher64(payload string) uint64 {
|
|
|
|
|
data := []byte(payload)
|
|
|
|
|
if len(data)%4 != 0 {
|
|
|
|
|
padding := 4 - (len(data) % 4)
|
|
|
|
|
data = append(data, make([]byte, padding)...)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
var sum1, sum2 uint64
|
|
|
|
|
for i := 0; i < len(data); i += 4 {
|
|
|
|
|
val := binary.LittleEndian.Uint32(data[i : i+4])
|
|
|
|
|
sum1 = (sum1 + uint64(val)) % 4294967295
|
|
|
|
|
sum2 = (sum2 + sum1) % 4294967295
|
|
|
|
|
}
|
|
|
|
|
return (sum2 << 32) | sum1
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// --- PGP ---
|
|
|
|
|
|
|
|
|
|
// EncryptPGP encrypts data for a recipient, optionally signing it.
|
|
|
|
|
func (s *Service) EncryptPGP(writer io.Writer, recipientPath, data string, signerPath, signerPassphrase *string) (string, error) {
|
2025-10-30 14:18:37 +00:00
|
|
|
var buf bytes.Buffer
|
|
|
|
|
err := openpgp.EncryptPGP(&buf, recipientPath, data, signerPath, signerPassphrase)
|
|
|
|
|
if err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// Copy the encrypted data to the original writer.
|
|
|
|
|
if _, err := writer.Write(buf.Bytes()); err != nil {
|
|
|
|
|
return "", err
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
return buf.String(), nil
|
2025-10-27 03:14:50 +00:00
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// DecryptPGP decrypts a PGP message, optionally verifying the signature.
|
|
|
|
|
func (s *Service) DecryptPGP(recipientPath, message, passphrase string, signerPath *string) (string, error) {
|
|
|
|
|
return openpgp.DecryptPGP(recipientPath, message, passphrase, signerPath)
|
|
|
|
|
}
|