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