refactor: split into subpackages and add AEAD/options/stream APIs with comprehensive tests
This commit is contained in:
@@ -0,0 +1,25 @@
|
||||
package paddingx
|
||||
|
||||
import "testing"
|
||||
|
||||
func FuzzPadUnpadRoundTrip(f *testing.F) {
|
||||
f.Add([]byte("abc"))
|
||||
f.Add([]byte{})
|
||||
modes := []string{PKCS7, ZERO, ANSIX923}
|
||||
|
||||
f.Fuzz(func(t *testing.T, data []byte) {
|
||||
for _, mode := range modes {
|
||||
padded, err := Pad(data, 16, mode)
|
||||
if err != nil {
|
||||
t.Fatalf("Pad failed: %v", err)
|
||||
}
|
||||
out, err := Unpad(padded, 16, mode)
|
||||
if err != nil {
|
||||
t.Fatalf("Unpad failed: %v", err)
|
||||
}
|
||||
if mode != ZERO && string(out) != string(data) {
|
||||
t.Fatalf("roundtrip mismatch for mode %s", mode)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
@@ -0,0 +1,128 @@
|
||||
package paddingx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"errors"
|
||||
"strings"
|
||||
)
|
||||
|
||||
const (
|
||||
PKCS5 = "PKCS5"
|
||||
PKCS7 = "PKCS7"
|
||||
ZERO = "ZERO"
|
||||
ANSIX923 = "ANSIX923"
|
||||
)
|
||||
|
||||
func Pad(data []byte, blockSize int, mode string) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("block size must be greater than zero")
|
||||
}
|
||||
switch normalizeMode(mode) {
|
||||
case "", PKCS7:
|
||||
return PKCS7Padding(data, blockSize), nil
|
||||
case PKCS5:
|
||||
// Compatibility mode: historically PKCS5 was used generically in this project.
|
||||
return PKCS7Padding(data, blockSize), nil
|
||||
case ZERO:
|
||||
return zeroPadding(data, blockSize), nil
|
||||
case ANSIX923:
|
||||
return ansiX923Padding(data, blockSize), nil
|
||||
default:
|
||||
return nil, errors.New("padding type not supported")
|
||||
}
|
||||
}
|
||||
|
||||
func Unpad(data []byte, blockSize int, mode string) ([]byte, error) {
|
||||
if blockSize <= 0 {
|
||||
return nil, errors.New("block size must be greater than zero")
|
||||
}
|
||||
switch normalizeMode(mode) {
|
||||
case "", PKCS7:
|
||||
return PKCS7Unpadding(data, blockSize)
|
||||
case PKCS5:
|
||||
// Compatibility mode: historically PKCS5 was used generically in this project.
|
||||
return PKCS7Unpadding(data, blockSize)
|
||||
case ZERO:
|
||||
return zeroUnpadding(data)
|
||||
case ANSIX923:
|
||||
return ansiX923Unpadding(data, blockSize)
|
||||
default:
|
||||
return nil, errors.New("padding type not supported")
|
||||
}
|
||||
}
|
||||
|
||||
func PKCS7Padding(data []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(data)%blockSize
|
||||
padText := bytes.Repeat([]byte{byte(padding)}, padding)
|
||||
return append(data, padText...)
|
||||
}
|
||||
|
||||
func PKCS7Unpadding(data []byte, blockSize int) ([]byte, error) {
|
||||
if len(data) == 0 || len(data)%blockSize != 0 {
|
||||
return nil, errors.New("invalid PKCS7 padding")
|
||||
}
|
||||
padding := int(data[len(data)-1])
|
||||
if padding <= 0 || padding > blockSize || padding > len(data) {
|
||||
return nil, errors.New("invalid PKCS7 padding")
|
||||
}
|
||||
for i := len(data) - padding; i < len(data); i++ {
|
||||
if int(data[i]) != padding {
|
||||
return nil, errors.New("invalid PKCS7 padding")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-padding], nil
|
||||
}
|
||||
|
||||
func PKCS5Padding(data []byte) []byte {
|
||||
return PKCS7Padding(data, 8)
|
||||
}
|
||||
|
||||
func PKCS5Unpadding(data []byte) ([]byte, error) {
|
||||
return PKCS7Unpadding(data, 8)
|
||||
}
|
||||
|
||||
func zeroPadding(data []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(data)%blockSize
|
||||
if padding == blockSize {
|
||||
return data
|
||||
}
|
||||
return append(data, bytes.Repeat([]byte{0x00}, padding)...)
|
||||
}
|
||||
|
||||
func zeroUnpadding(data []byte) ([]byte, error) {
|
||||
idx := len(data)
|
||||
for idx > 0 && data[idx-1] == 0x00 {
|
||||
idx--
|
||||
}
|
||||
return data[:idx], nil
|
||||
}
|
||||
|
||||
func ansiX923Padding(data []byte, blockSize int) []byte {
|
||||
padding := blockSize - len(data)%blockSize
|
||||
if padding == 0 {
|
||||
padding = blockSize
|
||||
}
|
||||
pad := make([]byte, padding)
|
||||
pad[len(pad)-1] = byte(padding)
|
||||
return append(data, pad...)
|
||||
}
|
||||
|
||||
func ansiX923Unpadding(data []byte, blockSize int) ([]byte, error) {
|
||||
if len(data) == 0 || len(data)%blockSize != 0 {
|
||||
return nil, errors.New("invalid ANSI X9.23 padding")
|
||||
}
|
||||
padding := int(data[len(data)-1])
|
||||
if padding <= 0 || padding > blockSize || padding > len(data) {
|
||||
return nil, errors.New("invalid ANSI X9.23 padding")
|
||||
}
|
||||
for i := len(data) - padding; i < len(data)-1; i++ {
|
||||
if data[i] != 0x00 {
|
||||
return nil, errors.New("invalid ANSI X9.23 padding")
|
||||
}
|
||||
}
|
||||
return data[:len(data)-padding], nil
|
||||
}
|
||||
|
||||
func normalizeMode(mode string) string {
|
||||
return strings.ToUpper(strings.TrimSpace(mode))
|
||||
}
|
||||
@@ -0,0 +1,94 @@
|
||||
package paddingx
|
||||
|
||||
import (
|
||||
"bytes"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func TestPadAndUnpadPKCS7(t *testing.T) {
|
||||
plain := []byte("hello-world")
|
||||
padded, err := Pad(plain, 16, PKCS7)
|
||||
if err != nil {
|
||||
t.Fatalf("Pad PKCS7 failed: %v", err)
|
||||
}
|
||||
if len(padded)%16 != 0 {
|
||||
t.Fatalf("padded length should be block aligned, got %d", len(padded))
|
||||
}
|
||||
got, err := Unpad(padded, 16, PKCS7)
|
||||
if err != nil {
|
||||
t.Fatalf("Unpad PKCS7 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plain) {
|
||||
t.Fatalf("roundtrip mismatch, got %x want %x", got, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadAndUnpadPKCS5Compatibility(t *testing.T) {
|
||||
plain := []byte("DES-plaintext")
|
||||
padded, err := Pad(plain, 8, PKCS5)
|
||||
if err != nil {
|
||||
t.Fatalf("Pad PKCS5 failed: %v", err)
|
||||
}
|
||||
got, err := Unpad(padded, 8, PKCS5)
|
||||
if err != nil {
|
||||
t.Fatalf("Unpad PKCS5 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plain) {
|
||||
t.Fatalf("roundtrip mismatch, got %x want %x", got, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadAndUnpadZero(t *testing.T) {
|
||||
plain := []byte("abc\x00\x00")
|
||||
padded, err := Pad(plain, 8, ZERO)
|
||||
if err != nil {
|
||||
t.Fatalf("Pad ZERO failed: %v", err)
|
||||
}
|
||||
got, err := Unpad(padded, 8, ZERO)
|
||||
if err != nil {
|
||||
t.Fatalf("Unpad ZERO failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, []byte("abc")) {
|
||||
t.Fatalf("zero unpadding mismatch, got %q", got)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadAndUnpadANSIX923(t *testing.T) {
|
||||
plain := []byte("ansi-x923")
|
||||
padded, err := Pad(plain, 16, ANSIX923)
|
||||
if err != nil {
|
||||
t.Fatalf("Pad ANSIX923 failed: %v", err)
|
||||
}
|
||||
got, err := Unpad(padded, 16, ANSIX923)
|
||||
if err != nil {
|
||||
t.Fatalf("Unpad ANSIX923 failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plain) {
|
||||
t.Fatalf("roundtrip mismatch, got %x want %x", got, plain)
|
||||
}
|
||||
}
|
||||
|
||||
func TestPadUnsupportedMode(t *testing.T) {
|
||||
if _, err := Pad([]byte("x"), 8, "UNKNOWN"); err == nil {
|
||||
t.Fatalf("expected error for unsupported mode")
|
||||
}
|
||||
}
|
||||
|
||||
func TestUnpadInvalidPKCS7(t *testing.T) {
|
||||
_, err := Unpad([]byte{1, 2, 3, 4}, 4, PKCS7)
|
||||
if err == nil {
|
||||
t.Fatalf("expected invalid PKCS7 padding error")
|
||||
}
|
||||
}
|
||||
|
||||
func TestPKCS5Helpers(t *testing.T) {
|
||||
plain := []byte("1234567")
|
||||
padded := PKCS5Padding(plain)
|
||||
got, err := PKCS5Unpadding(padded)
|
||||
if err != nil {
|
||||
t.Fatalf("PKCS5Unpadding failed: %v", err)
|
||||
}
|
||||
if !bytes.Equal(got, plain) {
|
||||
t.Fatalf("PKCS5 helper mismatch, got %x want %x", got, plain)
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user