830 lines
21 KiB
Go
Executable File
830 lines
21 KiB
Go
Executable File
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
|
|
|
|
*/
|
|
|
|
}
|
|
|
|
///////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////////
|