New Upstream Release - golang-gopkg-jcmturner-aescts.v1

Ready changes

Summary

Merged new upstream version: 2.0.0 (was: 1.0.1).

Resulting package

Built on 2022-11-24T02:42 (took 3m36s)

The resulting binary packages can be installed (if you have the apt repository enabled) by running one of:

apt install -t fresh-releases golang-gopkg-jcmturner-aescts.v1-dev

Lintian Result

Diff

diff --git a/.github/workflows/testing.yml b/.github/workflows/testing.yml
new file mode 100644
index 0000000..46478fa
--- /dev/null
+++ b/.github/workflows/testing.yml
@@ -0,0 +1,67 @@
+name: v1
+on:
+  push:
+    paths-ignore:
+      - 'v[0-9]+/**'
+  pull_request:
+    paths-ignore:
+      - 'v[0-9]+/**'
+
+jobs:
+  build:
+    name: Tests
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        go: [ '1.11.x', '1.12.x', '1.13.x' ]
+    steps:
+      - name: Set up Go ${{ matrix.go }}
+        uses: actions/setup-go@v1
+        with:
+          go-version: ${{ matrix.go }}
+
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: ${{ github.ref }}
+
+      - name: Test well formatted with gofmt
+        run: |
+          # Remove major version sub directories
+          find . -maxdepth 1 -type d -regex '\./v[0-9]+' | xargs -i rm -rf {}
+          GO_FILES=$(find . -iname '*.go' -type f | grep -v /vendor/)
+          test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g')
+        id: gofmt
+
+      - name: Copy into GOPATH
+        run: |
+          # Default GOPATH=${HOME}/go
+          mkdir -p ${HOME}/go/src/github.com/${GITHUB_REPOSITORY}
+          cp -r $GITHUB_WORKSPACE/*  /home/runner/go/src/github.com/${GITHUB_REPOSITORY}
+        id: copyToGOPATH
+
+      - name: Get dependencies
+        run: |
+          cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY}
+          go get -v -t -d ./...
+        id: goGet
+
+      - name: Go vet
+        run: |
+          cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY}
+          go vet $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/)
+        id: govet
+
+      - name: Unit tests
+        run: |
+          cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY}
+          go test -race $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/)
+        id: unitTests
+
+      - name: Unit tests (32bit)
+        run: |
+          cd ${HOME}/go/src/github.com/${GITHUB_REPOSITORY}
+          go test $(go list ./... | grep -E -v '/v[0-9]+' | grep -v /vendor/)
+        env:
+          GOARCH: 386
+        id: unitTest32
\ No newline at end of file
diff --git a/.github/workflows/testingv2.yml b/.github/workflows/testingv2.yml
new file mode 100644
index 0000000..ec65a3e
--- /dev/null
+++ b/.github/workflows/testingv2.yml
@@ -0,0 +1,53 @@
+# Name of the workflow needs to match the name of the major version directory
+name: v2
+on:
+  push:
+    paths:
+      - 'v2/**'
+  pull_request:
+    paths:
+      - 'v2/**'
+
+jobs:
+  build:
+    name: Tests
+    runs-on: ubuntu-latest
+    strategy:
+      matrix:
+        go: [ '1.11.x', '1.12.x', '1.13.x' ]
+    steps:
+      - name: Set up Go ${{ matrix.go }}
+        uses: actions/setup-go@v1
+        with:
+          go-version: ${{ matrix.go }}
+
+      - name: Checkout
+        uses: actions/checkout@v2
+        with:
+          ref: ${{ github.ref }}
+
+      - name: Test well formatted with gofmt
+        run: |
+          GO_FILES=$(find ${GITHUB_WORKFLOW} -iname '*.go' -type f | grep -v /vendor/)
+          test -z $(gofmt -s -d -l -e $GO_FILES | tee /dev/fd/2 | xargs | sed 's/\s//g')
+        id: gofmt
+
+      - name: Go vet
+        run: |
+          cd ${GITHUB_WORKFLOW}
+          go vet ./...
+        id: govet
+
+      - name: Unit tests
+        run: |
+          cd ${GITHUB_WORKFLOW}
+          go test -race ./...
+        id: unitTests
+
+      - name: Unit tests (32bit)
+        run: |
+          cd ${GITHUB_WORKFLOW}
+          go test ./...
+        env:
+          GOARCH: 386
+        id: unitTest32
\ No newline at end of file
diff --git a/aescts.go b/aescts.go
index 278713e..fee3b43 100644
--- a/aescts.go
+++ b/aescts.go
@@ -15,7 +15,7 @@ func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) {
 
 	block, err := aes.NewCipher(key)
 	if err != nil {
-		return []byte{}, []byte{}, fmt.Errorf("Error creating cipher: %v", err)
+		return []byte{}, []byte{}, fmt.Errorf("error creating cipher: %v", err)
 	}
 	mode := cipher.NewCBCEncrypter(block, iv)
 
@@ -43,7 +43,7 @@ func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) {
 	m, _ = zeroPad(m, aes.BlockSize)
 	rb, pb, lb, err := tailBlocks(m, aes.BlockSize)
 	if err != nil {
-		return []byte{}, []byte{}, fmt.Errorf("Error tailing blocks: %v", err)
+		return []byte{}, []byte{}, fmt.Errorf("error tailing blocks: %v", err)
 	}
 	var ct []byte
 	if rb != nil {
@@ -70,12 +70,12 @@ func Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
 	ct := make([]byte, len(ciphertext))
 	copy(ct, ciphertext)
 	if len(ct) < aes.BlockSize {
-		return []byte{}, fmt.Errorf("Ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
+		return []byte{}, fmt.Errorf("ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
 	}
 	// Configure the CBC
 	block, err := aes.NewCipher(key)
 	if err != nil {
-		return nil, fmt.Errorf("Error creating cipher: %v", err)
+		return nil, fmt.Errorf("error creating cipher: %v", err)
 	}
 	var mode cipher.BlockMode
 
@@ -172,10 +172,10 @@ func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
 // zeroPad pads bytes with zeros to nearest multiple of message size m.
 func zeroPad(b []byte, m int) ([]byte, error) {
 	if m <= 0 {
-		return nil, errors.New("Invalid message block size when padding")
+		return nil, errors.New("invalid message block size when padding")
 	}
 	if b == nil || len(b) == 0 {
-		return nil, errors.New("Data not valid to pad: Zero size")
+		return nil, errors.New("data not valid to pad: Zero size")
 	}
 	if l := len(b) % m; l != 0 {
 		n := m - l
diff --git a/debian/changelog b/debian/changelog
index fe5a270..a0462d3 100644
--- a/debian/changelog
+++ b/debian/changelog
@@ -1,3 +1,9 @@
+golang-gopkg-jcmturner-aescts.v1 (2.0.0-1) UNRELEASED; urgency=low
+
+  * New upstream release.
+
+ -- Debian Janitor <janitor@jelmer.uk>  Thu, 24 Nov 2022 02:39:02 -0000
+
 golang-gopkg-jcmturner-aescts.v1 (1.0.1-4) unstable; urgency=medium
 
   [ Debian Janitor ]
diff --git a/v2/aescts.go b/v2/aescts.go
new file mode 100644
index 0000000..fee3b43
--- /dev/null
+++ b/v2/aescts.go
@@ -0,0 +1,186 @@
+// Package aescts provides AES CBC CipherText Stealing encryption and decryption methods
+package aescts
+
+import (
+	"crypto/aes"
+	"crypto/cipher"
+	"errors"
+	"fmt"
+)
+
+// Encrypt the message with the key and the initial vector.
+// Returns: next iv, ciphertext bytes, error
+func Encrypt(key, iv, plaintext []byte) ([]byte, []byte, error) {
+	l := len(plaintext)
+
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return []byte{}, []byte{}, fmt.Errorf("error creating cipher: %v", err)
+	}
+	mode := cipher.NewCBCEncrypter(block, iv)
+
+	m := make([]byte, len(plaintext))
+	copy(m, plaintext)
+
+	/*For consistency, ciphertext stealing is always used for the last two
+	blocks of the data to be encrypted, as in [RC5].  If the data length
+	is a multiple of the block size, this is equivalent to plain CBC mode
+	with the last two ciphertext blocks swapped.*/
+	/*The initial vector carried out from one encryption for use in a
+	subsequent encryption is the next-to-last block of the encryption
+	output; this is the encrypted form of the last plaintext block.*/
+	if l <= aes.BlockSize {
+		m, _ = zeroPad(m, aes.BlockSize)
+		mode.CryptBlocks(m, m)
+		return m, m, nil
+	}
+	if l%aes.BlockSize == 0 {
+		mode.CryptBlocks(m, m)
+		iv = m[len(m)-aes.BlockSize:]
+		rb, _ := swapLastTwoBlocks(m, aes.BlockSize)
+		return iv, rb, nil
+	}
+	m, _ = zeroPad(m, aes.BlockSize)
+	rb, pb, lb, err := tailBlocks(m, aes.BlockSize)
+	if err != nil {
+		return []byte{}, []byte{}, fmt.Errorf("error tailing blocks: %v", err)
+	}
+	var ct []byte
+	if rb != nil {
+		// Encrpt all but the lats 2 blocks and update the rolling iv
+		mode.CryptBlocks(rb, rb)
+		iv = rb[len(rb)-aes.BlockSize:]
+		mode = cipher.NewCBCEncrypter(block, iv)
+		ct = append(ct, rb...)
+	}
+	mode.CryptBlocks(pb, pb)
+	mode = cipher.NewCBCEncrypter(block, pb)
+	mode.CryptBlocks(lb, lb)
+	// Cipher Text Stealing (CTS) - Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
+	// Swap the last two cipher blocks
+	// Truncate the ciphertext to the length of the original plaintext
+	ct = append(ct, lb...)
+	ct = append(ct, pb...)
+	return lb, ct[:l], nil
+}
+
+// Decrypt the ciphertext with the key and the initial vector.
+func Decrypt(key, iv, ciphertext []byte) ([]byte, error) {
+	// Copy the cipher text as golang slices even when passed by value to this method can result in the backing arrays of the calling code value being updated.
+	ct := make([]byte, len(ciphertext))
+	copy(ct, ciphertext)
+	if len(ct) < aes.BlockSize {
+		return []byte{}, fmt.Errorf("ciphertext is not large enough. It is less that one block size. Blocksize:%v; Ciphertext:%v", aes.BlockSize, len(ct))
+	}
+	// Configure the CBC
+	block, err := aes.NewCipher(key)
+	if err != nil {
+		return nil, fmt.Errorf("error creating cipher: %v", err)
+	}
+	var mode cipher.BlockMode
+
+	//If ciphertext is multiple of blocksize we just need to swap back the last two blocks and then do CBC
+	//If the ciphertext is just one block we can't swap so we just decrypt
+	if len(ct)%aes.BlockSize == 0 {
+		if len(ct) > aes.BlockSize {
+			ct, _ = swapLastTwoBlocks(ct, aes.BlockSize)
+		}
+		mode = cipher.NewCBCDecrypter(block, iv)
+		message := make([]byte, len(ct))
+		mode.CryptBlocks(message, ct)
+		return message[:len(ct)], nil
+	}
+
+	// Cipher Text Stealing (CTS) using CBC interface. Ref: https://en.wikipedia.org/wiki/Ciphertext_stealing#CBC_ciphertext_stealing
+	// Get ciphertext of the 2nd to last (penultimate) block (cpb), the last block (clb) and the rest (crb)
+	crb, cpb, clb, _ := tailBlocks(ct, aes.BlockSize)
+	v := make([]byte, len(iv), len(iv))
+	copy(v, iv)
+	var message []byte
+	if crb != nil {
+		//If there is more than just the last and the penultimate block we decrypt it and the last bloc of this becomes the iv for later
+		rb := make([]byte, len(crb))
+		mode = cipher.NewCBCDecrypter(block, v)
+		v = crb[len(crb)-aes.BlockSize:]
+		mode.CryptBlocks(rb, crb)
+		message = append(message, rb...)
+	}
+
+	// We need to modify the cipher text
+	// Decryt the 2nd to last (penultimate) block with a the original iv
+	pb := make([]byte, aes.BlockSize)
+	mode = cipher.NewCBCDecrypter(block, iv)
+	mode.CryptBlocks(pb, cpb)
+	// number of byte needed to pad
+	npb := aes.BlockSize - len(ct)%aes.BlockSize
+	//pad last block using the number of bytes needed from the tail of the plaintext 2nd to last (penultimate) block
+	clb = append(clb, pb[len(pb)-npb:]...)
+
+	// Now decrypt the last block in the penultimate position (iv will be from the crb, if the is no crb it's zeros)
+	// iv for the penultimate block decrypted in the last position becomes the modified last block
+	lb := make([]byte, aes.BlockSize)
+	mode = cipher.NewCBCDecrypter(block, v)
+	v = clb
+	mode.CryptBlocks(lb, clb)
+	message = append(message, lb...)
+
+	// Now decrypt the penultimate block in the last position (iv will be from the modified last block)
+	mode = cipher.NewCBCDecrypter(block, v)
+	mode.CryptBlocks(cpb, cpb)
+	message = append(message, cpb...)
+
+	// Truncate to the size of the original cipher text
+	return message[:len(ct)], nil
+}
+
+func tailBlocks(b []byte, c int) ([]byte, []byte, []byte, error) {
+	if len(b) <= c {
+		return []byte{}, []byte{}, []byte{}, errors.New("bytes slice is not larger than one block so cannot tail")
+	}
+	// Get size of last block
+	var lbs int
+	if l := len(b) % aes.BlockSize; l == 0 {
+		lbs = aes.BlockSize
+	} else {
+		lbs = l
+	}
+	// Get last block
+	lb := b[len(b)-lbs:]
+	// Get 2nd to last (penultimate) block
+	pb := b[len(b)-lbs-c : len(b)-lbs]
+	if len(b) > 2*c {
+		rb := b[:len(b)-lbs-c]
+		return rb, pb, lb, nil
+	}
+	return nil, pb, lb, nil
+}
+
+func swapLastTwoBlocks(b []byte, c int) ([]byte, error) {
+	rb, pb, lb, err := tailBlocks(b, c)
+	if err != nil {
+		return nil, err
+	}
+	var out []byte
+	if rb != nil {
+		out = append(out, rb...)
+	}
+	out = append(out, lb...)
+	out = append(out, pb...)
+	return out, nil
+}
+
+// zeroPad pads bytes with zeros to nearest multiple of message size m.
+func zeroPad(b []byte, m int) ([]byte, error) {
+	if m <= 0 {
+		return nil, errors.New("invalid message block size when padding")
+	}
+	if b == nil || len(b) == 0 {
+		return nil, errors.New("data not valid to pad: Zero size")
+	}
+	if l := len(b) % m; l != 0 {
+		n := m - l
+		z := make([]byte, n)
+		b = append(b, z...)
+	}
+	return b, nil
+}
diff --git a/v2/aescts_test.go b/v2/aescts_test.go
new file mode 100644
index 0000000..1a1da4f
--- /dev/null
+++ b/v2/aescts_test.go
@@ -0,0 +1,44 @@
+package aescts
+
+import (
+	"encoding/hex"
+	"github.com/stretchr/testify/assert"
+	"testing"
+)
+
+func TestAesCts_Encrypt_Decrypt(t *testing.T) {
+	iv := make([]byte, 16)
+	key, _ := hex.DecodeString("636869636b656e207465726979616b69")
+	var tests = []struct {
+		plain  string
+		cipher string
+		nextIV string
+	}{
+		//Test vectors from RFC 3962 Appendix B
+		{"4920776f756c64206c696b652074686520", "c6353568f2bf8cb4d8a580362da7ff7f97", "c6353568f2bf8cb4d8a580362da7ff7f"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320", "fc00783e0efdb2c1d445d4c8eff7ed2297687268d6ecccc0c07b25e25ecfe5", "fc00783e0efdb2c1d445d4c8eff7ed22"},
+		{"4920776f756c64206c696b65207468652047656e6572616c2047617527732043", "39312523a78662d5be7fcbcc98ebf5a897687268d6ecccc0c07b25e25ecfe584", "39312523a78662d5be7fcbcc98ebf5a8"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c", "97687268d6ecccc0c07b25e25ecfe584b3fffd940c16a18c1b5549d2f838029e39312523a78662d5be7fcbcc98ebf5", "b3fffd940c16a18c1b5549d2f838029e"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20", "97687268d6ecccc0c07b25e25ecfe5849dad8bbb96c4cdc03bc103e1a194bbd839312523a78662d5be7fcbcc98ebf5a8", "9dad8bbb96c4cdc03bc103e1a194bbd8"},
+		{"4920776f756c64206c696b65207468652047656e6572616c20476175277320436869636b656e2c20706c656173652c20616e6420776f6e746f6e20736f75702e", "97687268d6ecccc0c07b25e25ecfe58439312523a78662d5be7fcbcc98ebf5a84807efe836ee89a526730dbc2f7bc8409dad8bbb96c4cdc03bc103e1a194bbd8", "4807efe836ee89a526730dbc2f7bc840"},
+	}
+	for i, test := range tests {
+		m, _ := hex.DecodeString(test.plain)
+		niv, c, err := Encrypt(key, iv, m)
+		if err != nil {
+			t.Errorf("Encryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.cipher, hex.EncodeToString(c), "Encrypted result not as expected")
+		assert.Equal(t, test.nextIV, hex.EncodeToString(niv), "Next state IV not as expected")
+	}
+	//t.Log("AES CTS Encryption tests finished")
+	for i, test := range tests {
+		b, _ := hex.DecodeString(test.cipher)
+		p, err := Decrypt(key, iv, b)
+		if err != nil {
+			t.Errorf("Decryption failed for test %v: %v", i+1, err)
+		}
+		assert.Equal(t, test.plain, hex.EncodeToString(p), "Decrypted result not as expected")
+	}
+	//t.Log("AES CTS Decryption tests finished")
+}
diff --git a/v2/go.mod b/v2/go.mod
new file mode 100644
index 0000000..034c3ce
--- /dev/null
+++ b/v2/go.mod
@@ -0,0 +1,5 @@
+module github.com/jcmturner/aescts/v2
+
+go 1.13
+
+require github.com/stretchr/testify v1.4.0
diff --git a/v2/go.sum b/v2/go.sum
new file mode 100644
index 0000000..e863f51
--- /dev/null
+++ b/v2/go.sum
@@ -0,0 +1,10 @@
+github.com/davecgh/go-spew v1.1.0 h1:ZDRjVQ15GmhC3fiQ8ni8+OwkZQO4DARzQgrnXU1Liz8=
+github.com/davecgh/go-spew v1.1.0/go.mod h1:J7Y8YcW2NihsgmVo/mv3lAwl/skON4iLHjSsI+c5H38=
+github.com/pmezard/go-difflib v1.0.0 h1:4DBwDE0NGyQoBHbLQYPwSUPoCMWR5BEzIk/f1lZbAQM=
+github.com/pmezard/go-difflib v1.0.0/go.mod h1:iKH77koFhYxTK1pcRnkKkqfTogsbg7gZNVY4sRDYZ/4=
+github.com/stretchr/objx v0.1.0/go.mod h1:HFkY916IF+rwdDfMAkV7OtwuqBVzrE8GR6GFx+wExME=
+github.com/stretchr/testify v1.4.0 h1:2E4SXV/wtOkTonXsotYi4li6zVWxYlZuYNCXe9XRJyk=
+github.com/stretchr/testify v1.4.0/go.mod h1:j7eGeouHqKxXV5pUuKE4zz7dFj8WfuZ+81PSLYec5m4=
+gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0=
+gopkg.in/yaml.v2 v2.2.2 h1:ZCJp+EgiOT7lHqUV2J862kp8Qj64Jo6az82+3Td9dZw=
+gopkg.in/yaml.v2 v2.2.2/go.mod h1:hI93XBmqTisBFMUTm0b8Fm+jr3Dg1NNxqwp+5A1VGuI=

Debdiff

[The following lists of changes regard files as different if they have different names, permissions or owners.]

Files in second set of .debs but not in first

-rw-r--r--  root/root   /usr/share/gocode/src/gopkg.in/jcmturner/aescts.v1/v2/aescts.go
-rw-r--r--  root/root   /usr/share/gocode/src/gopkg.in/jcmturner/aescts.v1/v2/aescts_test.go

No differences were encountered in the control files

More details

Full run details