mirror of
https://github.com/emmansun/gmsm.git
synced 2025-04-26 20:26:19 +08:00
padding: implement ISO IEC9797-1 padding method 3 #319
This commit is contained in:
parent
5734e67634
commit
069babe703
@ -31,17 +31,21 @@ func (pad iso9797M2Padding) Unpad(src []byte) ([]byte, error) {
|
||||
if srcLen == 0 || srcLen%pad.BlockSize() != 0 {
|
||||
return nil, errors.New("padding: src length is not multiple of block size")
|
||||
}
|
||||
padStart := -1
|
||||
|
||||
for i, b := range src[srcLen-pad.BlockSize() : srcLen] {
|
||||
if b == 0x80 {
|
||||
tail := src[srcLen-pad.BlockSize():]
|
||||
allZero := true
|
||||
padStart := 0
|
||||
for i := pad.BlockSize() - 1; i >= 0; i-- {
|
||||
if tail[i] == 0x80 {
|
||||
padStart = i
|
||||
} else if b != 0 && padStart >=0 {
|
||||
padStart = -1
|
||||
break
|
||||
}
|
||||
if tail[i] != 0 {
|
||||
allZero = false
|
||||
break
|
||||
}
|
||||
}
|
||||
if padStart >= 0 {
|
||||
if !allZero {
|
||||
return nil, errors.New("padding: inconsistent padding bytes")
|
||||
}
|
||||
return src[:srcLen-pad.BlockSize()+padStart], nil
|
||||
}
|
||||
return src, nil
|
||||
}
|
||||
|
@ -28,6 +28,7 @@ func Test_iso9797M2Padding_Pad(t *testing.T) {
|
||||
{"3 bytes", []byte{0, 1, 2}, []byte{0, 1, 2, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"2 bytes", []byte{0, 1}, []byte{0, 1, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"1 bytes", []byte{0}, []byte{0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"0 bytes", []byte{}, []byte{0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
@ -62,10 +63,12 @@ func Test_iso9797M2Padding_Unpad(t *testing.T) {
|
||||
{"3 bytes", []byte{0, 1, 2}, []byte{0, 1, 2, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"2 bytes", []byte{0, 1}, []byte{0, 1, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"1 bytes", []byte{0}, []byte{0, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"0 bytes", []byte{}, []byte{0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"1 bytes with tag", []byte{0x80}, []byte{0x80, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"3 bytes with tag", []byte{0x80, 0, 0x80}, []byte{0x80, 0, 0x80, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"19 bytes with tag", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0x80, 0, 0x80}, []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15, 0x80, 0, 0x80, 0x80, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"invalid src length", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
|
||||
{"inconsistent padding bytes", nil, []byte{0, 0x80, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
|
71
padding/iso9797_m3.go
Normal file
71
padding/iso9797_m3.go
Normal file
@ -0,0 +1,71 @@
|
||||
package padding
|
||||
|
||||
import (
|
||||
"errors"
|
||||
|
||||
"github.com/emmansun/gmsm/internal/byteorder"
|
||||
)
|
||||
|
||||
// The padded data comprises (in this order):
|
||||
//
|
||||
// - The length of the unpadded data (in bits) expressed in big-endian binary in n bits (i.e. one cipher block)
|
||||
// - The unpadded data
|
||||
// - As many (possibly none) bits with value 0 as are required to bring the total length to a multiple of n bits
|
||||
// It is not necessary to transmit or store the padding bits, because the recipient can regenerate them, knowing the length of the unpadded data and the padding method used.
|
||||
//
|
||||
// https://en.wikipedia.org/wiki/ISO/IEC_9797-1#Padding_method_3
|
||||
// also GB/T 17964-2021 C.4 Padding method 3
|
||||
type iso9797M3Padding uint
|
||||
|
||||
func (pad iso9797M3Padding) BlockSize() int {
|
||||
return int(pad)
|
||||
}
|
||||
|
||||
func (pad iso9797M3Padding) Pad(src []byte) []byte {
|
||||
srcLen := len(src)
|
||||
overhead := pad.BlockSize() - srcLen%pad.BlockSize()
|
||||
if overhead == pad.BlockSize() && srcLen > 0 {
|
||||
overhead = 0
|
||||
}
|
||||
|
||||
var head, tail []byte
|
||||
total := srcLen + overhead + pad.BlockSize()
|
||||
if cap(src) >= total {
|
||||
head = src[:total]
|
||||
} else {
|
||||
head = make([]byte, total)
|
||||
}
|
||||
|
||||
tail = head[srcLen+pad.BlockSize():]
|
||||
clear(head[:pad.BlockSize()])
|
||||
copy(head[pad.BlockSize():], src)
|
||||
if overhead > 0 {
|
||||
clear(tail)
|
||||
}
|
||||
byteorder.BEPutUint64(head[8:], uint64(srcLen*8))
|
||||
return head
|
||||
}
|
||||
|
||||
// Unpad decrypted plaintext, non-constant-time
|
||||
func (pad iso9797M3Padding) Unpad(src []byte) ([]byte, error) {
|
||||
srcLen := len(src)
|
||||
if srcLen < 2*pad.BlockSize() || srcLen%pad.BlockSize() != 0 {
|
||||
return nil, errors.New("padding: invalid src length")
|
||||
}
|
||||
for _, b := range src[:8] {
|
||||
if b != 0 {
|
||||
return nil, errors.New("padding: invalid padding header")
|
||||
}
|
||||
}
|
||||
dstLen := int(byteorder.BEUint64(src[8:pad.BlockSize()])/8)
|
||||
if dstLen < 0 || dstLen > srcLen-pad.BlockSize() {
|
||||
return nil, errors.New("padding: invalid padding header")
|
||||
}
|
||||
padded := src[pad.BlockSize()+dstLen:]
|
||||
for _, b := range padded {
|
||||
if b != 0 {
|
||||
return nil, errors.New("padding: invalid padding bytes")
|
||||
}
|
||||
}
|
||||
return src[pad.BlockSize() : pad.BlockSize()+dstLen], nil
|
||||
}
|
84
padding/iso9797_m3_test.go
Normal file
84
padding/iso9797_m3_test.go
Normal file
@ -0,0 +1,84 @@
|
||||
package padding
|
||||
|
||||
import (
|
||||
"reflect"
|
||||
"testing"
|
||||
)
|
||||
|
||||
func Test_iso9797M3Padding_Pad(t *testing.T) {
|
||||
iso9797 := NewISO9797M3Padding(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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}},
|
||||
{"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}},
|
||||
{"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0}},
|
||||
{"13 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0}},
|
||||
{"12 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 0}},
|
||||
{"11 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0}},
|
||||
{"10 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0}},
|
||||
{"9 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"8 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"7 bytes", []byte{0, 1, 2, 3, 4, 5, 6}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"6 bytes", []byte{0, 1, 2, 3, 4, 5}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"5 bytes", []byte{0, 1, 2, 3, 4}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"4 bytes", []byte{0, 1, 2, 3}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"3 bytes", []byte{0, 1, 2}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"2 bytes", []byte{0, 1}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"1 bytes", []byte{1}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
{"0 bytes", []byte{}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
if got := iso9797.Pad(tt.src); !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("iso9797M2Padding.Pad() = %v, want %v", got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func Test_iso9797M3Padding_Unpad(t *testing.T) {
|
||||
iso9797 := NewISO9797M3Padding(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, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 128, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 15}, false},
|
||||
{"15 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 120, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 14, 0}, false},
|
||||
{"14 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 112, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 13, 0, 0}, false},
|
||||
{"13 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 104, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 12, 0, 0, 0}, false},
|
||||
{"12 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 96, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 11, 0, 0, 0, 0}, false},
|
||||
{"11 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 88, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 10, 0, 0, 0, 0, 0}, false},
|
||||
{"10 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8, 9}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 80, 0, 1, 2, 3, 4, 5, 6, 7, 8, 9, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"9 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7, 8}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 72, 0, 1, 2, 3, 4, 5, 6, 7, 8, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"8 bytes", []byte{0, 1, 2, 3, 4, 5, 6, 7}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 64, 0, 1, 2, 3, 4, 5, 6, 7, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"7 bytes", []byte{0, 1, 2, 3, 4, 5, 6}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 56, 0, 1, 2, 3, 4, 5, 6, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"6 bytes", []byte{0, 1, 2, 3, 4, 5}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 48, 0, 1, 2, 3, 4, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"5 bytes", []byte{0, 1, 2, 3, 4}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 40, 0, 1, 2, 3, 4, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"4 bytes", []byte{0, 1, 2, 3}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 1, 2, 3, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"3 bytes", []byte{0, 1, 2}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 24, 0, 1, 2, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"2 bytes", []byte{0, 1}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"1 bytes", []byte{1}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 8, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"0 bytes", []byte{}, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, false},
|
||||
{"invalid length", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
|
||||
{"invalid padding header", nil, []byte{1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
|
||||
{"invalid padding header 2", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 15, 15, 15, 15, 15, 15, 15, 15, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0}, true},
|
||||
{"invalid padding bytes", nil, []byte{0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 1}, true},
|
||||
}
|
||||
for _, tt := range tests {
|
||||
t.Run(tt.name, func(t *testing.T) {
|
||||
got, err := iso9797.Unpad(tt.src)
|
||||
if (err != nil) != tt.wantErr {
|
||||
t.Errorf("case %v: iso9797M2Padding.Unpad() error = %v, wantErr %v", tt.name, err, tt.wantErr)
|
||||
return
|
||||
}
|
||||
if !reflect.DeepEqual(got, tt.want) {
|
||||
t.Errorf("case %v: iso9797M2Padding.Unpad() = %v, want %v", tt.name, got, tt.want)
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
@ -28,3 +28,10 @@ func NewISO9797M2Padding(blockSize uint) Padding {
|
||||
}
|
||||
return iso9797M2Padding(blockSize)
|
||||
}
|
||||
|
||||
func NewISO9797M3Padding(blockSize uint) Padding {
|
||||
if blockSize == 0 || blockSize > 255 {
|
||||
panic("padding: invalid block size")
|
||||
}
|
||||
return iso9797M3Padding(blockSize)
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user