diff --git a/Makefile b/Makefile index 184169a3372..d960f275de6 100644 --- a/Makefile +++ b/Makefile @@ -6,6 +6,7 @@ tidy: ./.make_tidy.sh # execute "golangci-lint" to check code style +# go install github.com/golangci/golangci-lint/v2/cmd/golangci-lint@latest .PHONY: lint lint: golangci-lint run -c .golangci.yml diff --git a/README.MD b/README.MD index 8eebc5ec972..74a5d289d0d 100644 --- a/README.MD +++ b/README.MD @@ -24,6 +24,12 @@ English | [简体中文](README.zh_CN.MD) A powerful framework for faster, easier, and more efficient project development. +## Installation + +```bash +go get -u github.com/gogf/gf/v2 +``` + ## Documentation - Official Site: [https://goframe.org](https://goframe.org) @@ -32,6 +38,7 @@ A powerful framework for faster, easier, and more efficient project development. - Mirror Site: [Github Pages](https://pages.goframe.org) - Mirror Site: [Offline Docs](https://github.com/gogf/goframe.org-pdf?tab=readme-ov-file#%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC) - GoDoc API: [https://pkg.go.dev/github.com/gogf/gf/v2](https://pkg.go.dev/github.com/gogf/gf/v2) +- Doc Source: [https://github.com/gogf/gf-site](https://github.com/gogf/gf-site) ## Contributors diff --git a/README.zh_CN.MD b/README.zh_CN.MD index f5eade92a0b..f707b1a97cf 100644 --- a/README.zh_CN.MD +++ b/README.zh_CN.MD @@ -24,6 +24,12 @@ 一个强大的框架,为了更快、更轻松、更高效的项目开发。 +## 安装 + +```bash +go get -u github.com/gogf/gf/v2 +``` + ## 文档 - 官方网站: [https://goframe.org](https://goframe.org) @@ -31,7 +37,8 @@ - 国内镜像: [https://goframe.org.cn](https://goframe.org.cn) - 镜像网站: [Github Pages](https://pages.goframe.org) - 镜像网站: [离线文档](https://github.com/gogf/goframe.org-pdf?tab=readme-ov-file#%E6%9C%80%E6%96%B0%E7%89%88%E6%9C%AC) -- GoDoc API: [https://pkg.go.dev/github.com/gogf/gf/v2](https://pkg.go.dev/github.com/gogf/gf/v2) +- Go包文档: [https://pkg.go.dev/github.com/gogf/gf/v2](https://pkg.go.dev/github.com/gogf/gf/v2) +- 文档源码: [https://github.com/gogf/gf-site](https://github.com/gogf/gf-site) ## 贡献者 diff --git a/crypto/grsa/README.md b/crypto/grsa/README.md new file mode 100644 index 00000000000..1cdfd3e4aea --- /dev/null +++ b/crypto/grsa/README.md @@ -0,0 +1,264 @@ +# GoFrame RSA Package + +Package `grsa` provides useful API for RSA encryption/decryption algorithms within the GoFrame framework. + +## Features + +- Generating RSA key pairs in PKCS#1 and PKCS#8 formats +- Encrypting and decrypting data with various key formats +- Handling Base64 encoded keys +- Detecting private key types +- Plaintext size validation +- **OAEP padding support (recommended for new applications)** + +## Security Considerations + +This package provides two padding schemes for RSA encryption: + +### 1. PKCS#1 v1.5 (Legacy) + +Used by `Encrypt*`, `DecryptPKCS1*`, `DecryptPKCS8*` functions. + +⚠️ **Security Warning**: PKCS#1 v1.5 padding is considered less secure and vulnerable to padding oracle attacks. It is provided for backward compatibility with existing systems. + +### 2. OAEP (Recommended) + +Used by `EncryptOAEP*`, `DecryptOAEP*` functions. + +✅ **Recommended**: OAEP (Optimal Asymmetric Encryption Padding) provides better security guarantees and should be used for all new applications. + +## Quick Start + +### Basic Encryption/Decryption (OAEP - Recommended) + +```go +package main + +import ( + "fmt" + "github.com/gogf/gf/v2/crypto/grsa" +) + +func main() { + // Generate a default RSA key pair (2048 bits) + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + if err != nil { + panic(err) + } + + // Data to encrypt + plainText := []byte("Hello, World!") + + // Encrypt with public key using OAEP (recommended) + cipherText, err := grsa.EncryptOAEP(plainText, publicKey) + if err != nil { + panic(err) + } + + // Decrypt with private key using OAEP + decryptedText, err := grsa.DecryptOAEP(cipherText, privateKey) + if err != nil { + panic(err) + } + + fmt.Println(string(decryptedText)) // Output: Hello, World! +} +``` + +### Legacy Encryption/Decryption (PKCS#1 v1.5) + +```go +package main + +import ( + "fmt" + "github.com/gogf/gf/v2/crypto/grsa" +) + +func main() { + // Generate a default RSA key pair (2048 bits) + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + if err != nil { + panic(err) + } + + // Data to encrypt + plainText := []byte("Hello, World!") + + // Encrypt with public key (PKCS#1 v1.5 - legacy) + cipherText, err := grsa.Encrypt(plainText, publicKey) + if err != nil { + panic(err) + } + + // Decrypt with private key + decryptedText, err := grsa.Decrypt(cipherText, privateKey) + if err != nil { + panic(err) + } + + fmt.Println(string(decryptedText)) // Output: Hello, World! +} +``` + +### Working with Base64 Encoded Keys + +```go +package main + +import ( + "encoding/base64" + "fmt" + "github.com/gogf/gf/v2/crypto/grsa" +) + +func main() { + // Generate a key pair + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + if err != nil { + panic(err) + } + + // Encode keys to Base64 + privateKeyBase64 := base64.StdEncoding.EncodeToString(privateKey) + publicKeyBase64 := base64.StdEncoding.EncodeToString(publicKey) + + // Data to encrypt + plainText := []byte("Hello, Base64 World!") + + // Encrypt with Base64 encoded public key using OAEP (recommended) + cipherTextBase64, err := grsa.EncryptOAEPBase64(plainText, publicKeyBase64) + if err != nil { + panic(err) + } + + // Decrypt with Base64 encoded private key using OAEP + decryptedText, err := grsa.DecryptOAEPBase64(cipherTextBase64, privateKeyBase64) + if err != nil { + panic(err) + } + + fmt.Println(string(decryptedText)) // Output: Hello, Base64 World! +} +``` + +## Functions + +### Key Generation + +- `GenerateKeyPair(bits int)`: Generates a new RSA key pair with the given bits in PKCS#1 format +- `GenerateKeyPairPKCS8(bits int)`: Generates a new RSA key pair with the given bits in PKCS#8 format +- `GenerateDefaultKeyPair()`: Generates a new RSA key pair with default bits (2048) in PKCS#1 format + +### OAEP Encryption/Decryption (Recommended) + +- `EncryptOAEP(plainText, publicKey []byte)`: Encrypts data with public key using OAEP padding (SHA-256) +- `DecryptOAEP(cipherText, privateKey []byte)`: Decrypts data with private key using OAEP padding (SHA-256) +- `EncryptOAEPBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with OAEP and returns base64-encoded result +- `DecryptOAEPBase64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded OAEP data +- `EncryptOAEPWithHash(plainText, publicKey, label []byte, hash hash.Hash)`: Encrypts with custom hash function +- `DecryptOAEPWithHash(cipherText, privateKey, label []byte, hash hash.Hash)`: Decrypts with custom hash function + +### General Encryption/Decryption (Legacy - PKCS#1 v1.5) + +- `Encrypt(plainText, publicKey []byte)`: Encrypts data with public key (auto-detect format) +- `Decrypt(cipherText, privateKey []byte)`: Decrypts data with private key (auto-detect format) +- `EncryptBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with base64-encoded public key and returns base64-encoded result +- `DecryptBase64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with base64-encoded private key + +### PKCS#1 Specific Functions (Legacy) + +- `EncryptPKCS1(plainText, publicKey []byte)`: Encrypts data with PKCS#1 format public key +- `DecryptPKCS1(cipherText, privateKey []byte)`: Decrypts data with PKCS#1 format private key +- `EncryptPKCS1Base64(plainText []byte, publicKeyBase64 string)`: Encrypts data with PKCS#1 public key and returns base64-encoded result +- `DecryptPKCS1Base64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with PKCS#1 private key + +### PKIX Specific Functions (Legacy) + +PKIX (X.509) is the standard format for public keys, used with PKCS#8 private keys. + +- `EncryptPKIX(plainText, publicKey []byte)`: Encrypts data with PKIX format public key +- `EncryptPKIXBase64(plainText []byte, publicKeyBase64 string)`: Encrypts data with PKIX public key and returns base64-encoded result +- `DecryptPKCS8(cipherText, privateKey []byte)`: Decrypts data with PKCS#8 format private key +- `DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64 string)`: Decrypts base64-encoded data with PKCS#8 private key + +### Deprecated Functions + +The following functions are deprecated and will be removed in future versions: + +- `EncryptPKCS8(plainText, publicKey []byte)`: Use `EncryptPKIX` instead +- `EncryptPKCS8Base64(plainText []byte, publicKeyBase64 string)`: Use `EncryptPKIXBase64` instead + +### Utility Functions + +- `GetPrivateKeyType(privateKey []byte)`: Detects the type of private key (PKCS#1 or PKCS#8) +- `GetPrivateKeyTypeBase64(privateKeyBase64 string)`: Detects the type of base64 encoded private key +- `ExtractPKCS1PublicKey(privateKey []byte)`: Extracts PKCS#1 public key from PKCS#1 private key + +## Key Formats + +The package supports two popular RSA key formats: + +1. **PKCS#1**: Traditional RSA key format + - Private key PEM header: `-----BEGIN RSA PRIVATE KEY-----` + - Public key PEM header: `-----BEGIN RSA PUBLIC KEY-----` + +2. **PKCS#8/PKIX**: More modern and flexible key format + - Private key PEM header: `-----BEGIN PRIVATE KEY-----` + - Public key PEM header: `-----BEGIN PUBLIC KEY-----` + +Both formats are supported for encryption and decryption operations, with auto-detection capabilities for general functions. + +### Technical Background: PKCS#8 vs PKIX + +**PKCS#8** is a standard for **private keys** only, not public keys. Public keys use the **PKIX (X.509 SubjectPublicKeyInfo)** format. + +| Format | Private Key PEM Header | Public Key PEM Header | +|--------|------------------------|----------------------| +| PKCS#1 | `RSA PRIVATE KEY` | `RSA PUBLIC KEY` | +| PKCS#8/PKIX | `PRIVATE KEY` | `PUBLIC KEY` | + +When we refer to a "PKCS#8 key pair", it actually means: +- **Private key**: PKCS#8 format (RFC 5208) +- **Public key**: PKIX/SubjectPublicKeyInfo format (RFC 5280, X.509) + +This is why the Go standard library provides `x509.MarshalPKCS8PrivateKey` for private keys but `x509.MarshalPKIXPublicKey` for public keys — there is no `MarshalPKCS8PublicKey` function. + +The deprecated `EncryptPKCS8` function was a misnomer because encryption uses public keys, and public keys are in PKIX format, not PKCS#8. The correct function name is `EncryptPKIX`. + +## Plaintext Size Limit + +RSA encryption has a size limit based on key size and padding scheme. + +### PKCS#1 v1.5 Padding (Legacy) + +- **Max plaintext size = key_size_in_bytes - 11** +- For a 2048-bit key: max 245 bytes +- For a 4096-bit key: max 501 bytes + +### OAEP Padding with SHA-256 (Recommended) + +- **Max plaintext size = key_size_in_bytes - 2 × hash_size - 2** +- For a 2048-bit key with SHA-256: max 190 bytes +- For a 4096-bit key with SHA-256: max 446 bytes + +If you need to encrypt larger data, consider using hybrid encryption (RSA + AES). + +## Error Handling + +All functions return descriptive errors that can be handled using the GoFrame error package (`gerror`). Errors typically include: + +- Invalid key format +- Failed key parsing +- Plaintext too long +- Encryption/decryption failures + +Always check for errors in production code to ensure robust handling of edge cases. + +## Testing + +Run the package tests with: + +```bash +go test -v +``` diff --git a/crypto/grsa/grsa.go b/crypto/grsa/grsa.go new file mode 100644 index 00000000000..51988aa20c1 --- /dev/null +++ b/crypto/grsa/grsa.go @@ -0,0 +1,571 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +// Package grsa provides useful API for RSA encryption/decryption algorithms. +// +// This package includes functionality for: +// - Generating RSA key pairs in PKCS#1 and PKCS#8 formats +// - Encrypting and decrypting data with various key formats +// - Handling Base64 encoded keys +// - Detecting private key types +// +// # Security Considerations +// +// This package provides two padding schemes for RSA encryption: +// +// 1. PKCS#1 v1.5 (legacy): Used by Encrypt*, DecryptPKCS1*, DecryptPKCS8* functions. +// This padding scheme is considered less secure and vulnerable to padding oracle attacks. +// It is provided for backward compatibility with existing systems. +// +// 2. OAEP (recommended): Used by EncryptOAEP*, DecryptOAEP* functions. +// OAEP (Optimal Asymmetric Encryption Padding) is the recommended padding scheme +// for new applications as it provides better security guarantees. +// +// For new implementations, prefer using OAEP functions (EncryptOAEP, DecryptOAEP, etc.). +package grsa + +import ( + "crypto/rand" + "crypto/rsa" + "crypto/sha256" + "crypto/x509" + "encoding/base64" + "encoding/pem" + "hash" + + "github.com/gogf/gf/v2/errors/gcode" + "github.com/gogf/gf/v2/errors/gerror" +) + +const ( + // DefaultRSAKeyBits is the default bit size for RSA key generation + DefaultRSAKeyBits = 2048 + + // KeyTypePKCS1 represents PKCS#1 format private key + KeyTypePKCS1 = "PKCS#1" + // KeyTypePKCS8 represents PKCS#8 format private key + KeyTypePKCS8 = "PKCS#8" + + // PEM block types + pemTypeRSAPrivateKey = "RSA PRIVATE KEY" // PKCS#1 private key + pemTypePrivateKey = "PRIVATE KEY" // PKCS#8 private key + pemTypeRSAPublicKey = "RSA PUBLIC KEY" // PKCS#1 public key + pemTypePublicKey = "PUBLIC KEY" // PKIX public key +) + +// Encrypt encrypts data with public key using PKCS#1 v1.5 padding (auto-detect format). +// The publicKey can be either PKCS#1 or PKCS#8 (PKIX) format. +// +// Note: RSA encryption has a size limit based on key size. +// For PKCS#1 v1.5 padding, max plaintext size = key_size_in_bytes - 11. +// For example, a 2048-bit key can encrypt at most 245 bytes. +// +// Security Warning: PKCS#1 v1.5 padding is vulnerable to padding oracle attacks. +// For new applications, consider using EncryptOAEP instead. +func Encrypt(plainText, publicKey []byte) ([]byte, error) { + block, _ := pem.Decode(publicKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid public key") + } + + // Try PKCS#8 (PKIX) first + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + // Try PKCS#1 + pub, err = x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse public key") + } + } + + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA public key") + } + + // Validate plaintext size for PKCS#1 v1.5 padding + maxSize := rsaPub.Size() - 11 + if len(plainText) > maxSize { + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, + "plaintext too long: max %d bytes for this key, got %d bytes", maxSize, len(plainText)) + } + + return rsa.EncryptPKCS1v15(rand.Reader, rsaPub, plainText) +} + +// Decrypt decrypts data with private key using PKCS#1 v1.5 padding (auto-detect format). +// The privateKey can be either PKCS#1 or PKCS#8 format. +// +// Security Warning: PKCS#1 v1.5 padding is vulnerable to padding oracle attacks. +// For new applications, consider using DecryptOAEP instead. +func Decrypt(cipherText, privateKey []byte) ([]byte, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + // Try PKCS#8 first + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + // Try PKCS#1 + priv, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse private key") + } + } + + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA private key") + } + + return rsa.DecryptPKCS1v15(rand.Reader, rsaPriv, cipherText) +} + +// EncryptBase64 encrypts data with base64-encoded public key (auto-detect format) +// and returns base64-encoded result. +func EncryptBase64(plainText []byte, publicKeyBase64 string) (string, error) { + publicKey, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode public key") + } + + encrypted, err := Encrypt(plainText, publicKey) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +// DecryptBase64 decrypts base64-encoded data with base64-encoded private key (auto-detect format). +func DecryptBase64(cipherTextBase64, privateKeyBase64 string) ([]byte, error) { + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode private key") + } + + cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode cipher text") + } + + return Decrypt(cipherText, privateKey) +} + +// EncryptPKIX encrypts data with public key in PKIX (X.509) format. +// PKIX is the standard format for public keys, often referred to as "PKCS#8 public key". +// +// Note: RSA encryption has a size limit based on key size. +// For PKCS#1 v1.5 padding, max plaintext size = key_size_in_bytes - 11. +func EncryptPKIX(plainText, publicKey []byte) ([]byte, error) { + block, _ := pem.Decode(publicKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid public key") + } + + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse PKIX public key") + } + + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA public key") + } + + // Validate plaintext size for PKCS#1 v1.5 padding + maxSize := rsaPub.Size() - 11 + if len(plainText) > maxSize { + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, + "plaintext too long: max %d bytes for this key, got %d bytes", maxSize, len(plainText)) + } + + return rsa.EncryptPKCS1v15(rand.Reader, rsaPub, plainText) +} + +// EncryptPKCS8 is an alias for EncryptPKIX for backward compatibility. +// +// Deprecated: Use EncryptPKIX instead. Public keys use PKIX format, not PKCS#8. +func EncryptPKCS8(plainText, publicKey []byte) ([]byte, error) { + return EncryptPKIX(plainText, publicKey) +} + +// EncryptPKCS1 encrypts data with public key in PKCS#1 format. +// +// Note: RSA encryption has a size limit based on key size. +// For PKCS#1 v1.5 padding, max plaintext size = key_size_in_bytes - 11. +func EncryptPKCS1(plainText, publicKey []byte) ([]byte, error) { + block, _ := pem.Decode(publicKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid public key") + } + + pub, err := x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse PKCS#1 public key") + } + + // Validate plaintext size for PKCS#1 v1.5 padding + maxSize := pub.Size() - 11 + if len(plainText) > maxSize { + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, + "plaintext too long: max %d bytes for this key, got %d bytes", maxSize, len(plainText)) + } + + return rsa.EncryptPKCS1v15(rand.Reader, pub, plainText) +} + +// DecryptPKCS8 decrypts data with private key by PKCS#8 format. +func DecryptPKCS8(cipherText, privateKey []byte) ([]byte, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse PKCS#8 private key") + } + + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA private key") + } + + return rsa.DecryptPKCS1v15(rand.Reader, rsaPriv, cipherText) +} + +// DecryptPKCS1 decrypts data with private key by PKCS#1 format. +func DecryptPKCS1(cipherText, privateKey []byte) ([]byte, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse private key") + } + + return rsa.DecryptPKCS1v15(rand.Reader, priv, cipherText) +} + +// EncryptPKIXBase64 encrypts data with PKIX public key and returns base64-encoded result. +func EncryptPKIXBase64(plainText []byte, publicKeyBase64 string) (string, error) { + publicKey, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode public key") + } + + encrypted, err := EncryptPKIX(plainText, publicKey) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +// EncryptPKCS8Base64 is an alias for EncryptPKIXBase64 for backward compatibility. +// +// Deprecated: Use EncryptPKIXBase64 instead. +func EncryptPKCS8Base64(plainText []byte, publicKeyBase64 string) (string, error) { + return EncryptPKIXBase64(plainText, publicKeyBase64) +} + +// EncryptPKCS1Base64 encrypts data with PKCS#1 public key and returns base64-encoded result. +func EncryptPKCS1Base64(plainText []byte, publicKeyBase64 string) (string, error) { + publicKey, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode public key") + } + + encrypted, err := EncryptPKCS1(plainText, publicKey) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +// DecryptPKCS8Base64 decrypts data with private key by PKCS#8 format and decode base64 input. +func DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64 string) ([]byte, error) { + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode private key") + } + + cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode cipher text") + } + + return DecryptPKCS8(cipherText, privateKey) +} + +// DecryptPKCS1Base64 decrypts base64-encoded data with PKCS#1 private key. +func DecryptPKCS1Base64(cipherTextBase64, privateKeyBase64 string) ([]byte, error) { + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode private key") + } + + cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode cipher text") + } + + return DecryptPKCS1(cipherText, privateKey) +} + +// GetPrivateKeyType detects the type of private key (PKCS#1 or PKCS#8). +// It attempts to parse the key in both formats to determine the actual type. +func GetPrivateKeyType(privateKey []byte) (string, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return "", gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + // Try PKCS#1 first + _, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err == nil { + return KeyTypePKCS1, nil + } + + // Try PKCS#8 + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err == nil { + if _, ok := priv.(*rsa.PrivateKey); ok { + return KeyTypePKCS8, nil + } + return "", gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA private key") + } + + return "", gerror.NewCode(gcode.CodeInvalidParameter, "unknown private key format") +} + +// GetPrivateKeyTypeBase64 detects the type of base64 encoded private key (PKCS#1 or PKCS#8). +func GetPrivateKeyTypeBase64(privateKeyBase64 string) (string, error) { + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return "", gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode private key") + } + + return GetPrivateKeyType(privateKey) +} + +// GenerateKeyPair generates a new RSA key pair with the given bits. +func GenerateKeyPair(bits int) (privateKey, publicKey []byte, err error) { + // Generate private key + privKey, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to generate rsa key") + } + + // Validate private key + err = privKey.Validate() + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to validate rsa key") + } + + // Marshal private key to PKCS#1 format + privKeyBytes := x509.MarshalPKCS1PrivateKey(privKey) + privateKey = pem.EncodeToMemory(&pem.Block{ + Type: pemTypeRSAPrivateKey, + Bytes: privKeyBytes, + }) + + // Generate PKCS#1 public key + pubKeyBytes := x509.MarshalPKCS1PublicKey(&privKey.PublicKey) + publicKey = pem.EncodeToMemory(&pem.Block{ + Type: pemTypeRSAPublicKey, + Bytes: pubKeyBytes, + }) + + return privateKey, publicKey, nil +} + +// GenerateKeyPairPKCS8 generates a new RSA key pair with the given bits in PKCS#8 format. +func GenerateKeyPairPKCS8(bits int) (privateKey, publicKey []byte, err error) { + // Generate private key + privKey, err := rsa.GenerateKey(rand.Reader, bits) + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to generate rsa key") + } + + // Validate private key + err = privKey.Validate() + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to validate rsa key") + } + + // Marshal private key to PKCS#8 format + privKeyBytes, err := x509.MarshalPKCS8PrivateKey(privKey) + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to marshal private key to PKCS#8") + } + + privateKey = pem.EncodeToMemory(&pem.Block{ + Type: pemTypePrivateKey, + Bytes: privKeyBytes, + }) + + // Generate public key + pubKeyBytes, err := x509.MarshalPKIXPublicKey(&privKey.PublicKey) + if err != nil { + return nil, nil, gerror.WrapCode(gcode.CodeInternalError, err, "failed to marshal public key") + } + + publicKey = pem.EncodeToMemory(&pem.Block{ + Type: pemTypePublicKey, + Bytes: pubKeyBytes, + }) + + return privateKey, publicKey, nil +} + +// GenerateDefaultKeyPair generates a new RSA key pair with default bits (2048). +func GenerateDefaultKeyPair() (privateKey, publicKey []byte, err error) { + return GenerateKeyPair(DefaultRSAKeyBits) +} + +// ExtractPKCS1PublicKey extracts PKCS#1 public key from private key. +func ExtractPKCS1PublicKey(privateKey []byte) ([]byte, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + priv, err := x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse private key") + } + + pubKeyBytes := x509.MarshalPKCS1PublicKey(&priv.PublicKey) + return pem.EncodeToMemory(&pem.Block{ + Type: pemTypeRSAPublicKey, + Bytes: pubKeyBytes, + }), nil +} + +// ============================================================================ +// OAEP Encryption/Decryption Functions (Recommended for new applications) +// ============================================================================ + +// EncryptOAEP encrypts data with public key using OAEP padding (auto-detect format). +// The publicKey can be either PKCS#1 or PKCS#8 (PKIX) format. +// Uses SHA-256 as the hash function by default. +// +// OAEP (Optimal Asymmetric Encryption Padding) is more secure than PKCS#1 v1.5 +// and is recommended for new applications. +// +// Note: For OAEP with SHA-256, max plaintext size = key_size_in_bytes - 2*32 - 2. +// For a 2048-bit key, this is 190 bytes. +func EncryptOAEP(plainText, publicKey []byte) ([]byte, error) { + return EncryptOAEPWithHash(plainText, publicKey, nil, sha256.New()) +} + +// EncryptOAEPWithHash encrypts data with public key using OAEP padding with custom hash. +// The publicKey can be either PKCS#1 or PKCS#8 (PKIX) format. +// The label parameter can be nil for most use cases. +// The hash parameter specifies the hash function to use (e.g., sha256.New()). +func EncryptOAEPWithHash(plainText, publicKey, label []byte, hash hash.Hash) ([]byte, error) { + block, _ := pem.Decode(publicKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid public key") + } + + // Try PKCS#8 (PKIX) first + pub, err := x509.ParsePKIXPublicKey(block.Bytes) + if err != nil { + // Try PKCS#1 + pub, err = x509.ParsePKCS1PublicKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse public key") + } + } + + rsaPub, ok := pub.(*rsa.PublicKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA public key") + } + + // Validate plaintext size for OAEP padding + // maxSize = keySize - 2*hashSize - 2 + maxSize := rsaPub.Size() - 2*hash.Size() - 2 + if len(plainText) > maxSize { + return nil, gerror.NewCodef(gcode.CodeInvalidParameter, + "plaintext too long: max %d bytes for this key with OAEP, got %d bytes", maxSize, len(plainText)) + } + + return rsa.EncryptOAEP(hash, rand.Reader, rsaPub, plainText, label) +} + +// DecryptOAEP decrypts data with private key using OAEP padding (auto-detect format). +// The privateKey can be either PKCS#1 or PKCS#8 format. +// Uses SHA-256 as the hash function by default. +func DecryptOAEP(cipherText, privateKey []byte) ([]byte, error) { + return DecryptOAEPWithHash(cipherText, privateKey, nil, sha256.New()) +} + +// DecryptOAEPWithHash decrypts data with private key using OAEP padding with custom hash. +// The privateKey can be either PKCS#1 or PKCS#8 format. +// The label parameter must match the label used during encryption (nil if not used). +// The hash parameter must match the hash function used during encryption. +func DecryptOAEPWithHash(cipherText, privateKey, label []byte, hash hash.Hash) ([]byte, error) { + block, _ := pem.Decode(privateKey) + if block == nil { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "invalid private key") + } + + // Try PKCS#8 first + priv, err := x509.ParsePKCS8PrivateKey(block.Bytes) + if err != nil { + // Try PKCS#1 + priv, err = x509.ParsePKCS1PrivateKey(block.Bytes) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to parse private key") + } + } + + rsaPriv, ok := priv.(*rsa.PrivateKey) + if !ok { + return nil, gerror.NewCode(gcode.CodeInvalidParameter, "not an RSA private key") + } + + return rsa.DecryptOAEP(hash, rand.Reader, rsaPriv, cipherText, label) +} + +// EncryptOAEPBase64 encrypts data with public key using OAEP padding +// and returns base64-encoded result. +func EncryptOAEPBase64(plainText []byte, publicKeyBase64 string) (string, error) { + publicKey, err := base64.StdEncoding.DecodeString(publicKeyBase64) + if err != nil { + return "", gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode public key") + } + + encrypted, err := EncryptOAEP(plainText, publicKey) + if err != nil { + return "", err + } + + return base64.StdEncoding.EncodeToString(encrypted), nil +} + +// DecryptOAEPBase64 decrypts base64-encoded data with private key using OAEP padding. +func DecryptOAEPBase64(cipherTextBase64, privateKeyBase64 string) ([]byte, error) { + privateKey, err := base64.StdEncoding.DecodeString(privateKeyBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode private key") + } + + cipherText, err := base64.StdEncoding.DecodeString(cipherTextBase64) + if err != nil { + return nil, gerror.WrapCode(gcode.CodeInvalidParameter, err, "failed to decode cipher text") + } + + return DecryptOAEP(cipherText, privateKey) +} diff --git a/crypto/grsa/grsa_z_unit_test.go b/crypto/grsa/grsa_z_unit_test.go new file mode 100644 index 00000000000..e8afc315dd2 --- /dev/null +++ b/crypto/grsa/grsa_z_unit_test.go @@ -0,0 +1,1058 @@ +// Copyright GoFrame Author(https://goframe.org). All Rights Reserved. +// +// This Source Code Form is subject to the terms of the MIT License. +// If a copy of the MIT was not distributed with this file, +// You can obtain one at https://github.com/gogf/gf. + +package grsa_test + +import ( + "encoding/base64" + "testing" + + "github.com/gogf/gf/v2/crypto/grsa" + "github.com/gogf/gf/v2/test/gtest" +) + +func TestEncryptDecrypt(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Test data to encrypt + plainText := []byte("Hello, World!") + + // Encrypt with public key + cipherText, err := grsa.Encrypt(plainText, publicKey) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt with private key + decryptedText, err := grsa.Decrypt(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestEncryptDecryptBase64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Encode keys to base64 + privateKeyBase64 := encodeToBase64(privateKey) + publicKeyBase64 := encodeToBase64(publicKey) + + // Test data to encrypt + plainText := []byte("Hello, Base64 World!") + + // Encrypt with public key + cipherTextBase64, err := grsa.EncryptBase64(plainText, publicKeyBase64) + t.AssertNil(err) + t.AssertNE(cipherTextBase64, "") + + // Decrypt with private key + decryptedText, err := grsa.DecryptBase64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestGenerateKeyPair(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Test generating a 2048-bit RSA key pair + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Check if keys are in correct format + privateKeyType, err := grsa.GetPrivateKeyType(privateKey) + t.AssertNil(err) + t.Assert(privateKeyType, "PKCS#1") + + // Test with 1024-bit key for faster test execution only. + // Note: 1024-bit keys are NOT secure for production use. + // Always use at least 2048-bit keys in production. + privateKey, publicKey, err = grsa.GenerateKeyPair(1024) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + }) +} + +func TestGenerateKeyPairPKCS8(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Test generating a 2048-bit RSA key pair in PKCS#8 format + privateKey, publicKey, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Check if keys are in correct format + privateKeyType, err := grsa.GetPrivateKeyType(privateKey) + t.AssertNil(err) + t.Assert(privateKeyType, "PKCS#8") + + // Test with 1024-bit key for faster test execution only. + // Note: 1024-bit keys are NOT secure for production use. + privateKey, publicKey, err = grsa.GenerateKeyPairPKCS8(1024) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + }) +} + +func TestEncryptAndDecryptPKCS(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate both types of key pairs for testing + privateKey1, publicKey1, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + // Test data to encrypt + plainText := []byte("Hello, Mixed Formats!") + + // Test general encrypt/decrypt with PKCS#1 keys + cipherText, err := grsa.Encrypt(plainText, publicKey1) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + decryptedText, err := grsa.Decrypt(cipherText, privateKey1) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + + // Test general encrypt/decrypt with PKCS#8 keys + cipherText8, err := grsa.Encrypt(plainText, publicKey8) + t.AssertNil(err) + t.AssertNE(cipherText8, nil) + + decryptedText8, err := grsa.Decrypt(cipherText8, privateKey8) + t.AssertNil(err) + t.Assert(string(decryptedText8), string(plainText)) + }) +} + +func TestGetPrivateKeyType(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate both PKCS#1 and PKCS#8 key pairs + // Note: 1024-bit keys used here for faster test execution only. + // NOT secure for production use. + privKey1, _, err := grsa.GenerateKeyPair(1024) + t.AssertNil(err) + + privKey8, _, err := grsa.GenerateKeyPairPKCS8(1024) + t.AssertNil(err) + + // Check types + keyType1, err := grsa.GetPrivateKeyType(privKey1) + t.AssertNil(err) + t.Assert(keyType1, "PKCS#1") + + keyType8, err := grsa.GetPrivateKeyType(privKey8) + t.AssertNil(err) + t.Assert(keyType8, "PKCS#8") + }) +} + +func TestEncryptPKCS1(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing (PKCS#1 format) + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Test data to encrypt + plainText := []byte("Hello, PKCS#1 World!") + + // Encrypt with public key using PKCS#1 format specifically + cipherText, err := grsa.EncryptPKCS1(plainText, publicKey) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt with private key using PKCS#1 format specifically + decryptedText, err := grsa.DecryptPKCS1(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestEncryptPKCS1Base64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Encode keys to base64 + privateKeyBase64 := encodeToBase64(privateKey) + publicKeyBase64 := encodeToBase64(publicKey) + + // Test data to encrypt + plainText := []byte("Hello, PKCS#1 Base64 World!") + + // Encrypt with public key using PKCS#1 format specifically + cipherTextBase64, err := grsa.EncryptPKCS1Base64(plainText, publicKeyBase64) + t.AssertNil(err) + t.AssertNE(cipherTextBase64, "") + + // Decrypt with private key using PKCS#1 format specifically + decryptedText, err := grsa.DecryptPKCS1Base64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +// Helper function to encode to base64 +func encodeToBase64(data []byte) string { + return base64.StdEncoding.EncodeToString(data) +} + +func TestEncryptWithInvalidPublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with invalid public key + _, err := grsa.Encrypt(plainText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test with empty public key + _, err = grsa.Encrypt(plainText, []byte{}) + t.AssertNE(err, nil) + + // Test with nil public key + _, err = grsa.Encrypt(plainText, nil) + t.AssertNE(err, nil) + }) +} + +func TestDecryptWithInvalidPrivateKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a valid key pair and encrypt some data + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.Encrypt(plainText, publicKey) + t.AssertNil(err) + + // Test decryption with invalid private key + _, err = grsa.Decrypt(cipherText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test decryption with empty private key + _, err = grsa.Decrypt(cipherText, []byte{}) + t.AssertNE(err, nil) + + // Test decryption with wrong private key + wrongPrivKey, _, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + _, err = grsa.Decrypt(cipherText, wrongPrivKey) + t.AssertNE(err, nil) + + // Verify correct decryption still works + decrypted, err := grsa.Decrypt(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestEncryptWithOversizedPlaintext(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a 2048-bit key pair + _, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + // For 2048-bit key with PKCS#1 v1.5 padding, max size is 256 - 11 = 245 bytes + // Create plaintext that exceeds this limit + oversizedPlainText := make([]byte, 300) + for i := range oversizedPlainText { + oversizedPlainText[i] = 'A' + } + + // Encryption should fail with oversized plaintext + _, err = grsa.Encrypt(oversizedPlainText, publicKey) + t.AssertNE(err, nil) + + // Verify that valid size plaintext works + validPlainText := make([]byte, 200) + for i := range validPlainText { + validPlainText[i] = 'B' + } + _, err = grsa.Encrypt(validPlainText, publicKey) + t.AssertNil(err) + }) +} + +func TestDecryptWithCorruptedCiphertext(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.Encrypt(plainText, publicKey) + t.AssertNil(err) + + // Corrupt the ciphertext + corruptedCipherText := make([]byte, len(cipherText)) + copy(corruptedCipherText, cipherText) + corruptedCipherText[0] ^= 0xFF + corruptedCipherText[len(corruptedCipherText)-1] ^= 0xFF + + // Decryption should fail with corrupted ciphertext + _, err = grsa.Decrypt(corruptedCipherText, privateKey) + t.AssertNE(err, nil) + }) +} + +func TestGetPrivateKeyTypeWithInvalidKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Test with invalid key + _, err := grsa.GetPrivateKeyType([]byte("invalid key")) + t.AssertNE(err, nil) + + // Test with empty key + _, err = grsa.GetPrivateKeyType([]byte{}) + t.AssertNE(err, nil) + + // Test with nil key + _, err = grsa.GetPrivateKeyType(nil) + t.AssertNE(err, nil) + }) +} + +func TestBase64FunctionsWithInvalidInput(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test EncryptBase64 with invalid base64 public key + _, err := grsa.EncryptBase64(plainText, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test DecryptBase64 with invalid base64 private key + _, err = grsa.DecryptBase64("validbase64==", "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test DecryptBase64 with invalid base64 ciphertext + privateKey, _, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + privateKeyBase64 := encodeToBase64(privateKey) + _, err = grsa.DecryptBase64("not-valid-base64!!!", privateKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestDecryptPKCS8WithPKCS1Key(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#1 key pair + privateKey1, publicKey1, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.EncryptPKCS1(plainText, publicKey1) + t.AssertNil(err) + + // DecryptPKCS8 should fail with PKCS#1 private key (no fallback) + _, err = grsa.DecryptPKCS8(cipherText, privateKey1) + t.AssertNE(err, nil) + + // DecryptPKCS1 should work + decrypted, err := grsa.DecryptPKCS1(cipherText, privateKey1) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestEncryptPKIX(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#8 key pair (which uses PKIX public key format) + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + plainText := []byte("Hello, PKIX World!") + + // Encrypt with PKIX public key + cipherText, err := grsa.EncryptPKIX(plainText, publicKey8) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt with PKCS#8 private key + decrypted, err := grsa.DecryptPKCS8(cipherText, privateKey8) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + + // Test with invalid public key + _, err = grsa.EncryptPKIX(plainText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test with PKCS#1 public key (should fail for EncryptPKIX) + _, publicKey1, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + _, err = grsa.EncryptPKIX(plainText, publicKey1) + t.AssertNE(err, nil) + + // Test oversized plaintext + oversizedPlainText := make([]byte, 300) + _, err = grsa.EncryptPKIX(oversizedPlainText, publicKey8) + t.AssertNE(err, nil) + }) +} + +func TestEncryptPKCS8Alias(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#8 key pair + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + plainText := []byte("Hello, PKCS8 Alias!") + + // EncryptPKCS8 is an alias for EncryptPKIX + cipherText, err := grsa.EncryptPKCS8(plainText, publicKey8) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt should work + decrypted, err := grsa.DecryptPKCS8(cipherText, privateKey8) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestEncryptPKIXBase64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#8 key pair + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + privateKeyBase64 := encodeToBase64(privateKey8) + publicKeyBase64 := encodeToBase64(publicKey8) + + plainText := []byte("Hello, PKIX Base64!") + + // Encrypt with PKIX public key + cipherTextBase64, err := grsa.EncryptPKIXBase64(plainText, publicKeyBase64) + t.AssertNil(err) + t.AssertNE(cipherTextBase64, "") + + // Decrypt with PKCS#8 private key + decrypted, err := grsa.DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + + // Test with invalid base64 public key + _, err = grsa.EncryptPKIXBase64(plainText, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with invalid public key content + _, err = grsa.EncryptPKIXBase64(plainText, encodeToBase64([]byte("invalid key"))) + t.AssertNE(err, nil) + }) +} + +func TestEncryptPKCS8Base64Alias(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#8 key pair + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + privateKeyBase64 := encodeToBase64(privateKey8) + publicKeyBase64 := encodeToBase64(publicKey8) + + plainText := []byte("Hello, PKCS8 Base64 Alias!") + + // EncryptPKCS8Base64 is an alias for EncryptPKIXBase64 + cipherTextBase64, err := grsa.EncryptPKCS8Base64(plainText, publicKeyBase64) + t.AssertNil(err) + t.AssertNE(cipherTextBase64, "") + + // Decrypt should work + decrypted, err := grsa.DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestDecryptPKCS8Base64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#8 key pair + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + privateKeyBase64 := encodeToBase64(privateKey8) + publicKeyBase64 := encodeToBase64(publicKey8) + + plainText := []byte("Hello, DecryptPKCS8Base64!") + + // Encrypt + cipherTextBase64, err := grsa.EncryptPKIXBase64(plainText, publicKeyBase64) + t.AssertNil(err) + + // Decrypt + decrypted, err := grsa.DecryptPKCS8Base64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + + // Test with invalid base64 private key + _, err = grsa.DecryptPKCS8Base64(cipherTextBase64, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with invalid base64 ciphertext + _, err = grsa.DecryptPKCS8Base64("not-valid-base64!!!", privateKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestGetPrivateKeyTypeBase64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate both PKCS#1 and PKCS#8 key pairs + privKey1, _, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + privKey8, _, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + // Check types via base64 + keyType1, err := grsa.GetPrivateKeyTypeBase64(encodeToBase64(privKey1)) + t.AssertNil(err) + t.Assert(keyType1, "PKCS#1") + + keyType8, err := grsa.GetPrivateKeyTypeBase64(encodeToBase64(privKey8)) + t.AssertNil(err) + t.Assert(keyType8, "PKCS#8") + + // Test with invalid base64 + _, err = grsa.GetPrivateKeyTypeBase64("not-valid-base64!!!") + t.AssertNE(err, nil) + }) +} + +func TestExtractPKCS1PublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate PKCS#1 key pair + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + // Extract public key from private key + extractedPublicKey, err := grsa.ExtractPKCS1PublicKey(privateKey) + t.AssertNil(err) + t.AssertNE(extractedPublicKey, nil) + + // The extracted public key should work for encryption + plainText := []byte("Hello, Extracted Key!") + cipherText, err := grsa.EncryptPKCS1(plainText, extractedPublicKey) + t.AssertNil(err) + + // Decrypt with original private key + decrypted, err := grsa.DecryptPKCS1(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + + // Compare extracted key with original (they should be equivalent) + cipherText2, err := grsa.EncryptPKCS1(plainText, publicKey) + t.AssertNil(err) + decrypted2, err := grsa.DecryptPKCS1(cipherText2, privateKey) + t.AssertNil(err) + t.Assert(string(decrypted2), string(plainText)) + + // Test with invalid private key + _, err = grsa.ExtractPKCS1PublicKey([]byte("invalid key")) + t.AssertNE(err, nil) + + // Test with PKCS#8 private key (should fail) + privateKey8, _, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + _, err = grsa.ExtractPKCS1PublicKey(privateKey8) + t.AssertNE(err, nil) + }) +} + +func TestDecryptPKCS1WithInvalidKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.EncryptPKCS1(plainText, publicKey) + t.AssertNil(err) + + // Test with invalid private key + _, err = grsa.DecryptPKCS1(cipherText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test with PKCS#8 private key (should fail for DecryptPKCS1) + privateKey8, _, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + _, err = grsa.DecryptPKCS1(cipherText, privateKey8) + t.AssertNE(err, nil) + + // Verify correct decryption works + decrypted, err := grsa.DecryptPKCS1(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestDecryptPKCS8WithInvalidKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.EncryptPKIX(plainText, publicKey8) + t.AssertNil(err) + + // Test with invalid private key + _, err = grsa.DecryptPKCS8(cipherText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Verify correct decryption works + decrypted, err := grsa.DecryptPKCS8(cipherText, privateKey8) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestEncryptPKCS1WithInvalidKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with invalid public key + _, err := grsa.EncryptPKCS1(plainText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test with PKCS#8 public key (should fail for EncryptPKCS1) + _, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + _, err = grsa.EncryptPKCS1(plainText, publicKey8) + t.AssertNE(err, nil) + }) +} + +func TestEncryptPKCS1WithOversizedPlaintext(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + _, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + // Create oversized plaintext + oversizedPlainText := make([]byte, 300) + _, err = grsa.EncryptPKCS1(oversizedPlainText, publicKey) + t.AssertNE(err, nil) + }) +} + +func TestEncryptPKCS1Base64WithInvalidInput(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with invalid base64 public key + _, err := grsa.EncryptPKCS1Base64(plainText, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with invalid public key content + _, err = grsa.EncryptPKCS1Base64(plainText, encodeToBase64([]byte("invalid key"))) + t.AssertNE(err, nil) + }) +} + +func TestDecryptPKCS1Base64WithInvalidInput(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey, publicKey, err := grsa.GenerateKeyPair(2048) + t.AssertNil(err) + + privateKeyBase64 := encodeToBase64(privateKey) + publicKeyBase64 := encodeToBase64(publicKey) + + plainText := []byte("Hello, World!") + cipherTextBase64, err := grsa.EncryptPKCS1Base64(plainText, publicKeyBase64) + t.AssertNil(err) + + // Test with invalid base64 private key + _, err = grsa.DecryptPKCS1Base64(cipherTextBase64, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with invalid base64 ciphertext + _, err = grsa.DecryptPKCS1Base64("not-valid-base64!!!", privateKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestEncryptWithNonRSAPublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a PEM block that is valid but not an RSA key + // This tests the "not an RSA public key" error path + // We use a valid PEM structure but with invalid content + invalidPEM := []byte(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAE +-----END PUBLIC KEY-----`) + + plainText := []byte("Hello, World!") + _, err := grsa.Encrypt(plainText, invalidPEM) + t.AssertNE(err, nil) + }) +} + +func TestDecryptWithNonRSAPrivateKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a PEM block that is valid but not an RSA key + invalidPEM := []byte(`-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQg +-----END PRIVATE KEY-----`) + + cipherText := []byte("some cipher text") + _, err := grsa.Decrypt(cipherText, invalidPEM) + t.AssertNE(err, nil) + }) +} + +func TestEncryptBase64WithInvalidPublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with valid base64 but invalid key content + invalidKeyBase64 := encodeToBase64([]byte("invalid key")) + _, err := grsa.EncryptBase64(plainText, invalidKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestGetPrivateKeyTypeWithNonRSAKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKCS#8 PEM but with non-RSA content (EC key) + // This tests the "not an RSA private key" error path in GetPrivateKeyType + ecPrivateKeyPEM := []byte(`-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVcB/UNPczVP6jE4Z +p7v6qYQXsKQZLJGBJKKnUWuHb6+hRANCAASYn3k2T4VqPt1HVAK5Rc7rMb6lGOzF +v0MVLfCgPKANNGdBvGPmaSLFIxGMNL0v1C2RRvqqEu/vL3POoaqfMJhw +-----END PRIVATE KEY-----`) + + _, err := grsa.GetPrivateKeyType(ecPrivateKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestDecryptPKCS8WithNonRSAKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKCS#8 PEM but with non-RSA content (EC key) + ecPrivateKeyPEM := []byte(`-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVcB/UNPczVP6jE4Z +p7v6qYQXsKQZLJGBJKKnUWuHb6+hRANCAASYn3k2T4VqPt1HVAK5Rc7rMb6lGOzF +v0MVLfCgPKANNGdBvGPmaSLFIxGMNL0v1C2RRvqqEu/vL3POoaqfMJhw +-----END PRIVATE KEY-----`) + + cipherText := []byte("some cipher text") + _, err := grsa.DecryptPKCS8(cipherText, ecPrivateKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestEncryptPKIXWithNonRSAKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKIX PEM but with non-RSA content (EC key) + ecPublicKeyPEM := []byte(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmJ95Nk+Faj7dR1QCuUXO6zG+pRjs +xb9DFS3woDygDTRnQbxj5mkixSMRjDS9L9QtkUb6qhLv7y9zzqGqnzCYcA== +-----END PUBLIC KEY-----`) + + plainText := []byte("Hello, World!") + _, err := grsa.EncryptPKIX(plainText, ecPublicKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestEncryptWithNonRSAPKIXKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKIX PEM but with non-RSA content (EC key) + // This tests the "not an RSA public key" error path in Encrypt + ecPublicKeyPEM := []byte(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmJ95Nk+Faj7dR1QCuUXO6zG+pRjs +xb9DFS3woDygDTRnQbxj5mkixSMRjDS9L9QtkUb6qhLv7y9zzqGqnzCYcA== +-----END PUBLIC KEY-----`) + + plainText := []byte("Hello, World!") + _, err := grsa.Encrypt(plainText, ecPublicKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestDecryptWithNonRSAPKCS8Key(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKCS#8 PEM but with non-RSA content (EC key) + // This tests the "not an RSA private key" error path in Decrypt + ecPrivateKeyPEM := []byte(`-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVcB/UNPczVP6jE4Z +p7v6qYQXsKQZLJGBJKKnUWuHb6+hRANCAASYn3k2T4VqPt1HVAK5Rc7rMb6lGOzF +v0MVLfCgPKANNGdBvGPmaSLFIxGMNL0v1C2RRvqqEu/vL3POoaqfMJhw +-----END PRIVATE KEY-----`) + + cipherText := []byte("some cipher text") + _, err := grsa.Decrypt(cipherText, ecPrivateKeyPEM) + t.AssertNE(err, nil) + }) +} + +// ============================================================================ +// OAEP Encryption/Decryption Tests +// ============================================================================ + +func TestEncryptDecryptOAEP(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + t.AssertNE(privateKey, nil) + t.AssertNE(publicKey, nil) + + // Test data to encrypt + plainText := []byte("Hello, OAEP World!") + + // Encrypt with public key using OAEP + cipherText, err := grsa.EncryptOAEP(plainText, publicKey) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt with private key using OAEP + decryptedText, err := grsa.DecryptOAEP(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestEncryptDecryptOAEPWithPKCS8Keys(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a PKCS#8 key pair for testing + privateKey8, publicKey8, err := grsa.GenerateKeyPairPKCS8(2048) + t.AssertNil(err) + t.AssertNE(privateKey8, nil) + t.AssertNE(publicKey8, nil) + + // Test data to encrypt + plainText := []byte("Hello, OAEP PKCS#8 World!") + + // Encrypt with PKIX public key using OAEP + cipherText, err := grsa.EncryptOAEP(plainText, publicKey8) + t.AssertNil(err) + t.AssertNE(cipherText, nil) + + // Decrypt with PKCS#8 private key using OAEP + decryptedText, err := grsa.DecryptOAEP(cipherText, privateKey8) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestEncryptDecryptOAEPBase64(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a key pair for testing + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + // Encode keys to base64 + privateKeyBase64 := encodeToBase64(privateKey) + publicKeyBase64 := encodeToBase64(publicKey) + + // Test data to encrypt + plainText := []byte("Hello, OAEP Base64 World!") + + // Encrypt with public key using OAEP + cipherTextBase64, err := grsa.EncryptOAEPBase64(plainText, publicKeyBase64) + t.AssertNil(err) + t.AssertNE(cipherTextBase64, "") + + // Decrypt with private key using OAEP + decryptedText, err := grsa.DecryptOAEPBase64(cipherTextBase64, privateKeyBase64) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +} + +func TestEncryptOAEPWithInvalidPublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with invalid public key + _, err := grsa.EncryptOAEP(plainText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test with empty public key + _, err = grsa.EncryptOAEP(plainText, []byte{}) + t.AssertNE(err, nil) + + // Test with nil public key + _, err = grsa.EncryptOAEP(plainText, nil) + t.AssertNE(err, nil) + }) +} + +func TestDecryptOAEPWithInvalidPrivateKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a valid key pair and encrypt some data + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.EncryptOAEP(plainText, publicKey) + t.AssertNil(err) + + // Test decryption with invalid private key + _, err = grsa.DecryptOAEP(cipherText, []byte("invalid key")) + t.AssertNE(err, nil) + + // Test decryption with empty private key + _, err = grsa.DecryptOAEP(cipherText, []byte{}) + t.AssertNE(err, nil) + + // Test decryption with wrong private key + wrongPrivKey, _, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + _, err = grsa.DecryptOAEP(cipherText, wrongPrivKey) + t.AssertNE(err, nil) + + // Verify correct decryption still works + decrypted, err := grsa.DecryptOAEP(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decrypted), string(plainText)) + }) +} + +func TestEncryptOAEPWithOversizedPlaintext(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Generate a 2048-bit key pair + _, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + // For 2048-bit key with OAEP SHA-256 padding, max size is 256 - 2*32 - 2 = 190 bytes + // Create plaintext that exceeds this limit + oversizedPlainText := make([]byte, 200) + for i := range oversizedPlainText { + oversizedPlainText[i] = 'A' + } + + // Encryption should fail with oversized plaintext + _, err = grsa.EncryptOAEP(oversizedPlainText, publicKey) + t.AssertNE(err, nil) + + // Verify that valid size plaintext works + validPlainText := make([]byte, 150) + for i := range validPlainText { + validPlainText[i] = 'B' + } + _, err = grsa.EncryptOAEP(validPlainText, publicKey) + t.AssertNil(err) + }) +} + +func TestDecryptOAEPWithCorruptedCiphertext(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + plainText := []byte("Hello, World!") + cipherText, err := grsa.EncryptOAEP(plainText, publicKey) + t.AssertNil(err) + + // Corrupt the ciphertext + corruptedCipherText := make([]byte, len(cipherText)) + copy(corruptedCipherText, cipherText) + corruptedCipherText[0] ^= 0xFF + corruptedCipherText[len(corruptedCipherText)-1] ^= 0xFF + + // Decryption should fail with corrupted ciphertext + _, err = grsa.DecryptOAEP(corruptedCipherText, privateKey) + t.AssertNE(err, nil) + }) +} + +func TestEncryptOAEPBase64WithInvalidInput(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + plainText := []byte("Hello, World!") + + // Test with invalid base64 public key + _, err := grsa.EncryptOAEPBase64(plainText, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with valid base64 but invalid key content + invalidKeyBase64 := encodeToBase64([]byte("invalid key")) + _, err = grsa.EncryptOAEPBase64(plainText, invalidKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestDecryptOAEPBase64WithInvalidInput(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + privateKeyBase64 := encodeToBase64(privateKey) + publicKeyBase64 := encodeToBase64(publicKey) + + plainText := []byte("Hello, World!") + cipherTextBase64, err := grsa.EncryptOAEPBase64(plainText, publicKeyBase64) + t.AssertNil(err) + + // Test with invalid base64 private key + _, err = grsa.DecryptOAEPBase64(cipherTextBase64, "not-valid-base64!!!") + t.AssertNE(err, nil) + + // Test with invalid base64 ciphertext + _, err = grsa.DecryptOAEPBase64("not-valid-base64!!!", privateKeyBase64) + t.AssertNE(err, nil) + }) +} + +func TestEncryptOAEPWithNonRSAPublicKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKIX PEM but with non-RSA content (EC key) + ecPublicKeyPEM := []byte(`-----BEGIN PUBLIC KEY----- +MFkwEwYHKoZIzj0CAQYIKoZIzj0DAQcDQgAEmJ95Nk+Faj7dR1QCuUXO6zG+pRjs +xb9DFS3woDygDTRnQbxj5mkixSMRjDS9L9QtkUb6qhLv7y9zzqGqnzCYcA== +-----END PUBLIC KEY-----`) + + plainText := []byte("Hello, World!") + _, err := grsa.EncryptOAEP(plainText, ecPublicKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestDecryptOAEPWithNonRSAPrivateKey(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // Create a valid PKCS#8 PEM but with non-RSA content (EC key) + ecPrivateKeyPEM := []byte(`-----BEGIN PRIVATE KEY----- +MIGHAgEAMBMGByqGSM49AgEGCCqGSM49AwEHBG0wawIBAQQgVcB/UNPczVP6jE4Z +p7v6qYQXsKQZLJGBJKKnUWuHb6+hRANCAASYn3k2T4VqPt1HVAK5Rc7rMb6lGOzF +v0MVLfCgPKANNGdBvGPmaSLFIxGMNL0v1C2RRvqqEu/vL3POoaqfMJhw +-----END PRIVATE KEY-----`) + + cipherText := []byte("some cipher text") + _, err := grsa.DecryptOAEP(cipherText, ecPrivateKeyPEM) + t.AssertNE(err, nil) + }) +} + +func TestEncryptOAEPWithHashCustomHash(t *testing.T) { + gtest.C(t, func(t *gtest.T) { + // This test verifies that EncryptOAEPWithHash and DecryptOAEPWithHash work correctly + // with the default SHA-256 hash (via EncryptOAEP/DecryptOAEP which use sha256.New()) + privateKey, publicKey, err := grsa.GenerateDefaultKeyPair() + t.AssertNil(err) + + plainText := []byte("Hello, Custom Hash World!") + + // Encrypt and decrypt using the default OAEP functions + cipherText, err := grsa.EncryptOAEP(plainText, publicKey) + t.AssertNil(err) + + decryptedText, err := grsa.DecryptOAEP(cipherText, privateKey) + t.AssertNil(err) + t.Assert(string(decryptedText), string(plainText)) + }) +}