From a1d21cef64cc873eca76ace6c59887f132cad1ce Mon Sep 17 00:00:00 2001 From: Ch1ffr3punk Date: Sat, 7 Mar 2026 00:07:10 +0100 Subject: Add files via upload --- yubicrypt.go | 271 ++++++++++------------------------------------------------- 1 file changed, 46 insertions(+), 225 deletions(-) diff --git a/yubicrypt.go b/yubicrypt.go index 720b74b..a110d99 100644 --- a/yubicrypt.go +++ b/yubicrypt.go @@ -36,7 +36,6 @@ import ( "fyne.io/fyne/v2/layout" "fyne.io/fyne/v2/theme" "fyne.io/fyne/v2/widget" - "github.com/awnumar/memguard" "github.com/go-piv/piv-go/v2/piv" ) @@ -102,19 +101,15 @@ type GUI struct { func main() { defer memguard.Purge() - gui := &GUI{ app: app.NewWithID("oc2mx.net.yubicrypt"), currentTheme: "dark", encryptionUsed: false, } - gui.window = gui.app.NewWindow("yubicrypt") gui.window.Resize(fyne.NewSize(800, 600)) - gui.createUI() gui.applyTheme() - gui.window.SetContent(gui.createMainUI()) gui.window.ShowAndRun() } @@ -122,7 +117,6 @@ func main() { // createUI initializes all UI components func (g *GUI) createUI() { monospace := &fyne.TextStyle{Monospace: true} - g.textArea = widget.NewMultiLineEntry() g.textArea.Wrapping = fyne.TextWrapOff g.textArea.TextStyle = *monospace @@ -140,32 +134,39 @@ func (g *GUI) createUI() { g.statusLabel = widget.NewLabel("Ready") g.statusLabel.Wrapping = fyne.TextWrapWord - // Theme toggle button - g.themeToggle = widget.NewButtonWithIcon("", theme.ViewRefreshIcon(), g.toggleTheme) + // Theme toggle button with emoji (starts with Sun for dark mode) + g.themeToggle = widget.NewButton("☀️", g.toggleTheme) } // createMainUI builds the main layout func (g *GUI) createMainUI() fyne.CanvasObject { - signTextBtn := widget.NewButtonWithIcon("Sign", theme.ConfirmIcon(), g.onSignText) - verifyTextBtn := widget.NewButtonWithIcon("Verify", theme.VisibilityIcon(), g.onVerifyText) - - padBtn := widget.NewButtonWithIcon("Pad", theme.ContentAddIcon(), g.onPad) - unpadBtn := widget.NewButtonWithIcon("Unpad", theme.ContentRemoveIcon(), g.onUnpad) - encryptBtn := widget.NewButtonWithIcon("Encrypt", theme.MailComposeIcon(), g.onEncrypt) - decryptBtn := widget.NewButtonWithIcon("Decrypt", theme.MailForwardIcon(), g.onDecrypt) - - buttonContainer := container.NewHBox( - layout.NewSpacer(), + // Buttons without icons, new order + signTextBtn := widget.NewButton("Sign", g.onSignText) + padBtn := widget.NewButton("Pad", g.onPad) + + encryptBtn := widget.NewButton("Encrypt", g.onEncrypt) + encryptBtn.Importance = widget.HighImportance // Blue + + decryptBtn := widget.NewButton("Decrypt", g.onDecrypt) + decryptBtn.Importance = widget.HighImportance // Blue + + unpadBtn := widget.NewButton("Unpad", g.onUnpad) + verifyTextBtn := widget.NewButton("Verify", g.onVerifyText) + + // Grid layout to fill width without side spaces + buttonContainer := container.NewGridWithColumns(6, signTextBtn, - verifyTextBtn, padBtn, - unpadBtn, encryptBtn, decryptBtn, - layout.NewSpacer(), + unpadBtn, + verifyTextBtn, ) - clearBtn := widget.NewButtonWithIcon("Clear", theme.DeleteIcon(), g.onClear) + // Clear Button (Blue, no icon) + clearBtn := widget.NewButton("Clear", g.onClear) + clearBtn.Importance = widget.HighImportance + pinContainer := container.NewVBox( container.NewHBox( layout.NewSpacer(), @@ -194,7 +195,6 @@ func (g *GUI) createMainUI() fyne.CanvasObject { nil, container.NewScroll(g.textArea), ) - return mainContainer } @@ -203,11 +203,11 @@ func (g *GUI) toggleTheme() { if g.currentTheme == "dark" { g.app.Settings().SetTheme(theme.LightTheme()) g.currentTheme = "light" - g.themeToggle.SetIcon(theme.ViewRefreshIcon()) + g.themeToggle.SetText("🌙") // Moon for light mode } else { g.app.Settings().SetTheme(theme.DarkTheme()) g.currentTheme = "dark" - g.themeToggle.SetIcon(theme.ViewRefreshIcon()) + g.themeToggle.SetText("☀️") // Sun for dark mode } } @@ -215,8 +215,10 @@ func (g *GUI) toggleTheme() { func (g *GUI) applyTheme() { if g.currentTheme == "dark" { g.app.Settings().SetTheme(theme.DarkTheme()) + g.themeToggle.SetText("☀️") // Sun for dark mode } else { g.app.Settings().SetTheme(theme.LightTheme()) + g.themeToggle.SetText("🌙") // Moon for light mode } } @@ -226,13 +228,11 @@ func (g *GUI) onSignText() { g.statusLabel.SetText("Error: PIN required for signing") return } - input := g.textArea.Text if input == "" { g.statusLabel.SetText("Error: No text to sign") return } - // Check for existing signature to prevent double signing s := string(input) for algo := range supportedAlgorithms { @@ -241,14 +241,12 @@ func (g *GUI) onSignText() { return } } - // Sign data in the text area result, err := g.signData([]byte(input), g.pinEntry.Text) if err != nil { g.statusLabel.SetText("Signing failed: " + err.Error()) return } - g.textArea.SetText(result) g.statusLabel.SetText("✓ Message signed successfully (" + formatByteSize(len(input)) + ")") } @@ -260,13 +258,11 @@ func (g *GUI) onVerifyText() { g.statusLabel.SetText("Error: No text to verify") return } - err := g.verifyData([]byte(input)) if err != nil { g.statusLabel.SetText("Verification failed: " + err.Error()) return } - g.statusLabel.SetText("✓ Signature is valid") } @@ -278,26 +274,22 @@ func (g *GUI) onEncrypt() { g.choosePublicKey() return } - if g.publicKeyPath != "" { input := g.textArea.Text if input == "" { g.statusLabel.SetText("Error: No text to encrypt") return } - result, err := g.encryptData([]byte(input), g.publicKeyPath) if err != nil { g.statusLabel.SetText("Encryption failed: " + err.Error()) return } - g.encryptionUsed = true g.textArea.SetText(result) g.statusLabel.SetText("✓ Encrypted with: " + filepath.Base(g.publicKeyPath)) return } - g.choosePublicKey() } @@ -312,32 +304,27 @@ func (g *GUI) choosePublicKey() { return } defer reader.Close() - path := reader.URI().Path() if filepath.Ext(path) != ".pem" { g.statusLabel.SetText("Error: Please select a .pem file") return } - - g.publicKeyPath = path - g.encryptionUsed = false - g.statusLabel.SetText("Selected public key: " + filepath.Base(path) + " - Encrypting...") - - input := g.textArea.Text - if input == "" { - g.statusLabel.SetText("Selected: " + filepath.Base(path) + " - No text to encrypt") - return - } - - result, err := g.encryptData([]byte(input), g.publicKeyPath) - if err != nil { - g.statusLabel.SetText("Encryption failed: %v" + err.Error()) - return - } - - g.encryptionUsed = true - g.textArea.SetText(result) - g.statusLabel.SetText("✓ Encrypted with: " + filepath.Base(path)) + g.publicKeyPath = path + g.encryptionUsed = false + g.statusLabel.SetText("Selected public key: " + filepath.Base(path) + " - Encrypting...") + input := g.textArea.Text + if input == "" { + g.statusLabel.SetText("Selected: " + filepath.Base(path) + " - No text to encrypt") + return + } + result, err := g.encryptData([]byte(input), g.publicKeyPath) + if err != nil { + g.statusLabel.SetText("Encryption failed: %v" + err.Error()) + return + } + g.encryptionUsed = true + g.textArea.SetText(result) + g.statusLabel.SetText("✓ Encrypted with: " + filepath.Base(path)) }, g.window) } @@ -347,19 +334,16 @@ func (g *GUI) onDecrypt() { g.statusLabel.SetText("Error: PIN required for decryption") return } - input := g.textArea.Text if input == "" { g.statusLabel.SetText("Error: No text to decrypt") return } - result, err := g.decryptData([]byte(input), g.pinEntry.Text) if err != nil { g.statusLabel.SetText("Decryption failed: " + err.Error()) return } - g.textArea.SetText(string(result)) g.statusLabel.SetText("✓ Message decrypted successfully") } @@ -369,12 +353,10 @@ func (g *GUI) onClear() { g.textArea.SetText("") g.publicKeyPath = "" g.encryptionUsed = false - clipboard := g.app.Clipboard() if clipboard != nil { clipboard.SetContent("") } - g.statusLabel.SetText("Cleared text area, clipboard and reset encryption state") } @@ -390,21 +372,17 @@ func safePad(b []byte, size int) []byte { func (g *GUI) signData(data []byte, pin string) (string, error) { pinGuard := memguard.NewBufferFromBytes([]byte(pin)) defer pinGuard.Destroy() - // Normalize line endings to RFC-compliant CRLF before hashing normalizedData := normalizeToRFCCompliantCRLF(data) - // Display status that we're hashing large document if len(normalizedData) > 1024*1024 { // > 1MB g.statusLabel.SetText("Hashing large document (" + formatByteSize(len(normalizedData)) + ")...") g.window.Canvas().Refresh(g.statusLabel) } - sig, algo, err := g.signDataInternal(pinGuard.Bytes(), normalizedData) if err != nil { return "", fmt.Errorf("signing failed: %v", err) } - // Ensure clean separation with CRLF sep := "\r\n" if len(normalizedData) > 0 { @@ -413,7 +391,6 @@ func (g *GUI) signData(data []byte, pin string) (string, error) { sep = "\n" } } - return string(normalizedData) + sep + "-----BEGIN " + algo + " SIGNATURE-----" + sep + formatSignatureRFC(sig) + @@ -428,33 +405,27 @@ func (g *GUI) signDataInternal(pin, data []byte) (string, string, error) { return "", "", err } defer yk.Close() - cert, err := yk.Certificate(piv.SlotSignature) if err != nil { return "", "", fmt.Errorf("failed to get certificate from signature slot: %v", err) } - // Handle Ed25519 signing if ed25519PubKey, ok := cert.PublicKey.(ed25519.PublicKey); ok { // Ed25519 signs the hash of the data, not the raw data (YubiKey requirement) hash := sha256.Sum256(data) return g.signEd25519Data(string(pin), hash[:], ed25519PubKey, yk) } - // Handle ECDSA signing pubKey, ok := cert.PublicKey.(*ecdsa.PublicKey) if !ok { return "", "", fmt.Errorf("public key is not ECDSA or Ed25519") } - // Algorithm name is "ECCP256", not "P-256" algorithm, exists := curveToAlgorithm[pubKey.Curve] if !exists { return "", "", fmt.Errorf("unsupported curve: %v", pubKey.Curve) } - hashFunc := curveToHash[pubKey.Curve] - // Create hash of the data for ECDSA signing var digest []byte switch hashFunc { @@ -469,37 +440,30 @@ func (g *GUI) signDataInternal(pin, data []byte) (string, string, error) { default: return "", "", fmt.Errorf("unsupported hash algorithm for curve") } - auth := piv.KeyAuth{PIN: string(pin)} priv, err := yk.PrivateKey(piv.SlotSignature, cert.PublicKey, auth) if err != nil { return "", "", fmt.Errorf("failed to get private key: %v", err) } - signer, ok := priv.(crypto.Signer) if !ok { return "", "", fmt.Errorf("key does not implement crypto.Signer") } - asn1sig, err := signer.Sign(rand.Reader, digest, nil) if err != nil { return "", "", fmt.Errorf("signing failed: %v", err) } - var sig ecSignature if _, err := asn1.Unmarshal(asn1sig, &sig); err != nil { return "", "", fmt.Errorf("ASN.1 unmarshal failed: %v", err) } - curveSize := (pubKey.Curve.Params().BitSize + 7) / 8 - // Build combined signature: X || Y || R || S (all padded to curveSize) var raw []byte raw = append(raw, safePad(pubKey.X.Bytes(), curveSize)...) raw = append(raw, safePad(pubKey.Y.Bytes(), curveSize)...) raw = append(raw, safePad(sig.R.Bytes(), curveSize)...) raw = append(raw, safePad(sig.S.Bytes(), curveSize)...) - return hex.EncodeToString(raw), algorithm, nil } @@ -510,17 +474,14 @@ func (g *GUI) signEd25519Data(pin string, hash []byte, pubKey ed25519.PublicKey, if err != nil { return "", "", fmt.Errorf("failed to get private key: %v", err) } - signer, ok := priv.(crypto.Signer) if !ok { return "", "", fmt.Errorf("key does not implement crypto.Signer") } - signature, err := signer.Sign(rand.Reader, hash, crypto.Hash(0)) if err != nil { return "", "", fmt.Errorf("Ed25519 signing failed: %v", err) } - combined := append(pubKey, signature...) return hex.EncodeToString(combined), AlgorithmED25519, nil } @@ -529,17 +490,14 @@ func (g *GUI) signEd25519Data(pin string, hash []byte, pubKey ed25519.PublicKey, func (g *GUI) verifyData(data []byte) error { // Normalize input to handle both LF and CRLF s := string(normalizeToRFCCompliantCRLF(data)) - var algorithm string var beg, end string - // Try to find BEGIN/END block with CRLF or LF for algo := range supportedAlgorithms { begCRLF := "\r\n-----BEGIN " + algo + " SIGNATURE-----\r\n" endCRLF := "-----END " + algo + " SIGNATURE-----\r\n" begLF := "\n-----BEGIN " + algo + " SIGNATURE-----\n" endLF := "-----END " + algo + " SIGNATURE-----\n" - if strings.Contains(s, begCRLF) { algorithm = algo beg = begCRLF @@ -552,35 +510,29 @@ func (g *GUI) verifyData(data []byte) error { break } } - if algorithm == "" { g.showErrorPopup("No supported signature found", []byte{}, "") return fmt.Errorf("no supported signature block found") } - i := strings.Index(s, beg) j := strings.Index(s, end) if i == -1 || j == -1 || j <= i { g.showErrorPopup("Invalid signature format", []byte{}, algorithm) return fmt.Errorf("invalid signature block format") } - originalMessage := []byte(s[:i]) hexPart := s[i+len(beg) : j] hexPart = regexp.MustCompile(`[\r\n\s\t]+`).ReplaceAllString(hexPart, "") - combined, err := hex.DecodeString(hexPart) if err != nil { g.showErrorPopup("Hex decoding failed", []byte{}, algorithm) return fmt.Errorf("hex decode failed: %v", err) } - // Status for large files if len(originalMessage) > 1024*1024 { g.statusLabel.SetText("Verifying large document (" + formatByteSize(len(originalMessage)) + ")...") g.window.Canvas().Refresh(g.statusLabel) } - var verificationErr error switch algorithm { case AlgorithmED25519: @@ -591,19 +543,16 @@ func (g *GUI) verifyData(data []byte) error { default: verificationErr = fmt.Errorf("unsupported algorithm: %s", algorithm) } - if verificationErr != nil { publicKeyBytes, _ := extractPublicKeyFromSignature(combined, algorithm) g.showErrorPopup("Signature verification failed: "+verificationErr.Error(), publicKeyBytes, algorithm) return verificationErr } - publicKeyBytes, err := extractPublicKeyFromSignature(combined, algorithm) if err != nil { g.showErrorPopup("Error extracting public key: "+err.Error(), []byte{}, algorithm) return err } - // Successfully verified - show identicon from public key (hashed!) g.showSuccessPopup(publicKeyBytes, algorithm) return nil @@ -618,7 +567,6 @@ func extractPublicKeyFromSignature(combined []byte, algorithm string) ([]byte, e } // Return only the public key (first 32 bytes) return combined[:Ed25519PublicKeySize], nil - case AlgorithmECCP256, AlgorithmECCP384: var curve elliptic.Curve switch algorithm { @@ -629,16 +577,13 @@ func extractPublicKeyFromSignature(combined []byte, algorithm string) ([]byte, e default: return nil, fmt.Errorf("unsupported ECDSA algorithm: %s", algorithm) } - curveSize := (curve.Params().BitSize + 7) / 8 expectedBytes := 4 * curveSize if len(combined) != expectedBytes { return nil, fmt.Errorf("invalid signature block size: expected %d, got %d", expectedBytes, len(combined)) } - // Return only the public key (X || Y) return combined[:2*curveSize], nil - default: return nil, fmt.Errorf("unsupported algorithm: %s", algorithm) } @@ -649,14 +594,11 @@ func (g *GUI) verifyEd25519(dataHash, combined []byte) error { if len(combined) != Ed25519CombinedSize { return fmt.Errorf("invalid Ed25519 signature block") } - publicKey := combined[:Ed25519PublicKeySize] signature := combined[Ed25519PublicKeySize:] - if !ed25519.Verify(ed25519.PublicKey(publicKey), dataHash, signature) { return fmt.Errorf("Ed25519 signature verification failed") } - return nil } @@ -664,7 +606,6 @@ func (g *GUI) verifyEd25519(dataHash, combined []byte) error { func (g *GUI) verifyECDSA(data, combined []byte, algorithm string) error { var curve elliptic.Curve var hashFunc crypto.Hash - switch algorithm { case AlgorithmECCP256: curve = elliptic.P256() @@ -675,28 +616,23 @@ func (g *GUI) verifyECDSA(data, combined []byte, algorithm string) error { default: return fmt.Errorf("unsupported ECDSA algorithm: %s", algorithm) } - curveSize := (curve.Params().BitSize + 7) / 8 expectedBytes := 4 * curveSize if len(combined) != expectedBytes { return fmt.Errorf("invalid signature block size: expected %d, got %d", expectedBytes, len(combined)) } - X := new(big.Int).SetBytes(safePad(combined[0:curveSize], curveSize)) Y := new(big.Int).SetBytes(safePad(combined[curveSize:2*curveSize], curveSize)) R := new(big.Int).SetBytes(safePad(combined[2*curveSize:3*curveSize], curveSize)) S := new(big.Int).SetBytes(safePad(combined[3*curveSize:], curveSize)) - if !curve.IsOnCurve(X, Y) { return fmt.Errorf("public key point (X,Y) is not on the curve %s", curve.Params().Name) } - pub := &ecdsa.PublicKey{ Curve: curve, X: X, Y: Y, } - var digest []byte switch hashFunc { case crypto.SHA256: @@ -708,11 +644,9 @@ func (g *GUI) verifyECDSA(data, combined []byte, algorithm string) error { h.Write(data) digest = h.Sum(nil) } - if !ecdsa.Verify(pub, digest, R, S) { return fmt.Errorf("signature verification failed") } - return nil } @@ -736,7 +670,6 @@ func extractPublicKeyDisplayBytes(combined []byte, algorithm string) ([]byte, er } // Return full 32 bytes — no stripping return combined[:Ed25519PublicKeySize], nil - case AlgorithmECCP256, AlgorithmECCP384: var curve elliptic.Curve switch algorithm { @@ -747,28 +680,22 @@ func extractPublicKeyDisplayBytes(combined []byte, algorithm string) ([]byte, er default: return nil, fmt.Errorf("unsupported ECDSA algorithm: %s", algorithm) } - curveSize := (curve.Params().BitSize + 7) / 8 expectedBytes := 4 * curveSize if len(combined) != expectedBytes { return nil, fmt.Errorf("invalid signature block size: expected %d, got %d", expectedBytes, len(combined)) } - // Extract X and Y with leading zeros (as stored) XBytes := combined[0:curveSize] YBytes := combined[curveSize : 2*curveSize] - // Strip leading zeros for display — but keep at least one byte! XStripped := stripLeadingZeros(XBytes) YStripped := stripLeadingZeros(YBytes) - // Concatenate stripped X and Y for display/hashing result := make([]byte, 0, len(XStripped)+len(YStripped)) result = append(result, XStripped...) result = append(result, YStripped...) - return result, nil - default: return nil, fmt.Errorf("unsupported algorithm: %s", algorithm) } @@ -780,20 +707,15 @@ func (g *GUI) showSuccessPopup(publicKeyBytes []byte, algorithm string) { if err != nil { displayBytes = publicKeyBytes } - hexString := hex.EncodeToString(displayBytes) hash := sha256.Sum256([]byte(hexString)) - identicon := NewClassicIdenticon(hash[:]) img := identicon.Generate() - fyneImg := canvas.NewImageFromImage(img) fyneImg.FillMode = canvas.ImageFillContain fyneImg.SetMinSize(fyne.NewSize(128, 128)) - successLabel := widget.NewLabel("Signature is valid") successLabel.Alignment = fyne.TextAlignCenter - copyBtn := widget.NewButton("Copy Signature Component", func() { clipboard := g.app.Clipboard() if clipboard != nil { @@ -804,16 +726,13 @@ func (g *GUI) showSuccessPopup(publicKeyBytes []byte, algorithm string) { }) } }) - content := container.NewVBox( container.NewCenter(fyneImg), container.NewCenter(successLabel), container.NewCenter(copyBtn), ) - d := dialog.NewCustom("", "OK", content, g.window) d.Show() - } // showErrorPopup shows an error popup with identicon for failed verification @@ -826,7 +745,6 @@ func (g *GUI) showErrorPopup(message string, publicKeyBytes []byte, algorithm st d.Show() return } - //d := dialog.NewCustom("", "OK", g.window) //d.Show() } @@ -837,28 +755,23 @@ func (g *GUI) encryptData(data []byte, pubKeyFile string) (string, error) { if err != nil { return "", fmt.Errorf("failed to load public key: %v", err) } - aesKeyGuard := memguard.NewBuffer(32) defer aesKeyGuard.Destroy() if _, err := rand.Read(aesKeyGuard.Bytes()); err != nil { return "", fmt.Errorf("failed to generate AES key: %v", err) } - encryptedKey, err := rsa.EncryptPKCS1v15(rand.Reader, pubKey, aesKeyGuard.Bytes()) if err != nil { return "", fmt.Errorf("RSA encryption failed: %v", err) } defer memguard.WipeBytes(encryptedKey) - encryptedData, err := encryptAES(data, aesKeyGuard.Bytes()) if err != nil { return "", fmt.Errorf("AES encryption failed: %v", err) } defer memguard.WipeBytes(encryptedData) - combined := append(encryptedKey, encryptedData...) defer memguard.WipeBytes(combined) - base64Str := base64.StdEncoding.EncodeToString(combined) return formatBase64RFC(base64Str), nil } @@ -867,72 +780,58 @@ func (g *GUI) encryptData(data []byte, pubKeyFile string) (string, error) { func (g *GUI) decryptData(data []byte, pin string) ([]byte, error) { pinGuard := memguard.NewBufferFromBytes([]byte(pin)) defer pinGuard.Destroy() - s := string(data) s = strings.ReplaceAll(s, "\r\n", "") s = strings.ReplaceAll(s, " ", "") - combined, err := base64.StdEncoding.DecodeString(s) if err != nil { return nil, fmt.Errorf("base64 decode failed: %v", err) } defer memguard.WipeBytes(combined) - yk, err := openYubiKey(0) if err != nil { return nil, fmt.Errorf("failed to open YubiKey: %v", err) } defer yk.Close() - cert, err := yk.Certificate(piv.SlotKeyManagement) if err != nil { return nil, fmt.Errorf("failed to get certificate from slot 9d: %v", err) } - rsaPubKey, ok := cert.PublicKey.(*rsa.PublicKey) if !ok { return nil, fmt.Errorf("certificate does not contain RSA public key") } - if err := checkRSASecurity(rsaPubKey, "on YubiKey"); err != nil { return nil, err } - keySize := rsaPubKey.Size() if len(combined) < keySize { return nil, fmt.Errorf("ciphertext too short") } - encryptedKey := combined[:keySize] encryptedData := combined[keySize:] defer memguard.WipeBytes(encryptedKey) - auth := piv.KeyAuth{PIN: pin} priv, err := yk.PrivateKey(piv.SlotKeyManagement, cert.PublicKey, auth) if err != nil { return nil, fmt.Errorf("failed to get private key: %v", err) } - decrypter, ok := priv.(crypto.Decrypter) if !ok { return nil, fmt.Errorf("private key does not support decryption") } - decryptedPayload, err := decrypter.Decrypt(rand.Reader, encryptedKey, nil) if err != nil { return nil, fmt.Errorf("RSA decryption failed: %v", err) } defer memguard.WipeBytes(decryptedPayload) - if len(decryptedPayload) != 32 { return nil, fmt.Errorf("invalid AES key size") } - decryptedData, err := decryptAES(encryptedData, decryptedPayload) if err != nil { return nil, fmt.Errorf("AES decryption failed: %v", err) } - return decryptedData, nil } @@ -987,14 +886,13 @@ func formatByteSize(bytes int) string { return fmt.Sprintf("%.1f %cB", float64(bytes)/float64(div), "KMGTPE"[exp]) } -// securePadMessage adds ISO/IEC 7816-4 padding to align data to 4096-byte blocks +// securePadMessage adds ISO/IEC 7816-4 padding to align data to 1024-byte blocks func securePadMessage(data []byte) []byte { - const blockSize = 4096 + const blockSize = 1024 // Changed from 4096 to 1024 paddingNeeded := blockSize - (len(data) % blockSize) if paddingNeeded == blockSize { return data } - paddedData := make([]byte, len(data)+paddingNeeded) copy(paddedData, data) paddedData[len(data)] = 0x80 @@ -1006,11 +904,9 @@ func secureUnpadMessage(data []byte) ([]byte, error) { if len(data) == 0 { return nil, errors.New("cannot unpad empty data") } - - if len(data)%4096 != 0 { + if len(data)%1024 != 0 { // Changed from 4096 to 1024 return nil, errors.New("invalid block size for unpadding") } - lastIndex := -1 for i := len(data) - 1; i >= 0; i-- { if data[i] == 0x80 { @@ -1021,11 +917,9 @@ func secureUnpadMessage(data []byte) ([]byte, error) { return nil, errors.New("invalid padding format: unexpected non-zero byte") } } - if lastIndex == -1 { return nil, errors.New("no padding marker found") } - return data[:lastIndex], nil } @@ -1035,13 +929,10 @@ func (g *GUI) onPad() { g.statusLabel.SetText("Error: No text to pad") return } - paddedData := securePadMessage([]byte(input)) base64String := base64.StdEncoding.EncodeToString(paddedData) formattedBase64 := formatBase64RFC(base64String) - g.textArea.SetText(formattedBase64) - originalLen := len(input) paddedLen := len(paddedData) g.statusLabel.SetText(fmt.Sprintf("✓ Padded: %d -> %d bytes (Base64)", originalLen, paddedLen)) @@ -1053,19 +944,16 @@ func (g *GUI) onUnpad() { g.statusLabel.SetText("Error: No text to unpad") return } - binaryData, err := base64.StdEncoding.DecodeString(input) if err != nil { g.statusLabel.SetText("Unpadding failed: Invalid Base64 data") return } - unpaddedData, err := secureUnpadMessage(binaryData) if err != nil { g.statusLabel.SetText("Unpadding failed: " + err.Error()) return } - g.textArea.SetText(string(unpaddedData)) g.statusLabel.SetText("✓ Message unpadded successfully") } @@ -1073,19 +961,15 @@ func (g *GUI) onUnpad() { // checkRSASecurity validates RSA key size func checkRSASecurity(pubKey *rsa.PublicKey, context string) error { keySize := pubKey.N.BitLen() - if keySize < minRSABits { return fmt.Errorf("insecure %d-bit RSA key %s - minimum is %d-bit", keySize, context, minRSABits) } - if _, supported := supportedRSASizes[keySize]; !supported { fmt.Fprintf(os.Stderr, "WARNING: %d-bit RSA key %s - supported sizes are 2048, 3072, 4096 bits\n", keySize, context) } - if keySize == 1024 { fmt.Fprintf(os.Stderr, "CRITICAL WARNING: 1024-bit RSA keys %s are insecure and should not be used!\n", context) } - return nil } @@ -1096,12 +980,10 @@ func loadRSAPublicKey(filename string) (*rsa.PublicKey, error) { return nil, fmt.Errorf("failed to read public key file: %v", err) } defer memguard.WipeBytes(data) - block, _ := pem.Decode(data) if block == nil { return nil, fmt.Errorf("no PEM data found in file") } - switch block.Type { case "CERTIFICATE": cert, err := x509.ParseCertificate(block.Bytes) @@ -1116,7 +998,6 @@ func loadRSAPublicKey(filename string) (*rsa.PublicKey, error) { return nil, err } return pubKey, nil - case "PUBLIC KEY": pubInterface, err := x509.ParsePKIXPublicKey(block.Bytes) if err != nil { @@ -1130,7 +1011,6 @@ func loadRSAPublicKey(filename string) (*rsa.PublicKey, error) { return nil, err } return pubKey, nil - case "RSA PUBLIC KEY": pubKey, err := x509.ParsePKCS1PublicKey(block.Bytes) if err != nil { @@ -1140,7 +1020,6 @@ func loadRSAPublicKey(filename string) (*rsa.PublicKey, error) { return nil, err } return pubKey, nil - default: return nil, fmt.Errorf("unsupported PEM type: %s, expected CERTIFICATE, PUBLIC KEY or RSA PUBLIC KEY", block.Type) } @@ -1152,17 +1031,14 @@ func encryptAES(data, key []byte) ([]byte, error) { if err != nil { return nil, err } - gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } - nonce := make([]byte, gcm.NonceSize()) if _, err := rand.Read(nonce); err != nil { return nil, err } - ciphertext := gcm.Seal(nonce, nonce, data, nil) return ciphertext, nil } @@ -1173,23 +1049,19 @@ func decryptAES(data, key []byte) ([]byte, error) { if err != nil { return nil, err } - gcm, err := cipher.NewGCM(block) if err != nil { return nil, err } - nonceSize := gcm.NonceSize() if len(data) < nonceSize { return nil, fmt.Errorf("ciphertext too short") } - nonce, ciphertext := data[:nonceSize], data[nonceSize:] plaintext, err := gcm.Open(nil, nonce, ciphertext, nil) if err != nil { return nil, err } - return plaintext, nil } @@ -1202,7 +1074,6 @@ func openYubiKey(index int) (*piv.YubiKey, error) { if len(cards) == 0 { return nil, fmt.Errorf("no smart card found") } - count := 0 for _, card := range cards { if strings.Contains(strings.ToLower(card), "yubikey") { @@ -1263,7 +1134,6 @@ func (identicon *ClassicIdenticon) foreground() color.Color { if len(identicon.source) < 32 { return color.RGBA{0, 0, 0, 255} } - // Primary color index (4 bits → 16 colors) - EXACTLY like identicons program colorIndex := 0 for i := 0; i < 4; i++ { @@ -1272,7 +1142,6 @@ func (identicon *ClassicIdenticon) foreground() color.Color { } } colorIndex %= 16 - // Vibrant color palette — 16 beautiful, distinct colors (SAME as identicons program) palette := []color.RGBA{ {0x00, 0xbf, 0x93, 0xff}, // turquoise @@ -1292,7 +1161,6 @@ func (identicon *ClassicIdenticon) foreground() color.Color { {0x24, 0xc3, 0x33, 0xff}, // greenIntense {0x1c, 0xab, 0xbb, 0xff}, // lightBlueIntense } - return palette[colorIndex] } @@ -1301,7 +1169,6 @@ func (identicon *ClassicIdenticon) secondaryColor() color.Color { if len(identicon.source) < 32 { return color.RGBA{100, 100, 100, 255} } - // Secondary color index (4 bits → 16 colors) - EXACTLY like identicons program colorIndex := 0 for i := 0; i < 4; i++ { @@ -1310,7 +1177,6 @@ func (identicon *ClassicIdenticon) secondaryColor() color.Color { } } colorIndex %= 16 - // Secondary color palette — 16 distinct colors (SAME as identicons program) palette := []color.RGBA{ {0x34, 0x49, 0x5e, 0xff}, // darkBlue @@ -1330,7 +1196,6 @@ func (identicon *ClassicIdenticon) secondaryColor() color.Color { {0x23, 0x23, 0x23, 0xff}, // lightBlackIntense {0x7e, 0x8c, 0x8d, 0xff}, // greyIntense } - return palette[colorIndex] } @@ -1339,7 +1204,6 @@ func (identicon *ClassicIdenticon) hslToRgb(h, s, l float32) color.Color { hue := h / 360.0 sat := s / 100.0 lum := l / 100.0 - var b float32 if lum <= 0.5 { b = lum * (sat + 1.0) @@ -1347,11 +1211,9 @@ func (identicon *ClassicIdenticon) hslToRgb(h, s, l float32) color.Color { b = lum + sat - lum*sat } a := lum*2.0 - b - red := identicon.hueToRgb(a, b, hue+1.0/3.0) green := identicon.hueToRgb(a, b, hue) blue := identicon.hueToRgb(a, b, hue-1.0/3.0) - return color.RGBA{ R: uint8(math.Round(float64(red * 255.0))), G: uint8(math.Round(float64(green * 255.0))), @@ -1367,7 +1229,6 @@ func (identicon *ClassicIdenticon) hueToRgb(a, b, hue float32) float32 { } else if hue >= 1.0 { hue -= 1.0 } - switch { case hue < 1.0/6.0: return a + (b-a)*6.0*hue @@ -1387,11 +1248,9 @@ func (identicon *ClassicIdenticon) drawRect(img *image.RGBA, x0, y0, x1, y1 int, y0 = max(y0, rect.Min.Y) x1 = min(x1, rect.Max.X) y1 = min(y1, rect.Max.Y) - if x0 >= x1 || y0 >= y1 { return } - r, g, b, a := c.RGBA() rgba := color.RGBA{ R: uint8(r >> 8), @@ -1399,7 +1258,6 @@ func (identicon *ClassicIdenticon) drawRect(img *image.RGBA, x0, y0, x1, y1 int, B: uint8(b >> 8), A: uint8(a >> 8), } - for y := y0; y < y1; y++ { rowStart := img.PixOffset(x0, y) for x := 0; x < x1-x0; x++ { @@ -1417,34 +1275,29 @@ func (identicon *ClassicIdenticon) drawRect(img *image.RGBA, x0, y0, x1, y1 int, func (identicon *ClassicIdenticon) generatePixelPattern() ([]bool, []bool) { primary := make([]bool, 25) secondary := make([]bool, 25) - // Use bits 0-14 for primary pattern (15 bits) bitIndex := 0 for row := 0; row < 5; row++ { for col := 0; col < 3; col++ { paint := identicon.getBit(bitIndex) bitIndex++ - ix := row*5 + col mirrorIx := row*5 + (4 - col) primary[ix] = paint primary[mirrorIx] = paint } } - // Use bits 15-29 for secondary pattern (next 15 bits) for row := 0; row < 5; row++ { for col := 0; col < 3; col++ { paint := identicon.getBit(bitIndex) bitIndex++ - ix := row*5 + col mirrorIx := row*5 + (4 - col) secondary[ix] = paint secondary[mirrorIx] = paint } } - return primary, secondary } @@ -1455,11 +1308,9 @@ func (identicon *ClassicIdenticon) Generate() image.Image { spriteSize = 5 margin = (256 - pixelSize*spriteSize) / 2 ) - primaryColor := identicon.foreground() secondaryColor := identicon.secondaryColor() img := image.NewRGBA(image.Rect(0, 0, identicon.size, identicon.size)) - // Background adapts to theme — use bits 252-253 to pick variation (2 bits → 3 options) bgChoice := 0 for i := 0; i < 2; i++ { // Nur 2 Bits verwenden wie in identicons program @@ -1468,7 +1319,6 @@ func (identicon *ClassicIdenticon) Generate() image.Image { } } bgChoice %= 3 - lightBackgrounds := []color.RGBA{ {255, 255, 255, 255}, // pure white {243, 245, 247, 255}, // light1 @@ -1479,23 +1329,19 @@ func (identicon *ClassicIdenticon) Generate() image.Image { {45, 62, 80, 255}, // darkBlueIntense {57, 57, 57, 255}, // dark2 } - var bg color.RGBA if fyne.CurrentApp().Settings().ThemeVariant() == theme.VariantDark { bg = darkBackgrounds[bgChoice] } else { bg = lightBackgrounds[bgChoice] } - for i := 0; i < len(img.Pix); i += 4 { img.Pix[i] = bg.R img.Pix[i+1] = bg.G img.Pix[i+2] = bg.B img.Pix[i+3] = bg.A } - primaryPixels, secondaryPixels := identicon.generatePixelPattern() - // Draw secondary pixels first (background layer) for row := 0; row < spriteSize; row++ { for col := 0; col < spriteSize; col++ { @@ -1506,7 +1352,6 @@ func (identicon *ClassicIdenticon) Generate() image.Image { } } } - // Draw primary pixels on top (foreground layer) for row := 0; row < spriteSize; row++ { for col := 0; col < spriteSize; col++ { @@ -1517,7 +1362,6 @@ func (identicon *ClassicIdenticon) Generate() image.Image { } } } - return img } @@ -1528,11 +1372,9 @@ func (identicon *ClassicIdenticon) GenerateForExport(transparent bool) image.Ima spriteSize = 5 margin = (256 - pixelSize*spriteSize) / 2 ) - primaryColor := identicon.foreground() secondaryColor := identicon.secondaryColor() img := image.NewRGBA(image.Rect(0, 0, identicon.size, identicon.size)) - // Set export background var bg color.RGBA if transparent { @@ -1546,7 +1388,6 @@ func (identicon *ClassicIdenticon) GenerateForExport(transparent bool) image.Ima } } bgChoice %= 3 - lightBackgrounds := []color.RGBA{ {255, 255, 255, 255}, {243, 245, 247, 255}, @@ -1554,16 +1395,13 @@ func (identicon *ClassicIdenticon) GenerateForExport(transparent bool) image.Ima } bg = lightBackgrounds[bgChoice] } - for i := 0; i < len(img.Pix); i += 4 { img.Pix[i] = bg.R img.Pix[i+1] = bg.G img.Pix[i+2] = bg.B img.Pix[i+3] = bg.A } - primaryPixels, secondaryPixels := identicon.generatePixelPattern() - // Draw secondary pixels first for row := 0; row < spriteSize; row++ { for col := 0; col < spriteSize; col++ { @@ -1574,7 +1412,6 @@ func (identicon *ClassicIdenticon) GenerateForExport(transparent bool) image.Ima } } } - // Draw primary pixels on top for row := 0; row < spriteSize; row++ { for col := 0; col < spriteSize; col++ { @@ -1585,21 +1422,5 @@ func (identicon *ClassicIdenticon) GenerateForExport(transparent bool) image.Ima } } } - return img } - -// Helper functions -func min(a, b int) int { - if a < b { - return a - } - return b -} - -func max(a, b int) int { - if a > b { - return a - } - return b -} -- cgit v1.2.3