Initial: fixing internal package metadata
This commit is contained in:
97
cmd/syscrypt/main.go
Executable file
97
cmd/syscrypt/main.go
Executable file
@@ -0,0 +1,97 @@
|
||||
package main
|
||||
|
||||
import (
|
||||
"flag"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
|
||||
"sources.truenas.cloud/code/syscrypt/internal/actions/decrypt"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/actions/encrypt"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/actions/keygen"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/config"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/utils"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
)
|
||||
|
||||
func main() {
|
||||
|
||||
fs := flag.NewFlagSet("syscrypt", flag.ContinueOnError)
|
||||
fs.Usage = func() {}
|
||||
fs.SetOutput(io.Discard)
|
||||
|
||||
fs.BoolVar(&vars.VersionFlag, "v", false, "prints the version")
|
||||
fs.BoolVar(&vars.VersionFlag, "version", false, "prints the version")
|
||||
fs.StringVar(&vars.CommandFlag, "c", "", "command flag")
|
||||
fs.BoolVar(&vars.ArmoredFlag, "a", false, "generates an armored encrypted file")
|
||||
fs.StringVar(&vars.KeyFlag, "k", "", "public or private key file")
|
||||
fs.StringVar(&vars.InputFlag, "i", "", "input file")
|
||||
fs.StringVar(&vars.OutputFlag, "o", "", "output file")
|
||||
fs.BoolVar(&vars.LockFlag, "L", false, "lock the encrypted file to API Key and Master Password")
|
||||
fs.StringVar(&vars.ApiFlag, "A", "", "API key for your locked encrypted file")
|
||||
fs.StringVar(&vars.MasterPass, "P", "", "Master password value")
|
||||
fs.StringVar(&vars.Comment, "C", "", "Commend for API Key or Public/Private Keys")
|
||||
fs.StringVar(&vars.StringFlag, "s", "", "String instead of a file")
|
||||
fs.BoolVar(&vars.PQFlag, "PQ", false, "sets the keys to post-quantum mode")
|
||||
fs.StringVar(&vars.KeyPath, "K", "", "key path (optional)")
|
||||
fs.StringVar(&vars.FriendlyName, "f", "", "Friendly Name")
|
||||
|
||||
_ = fs.Parse(os.Args[1:])
|
||||
|
||||
if vars.VersionFlag {
|
||||
fmt.Printf("%s version: %s\nBuild Date: %s\n", config.BuildProject, config.BuildVersion, config.BuildDate)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if err := utils.CheckFirstFlag(); err != nil {
|
||||
msg := fmt.Sprintf("%s: %s", vars.CommandFlag, err)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
firstFlag := vars.CommandFlag
|
||||
|
||||
if firstFlag != "firstrun" {
|
||||
|
||||
_, err := utils.CheckExtraFlags()
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: %s", vars.CommandFlag, err)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
switch vars.CommandFlag {
|
||||
case "firstrun":
|
||||
keygen.FirstRun()
|
||||
case "apikey":
|
||||
case "dictionary":
|
||||
case "encrypt":
|
||||
encrypt.Validate()
|
||||
case "decrypt":
|
||||
decrypt.Validate()
|
||||
case "keygen":
|
||||
|
||||
defaultKey := config.KeyFolder + "/" + config.MasterKey
|
||||
defaultKeyExists := utils.FileExists(defaultKey)
|
||||
|
||||
fmt.Printf("default key: %s\n", defaultKey)
|
||||
os.Exit(1)
|
||||
|
||||
if !defaultKeyExists {
|
||||
msg := fmt.Sprintf("%s: No default keys found. \nPlease run 'syscrypt -c firstrun -PQ' before running any other command.\n"+
|
||||
"This will generate your default keys.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
} else {
|
||||
keygen.Validate()
|
||||
}
|
||||
|
||||
default:
|
||||
fmt.Printf("\nUnrecognized command '%s' \n\n", vars.CommandFlag)
|
||||
fmt.Printf("Allowed commands: firstrun, encrypt, decrypt, encode, decode, keygen, apikeygen, reset, resetpassword\n\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
}
|
||||
25
go.mod
Normal file
25
go.mod
Normal file
@@ -0,0 +1,25 @@
|
||||
module sources.truenas.cloud/code/syscrypt
|
||||
|
||||
go 1.25.5
|
||||
|
||||
require github.com/charmbracelet/lipgloss v1.1.0
|
||||
|
||||
require (
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 // indirect
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc // indirect
|
||||
github.com/charmbracelet/x/ansi v0.8.0 // indirect
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd // indirect
|
||||
github.com/charmbracelet/x/term v0.2.2
|
||||
github.com/cloudflare/circl v1.6.3
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 // indirect
|
||||
github.com/mattn/go-isatty v0.0.20 // indirect
|
||||
github.com/mattn/go-runewidth v0.0.16 // indirect
|
||||
github.com/muesli/termenv v0.16.0 // indirect
|
||||
github.com/rivo/uniseg v0.4.7 // indirect
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e // indirect
|
||||
golang.org/x/crypto v0.49.0
|
||||
golang.org/x/sys v0.42.0 // indirect
|
||||
|
||||
)
|
||||
|
||||
replace sources.truenas.cloud/code/syscrypt => ./
|
||||
34
go.sum
Normal file
34
go.sum
Normal file
@@ -0,0 +1,34 @@
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1 h1:HwpRHbFMcZLEVr42D4p7XBqjyuxQH5SMiErDT4WkJ2k=
|
||||
github.com/aymanbagabas/go-osc52/v2 v2.0.1/go.mod h1:uYgXzlJ7ZpABp8OJ+exZzJJhRNQ2ASbcXHWsFqH8hp8=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc h1:4pZI35227imm7yK2bGPcfpFEmuY1gc2YSTShr4iJBfs=
|
||||
github.com/charmbracelet/colorprofile v0.2.3-0.20250311203215-f60798e515dc/go.mod h1:X4/0JoqgTIPSFcRA/P6INZzIuyqdFY5rm8tb41s9okk=
|
||||
github.com/charmbracelet/lipgloss v1.1.0 h1:vYXsiLHVkK7fp74RkV7b2kq9+zDLoEU4MZoFqR/noCY=
|
||||
github.com/charmbracelet/lipgloss v1.1.0/go.mod h1:/6Q8FR2o+kj8rz4Dq0zQc3vYf7X+B0binUUBwA0aL30=
|
||||
github.com/charmbracelet/x/ansi v0.8.0 h1:9GTq3xq9caJW8ZrBTe0LIe2fvfLR/bYXKTx2llXn7xE=
|
||||
github.com/charmbracelet/x/ansi v0.8.0/go.mod h1:wdYl/ONOLHLIVmQaxbIYEC/cRKOQyjTkowiI4blgS9Q=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd h1:vy0GVL4jeHEwG5YOXDmi86oYw2yuYUGqz6a8sLwg0X8=
|
||||
github.com/charmbracelet/x/cellbuf v0.0.13-0.20250311204145-2c3ea96c31dd/go.mod h1:xe0nKWGd3eJgtqZRaN9RjMtK7xUYchjzPr7q6kcvCCs=
|
||||
github.com/charmbracelet/x/term v0.2.2 h1:xVRT/S2ZcKdhhOuSP4t5cLi5o+JxklsoEObBSgfgZRk=
|
||||
github.com/charmbracelet/x/term v0.2.2/go.mod h1:kF8CY5RddLWrsgVwpw4kAa6TESp6EB5y3uxGLeCqzAI=
|
||||
github.com/cloudflare/circl v1.6.3 h1:9GPOhQGF9MCYUeXyMYlqTR6a5gTrgR/fBLXvUgtVcg8=
|
||||
github.com/cloudflare/circl v1.6.3/go.mod h1:2eXP6Qfat4O/Yhh8BznvKnJ+uzEoTQ6jVKJRn81BiS4=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0 h1:1nnpGOrhyZZuNyfu1QjKiUICQ74+3FNCN69Aj6K7nkY=
|
||||
github.com/lucasb-eyer/go-colorful v1.2.0/go.mod h1:R4dSotOR9KMtayYi1e77YzuveK+i7ruzyGqttikkLy0=
|
||||
github.com/mattn/go-isatty v0.0.20 h1:xfD0iDuEKnDkl03q4limB+vH+GxLEtL/jb4xVJSWWEY=
|
||||
github.com/mattn/go-isatty v0.0.20/go.mod h1:W+V8PltTTMOvKvAeJH7IuucS94S2C6jfK/D7dTCTo3Y=
|
||||
github.com/mattn/go-runewidth v0.0.16 h1:E5ScNMtiwvlvB5paMFdw9p4kSQzbXFikJ5SQO6TULQc=
|
||||
github.com/mattn/go-runewidth v0.0.16/go.mod h1:Jdepj2loyihRzMpdS35Xk/zdY8IAYHsh153qUoGf23w=
|
||||
github.com/muesli/termenv v0.16.0 h1:S5AlUN9dENB57rsbnkPyfdGuWIlkmzJjbFf0Tf5FWUc=
|
||||
github.com/muesli/termenv v0.16.0/go.mod h1:ZRfOIKPFDYQoDFF4Olj7/QJbW60Ol/kL1pU3VfY/Cnk=
|
||||
github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc=
|
||||
github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ=
|
||||
github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e h1:JVG44RsyaB9T2KIHavMF/ppJZNG9ZpyihvCd0w101no=
|
||||
github.com/xo/terminfo v0.0.0-20220910002029-abceb7e1c41e/go.mod h1:RbqR21r5mrJuqunuUZ/Dhy/avygyECGrLceyNeo4LiM=
|
||||
golang.org/x/crypto v0.49.0 h1:+Ng2ULVvLHnJ/ZFEq4KdcDd/cfjrrjjNSXNzxg0Y4U4=
|
||||
golang.org/x/crypto v0.49.0/go.mod h1:ErX4dUh2UM+CFYiXZRTcMpEcN8b/1gxEuv3nODoYtCA=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561 h1:MDc5xs78ZrZr3HMQugiXOAkSZtfTpbJLDr/lwfgO53E=
|
||||
golang.org/x/exp v0.0.0-20220909182711-5c715a9e8561/go.mod h1:cyybsKvd6eL0RnXn6p/Grxp8F5bW7iYuBgsNCOHpMYE=
|
||||
golang.org/x/sys v0.6.0/go.mod h1:oPkhp1MJrh7nUepCBck5+mAzfO9JrbApNNgaTdGDITg=
|
||||
golang.org/x/sys v0.42.0 h1:omrd2nAlyT5ESRdCLYdm3+fMfNFE/+Rf4bDIQImRJeo=
|
||||
golang.org/x/sys v0.42.0/go.mod h1:4GL1E5IUh+htKOUEOaiffhrAeqysfVGipDYzABqnCmw=
|
||||
1
internal/actions/apikey/apikey.go
Executable file
1
internal/actions/apikey/apikey.go
Executable file
@@ -0,0 +1 @@
|
||||
package apikey
|
||||
600
internal/actions/decrypt/decrypt.go
Executable file
600
internal/actions/decrypt/decrypt.go
Executable file
@@ -0,0 +1,600 @@
|
||||
package decrypt
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/circl/kem/kyber/kyber768"
|
||||
"golang.org/x/crypto/argon2"
|
||||
cc20 "golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"sources.truenas.cloud/code/syscrypt"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/utils"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func Validate() {
|
||||
|
||||
utils.ClearTerminal()
|
||||
|
||||
decryptAllowedFlags := map[string]struct{}{
|
||||
"-C": {},
|
||||
"-k": {},
|
||||
"-i": {},
|
||||
"-c": {},
|
||||
"-o": {},
|
||||
"-A": {},
|
||||
"-P": {},
|
||||
}
|
||||
|
||||
utils.ValidateAllowedFlags(decryptAllowedFlags)
|
||||
|
||||
decryptRequiredFlags := map[string]bool{
|
||||
"-k": true,
|
||||
"-i": true,
|
||||
}
|
||||
|
||||
_ = decryptRequiredFlags
|
||||
|
||||
//utils.ValidateRequiredFlags(decryptRequiredFlags, "decrypt")
|
||||
|
||||
// -- Keys
|
||||
|
||||
isKeySet, keyHasValue := utils.IsFlagPassed("k")
|
||||
keyValue, _ := utils.GetFlagValue("k")
|
||||
keyIsValidPath := utils.IsValidPath(keyValue)
|
||||
keyExists := utils.FileExists(keyValue)
|
||||
keyIsValidJson, _ := utils.ValidateJSON(keyValue)
|
||||
|
||||
if isKeySet && !keyHasValue {
|
||||
msg := fmt.Sprintf("%s: -k KEY: requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isKeySet && !keyIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -k KEY: requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isKeySet && !keyExists {
|
||||
msg := fmt.Sprintf("%s: -k KEY: key file does not exist.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !keyIsValidJson {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Invalid JSON format.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var privateKeyPrefix string
|
||||
|
||||
privateKey, _ := utils.FetchFileKey(keyValue, "key")
|
||||
privateKeyParts := strings.Split(privateKey, "--")
|
||||
if len(privateKeyParts) != 2 {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Mismatch: Invalid Prefix '%s'. Invalid Length.\n", vars.CommandFlag, privateKeyParts[0]+vars.EndAnchor)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
privateKeyStart := privateKeyParts[0]
|
||||
privateKeyPrefix = strings.ReplaceAll(vars.PrivateKeyPrefixLabel, "--", "")
|
||||
|
||||
var match bool
|
||||
if privateKeyStart == privateKeyPrefix {
|
||||
match = true
|
||||
} else {
|
||||
match = false
|
||||
}
|
||||
|
||||
if !match {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Mismatch: Invalid Prefix '%s'\n", vars.CommandFlag, privateKeyStart+vars.EndAnchor)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Input
|
||||
|
||||
isInputSet, inputHasValue := utils.IsFlagPassed("i")
|
||||
inputValue, _ := utils.GetFlagValue("i")
|
||||
inputIsValidPath := utils.IsValidPath(inputValue)
|
||||
inputExists := utils.FileExists(inputValue)
|
||||
|
||||
if isInputSet && !inputHasValue {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isInputSet && !inputIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if !inputExists {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: File does not exist.\n%s", vars.CommandFlag, inputValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inputFileMode := utils.GetFileMode(inputValue)
|
||||
|
||||
if inputFileMode == 0 {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: File does not appear to be a valid encrypted file.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var apiValue string
|
||||
var apiIsValidPath bool
|
||||
var apiExists bool
|
||||
var apiIsValidJson bool
|
||||
var password string
|
||||
|
||||
isAPISet, apiHasValue := utils.IsFlagPassed("A")
|
||||
if isAPISet {
|
||||
apiValue, _ = utils.GetFlagValue("A")
|
||||
apiIsValidPath = utils.IsValidPath(apiValue)
|
||||
apiExists = utils.FileExists(apiValue)
|
||||
apiIsValidJson, _ = utils.ValidateJSON(apiValue)
|
||||
}
|
||||
|
||||
_ = apiHasValue
|
||||
_ = apiExists
|
||||
_ = apiIsValidJson
|
||||
|
||||
var lockValidationRequired bool
|
||||
|
||||
switch inputFileMode {
|
||||
case 1: // classic - do nothing
|
||||
lockValidationRequired = false
|
||||
case 2: // classic locked - apikey and password required
|
||||
lockValidationRequired = true
|
||||
|
||||
case 3: // hybrid - do nothing
|
||||
lockValidationRequired = false
|
||||
case 4: // hybrid locked - apikey and password required
|
||||
lockValidationRequired = true
|
||||
default:
|
||||
lockValidationRequired = false
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Unable to determine encryption mode. Invalid File.\n%s\n", vars.CommandFlag, inputValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isAPISet && lockValidationRequired {
|
||||
//
|
||||
|
||||
if !isAPISet {
|
||||
msg := fmt.Sprintf("%s: LOCKED FILE: -A APIKEY: flag is required.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isAPISet && !apiHasValue {
|
||||
msg := fmt.Sprintf("%s: LOCKED FILE: -A APIKEY: requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isAPISet && !apiIsValidPath {
|
||||
msg := fmt.Sprintf("%s: LOCKED FILE: -A APIKEY: requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
password := utils.PromptPassword()
|
||||
|
||||
_ = password
|
||||
|
||||
//
|
||||
}
|
||||
|
||||
_ = apiIsValidPath
|
||||
_ = password
|
||||
|
||||
// -- Output
|
||||
|
||||
isOutputSet, outputHasValue := utils.IsFlagPassed("o")
|
||||
outputValue, _ := utils.GetFlagValue("o")
|
||||
outputIsValidPath := utils.IsValidPath(outputValue)
|
||||
outputExists := utils.FileExists(outputValue)
|
||||
|
||||
if isOutputSet && !outputHasValue {
|
||||
msg := fmt.Sprintf("%s: -o OUTPUT: requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isOutputSet && !outputIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -o OUTPUT: requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// confirm overwrite.....
|
||||
|
||||
if isOutputSet && outputExists {
|
||||
utils.ConfirmOverwrite(outputValue)
|
||||
}
|
||||
|
||||
// Get Private Key
|
||||
|
||||
privateKey, err := utils.FetchFileKey(keyValue, "key")
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Error: %s", vars.CommandFlag, err)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
keyBytes, err := os.ReadFile(keyValue)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Could not read key file.\n%s\n", vars.CommandFlag, err)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var pKey syscrypt.PrivateKeyWrapper
|
||||
err = json.Unmarshal(keyBytes, &pKey)
|
||||
if err != nil {
|
||||
msg := fmt.Sprintf("%s: -k KEY: File is not a valid syscrypt key file. %v\n", vars.CommandFlag, err)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
DecryptFile(outputValue, inputValue, pKey)
|
||||
|
||||
_ = err
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func validateFile(inputValue string) (encryptionType bool, isLocked bool, isArmored bool) {
|
||||
|
||||
//switch mode {
|
||||
//case 0x01:
|
||||
// fmt.Printf("classic")
|
||||
//case 0x02:
|
||||
// fmt.Printf("classic with lock")
|
||||
//case 0x03:
|
||||
// fmt.Printf("post quantum")
|
||||
//case 0x04:
|
||||
// fmt.Printf("post quantum with lock")
|
||||
//default:
|
||||
// fmt.Printf("invalid")
|
||||
//}
|
||||
|
||||
return false, false, false
|
||||
|
||||
//file, err := os.Open(inputValue)
|
||||
//if err != nil {
|
||||
// fmt.Printf("Error: cannot open %s\n", inputValue)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
//defer file.Close()
|
||||
|
||||
//header := make([]byte, 1)
|
||||
//_, err = file.Read(header)
|
||||
//if err != nil {
|
||||
// fmt.Println("Error: file is empty or unreadable")
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
//file, err := os.Open(filePath)
|
||||
//if err != nil {
|
||||
// return false, false, err
|
||||
//}
|
||||
//defer file.Close()
|
||||
|
||||
//buffer := make([]byte, 512)
|
||||
//n, err := file.Read(buffer)
|
||||
//if err != nil && n == 0 {
|
||||
// return false, false, err
|
||||
//}
|
||||
|
||||
//preview := string(buffer[:n])
|
||||
//nPreview := strings.ReplaceAll(preview, vars.PrivateKeyHeader+"\n", "")
|
||||
//nPreview = strings.ReplaceAll(nPreview, vars.PrivateKeyFooter+"\n", "")
|
||||
//isBase64 := IsValidBase64WithLines(preview)
|
||||
|
||||
//if strings.HasPrefix(preview, vars.PrivateKeyHeader) {
|
||||
// isArmored = true
|
||||
//} else {
|
||||
// isArmored = false
|
||||
//}
|
||||
|
||||
//mode := header[0]
|
||||
|
||||
//switch mode {
|
||||
//case 0x01:
|
||||
// return 1 // Classical Encryption
|
||||
//case 0x02:
|
||||
// return 2 // Classical Encryption [with lock]
|
||||
//case 0x03:
|
||||
// return 3 // Post Quantum Encryption
|
||||
//case 0x04:
|
||||
// return 4 // Post Quantum Encryption [with lock]
|
||||
//default:
|
||||
// return 5 // Invalid or Armored
|
||||
//}
|
||||
|
||||
}
|
||||
|
||||
func DecryptFile(out string, input string, priv syscrypt.PrivateKeyWrapper) {
|
||||
|
||||
outputValue, _ := utils.GetFlagValue("o")
|
||||
inputValue, _ := utils.GetFlagValue("i")
|
||||
|
||||
isOutputSet, outputHasValue := utils.IsFlagPassed("o")
|
||||
isOutputValidPath := utils.IsValidPath(outputValue)
|
||||
outputExists := utils.FileExists(outputValue)
|
||||
passValue, _ := utils.GetFlagValue("P")
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
|
||||
raw, _ := os.ReadFile(input)
|
||||
var blob []byte
|
||||
|
||||
if strings.Contains(string(raw), vars.PrivateKeyHeader) {
|
||||
c := strings.ReplaceAll(string(raw), vars.PrivateKeyHeader, "")
|
||||
c = strings.ReplaceAll(c, vars.PrivateKeyFooter, "")
|
||||
c = strings.ReplaceAll(c, "\n", "")
|
||||
c = strings.ReplaceAll(c, "\r", "")
|
||||
blob, _ = base64.StdEncoding.DecodeString(strings.TrimSpace(c))
|
||||
} else {
|
||||
|
||||
blob = raw
|
||||
|
||||
}
|
||||
|
||||
// unblind header
|
||||
|
||||
ephPubX := blob[2:34]
|
||||
mode := blob[0] ^ ephPubX[0]
|
||||
serialSize := int(blob[1] ^ ephPubX[1])
|
||||
|
||||
serialOffset := 34
|
||||
dataPtr := serialOffset + serialSize
|
||||
|
||||
recoveredSerial := make([]byte, serialSize)
|
||||
for i := 0; i < serialSize; i++ {
|
||||
recoveredSerial[i] = blob[serialOffset+i] ^ ephPubX[(i+2)%32]
|
||||
}
|
||||
|
||||
if priv.PrivateKey.Serial != string(recoveredSerial) {
|
||||
fmt.Printf("Access Denied: Serial Mismatch or invalid key type.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
isHybrid := (mode == 0x03 || mode == 0x04)
|
||||
isLocked := (mode == 0x02 || mode == 0x04)
|
||||
|
||||
// Password Verification for Locked files
|
||||
var passKey []byte
|
||||
|
||||
if isLocked {
|
||||
//
|
||||
apiKey, err := utils.FetchFileKey(apiValue, "key")
|
||||
if err != nil {
|
||||
fmt.Printf("Error: %s", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
passKey = argon2.IDKey([]byte(passValue), []byte(apiKey), 1, 64*1024, 4, 32)
|
||||
|
||||
vH := hkdf.New(sha256.New, passKey, nil, []byte("syscrypt-pass-verify"))
|
||||
vK := make([]byte, 32)
|
||||
io.ReadFull(vH, vK)
|
||||
vAead, _ := cc20.New(vK)
|
||||
res, err := vAead.Open(nil, make([]byte, 12), blob[dataPtr:dataPtr+vars.PassTagSize], nil)
|
||||
if err != nil || !bytes.Equal(res, []byte("SYSC-PASS-OK")) {
|
||||
fmt.Printf("Invalid Master Password.")
|
||||
os.Exit(1)
|
||||
}
|
||||
dataPtr += vars.PassTagSize
|
||||
//
|
||||
}
|
||||
|
||||
// Slice Hybrid & Data
|
||||
|
||||
var kyberCT []byte
|
||||
if isHybrid {
|
||||
kyberCT = blob[dataPtr : dataPtr+vars.KyberCTSize]
|
||||
dataPtr += vars.KyberCTSize
|
||||
}
|
||||
|
||||
nonce := blob[dataPtr : dataPtr+12]
|
||||
ciphertext := blob[dataPtr+12:]
|
||||
|
||||
// Final Key Reconstruction
|
||||
|
||||
cleanPrivX := strings.TrimPrefix(priv.PrivateKey.Key, vars.PrivateKeyPrefixLabel)
|
||||
myPrivX, _ := hex.DecodeString(cleanPrivX)
|
||||
sharedX, _ := curve25519.X25519(myPrivX, ephPubX)
|
||||
|
||||
var sharedML []byte
|
||||
if isHybrid {
|
||||
scheme := kyber768.Scheme()
|
||||
cleanML := strings.TrimPrefix(priv.PrivateKey.MLKEMKey, vars.PQPrivateKeyPrefixLabel)
|
||||
skBytes, _ := hex.DecodeString(cleanML)
|
||||
skK, _ := scheme.UnmarshalBinaryPrivateKey(skBytes)
|
||||
sharedML, _ = scheme.Decapsulate(skK, kyberCT)
|
||||
}
|
||||
|
||||
combined := append(sharedX, sharedML...)
|
||||
if isLocked {
|
||||
combined = append(combined, passKey...)
|
||||
}
|
||||
|
||||
h := hkdf.New(sha256.New, combined, nil, []byte("syscrypt-v1-hybrid"))
|
||||
symmK := make([]byte, 32)
|
||||
io.ReadFull(h, symmK)
|
||||
aead, _ := cc20.New(symmK)
|
||||
plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Decryption failed")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isOutputSet {
|
||||
os.WriteFile(outputValue, plaintext, 0644)
|
||||
fmt.Printf("Successfully decrypted file to: %s\n", outputValue)
|
||||
}
|
||||
|
||||
if !isOutputSet {
|
||||
fmt.Printf("%s\n", plaintext)
|
||||
}
|
||||
|
||||
_ = inputValue
|
||||
_ = isOutputSet
|
||||
_ = outputHasValue
|
||||
_ = isOutputValidPath
|
||||
_ = outputExists
|
||||
_ = dataPtr
|
||||
_ = kyberCT
|
||||
_ = nonce
|
||||
_ = ciphertext
|
||||
_ = sharedX
|
||||
|
||||
//outputValue, _ := utils.GetFlagValue("o")
|
||||
//inputValue, _ := utils.GetFlagValue("i")
|
||||
|
||||
//isOutputSet, outputHasValue := utils.IsFlagPassed("o")
|
||||
//outputValue, _ := utils.GetFlagValue("k")
|
||||
//outputIsValidPath := utils.IsValidPath(outValue)
|
||||
//outputExists := utils.FileExists(outputValue)
|
||||
|
||||
/*
|
||||
|
||||
rawInput, err := os.ReadFile(inputValue)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Unable to read %s\n", inputValue)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var blob []byte
|
||||
inputStr := string(rawInput)
|
||||
|
||||
if strings.Contains(inputStr, vars.PrivateKeyHeader) {
|
||||
content := strings.ReplaceAll(inputStr, vars.PrivateKeyHeader, "")
|
||||
content = strings.ReplaceAll(content, vars.PrivateKeyFooter, "")
|
||||
content = strings.ReplaceAll(content, "\n", "")
|
||||
content = strings.ReplaceAll(content, "\r", "")
|
||||
content = strings.TrimSpace(content)
|
||||
blob, err = base64.StdEncoding.DecodeString(content)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Malformed Armored Base64 data.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
} else {
|
||||
|
||||
blob = rawInput
|
||||
|
||||
}
|
||||
|
||||
if len(blob) < 35 {
|
||||
fmt.Printf("Error: -i INPUT: File is too small to be a valid syscrypt encrypted message.\n")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
mode := blob[0]
|
||||
serialSize := int(blob[1])
|
||||
|
||||
ephOffset := 2 + serialSize
|
||||
headerOffset := ephOffset + 32
|
||||
|
||||
if len(blob) < headerOffset {
|
||||
fmt.Printf("Error: Header truncated or invalid serial length.\n")
|
||||
}
|
||||
|
||||
maskedSerial := blob[2:ephOffset]
|
||||
ephPubX := blob[ephOffset:headerOffset]
|
||||
|
||||
recoveredBytes := make([]byte, serialSize)
|
||||
for i := 0; i < serialSize; i++ {
|
||||
recoveredBytes[i] = maskedSerial[i] ^ ephPubX[i%32]
|
||||
}
|
||||
fileSerial := string(recoveredBytes)
|
||||
|
||||
if fileSerial != priv.PrivateKey.Serial {
|
||||
fmt.Printf("Error: File requires key [%s], but you provided [%s]\n", fileSerial, priv.PrivateKey.Serial)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var kyberCT, nonce, ciphertext []byte
|
||||
isHybrid := mode == 0x03 || mode == 0x04
|
||||
isLocked := mode == 0x02 || mode == 0x04
|
||||
|
||||
if isHybrid {
|
||||
kyberCT = blob[headerOffset : headerOffset+1088]
|
||||
nonce = blob[headerOffset+1088 : headerOffset+1100]
|
||||
ciphertext = blob[headerOffset+1100:]
|
||||
} else {
|
||||
nonce = blob[headerOffset : headerOffset+12]
|
||||
ciphertext = blob[headerOffset+12:]
|
||||
}
|
||||
|
||||
cleanPriv := strings.TrimPrefix(priv.PrivateKey.Key, vars.PrivateKeyPrefixLabel)
|
||||
myPrivX, _ := hex.DecodeString(cleanPriv)
|
||||
sharedX, _ := curve25519.X25519(myPrivX, ephPubX)
|
||||
|
||||
var sharedML []byte
|
||||
if isHybrid {
|
||||
scheme := kyber768.Scheme()
|
||||
skBytes, _ := hex.DecodeString(priv.PrivateKey.MLKEMKey)
|
||||
skK, _ := scheme.UnmarshalBinaryPrivateKey(skBytes)
|
||||
sharedML, _ = scheme.Decapsulate(skK, kyberCT)
|
||||
}
|
||||
|
||||
combined := append(sharedX, sharedML...)
|
||||
if isLocked {
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
passValue, _ := utils.GetFlagValue("P")
|
||||
|
||||
passKey := argon2.IDKey([]byte(passValue), []byte(apiKey), 1, 64*1024, 4, 32)
|
||||
combined = append(combined, passKey...)
|
||||
defer utils.Zeroize(passKey)
|
||||
}
|
||||
|
||||
h := hkdf.New(sha256.New, combined, nil, []byte("syscrypt-v1-hybrid"))
|
||||
symmKey := make([]byte, 32)
|
||||
io.ReadFull(h, symmKey)
|
||||
defer utils.Zeroize(symmKey)
|
||||
|
||||
aead, _ := cc20.New(symmKey)
|
||||
plaintext, err := aead.Open(nil, nonce, ciphertext, nil)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Decryption failed. Potentional data corruption or wrong password.")
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isOutputSet && outputIsValidPath {
|
||||
|
||||
err = os.WriteFile(outputValue, plaintext, 0644)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Unable to write decrypted file %s\n", outputValue)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
} else {
|
||||
/// do stuff that isnt output
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
829
internal/actions/encrypt/encrypt.go
Executable file
829
internal/actions/encrypt/encrypt.go
Executable file
@@ -0,0 +1,829 @@
|
||||
package encrypt
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"io"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"github.com/cloudflare/circl/kem/kyber/kyber768"
|
||||
"golang.org/x/crypto/argon2"
|
||||
cc20 "golang.org/x/crypto/chacha20poly1305"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
"sources.truenas.cloud/code/syscrypt"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/utils"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func Validate() {
|
||||
|
||||
encryptAllowedFlags := map[string]struct{}{
|
||||
"-C": {},
|
||||
"-L": {},
|
||||
"-a": {},
|
||||
"-k": {},
|
||||
"-i": {},
|
||||
"-c": {},
|
||||
"-o": {},
|
||||
"-A": {},
|
||||
"-P": {},
|
||||
"-PQ": {},
|
||||
}
|
||||
|
||||
for _, arg := range os.Args {
|
||||
if arg == "-L" {
|
||||
encryptAllowedFlags["-A"] = struct{}{}
|
||||
encryptAllowedFlags["-P"] = struct{}{}
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
utils.ValidateAllowedFlags(encryptAllowedFlags)
|
||||
|
||||
encryptRequiredFlags := map[string]bool{
|
||||
"-k": true,
|
||||
"-i": true,
|
||||
"-o": true,
|
||||
}
|
||||
|
||||
for _, arg := range os.Args {
|
||||
if arg == "-L" {
|
||||
encryptRequiredFlags["-A"] = true
|
||||
encryptRequiredFlags["-P"] = true
|
||||
break
|
||||
}
|
||||
}
|
||||
|
||||
//utils.ValidateRequiredFlags(encryptRequiredFlags, "encrypt")
|
||||
|
||||
isKeySet, keyHasValue := utils.IsFlagPassed("k")
|
||||
keyValue, _ := utils.GetFlagValue("k")
|
||||
keyIsValidPath := utils.IsValidPath(keyValue)
|
||||
|
||||
isInputSet, inputHasValue := utils.IsFlagPassed("i")
|
||||
inputValue, _ := utils.GetFlagValue("i")
|
||||
inputIsValidPath := utils.IsValidPath(inputValue)
|
||||
|
||||
isOutputSet, outputHasValue := utils.IsFlagPassed("o")
|
||||
outputValue, _ := utils.GetFlagValue("o")
|
||||
outputIsValidPath := utils.IsValidPath(outputValue)
|
||||
outputFileExists := utils.FileExists(outputValue)
|
||||
|
||||
isArmoredSet, armoredHasValue := utils.IsFlagPassed("a")
|
||||
|
||||
isLockSet, lockHasValue := utils.IsFlagPassed("L")
|
||||
lockValue, _ := utils.GetFlagValue("L")
|
||||
|
||||
_ = lockValue
|
||||
|
||||
isAPISet, _ := utils.IsFlagPassed("A")
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
apiIsValidPath := utils.IsValidPath(apiValue)
|
||||
apiFileExists := utils.FileExists(apiValue)
|
||||
|
||||
//isPassSet, _ := utils.IsFlagPassed("P")
|
||||
//passValue, _ := utils.GetFlagValue("P")
|
||||
//passIsValidPath := utils.IsValidPath(passValue)
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
// -- Key
|
||||
|
||||
if isKeySet && !keyHasValue {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isKeySet && !keyIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
keyExists := utils.FileExists(keyValue)
|
||||
if !keyExists {
|
||||
msg := fmt.Sprintf("%s: -k KEY: Key file does not exist.\n%s\n", vars.CommandFlag, keyValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Input
|
||||
|
||||
if isInputSet && !inputHasValue {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isInputSet && !inputIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
inputExists := utils.FileExists(inputValue)
|
||||
if !inputExists {
|
||||
msg := fmt.Sprintf("%s: -i INPUT: Input file does not exist \n%s\n", vars.CommandFlag, inputValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Output
|
||||
|
||||
if isOutputSet && !outputHasValue {
|
||||
msg := fmt.Sprintf("%s: -o OUTPUT: Requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isOutputSet && !outputIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -o OUTPUT: Requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Armored
|
||||
|
||||
if isArmoredSet && armoredHasValue {
|
||||
msg := fmt.Sprintf("%s: -a ARMORED: Armored does not support a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Lock
|
||||
|
||||
if isLockSet && lockHasValue {
|
||||
msg := fmt.Sprintf("%s: -L LOCK: Lock does not support a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isLockSet && !isAPISet {
|
||||
msg := fmt.Sprintf("%s: -L LOCK: Lock requires the -A APIKEY flag and value to be set.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
//if isLockSet && !isPassSet {
|
||||
// msg := fmt.Sprintf("%s: -L LOCK: Lock requires the -P MASTER PASSWORD flag and value to be set.", vars.CommandFlag)
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
// -- API
|
||||
|
||||
if isLockSet && apiValue == "" {
|
||||
msg := fmt.Sprintf("%s: -A APIKEY: Requires a value.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isLockSet && !apiIsValidPath {
|
||||
msg := fmt.Sprintf("%s: -A APIKEY: Requires a valid file path.\n", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isLockSet && !apiFileExists {
|
||||
msg := fmt.Sprintf("%s: -A APIKEY: Key file does not exist.\n%s\n", vars.CommandFlag, apiValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// -- Password
|
||||
|
||||
//if isLockSet && passValue == "" {
|
||||
// msg := fmt.Sprintf("%s: -P MASTER PASSWORD: Requires a value.\n", vars.CommandFlag)
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
if isLockSet {
|
||||
//masterPass := config.MasterPass
|
||||
//masterPassIsValidPath := utils.IsValidPath(masterPass)
|
||||
|
||||
//if masterPass == "" {
|
||||
// msg := fmt.Sprintf("%s: Unable to determine location of the Master Password file.\nUnable to continue.\n"+
|
||||
// "Please run syscrypt -c keygen again to generate the default keys.", vars.CommandFlag)
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
//if !masterPassIsValidPath {
|
||||
// msg := fmt.Sprintf("%s: Invalid Master Password location.\n%s"+
|
||||
// "Please run syscrypt -c keygen again to generate the default keys.", vars.CommandFlag, masterPass)
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
}
|
||||
|
||||
//if isLockSet && !passIsValidPath {
|
||||
// msg := fmt.Sprintf("%s: -P MASTER PASSWORD: Requires a valid file path.\n", vars.CommandFlag)
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
if outputFileExists {
|
||||
utils.ConfirmOverwrite(outputValue)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////
|
||||
|
||||
fmt.Printf("\n\n")
|
||||
fmt.Printf("keyset: %v\n", isKeySet)
|
||||
fmt.Printf("keyvalue: %v\n", keyValue)
|
||||
fmt.Printf("keyvalidpath: %v\n", keyIsValidPath)
|
||||
fmt.Printf("\n")
|
||||
|
||||
fmt.Printf("inputset: %v\n", isInputSet)
|
||||
fmt.Printf("inputvalue: %v\n", inputValue)
|
||||
fmt.Printf("inputvalidpath: %v\n", inputIsValidPath)
|
||||
fmt.Printf("\n")
|
||||
|
||||
fmt.Printf("outputset: %v\n", isOutputSet)
|
||||
fmt.Printf("outputvalue: %v\n", outputValue)
|
||||
fmt.Printf("outputvalidpath: %v\n", outputIsValidPath)
|
||||
fmt.Printf("\n")
|
||||
|
||||
// Open Files
|
||||
|
||||
keyBytes, err := os.ReadFile(keyValue)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Could not read key file at %s\n", keyValue)
|
||||
}
|
||||
|
||||
var pub syscrypt.PublicKeyWrapper
|
||||
err = json.Unmarshal(keyBytes, &pub)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: key file is not a valid syscrypt JSON: %v\n", err)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
EncryptFile(outputValue, inputValue, pub)
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func zeroize(data []byte) {
|
||||
for i := range data {
|
||||
data[i] = 0
|
||||
}
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const PassTagSize = 28
|
||||
|
||||
// func EncryptFile(dst io.Writer, src io.Reader, pub syscrypt.PublicKeyWrapper) {
|
||||
func EncryptFile(outputValue string, inputValue string, pub syscrypt.PublicKeyWrapper) {
|
||||
|
||||
// Get Values
|
||||
|
||||
isLockSet, _ := utils.IsFlagPassed("L")
|
||||
isArmoredSet, _ := utils.IsFlagPassed("a")
|
||||
keyValue, _ := utils.GetFlagValue("k")
|
||||
passValue, _ := utils.GetFlagValue("P")
|
||||
apiIsSet, _ := utils.IsFlagPassed("A")
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
serialKey, _ := utils.FetchFileKey(keyValue, "serial")
|
||||
|
||||
src, _ := os.Open(inputValue)
|
||||
plaintext, _ := io.ReadAll(src)
|
||||
src.Close()
|
||||
|
||||
// Keys
|
||||
|
||||
cleanPubX := strings.TrimPrefix(pub.PublicKey.Key, vars.DefaultPrefixLabel)
|
||||
recipientPubX, _ := hex.DecodeString(cleanPubX)
|
||||
ephPrivX := make([]byte, 32)
|
||||
io.ReadFull(rand.Reader, ephPrivX)
|
||||
ephPubX, _ := curve25519.X25519(ephPrivX, curve25519.Basepoint)
|
||||
sharedX, _ := curve25519.X25519(ephPrivX, recipientPubX)
|
||||
|
||||
var kyberCT []byte
|
||||
var sharedML []byte
|
||||
|
||||
isHybrid := pub.PublicKey.MLKEMKey != ""
|
||||
|
||||
if isHybrid {
|
||||
scheme := kyber768.Scheme()
|
||||
cleanML := strings.TrimPrefix(pub.PublicKey.MLKEMKey, vars.PQPublicKeyPrefixLabel)
|
||||
pkBytes, err := hex.DecodeString(cleanML)
|
||||
if err != nil {
|
||||
fmt.Printf("Hex Decode Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
pkK, err := scheme.UnmarshalBinaryPublicKey(pkBytes)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: kyber key unmarshal fail: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
kyberCT, sharedML, err = scheme.Encapsulate(pkK)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Kyber encapsulation failed: %v\n", err)
|
||||
}
|
||||
}
|
||||
|
||||
// Password Tag Generation
|
||||
|
||||
var passKey, passVerifyTag []byte
|
||||
if isLockSet {
|
||||
apiKey, _ := utils.FetchFileKey(apiValue, "key")
|
||||
passKey = argon2.IDKey([]byte(passValue), []byte(apiKey), 1, 64*1024, 4, 32)
|
||||
|
||||
vH := hkdf.New(sha256.New, passKey, nil, []byte("syscrypt-pass-verify"))
|
||||
vKey := make([]byte, 32)
|
||||
io.ReadFull(vH, vKey)
|
||||
vAead, _ := cc20.New(vKey)
|
||||
vNonce := make([]byte, 12)
|
||||
passVerifyTag = vAead.Seal(nil, vNonce, []byte("SYSC-PASS-OK"), nil)
|
||||
}
|
||||
|
||||
// Final Symmetric Key
|
||||
|
||||
combined := append(sharedX, sharedML...)
|
||||
if isLockSet {
|
||||
combined = append(combined, passKey...)
|
||||
}
|
||||
|
||||
h := hkdf.New(sha256.New, combined, nil, []byte("syscrypt-v1-hybrid"))
|
||||
symmKey := make([]byte, 32)
|
||||
io.ReadFull(h, symmKey)
|
||||
aead, _ := cc20.New(symmKey)
|
||||
for i := range symmKey {
|
||||
symmKey[i] = 0
|
||||
}
|
||||
|
||||
nonce := make([]byte, 12)
|
||||
io.ReadFull(rand.Reader, nonce)
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
||||
|
||||
// Blinded Header Assembly
|
||||
|
||||
serialBytes := []byte(serialKey)
|
||||
serialSize := len(serialBytes)
|
||||
|
||||
var mode byte
|
||||
if isHybrid && isLockSet {
|
||||
mode = 0x04
|
||||
} else if isHybrid {
|
||||
mode = 0x03
|
||||
} else if isLockSet {
|
||||
mode = 0x02
|
||||
} else {
|
||||
mode = 0x01
|
||||
}
|
||||
|
||||
var finalBlob []byte
|
||||
finalBlob = append(finalBlob, mode^ephPubX[0])
|
||||
finalBlob = append(finalBlob, byte(serialSize)^ephPubX[1])
|
||||
finalBlob = append(finalBlob, ephPubX...)
|
||||
|
||||
for i := 0; i < serialSize; i++ {
|
||||
finalBlob = append(finalBlob, serialBytes[i]^ephPubX[(i+2)%32])
|
||||
}
|
||||
|
||||
if isLockSet {
|
||||
finalBlob = append(finalBlob, passVerifyTag...)
|
||||
}
|
||||
|
||||
if isHybrid {
|
||||
finalBlob = append(finalBlob, kyberCT...)
|
||||
}
|
||||
|
||||
finalBlob = append(finalBlob, nonce...)
|
||||
finalBlob = append(finalBlob, ciphertext...)
|
||||
|
||||
dst, err := os.Create(outputValue)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: Unable to create output file %s", outputValue)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if isArmoredSet {
|
||||
dst.WriteString(vars.PrivateKeyHeader + "\n")
|
||||
enc := base64.StdEncoding.EncodeToString(finalBlob)
|
||||
|
||||
for i := 0; i < len(enc); i += 64 {
|
||||
end := i + 64
|
||||
if end > len(enc) {
|
||||
end = len(enc)
|
||||
}
|
||||
dst.WriteString(enc[i:end] + "\n")
|
||||
}
|
||||
dst.WriteString(vars.PrivateKeyFooter + "\n")
|
||||
|
||||
} else {
|
||||
|
||||
dst.Write(finalBlob)
|
||||
|
||||
}
|
||||
|
||||
_ = apiIsSet
|
||||
|
||||
fmt.Printf("Success: file saved at: %s\n", outputValue)
|
||||
|
||||
/*
|
||||
|
||||
// 1. Setup Flags
|
||||
|
||||
isLockSet, _ := utils.IsFlagPassed("L")
|
||||
isAPISet, _ := utils.IsFlagPassed("A")
|
||||
isArmoredSet, _ := utils.IsFlagPassed("a")
|
||||
keyValue, _ := utils.GetFlagValue("k")
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
passValue, _ := utils.GetFlagValue("P")
|
||||
|
||||
// 2. Read Plaintext
|
||||
|
||||
src, _ := os.Open(inputValue)
|
||||
plaintext, _ := io.ReadAll(src)
|
||||
src.Close()
|
||||
|
||||
// 3. Classical Key Exchange (X25519)
|
||||
|
||||
cleanPubX := strings.TrimPrefix(pub.PublicKey.Key, "syscrypt-")
|
||||
recipientPubX, _ := hex.DecodeString(cleanPubX)
|
||||
|
||||
ephPrivX := make([]byte, 32)
|
||||
io.ReadFull(rand.Reader, ephPrivX)
|
||||
ephPubX, _ := curve25519.X25519(ephPrivX, curve25519.Basepoint)
|
||||
sharedX, _ := curve25519.X25519(ephPrivX, recipientPubX)
|
||||
|
||||
// 4. Post-Quantum Key Exchange (Kyber768)
|
||||
|
||||
//var kyberCT, sharedML []byte
|
||||
//isHybrid := pub.PublicKey.MLKEMKey != ""
|
||||
|
||||
var kyberCT []byte
|
||||
var sharedML []byte
|
||||
isHybrid := pub.PublicKey.MLKEMKey != ""
|
||||
|
||||
if isHybrid {
|
||||
scheme := kyber768.Scheme()
|
||||
cleanML := strings.TrimPrefix(pub.PublicKey.MLKEMKey, "syscrypt-pq-")
|
||||
pkBytes, err := hex.DecodeString(cleanML)
|
||||
if err != nil {
|
||||
fmt.Printf("Hex Decode Error: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
pkK, err := scheme.UnmarshalBinaryPublicKey(pkBytes)
|
||||
if err != nil {
|
||||
fmt.Printf("Error: kyber key unmarshal failed: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
var encapErr error
|
||||
kyberCT, sharedML, encapErr = scheme.Encapsulate(pkK)
|
||||
if encapErr != nil {
|
||||
fmt.Printf("Error: Kyber encapsulation failed: %v\n", encapErr)
|
||||
return
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
// 5. Entropy Binding
|
||||
|
||||
combined := append(sharedX, sharedML...)
|
||||
if isLockSet {
|
||||
var apiKey string
|
||||
if isAPISet {
|
||||
apiKey, _ = utils.FetchFileKey(apiValue, "key")
|
||||
}
|
||||
passKey := argon2.IDKey([]byte(passValue), []byte(apiKey), 1, 64*1024, 4, 32)
|
||||
combined = append(combined, passKey...)
|
||||
}
|
||||
|
||||
// 6. Key Derivation & Encryption
|
||||
|
||||
h := hkdf.New(sha256.New, combined, nil, []byte("syscrypt-v1-hybrid"))
|
||||
symmetricKey := make([]byte, 32)
|
||||
io.ReadFull(h, symmetricKey)
|
||||
|
||||
aead, _ := cc20.New(symmetricKey)
|
||||
for i := range symmetricKey {
|
||||
symmetricKey[i] = 0
|
||||
}
|
||||
|
||||
nonce := make([]byte, aead.NonceSize())
|
||||
io.ReadFull(rand.Reader, nonce)
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
||||
|
||||
// 7. Metadata/Serial Masking
|
||||
|
||||
serialKey, _ := utils.FetchFileKey(keyValue, "serial")
|
||||
serialBytes := []byte(serialKey)
|
||||
serialSize := len(serialBytes)
|
||||
maskedSerial := make([]byte, serialSize)
|
||||
for i := 0; i < serialSize; i++ {
|
||||
maskedSerial[i] = serialBytes[i] ^ ephPubX[i%32]
|
||||
}
|
||||
|
||||
// 8. Determine Mode
|
||||
|
||||
var mode byte
|
||||
if isHybrid && isLockSet {
|
||||
mode = 0x04
|
||||
} else if isHybrid {
|
||||
mode = 0x03
|
||||
} else if isLockSet {
|
||||
mode = 0x02
|
||||
} else {
|
||||
mode = 0x01
|
||||
}
|
||||
|
||||
// 9. Final Assembly
|
||||
|
||||
var finalBlob []byte
|
||||
finalBlob = append(finalBlob, mode)
|
||||
finalBlob = append(finalBlob, byte(serialSize))
|
||||
finalBlob = append(finalBlob, maskedSerial...)
|
||||
finalBlob = append(finalBlob, ephPubX...)
|
||||
|
||||
if isHybrid {
|
||||
if len(kyberCT) != 1088 {
|
||||
fmt.Printf("CRITICAL: kyberCT is %d bytes, expected 1088\n", len(kyberCT))
|
||||
return
|
||||
}
|
||||
finalBlob = append(finalBlob, kyberCT...)
|
||||
}
|
||||
|
||||
finalBlob = append(finalBlob, nonce...)
|
||||
finalBlob = append(finalBlob, ciphertext...)
|
||||
|
||||
// 10. Write output
|
||||
|
||||
dst, _ := os.Create(outputValue)
|
||||
defer dst.Close()
|
||||
|
||||
if isArmoredSet {
|
||||
dst.WriteString(vars.PrivateKeyHeader + "\n")
|
||||
encoded := base64.StdEncoding.EncodeToString(finalBlob)
|
||||
|
||||
for i := 0; i < len(encoded); i += 64 {
|
||||
end := i + 64
|
||||
if end > len(encoded) {
|
||||
end = len(encoded)
|
||||
}
|
||||
dst.WriteString(encoded[i:end] + "\n")
|
||||
}
|
||||
dst.WriteString(vars.PrivateKeyFooter + "\n")
|
||||
|
||||
} else {
|
||||
dst.Write(finalBlob)
|
||||
}
|
||||
|
||||
fmt.Printf("File created: %s (mode %d, %d bytes)\n", outputValue, mode, len(finalBlob))
|
||||
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
isLockSet, _ := utils.IsFlagPassed("L")
|
||||
isAPISet, _ := utils.IsFlagPassed("A")
|
||||
isPassSet, _ := utils.IsFlagPassed("P")
|
||||
isArmoredSet, _ := utils.IsFlagPassed("a")
|
||||
/////////
|
||||
isKeyPassed, _ := utils.IsFlagPassed("k")
|
||||
keyValue, _ := utils.GetFlagValue("k")
|
||||
_ = isKeyPassed
|
||||
//////////
|
||||
|
||||
// set values
|
||||
|
||||
apiValue, _ := utils.GetFlagValue("A")
|
||||
passValue, _ := utils.GetFlagValue("P")
|
||||
|
||||
_ = passValue
|
||||
|
||||
var apiKey string
|
||||
var pass string
|
||||
|
||||
if isAPISet {
|
||||
apiKey, _ = utils.FetchFileKey(apiValue, "key")
|
||||
}
|
||||
|
||||
if isPassSet {
|
||||
pass = ""
|
||||
}
|
||||
|
||||
//isInputSet, inputHasValue := utils.IsFlagPassed("i")
|
||||
//inputValue, _ := utils.GetFlagValue("i")
|
||||
|
||||
src, _ := os.Open(inputValue)
|
||||
|
||||
plaintext, _ := io.ReadAll(src)
|
||||
|
||||
cleanPub := strings.TrimPrefix(pub.PublicKey.Key, "syscrypt")
|
||||
recipientPubX, _ := hex.DecodeString(cleanPub)
|
||||
|
||||
ephPrivX := make([]byte, 32)
|
||||
io.ReadFull(rand.Reader, ephPrivX)
|
||||
ephPubX, _ := curve25519.X25519(ephPrivX, curve25519.Basepoint)
|
||||
sharedX, _ := curve25519.X25519(ephPrivX, recipientPubX)
|
||||
|
||||
var kyberCT, sharedML []byte
|
||||
|
||||
isHybrid := pub.PublicKey.MLKEMKey != ""
|
||||
if isHybrid {
|
||||
scheme := kyber768.Scheme()
|
||||
pkBytes, _ := hex.DecodeString(pub.PublicKey.MLKEMKey)
|
||||
pkK, _ := scheme.UnmarshalBinaryPublicKey(pkBytes)
|
||||
sharedML, kyberCT, _ = scheme.Encapsulate(pkK)
|
||||
}
|
||||
|
||||
combined := append(sharedX, sharedML...)
|
||||
if isLockSet {
|
||||
passKey := argon2.IDKey([]byte(pass), []byte(apiKey), 1, 64*1024, 4, 32)
|
||||
combined = append(combined, passKey...)
|
||||
zeroize(passKey)
|
||||
}
|
||||
|
||||
h := hkdf.New(sha256.New, combined, nil, []byte("syscrypt-v1-hybrid"))
|
||||
symmetricKey := make([]byte, 32)
|
||||
io.ReadFull(h, symmetricKey)
|
||||
zeroize(symmetricKey)
|
||||
|
||||
aead, _ := cc20.New(symmetricKey)
|
||||
nonce := make([]byte, aead.NonceSize())
|
||||
io.ReadFull(rand.Reader, nonce)
|
||||
ciphertext := aead.Seal(nil, nonce, plaintext, nil)
|
||||
|
||||
var mode byte
|
||||
|
||||
switch {
|
||||
case isHybrid && isLockSet:
|
||||
mode = 0x04 // Post-Quantum + Password Protected
|
||||
case isHybrid:
|
||||
mode = 0x03 // Post-Quantum only
|
||||
case isLockSet:
|
||||
mode = 0x02 // Classical + Password Protected
|
||||
default:
|
||||
mode = 0x01 // Classical Only
|
||||
}
|
||||
|
||||
serialKey, err := utils.FetchFileKey(keyValue, "serial")
|
||||
if err != nil {
|
||||
msg := "unable to fetch key serial. Unable to proceed."
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
serialBytes := []byte(serialKey)
|
||||
serialSize := len(serialBytes)
|
||||
|
||||
maskedSerial := make([]byte, serialSize)
|
||||
for i := 0; i < serialSize; i++ {
|
||||
maskedSerial[i] = serialBytes[i] ^ ephPubX[i%32]
|
||||
}
|
||||
|
||||
var finalBlob []byte
|
||||
|
||||
finalBlob = append(finalBlob, mode)
|
||||
finalBlob = append(finalBlob, byte(serialSize))
|
||||
finalBlob = append(finalBlob, maskedSerial...)
|
||||
finalBlob = append(finalBlob, ephPubX...)
|
||||
|
||||
if isHybrid {
|
||||
finalBlob = append(finalBlob, kyberCT...)
|
||||
}
|
||||
|
||||
finalBlob = append(finalBlob, nonce...)
|
||||
finalBlob = append(finalBlob, ciphertext...)
|
||||
|
||||
dst, err := os.Create(outputValue)
|
||||
if err != nil {
|
||||
fmt.Printf("error: unable to create %s\n", outputValue)
|
||||
os.Exit(1)
|
||||
}
|
||||
defer dst.Close()
|
||||
|
||||
if isArmoredSet {
|
||||
|
||||
dst.WriteString(vars.PrivateKeyHeader + "\n")
|
||||
encoded := base64.StdEncoding.EncodeToString(finalBlob)
|
||||
|
||||
for i := 0; i < len(encoded); i += 64 {
|
||||
end := i + 64
|
||||
if end > len(encoded) {
|
||||
end = len(encoded)
|
||||
}
|
||||
dst.WriteString(encoded[i:end] + "\n")
|
||||
}
|
||||
|
||||
dst.WriteString(vars.PrivateKeyFooter + "\n")
|
||||
|
||||
} else {
|
||||
dst.Write(finalBlob)
|
||||
}
|
||||
|
||||
fmt.Printf("final blob: %v\n", finalBlob)
|
||||
fmt.Printf("is hybrid: %v\n", isHybrid)
|
||||
|
||||
_ = ciphertext
|
||||
_ = isArmoredSet
|
||||
_ = ephPubX
|
||||
_ = kyberCT
|
||||
_ = mode
|
||||
_ = serialBytes
|
||||
|
||||
//////////////////////////////////////////
|
||||
|
||||
//serialKey, err := utils.FetchFileKey(keyValue, "serial")
|
||||
//if err != nil {
|
||||
// msg := "Unable to fetch key serial. Unable to proceed."
|
||||
// utils.HandleFailure(msg)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
//serialBytes := []byte(serialKey)
|
||||
//serialSize := len(serialBytes)
|
||||
|
||||
//maskedSerial := make([]byte, serialSize)
|
||||
//for i := 0; i < serialSize; i++ {
|
||||
// maskedSerial[i] = serialBytes[i] ^ ephPubX[i%32]
|
||||
//}
|
||||
|
||||
//fmt.Printf("serial: %s\n", serialKey)
|
||||
//fmt.Printf("masked: %s\n", maskedSerial)
|
||||
|
||||
//os.Exit(1)
|
||||
|
||||
///////////////////////////////////////////
|
||||
|
||||
//var finalBlob []byte
|
||||
//finalBlob = append(finalBlob, mode)
|
||||
//finalBlob = append(finalBlob, ephPubX...)
|
||||
|
||||
//if isHybrid {
|
||||
// finalBlob = append(finalBlob, kyberCT...)
|
||||
//}
|
||||
|
||||
//finalBlob = append(finalBlob, nonce...)
|
||||
//finalBlob = append(finalBlob, ciphertext...)
|
||||
|
||||
//dst, err := os.Create(outputValue)
|
||||
//if err != nil {
|
||||
// fmt.Printf("Error: unable to create %s\n", outputValue)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
//defer dst.Close()
|
||||
|
||||
//if isArmoredSet {
|
||||
// dst.WriteString(vars.PrivateKeyHeader + "\n")
|
||||
// encoded := base64.StdEncoding.EncodeToString(finalBlob)
|
||||
|
||||
// for i := 0; i < len(encoded); i += 64 {
|
||||
// end := i + 64
|
||||
// if end > len(encoded) {
|
||||
// end = len(encoded)
|
||||
// }
|
||||
// dst.WriteString(encoded[i:end] + "\n")
|
||||
// }
|
||||
|
||||
// dst.WriteString(vars.PrivateKeyFooter + "\n")
|
||||
//} else {
|
||||
|
||||
// dst.Write(finalBlob)
|
||||
//}
|
||||
|
||||
//fmt.Printf("Encryption Complete: %s\n", outputValue)
|
||||
|
||||
//dst, err := os.Create(outputValue)
|
||||
//if err != nil {
|
||||
// fmt.Printf("Error: unable to create %s\n", outputValue)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
//defer dst.Close()
|
||||
|
||||
//dst.Write([]byte{mode})
|
||||
//dst.Write(ephPubX)
|
||||
|
||||
//switch mode {
|
||||
//case 0x03, 0x04:
|
||||
// dst.Write(kyberCT)
|
||||
//}
|
||||
|
||||
//dst.Write(nonce)
|
||||
//dst.Write(ciphertext)
|
||||
|
||||
// validate headers
|
||||
|
||||
*/
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
504
internal/actions/keygen/keygen.go
Executable file
504
internal/actions/keygen/keygen.go
Executable file
@@ -0,0 +1,504 @@
|
||||
package keygen
|
||||
|
||||
import (
|
||||
"crypto/rand"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strconv"
|
||||
"time"
|
||||
|
||||
"sources.truenas.cloud/code/syscrypt/internal/utils"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func FirstRun() {
|
||||
|
||||
defaultFolder := configuration.ConfigKeyFolder
|
||||
|
||||
//fmt.Printf("default folder: %s\n", defaultFolder)
|
||||
|
||||
//fmt.Printf("[DEBUG KEYGEN] Value: %s | Addr: %p\n", config.KeyFolder, &config.KeyFolder)
|
||||
|
||||
//defaultFolder := config.KeyFolder
|
||||
|
||||
//defaultKey := config.KeyFolder + "/" + config.MasterKey
|
||||
//defaultKeyExists := utils.FileExists(defaultKey)
|
||||
//defaultFolderExists := utils.FolderExists(defaultFolder)
|
||||
|
||||
//fmt.Printf("CONFIG KEY FOLDER: %s\n", config.KeyFolder)
|
||||
|
||||
_ = defaultFolder
|
||||
|
||||
os.Exit(1)
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func Validate() {
|
||||
|
||||
keygenAllowedFlags := map[string]struct{}{
|
||||
"-C": {},
|
||||
"-i": {},
|
||||
"-c": {},
|
||||
"-PQ": {},
|
||||
"-k": {},
|
||||
"-f": {},
|
||||
}
|
||||
|
||||
utils.ValidateAllowedFlags(keygenAllowedFlags)
|
||||
|
||||
keygenRequiredFlags := []vars.FlagRequirement{
|
||||
{Flag: "-f", IsRequired: true},
|
||||
}
|
||||
|
||||
// friendly name -f
|
||||
// keypath -K
|
||||
// output -o
|
||||
|
||||
utils.ValidateRequiredFlags(keygenRequiredFlags, "keygen")
|
||||
|
||||
// friendlyname
|
||||
|
||||
isFriendlySet, friendlyHasValue := utils.IsFlagPassed("f")
|
||||
friendlyValue, _ := utils.GetFlagValue("f")
|
||||
isFriendlyValid := utils.IsAlphaNumeric(friendlyValue)
|
||||
|
||||
if isFriendlySet && !friendlyHasValue {
|
||||
msg := fmt.Sprintf("%s: -f FRIENDLYNAME: requires a value.\n"+
|
||||
"Do not enter spaces or special characters. e.g. 'masterkey'", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isFriendlySet && friendlyHasValue && !isFriendlyValid {
|
||||
msg := fmt.Sprintf("%s: -f FRIENDLYNAME: invalid friendly name.\n"+
|
||||
"The friendly name requires an alphanumeric value. e.g. 'masterkey'\n"+
|
||||
"Do not enter paths, spaces, special characters or file extensions.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// check default keys
|
||||
|
||||
// keypath
|
||||
|
||||
isKeyPathSet, keyPathHasValue := utils.IsFlagPassed("k")
|
||||
keyPathValue, _ := utils.GetFlagValue("k")
|
||||
keyPathIsValid := utils.IsValidPath(keyPathValue)
|
||||
keyPathExists := utils.FolderExists(keyPathValue)
|
||||
|
||||
if isKeyPathSet && !keyPathHasValue {
|
||||
msg := fmt.Sprintf("%s: -k KEYPATH: requires a value.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isKeyPathSet && !keyPathIsValid {
|
||||
msg := fmt.Sprintf("%s: -k KEYPATH: requires a valid folder path.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
if isKeyPathSet && keyPathIsValid && !keyPathExists {
|
||||
msg := fmt.Sprintf("%s: -k KEYPATH: folder path '%s' does not exist.", vars.CommandFlag, keyPathValue)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
var keyFolder string
|
||||
var defaultSet bool
|
||||
|
||||
if isKeyPathSet {
|
||||
defaultSet = false
|
||||
keyFolder = keyPathValue
|
||||
} else {
|
||||
defaultSet = true
|
||||
keyFolder = config.KeyFolder
|
||||
}
|
||||
|
||||
if defaultSet {
|
||||
keyPathIsValid = utils.IsValidPath(keyFolder)
|
||||
}
|
||||
|
||||
if defaultSet && !keyPathIsValid {
|
||||
msg := fmt.Sprintf("%s: DEFAULT KEYPATH '%s' was not found.", vars.CommandFlag, config.KeyFolder)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
// comment
|
||||
|
||||
isCommentSet, commentHasValue := utils.IsFlagPassed("C")
|
||||
if isCommentSet && !commentHasValue {
|
||||
msg := fmt.Sprintf("%s: -C COMMENT: flag is set but no value was provided.", vars.CommandFlag)
|
||||
utils.HandleFailure(msg)
|
||||
os.Exit(1)
|
||||
}
|
||||
|
||||
fmt.Printf("key folder: %s\n", keyFolder)
|
||||
|
||||
_ = isKeyPathSet
|
||||
_ = keyPathHasValue
|
||||
_ = keyPathIsValid
|
||||
_ = keyPathExists
|
||||
_ = keyFolder
|
||||
|
||||
// comment
|
||||
|
||||
// output
|
||||
|
||||
KeyGen()
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func generateRandomSerial() string {
|
||||
token := make([]byte, 15)
|
||||
rand.Read(token)
|
||||
return hex.EncodeToString(token)
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func getCurrentDate() string {
|
||||
return time.Now().Format("2006-01-02 15:04:05")
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func saveKeyFile(filename string, permission string, data interface{}) {
|
||||
|
||||
fmt.Printf("%v\n", data)
|
||||
os.Exit(1)
|
||||
|
||||
if permission == "" {
|
||||
fmt.Printf("error: permission code required.\n")
|
||||
return
|
||||
}
|
||||
|
||||
fileData, err := json.MarshalIndent(data, "", " ")
|
||||
if err != nil {
|
||||
fmt.Printf("error encoding json: %v\n", err)
|
||||
return
|
||||
}
|
||||
|
||||
err = os.WriteFile(filename, fileData, 0600)
|
||||
if err != nil {
|
||||
fmt.Printf("Error writing %s: %v\n", filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
permUint, _ := strconv.ParseUint(permission, 8, 32)
|
||||
err = os.Chmod(filename, os.FileMode(permUint))
|
||||
if err != nil {
|
||||
fmt.Printf("Error adjusting permissions on %s: %v\n", filename, err)
|
||||
return
|
||||
}
|
||||
|
||||
fmt.Printf("Success: '%s' saved successfully.\n", filename)
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func KeyGen() {
|
||||
|
||||
outputFolder, _ := utils.GetFlagValue("o")
|
||||
isPQSet, _ := utils.IsFlagPassed("PQ")
|
||||
commentValue, _ := utils.GetFlagValue("C")
|
||||
|
||||
if outputFolder == "" {
|
||||
outputFolder = config.KeyFolder
|
||||
}
|
||||
|
||||
_ = isPQSet
|
||||
_ = commentValue
|
||||
|
||||
//fmt.Printf("%s", config.KeyFolder)
|
||||
|
||||
//outputValue, _ := utils.GetFlagValue("o")
|
||||
//isPQSet, _ := utils.IsFlagPassed("PQ")
|
||||
//commentValue, _ := utils.GetFlagValue("C")
|
||||
|
||||
//pubWrap := syscrypt.PublicKeyWrapper{}
|
||||
//privWrap := syscrypt.PrivateKeyWrapper{}
|
||||
//apiWrap := syscrypt.ApiKeyWrapper{}
|
||||
///
|
||||
//var privX, pubX [32]byte
|
||||
//io.ReadFull(rand.Reader, privX[:])
|
||||
//curve25519.ScalarBaseMult(&pubX, &privX)
|
||||
|
||||
//serial := generateRandomSerial()
|
||||
//date := getCurrentDate()
|
||||
|
||||
//password := (generatepassword)
|
||||
|
||||
//pubWrap.PublicKey.Key = vars.DefaultPrefixLabel + hex.EncodeToString(pubX[:])
|
||||
//pubWrap.PublicKey.Serial = serial
|
||||
//pubWrap.PublicKey.Date = date
|
||||
//pubWrap.PublicKey.Comment = commentValue
|
||||
|
||||
//privWrap.PrivateKey.Key = vars.PrivateKeyPrefixLabel + hex.EncodeToString(privX[:])
|
||||
//privWrap.PrivateKey.Serial = serial
|
||||
//privWrap.PrivateKey.Date = date
|
||||
//privWrap.PrivateKey.Comment = commentValue
|
||||
|
||||
//apiWrap.ApiKey.Key = ""
|
||||
//apiWrap.ApiKey.Serial = serial
|
||||
//apiWrap.ApiKey.Date = date
|
||||
//apiWrap.ApiKey.Comment = commentValue
|
||||
|
||||
/*
|
||||
type ApiKeyWrapper struct {
|
||||
ApiKey struct {
|
||||
Key string `json:"key"`
|
||||
Serial string `json:"serial"`
|
||||
Date string `json:"date"`
|
||||
Comment string `json:"comment"`
|
||||
} `json:"APIKey"`
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//if isPQSet {
|
||||
// scheme := kyber768.Scheme()
|
||||
// pkK, skK, err := scheme.GenerateKeyPair()
|
||||
// if err != nil {
|
||||
// fmt.Printf("error: keygen failed: %v\n", err)
|
||||
// return
|
||||
// }
|
||||
|
||||
// pkBytes, _ := pkK.MarshalBinary()
|
||||
// skBytes, _ := skK.MarshalBinary()
|
||||
|
||||
// pubWrap.PublicKey.MLKEMKey = vars.PQPublicKeyPrefixLabel + hex.EncodeToString(pkBytes)
|
||||
// privWrap.PrivateKey.MLKEMKey = vars.PQPrivateKeyPrefixLabel + hex.EncodeToString(skBytes)
|
||||
//}
|
||||
|
||||
//pubJSON, err := json.MarshalIndent(pubWrap, "", " ")
|
||||
//if err != nil {
|
||||
// fmt.Printf("Marshal Error: %v\n", err)
|
||||
// return
|
||||
//}
|
||||
|
||||
//privJSON, err := json.MarshalIndent(privWrap, "", " ")
|
||||
//if err != nil {
|
||||
// fmt.Printf("Marshal Error: %v\n", err)
|
||||
// return
|
||||
//}
|
||||
|
||||
//MasterFolder := config.KeyFolder
|
||||
|
||||
// Public Key File
|
||||
|
||||
//pubFullPath := outputValue
|
||||
//pubDir := filepath.Dir(pubFullPath)
|
||||
//pubBase := filepath.Base(pubFullPath)
|
||||
//pubExt := filepath.Ext(pubBase)
|
||||
//pubFileName := strings.TrimSuffix(pubBase, pubExt)
|
||||
//pubFilePath := MasterFolder + "/" + pubFileName + ".pub" + pubExt
|
||||
|
||||
// API Key File
|
||||
|
||||
//apiFullPath := outputValue
|
||||
//apiDir := filepath.Dir(apiFullPath)
|
||||
//apiBase := filepath.Base(apiFullPath)
|
||||
//apiExt := filepath.Ext(apiBase)
|
||||
//apiFileName := strings.TrimSuffix(apiBase, apiExt)
|
||||
//apiFilePath := MasterFolder + "/" + apiFileName + ".api" + apiExt
|
||||
|
||||
// Master Password
|
||||
/*
|
||||
passFullPath := outputValue
|
||||
passDir := filepath.Dir(passFullPath)
|
||||
passBase := filepath.Base(passFullPath)
|
||||
passExt := filepath.Ext(passBase)
|
||||
passFileName := strings.TrimSuffix(passBase, passExt)
|
||||
passFilePath := MasterFolder + "/" + passFileName + ".secret" + passExt
|
||||
|
||||
fmt.Printf("fullpath: %s\n", passFullPath)
|
||||
fmt.Printf("dir: %s\n", passDir)
|
||||
fmt.Printf("base: %s\n", passBase)
|
||||
fmt.Printf("ext: %s\n", passExt)
|
||||
fmt.Printf("filename: %s\n", passFileName)
|
||||
fmt.Printf("path: %s\n", passFilePath)
|
||||
|
||||
*/
|
||||
|
||||
// Save to disk
|
||||
|
||||
//err = os.WriteFile(pubFilePath, pubJSON, 0644)
|
||||
//if err != nil {
|
||||
// fmt.Printf("File Error: %v\n", err)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
//err = os.WriteFile(outputValue, privJSON, 0600)
|
||||
//if err != nil {
|
||||
// fmt.Printf("File Error: %v\n", err)
|
||||
// os.Exit(1)
|
||||
//}
|
||||
|
||||
//saveKeyFile(outputValue, "600", privJSON)
|
||||
|
||||
//saveKeyFile(filename string, permission string, data interface{})
|
||||
|
||||
////pubFullPath := outputValue
|
||||
////pubDir := filepath.Dir(pubFullPath)
|
||||
////pubBase := filepath.Base(pubFullPath)
|
||||
////pubExt := filepath.Ext(pubBase)
|
||||
////pubClean := strings.TrimSuffix(pubBase, pubExt)
|
||||
////pubFileOut := pubClean + ".pub" + pubExt
|
||||
////pubFileOut = pubDir + "/" + pubFileOut
|
||||
|
||||
//_ = outputValue
|
||||
//_ = commentValue
|
||||
//_ = pubJSON
|
||||
//_ = privJSON
|
||||
//_ = pubDir
|
||||
//_ = pubFilePath
|
||||
//_ = apiDir
|
||||
//_ = apiFilePath
|
||||
//_ = apiWrap
|
||||
//_ = passDir
|
||||
//_ = passFilePath
|
||||
|
||||
//fmt.Printf("%v\n", pkK)
|
||||
//fmt.Printf("%v\n", skK)
|
||||
|
||||
////outputValue, _ := utils.GetFlagValue("o")
|
||||
|
||||
////isPQSet, _ := utils.IsFlagPassed("PQ")
|
||||
////commentValue, _ := utils.GetFlagValue("C")
|
||||
|
||||
////privX := make([]byte, 32)
|
||||
////rand.Read(privX)
|
||||
////pubX, _ := curve25519.X25519(privX, curve25519.Basepoint)
|
||||
|
||||
////pubWrap := syscrypt.PublicKeyWrapper{}
|
||||
////privWrap := syscrypt.PrivateKeyWrapper{}
|
||||
////apiWrap := syscrypt.ApiKeyWrapper{}
|
||||
|
||||
////_ = apiWrap
|
||||
///
|
||||
////serial := generateRandomSerial()
|
||||
////date := getCurrentDate()
|
||||
|
||||
////if isPQSet {
|
||||
//// pubK, privK, _ := kyber768.GenerateKeyPair(rand.Reader)
|
||||
//// pBytes, _ := pubK.MarshalBinary()
|
||||
//// sBytes, _ := privK.MarshalBinary()
|
||||
//// pubWrap.PublicKey.MLKEMKey = vars.PQPublicKeyPrefixLabel + hex.EncodeToString(pBytes)
|
||||
//// privWrap.PrivateKey.MLKEMKey = vars.PQPrivateKeyPrefixLabel + hex.EncodeToString(sBytes)
|
||||
////}
|
||||
|
||||
////pubWrap.PublicKey.Key = vars.DefaultPrefixLabel + hex.EncodeToString(pubX)
|
||||
////pubWrap.PublicKey.Serial = serial
|
||||
////pubWrap.PublicKey.Date = date
|
||||
////pubWrap.PublicKey.Comment = commentValue
|
||||
|
||||
////privWrap.PrivateKey.Key = vars.PrivateKeyPrefixLabel + hex.EncodeToString(privX)
|
||||
////privWrap.PrivateKey.Serial = serial
|
||||
////privWrap.PrivateKey.Date = date
|
||||
////privWrap.PrivateKey.Comment = commentValue
|
||||
|
||||
//apiWrap.ApiKey.Key = GenerateAPIKey(xxx, )
|
||||
//apiWrap.ApiKey.Serial = serial
|
||||
//apiWrap.ApiKey.Date = date
|
||||
//apiWrap.ApiKey.Comment = commentValue
|
||||
|
||||
//type ApiKeyWrapper struct {
|
||||
// ApiKey struct {
|
||||
// Key string `json:"key"`
|
||||
// Serial string `json:"serial"`
|
||||
// Date string `json:"date"`
|
||||
// Comment string `json:"comment"`
|
||||
// } `json:"APIKey"`
|
||||
//}
|
||||
|
||||
////fmt.Printf("public key: %s\n", pubWrap.PublicKey.Key)
|
||||
////fmt.Printf("private key: %s\n", privWrap.PrivateKey.Key)
|
||||
////fmt.Printf("pq: %s\n", pubWrap.PublicKey.MLKEMKey)
|
||||
|
||||
//preview, _ := json.MarshalIndent(privWrap, "", " ")
|
||||
//fmt.Println(string(preview))
|
||||
|
||||
////pubFullPath := outputValue
|
||||
////pubDir := filepath.Dir(pubFullPath)
|
||||
////pubBase := filepath.Base(pubFullPath)
|
||||
////pubExt := filepath.Ext(pubBase)
|
||||
////pubClean := strings.TrimSuffix(pubBase, pubExt)
|
||||
////pubFileOut := pubClean + ".pub" + pubExt
|
||||
////pubFileOut = pubDir + "/" + pubFileOut
|
||||
|
||||
////apiFullPath := outputValue
|
||||
////apiDir := filepath.Dir(apiFullPath)
|
||||
////apiBase := filepath.Base(apiFullPath)
|
||||
////apiExt := filepath.Ext(apiBase)
|
||||
////apiClean := strings.TrimSuffix(apiBase, apiExt)
|
||||
////apiFileOut := apiClean + ".api" + apiExt
|
||||
////apiFileOut = apiDir + "/" + apiFileOut
|
||||
|
||||
// Generate Master Password if one doesnt already exist
|
||||
|
||||
//mpExists := utils.ReadJson("config.masterpass", config.ConfigPath)
|
||||
|
||||
////_ = pubDir
|
||||
////_ = apiDir
|
||||
////_ = apiFileOut
|
||||
|
||||
////fmt.Printf("public out: %s\n", pubDir+"/"+pubFileOut)
|
||||
////fmt.Printf("api out: %s\n", apiDir+"/"+apiFileOut)
|
||||
|
||||
////saveKeyFile(pubFileOut, "0600", pubWrap)
|
||||
////saveKeyFile(outputValue, "0644", privWrap)
|
||||
//saveKeyFile(xxxx, 0600, xxxxxx)
|
||||
|
||||
//func saveKeyFile(filename string, permission string, data interface{}) {
|
||||
|
||||
//type PublicKeyWrapper struct {
|
||||
// PublicKey struct {
|
||||
// Key string `json:"key"`
|
||||
// MLKEMKey string `json:"mlkem_key,omitempty"`
|
||||
// Serial string `json:"serial"`
|
||||
// Date string `json:"date"`
|
||||
// Comment string `json:"comment"`
|
||||
// } `json:"PublicKey"`
|
||||
//}
|
||||
|
||||
//privRaw := make([]byte, 32)
|
||||
//serial := utils.GenerateSerial(27)
|
||||
//io.ReadFull(rand.Reader, privRaw)
|
||||
//pubRaw, _ := curve25519.X25519(privRaw, curve25519.Basepoint)
|
||||
//dateStr := time.Now().Format("2006-01-02 15:04:05")
|
||||
|
||||
//privStruct := syscrypt.PrivateKeyWrapper{}
|
||||
//privStruct.PrivateKey.Key = vars.PrivateKeyPrefixLabel + hex.EncodeToString(privRaw)
|
||||
//privStruct.PrivateKey.Serial = serial
|
||||
//privStruct.PrivateKey.Date = dateStr
|
||||
//privStruct.PrivateKey.Comment = vars.Comment
|
||||
|
||||
//pubStruct := syscrypt.PublicKeyWrapper{}
|
||||
//pubStruct.PublicKey.Key = vars.DefaultPrefixLabel + hex.EncodeToString(pubRaw)
|
||||
//pubStruct.PublicKey.Serial = serial
|
||||
//pubStruct.PublicKey.Date = dateStr
|
||||
//pubStruct.PublicKey.Comment = vars.Comment
|
||||
|
||||
//pBytes, _ := json.MarshalIndent(privStruct, "", " ")
|
||||
//pubBytes, _ := json.MarshalIndent(pubStruct, "", " ")
|
||||
|
||||
//fmt.Printf("%s\n", pBytes)
|
||||
//fmt.Printf("%s\n", pubBytes)
|
||||
//fmt.Printf("%s\n", pubStruct.PublicKey.Serial)
|
||||
|
||||
//utils.GenerateKeys(pubStruct.PublicKey.Key, privStruct.PrivateKey.Key, serial)
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
58
internal/configuration/configuration.go
Executable file
58
internal/configuration/configuration.go
Executable file
@@ -0,0 +1,58 @@
|
||||
package configuration
|
||||
|
||||
import (
|
||||
"fmt"
|
||||
"os"
|
||||
)
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
const (
|
||||
|
||||
// BUILD DATA
|
||||
|
||||
BuildProject = "syscrypt "
|
||||
BuildVersion = "1.2.1"
|
||||
BuildDate = "20260222-183358"
|
||||
BuildSource = "https://www.github.com/mintsalad/syscrypt"
|
||||
|
||||
// JSON
|
||||
|
||||
ConfigPath = "/opt/syscrypt/config.json"
|
||||
|
||||
// KEYS
|
||||
|
||||
MasterExt = ".d"
|
||||
MasterKey = ".masterkey.d"
|
||||
MasterPublicKey = ".masterkey.pub.d"
|
||||
MasterKeySalt = ".register.d"
|
||||
MasterAPIKey = ".master.api.d"
|
||||
Dictionary = ".library.d"
|
||||
DictionaryTmp = ".library.tmp.txt"
|
||||
StatePath = ".ratelimit"
|
||||
LockPath = ".lockout"
|
||||
|
||||
// LOCKOUT SETTINGS
|
||||
|
||||
MaxTokens = 5
|
||||
RefillSeconds = 2
|
||||
MaxFailures = 3
|
||||
LockoutMinutes = 15
|
||||
)
|
||||
|
||||
var KeyFolder string
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func init() {
|
||||
|
||||
if _, err := os.Stat(ConfigPath); os.IsNotExist(err) {
|
||||
fmt.Printf("config file is missing at %s\n", ConfigPath)
|
||||
}
|
||||
|
||||
//KeyFolder = utils.ReadJSON(".config.keys.Path", ConfigPath)
|
||||
|
||||
}
|
||||
|
||||
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
//func ReadJson(path string, filePath string) (string, error) {
|
||||
141
internal/utils/salt.go
Executable file
141
internal/utils/salt.go
Executable file
@@ -0,0 +1,141 @@
|
||||
package utils
|
||||
|
||||
import (
|
||||
"crypto/aes"
|
||||
"crypto/cipher"
|
||||
"crypto/hmac"
|
||||
"crypto/rand"
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"os"
|
||||
"strings"
|
||||
|
||||
"sources.truenas.cloud/code/syscrypt"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/configuration"
|
||||
"sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
)
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func HashEntry(value, x25519Str string) string {
|
||||
key := strings.TrimPrefix(x25519Str, vars.PrivateKeyPrefixLabel)
|
||||
h := hmac.New(sha256.New, []byte(key))
|
||||
h.Write([]byte(value))
|
||||
|
||||
return hex.EncodeToString(h.Sum(nil))
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func AddSaltEntry(ref, serial, salt, x25519Key string) error {
|
||||
|
||||
var vault syscrypt.Vault
|
||||
//vaultPath := config.KeyFolder + "/" + config.MasterKeySalt
|
||||
vaultPath := configuration.KeyFolder + "/" + configuration.MasterKeySalt
|
||||
|
||||
if data, err := os.ReadFile(vaultPath); err == nil {
|
||||
json.Unmarshal(data, &vault)
|
||||
}
|
||||
|
||||
for _, e := range vault.Entries {
|
||||
if e.Ref == ref {
|
||||
return fmt.Errorf("reference %s already exists", ref)
|
||||
}
|
||||
}
|
||||
|
||||
encSalt := salt
|
||||
|
||||
newEntry := syscrypt.SaltEntry{
|
||||
Ref: ref,
|
||||
Salt: encSalt,
|
||||
}
|
||||
|
||||
vault.Entries = append(vault.Entries, newEntry)
|
||||
updated, _ := json.MarshalIndent(vault, "", " ")
|
||||
|
||||
return os.WriteFile(vaultPath, updated, 0644)
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func VerifySaltEntry(ref, x25519Key string) (salt string, exists bool, err error) {
|
||||
//var vault syscrypt.Vault
|
||||
//vaultPath := config.KeyFolder + "/" + config.MasterKeySalt
|
||||
|
||||
/*
|
||||
data, err := os.ReadFile(vaultPath)
|
||||
if err != nil {
|
||||
return "", false, err
|
||||
}
|
||||
json.Unmarshal(data, &vault)
|
||||
|
||||
for _, e := range vault.Entries {
|
||||
if e.Ref == ref {
|
||||
//
|
||||
//decSerial, errS := DecryptString(e.Serial, x25519Key)
|
||||
decSalt, errSalt := DecryptString(e.Salt, x25519Key)
|
||||
|
||||
if errSalt != nil {
|
||||
return "", false, fmt.Errorf("failed to decrypt entry")
|
||||
}
|
||||
|
||||
return decSalt, true, nil
|
||||
|
||||
//
|
||||
//
|
||||
}
|
||||
}
|
||||
|
||||
*/
|
||||
|
||||
//return "", false, fmt.Errorf("reference %s not found", ref)
|
||||
return "", false, nil
|
||||
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func EncryptString(plaintext, x25519Key string) (string, error) {
|
||||
keyBytes := sha256.Sum256([]byte(x25519Key))
|
||||
block, _ := aes.NewCipher(keyBytes[:])
|
||||
gcm, _ := cipher.NewGCM(block)
|
||||
|
||||
nonce := make([]byte, gcm.NonceSize())
|
||||
rand.Read(nonce)
|
||||
|
||||
return base64.StdEncoding.EncodeToString(gcm.Seal(nonce, nonce, []byte(plaintext), nil)), nil
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
func DecryptString(cipherText string, x25519Key string) (string, error) {
|
||||
data, err := base64.StdEncoding.DecodeString(cipherText)
|
||||
if err != nil {
|
||||
return "", err
|
||||
}
|
||||
|
||||
keyBytes := sha256.Sum256([]byte(x25519Key))
|
||||
block, _ := aes.NewCipher(keyBytes[:])
|
||||
gcm, _ := cipher.NewGCM(block)
|
||||
nonceSize := gcm.NonceSize()
|
||||
|
||||
nonce, ciphertextBytes := data[:nonceSize], data[nonceSize:]
|
||||
plain, err := gcm.Open(nil, nonce, ciphertextBytes, nil)
|
||||
|
||||
return string(plain), err
|
||||
|
||||
//keyBytes := sha256.Sum256([]byte(x25519Key))
|
||||
//block, _ := aes.NewCipher(keyBytes[:])
|
||||
//gcm, _ := cipher.NewGCM(block)
|
||||
|
||||
//nonceSize := gcm.NonceSize()
|
||||
//nonce, cipher := cipherText[:nonceSize], cipherText[nonceSize:]
|
||||
//plain, err := gcm.Open(nil, nonce, cipher, nil)
|
||||
//return string(plain), err
|
||||
}
|
||||
|
||||
////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|
||||
2391
internal/utils/utils.go
Executable file
2391
internal/utils/utils.go
Executable file
File diff suppressed because it is too large
Load Diff
108
internal/vars/vars.go
Executable file
108
internal/vars/vars.go
Executable file
@@ -0,0 +1,108 @@
|
||||
package vars
|
||||
|
||||
var (
|
||||
|
||||
// tag
|
||||
|
||||
Tag_P256Label = "syscrypt/p256tag" // TagLabel
|
||||
Tag_MLKem768p256Label = "syscrypt/mlkem768p256tag" // MLKemLabel
|
||||
Tag_Syscrypt1Tag1 = "syscrypt1tag1" // LabelSyscrypt1Tag1
|
||||
Tag_MLKem768X25519 = "syscrypt/mlkem768x25519" // MLKem768X25519
|
||||
Tag_Syscrypt1TagPq1 = "syscrypt1tagpq1" //
|
||||
|
||||
// post quantum
|
||||
|
||||
Pq_PrivateKeyLabel = "SYSCRYPT-PQ-PRIVATE-KEY-" // PrivateKeyPQPrefix
|
||||
Pq_PublicKeyLabel = "syscrypt1pq1" // PublicKeyPQPrefix
|
||||
|
||||
// plugins
|
||||
|
||||
Plugin_Label = "syscrypt-plugin-" // PluginLabel
|
||||
Plugin_PrivateKeyLabel = "SYSCRYPT-PLUGIN-" //PluginPrivateKeyLabel
|
||||
|
||||
// ssh
|
||||
|
||||
SSH_Label = "ssh-"
|
||||
|
||||
// Github
|
||||
|
||||
Github_label = "github:"
|
||||
|
||||
// OAEP
|
||||
|
||||
OAEP_Label = "syscrypt/base/v1/ssh-rsa" // OAEPLabel
|
||||
|
||||
// ED25519
|
||||
|
||||
ED25519_Label = "syscrypt/base/v1/ssh-ed25519" // ED25519Label
|
||||
|
||||
// X25519
|
||||
|
||||
X25519_Label = "syscrypt/base/v1/X25519" // X25519Label
|
||||
|
||||
// encrypt, decrypt
|
||||
|
||||
DefaultPrefixLabel = "syscrypt--" // DefaultPrefix
|
||||
PrivateKeyPrefixLabel = "SYSCRYPT-PRIVATE-KEY--" // PrivateKeyPrefix
|
||||
PublicKeyPrefixLabel = "syscrypt--" // PublicKeyPrefix
|
||||
DefaultPrivateKeyPrefixLabel = "SYSCRYPT--"
|
||||
PQPublicKeyPrefixLabel = "syscrypt-pq--"
|
||||
PQPrivateKeyPrefixLabel = "SYSCRYPT-PRIVATE-KEY-PQ--"
|
||||
|
||||
// Other
|
||||
|
||||
SyscryptLabel = "syscrypt/base/v1/"
|
||||
ScryptLabel = "syscrypt/base/v1/scrypt"
|
||||
PrivateKeyHeader = "-----BEGIN ENCRYPTED DATA-----" // PrivateKeyHeader
|
||||
PrivateKeyStartTrunc = "-----BEGIN"
|
||||
PrivateKeyEndTrunc = "-----END"
|
||||
PrivateKeyFooter = "-----END ENCRYPTED DATA-----" // PrivateKeyFooter
|
||||
VersionLabel = "syscrypt/base/v1/[serial]"
|
||||
|
||||
// Lock
|
||||
|
||||
LockPrivateKeyStartTrunc = "-----BEGIN LOCK ENCRYPTED DATA-----"
|
||||
LockPrivateKeyEndTrunc = "-----END LOCK ENCRYPTED DATA-----"
|
||||
LockVersionLabel = "syscrypt/lock/v1/[serial]"
|
||||
LockedLabel = "syscrypt/lock/"
|
||||
LockedLabelVersion = "syscrypt/lock/v1/"
|
||||
|
||||
// Anchors
|
||||
|
||||
StartAnchor = "/v1/"
|
||||
EndAnchor = "--"
|
||||
BoundaryAnchor = []byte("---")
|
||||
FooterAnchor = "---"
|
||||
)
|
||||
|
||||
var (
|
||||
Version string
|
||||
VersionFlag bool // version
|
||||
CommandFlag string // -c
|
||||
ArmoredFlag bool // -a
|
||||
KeyFlag string // -k
|
||||
InputFlag string // -i
|
||||
OutputFlag string // -o
|
||||
LockFlag bool // -L
|
||||
ApiFlag string // -A
|
||||
MasterPass string // -P
|
||||
Comment string // -COMMENT
|
||||
StringFlag string // -s
|
||||
PQFlag bool // post quantum
|
||||
KeyPath string // -K
|
||||
FriendlyName string // -f
|
||||
)
|
||||
|
||||
const (
|
||||
PassTagSize = 28 // 12 bytes nonce/ct + 16 bytes poly1305 tag
|
||||
KyberCTSize = 1088 // Kyber768 Ciphertext size
|
||||
)
|
||||
|
||||
type FlagRequirement struct {
|
||||
Flag string
|
||||
IsRequired bool
|
||||
}
|
||||
|
||||
type ExpectedKeys struct {
|
||||
File string
|
||||
}
|
||||
47
syscrypt.go
Executable file
47
syscrypt.go
Executable file
@@ -0,0 +1,47 @@
|
||||
package syscrypt
|
||||
|
||||
import "sources.truenas.cloud/code/syscrypt/internal/vars"
|
||||
|
||||
type PrivateKeyWrapper struct {
|
||||
PrivateKey struct {
|
||||
Key string `json:"key"`
|
||||
MLKEMKey string `json:"mlkem_key,omitempty"`
|
||||
Serial string `json:"serial"`
|
||||
Salt string `json:"salt"`
|
||||
Date string `json:"date"`
|
||||
Comment string `json:"comment"`
|
||||
} `json:"PrivateKey"`
|
||||
}
|
||||
|
||||
type PublicKeyWrapper struct {
|
||||
PublicKey struct {
|
||||
Key string `json:"key"`
|
||||
MLKEMKey string `json:"mlkem_key,omitempty"`
|
||||
Serial string `json:"serial"`
|
||||
Salt string `json:"salt"`
|
||||
Date string `json:"date"`
|
||||
Comment string `json:"comment"`
|
||||
} `json:"PublicKey"`
|
||||
}
|
||||
|
||||
type ApiKeyWrapper struct {
|
||||
ApiKey struct {
|
||||
Key string `json:"key"`
|
||||
Serial string `json:"serial"`
|
||||
Salt string `json:"salt"`
|
||||
Date string `json:"date"`
|
||||
Comment string `json:"comment"`
|
||||
} `json:"APIKey"`
|
||||
}
|
||||
|
||||
type SaltEntry struct {
|
||||
Ref string `json:"ref"`
|
||||
Salt string `json:"salt"`
|
||||
}
|
||||
|
||||
type Vault struct {
|
||||
Entries []SaltEntry `json:"entries"`
|
||||
}
|
||||
|
||||
var ArmorHeader = vars.PrivateKeyHeader
|
||||
var ArmorFooter = vars.PrivateKeyFooter
|
||||
Reference in New Issue
Block a user