diff --git a/padding/ansi_x923.go b/padding/ansi_x923.go new file mode 100644 index 0000000..68cf570 --- /dev/null +++ b/padding/ansi_x923.go @@ -0,0 +1,41 @@ +// https://www.ibm.com/docs/en/linux-on-systems?topic=processes-ansi-x923-cipher-block-chaining +package padding + +import ( + "errors" + + "github.com/emmansun/gmsm/internal/subtle" +) + +type ansiX923Padding uint + +func (pad ansiX923Padding) BlockSize() int { + return int(pad) +} + +func (pad ansiX923Padding) Pad(src []byte) []byte { + overhead := pad.BlockSize() - len(src)%pad.BlockSize() + ret, out := subtle.SliceForAppend(src, overhead) + out[overhead-1] = byte(overhead) + for i := 0; i < overhead-1; i++ { + out[i] = 0 + } + return ret +} + +func (pad ansiX923Padding) Unpad(src []byte) ([]byte, error) { + srcLen := len(src) + if srcLen == 0 || srcLen%pad.BlockSize() != 0 { + return nil, errors.New("ansi x9.23: invalid src length") + } + paddedLen := src[srcLen-1] + if paddedLen == 0 || int(paddedLen) > pad.BlockSize() { + return nil, errors.New("ansi x9.23: invalid padding length") + } + for _, b := range src[srcLen-int(paddedLen) : srcLen-1] { + if b != 0 { + return nil, errors.New("ansi x9.23: invalid padding bytes") + } + } + return src[:srcLen-int(paddedLen)], nil +} diff --git a/padding/ansi_x923_test.go b/padding/ansi_x923_test.go new file mode 100644 index 0000000..e08ef69 --- /dev/null +++ b/padding/ansi_x923_test.go @@ -0,0 +1,83 @@ +// https://www.ibm.com/docs/en/linux-on-systems?topic=processes-ansi-x923-cipher-block-chaining + +package padding + +import ( + "reflect" + "testing" +) + +func Test_ansiX923Padding_Pad(t *testing.T) { + x923 := NewANSIX923Padding(16) + tests := []struct { + name string + src []byte + want []byte + }{ + {"16 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16}}, + {"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1}}, + {"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 2}}, + {"13 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 3}}, + {"12 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 4}}, + {"11 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 5}}, + {"10 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 6}}, + {"9 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 7}}, + {"8 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 8}}, + {"7 bytes", []byte{0, 1, 2, 3, 4, 5, 6}, []byte{0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 9}}, + {"6 bytes", []byte{0, 1, 2, 3, 4, 5}, []byte{0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}}, + {"5 bytes", []byte{0, 1, 2, 3, 4}, []byte{0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11}}, + {"4 bytes", []byte{0, 1, 2, 3}, []byte{0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12}}, + {"3 bytes", []byte{0, 1, 2}, []byte{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13}}, + {"2 bytes", []byte{0, 1}, []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14}}, + {"1 bytes", []byte{0}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + if got := x923.Pad(tt.src); !reflect.DeepEqual(got, tt.want) { + t.Errorf("ansiX923Padding.Pad() = %v, want %v", got, tt.want) + } + }) + } +} + +func Test_ansiX923Padding_Unpad(t *testing.T) { + x923 := NewANSIX923Padding(16) + tests := []struct { + name string + want []byte + src []byte + wantErr bool + }{ + {"16 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16}, false}, + {"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1}, false}, + {"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 2}, false}, + {"13 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 3}, false}, + {"12 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 4}, false}, + {"11 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 5}, false}, + {"10 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 6}, false}, + {"9 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 7}, false}, + {"8 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 8}, false}, + {"7 bytes", []byte{0, 1, 2, 3, 4, 5, 6}, []byte{0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 9}, false}, + {"6 bytes", []byte{0, 1, 2, 3, 4, 5}, []byte{0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10}, false}, + {"5 bytes", []byte{0, 1, 2, 3, 4}, []byte{0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 11}, false}, + {"4 bytes", []byte{0, 1, 2, 3}, []byte{0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 12}, false}, + {"3 bytes", []byte{0, 1, 2}, []byte{0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 13}, false}, + {"2 bytes", []byte{0, 1}, []byte{0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14}, false}, + {"1 bytes", []byte{0}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, false}, + {"invalid src length", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 15}, true}, + {"invalid padding length", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 17}, true}, + {"invalid padding bytes", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 15}, true}, + } + for _, tt := range tests { + t.Run(tt.name, func(t *testing.T) { + got, err := x923.Unpad(tt.src) + if (err != nil) != tt.wantErr { + t.Errorf("ansiX923Padding.Unpad() error = %v, wantErr %v", err, tt.wantErr) + return + } + if !reflect.DeepEqual(got, tt.want) { + t.Errorf("ansiX923Padding.Unpad() = %v, want %v", got, tt.want) + } + }) + } +} diff --git a/padding/pkcs7.go b/padding/pkcs7.go index c070519..7b275c5 100644 --- a/padding/pkcs7.go +++ b/padding/pkcs7.go @@ -2,7 +2,6 @@ package padding import ( - goSubtle "crypto/subtle" "errors" "github.com/emmansun/gmsm/internal/subtle" @@ -24,21 +23,18 @@ func (pad pkcs7Padding) Pad(src []byte) []byte { } func (pad pkcs7Padding) Unpad(src []byte) ([]byte, error) { - if len(src)%pad.BlockSize() != 0 { - return nil, errors.New("pkcs7: invalid src size") + srcLen := len(src) + if srcLen == 0 || srcLen%pad.BlockSize() != 0 { + return nil, errors.New("pkcs7: invalid src length") } - overhead := src[len(src)-1] - if overhead == 0 || int(overhead) > pad.BlockSize() { + paddedLen := src[srcLen-1] + if paddedLen == 0 || int(paddedLen) > pad.BlockSize() { return nil, errors.New("pkcs7: invalid padding byte/length") } - tag := make([]byte, pad.BlockSize()) - copy(tag, src[len(src)-pad.BlockSize():]) - for i := pad.BlockSize() - int(overhead); i < pad.BlockSize(); i++ { - tag[i] = byte(overhead) + for _, b := range src[srcLen-int(paddedLen) : srcLen-1] { + if b != paddedLen { + return nil, errors.New("pkcs7: inconsistent padding bytes") + } } - if goSubtle.ConstantTimeCompare(tag, src[len(src)-pad.BlockSize():]) != 1 { - return nil, errors.New("pkcs7: inconsistent padding bytes") - } - - return src[:len(src)-int(overhead)], nil + return src[:srcLen-int(paddedLen)], nil } diff --git a/padding/pkcs7_test.go b/padding/pkcs7_test.go index 0d359fd..97a7775 100644 --- a/padding/pkcs7_test.go +++ b/padding/pkcs7_test.go @@ -8,14 +8,13 @@ import ( ) func Test_pkcs7Padding_Pad(t *testing.T) { - pad := NewPKCS7Padding(16) + pkcs7 := NewPKCS7Padding(16) tests := []struct { name string src []byte want []byte }{ - // TODO: Add test cases. {"16 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}}, {"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1}}, {"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 2, 2}}, @@ -35,7 +34,7 @@ func Test_pkcs7Padding_Pad(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - if got := pad.Pad(tt.src); !reflect.DeepEqual(got, tt.want) { + if got := pkcs7.Pad(tt.src); !reflect.DeepEqual(got, tt.want) { t.Errorf("pkcs7Padding.Pad() = %v, want %v", got, tt.want) } }) @@ -43,17 +42,13 @@ func Test_pkcs7Padding_Pad(t *testing.T) { } func Test_pkcs7Padding_Unpad(t *testing.T) { - pad := NewPKCS7Padding(16) - type args struct { - src []byte - } + pkcs7 := NewPKCS7Padding(16) tests := []struct { name string want []byte src []byte wantErr bool }{ - // TODO: Add test cases. {"16 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16, 16}, false}, {"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 1}, false}, {"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 2, 2}, false}, @@ -76,7 +71,7 @@ func Test_pkcs7Padding_Unpad(t *testing.T) { } for _, tt := range tests { t.Run(tt.name, func(t *testing.T) { - got, err := pad.Unpad(tt.src) + got, err := pkcs7.Unpad(tt.src) if (err != nil) != tt.wantErr { t.Errorf("pkcs7Padding.Unpad() error = %v, wantErr %v", err, tt.wantErr) return diff --git a/padding/pads.go b/padding/schemes.go similarity index 66% rename from padding/pads.go rename to padding/schemes.go index 7728e34..8e762e3 100644 --- a/padding/pads.go +++ b/padding/schemes.go @@ -13,3 +13,10 @@ func NewPKCS7Padding(blockSize uint) Padding { } return pkcs7Padding(blockSize) } + +func NewANSIX923Padding(blockSize uint) Padding { + if blockSize == 0 || blockSize > 255 { + panic("padding: invalid block size") + } + return ansiX923Padding(blockSize) +} diff --git a/sm4_test/cbc_sm4_test.go b/sm4_test/cbc_sm4_test.go index bba1655..f478d00 100644 --- a/sm4_test/cbc_sm4_test.go +++ b/sm4_test/cbc_sm4_test.go @@ -118,7 +118,7 @@ var cbcSM4Tests = []struct { } func TestCBCEncrypterSM4(t *testing.T) { - pad := padding.NewPKCS7Padding(sm4.BlockSize) + pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize) for _, test := range cbcSM4Tests { c, err := sm4.NewCipher(test.key) if err != nil { @@ -128,7 +128,7 @@ func TestCBCEncrypterSM4(t *testing.T) { encrypter := cipher.NewCBCEncrypter(c, test.iv) - plainText := pad.Pad(test.in) + plainText := pkcs7.Pad(test.in) data := make([]byte, len(plainText)) copy(data, plainText) @@ -146,7 +146,7 @@ func TestCBCEncrypterSM4(t *testing.T) { } func TestCBCDecrypterSM4(t *testing.T) { - pad := padding.NewPKCS7Padding(sm4.BlockSize) + pkcs7 := padding.NewPKCS7Padding(sm4.BlockSize) for _, test := range cbcSM4Tests { c, err := sm4.NewCipher(test.key) if err != nil { @@ -160,7 +160,7 @@ func TestCBCDecrypterSM4(t *testing.T) { copy(data, test.out) decrypter.CryptBlocks(data, data) - data, err = pad.Unpad(data) + data, err = pkcs7.Unpad(data) if err != nil { t.Fatal(err) }