From d844b4be113f4f6ae1cc592c20e0a3e03be934b4 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 13:02:22 +0100 Subject: [PATCH 01/26] remove obsolete file --- .../cryptography/pkcs11_archived.go | 255 ------------------ 1 file changed, 255 deletions(-) delete mode 100644 internal/infrastructure/cryptography/pkcs11_archived.go diff --git a/internal/infrastructure/cryptography/pkcs11_archived.go b/internal/infrastructure/cryptography/pkcs11_archived.go deleted file mode 100644 index 62f7a99..0000000 --- a/internal/infrastructure/cryptography/pkcs11_archived.go +++ /dev/null @@ -1,255 +0,0 @@ -package cryptography - -// For reference only, to demonstrate experiments with pkcs11-tool for encryption, decryption, signing, and verification. - -// // Encrypt encrypts data using the cryptographic capabilities of the PKCS#11 token. Currently only supports RSA keys. Refer to: https://docs.nitrokey.com/nethsm/pkcs11-tool#pkcs11-tool -// func (token *PKCS11TokenHandler) Encrypt(inputFilePath, outputFilePath string) error { -// // Validate required parameters -// if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { -// return fmt.Errorf("missing required arguments for encryption") -// } - -// if token.KeyType != "RSA" { -// return fmt.Errorf("only RSA keys are supported for encryption") -// } - -// uniqueID := uuid.New() -// // Temporary file to store the public key in DER format -// publicKeyFile := fmt.Sprintf("%s-public.der", uniqueID) - -// // Step 1: Retrieve the public key from the PKCS#11 token using pkcs11-tool -// args := []string{ -// "--module", token.ModulePath, -// "--token-label", token.Label, -// "--pin", token.UserPin, -// "--read-object", -// "--label", token.ObjectLabel, -// "--type", "pubkey", // Retrieve public key -// "--output-file", publicKeyFile, // Store public key in DER format -// } - -// cmd := exec.Command("pkcs11-tool", args...) -// output, err := cmd.CombinedOutput() -// if err != nil { -// return fmt.Errorf("failed to retrieve public key: %v\nOutput: %s", err, output) -// } -// fmt.Println("Public key retrieved successfully.") - -// // Check if the public key file was generated -// if _, err := os.Stat(publicKeyFile); os.IsNotExist(err) { -// return fmt.Errorf("public key file not found: %s", publicKeyFile) -// } - -// // Step 2: Encrypt the data using OpenSSL and the retrieved public key -// encryptCmd := exec.Command("openssl", "pkeyutl", "-encrypt", "-pubin", "-inkey", publicKeyFile, "-keyform", "DER", "-in", inputFilePath, "-out", outputFilePath) -// encryptOutput, err := encryptCmd.CombinedOutput() -// if err != nil { -// return fmt.Errorf("failed to encrypt data with OpenSSL: %v\nOutput: %s", err, encryptOutput) -// } - -// // Step 3: Remove the public key from the filesystem -// os.Remove(publicKeyFile) - -// fmt.Printf("Encryption successful. Encrypted data written to %s\n", outputFilePath) -// return nil -// } - -// // Decrypt decrypts data using the cryptographic capabilities of the PKCS#11 token. Currently only supports RSA keys. Refer to: https://docs.nitrokey.com/nethsm/pkcs11-tool#pkcs11-tool -// func (token *PKCS11TokenHandler) Decrypt(inputFilePath, outputFilePath string) error { -// // Validate required parameters -// if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { -// return fmt.Errorf("missing required arguments for decryption") -// } - -// if token.KeyType != "RSA" { -// return fmt.Errorf("only RSA keys are supported for decryption") -// } - -// // Check if input file exists -// if _, err := os.Stat(inputFilePath); os.IsNotExist(err) { -// return fmt.Errorf("input file does not exist: %v", err) -// } - -// // Create or validate the output file (will be overwritten) -// outputFile, err := os.Create(outputFilePath) -// if err != nil { -// return fmt.Errorf("failed to create or open output file: %v", err) -// } -// defer outputFile.Close() - -// // Step 1: Prepare the command to decrypt the data using pkcs11-tool -// args := []string{ -// "--module", token.ModulePath, -// "--token-label", token.Label, -// "--pin", token.UserPin, -// "--decrypt", -// "--label", token.ObjectLabel, -// "--mechanism", "RSA-PKCS", // Specify the RSA-PKCS mechanism -// "--input-file", inputFilePath, // Input file with encrypted data -// } - -// // Run the decryption command -// cmd := exec.Command("pkcs11-tool", args...) -// output, err := cmd.CombinedOutput() - -// // Capture the decrypted data and filter out any extra output -// if err != nil { -// return fmt.Errorf("decryption failed: %v\nOutput: %s", err, output) -// } - -// // Split the output into lines and filter out unwanted lines -// lines := strings.Split(string(output), "\n") -// var decryptedData []byte -// for i, line := range lines { -// if !strings.Contains(line, "Using decrypt algorithm RSA-PKCS") { -// // If this is not the last line, append a newline -// decryptedData = append(decryptedData, []byte(line)...) -// if i < len(lines)-1 { -// decryptedData = append(decryptedData, '\n') -// } -// } -// } - -// // Write the actual decrypted data (without extra info) to the output file -// _, err = outputFile.Write(decryptedData) -// if err != nil { -// return fmt.Errorf("failed to write decrypted data to output file: %v", err) -// } - -// fmt.Printf("Decryption successful. Decrypted data written to %s\n", outputFilePath) -// return nil -// } - -// // Sign signs data using the cryptographic capabilities of the PKCS#11 token. Currently only supports RSA keys. Refer to: https://docs.nitrokey.com/nethsm/pkcs11-tool#pkcs11-tool -// func (token *PKCS11TokenHandler) Sign(inputFilePath, outputFilePath string) error { -// // Validate required parameters -// if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { -// return fmt.Errorf("missing required arguments for signing") -// } - -// if token.KeyType != "RSA" { -// return fmt.Errorf("only RSA keys are supported for decryption") -// } - -// // Check if the input file exists -// if _, err := os.Stat(inputFilePath); os.IsNotExist(err) { -// return fmt.Errorf("input file does not exist: %v", err) -// } - -// uniqueID := uuid.New() -// // Step 1: Hash the data using OpenSSL (SHA-256) -// hashFile := fmt.Sprintf("%s-data.hash", uniqueID) -// hashCmd := exec.Command("openssl", "dgst", "-sha256", "-binary", inputFilePath) - -// // Redirect the output of the hash command to a file -// hashOut, err := os.Create(hashFile) -// if err != nil { -// return fmt.Errorf("failed to create hash output file: %v", err) -// } -// defer hashOut.Close() - -// hashCmd.Stdout = hashOut -// hashCmd.Stderr = os.Stderr - -// // Execute the hashing command -// if err := hashCmd.Run(); err != nil { -// return fmt.Errorf("failed to hash data: %v", err) -// } -// fmt.Println("Data hashed successfully.") - -// // Step 2: Sign the hashed data using pkcs11-tool -// signCmd := exec.Command("pkcs11-tool", -// "--module", token.ModulePath, -// "--token-label", token.Label, -// "--pin", token.UserPin, -// "--sign", -// "--mechanism", "RSA-PKCS-PSS", // Using RSA-PKCS-PSS for signing -// "--hash-algorithm", "SHA256", // Hash algorithm to match the hashing step -// "--input-file", hashFile, // Input file containing the hashed data -// "--output-file", outputFilePath, // Output signature file -// "--signature-format", "openssl", // Use OpenSSL signature format -// "--label", token.ObjectLabel, // Key label used for signing -// ) - -// // Run the signing command -// signOutput, err := signCmd.CombinedOutput() -// if err != nil { -// return fmt.Errorf("failed to sign data: %v\nOutput: %s", err, signOutput) -// } - -// // Step 3: Remove the hash file from the filesystem -// os.Remove(hashFile) - -// fmt.Printf("Signing successful. Signature written to %s\n", outputFilePath) -// return nil -// } - -// // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token. -// func (token *PKCS11TokenHandler) Verify(dataFilePath, signatureFilePath string) (bool, error) { -// valid := false -// // Validate required parameters -// if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { -// return valid, fmt.Errorf("missing required arguments for verification") -// } - -// if token.KeyType != "RSA" { -// return valid, fmt.Errorf("only RSA keys are supported for decryption") -// } - -// // Check if the input files exist -// if _, err := os.Stat(dataFilePath); os.IsNotExist(err) { -// return valid, fmt.Errorf("data file does not exist: %v", err) -// } -// if _, err := os.Stat(signatureFilePath); os.IsNotExist(err) { -// return valid, fmt.Errorf("signature file does not exist: %v", err) -// } - -// uniqueID := uuid.New() -// // Step 1: Retrieve the public key from the PKCS#11 token and save it as public.der -// publicKeyFile := fmt.Sprintf("%s-public.der", uniqueID) - -// args := []string{ -// "--module", token.ModulePath, -// "--token-label", token.Label, -// "--pin", token.UserPin, -// "--read-object", -// "--label", token.ObjectLabel, -// "--type", "pubkey", // Extract public key -// "--output-file", publicKeyFile, // Output file for public key in DER format -// } - -// cmd := exec.Command("pkcs11-tool", args...) -// output, err := cmd.CombinedOutput() -// if err != nil { -// return valid, fmt.Errorf("failed to retrieve public key: %v\nOutput: %s", err, output) -// } -// fmt.Println("Public key retrieved successfully.") - -// // Step 2: Verify the signature using OpenSSL and the retrieved public key -// verifyCmd := exec.Command( -// "openssl", "dgst", "-keyform", "DER", "-verify", publicKeyFile, "-sha256", // Use SHA256 for hash -// "-sigopt", "rsa_padding_mode:pss", // Use PSS padding -// "-sigopt", "rsa_pss_saltlen:-1", // Set salt length to default (-1 for auto) -// "-signature", signatureFilePath, // Path to the signature file -// "-binary", dataFilePath, // Path to the data file -// ) - -// // Run the verification command -// verifyOutput, err := verifyCmd.CombinedOutput() -// if err != nil { -// return valid, fmt.Errorf("failed to verify signature: %v\nOutput: %s", err, verifyOutput) -// } - -// // Check the output from OpenSSL to determine if the verification was successful -// if strings.Contains(string(verifyOutput), "Verified OK") { -// fmt.Println("Verification successful: The signature is valid.") -// valid = true -// } else { -// fmt.Println("Verification failed: The signature is invalid.") -// } - -// // Step 3: Remove the public key from the filesystem -// os.Remove(publicKeyFile) - -// return valid, nil -// } From e7ea202f419b0cef34d0a057e4996e7e09ce9abb Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 14:00:46 +0100 Subject: [PATCH 02/26] refactor by distinguishing settings from inputs and checking non empty strings --- .../infrastructure/cryptography/pkcs11.go | 294 +++++++++--------- .../settings/pkcs11_settings.go | 26 ++ internal/infrastructure/utils/validation.go | 13 + 3 files changed, 186 insertions(+), 147 deletions(-) create mode 100644 internal/infrastructure/settings/pkcs11_settings.go create mode 100644 internal/infrastructure/utils/validation.go diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 61a4619..0545fd9 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -1,6 +1,8 @@ package cryptography import ( + "crypto_vault_service/internal/infrastructure/settings" + "crypto_vault_service/internal/infrastructure/utils" "fmt" "os" "os/exec" @@ -9,26 +11,31 @@ import ( // IPKCS11TokenHandler defines the operations for working with a PKCS#11 token type IPKCS11TokenHandler interface { - IsTokenSet() (bool, error) - IsObjectSet() (bool, error) - InitializeToken(slot string) error - AddKey() error - Encrypt(inputFilePath, outputFilePath string) error - Decrypt(inputFilePath, outputFilePath string) error - Sign(inputFilePath, outputFilePath string) error - Verify(dataFilePath, signatureFilePath string) (bool, error) - DeleteObject(objectType, objectLabel string) error + IsTokenSet(label string) (bool, error) + ObjectExists(label, objectLabel string) (bool, error) + InitializeToken(label string) error + AddKey(label, objectLabel, keyType string, keySize uint) error + Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + Sign(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) + DeleteObject(label, objectType, objectLabel string) error } // PKCS11TokenHandler represents the parameters and operations for interacting with a PKCS#11 token type PKCS11TokenHandler struct { - ModulePath string - Label string - SOPin string - UserPin string - ObjectLabel string - KeyType string // "ECDSA" or "RSA" - KeySize int // Key size in bits for RSA or ECDSA (e.g., 256 for ECDSA, 2048 for RSA) + Settings settings.PKCS11Settings +} + +// NewPKCS11TokenHandler creates and returns a new instance of PKCS11TokenHandler +func NewPKCS11TokenHandler(settings settings.PKCS11Settings) (*PKCS11TokenHandler, error) { + if err := settings.Validate(); err != nil { + return nil, err + } + + return &PKCS11TokenHandler{ + Settings: settings, + }, nil } // Public method to execute pkcs11-tool commands and return output @@ -42,55 +49,54 @@ func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string } // IsTokenSet checks if the token exists in the given module path -func (token *PKCS11TokenHandler) IsTokenSet() (bool, error) { - if token.ModulePath == "" || token.Label == "" { - return false, fmt.Errorf("missing module path or token label") +func (token *PKCS11TokenHandler) IsTokenSet(label string) (bool, error) { + if err := utils.CheckNonEmptyStrings(label); err != nil { + return false, err } - args := []string{"--module", token.ModulePath, "-T"} + args := []string{"--module", token.Settings.ModulePath, "-T"} output, err := token.executePKCS11ToolCommand(args) if err != nil { return false, err } - if strings.Contains(output, token.Label) && strings.Contains(output, "token initialized") { - fmt.Printf("Token with label '%s' exists.\n", token.Label) + if strings.Contains(output, label) && strings.Contains(output, "token initialized") { + fmt.Printf("Token with label '%s' exists.\n", label) return true, nil } - fmt.Printf("Error: Token with label '%s' does not exist.\n", token.Label) + fmt.Printf("Error: Token with label '%s' does not exist.\n", label) return false, nil } -// IsObjectSet checks if the specified object exists on the given token -func (token *PKCS11TokenHandler) IsObjectSet() (bool, error) { - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return false, fmt.Errorf("missing required arguments") +// ObjectExists checks if the specified object exists on the given token +func (token *PKCS11TokenHandler) ObjectExists(label, objectLabel string) (bool, error) { + if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { + return false, err } - args := []string{"-O", "--module", token.ModulePath, "--token-label", token.Label, "--pin", token.UserPin} + args := []string{"-O", "--module", token.Settings.ModulePath, "--token-label", label, "--pin", token.Settings.UserPin} output, err := token.executePKCS11ToolCommand(args) if err != nil { return false, err } - if strings.Contains(output, token.ObjectLabel) { - fmt.Printf("Object with label '%s' exists.\n", token.ObjectLabel) + if strings.Contains(output, objectLabel) { + fmt.Printf("Object with label '%s' exists.\n", objectLabel) return true, nil } - fmt.Printf("Error: Object with label '%s' does not exist.\n", token.ObjectLabel) + fmt.Printf("Error: Object with label '%s' does not exist.\n", objectLabel) return false, nil } // InitializeToken initializes the token with the provided label and pins -func (token *PKCS11TokenHandler) InitializeToken(slot string) error { - if token.ModulePath == "" || token.Label == "" || token.SOPin == "" || token.UserPin == "" || slot == "" { - return fmt.Errorf("missing required parameters for token initialization") +func (token *PKCS11TokenHandler) InitializeToken(label string) error { + if err := utils.CheckNonEmptyStrings(label); err != nil { + return err } - // Check if the token is already initialized - tokenExists, err := token.IsTokenSet() + tokenExists, err := token.IsTokenSet(label) if err != nil { return err } @@ -100,98 +106,57 @@ func (token *PKCS11TokenHandler) InitializeToken(slot string) error { return nil } - // Initialize the token - args := []string{"--module", token.ModulePath, "--init-token", "--label", token.Label, "--so-pin", token.SOPin, "--init-pin", "--pin", token.UserPin, "--slot", slot} + args := []string{"--module", token.Settings.ModulePath, "--init-token", "--label", label, "--so-pin", token.Settings.SOPin, "--init-pin", "--pin", token.Settings.UserPin, "--slot", token.Settings.SlotId} _, err = token.executePKCS11ToolCommand(args) if err != nil { - return fmt.Errorf("failed to initialize token with label '%s': %v", token.Label, err) - } - - fmt.Printf("Token with label '%s' initialized successfully.\n", token.Label) - return nil -} - -// DeleteObject deletes a key or object from the token -func (token *PKCS11TokenHandler) DeleteObject(objectType, objectLabel string) error { - if token.ModulePath == "" || token.Label == "" || objectLabel == "" || token.UserPin == "" { - return fmt.Errorf("missing required arguments to delete object") - } - - // Ensure the object type is valid (privkey, pubkey, secrkey, cert, data) - validObjectTypes := map[string]bool{ - "privkey": true, - "pubkey": true, - "secrkey": true, - "cert": true, - "data": true, - } - - if !validObjectTypes[objectType] { - return fmt.Errorf("invalid object type '%s'. Valid types are privkey, pubkey, secrkey, cert, data", objectType) - } - - // Execute the pkcs11-tool command to delete the object - args := []string{ - "--module", token.ModulePath, - "--token-label", token.Label, - "--pin", token.UserPin, - "--delete-object", - "--type", objectType, - "--label", objectLabel, - } - - _, err := token.executePKCS11ToolCommand(args) - if err != nil { - return fmt.Errorf("failed to delete object of type '%s' with label '%s': %v", objectType, objectLabel, err) + return fmt.Errorf("failed to initialize token with label '%s': %v", label, err) } - fmt.Printf("Object of type '%s' with label '%s' deleted successfully.\n", objectType, objectLabel) + fmt.Printf("Token with label '%s' initialized successfully.\n", label) return nil } // AddKey adds the selected key (ECDSA or RSA) to the token -func (token *PKCS11TokenHandler) AddKey() error { - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return fmt.Errorf("missing required arguments") +func (token *PKCS11TokenHandler) AddKey(label, objectLabel, keyType string, keySize uint) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel, keyType); err != nil { + return err } - // Determine key type and call the appropriate function to generate the key - if token.KeyType == "ECDSA" { - return token.addECDSASignKey() - } else if token.KeyType == "RSA" { - return token.addRSASignKey() + if keyType == "ECDSA" { + return token.addECDSASignKey(label, objectLabel, keySize) + } else if keyType == "RSA" { + return token.addRSASignKey(label, objectLabel, keySize) } else { - return fmt.Errorf("unsupported key type: %s", token.KeyType) + return fmt.Errorf("unsupported key type: %s", keyType) } } // addECDSASignKey adds an ECDSA signing key to the token -func (token *PKCS11TokenHandler) addECDSASignKey() error { - if token.KeySize != 256 && token.KeySize != 384 && token.KeySize != 521 { - return fmt.Errorf("ECDSA key size must be one of 256, 384, or 521 bits, but got %d", token.KeySize) +func (token *PKCS11TokenHandler) addECDSASignKey(label, objectLabel string, keySize uint) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { + return err } // Generate the key pair (example using secp256r1) // Supported ECDSA key sizes and their corresponding elliptic curves - ecdsaCurves := map[int]string{ + ecdsaCurves := map[uint]string{ 256: "secp256r1", 384: "secp384r1", 521: "secp521r1", } - curve, supported := ecdsaCurves[token.KeySize] + curve, supported := ecdsaCurves[keySize] if !supported { - return fmt.Errorf("ECDSA key size must be one of 256, 384, or 521 bits, but got %d", token.KeySize) + return fmt.Errorf("ECDSA key size must be one of 256, 384, or 521 bits, but got %d", keySize) } - // Generate the key pair using the correct elliptic curve args := []string{ - "--module", token.ModulePath, - "--token-label", token.Label, + "--module", token.Settings.ModulePath, + "--token-label", label, "--keypairgen", "--key-type", fmt.Sprintf("EC:%s", curve), // Use the dynamically selected curve - "--label", token.ObjectLabel, - "--pin", token.UserPin, + "--label", objectLabel, + "--pin", token.Settings.UserPin, "--usage-sign", } @@ -200,34 +165,38 @@ func (token *PKCS11TokenHandler) addECDSASignKey() error { return fmt.Errorf("failed to add ECDSA key to token: %v", err) } - fmt.Printf("ECDSA key with label '%s' added to token '%s'.\n", token.ObjectLabel, token.Label) + fmt.Printf("ECDSA key with label '%s' added to token '%s'.\n", objectLabel, label) return nil } // addRSASignKey adds an RSA signing key to the token -func (token *PKCS11TokenHandler) addRSASignKey() error { +func (token *PKCS11TokenHandler) addRSASignKey(label, objectLabel string, keySize uint) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { + return err + } + // Supported RSA key sizes (for example, 2048, 3072, and 4096) - supportedRSASizes := []int{2048, 3072, 4096} + supportedRSASizes := []uint{2048, 3072, 4096} validKeySize := false for _, size := range supportedRSASizes { - if token.KeySize == size { + if keySize == size { validKeySize = true break } } if !validKeySize { - return fmt.Errorf("RSA key size must be one of %v bits, but got %d", supportedRSASizes, token.KeySize) + return fmt.Errorf("RSA key size must be one of %v bits, but got %d", supportedRSASizes, keySize) } args := []string{ - "--module", token.ModulePath, - "--token-label", token.Label, + "--module", token.Settings.ModulePath, + "--token-label", label, "--keypairgen", - "--key-type", fmt.Sprintf("RSA:%d", token.KeySize), - "--label", token.ObjectLabel, - "--pin", token.UserPin, + "--key-type", fmt.Sprintf("RSA:%d", keySize), + "--label", objectLabel, + "--pin", token.Settings.UserPin, "--usage-sign", } _, err := token.executePKCS11ToolCommand(args) @@ -235,25 +204,24 @@ func (token *PKCS11TokenHandler) addRSASignKey() error { return fmt.Errorf("failed to add RSA key to token: %v", err) } - fmt.Printf("RSA key with label '%s' added to token '%s'.\n", token.ObjectLabel, token.Label) + fmt.Printf("RSA key with label '%s' added to token '%s'.\n", objectLabel, label) return nil } // Encrypt encrypts data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pkcs -func (token *PKCS11TokenHandler) Encrypt(inputFilePath, outputFilePath string) error { - // Validate required parameters - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return fmt.Errorf("missing required arguments for encryption") +func (token *PKCS11TokenHandler) Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { + return err } - if token.KeyType != "RSA" { + if keyType != "RSA" { return fmt.Errorf("only RSA keys are supported for encryption") } - // Step 1: Prepare the URI to use PKCS#11 engine for accessing the public key - keyURI := fmt.Sprintf("pkcs11:token=%s;object=%s;type=public;pin-value=%s", token.Label, token.ObjectLabel, token.UserPin) + // Prepare the URI to use PKCS#11 engine for accessing the public key + keyURI := fmt.Sprintf("pkcs11:token=%s;object=%s;type=public;pin-value=%s", label, objectLabel, token.Settings.UserPin) - // Step 2: Run OpenSSL command to encrypt using the public key from the PKCS#11 token + // Run OpenSSL command to encrypt using the public key from the PKCS#11 token encryptCmd := exec.Command( "openssl", "pkeyutl", "-engine", "pkcs11", "-keyform", "engine", "-pubin", "-encrypt", "-inkey", keyURI, "-pkeyopt", "rsa_padding_mode:pkcs1", "-in", inputFilePath, "-out", outputFilePath, @@ -265,19 +233,17 @@ func (token *PKCS11TokenHandler) Encrypt(inputFilePath, outputFilePath string) e return fmt.Errorf("failed to encrypt data with OpenSSL: %v\nOutput: %s", err, encryptOutput) } - // Output success message fmt.Printf("Encryption successful. Encrypted data written to %s\n", outputFilePath) return nil } // Decrypt decrypts data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pkcs -func (token *PKCS11TokenHandler) Decrypt(inputFilePath, outputFilePath string) error { - // Validate required parameters - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return fmt.Errorf("missing required arguments for decryption") +func (token *PKCS11TokenHandler) Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { + return err } - if token.KeyType != "RSA" { + if keyType != "RSA" { return fmt.Errorf("only RSA keys are supported for decryption") } @@ -286,10 +252,10 @@ func (token *PKCS11TokenHandler) Decrypt(inputFilePath, outputFilePath string) e return fmt.Errorf("input file does not exist: %v", err) } - // Step 1: Prepare the URI to use PKCS#11 engine for accessing the private key - keyURI := fmt.Sprintf("pkcs11:token=%s;object=%s;type=private;pin-value=%s", token.Label, token.ObjectLabel, token.UserPin) + // Prepare the URI to use PKCS#11 engine for accessing the private key + keyURI := fmt.Sprintf("pkcs11:token=%s;object=%s;type=private;pin-value=%s", label, objectLabel, token.Settings.UserPin) - // Step 2: Run OpenSSL command to decrypt the data using the private key from the PKCS#11 token + // Run OpenSSL command to decrypt the data using the private key from the PKCS#11 token decryptCmd := exec.Command( "openssl", "pkeyutl", "-engine", "pkcs11", "-keyform", "engine", "-decrypt", "-inkey", keyURI, "-pkeyopt", "rsa_padding_mode:pkcs1", "-in", inputFilePath, "-out", outputFilePath, @@ -301,19 +267,17 @@ func (token *PKCS11TokenHandler) Decrypt(inputFilePath, outputFilePath string) e return fmt.Errorf("failed to decrypt data with OpenSSL: %v\nOutput: %s", err, decryptOutput) } - // Output success message fmt.Printf("Decryption successful. Decrypted data written to %s\n", outputFilePath) return nil } // Sign signs data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Sign(inputFilePath, outputFilePath string) error { - // Validate required parameters - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return fmt.Errorf("missing required arguments for signing") +func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { + return err } - if token.KeyType != "RSA" && token.KeyType != "ECDSA" { + if keyType != "RSA" && keyType != "ECDSA" { return fmt.Errorf("only RSA and ECDSA keys are supported for signing") } @@ -322,24 +286,24 @@ func (token *PKCS11TokenHandler) Sign(inputFilePath, outputFilePath string) erro return fmt.Errorf("input file does not exist: %v", err) } - // Step 1: Prepare the OpenSSL command based on key type + // Prepare the OpenSSL command based on key type var signCmd *exec.Cmd var signatureFormat string - if token.KeyType == "RSA" { + if keyType == "RSA" { signatureFormat = "rsa_padding_mode:pss" // Command for signing with RSA-PSS signCmd = exec.Command( "openssl", "dgst", "-engine", "pkcs11", "-keyform", "engine", "-sign", - "pkcs11:token="+token.Label+";object="+token.ObjectLabel+";type=private;pin-value="+token.UserPin, + "pkcs11:token="+label+";object="+objectLabel+";type=private;pin-value="+token.Settings.UserPin, "-sigopt", signatureFormat, "-sha384", // Use SHA-384 "-out", outputFilePath, inputFilePath, ) - } else if token.KeyType == "ECDSA" { + } else if keyType == "ECDSA" { // Command for signing with ECDSA signCmd = exec.Command( "openssl", "dgst", "-engine", "pkcs11", "-keyform", "engine", "-sign", - "pkcs11:token="+token.Label+";object="+token.ObjectLabel+";type=private;pin-value="+token.UserPin, + "pkcs11:token="+label+";object="+objectLabel+";type=private;pin-value="+token.Settings.UserPin, "-sha384", // ECDSA typically uses SHA-384 "-out", outputFilePath, inputFilePath, ) @@ -356,15 +320,14 @@ func (token *PKCS11TokenHandler) Sign(inputFilePath, outputFilePath string) erro } // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Verify(dataFilePath, signatureFilePath string) (bool, error) { +func (token *PKCS11TokenHandler) Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) { valid := false - // Validate required parameters - if token.ModulePath == "" || token.Label == "" || token.ObjectLabel == "" || token.UserPin == "" { - return valid, fmt.Errorf("missing required arguments for verification") + if err := utils.CheckNonEmptyStrings(label, objectLabel, keyType, dataFilePath, signatureFilePath); err != nil { + return valid, err } - if token.KeyType != "RSA" && token.KeyType != "ECDSA" { + if keyType != "RSA" && keyType != "ECDSA" { return valid, fmt.Errorf("only RSA and ECDSA keys are supported for verification") } @@ -376,22 +339,22 @@ func (token *PKCS11TokenHandler) Verify(dataFilePath, signatureFilePath string) return valid, fmt.Errorf("signature file does not exist: %v", err) } - // Step 1: Prepare the OpenSSL command based on key type + // Prepare the OpenSSL command based on key type var verifyCmd *exec.Cmd - if token.KeyType == "RSA" { + if keyType == "RSA" { // Command for verifying with RSA-PSS verifyCmd = exec.Command( "openssl", "dgst", "-engine", "pkcs11", "-keyform", "engine", "-verify", - "pkcs11:token="+token.Label+";object="+token.ObjectLabel+";type=public;pin-value="+token.UserPin, + "pkcs11:token="+label+";object="+objectLabel+";type=public;pin-value="+token.Settings.UserPin, "-sigopt", "rsa_padding_mode:pss", "-sha384", // Use SHA-384 for verification "-signature", signatureFilePath, "-binary", dataFilePath, ) - } else if token.KeyType == "ECDSA" { + } else if keyType == "ECDSA" { // Command for verifying with ECDSA verifyCmd = exec.Command( "openssl", "dgst", "-engine", "pkcs11", "-keyform", "engine", "-verify", - "pkcs11:token="+token.Label+";object="+token.ObjectLabel+";type=public;pin-value="+token.UserPin, + "pkcs11:token="+label+";object="+objectLabel+";type=public;pin-value="+token.Settings.UserPin, "-sha384", // ECDSA typically uses SHA-384 "-signature", signatureFilePath, "-binary", dataFilePath, ) @@ -413,3 +376,40 @@ func (token *PKCS11TokenHandler) Verify(dataFilePath, signatureFilePath string) return valid, nil } + +// DeleteObject deletes a key or object from the token +func (token *PKCS11TokenHandler) DeleteObject(label, objectType, objectLabel string) error { + if err := utils.CheckNonEmptyStrings(label, objectType, objectLabel); err != nil { + return err + } + + // Ensure the object type is valid (privkey, pubkey, secrkey, cert, data) + validObjectTypes := map[string]bool{ + "privkey": true, + "pubkey": true, + "secrkey": true, + "cert": true, + "data": true, + } + + if !validObjectTypes[objectType] { + return fmt.Errorf("invalid object type '%s'. Valid types are privkey, pubkey, secrkey, cert, data", objectType) + } + + args := []string{ + "--module", token.Settings.ModulePath, + "--token-label", label, + "--pin", token.Settings.UserPin, + "--delete-object", + "--type", objectType, + "--label", objectLabel, + } + + _, err := token.executePKCS11ToolCommand(args) + if err != nil { + return fmt.Errorf("failed to delete object of type '%s' with label '%s': %v", objectType, objectLabel, err) + } + + fmt.Printf("Object of type '%s' with label '%s' deleted successfully.\n", objectType, objectLabel) + return nil +} diff --git a/internal/infrastructure/settings/pkcs11_settings.go b/internal/infrastructure/settings/pkcs11_settings.go new file mode 100644 index 0000000..54b94a7 --- /dev/null +++ b/internal/infrastructure/settings/pkcs11_settings.go @@ -0,0 +1,26 @@ +package settings + +import ( + "fmt" + + "github.com/go-playground/validator/v10" +) + +// PKCS11Settings holds the configuration settings required to interact with a PKCS#11 module +type PKCS11Settings struct { + ModulePath string `validate:"required"` + SOPin string `validate:"required"` + UserPin string `validate:"required"` + SlotId string `validate:"required"` +} + +// Validate checks that all fields in PKCS11Settings are valid (non-empty in this case) +func (settings *PKCS11Settings) Validate() error { + validate := validator.New() + + err := validate.Struct(settings) + if err != nil { + return fmt.Errorf("validation failed: %v", err) + } + return nil +} diff --git a/internal/infrastructure/utils/validation.go b/internal/infrastructure/utils/validation.go new file mode 100644 index 0000000..5b645ce --- /dev/null +++ b/internal/infrastructure/utils/validation.go @@ -0,0 +1,13 @@ +package utils + +import "fmt" + +// CheckNonEmptyStrings checks if any of the input strings are empty +func CheckNonEmptyStrings(args ...string) error { + for _, arg := range args { + if arg == "" { + return fmt.Errorf("one of the input strings is empty") + } + } + return nil +} From b52ca3fe20ffd634cbe5943e0420f35bcb131b30 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 14:10:22 +0100 Subject: [PATCH 03/26] describe signatures --- internal/infrastructure/cryptography/pkcs11.go | 11 ++++++++++- 1 file changed, 10 insertions(+), 1 deletion(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 0545fd9..e10a3b1 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -11,14 +11,23 @@ import ( // IPKCS11TokenHandler defines the operations for working with a PKCS#11 token type IPKCS11TokenHandler interface { + // IsTokenSet checks if the token exists in the given module path IsTokenSet(label string) (bool, error) + // ObjectExists checks if the specified object exists on the given token ObjectExists(label, objectLabel string) (bool, error) + // InitializeToken initializes the token with the provided label and pins InitializeToken(label string) error + // AddKey adds the selected key (ECDSA or RSA) to the token AddKey(label, objectLabel, keyType string, keySize uint) error + // Encrypt encrypts data using the cryptographic capabilities of the PKCS#11 token Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + // Decrypt decrypts data using the cryptographic capabilities of the PKCS#11 token Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + // Sign signs data using the cryptographic capabilities of the PKCS#11 token Sign(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) + // DeleteObject deletes a key or object from the token DeleteObject(label, objectType, objectLabel string) error } @@ -38,7 +47,7 @@ func NewPKCS11TokenHandler(settings settings.PKCS11Settings) (*PKCS11TokenHandle }, nil } -// Public method to execute pkcs11-tool commands and return output +// Private method to execute pkcs11-tool commands and return output func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string, error) { cmd := exec.Command("pkcs11-tool", args...) output, err := cmd.CombinedOutput() From 8cf7497e72bd7ef18478a3ba26608cf60112f7bc Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 14:13:25 +0100 Subject: [PATCH 04/26] add variadic helper function checking wether file exists --- .../infrastructure/cryptography/pkcs11.go | 35 +++++++++---------- internal/infrastructure/utils/validation.go | 15 +++++++- 2 files changed, 30 insertions(+), 20 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index e10a3b1..8631f59 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -4,7 +4,6 @@ import ( "crypto_vault_service/internal/infrastructure/settings" "crypto_vault_service/internal/infrastructure/utils" "fmt" - "os" "os/exec" "strings" ) @@ -223,6 +222,10 @@ func (token *PKCS11TokenHandler) Encrypt(label, objectLabel, inputFilePath, outp return err } + if err := utils.CheckFilesExist(inputFilePath); err != nil { + return err + } + if keyType != "RSA" { return fmt.Errorf("only RSA keys are supported for encryption") } @@ -252,13 +255,12 @@ func (token *PKCS11TokenHandler) Decrypt(label, objectLabel, inputFilePath, outp return err } - if keyType != "RSA" { - return fmt.Errorf("only RSA keys are supported for decryption") + if err := utils.CheckFilesExist(inputFilePath); err != nil { + return err } - // Check if input file exists - if _, err := os.Stat(inputFilePath); os.IsNotExist(err) { - return fmt.Errorf("input file does not exist: %v", err) + if keyType != "RSA" { + return fmt.Errorf("only RSA keys are supported for decryption") } // Prepare the URI to use PKCS#11 engine for accessing the private key @@ -286,13 +288,12 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputF return err } - if keyType != "RSA" && keyType != "ECDSA" { - return fmt.Errorf("only RSA and ECDSA keys are supported for signing") + if err := utils.CheckFilesExist(inputFilePath); err != nil { + return err } - // Check if the input file exists - if _, err := os.Stat(inputFilePath); os.IsNotExist(err) { - return fmt.Errorf("input file does not exist: %v", err) + if keyType != "RSA" && keyType != "ECDSA" { + return fmt.Errorf("only RSA and ECDSA keys are supported for signing") } // Prepare the OpenSSL command based on key type @@ -336,16 +337,12 @@ func (token *PKCS11TokenHandler) Verify(label, objectLabel, keyType, dataFilePat return valid, err } - if keyType != "RSA" && keyType != "ECDSA" { - return valid, fmt.Errorf("only RSA and ECDSA keys are supported for verification") + if err := utils.CheckFilesExist(dataFilePath, signatureFilePath); err != nil { + return valid, err } - // Check if the input files exist - if _, err := os.Stat(dataFilePath); os.IsNotExist(err) { - return valid, fmt.Errorf("data file does not exist: %v", err) - } - if _, err := os.Stat(signatureFilePath); os.IsNotExist(err) { - return valid, fmt.Errorf("signature file does not exist: %v", err) + if keyType != "RSA" && keyType != "ECDSA" { + return valid, fmt.Errorf("only RSA and ECDSA keys are supported for verification") } // Prepare the OpenSSL command based on key type diff --git a/internal/infrastructure/utils/validation.go b/internal/infrastructure/utils/validation.go index 5b645ce..29eab3c 100644 --- a/internal/infrastructure/utils/validation.go +++ b/internal/infrastructure/utils/validation.go @@ -1,6 +1,9 @@ package utils -import "fmt" +import ( + "fmt" + "os" +) // CheckNonEmptyStrings checks if any of the input strings are empty func CheckNonEmptyStrings(args ...string) error { @@ -11,3 +14,13 @@ func CheckNonEmptyStrings(args ...string) error { } return nil } + +// CheckFilesExist checks if the input files exist +func CheckFilesExist(filePaths ...string) error { + for _, filePath := range filePaths { + if _, err := os.Stat(filePath); os.IsNotExist(err) { + return fmt.Errorf("file does not exist: %s", filePath) + } + } + return nil +} From 6dbae76f3b2d4aa92f3576495ae34d0a50613243 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 14:25:32 +0100 Subject: [PATCH 05/26] add tests for validation functions --- .../infrastructure/utils/validation_test.go | 50 +++++++++++++++++++ 1 file changed, 50 insertions(+) create mode 100644 test/unit/infrastructure/utils/validation_test.go diff --git a/test/unit/infrastructure/utils/validation_test.go b/test/unit/infrastructure/utils/validation_test.go new file mode 100644 index 0000000..a23f0f0 --- /dev/null +++ b/test/unit/infrastructure/utils/validation_test.go @@ -0,0 +1,50 @@ +package utils + +import ( + "crypto_vault_service/internal/infrastructure/utils" + "os" + "testing" + + "github.com/stretchr/testify/assert" +) + +// TestCheckNonEmptyStrings tests the CheckNonEmptyStrings function +func TestCheckNonEmptyStrings(t *testing.T) { + // Test with all non-empty strings + err := utils.CheckNonEmptyStrings("test", "hello", "world") + assert.NoError(t, err, "Expected no error for non-empty strings") + + // Test with one empty string + err = utils.CheckNonEmptyStrings("test", "", "world") + assert.Error(t, err, "Expected error for empty string") + assert.Equal(t, err.Error(), "one of the input strings is empty", "Error message should match") + + // Test with all empty strings + err = utils.CheckNonEmptyStrings("", "", "") + assert.Error(t, err, "Expected error for all empty strings") + assert.Equal(t, err.Error(), "one of the input strings is empty", "Error message should match") +} + +// TestCheckFilesExist tests the CheckFilesExist function +func TestCheckFilesExist(t *testing.T) { + // Prepare a test file path for a file that will be created during the test + existingFilePath := "testfile.txt" + nonExistentFilePath := "non_existent_file.txt" + + // Create the test file to ensure it exists + err := os.WriteFile(existingFilePath, []byte("This is a test file."), 0644) + assert.NoError(t, err, "Expected no error when creating test file") + + // Test with existing file + err = utils.CheckFilesExist(existingFilePath) + assert.NoError(t, err, "Expected no error for existing file") + + // Test with non-existent file + err = utils.CheckFilesExist(nonExistentFilePath) + assert.Error(t, err, "Expected error for non-existent file") + assert.Equal(t, err.Error(), "file does not exist: non_existent_file.txt", "Error message should match") + + // Clean up the test file + err = os.Remove(existingFilePath) + assert.NoError(t, err, "Error cleaning up the test file") +} From b72e7c7a1aef223b25bca0c35f98c82100d137a1 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 14:25:58 +0100 Subject: [PATCH 06/26] modify import of utils package --- test/unit/infrastructure/utils/io_test.go | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/unit/infrastructure/utils/io_test.go b/test/unit/infrastructure/utils/io_test.go index b85a75a..69152e9 100644 --- a/test/unit/infrastructure/utils/io_test.go +++ b/test/unit/infrastructure/utils/io_test.go @@ -4,7 +4,7 @@ import ( "os" "testing" - utils "crypto_vault_service/internal/infrastructure/utils" + "crypto_vault_service/internal/infrastructure/utils" "github.com/stretchr/testify/assert" ) From f9299ec3de6ae26d511863a03db7198ee3588d40 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 17:32:25 +0100 Subject: [PATCH 07/26] make method privat and fix tests --- .../infrastructure/cryptography/pkcs11.go | 8 +- .../cryptography/pkcs11_test.go | 158 ++++++++++-------- 2 files changed, 92 insertions(+), 74 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 8631f59..f979487 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -10,8 +10,6 @@ import ( // IPKCS11TokenHandler defines the operations for working with a PKCS#11 token type IPKCS11TokenHandler interface { - // IsTokenSet checks if the token exists in the given module path - IsTokenSet(label string) (bool, error) // ObjectExists checks if the specified object exists on the given token ObjectExists(label, objectLabel string) (bool, error) // InitializeToken initializes the token with the provided label and pins @@ -56,8 +54,8 @@ func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string return string(output), nil } -// IsTokenSet checks if the token exists in the given module path -func (token *PKCS11TokenHandler) IsTokenSet(label string) (bool, error) { +// isTokenSet checks if the token exists in the given module path +func (token *PKCS11TokenHandler) isTokenSet(label string) (bool, error) { if err := utils.CheckNonEmptyStrings(label); err != nil { return false, err } @@ -104,7 +102,7 @@ func (token *PKCS11TokenHandler) InitializeToken(label string) error { return err } - tokenExists, err := token.IsTokenSet(label) + tokenExists, err := token.isTokenSet(label) if err != nil { return err } diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index d779c9e..6ea94e5 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -6,147 +6,169 @@ import ( "testing" cryptography "crypto_vault_service/internal/infrastructure/cryptography" + "crypto_vault_service/internal/infrastructure/settings" "github.com/stretchr/testify/assert" "github.com/stretchr/testify/require" ) type PKCS11Test struct { - Slot string - Token *cryptography.PKCS11TokenHandler + Label string + ObjectLabel string + KeyType string + KeySize uint + TokenHandler *cryptography.PKCS11TokenHandler } // NewPKCS11Test sets up the test environment for PKCS#11 integration tests -func NewPKCS11Test(slot, modulePath, Label, soPin, userPin, objectLabel, keyType string, keySize int) *PKCS11Test { +func NewPKCS11Test(t *testing.T, slotId, modulePath, label, soPin, userPin, objectLabel, keyType string, keySize uint) *PKCS11Test { + settings := settings.PKCS11Settings{ + ModulePath: modulePath, + SOPin: soPin, + UserPin: userPin, + SlotId: slotId, + } + + tokenHandler, err := cryptography.NewPKCS11TokenHandler(settings) + if err != nil { + t.Logf("%v\n", err) + } return &PKCS11Test{ - Slot: slot, - Token: &cryptography.PKCS11TokenHandler{ - ModulePath: modulePath, - Label: Label, - SOPin: soPin, - UserPin: userPin, - ObjectLabel: objectLabel, - KeyType: keyType, - KeySize: keySize, - }, + Label: label, + ObjectLabel: objectLabel, + KeyType: keyType, + KeySize: keySize, + TokenHandler: tokenHandler, } } func (p *PKCS11Test) Setup(t *testing.T) { - err := p.Token.InitializeToken(p.Slot) + err := p.TokenHandler.InitializeToken(p.Label) require.NoError(t, err, "Failed to initialize PKCS#11 token") - - isTokenSet, err := p.Token.IsTokenSet() - require.NoError(t, err, "Error checking if token is set") - assert.True(t, isTokenSet, "The token should be initialized and set") } // DeleteKeyFromToken deletes any existing key with the same label before adding a new key. func (p *PKCS11Test) DeleteKeyFromToken(t *testing.T) { // Deleting the private key - err := p.Token.DeleteObject("privkey", p.Token.ObjectLabel) + objectType := "privkey" + err := p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { t.Logf("Warning: Failed to delete existing private key: %v\n", err) } // Deleting the public key - err = p.Token.DeleteObject("pubkey", p.Token.ObjectLabel) + objectType = "pubkey" + err = p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { t.Logf("Warning: Failed to delete existing public key: %v\n", err) } // Deleting the secret key (only if it exists) - err = p.Token.DeleteObject("secrkey", p.Token.ObjectLabel) + objectType = "secrkey" + err = p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { t.Logf("Warning: Failed to delete existing secret key: %v\n", err) } } // AddKeyToToken is a helper function to add a key to a token -func (p *PKCS11Test) AddKeyToToken(t *testing.T) { - err := p.Token.AddKey() +func (p *PKCS11Test) AddKeyToToken(t *testing.T, label, objectLabel, keyType string, keySize uint) { + err := p.TokenHandler.AddKey(label, objectLabel, keyType, keySize) assert.NoError(t, err, "Failed to add key to the token") - isObjectSet, err := p.Token.IsObjectSet() + isObjectSet, err := p.TokenHandler.ObjectExists(p.Label, p.ObjectLabel) assert.NoError(t, err, "Error checking if key is set") - assert.True(t, isObjectSet, fmt.Sprintf("The %s key should be added to the token", p.Token.KeyType)) + assert.True(t, isObjectSet, fmt.Sprintf("The %s key should be added to the token", p.KeyType)) } // TestAddRSAKey tests adding an RSA key to a PKCS#11 token func TestAddRSAKey(t *testing.T) { - test := NewPKCS11Test("0x0", "/usr/lib/softhsm/libsofthsm2.so", "MyToken", "123456", "234567", "TestRSAKey", "RSA", 2048) + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestRSAKey" + keyType := "RSA" + keySize := 2048 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) test.Setup(t) - // Add an RSA key to the token - test.AddKeyToToken(t) + test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) test.DeleteKeyFromToken(t) } // TestAddECDSAKey tests adding an ECDSA key to a PKCS#11 token func TestAddECDSAKey(t *testing.T) { - test := NewPKCS11Test("0x0", "/usr/lib/softhsm/libsofthsm2.so", "MyToken", "123456", "234567", "TestECDSAKey", "ECDSA", 256) + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestECDSAKey" + keyType := "ECDSA" + keySize := 256 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) test.Setup(t) - // Add an ECDSA key to the token - test.AddKeyToToken(t) + test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) test.DeleteKeyFromToken(t) } // TestEncryptDecrypt tests the encryption ad decryption functionality of the PKCS#11 token func TestEncryptDecrypt(t *testing.T) { - // Prepare the test PKCS#11Token - test := NewPKCS11Test("0x0", "/usr/lib/softhsm/libsofthsm2.so", "MyToken", "123456", "234567", "TestRSAKey", "RSA", 2048) + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestRSAKey" + keyType := "RSA" + keySize := 2048 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) test.Setup(t) - // Add an RSA key to the token - test.AddKeyToToken(t) + test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) - // Sample input file with plaintext data (for testing purposes) inputFilePath := "plain-text.txt" err := os.WriteFile(inputFilePath, []byte("This is some data to encrypt."), 0644) require.NoError(t, err, "Failed to write plaintext data to input file") - // Output file path where encrypted data will be stored outputFilePath := "encrypted.bin" - // Encrypt the data using the Encrypt method - err = test.Token.Encrypt(inputFilePath, outputFilePath) + err = test.TokenHandler.Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType) assert.NoError(t, err, "Failed to encrypt data using the PKCS#11 token") - // Try reading the encrypted data from the output file encryptedData, err := os.ReadFile(outputFilePath) if err != nil { t.Fatalf("Failed to read encrypted data from output file: %v", err) } - // Ensure the encrypted data is non-empty assert.NotEmpty(t, encryptedData, "Encrypted data should not be empty") - // Decrypt the data using the Decrypt method decryptedFilePath := "decrypted.txt" - err = test.Token.Decrypt(outputFilePath, decryptedFilePath) + err = test.TokenHandler.Decrypt(label, objectLabel, outputFilePath, decryptedFilePath, keyType) assert.NoError(t, err, "Failed to decrypt data using the PKCS#11 token") - // Try reading the decrypted data from the file decryptedData, err := os.ReadFile(decryptedFilePath) if err != nil { t.Fatalf("Failed to read decrypted data from output file: %v", err) } - // Ensure the decrypted data matches the original plaintext data originalData, err := os.ReadFile(inputFilePath) require.NoError(t, err, "Failed to read original input file") assert.Equal(t, originalData, decryptedData, "Decrypted data should match the original plaintext") - // Clean up by deleting the key from the token test.DeleteKeyFromToken(t) - // Optionally, delete the files after the test err = os.Remove(inputFilePath) require.NoError(t, err, "Failed to remove input file") err = os.Remove(outputFilePath) @@ -157,46 +179,44 @@ func TestEncryptDecrypt(t *testing.T) { // TestSignAndVerify tests the signing and verification functionality of the PKCS#11 token func TestSignAndVerify(t *testing.T) { - // Prepare the test PKCS#11Token - test := NewPKCS11Test("0x0", "/usr/lib/softhsm/libsofthsm2.so", "MyToken", "123456", "234567", "TestRSAKey", "RSA", 2048) + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestRSAKey" + keyType := "RSA" + keySize := 2048 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) test.Setup(t) - // Add an RSA key to the token - test.AddKeyToToken(t) + test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) - // Sample input file with data to sign (for testing purposes) - inputFilePath := "data-to-sign.txt" - err := os.WriteFile(inputFilePath, []byte("This is some data to sign."), 0644) + dataFilePath := "data-to-sign.txt" + err := os.WriteFile(dataFilePath, []byte("This is some data to sign."), 0644) require.NoError(t, err, "Failed to write data to sign to input file") - // Output file path where the signature will be stored signatureFilePath := "data.sig" - // Sign the data using the Sign method - err = test.Token.Sign(inputFilePath, signatureFilePath) + err = test.TokenHandler.Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType) assert.NoError(t, err, "Failed to sign data using the PKCS#11 token") - // Try reading the signature data from the output file signatureData, err := os.ReadFile(signatureFilePath) if err != nil { t.Fatalf("Failed to read signature data from output file: %v", err) } - // Ensure the signature data is non-empty assert.NotEmpty(t, signatureData, "Signature data should not be empty") - // Verify the signature using the Verify method - valid, err := test.Token.Verify(inputFilePath, signatureFilePath) + valid, err := test.TokenHandler.Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath) assert.NoError(t, err, "Failed to verify the signature using the PKCS#11 token") - // Ensure the signature is valid assert.True(t, valid, "The signature should be valid") - // Clean up by deleting the key from the token test.DeleteKeyFromToken(t) - // Optionally, delete the files after the test - err = os.Remove(inputFilePath) + err = os.Remove(dataFilePath) require.NoError(t, err, "Failed to remove input file") err = os.Remove(signatureFilePath) require.NoError(t, err, "Failed to remove signature file") @@ -208,11 +228,11 @@ func TestSignAndVerify(t *testing.T) { // // proper execution and will be addressed in a future update. // func TestSignAndVerifyECDSA(t *testing.T) { // // Prepare the test PKCS#11Token for ECDSA -// test := NewPKCS11Test("0x1", "/usr/lib/softhsm/libsofthsm2.so", "MyToken2", "123456", "234567", "TestECDSAKey", "ECDSA", 256) +// test := NewPKCS11Test(t, "0x1", "/usr/lib/softhsm/libsofthsm2.so", "MyToken2", "123456", "234567", "TestECDSAKey", "ECDSA", 256) // test.Setup(t) // // Add an ECDSA key to the token -// test.AddKeyToToken(t) +// test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) // // Sample input file with data to sign (for testing purposes) // inputFilePath := "data-to-sign.txt" @@ -223,7 +243,7 @@ func TestSignAndVerify(t *testing.T) { // signatureFilePath := "data.sig" // // Sign the data using the Sign method (ECDSA) -// err = test.Token.Sign(inputFilePath, signatureFilePath) +// err = p.TokenHandler.Sign(inputFilePath, signatureFilePath) // assert.NoError(t, err, "Failed to sign data using the PKCS#11 token with ECDSA") // // Try reading the signature data from the output file @@ -236,7 +256,7 @@ func TestSignAndVerify(t *testing.T) { // assert.NotEmpty(t, signatureData, "Signature data should not be empty") // // Verify the signature using the Verify method (ECDSA) -// valid, err := test.Token.Verify(inputFilePath, signatureFilePath) +// valid, err := p.TokenHandler.Verify(inputFilePath, signatureFilePath) // assert.NoError(t, err, "Failed to verify the signature using the PKCS#11 token with ECDSA") // // Ensure the signature is valid From 5d0cec9bf1870d855165c110f124cca9075189bd Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 18:26:10 +0100 Subject: [PATCH 08/26] implement list token and list objects methods --- .../infrastructure/cryptography/pkcs11.go | 122 ++++++++++++++++++ .../cryptography/pkcs11_test.go | 71 ++++++++-- 2 files changed, 184 insertions(+), 9 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index f979487..92da430 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -8,8 +8,28 @@ import ( "strings" ) +// Token represents a PKCS#11 token with its label and other metadata. +type Token struct { + Label string + Manufacturer string + Model string + SerialNumber string +} + +// TokenObject represents a PKCS#11 object (e.g. public or private key) with metadata. +type TokenObject struct { + Label string + Type string // The type of the object (e.g. RSA, ECDSA) + Usage string // The usage of the object (e.g. encrypt, sign, decrypt) + Access string // Access controls for the object (e.g. sensitive, always sensitive) +} + // IPKCS11TokenHandler defines the operations for working with a PKCS#11 token type IPKCS11TokenHandler interface { + // ListTokens lists all available tokens in the available slots + ListTokens() ([]Token, error) + // ListObjects lists all objects (e.g. keys) in a specific token based on the token label + ListObjects(tokenLabel string) ([]TokenObject, error) // ObjectExists checks if the specified object exists on the given token ObjectExists(label, objectLabel string) (bool, error) // InitializeToken initializes the token with the provided label and pins @@ -54,6 +74,108 @@ func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string return string(output), nil } +// ListTokens lists all available tokens in the available slots +func (token *PKCS11TokenHandler) ListTokens() ([]Token, error) { + if err := utils.CheckNonEmptyStrings(token.Settings.ModulePath); err != nil { + return nil, err + } + + listCmd := exec.Command( + "pkcs11-tool", "--module", token.Settings.ModulePath, "-L", + ) + + output, err := listCmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to list tokens with pkcs11-tool: %v\nOutput: %s", err, output) + } + + var tokens []Token + lines := strings.Split(string(output), "\n") + var currentToken *Token + + for _, line := range lines { + + if strings.Contains(line, "token label") { + if currentToken != nil { + tokens = append(tokens, *currentToken) + } + + currentToken = &Token{} + currentToken.Label = strings.TrimSpace(strings.Split(line, ":")[1]) + } + if currentToken != nil { + if strings.Contains(line, "token manufacturer") { + currentToken.Manufacturer = strings.TrimSpace(strings.Split(line, ":")[1]) + } + if strings.Contains(line, "token model") { + currentToken.Model = strings.TrimSpace(strings.Split(line, ":")[1]) + } + if strings.Contains(line, "serial num") { + currentToken.SerialNumber = strings.TrimSpace(strings.Split(line, ":")[1]) + } + } + } + + if currentToken != nil { + tokens = append(tokens, *currentToken) + } + + return tokens, nil +} + +// ListObjects lists all objects (e.g. keys) in a specific token based on the token label. +func (token *PKCS11TokenHandler) ListObjects(tokenLabel string) ([]TokenObject, error) { + // + if err := utils.CheckNonEmptyStrings(tokenLabel, token.Settings.ModulePath); err != nil { + return nil, err + } + + listObjectsCmd := exec.Command( + "pkcs11-tool", "--module", token.Settings.ModulePath, "-O", "--token-label", tokenLabel, "--pin", token.Settings.UserPin, + ) + + output, err := listObjectsCmd.CombinedOutput() + if err != nil { + return nil, fmt.Errorf("failed to list objects with pkcs11-tool: %v\nOutput: %s", err, output) + } + + var objects []TokenObject + lines := strings.Split(string(output), "\n") + var currentObject *TokenObject + + for _, line := range lines { + + if strings.Contains(line, "Private") || strings.Contains(line, "Public") || strings.Contains(line, "Secret") { + if currentObject != nil { + objects = append(objects, *currentObject) + } + + currentObject = &TokenObject{ + Label: "", + Type: "", + Usage: "", + Access: "", + } + currentObject.Type = line + } + if strings.Contains(line, "label:") { + currentObject.Label = strings.TrimSpace(strings.Split(line, ":")[1]) + } + if strings.Contains(line, "Usage:") { + currentObject.Usage = strings.TrimSpace(strings.Split(line, ":")[1]) + } + if strings.Contains(line, "Access:") { + currentObject.Access = strings.TrimSpace(strings.Split(line, ":")[1]) + } + } + + if currentObject != nil { + objects = append(objects, *currentObject) + } + + return objects, nil +} + // isTokenSet checks if the token exists in the given module path func (token *PKCS11TokenHandler) isTokenSet(label string) (bool, error) { if err := utils.CheckNonEmptyStrings(label); err != nil { diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 6ea94e5..500fdcf 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -42,28 +42,26 @@ func NewPKCS11Test(t *testing.T, slotId, modulePath, label, soPin, userPin, obje } } -func (p *PKCS11Test) Setup(t *testing.T) { +// InitializeToken initializes a PKCS#11 token by calling the TokenHandler's InitializeToken method +func (p *PKCS11Test) InitializeToken(t *testing.T) { err := p.TokenHandler.InitializeToken(p.Label) require.NoError(t, err, "Failed to initialize PKCS#11 token") } // DeleteKeyFromToken deletes any existing key with the same label before adding a new key. func (p *PKCS11Test) DeleteKeyFromToken(t *testing.T) { - // Deleting the private key objectType := "privkey" err := p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { t.Logf("Warning: Failed to delete existing private key: %v\n", err) } - // Deleting the public key objectType = "pubkey" err = p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { t.Logf("Warning: Failed to delete existing public key: %v\n", err) } - // Deleting the secret key (only if it exists) objectType = "secrkey" err = p.TokenHandler.DeleteObject(p.Label, objectType, p.ObjectLabel) if err != nil { @@ -81,6 +79,32 @@ func (p *PKCS11Test) AddKeyToToken(t *testing.T, label, objectLabel, keyType str assert.True(t, isObjectSet, fmt.Sprintf("The %s key should be added to the token", p.KeyType)) } +// TestListTokens tests listing available tokens using PKCS#11 +func TestListTokens(t *testing.T) { + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestRSAKey" + keyType := "RSA" + keySize := 2048 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) + test.InitializeToken(t) // creates a token slot + + tokens, err := test.TokenHandler.ListTokens() + require.NoError(t, err, "Failed to list tokens") + + require.NotEmpty(t, tokens, "Token list should not be empty") + + token := tokens[0] + assert.NotEmpty(t, token.Label, "Token label should not be empty") + assert.NotEmpty(t, token.Manufacturer, "Token manufacturer should not be empty") + assert.NotEmpty(t, token.Model, "Token model should not be empty") + assert.NotEmpty(t, token.SerialNumber, "Token serial number should not be empty") +} + // TestAddRSAKey tests adding an RSA key to a PKCS#11 token func TestAddRSAKey(t *testing.T) { slotId := "0x0" @@ -94,7 +118,7 @@ func TestAddRSAKey(t *testing.T) { test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) - test.Setup(t) + test.InitializeToken(t) test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) @@ -114,13 +138,42 @@ func TestAddECDSAKey(t *testing.T) { test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) - test.Setup(t) + test.InitializeToken(t) test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) test.DeleteKeyFromToken(t) } +// TestListObjects tests listing available objects in a specific PKCS#11 token +func TestListObjects(t *testing.T) { + slotId := "0x0" + modulePath := "/usr/lib/softhsm/libsofthsm2.so" + label := "MyToken" + soPin := "123456" + userPin := "234567" + objectLabel := "TestRSAKey2" + keyType := "RSA" + keySize := 2048 + + test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) + test.InitializeToken(t) + + test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) + + objects, err := test.TokenHandler.ListObjects(label) + require.NoError(t, err, "Failed to list objects") + + require.NotEmpty(t, objects, "Object list should not be empty") + + object := objects[0] + assert.NotEmpty(t, object.Label, "Object label should not be empty") + assert.NotEmpty(t, object.Type, "Object type should not be empty") + assert.NotEmpty(t, object.Usage, "Object usage should not be empty") + + test.DeleteKeyFromToken(t) +} + // TestEncryptDecrypt tests the encryption ad decryption functionality of the PKCS#11 token func TestEncryptDecrypt(t *testing.T) { slotId := "0x0" @@ -133,7 +186,7 @@ func TestEncryptDecrypt(t *testing.T) { keySize := 2048 test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) - test.Setup(t) + test.InitializeToken(t) test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) @@ -189,7 +242,7 @@ func TestSignAndVerify(t *testing.T) { keySize := 2048 test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) - test.Setup(t) + test.InitializeToken(t) test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) @@ -229,7 +282,7 @@ func TestSignAndVerify(t *testing.T) { // func TestSignAndVerifyECDSA(t *testing.T) { // // Prepare the test PKCS#11Token for ECDSA // test := NewPKCS11Test(t, "0x1", "/usr/lib/softhsm/libsofthsm2.so", "MyToken2", "123456", "234567", "TestECDSAKey", "ECDSA", 256) -// test.Setup(t) +// test.InitializeToken(t) // // Add an ECDSA key to the token // test.AddKeyToToken(t, label, objectLabel, keyType, uint(keySize)) From a0a01a9089768a6f9e842bed2fea885213a11a3f Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 18:31:07 +0100 Subject: [PATCH 09/26] remove obsolete method --- .../infrastructure/cryptography/pkcs11.go | 23 ------------------- .../cryptography/pkcs11_test.go | 5 ---- 2 files changed, 28 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 92da430..1869507 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -30,8 +30,6 @@ type IPKCS11TokenHandler interface { ListTokens() ([]Token, error) // ListObjects lists all objects (e.g. keys) in a specific token based on the token label ListObjects(tokenLabel string) ([]TokenObject, error) - // ObjectExists checks if the specified object exists on the given token - ObjectExists(label, objectLabel string) (bool, error) // InitializeToken initializes the token with the provided label and pins InitializeToken(label string) error // AddKey adds the selected key (ECDSA or RSA) to the token @@ -197,27 +195,6 @@ func (token *PKCS11TokenHandler) isTokenSet(label string) (bool, error) { return false, nil } -// ObjectExists checks if the specified object exists on the given token -func (token *PKCS11TokenHandler) ObjectExists(label, objectLabel string) (bool, error) { - if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { - return false, err - } - - args := []string{"-O", "--module", token.Settings.ModulePath, "--token-label", label, "--pin", token.Settings.UserPin} - output, err := token.executePKCS11ToolCommand(args) - if err != nil { - return false, err - } - - if strings.Contains(output, objectLabel) { - fmt.Printf("Object with label '%s' exists.\n", objectLabel) - return true, nil - } - - fmt.Printf("Error: Object with label '%s' does not exist.\n", objectLabel) - return false, nil -} - // InitializeToken initializes the token with the provided label and pins func (token *PKCS11TokenHandler) InitializeToken(label string) error { if err := utils.CheckNonEmptyStrings(label); err != nil { diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 500fdcf..892d820 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -1,7 +1,6 @@ package cryptography import ( - "fmt" "os" "testing" @@ -73,10 +72,6 @@ func (p *PKCS11Test) DeleteKeyFromToken(t *testing.T) { func (p *PKCS11Test) AddKeyToToken(t *testing.T, label, objectLabel, keyType string, keySize uint) { err := p.TokenHandler.AddKey(label, objectLabel, keyType, keySize) assert.NoError(t, err, "Failed to add key to the token") - - isObjectSet, err := p.TokenHandler.ObjectExists(p.Label, p.ObjectLabel) - assert.NoError(t, err, "Error checking if key is set") - assert.True(t, isObjectSet, fmt.Sprintf("The %s key should be added to the token", p.KeyType)) } // TestListTokens tests listing available tokens using PKCS#11 From 9ca6f63fed8dcd31746776ffee5ce9e6da75b6ea Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 19:32:25 +0100 Subject: [PATCH 10/26] rename input parameters in Sign(...) method --- internal/infrastructure/cryptography/pkcs11.go | 14 +++++++------- 1 file changed, 7 insertions(+), 7 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 1869507..f68e6b2 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -39,7 +39,7 @@ type IPKCS11TokenHandler interface { // Decrypt decrypts data using the cryptographic capabilities of the PKCS#11 token Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error // Sign signs data using the cryptographic capabilities of the PKCS#11 token - Sign(label, objectLabel, inputFilePath, outputFilePath, keyType string) error + Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType string) error // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) // DeleteObject deletes a key or object from the token @@ -380,12 +380,12 @@ func (token *PKCS11TokenHandler) Decrypt(label, objectLabel, inputFilePath, outp } // Sign signs data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { - if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { +func (token *PKCS11TokenHandler) Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType string) error { + if err := utils.CheckNonEmptyStrings(label, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { return err } - if err := utils.CheckFilesExist(inputFilePath); err != nil { + if err := utils.CheckFilesExist(dataFilePath); err != nil { return err } @@ -404,7 +404,7 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputF "pkcs11:token="+label+";object="+objectLabel+";type=private;pin-value="+token.Settings.UserPin, "-sigopt", signatureFormat, "-sha384", // Use SHA-384 - "-out", outputFilePath, inputFilePath, + "-out", signatureFilePath, dataFilePath, ) } else if keyType == "ECDSA" { // Command for signing with ECDSA @@ -412,7 +412,7 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputF "openssl", "dgst", "-engine", "pkcs11", "-keyform", "engine", "-sign", "pkcs11:token="+label+";object="+objectLabel+";type=private;pin-value="+token.Settings.UserPin, "-sha384", // ECDSA typically uses SHA-384 - "-out", outputFilePath, inputFilePath, + "-out", signatureFilePath, dataFilePath, ) } @@ -422,7 +422,7 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, inputFilePath, outputF return fmt.Errorf("failed to sign data: %v\nOutput: %s", err, signOutput) } - fmt.Printf("Signing successful. Signature written to %s\n", outputFilePath) + fmt.Printf("Signing successful. Signature written to %s\n", signatureFilePath) return nil } From 6118a153974c8cc4250f751e8da94c0d7442a3ac Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 19:49:13 +0100 Subject: [PATCH 11/26] remove obsolete io helper functions --- .../internal/commands/aes-commands.go | 11 +++-- .../internal/commands/ecdsa-commands.go | 5 +-- .../internal/commands/rsa-commands.go | 10 ++--- internal/infrastructure/utils/io.go | 12 ----- test/unit/infrastructure/utils/io_test.go | 45 ------------------- 5 files changed, 12 insertions(+), 71 deletions(-) delete mode 100644 internal/infrastructure/utils/io.go delete mode 100644 test/unit/infrastructure/utils/io_test.go diff --git a/cmd/crypto-vault-cli/internal/commands/aes-commands.go b/cmd/crypto-vault-cli/internal/commands/aes-commands.go index 3d7af43..ec41d5e 100644 --- a/cmd/crypto-vault-cli/internal/commands/aes-commands.go +++ b/cmd/crypto-vault-cli/internal/commands/aes-commands.go @@ -2,7 +2,6 @@ package commands import ( "crypto_vault_service/internal/infrastructure/cryptography" - "crypto_vault_service/internal/infrastructure/utils" "fmt" "log" "os" @@ -33,7 +32,7 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { } // Encrypt the file - plainText, err := utils.ReadFile(inputFile) + plainText, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } @@ -44,7 +43,7 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { } // Save encrypted file - err = utils.WriteFile(outputFile, encryptedData) + err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { log.Fatalf("Error writing encrypted file: %v\n", err) } @@ -55,7 +54,7 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { // Save the AES key with the UUID prefix in the specified key directory keyFilePath := filepath.Join(keyDir, fmt.Sprintf("%s-symmetric_key.bin", uniqueID)) - err = utils.WriteFile(keyFilePath, key) + err = os.WriteFile(keyFilePath, key, 0644) if err != nil { log.Fatalf("Error writing AES key to file: %v\n", err) } @@ -80,7 +79,7 @@ func DecryptAESCmd(cmd *cobra.Command, args []string) { } // Decrypt the file - encryptedData, err := utils.ReadFile(inputFile) + encryptedData, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading encrypted file: %v\n", err) } @@ -93,7 +92,7 @@ func DecryptAESCmd(cmd *cobra.Command, args []string) { } // Save decrypted file - err = utils.WriteFile(outputFile, decryptedData) + err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { log.Fatalf("Error writing decrypted file: %v\n", err) } diff --git a/cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go b/cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go index 9199d6f..021e1a1 100644 --- a/cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go +++ b/cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go @@ -4,7 +4,6 @@ import ( "crypto/ecdsa" "crypto/elliptic" "crypto_vault_service/internal/infrastructure/cryptography" - "crypto_vault_service/internal/infrastructure/utils" "encoding/hex" "fmt" "log" @@ -38,7 +37,7 @@ func SignECCCmd(cmd *cobra.Command, args []string) { } // Read the file content - fileContent, err := utils.ReadFile(inputFile) + fileContent, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } @@ -106,7 +105,7 @@ func VerifyECCCmd(cmd *cobra.Command, args []string) { } // Read the file content (optional: you can also hash the content before verifying) - fileContent, err := utils.ReadFile(inputFile) + fileContent, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } diff --git a/cmd/crypto-vault-cli/internal/commands/rsa-commands.go b/cmd/crypto-vault-cli/internal/commands/rsa-commands.go index a0fe599..d529d82 100644 --- a/cmd/crypto-vault-cli/internal/commands/rsa-commands.go +++ b/cmd/crypto-vault-cli/internal/commands/rsa-commands.go @@ -3,9 +3,9 @@ package commands import ( "crypto/rsa" "crypto_vault_service/internal/infrastructure/cryptography" - "crypto_vault_service/internal/infrastructure/utils" "fmt" "log" + "os" "github.com/google/uuid" "github.com/spf13/cobra" @@ -52,7 +52,7 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { fmt.Println("Public key path:", publicKeyFilePath) // Encrypt the file - plainText, err := utils.ReadFile(inputFile) + plainText, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } @@ -63,7 +63,7 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { } // Save encrypted file - err = utils.WriteFile(outputFile, encryptedData) + err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { log.Fatalf("Error writing encrypted file: %v\n", err) } @@ -102,7 +102,7 @@ func DecryptRSACmd(cmd *cobra.Command, args []string) { } // Decrypt the file - encryptedData, err := utils.ReadFile(inputFile) + encryptedData, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading encrypted file: %v\n", err) } @@ -113,7 +113,7 @@ func DecryptRSACmd(cmd *cobra.Command, args []string) { } // Save decrypted file - err = utils.WriteFile(outputFile, decryptedData) + err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { log.Fatalf("Error writing decrypted file: %v\n", err) } diff --git a/internal/infrastructure/utils/io.go b/internal/infrastructure/utils/io.go deleted file mode 100644 index 903f7eb..0000000 --- a/internal/infrastructure/utils/io.go +++ /dev/null @@ -1,12 +0,0 @@ -package utils - -import "os" - -// File Operations -func ReadFile(filePath string) ([]byte, error) { - return os.ReadFile(filePath) -} - -func WriteFile(filePath string, data []byte) error { - return os.WriteFile(filePath, data, 0644) -} diff --git a/test/unit/infrastructure/utils/io_test.go b/test/unit/infrastructure/utils/io_test.go deleted file mode 100644 index 69152e9..0000000 --- a/test/unit/infrastructure/utils/io_test.go +++ /dev/null @@ -1,45 +0,0 @@ -package utils - -import ( - "os" - "testing" - - "crypto_vault_service/internal/infrastructure/utils" - - "github.com/stretchr/testify/assert" -) - -// TestWriteFileAndReadFile tests the WriteFile and ReadFile functions -func TestWriteFileAndReadFile(t *testing.T) { - // Prepare a test file path - testFilePath := "testfile.txt" - testData := []byte("This is a test message.") - - // Write data to the file - err := utils.WriteFile(testFilePath, testData) - assert.NoError(t, err, "Error writing to file") - - // Read data from the file - readData, err := utils.ReadFile(testFilePath) - assert.NoError(t, err, "Error reading from file") - - // Ensure that the written and read data are the same - assert.Equal(t, testData, readData, "The data read from the file should match the data written") - - // Clean up the test file - err = os.Remove(testFilePath) - assert.NoError(t, err, "Error cleaning up the test file") -} - -// TestReadFileWithNonExistentFile tests the ReadFile function with a non-existent file -func TestReadFileWithNonExistentFile(t *testing.T) { - // Use a file path that doesn't exist - testFilePath := "non_existent_file.txt" - - // Attempt to read from the non-existent file - data, err := utils.ReadFile(testFilePath) - - // We expect an error because the file does not exist - assert.Error(t, err, "Reading from a non-existent file should return an error") - assert.Nil(t, data, "No data should be returned for a non-existent file") -} From c2ca6b32aedfbe6a5262c1868d6dfcdaf4935e2b Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 20:09:27 +0100 Subject: [PATCH 12/26] utilize settings object pointer --- internal/infrastructure/cryptography/pkcs11.go | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index f68e6b2..22460b7 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -48,7 +48,7 @@ type IPKCS11TokenHandler interface { // PKCS11TokenHandler represents the parameters and operations for interacting with a PKCS#11 token type PKCS11TokenHandler struct { - Settings settings.PKCS11Settings + Settings *settings.PKCS11Settings } // NewPKCS11TokenHandler creates and returns a new instance of PKCS11TokenHandler @@ -58,7 +58,7 @@ func NewPKCS11TokenHandler(settings settings.PKCS11Settings) (*PKCS11TokenHandle } return &PKCS11TokenHandler{ - Settings: settings, + Settings: &settings, }, nil } From 90a3ddefdfd6186dd7af9057a7df448c22d5a491 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 20:16:58 +0100 Subject: [PATCH 13/26] order input parameters in Verify(...) method --- internal/infrastructure/cryptography/pkcs11.go | 4 ++-- test/integration/infrastructure/cryptography/pkcs11_test.go | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 22460b7..1564632 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -41,7 +41,7 @@ type IPKCS11TokenHandler interface { // Sign signs data using the cryptographic capabilities of the PKCS#11 token Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType string) error // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token - Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) + Verify(label, objectLabel, dataFilePath, signatureFilePath, keyType string) (bool, error) // DeleteObject deletes a key or object from the token DeleteObject(label, objectType, objectLabel string) error } @@ -427,7 +427,7 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, dataFilePath, signatur } // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath string) (bool, error) { +func (token *PKCS11TokenHandler) Verify(label, objectLabel, dataFilePath, signatureFilePath, keyType string) (bool, error) { valid := false if err := utils.CheckNonEmptyStrings(label, objectLabel, keyType, dataFilePath, signatureFilePath); err != nil { diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 892d820..5a751c5 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -257,7 +257,7 @@ func TestSignAndVerify(t *testing.T) { assert.NotEmpty(t, signatureData, "Signature data should not be empty") - valid, err := test.TokenHandler.Verify(label, objectLabel, keyType, dataFilePath, signatureFilePath) + valid, err := test.TokenHandler.Verify(label, objectLabel, dataFilePath, signatureFilePath, keyType) assert.NoError(t, err, "Failed to verify the signature using the PKCS#11 token") assert.True(t, valid, "The signature should be valid") From dd9472812a2bd5e4b9a45cff943b3bce1f70bd0b Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 20:30:07 +0100 Subject: [PATCH 14/26] fix cli tool --- cmd/crypto-vault-cli/.gitignore | 1 + cmd/crypto-vault-cli/README.md | 42 +- .../internal/commands/pkcs11-commands.go | 403 +++++++++--------- 3 files changed, 225 insertions(+), 221 deletions(-) create mode 100644 cmd/crypto-vault-cli/.gitignore diff --git a/cmd/crypto-vault-cli/.gitignore b/cmd/crypto-vault-cli/.gitignore new file mode 100644 index 0000000..ebfba80 --- /dev/null +++ b/cmd/crypto-vault-cli/.gitignore @@ -0,0 +1 @@ +pkcs11-settings.json \ No newline at end of file diff --git a/cmd/crypto-vault-cli/README.md b/cmd/crypto-vault-cli/README.md index 4c6c387..e04632b 100644 --- a/cmd/crypto-vault-cli/README.md +++ b/cmd/crypto-vault-cli/README.md @@ -54,10 +54,10 @@ go run crypto-vault-cli.go decrypt-rsa --input data/${uuid}-encrypted.txt --outp ```sh # RSA-PKCS # Encryption -go run crypto-vault-cli.go encrypt --module /usr/lib/softhsm/libsofthsm2.so --token-label my-token --object-label my-rsa-key --user-pin 5678 --key-type RSA --input-file data/input.txt --output-file data/encrypted-output.enc +go run crypto-vault-cli.go encrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/input.txt --output-file data/encrypted-output.enc # Decryption -go run crypto-vault-cli.go decrypt --module /usr/lib/softhsm/libsofthsm2.so --token-label my-token --object-label my-rsa-key --user-pin 5678 --key-type RSA --input-file data/encrypted-output.enc --output-file data/decrypted-output.txt +go run crypto-vault-cli.go decrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/encrypted-output.enc --output-file data/decrypted-output.txt ``` --- @@ -83,17 +83,17 @@ go run crypto-vault-cli.go verify-ecc --input data/input.txt --publicKey Date: Sat, 14 Dec 2024 20:57:51 +0100 Subject: [PATCH 15/26] rename struct --- .../internal/commands/pkcs11-commands.go | 16 ++++---- .../infrastructure/cryptography/pkcs11.go | 40 +++++++++---------- .../cryptography/pkcs11_test.go | 4 +- 3 files changed, 30 insertions(+), 30 deletions(-) diff --git a/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go b/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go index 12fd85b..7be4570 100644 --- a/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go +++ b/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go @@ -24,7 +24,7 @@ func GetFlagString(cmd *cobra.Command, flagName, errMessage string) string { return value } -func (h *PKCS11CommandsHandler) PKCS11SettingsSet(tokenHandler *cryptography.PKCS11TokenHandler) { +func (h *PKCS11CommandsHandler) PKCS11SettingsSet(tokenHandler *cryptography.PKCS11Handler) { if err := utils.CheckNonEmptyStrings( tokenHandler.Settings.ModulePath, tokenHandler.Settings.SOPin, @@ -93,7 +93,7 @@ func (h *PKCS11CommandsHandler) storePKCS11SettingsCmd(cmd *cobra.Command, args func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -109,7 +109,7 @@ func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []st func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -133,7 +133,7 @@ func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -152,7 +152,7 @@ func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []strin func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -173,7 +173,7 @@ func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -194,7 +194,7 @@ func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } @@ -215,7 +215,7 @@ func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) VerifyCmd(cmd *cobra.Command, args []string) { pkcs11Settings := h.readPkcs11ConfigFile() - tokenHandler := &cryptography.PKCS11TokenHandler{ + tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 1564632..ca9e1bb 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -24,8 +24,8 @@ type TokenObject struct { Access string // Access controls for the object (e.g. sensitive, always sensitive) } -// IPKCS11TokenHandler defines the operations for working with a PKCS#11 token -type IPKCS11TokenHandler interface { +// IPKCS11Handler defines the operations for working with a PKCS#11 token +type IPKCS11Handler interface { // ListTokens lists all available tokens in the available slots ListTokens() ([]Token, error) // ListObjects lists all objects (e.g. keys) in a specific token based on the token label @@ -46,24 +46,24 @@ type IPKCS11TokenHandler interface { DeleteObject(label, objectType, objectLabel string) error } -// PKCS11TokenHandler represents the parameters and operations for interacting with a PKCS#11 token -type PKCS11TokenHandler struct { +// PKCS11Handler represents the parameters and operations for interacting with a PKCS#11 token +type PKCS11Handler struct { Settings *settings.PKCS11Settings } -// NewPKCS11TokenHandler creates and returns a new instance of PKCS11TokenHandler -func NewPKCS11TokenHandler(settings settings.PKCS11Settings) (*PKCS11TokenHandler, error) { +// NewPKCS11Handler creates and returns a new instance of PKCS11Handler +func NewPKCS11Handler(settings settings.PKCS11Settings) (*PKCS11Handler, error) { if err := settings.Validate(); err != nil { return nil, err } - return &PKCS11TokenHandler{ + return &PKCS11Handler{ Settings: &settings, }, nil } // Private method to execute pkcs11-tool commands and return output -func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string, error) { +func (token *PKCS11Handler) executePKCS11ToolCommand(args []string) (string, error) { cmd := exec.Command("pkcs11-tool", args...) output, err := cmd.CombinedOutput() if err != nil { @@ -73,7 +73,7 @@ func (token *PKCS11TokenHandler) executePKCS11ToolCommand(args []string) (string } // ListTokens lists all available tokens in the available slots -func (token *PKCS11TokenHandler) ListTokens() ([]Token, error) { +func (token *PKCS11Handler) ListTokens() ([]Token, error) { if err := utils.CheckNonEmptyStrings(token.Settings.ModulePath); err != nil { return nil, err } @@ -122,7 +122,7 @@ func (token *PKCS11TokenHandler) ListTokens() ([]Token, error) { } // ListObjects lists all objects (e.g. keys) in a specific token based on the token label. -func (token *PKCS11TokenHandler) ListObjects(tokenLabel string) ([]TokenObject, error) { +func (token *PKCS11Handler) ListObjects(tokenLabel string) ([]TokenObject, error) { // if err := utils.CheckNonEmptyStrings(tokenLabel, token.Settings.ModulePath); err != nil { return nil, err @@ -175,7 +175,7 @@ func (token *PKCS11TokenHandler) ListObjects(tokenLabel string) ([]TokenObject, } // isTokenSet checks if the token exists in the given module path -func (token *PKCS11TokenHandler) isTokenSet(label string) (bool, error) { +func (token *PKCS11Handler) isTokenSet(label string) (bool, error) { if err := utils.CheckNonEmptyStrings(label); err != nil { return false, err } @@ -196,7 +196,7 @@ func (token *PKCS11TokenHandler) isTokenSet(label string) (bool, error) { } // InitializeToken initializes the token with the provided label and pins -func (token *PKCS11TokenHandler) InitializeToken(label string) error { +func (token *PKCS11Handler) InitializeToken(label string) error { if err := utils.CheckNonEmptyStrings(label); err != nil { return err } @@ -222,7 +222,7 @@ func (token *PKCS11TokenHandler) InitializeToken(label string) error { } // AddKey adds the selected key (ECDSA or RSA) to the token -func (token *PKCS11TokenHandler) AddKey(label, objectLabel, keyType string, keySize uint) error { +func (token *PKCS11Handler) AddKey(label, objectLabel, keyType string, keySize uint) error { if err := utils.CheckNonEmptyStrings(label, objectLabel, keyType); err != nil { return err } @@ -237,7 +237,7 @@ func (token *PKCS11TokenHandler) AddKey(label, objectLabel, keyType string, keyS } // addECDSASignKey adds an ECDSA signing key to the token -func (token *PKCS11TokenHandler) addECDSASignKey(label, objectLabel string, keySize uint) error { +func (token *PKCS11Handler) addECDSASignKey(label, objectLabel string, keySize uint) error { if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { return err } @@ -275,7 +275,7 @@ func (token *PKCS11TokenHandler) addECDSASignKey(label, objectLabel string, keyS } // addRSASignKey adds an RSA signing key to the token -func (token *PKCS11TokenHandler) addRSASignKey(label, objectLabel string, keySize uint) error { +func (token *PKCS11Handler) addRSASignKey(label, objectLabel string, keySize uint) error { if err := utils.CheckNonEmptyStrings(label, objectLabel); err != nil { return err } @@ -314,7 +314,7 @@ func (token *PKCS11TokenHandler) addRSASignKey(label, objectLabel string, keySiz } // Encrypt encrypts data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pkcs -func (token *PKCS11TokenHandler) Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { +func (token *PKCS11Handler) Encrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { return err } @@ -347,7 +347,7 @@ func (token *PKCS11TokenHandler) Encrypt(label, objectLabel, inputFilePath, outp } // Decrypt decrypts data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pkcs -func (token *PKCS11TokenHandler) Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { +func (token *PKCS11Handler) Decrypt(label, objectLabel, inputFilePath, outputFilePath, keyType string) error { if err := utils.CheckNonEmptyStrings(label, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { return err } @@ -380,7 +380,7 @@ func (token *PKCS11TokenHandler) Decrypt(label, objectLabel, inputFilePath, outp } // Sign signs data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType string) error { +func (token *PKCS11Handler) Sign(label, objectLabel, dataFilePath, signatureFilePath, keyType string) error { if err := utils.CheckNonEmptyStrings(label, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { return err } @@ -427,7 +427,7 @@ func (token *PKCS11TokenHandler) Sign(label, objectLabel, dataFilePath, signatur } // Verify verifies the signature of data using the cryptographic capabilities of the PKCS#11 token. Refer to: https://docs.yubico.com/hardware/yubihsm-2/hsm-2-user-guide/hsm2-openssl-libp11.html#rsa-pss -func (token *PKCS11TokenHandler) Verify(label, objectLabel, dataFilePath, signatureFilePath, keyType string) (bool, error) { +func (token *PKCS11Handler) Verify(label, objectLabel, dataFilePath, signatureFilePath, keyType string) (bool, error) { valid := false if err := utils.CheckNonEmptyStrings(label, objectLabel, keyType, dataFilePath, signatureFilePath); err != nil { @@ -481,7 +481,7 @@ func (token *PKCS11TokenHandler) Verify(label, objectLabel, dataFilePath, signat } // DeleteObject deletes a key or object from the token -func (token *PKCS11TokenHandler) DeleteObject(label, objectType, objectLabel string) error { +func (token *PKCS11Handler) DeleteObject(label, objectType, objectLabel string) error { if err := utils.CheckNonEmptyStrings(label, objectType, objectLabel); err != nil { return err } diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 5a751c5..1d9bd6d 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -16,7 +16,7 @@ type PKCS11Test struct { ObjectLabel string KeyType string KeySize uint - TokenHandler *cryptography.PKCS11TokenHandler + TokenHandler *cryptography.PKCS11Handler } // NewPKCS11Test sets up the test environment for PKCS#11 integration tests @@ -28,7 +28,7 @@ func NewPKCS11Test(t *testing.T, slotId, modulePath, label, soPin, userPin, obje SlotId: slotId, } - tokenHandler, err := cryptography.NewPKCS11TokenHandler(settings) + tokenHandler, err := cryptography.NewPKCS11Handler(settings) if err != nil { t.Logf("%v\n", err) } From 0b8a61736d59c2f1e1147e794bc68cedf2a78fd3 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 21:10:06 +0100 Subject: [PATCH 16/26] consider slot id member attribute when listing tokens --- .../infrastructure/cryptography/pkcs11.go | 19 +++++++++++++++++-- .../cryptography/pkcs11_test.go | 1 + 2 files changed, 18 insertions(+), 2 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index ca9e1bb..82c5f3b 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -5,11 +5,13 @@ import ( "crypto_vault_service/internal/infrastructure/utils" "fmt" "os/exec" + "regexp" "strings" ) // Token represents a PKCS#11 token with its label and other metadata. type Token struct { + SlotId string Label string Manufacturer string Model string @@ -93,12 +95,25 @@ func (token *PKCS11Handler) ListTokens() ([]Token, error) { for _, line := range lines { - if strings.Contains(line, "token label") { + if strings.Contains(line, "Slot") { if currentToken != nil { tokens = append(tokens, *currentToken) } - currentToken = &Token{} + currentToken = &Token{ + SlotId: "", + Label: "", + Manufacturer: "", + Model: "", + SerialNumber: "", + } + + re := regexp.MustCompile(`\((0x[0-9a-fA-F]+)\)`) // e.g. `(0x39e9d82d)` in `Slot 1 (0x39e9d82d): SoftHSM slot ID 0x39e9d82d` + matches := re.FindStringSubmatch(line) + currentToken.SlotId = matches[1] + } + + if strings.Contains(line, "token label") { currentToken.Label = strings.TrimSpace(strings.Split(line, ":")[1]) } if currentToken != nil { diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 1d9bd6d..69953bb 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -94,6 +94,7 @@ func TestListTokens(t *testing.T) { require.NotEmpty(t, tokens, "Token list should not be empty") token := tokens[0] + assert.NotEmpty(t, token.SlotId, "Token serial number should not be empty") assert.NotEmpty(t, token.Label, "Token label should not be empty") assert.NotEmpty(t, token.Manufacturer, "Token manufacturer should not be empty") assert.NotEmpty(t, token.Model, "Token model should not be empty") From ebead2f2e73145fe9eef0899e0a743189a815662 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 22:46:19 +0100 Subject: [PATCH 17/26] rename method --- internal/infrastructure/cryptography/pkcs11.go | 8 ++++---- .../infrastructure/cryptography/pkcs11_test.go | 2 +- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/internal/infrastructure/cryptography/pkcs11.go b/internal/infrastructure/cryptography/pkcs11.go index 82c5f3b..9ab3540 100644 --- a/internal/infrastructure/cryptography/pkcs11.go +++ b/internal/infrastructure/cryptography/pkcs11.go @@ -28,8 +28,8 @@ type TokenObject struct { // IPKCS11Handler defines the operations for working with a PKCS#11 token type IPKCS11Handler interface { - // ListTokens lists all available tokens in the available slots - ListTokens() ([]Token, error) + // ListTokenSlots lists all available tokens in the available slots + ListTokenSlots() ([]Token, error) // ListObjects lists all objects (e.g. keys) in a specific token based on the token label ListObjects(tokenLabel string) ([]TokenObject, error) // InitializeToken initializes the token with the provided label and pins @@ -74,8 +74,8 @@ func (token *PKCS11Handler) executePKCS11ToolCommand(args []string) (string, err return string(output), nil } -// ListTokens lists all available tokens in the available slots -func (token *PKCS11Handler) ListTokens() ([]Token, error) { +// ListTokenSlots lists all available tokens in the available slots +func (token *PKCS11Handler) ListTokenSlots() ([]Token, error) { if err := utils.CheckNonEmptyStrings(token.Settings.ModulePath); err != nil { return nil, err } diff --git a/test/integration/infrastructure/cryptography/pkcs11_test.go b/test/integration/infrastructure/cryptography/pkcs11_test.go index 69953bb..82f8d62 100644 --- a/test/integration/infrastructure/cryptography/pkcs11_test.go +++ b/test/integration/infrastructure/cryptography/pkcs11_test.go @@ -88,7 +88,7 @@ func TestListTokens(t *testing.T) { test := NewPKCS11Test(t, slotId, modulePath, label, soPin, userPin, objectLabel, keyType, uint(keySize)) test.InitializeToken(t) // creates a token slot - tokens, err := test.TokenHandler.ListTokens() + tokens, err := test.TokenHandler.ListTokenSlots() require.NoError(t, err, "Failed to list tokens") require.NotEmpty(t, tokens, "Token list should not be empty") From 323eab67601378f09f78b2843bc8b0e0ed6f6def Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 22:46:47 +0100 Subject: [PATCH 18/26] add list commands; update error handling --- cmd/crypto-vault-cli/README.md | 9 +- .../internal/commands/pkcs11-commands.go | 374 ++++++++++++++---- 2 files changed, 292 insertions(+), 91 deletions(-) diff --git a/cmd/crypto-vault-cli/README.md b/cmd/crypto-vault-cli/README.md index e04632b..de404a9 100644 --- a/cmd/crypto-vault-cli/README.md +++ b/cmd/crypto-vault-cli/README.md @@ -104,8 +104,9 @@ go run crypto-vault-cli.go verify --token-label my-token --object-label my-ecdsa # Configure settings go run crypto-vault-cli.go store-pkcs11-settings --module /usr/lib/softhsm/libsofthsm2.so --so-pin 1234 --user-pin 5678 --slot-id "0x0" -# # Check available slots -# pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -L +# List token slots +go run crypto-vault-cli.go list-slots + # Initialize a PKCS#11 token go run crypto-vault-cli.go initialize-token --token-label my-token @@ -115,8 +116,8 @@ go run crypto-vault-cli.go initialize-token --token-label my-token go run crypto-vault-cli.go add-key --token-label my-token --object-label my-rsa-key --key-type RSA --key-size 2048 go run crypto-vault-cli.go add-key --token-label my-token --object-label my-ecdsa-key --key-type ECDSA --key-size 256 -# # Check all keys of a token -# pkcs11-tool --module /usr/lib/softhsm/libsofthsm2.so -O --token-label "my-token" --pin 5678 +# List token objects +go run crypto-vault-cli.go list-objects --token-label "my-token" # Deleting keys from tokens # Delete an object (e.g., RSA or EC key) from the PKCS#11 token diff --git a/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go b/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go index 7be4570..5a7f2f3 100644 --- a/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go +++ b/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go @@ -5,6 +5,7 @@ import ( "crypto_vault_service/internal/infrastructure/settings" "crypto_vault_service/internal/infrastructure/utils" "encoding/json" + "fmt" "os" "log" @@ -16,42 +17,44 @@ import ( type PKCS11CommandsHandler struct{} // GetFlagString retrieves a flag value and logs an error if it is missing or invalid -func GetFlagString(cmd *cobra.Command, flagName, errMessage string) string { +func GetFlagString(cmd *cobra.Command, flagName, errMessage string) (string, error) { value, err := cmd.Flags().GetString(flagName) if err != nil || value == "" { - log.Panicf("%s: %v", errMessage, err) + return "", fmt.Errorf("%s: %v", errMessage, err) } - return value + return value, nil } -func (h *PKCS11CommandsHandler) PKCS11SettingsSet(tokenHandler *cryptography.PKCS11Handler) { +// validatePKCS11Settings checks if the PKCS#11 settings (ModulePath, SOPin, UserPin, and SlotId) +func (h *PKCS11CommandsHandler) validatePKCS11Settings(tokenHandler *cryptography.PKCS11Handler) error { if err := utils.CheckNonEmptyStrings( tokenHandler.Settings.ModulePath, tokenHandler.Settings.SOPin, tokenHandler.Settings.UserPin, tokenHandler.Settings.SlotId); err != nil { - log.Panicf("Ensure PKCS#11 settings have been configured trough `configure-pkcs11-settings` command: %v", err) + return fmt.Errorf("ensure PKCS#11 settings have been configured trough `configure-pkcs11-settings` command: %v", err) } + return nil } // readPkcs11ConfigFile reads the pkcs11-settings.json file and create the settings object -func (h *PKCS11CommandsHandler) readPkcs11ConfigFile() *settings.PKCS11Settings { +func (h *PKCS11CommandsHandler) readPkcs11ConfigFile() (*settings.PKCS11Settings, error) { plainText, err := os.ReadFile("pkcs11-settings.json") if err != nil { - log.Panicf("error reading JSON file: %v", err) + return nil, fmt.Errorf("error reading JSON file: %s", err) } var settings settings.PKCS11Settings err = json.Unmarshal(plainText, &settings) if err != nil { - log.Panicf("error unmarshalling JSON into struct: %v", err) + return nil, fmt.Errorf("error unmarshalling JSON into struct: %s", err) } - return &settings + return &settings, nil } // writePkcs11ConfigFile writes the pkcs11-settings.json config file -func (h *PKCS11CommandsHandler) writePkcs11ConfigFile(modulePath, soPin, userPin, slotId string) { +func (h *PKCS11CommandsHandler) writePkcs11ConfigFile(modulePath, soPin, userPin, slotId string) error { settings := map[string]string{ "modulePath": modulePath, "soPin": soPin, @@ -61,44 +64,134 @@ func (h *PKCS11CommandsHandler) writePkcs11ConfigFile(modulePath, soPin, userPin settingsJSON, err := json.MarshalIndent(settings, "", " ") if err != nil { - log.Fatalf("Error marshalling settings to JSON: %v", err) - return + return fmt.Errorf("error marshalling settings to JSON: %v", err) } file, err := os.Create("pkcs11-settings.json") if err != nil { - log.Fatalf("Error creating JSON file: %v", err) - return + return fmt.Errorf("error creating JSON file: %v", err) } defer file.Close() _, err = file.Write(settingsJSON) if err != nil { - log.Fatalf("Error writing to JSON file: %v", err) - return + return fmt.Errorf("error writing to JSON file: %v", err) } + return nil } // storePKCS11SettingsCmd command saves the PKCS#11 settings to a JSON configuration file func (h *PKCS11CommandsHandler) storePKCS11SettingsCmd(cmd *cobra.Command, args []string) { - modulePath := GetFlagString(cmd, "module", "Error fetching module path flag") - soPin := GetFlagString(cmd, "so-pin", "Error fetching SO Pin flag") - userPin := GetFlagString(cmd, "user-pin", "Error fetching user pin flag") - slotId := GetFlagString(cmd, "slot-id", "Error fetching slot id flag") + modulePath, err := GetFlagString(cmd, "module", "Error fetching module path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + soPin, err := GetFlagString(cmd, "so-pin", "Error fetching SO Pin flag") + if err != nil { + log.Fatalf("%v", err) + return + } + userPin, err := GetFlagString(cmd, "user-pin", "Error fetching user pin flag") + if err != nil { + log.Fatalf("%v", err) + return + } + slotId, err := GetFlagString(cmd, "slot-id", "Error fetching slot id flag") + if err != nil { + log.Fatalf("%v", err) + return + } h.writePkcs11ConfigFile(modulePath, soPin, userPin, slotId) + fmt.Println("created pkcs11-settings.json") } -// InitializeTokenCmd initializes a PKCS#11 token -func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() +// getTokenHandler reads the PKCS#11 config file and validates the settings. +func (h *PKCS11CommandsHandler) getTokenHandler() (*cryptography.PKCS11Handler, error) { + + pkcs11Settings, err := h.readPkcs11ConfigFile() + if err != nil { + return nil, fmt.Errorf("error reading PKCS#11 config file: %v", err) + } tokenHandler := &cryptography.PKCS11Handler{ Settings: pkcs11Settings, } - h.PKCS11SettingsSet(tokenHandler) - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + err = h.validatePKCS11Settings(tokenHandler) + if err != nil { + return nil, fmt.Errorf("error validating PKCS#11 settings: %v", err) + } + + return tokenHandler, nil +} + +// ListTokenSlotsCmd lists PKCS#11 tokens +func (h *PKCS11CommandsHandler) ListTokenSlotsCmd(cmd *cobra.Command, args []string) { + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return + } + + tokens, err := tokenHandler.ListTokenSlots() + if err != nil { + log.Fatalf("Error initializing token: %v", err) + return + } + + tokensJSON, err := json.MarshalIndent(tokens, "", " ") + if err != nil { + log.Fatalf("Error marshalling tokens to JSON: %v", err) + return + } + + fmt.Println(string(tokensJSON)) +} + +// ListObjectsSlotsCmd lists PKCS#11 token objects +func (h *PKCS11CommandsHandler) ListObjectsSlotsCmd(cmd *cobra.Command, args []string) { + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return + } + + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + + objects, err := tokenHandler.ListObjects(tokenLabel) + if err != nil { + log.Fatalf("Error initializing token: %v", err) + return + } + + objectsJSON, err := json.MarshalIndent(objects, "", " ") + if err != nil { + log.Fatalf("Error marshalling objects to JSON: %v", err) + return + } + + fmt.Println(string(objectsJSON)) +} + +// InitializeTokenCmd initializes a PKCS#11 token +func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []string) { + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return + } + + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if err := tokenHandler.InitializeToken(tokenLabel); err != nil { log.Fatalf("Error initializing token: %v", err) @@ -107,17 +200,27 @@ func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []st // AddKeyCmd adds a key to the PKCS#11 token func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") - keyType := GetFlagString(cmd, "key-type", "Error fetching key-type path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + keyType, err := GetFlagString(cmd, "key-type", "Error fetching key-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } keySize, err := cmd.Flags().GetUint("key-size") if err != nil { @@ -131,17 +234,27 @@ func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { // DeleteObjectCmd deletes an object (key) from the PKCS#11 token func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectType := GetFlagString(cmd, "object-type", "Error fetching object-type path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectType, err := GetFlagString(cmd, "object-type", "Error fetching object-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if err := tokenHandler.DeleteObject(tokenLabel, objectType, objectLabel); err != nil { log.Fatalf("Error deleting object: %v", err) @@ -150,19 +263,37 @@ func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []strin // EncryptCmd encrypts data using the PKCS#11 token func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") - inputFilePath := GetFlagString(cmd, "input-file", "Error input-file path flag") - outputFilePath := GetFlagString(cmd, "output-file", "Error output-file path flag") - keyType := GetFlagString(cmd, "key-type", "Error key-type path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + inputFilePath, err := GetFlagString(cmd, "input-file", "Error input-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + outputFilePath, err := GetFlagString(cmd, "output-file", "Error output-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + keyType, err := GetFlagString(cmd, "key-type", "Error key-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if err := tokenHandler.Encrypt(tokenLabel, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { log.Fatalf("Error encrypting data: %v", err) @@ -171,19 +302,37 @@ func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { // DecryptCmd decrypts data using the PKCS#11 token func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") - inputFilePath := GetFlagString(cmd, "input-file", "Error input-file path flag") - outputFilePath := GetFlagString(cmd, "output-file", "Error output-file path flag") - keyType := GetFlagString(cmd, "key-type", "Error key-type path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + inputFilePath, err := GetFlagString(cmd, "input-file", "Error input-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + outputFilePath, err := GetFlagString(cmd, "output-file", "Error output-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + keyType, err := GetFlagString(cmd, "key-type", "Error key-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if err := tokenHandler.Decrypt(tokenLabel, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { log.Fatalf("Error decrypting data: %v", err) @@ -192,19 +341,37 @@ func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { // SignCmd signs data using the PKCS#11 token func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") - dataFilePath := GetFlagString(cmd, "data-file", "Error data-file path flag") - signatureFilePath := GetFlagString(cmd, "signature-file", "Error signature-file path flag") - keyType := GetFlagString(cmd, "key-type", "Error key-type path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + dataFilePath, err := GetFlagString(cmd, "data-file", "Error data-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + signatureFilePath, err := GetFlagString(cmd, "signature-file", "Error signature-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + keyType, err := GetFlagString(cmd, "key-type", "Error key-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if err := tokenHandler.Sign(tokenLabel, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { log.Fatalf("Error signing data: %v", err) @@ -213,19 +380,37 @@ func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { // VerifyCmd verifies the signature using the PKCS#11 token func (h *PKCS11CommandsHandler) VerifyCmd(cmd *cobra.Command, args []string) { - pkcs11Settings := h.readPkcs11ConfigFile() - - tokenHandler := &cryptography.PKCS11Handler{ - Settings: pkcs11Settings, + tokenHandler, err := h.getTokenHandler() + if err != nil { + log.Fatalf("%v", err) + return } - h.PKCS11SettingsSet(tokenHandler) - - tokenLabel := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") - objectLabel := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") - dataFilePath := GetFlagString(cmd, "data-file", "Error data-file path flag") - signatureFilePath := GetFlagString(cmd, "signature-file", "Error signature-file path flag") - keyType := GetFlagString(cmd, "key-type", "Error key-type path flag") + tokenLabel, err := GetFlagString(cmd, "token-label", "Error fetching token-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + objectLabel, err := GetFlagString(cmd, "object-label", "Error fetching object-label path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + dataFilePath, err := GetFlagString(cmd, "data-file", "Error data-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + signatureFilePath, err := GetFlagString(cmd, "signature-file", "Error signature-file path flag") + if err != nil { + log.Fatalf("%v", err) + return + } + keyType, err := GetFlagString(cmd, "key-type", "Error key-type path flag") + if err != nil { + log.Fatalf("%v", err) + return + } if _, err := tokenHandler.Verify(tokenLabel, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { log.Fatalf("Error verifying signature: %v", err) @@ -255,6 +440,21 @@ func InitPKCS11Commands(rootCmd *cobra.Command) { pkcs11InitializeTokenCmd.Flags().String("token-label", "", "Label of the PKCS#11 token") rootCmd.AddCommand(pkcs11InitializeTokenCmd) + var listTokenSlotsCmd = &cobra.Command{ + Use: "list-slots", + Short: "List PKCS#11 token slots", + Run: handler.ListTokenSlotsCmd, + } + rootCmd.AddCommand(listTokenSlotsCmd) + + var listObjectsSlotsCmd = &cobra.Command{ + Use: "list-objects", + Short: "List PKCS#11 token objects", + Run: handler.ListObjectsSlotsCmd, + } + listObjectsSlotsCmd.Flags().String("token-label", "", "Label of the PKCS#11 token") + rootCmd.AddCommand(listObjectsSlotsCmd) + var pkcs11AddKeyCmd = &cobra.Command{ Use: "add-key", Short: "Add key (ECDSA or RSA) to the PKCS#11 token", From 0f3682ccb25731858da7f15077139e462c57b99d Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 22:49:26 +0100 Subject: [PATCH 19/26] rename files --- .../internal/commands/{aes-commands.go => aes_commands.go} | 0 .../internal/commands/{ecdsa-commands.go => ecdsa_commands.go} | 0 .../internal/commands/{pkcs11-commands.go => pkcs11_commands.go} | 0 .../internal/commands/{rsa-commands.go => rsa_commands.go} | 0 4 files changed, 0 insertions(+), 0 deletions(-) rename cmd/crypto-vault-cli/internal/commands/{aes-commands.go => aes_commands.go} (100%) rename cmd/crypto-vault-cli/internal/commands/{ecdsa-commands.go => ecdsa_commands.go} (100%) rename cmd/crypto-vault-cli/internal/commands/{pkcs11-commands.go => pkcs11_commands.go} (100%) rename cmd/crypto-vault-cli/internal/commands/{rsa-commands.go => rsa_commands.go} (100%) diff --git a/cmd/crypto-vault-cli/internal/commands/aes-commands.go b/cmd/crypto-vault-cli/internal/commands/aes_commands.go similarity index 100% rename from cmd/crypto-vault-cli/internal/commands/aes-commands.go rename to cmd/crypto-vault-cli/internal/commands/aes_commands.go diff --git a/cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go b/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go similarity index 100% rename from cmd/crypto-vault-cli/internal/commands/ecdsa-commands.go rename to cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go diff --git a/cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go b/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go similarity index 100% rename from cmd/crypto-vault-cli/internal/commands/pkcs11-commands.go rename to cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go diff --git a/cmd/crypto-vault-cli/internal/commands/rsa-commands.go b/cmd/crypto-vault-cli/internal/commands/rsa_commands.go similarity index 100% rename from cmd/crypto-vault-cli/internal/commands/rsa-commands.go rename to cmd/crypto-vault-cli/internal/commands/rsa_commands.go From f8e3fbea62df0bffcf65771fad6ec30e63112fba Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 22:50:29 +0100 Subject: [PATCH 20/26] rename files --- cmd/crypto-vault-cli/{crypto-vault-cli.go => crypto_vault_cli.go} | 0 .../{crypto-vault-service.go => crypto_vault_service.go} | 0 2 files changed, 0 insertions(+), 0 deletions(-) rename cmd/crypto-vault-cli/{crypto-vault-cli.go => crypto_vault_cli.go} (100%) rename cmd/crypto-vault-service/{crypto-vault-service.go => crypto_vault_service.go} (100%) diff --git a/cmd/crypto-vault-cli/crypto-vault-cli.go b/cmd/crypto-vault-cli/crypto_vault_cli.go similarity index 100% rename from cmd/crypto-vault-cli/crypto-vault-cli.go rename to cmd/crypto-vault-cli/crypto_vault_cli.go diff --git a/cmd/crypto-vault-service/crypto-vault-service.go b/cmd/crypto-vault-service/crypto_vault_service.go similarity index 100% rename from cmd/crypto-vault-service/crypto-vault-service.go rename to cmd/crypto-vault-service/crypto_vault_service.go From 962adc6e9b34256f3f038377b576ef8fef317991 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 22:51:08 +0100 Subject: [PATCH 21/26] remove obsolete comments --- cmd/crypto-vault-cli/crypto_vault_cli.go | 5 ----- 1 file changed, 5 deletions(-) diff --git a/cmd/crypto-vault-cli/crypto_vault_cli.go b/cmd/crypto-vault-cli/crypto_vault_cli.go index 0e3c196..d8a90e7 100644 --- a/cmd/crypto-vault-cli/crypto_vault_cli.go +++ b/cmd/crypto-vault-cli/crypto_vault_cli.go @@ -12,19 +12,14 @@ import ( func main() { var rootCmd = &cobra.Command{Use: "crypto-vault-cli"} - // AES Commands commands.InitAESCommands(rootCmd) - // RSA Commands commands.InitRSACommands(rootCmd) - // ECDSA Commands commands.InitECDSACommands(rootCmd) - // PKCS11 Token Commands commands.InitPKCS11Commands(rootCmd) - // Execute the root command if err := rootCmd.Execute(); err != nil { fmt.Println(err) os.Exit(1) From cf02dc5c409a38ed9a6ec5ac984834905a8cdd19 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sat, 14 Dec 2024 23:13:00 +0100 Subject: [PATCH 22/26] consider consistent input argument naming convention --- cmd/crypto-vault-cli/README.md | 44 +++++------ .../internal/commands/aes_commands.go | 67 ++++++++++++----- .../internal/commands/ecdsa_commands.go | 51 ++++++++----- .../internal/commands/pkcs11_commands.go | 73 ++++++++----------- .../internal/commands/rsa_commands.go | 72 ++++++++++++------ 5 files changed, 185 insertions(+), 122 deletions(-) diff --git a/cmd/crypto-vault-cli/README.md b/cmd/crypto-vault-cli/README.md index de404a9..bc4b90b 100644 --- a/cmd/crypto-vault-cli/README.md +++ b/cmd/crypto-vault-cli/README.md @@ -1,4 +1,4 @@ -# crypto-vault-cli +# crypto_vault_cli ## Table of Contents @@ -15,7 +15,7 @@ ## Summary -`crypto-vault-cli` is a command-line tool for file encryption and decryption using AES, RSA and EC algorithms. It provides an easy interface to securely encrypt and decrypt files using symmetric (AES) and asymmetric (RSA, EC) cryptography. +`crypto_vault_cli` is a command-line tool for file encryption and decryption using AES, RSA and EC algorithms. It provides an easy interface to securely encrypt and decrypt files using symmetric (AES) and asymmetric (RSA, EC) cryptography. ## Getting Started @@ -28,9 +28,9 @@ ```sh uuid=$(cat /proc/sys/kernel/random/uuid) # Encryption -go run crypto-vault-cli.go encrypt-aes --input data/input.txt --output data/${uuid}-output.enc --keySize 16 --keyDir data/ +go run crypto_vault_cli.go encrypt-aes --input-file data/input.txt --output-file data/${uuid}-output.enc --key-size 16 --key-dir data/ # Decryption -go run crypto-vault-cli.go decrypt-aes --input data/${uuid}-output.enc --output data/${uuid}-decrypted.txt --symmetricKey +go run crypto_vault_cli.go decrypt-aes --input-file data/${uuid}-output.enc --output-file data/${uuid}-decrypted.txt --symmetric-key ``` #### RSA Example @@ -41,10 +41,10 @@ go run crypto-vault-cli.go decrypt-aes --input data/${uuid}-output.enc --output uuid=$(cat /proc/sys/kernel/random/uuid) # Encryption -go run crypto-vault-cli.go encrypt-rsa --input data/input.txt --output data/${uuid}-encrypted.txt --keyDir data/ +go run crypto_vault_cli.go encrypt-rsa --input-file data/input.txt --output-file data/${uuid}-encrypted.txt --key-dir data/ # Decryption -go run crypto-vault-cli.go decrypt-rsa --input data/${uuid}-encrypted.txt --output data/${uuid}-decrypted.txt --privateKey +go run crypto_vault_cli.go decrypt-rsa --input-file data/${uuid}-encrypted.txt --output-file data/${uuid}-decrypted.txt --private-key ``` #### PKCS#11 encryption and decryption @@ -54,10 +54,10 @@ go run crypto-vault-cli.go decrypt-rsa --input data/${uuid}-encrypted.txt --outp ```sh # RSA-PKCS # Encryption -go run crypto-vault-cli.go encrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/input.txt --output-file data/encrypted-output.enc +go run crypto_vault_cli.go encrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/input.txt --output-file data/encrypted-output.enc # Decryption -go run crypto-vault-cli.go decrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/encrypted-output.enc --output-file data/decrypted-output.txt +go run crypto_vault_cli.go decrypt --token-label my-token --object-label my-rsa-key --key-type RSA --input-file data/encrypted-output.enc --output-file data/decrypted-output.txt ``` --- @@ -70,10 +70,10 @@ go run crypto-vault-cli.go decrypt --token-label my-token --object-label my-rsa- ```sh # Sign a file with a newly generated ECC key pair (internally generated) -go run crypto-vault-cli.go sign-ecc --input data/input.txt --keyDir data +go run crypto_vault_cli.go sign-ecc --input-file data/input.txt --key-dir data # Verify the signature using the generated public key -go run crypto-vault-cli.go verify-ecc --input data/input.txt --publicKey --signature +go run crypto_vault_cli.go verify-ecc --input-file data/input.txt --public-key --signature-file ``` #### PKCS#11 signing and verifying @@ -83,17 +83,17 @@ go run crypto-vault-cli.go verify-ecc --input data/input.txt --publicKey Date: Sat, 14 Dec 2024 23:14:55 +0100 Subject: [PATCH 23/26] remove obsolete comments --- .../internal/commands/aes_commands.go | 12 +----------- .../internal/commands/ecdsa_commands.go | 15 +-------------- .../internal/commands/rsa_commands.go | 15 +++------------ 3 files changed, 5 insertions(+), 37 deletions(-) diff --git a/cmd/crypto-vault-cli/internal/commands/aes_commands.go b/cmd/crypto-vault-cli/internal/commands/aes_commands.go index 1c1f456..db267eb 100644 --- a/cmd/crypto-vault-cli/internal/commands/aes_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/aes_commands.go @@ -7,7 +7,7 @@ import ( "os" "path/filepath" - "github.com/google/uuid" // Import UUID package + "github.com/google/uuid" "github.com/spf13/cobra" ) @@ -40,13 +40,11 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { aes := &cryptography.AES{} - // Generate AES Key key, err := aes.GenerateKey(keySize) if err != nil { log.Fatalf("Error generating AES key: %v\n", err) } - // Encrypt the file plainText, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) @@ -57,17 +55,14 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { log.Fatalf("Error encrypting data: %v\n", err) } - // Save encrypted file err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { log.Fatalf("Error writing encrypted file: %v\n", err) } fmt.Printf("Encrypted data saved to %s\n", outputFile) - // Generate a UUID for the key filename uniqueID := uuid.New().String() - // Save the AES key with the UUID prefix in the specified key directory keyFilePath := filepath.Join(keyDir, fmt.Sprintf("%s-symmetric-key.bin", uniqueID)) err = os.WriteFile(keyFilePath, key, 0644) if err != nil { @@ -96,18 +91,15 @@ func DecryptAESCmd(cmd *cobra.Command, args []string) { return } - // Validate input arguments if inputFile == "" || outputFile == "" || symmetricKey == "" { log.Fatalf("Error: input, output and symmetricKey flags are required\n") } - // Read the symmetric key from the file key, err := os.ReadFile(symmetricKey) if err != nil { log.Fatalf("Error reading symmetric key from file: %v\n", err) } - // Decrypt the file encryptedData, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading encrypted file: %v\n", err) @@ -120,7 +112,6 @@ func DecryptAESCmd(cmd *cobra.Command, args []string) { log.Fatalf("Error decrypting data: %v\n", err) } - // Save decrypted file err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { log.Fatalf("Error writing decrypted file: %v\n", err) @@ -129,7 +120,6 @@ func DecryptAESCmd(cmd *cobra.Command, args []string) { } func InitAESCommands(rootCmd *cobra.Command) { - // AES Commands var encryptAESFileCmd = &cobra.Command{ Use: "encrypt-aes", Short: "Encrypt a file using AES", diff --git a/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go b/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go index 1b816d0..0bd1aa6 100644 --- a/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go @@ -13,7 +13,6 @@ import ( "github.com/spf13/cobra" ) -// ECDSA command // signECCCmd signs the contents of a file with ECDSA func SignECCCmd(cmd *cobra.Command, args []string) { @@ -29,34 +28,29 @@ func SignECCCmd(cmd *cobra.Command, args []string) { return } - // ECC implementation EC := &cryptography.EC{} var privateKey *ecdsa.PrivateKey var publicKey *ecdsa.PublicKey - // Generate new ECC keys if no private key is provided privateKey, publicKey, err = EC.GenerateKeys(elliptic.P256()) if err != nil { log.Fatalf("Error generating ECC keys: %v\n", err) } - // Read the file content fileContent, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } - // Sign the file content (hash the content before signing) signature, err := EC.Sign(fileContent, privateKey) if err != nil { log.Fatalf("Error signing file content: %v\n", err) } - // Output the signature fmt.Printf("Signature: %x\n", signature) uniqueID := uuid.New() - // Save the private and public keys to files (if they were generated) + if privateKey != nil && keyDir != "" { privateKeyFilePath := fmt.Sprintf("%s/%s-private-key.pem", keyDir, uniqueID.String()) @@ -76,7 +70,6 @@ func SignECCCmd(cmd *cobra.Command, args []string) { fmt.Printf("Public key saved to: %s\n", publicKeyFilePath) } - // Save the signature to a file in the data folder (optional, based on the input file) if keyDir != "" { signatureFilePath := fmt.Sprintf("%s/%s-signature.sig", keyDir, uniqueID.String()) err = EC.SaveSignatureToFile(signatureFilePath, signature) @@ -107,11 +100,9 @@ func VerifyECCCmd(cmd *cobra.Command, args []string) { return } - // ECC implementation EC := &cryptography.EC{} var publicKey *ecdsa.PublicKey - // Read the public key if publicKeyPath == "" { log.Fatalf("Public key is required for ECC signature verification.\n") } else { @@ -121,25 +112,21 @@ func VerifyECCCmd(cmd *cobra.Command, args []string) { } } - // Read the file content (optional: you can also hash the content before verifying) fileContent, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) } - // Read the signature (from hex file) signatureHex, err := os.ReadFile(signatureFile) if err != nil { log.Fatalf("Error reading signature file: %v\n", err) } - // Decode the hex string back to bytes signature, err := hex.DecodeString(string(signatureHex)) if err != nil { log.Fatalf("Error decoding signature hex: %v\n", err) } - // Verify the signature valid, err := EC.Verify(fileContent, signature, publicKey) if err != nil { log.Fatalf("Error verifying signature: %v\n", err) diff --git a/cmd/crypto-vault-cli/internal/commands/rsa_commands.go b/cmd/crypto-vault-cli/internal/commands/rsa_commands.go index 7a556bf..dfa138c 100644 --- a/cmd/crypto-vault-cli/internal/commands/rsa_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/rsa_commands.go @@ -31,17 +31,14 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { return } - // Validate input arguments if inputFile == "" || outputFile == "" || keyDir == "" { log.Fatalf("Error: input, output and keyDir flags are required\n") } - // Generate RSA keys if no public key is provided var publicKey *rsa.PublicKey rsa := &cryptography.RSA{} uniqueID := uuid.New() - // Generate RSA keys privateKey, publicKey, err := rsa.GenerateKeys(2048) if err != nil { @@ -49,7 +46,7 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { } privateKeyFilePath := fmt.Sprintf("%s/%s-private-key.pem", keyDir, uniqueID.String()) - // Optionally save the private and public keys + err = rsa.SavePrivateKeyToFile(privateKey, privateKeyFilePath) if err != nil { log.Fatalf("Error saving private key: %v\n", err) @@ -64,7 +61,6 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { fmt.Println("Private key path:", privateKeyFilePath) fmt.Println("Public key path:", publicKeyFilePath) - // Encrypt the file plainText, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading input file: %v\n", err) @@ -75,7 +71,6 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { log.Fatalf("Error encrypting data: %v\n", err) } - // Save encrypted file err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { log.Fatalf("Error writing encrypted file: %v\n", err) @@ -102,32 +97,29 @@ func DecryptRSACmd(cmd *cobra.Command, args []string) { return } - // Generate RSA keys if no private key is provided var privateKey *rsa.PrivateKey rsa := &cryptography.RSA{} if privateKeyPath == "" { - // Generate RSA keys + privKey, _, err := rsa.GenerateKeys(2048) if err != nil { log.Fatalf("Error generating RSA keys: %v\n", err) } privateKey = privKey - // Optionally save the private and public keys err = rsa.SavePrivateKeyToFile(privateKey, "private-key.pem") if err != nil { log.Fatalf("Error saving private key: %v\n", err) } fmt.Println("Generated and saved private key.") } else { - // Read the provided private key + privateKey, err = rsa.ReadPrivateKey(privateKeyPath) if err != nil { log.Fatalf("Error reading private key: %v\n", err) } } - // Decrypt the file encryptedData, err := os.ReadFile(inputFile) if err != nil { log.Fatalf("Error reading encrypted file: %v\n", err) @@ -138,7 +130,6 @@ func DecryptRSACmd(cmd *cobra.Command, args []string) { log.Fatalf("Error decrypting data: %v\n", err) } - // Save decrypted file err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { log.Fatalf("Error writing decrypted file: %v\n", err) From d0e9c7337f3e3a0b575ec820023c4592bc317b9e Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sun, 15 Dec 2024 00:14:50 +0100 Subject: [PATCH 24/26] consider returning json info or error responses --- .../internal/commands/aes_commands.go | 71 ++++--- .../internal/commands/ecdsa_commands.go | 85 ++++++--- .../internal/commands/pkcs11_commands.go | 179 ++++++++++++------ .../internal/commands/rsa_commands.go | 84 +++++--- .../internal/status/status_codes.go | 62 ++++++ 5 files changed, 347 insertions(+), 134 deletions(-) create mode 100644 cmd/crypto-vault-cli/internal/status/status_codes.go diff --git a/cmd/crypto-vault-cli/internal/commands/aes_commands.go b/cmd/crypto-vault-cli/internal/commands/aes_commands.go index db267eb..61bd784 100644 --- a/cmd/crypto-vault-cli/internal/commands/aes_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/aes_commands.go @@ -1,9 +1,9 @@ package commands import ( + "crypto_vault_service/cmd/crypto-vault-cli/internal/status" "crypto_vault_service/internal/infrastructure/cryptography" "fmt" - "log" "os" "path/filepath" @@ -16,25 +16,29 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFile, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keySize, err := cmd.Flags().GetInt("key-size") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyDir, _ := cmd.Flags().GetString("key-dir") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } @@ -42,81 +46,100 @@ func EncryptAESCmd(cmd *cobra.Command, args []string) { key, err := aes.GenerateKey(keySize) if err != nil { - log.Fatalf("Error generating AES key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } plainText, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading input file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } encryptedData, err := aes.Encrypt(plainText, key) if err != nil { - log.Fatalf("Error encrypting data: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { - log.Fatalf("Error writing encrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Encrypted data saved to %s\n", outputFile) uniqueID := uuid.New().String() keyFilePath := filepath.Join(keyDir, fmt.Sprintf("%s-symmetric-key.bin", uniqueID)) err = os.WriteFile(keyFilePath, key, 0644) if err != nil { - log.Fatalf("Error writing AES key to file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("AES key saved to %s\n", keyFilePath) + + info := status.NewInfo(fmt.Sprintf("Encrypted data saved to %s. AES key saved to %s", outputFile, keyFilePath)) + info.PrintJsonInfo(false) } // Decrypts a file using AES and reads the corresponding symmetric key with a UUID prefix func DecryptAESCmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFile, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } symmetricKey, err := cmd.Flags().GetString("symmetric-key") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } - if inputFile == "" || outputFile == "" || symmetricKey == "" { - log.Fatalf("Error: input, output and symmetricKey flags are required\n") - } - key, err := os.ReadFile(symmetricKey) if err != nil { - log.Fatalf("Error reading symmetric key from file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } encryptedData, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading encrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } aes := &cryptography.AES{} decryptedData, err := aes.Decrypt(encryptedData, key) if err != nil { - log.Fatalf("Error decrypting data: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { - log.Fatalf("Error writing decrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Decrypted data saved to %s\n", outputFile) + info := status.NewInfo(fmt.Sprintf("Decrypted data saved to %s", outputFile)) + info.PrintJsonInfo(false) } func InitAESCommands(rootCmd *cobra.Command) { diff --git a/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go b/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go index 0bd1aa6..e9365c0 100644 --- a/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/ecdsa_commands.go @@ -3,6 +3,7 @@ package commands import ( "crypto/ecdsa" "crypto/elliptic" + "crypto_vault_service/cmd/crypto-vault-cli/internal/status" "crypto_vault_service/internal/infrastructure/cryptography" "encoding/hex" "fmt" @@ -18,13 +19,15 @@ func SignECCCmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyDir, err := cmd.Flags().GetString("key-dir") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } @@ -34,20 +37,27 @@ func SignECCCmd(cmd *cobra.Command, args []string) { privateKey, publicKey, err = EC.GenerateKeys(elliptic.P256()) if err != nil { - log.Fatalf("Error generating ECC keys: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } fileContent, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading input file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } signature, err := EC.Sign(fileContent, privateKey) if err != nil { - log.Fatalf("Error signing file content: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Signature: %x\n", signature) + info := status.NewInfo(fmt.Sprintf("Signature: %x", signature)) + info.PrintJsonInfo(false) uniqueID := uuid.New() @@ -56,27 +66,38 @@ func SignECCCmd(cmd *cobra.Command, args []string) { err = EC.SavePrivateKeyToFile(privateKey, privateKeyFilePath) if err != nil { - log.Fatalf("Error saving private key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Private key saved to: %s\n", privateKeyFilePath) + info := status.NewInfo(fmt.Sprintf("Private key saved to: %s", privateKeyFilePath)) + info.PrintJsonInfo(false) } if publicKey != nil && keyDir != "" { publicKeyFilePath := fmt.Sprintf("%s/%s-public-key.pem", keyDir, uniqueID.String()) err = EC.SavePublicKeyToFile(publicKey, publicKeyFilePath) if err != nil { - log.Fatalf("Error saving public key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Public key saved to: %s\n", publicKeyFilePath) + info := status.NewInfo(fmt.Sprintf("Public key saved to: %s", publicKeyFilePath)) + info.PrintJsonInfo(false) } if keyDir != "" { signatureFilePath := fmt.Sprintf("%s/%s-signature.sig", keyDir, uniqueID.String()) err = EC.SaveSignatureToFile(signatureFilePath, signature) if err != nil { - log.Fatalf("Error saving signature: %v\n", err) + if err != nil { + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return + } } - fmt.Printf("Signature saved to: %s\n", signatureFilePath) + info := status.NewInfo(fmt.Sprintf("Signature file saved to: %s", signatureFilePath)) + info.PrintJsonInfo(false) } } @@ -84,19 +105,22 @@ func SignECCCmd(cmd *cobra.Command, args []string) { func VerifyECCCmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } publicKeyPath, err := cmd.Flags().GetString("public-key") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } signatureFile, err := cmd.Flags().GetString("signature-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } @@ -104,38 +128,53 @@ func VerifyECCCmd(cmd *cobra.Command, args []string) { var publicKey *ecdsa.PublicKey if publicKeyPath == "" { - log.Fatalf("Public key is required for ECC signature verification.\n") + log.Fatalf("Public key is required for ECC signature verification.") + e := status.NewError("Public key is required for ECC signature verification", status.ErrCodeInternalError) + e.PrintJsonError() + return } else { publicKey, err = EC.ReadPublicKey(publicKeyPath, elliptic.P256()) if err != nil { - log.Fatalf("Error reading public key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } fileContent, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading input file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } signatureHex, err := os.ReadFile(signatureFile) if err != nil { - log.Fatalf("Error reading signature file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } signature, err := hex.DecodeString(string(signatureHex)) if err != nil { - log.Fatalf("Error decoding signature hex: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } valid, err := EC.Verify(fileContent, signature, publicKey) if err != nil { - log.Fatalf("Error verifying signature: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } if valid { - fmt.Println("Signature is valid.") + info := status.NewInfo("Signature is valid") + info.PrintJsonInfo(false) } else { - fmt.Println("Signature is invalid.") + info := status.NewInfo("Signature is invalid") + info.PrintJsonInfo(false) } } diff --git a/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go b/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go index 868ba99..1a2c090 100644 --- a/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go @@ -1,6 +1,7 @@ package commands import ( + "crypto_vault_service/cmd/crypto-vault-cli/internal/status" "crypto_vault_service/internal/infrastructure/cryptography" "crypto_vault_service/internal/infrastructure/settings" "crypto_vault_service/internal/infrastructure/utils" @@ -8,8 +9,6 @@ import ( "fmt" "os" - "log" - "github.com/spf13/cobra" ) @@ -75,27 +74,32 @@ func (h *PKCS11CommandsHandler) writePkcs11ConfigFile(modulePath, soPin, userPin func (h *PKCS11CommandsHandler) storePKCS11SettingsCmd(cmd *cobra.Command, args []string) { modulePath, err := cmd.Flags().GetString("module") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } soPin, err := cmd.Flags().GetString("so-pin") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } userPin, err := cmd.Flags().GetString("user-pin") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } slotId, err := cmd.Flags().GetString("slot-id") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } h.writePkcs11ConfigFile(modulePath, soPin, userPin, slotId) - fmt.Println("created pkcs11-settings.json") + info := status.NewInfo("created pkcs11-settings.json") + info.PrintJsonInfo(false) } // getTokenHandler reads the PKCS#11 config file and validates the settings. @@ -122,70 +126,83 @@ func (h *PKCS11CommandsHandler) getTokenHandler() (*cryptography.PKCS11Handler, func (h *PKCS11CommandsHandler) ListTokenSlotsCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokens, err := tokenHandler.ListTokenSlots() if err != nil { - log.Fatalf("Error initializing token: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokensJSON, err := json.MarshalIndent(tokens, "", " ") if err != nil { - log.Fatalf("Error marshalling tokens to JSON: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } - fmt.Println(string(tokensJSON)) + info := status.NewInfo(string(tokensJSON)) + info.PrintJsonInfo(true) } // ListObjectsSlotsCmd lists PKCS#11 token objects func (h *PKCS11CommandsHandler) ListObjectsSlotsCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objects, err := tokenHandler.ListObjects(tokenLabel) if err != nil { - log.Fatalf("Error initializing token: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectsJSON, err := json.MarshalIndent(objects, "", " ") if err != nil { - log.Fatalf("Error marshalling objects to JSON: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } - fmt.Println(string(objectsJSON)) + info := status.NewInfo(string(objectsJSON)) + info.PrintJsonInfo(true) } // InitializeTokenCmd initializes a PKCS#11 token func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if err := tokenHandler.InitializeToken(tokenLabel); err != nil { - log.Fatalf("Error initializing token: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -193,33 +210,41 @@ func (h *PKCS11CommandsHandler) InitializeTokenCmd(cmd *cobra.Command, args []st func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyType, err := cmd.Flags().GetString("key-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keySize, err := cmd.Flags().GetUint("key-size") if err != nil { - log.Panicf("Error fetching key-size path flag: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } if err := tokenHandler.AddKey(tokenLabel, objectLabel, keyType, keySize); err != nil { - log.Fatalf("Error adding key: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -227,28 +252,34 @@ func (h *PKCS11CommandsHandler) AddKeyCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectType, err := cmd.Flags().GetString("object-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if err := tokenHandler.DeleteObject(tokenLabel, objectType, objectLabel); err != nil { - log.Fatalf("Error deleting object: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -256,38 +287,46 @@ func (h *PKCS11CommandsHandler) DeleteObjectCmd(cmd *cobra.Command, args []strin func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } inputFilePath, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFilePath, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyType, err := cmd.Flags().GetString("key-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if err := tokenHandler.Encrypt(tokenLabel, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { - log.Fatalf("Error encrypting data: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -295,38 +334,46 @@ func (h *PKCS11CommandsHandler) EncryptCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } inputFilePath, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFilePath, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyType, err := cmd.Flags().GetString("key-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if err := tokenHandler.Decrypt(tokenLabel, objectLabel, inputFilePath, outputFilePath, keyType); err != nil { - log.Fatalf("Error decrypting data: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -334,38 +381,46 @@ func (h *PKCS11CommandsHandler) DecryptCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } dataFilePath, err := cmd.Flags().GetString("data-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } signatureFilePath, err := cmd.Flags().GetString("signature-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyType, err := cmd.Flags().GetString("key-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if err := tokenHandler.Sign(tokenLabel, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { - log.Fatalf("Error signing data: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } @@ -373,38 +428,46 @@ func (h *PKCS11CommandsHandler) SignCmd(cmd *cobra.Command, args []string) { func (h *PKCS11CommandsHandler) VerifyCmd(cmd *cobra.Command, args []string) { tokenHandler, err := h.getTokenHandler() if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } tokenLabel, err := cmd.Flags().GetString("token-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } objectLabel, err := cmd.Flags().GetString("object-label") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } dataFilePath, err := cmd.Flags().GetString("data-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } signatureFilePath, err := cmd.Flags().GetString("signature-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyType, err := cmd.Flags().GetString("key-type") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } if _, err := tokenHandler.Verify(tokenLabel, objectLabel, dataFilePath, signatureFilePath, keyType); err != nil { - log.Fatalf("Error verifying signature: %v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } diff --git a/cmd/crypto-vault-cli/internal/commands/rsa_commands.go b/cmd/crypto-vault-cli/internal/commands/rsa_commands.go index dfa138c..ed69606 100644 --- a/cmd/crypto-vault-cli/internal/commands/rsa_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/rsa_commands.go @@ -2,9 +2,9 @@ package commands import ( "crypto/rsa" + "crypto_vault_service/cmd/crypto-vault-cli/internal/status" "crypto_vault_service/internal/infrastructure/cryptography" "fmt" - "log" "os" "github.com/google/uuid" @@ -15,26 +15,25 @@ import ( func EncryptRSACmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFile, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } keyDir, err := cmd.Flags().GetString("key-dir") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } - if inputFile == "" || outputFile == "" || keyDir == "" { - log.Fatalf("Error: input, output and keyDir flags are required\n") - } - var publicKey *rsa.PublicKey rsa := &cryptography.RSA{} @@ -42,58 +41,71 @@ func EncryptRSACmd(cmd *cobra.Command, args []string) { privateKey, publicKey, err := rsa.GenerateKeys(2048) if err != nil { - log.Fatalf("Error generating RSA keys: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } privateKeyFilePath := fmt.Sprintf("%s/%s-private-key.pem", keyDir, uniqueID.String()) err = rsa.SavePrivateKeyToFile(privateKey, privateKeyFilePath) if err != nil { - log.Fatalf("Error saving private key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } publicKeyFilePath := fmt.Sprintf("%s/%s-public-key.pem", keyDir, uniqueID.String()) err = rsa.SavePublicKeyToFile(publicKey, publicKeyFilePath) if err != nil { - log.Fatalf("Error saving public key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Println("Generated and saved RSA keys.") - fmt.Println("Private key path:", privateKeyFilePath) - fmt.Println("Public key path:", publicKeyFilePath) plainText, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading input file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } encryptedData, err := rsa.Encrypt(plainText, publicKey) if err != nil { - log.Fatalf("Error encrypting data: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } err = os.WriteFile(outputFile, encryptedData, 0644) if err != nil { - log.Fatalf("Error writing encrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Encrypted data saved to %s\n", outputFile) + info := status.NewInfo(fmt.Sprintf("Generated and saved RSA keys. Private key path: %s. Public key path: %s. Encrypted data saved to %s", privateKeyFilePath, publicKeyFilePath, outputFile)) + info.PrintJsonInfo(false) } func DecryptRSACmd(cmd *cobra.Command, args []string) { inputFile, err := cmd.Flags().GetString("input-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } outputFile, err := cmd.Flags().GetString("output-file") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } privateKeyPath, err := cmd.Flags().GetString("private-key") if err != nil { - log.Fatalf("%v", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() return } @@ -103,38 +115,52 @@ func DecryptRSACmd(cmd *cobra.Command, args []string) { privKey, _, err := rsa.GenerateKeys(2048) if err != nil { - log.Fatalf("Error generating RSA keys: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } privateKey = privKey err = rsa.SavePrivateKeyToFile(privateKey, "private-key.pem") if err != nil { - log.Fatalf("Error saving private key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Println("Generated and saved private key.") + info := status.NewInfo("Generated and saved private key") + info.PrintJsonInfo(false) } else { privateKey, err = rsa.ReadPrivateKey(privateKeyPath) if err != nil { - log.Fatalf("Error reading private key: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } } encryptedData, err := os.ReadFile(inputFile) if err != nil { - log.Fatalf("Error reading encrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } decryptedData, err := rsa.Decrypt(encryptedData, privateKey) if err != nil { - log.Fatalf("Error decrypting data: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } err = os.WriteFile(outputFile, decryptedData, 0644) if err != nil { - log.Fatalf("Error writing decrypted file: %v\n", err) + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return } - fmt.Printf("Decrypted data saved to %s\n", outputFile) + info := status.NewInfo(fmt.Sprintf("Decrypted data saved to %s", outputFile)) + info.PrintJsonInfo(false) } func InitRSACommands(rootCmd *cobra.Command) { diff --git a/cmd/crypto-vault-cli/internal/status/status_codes.go b/cmd/crypto-vault-cli/internal/status/status_codes.go new file mode 100644 index 0000000..0b36745 --- /dev/null +++ b/cmd/crypto-vault-cli/internal/status/status_codes.go @@ -0,0 +1,62 @@ +package status + +import ( + "encoding/json" + "fmt" + "log" +) + +const ( + ErrCodeInternalError = iota + 1 + ErrCodeInvalidInput + ErrCodePermissionDenied + ErrCodeNotFound +) + +// Error struct to hold the error message and code +type Error struct { + Message string `json:"message"` + Code uint `json:"code"` +} + +// Info struct to hold the information message +type Info struct { + Message string `json:"message"` +} + +// NewError creates a new error with a message and code +func NewError(message string, code uint) *Error { + return &Error{ + Message: message, + Code: code, + } +} + +// NewInfo creates a new error with a message and code +func NewInfo(message string) *Info { + return &Info{ + Message: message, + } +} + +// PrintJsonError method to print error in JSON format and terminate program +func (e *Error) PrintJsonError() { + errorJSON, err := json.MarshalIndent(e, "", " ") + if err != nil { + log.Fatalf("Error marshaling error: %v", err) + } + log.Fatalf("%s", string(errorJSON)) +} + +// PrintJsonInfo method to print info in JSON format +func (i *Info) PrintJsonInfo(isJsonFormatted bool) { + if isJsonFormatted { + fmt.Printf("{\n message: %v \n}", string(i.Message)) + } else { + infoJSON, err := json.MarshalIndent(i, "", " ") + if err != nil { + log.Fatalf("Error marshaling info: %v", err) + } + fmt.Printf("%s\n", string(infoJSON)) + } +} From 72dbd265a520372e59b55f15db700afc0fdb8213 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sun, 15 Dec 2024 01:15:16 +0100 Subject: [PATCH 25/26] fix linter finding --- cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go b/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go index 1a2c090..ff83c0f 100644 --- a/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go +++ b/cmd/crypto-vault-cli/internal/commands/pkcs11_commands.go @@ -97,7 +97,13 @@ func (h *PKCS11CommandsHandler) storePKCS11SettingsCmd(cmd *cobra.Command, args return } - h.writePkcs11ConfigFile(modulePath, soPin, userPin, slotId) + err = h.writePkcs11ConfigFile(modulePath, soPin, userPin, slotId) + if err != nil { + e := status.NewError(fmt.Sprintf("%v", err), status.ErrCodeInternalError) + e.PrintJsonError() + return + } + info := status.NewInfo("created pkcs11-settings.json") info.PrintJsonInfo(false) } From 05c758e6ed48e5e101ba7bb3f60af1db46855da9 Mon Sep 17 00:00:00 2001 From: Marvin Gajek Date: Sun, 15 Dec 2024 01:15:33 +0100 Subject: [PATCH 26/26] add action for shutting down containters --- .github/workflows/dev.yml | 4 ++++ .github/workflows/pre-release.yml | 4 ++++ .github/workflows/release.yml | 4 ++++ 3 files changed, 12 insertions(+) diff --git a/.github/workflows/dev.yml b/.github/workflows/dev.yml index eeb857d..8e82dd3 100644 --- a/.github/workflows/dev.yml +++ b/.github/workflows/dev.yml @@ -40,6 +40,10 @@ jobs: run: sudo ./run-test.sh -i working-directory: ./scripts + - name: Shut down external storage services + run: | + docker compose down -v + # Run static code analysis on source code # Run vulnerability scanner and generate SBOMs on third part dependencies # # Create build artifacts, e.g. Build docker image with dev tag for applications and push to container registry \ No newline at end of file diff --git a/.github/workflows/pre-release.yml b/.github/workflows/pre-release.yml index 89716d6..cd3b906 100644 --- a/.github/workflows/pre-release.yml +++ b/.github/workflows/pre-release.yml @@ -40,6 +40,10 @@ jobs: run: sudo ./run-test.sh -i working-directory: ./scripts + - name: Shut down external storage services + run: | + docker compose down -v + # Run static code analysis on source code # Run vulnerability scanner and generate SBOMs on third part dependencies # Create build artifacts, e.g. Build docker image with pre-release tag for applications, scan docker image and push to container registry \ No newline at end of file diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index cfe34f0..ef414c6 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -41,6 +41,10 @@ jobs: run: sudo ./run-test.sh -i working-directory: ./scripts + - name: Shut down external storage services + run: | + docker compose down -v + # Run static code analysis on source code # Run vulnerability scanner and generate SBOMs on third part dependencies # Create build artifacts, e.g. Build docker image with release tag for applications, scan docker image and push to container registry \ No newline at end of file