Browse Source

Initial import.

Yawning Angel 2 years ago
commit
381a9e7fe9
6 changed files with 508 additions and 0 deletions
  1. 122 0
      LICENSE
  2. 9 0
      README.md
  3. 209 0
      lioness.go
  4. 119 0
      lioness_test.go
  5. 29 0
      xorbytes_amd64.go
  6. 20 0
      xorbytes_amd64.s

+ 122 - 0
LICENSE

@@ -0,0 +1,122 @@
+Creative Commons Legal Code
+
+CC0 1.0 Universal
+
+    CREATIVE COMMONS CORPORATION IS NOT A LAW FIRM AND DOES NOT PROVIDE
+    LEGAL SERVICES. DISTRIBUTION OF THIS DOCUMENT DOES NOT CREATE AN
+    ATTORNEY-CLIENT RELATIONSHIP. CREATIVE COMMONS PROVIDES THIS
+    INFORMATION ON AN "AS-IS" BASIS. CREATIVE COMMONS MAKES NO WARRANTIES
+    REGARDING THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS
+    PROVIDED HEREUNDER, AND DISCLAIMS LIABILITY FOR DAMAGES RESULTING FROM
+    THE USE OF THIS DOCUMENT OR THE INFORMATION OR WORKS PROVIDED
+    HEREUNDER.
+
+Statement of Purpose
+
+The laws of most jurisdictions throughout the world automatically confer
+exclusive Copyright and Related Rights (defined below) upon the creator
+and subsequent owner(s) (each and all, an "owner") of an original work of
+authorship and/or a database (each, a "Work").
+
+Certain owners wish to permanently relinquish those rights to a Work for
+the purpose of contributing to a commons of creative, cultural and
+scientific works ("Commons") that the public can reliably and without fear
+of later claims of infringement build upon, modify, incorporate in other
+works, reuse and redistribute as freely as possible in any form whatsoever
+and for any purposes, including without limitation commercial purposes.
+These owners may contribute to the Commons to promote the ideal of a free
+culture and the further production of creative, cultural and scientific
+works, or to gain reputation or greater distribution for their Work in
+part through the use and efforts of others.
+
+For these and/or other purposes and motivations, and without any
+expectation of additional consideration or compensation, the person
+associating CC0 with a Work (the "Affirmer"), to the extent that he or she
+is an owner of Copyright and Related Rights in the Work, voluntarily
+elects to apply CC0 to the Work and publicly distribute the Work under its
+terms, with knowledge of his or her Copyright and Related Rights in the
+Work and the meaning and intended legal effect of CC0 on those rights.
+
+1. Copyright and Related Rights. A Work made available under CC0 may be
+protected by copyright and related or neighboring rights ("Copyright and
+Related Rights"). Copyright and Related Rights include, but are not
+limited to, the following:
+
+  i. the right to reproduce, adapt, distribute, perform, display,
+     communicate, and translate a Work;
+ ii. moral rights retained by the original author(s) and/or performer(s);
+iii. publicity and privacy rights pertaining to a person's image or
+     likeness depicted in a Work;
+ iv. rights protecting against unfair competition in regards to a Work,
+     subject to the limitations in paragraph 4(a), below;
+  v. rights protecting the extraction, dissemination, use and reuse of data
+     in a Work;
+ vi. database rights (such as those arising under Directive 96/9/EC of the
+     European Parliament and of the Council of 11 March 1996 on the legal
+     protection of databases, and under any national implementation
+     thereof, including any amended or successor version of such
+     directive); and
+vii. other similar, equivalent or corresponding rights throughout the
+     world based on applicable law or treaty, and any national
+     implementations thereof.
+
+2. Waiver. To the greatest extent permitted by, but not in contravention
+of, applicable law, Affirmer hereby overtly, fully, permanently,
+irrevocably and unconditionally waives, abandons, and surrenders all of
+Affirmer's Copyright and Related Rights and associated claims and causes
+of action, whether now known or unknown (including existing as well as
+future claims and causes of action), in the Work (i) in all territories
+worldwide, (ii) for the maximum duration provided by applicable law or
+treaty (including future time extensions), (iii) in any current or future
+medium and for any number of copies, and (iv) for any purpose whatsoever,
+including without limitation commercial, advertising or promotional
+purposes (the "Waiver"). Affirmer makes the Waiver for the benefit of each
+member of the public at large and to the detriment of Affirmer's heirs and
+successors, fully intending that such Waiver shall not be subject to
+revocation, rescission, cancellation, termination, or any other legal or
+equitable action to disrupt the quiet enjoyment of the Work by the public
+as contemplated by Affirmer's express Statement of Purpose.
+
+3. Public License Fallback. Should any part of the Waiver for any reason
+be judged legally invalid or ineffective under applicable law, then the
+Waiver shall be preserved to the maximum extent permitted taking into
+account Affirmer's express Statement of Purpose. In addition, to the
+extent the Waiver is so judged Affirmer hereby grants to each affected
+person a royalty-free, non transferable, non sublicensable, non exclusive,
+irrevocable and unconditional license to exercise Affirmer's Copyright and
+Related Rights in the Work (i) in all territories worldwide, (ii) for the
+maximum duration provided by applicable law or treaty (including future
+time extensions), (iii) in any current or future medium and for any number
+of copies, and (iv) for any purpose whatsoever, including without
+limitation commercial, advertising or promotional purposes (the
+"License"). The License shall be deemed effective as of the date CC0 was
+applied by Affirmer to the Work. Should any part of the License for any
+reason be judged legally invalid or ineffective under applicable law, such
+partial invalidity or ineffectiveness shall not invalidate the remainder
+of the License, and in such case Affirmer hereby affirms that he or she
+will not (i) exercise any of his or her remaining Copyright and Related
+Rights in the Work or (ii) assert any associated claims and causes of
+action with respect to the Work, in either case contrary to Affirmer's
+express Statement of Purpose.
+
+4. Limitations and Disclaimers.
+
+ a. No trademark or patent rights held by Affirmer are waived, abandoned,
+    surrendered, licensed or otherwise affected by this document.
+ b. Affirmer offers the Work as-is and makes no representations or
+    warranties of any kind concerning the Work, express, implied,
+    statutory or otherwise, including without limitation warranties of
+    title, merchantability, fitness for a particular purpose, non
+    infringement, or the absence of latent or other defects, accuracy, or
+    the present or absence of errors, whether or not discoverable, all to
+    the greatest extent permissible under applicable law.
+ c. Affirmer disclaims responsibility for clearing rights of other persons
+    that may apply to the Work or any use thereof, including without
+    limitation any person's Copyright and Related Rights in the Work.
+    Further, Affirmer disclaims responsibility for obtaining any necessary
+    consents, permissions or other rights required for any use of the
+    Work.
+ d. Affirmer understands and acknowledges that Creative Commons is not a
+    party to this document and has no duty or obligation with respect to
+    this CC0 or use of the Work.
+

+ 9 - 0
README.md

@@ -0,0 +1,9 @@
+# lioness - LIONESS-BLAKE2b-ChaCha20
+#### Yawning Angel (yawning at schwanenlied dot me)
+
+Yet another Go LIONESS implementation, similar to go-lioness, though with
+slightly different parameterization, and support for a tweak.  The
+implementations are NOT interoperable.
+
+H() is BLAKE2b with a 256 bit key, 96 bit tweak.
+S() is ChaCha20 with a 256 bit key, 96 bit tweak.

+ 209 - 0
lioness.go

@@ -0,0 +1,209 @@
+// lioness.go - A LIONESS-BLAKE2b-ChaCha20 implementation.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to lioness, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+package lioness
+
+import (
+	"errors"
+
+	"git.schwanenlied.me/yawning/chacha20.git"
+	"github.com/minio/blake2b-simd"
+)
+
+const (
+	// KeySize is the key size in bytes.
+	KeySize = 128
+
+	// IVSize is the initialization vector size in bytes.
+	IVSize = 48
+
+	// MinBlockSize is the minimum block size in bytes.
+	MinBlockSize = 32 + 1
+
+	// MaxBlockSize is the maximum block size in bytes.
+	MaxBlockSize = 32 + (1 << 36)
+
+	lSize = chacha20.KeySize
+)
+
+var (
+	// ErrBlockSize is the error returned when the block size is invalid.
+	ErrBlockSize = errors.New("lioness: invalid block size")
+
+	xorBytes32 = xorBytes32Ref
+)
+
+// Encrypt encrypts a block.  dst and src may overlap.
+func Encrypt(key [KeySize]byte, iv [IVSize]byte, dst, src []byte) error {
+	// XXX: In theory I should enforce MaxBlockSize, but it's > sizeof(int)
+	// on 32 bit systems.
+	if len(src) < MinBlockSize || len(dst) < len(src) {
+		return ErrBlockSize
+	}
+
+	rSize := len(dst) - lSize
+	l := make([]byte, lSize)
+	r := make([]byte, rSize)
+	var tmp [lSize + IVSize/4]byte
+	defer zeroBytes(tmp[:])
+
+	k1 := key[0:32]
+	k2 := key[32:64]
+	k3 := key[64:96]
+	k4 := key[96:128]
+	iv1 := iv[0:12]
+	iv2 := iv[12:24]
+	iv3 := iv[24:36]
+	iv4 := iv[36:48]
+
+	var s chacha20.Cipher
+	defer s.Reset()
+
+	var hCfg blake2b.Config
+	hCfg.Size = lSize
+	hCfg.Key = tmp[:]
+
+	// R = ChaCha20(L ^ k1, iv1, R)
+	xorBytes32(tmp[:lSize], src[0:lSize], k1)
+	if err := s.ReKey(tmp[:lSize], iv1); err != nil {
+		return err
+	}
+	s.XORKeyStream(r, src[lSize:])
+
+	// L = L ^ BLAKE2b(k2 | iv2, R)
+	copy(tmp[:lSize], k2)
+	copy(tmp[lSize:], iv2)
+	h, err := blake2b.New(&hCfg)
+	if err != nil {
+		return err
+	}
+	defer h.Reset()
+	h.Write(r)
+	htmp := h.Sum(nil)
+	xorBytes32(l, src[0:lSize], htmp)
+	htmp = htmp[:0]
+
+	// R = ChaCha20(L ^ k3, iv3, R)
+	xorBytes32(tmp[:lSize], l, k3)
+	if err := s.ReKey(tmp[:lSize], iv3); err != nil {
+		return err
+	}
+	s.XORKeyStream(r, r)
+
+	// L ^ BLAKE2b(k4 | iv4, R)
+	copy(tmp[:lSize], k4)
+	copy(tmp[lSize:], iv4)
+	hh, err := blake2b.New(&hCfg) // I wish blake2b-simd supported rekeying.
+	if err != nil {
+		return err
+	}
+	defer hh.Reset()
+	hh.Write(r)
+	htmp = hh.Sum(htmp)
+	defer zeroBytes(htmp)
+	xorBytes32(l, l, htmp)
+
+	copy(dst, l)
+	copy(dst[lSize:], r)
+
+	return nil
+}
+
+// Decrypt decrypts a block.  dst and src may overlap.
+func Decrypt(key [KeySize]byte, iv [IVSize]byte, dst, src []byte) error {
+	// XXX: In theory I should enforce MaxBlockSize, but it's > sizeof(int)
+	// on 32 bit systems.
+	if len(src) < MinBlockSize || len(dst) < len(src) {
+		return ErrBlockSize
+	}
+
+	k1 := key[0:32]
+	k2 := key[32:64]
+	k3 := key[64:96]
+	k4 := key[96:128]
+	iv1 := iv[0:12]
+	iv2 := iv[12:24]
+	iv3 := iv[24:36]
+	iv4 := iv[36:48]
+
+	rSize := len(dst) - lSize
+	l := make([]byte, lSize)
+	r := make([]byte, rSize)
+	var tmp [lSize + IVSize/4]byte
+	defer zeroBytes(tmp[:])
+
+	var s chacha20.Cipher
+	defer s.Reset()
+
+	var hCfg blake2b.Config
+	hCfg.Size = lSize
+	hCfg.Key = tmp[:]
+
+	// L = L ^ BLAKE2b(k4 | iv4, R)
+	copy(tmp[:lSize], k4)
+	copy(tmp[lSize:], iv4)
+	h, err := blake2b.New(&hCfg)
+	if err != nil {
+		return err
+	}
+	defer h.Reset()
+	h.Write(src[lSize:])
+	htmp := h.Sum(nil)
+	xorBytes32(l, src[0:lSize], htmp)
+	htmp = htmp[:0]
+
+	// R = ChaCha20(L ^ k3, iv3, R)
+	xorBytes32(tmp[:lSize], l, k3)
+	if err := s.ReKey(tmp[:lSize], iv3); err != nil {
+		return err
+	}
+	s.XORKeyStream(r, src[lSize:])
+
+	// L = L ^ BLAKE2b(k2 | iv2, R)
+	copy(tmp[:lSize], k2)
+	copy(tmp[lSize:], iv2)
+	hh, err := blake2b.New(&hCfg) // I wish blake2b-simd supported rekeying.
+	if err != nil {
+		return err
+	}
+	defer hh.Reset()
+	hh.Write(r)
+	htmp = hh.Sum(htmp)
+	defer zeroBytes(htmp)
+	xorBytes32(l, l, htmp)
+
+	// R = ChaCha20(L ^ k1, iv1, R)
+	xorBytes32(tmp[:lSize], l, k1)
+	if err := s.ReKey(tmp[:lSize], iv1); err != nil {
+		return err
+	}
+	s.XORKeyStream(r, r)
+
+	copy(dst, l)
+	copy(dst[lSize:], r)
+
+	return nil
+}
+
+func xorBytes32Ref(dst, a, b []byte) {
+	// Note: Before you freak the fuck out and try to optimize this,
+	// for more platforms, take note that it's 32 bytes.  Performance
+	// here is only meaningful for extremely small messages.
+
+	if len(dst) != 32 {
+		panic("lioness: xorBytes32Ref() len != 32")
+	}
+	for i, v := range a {
+		dst[i] = v ^ b[i]
+	}
+}
+
+func zeroBytes(a []byte) {
+	for i := range a {
+		a[i] = 0
+	}
+}

+ 119 - 0
lioness_test.go

@@ -0,0 +1,119 @@
+// lioness.go - A LIONESS-BLAKE2b-ChaCha20 implementation.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to lioness, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+package lioness
+
+import (
+	"bytes"
+	"crypto/rand"
+	"testing"
+)
+
+func TestLionessBasic(t *testing.T) {
+	var key [KeySize]byte
+	var iv [IVSize]byte
+	var src, dst, tmp [1024]byte
+
+	if _, err := rand.Read(key[:]); err != nil {
+		t.Error(err)
+		t.Fail()
+	}
+	if _, err := rand.Read(iv[:]); err != nil {
+		t.Error(err)
+		t.Fail()
+	}
+
+	if _, err := rand.Read(src[:]); err != nil {
+		t.Error(err)
+		t.Fail()
+	}
+
+	if err := Encrypt(key, iv, dst[:], src[:]); err != nil {
+		t.Error(err)
+		t.Fail()
+	}
+
+	if bytes.Equal(src[:], dst[:]) {
+		t.Error("src/dest match")
+		t.Fail()
+	}
+
+	if err := Decrypt(key, iv, tmp[:], dst[:]); err != nil {
+		t.Error(err)
+		t.Fail()
+	}
+
+	if !bytes.Equal(src[:], tmp[:]) {
+		t.Error("src/tmp mismatch")
+		t.Fail()
+	}
+
+	// TODO: Test that each subnonce actually changes things.
+
+	// TODO: Test that each subkey actually changes things.
+}
+
+var benchOutput []byte
+
+func doBenchEncrypt(b *testing.B, n int) {
+	var key [KeySize]byte
+	var iv [IVSize]byte
+	if _, err := rand.Read(key[:]); err != nil {
+		b.Error(err)
+		b.Fail()
+	}
+	if _, err := rand.Read(iv[:]); err != nil {
+		b.Error(err)
+		b.Fail()
+	}
+
+	src := make([]byte, n)
+	dst := make([]byte, n)
+
+	b.SetBytes(int64(n))
+	b.ResetTimer()
+	for i := 0; i < b.N; i++ {
+		if err := Encrypt(key, iv, dst, src); err != nil {
+			b.Error(err)
+			b.Fatal()
+		}
+	}
+
+	benchOutput = dst
+}
+
+func BenchmarkLionessEncrypt_33(b *testing.B) {
+	doBenchEncrypt(b, 33)
+}
+
+func BenchmarkLionessEncrypt_512(b *testing.B) {
+	doBenchEncrypt(b, 512)
+}
+
+func BenchmarkLionessEncrypt_1024(b *testing.B) {
+	doBenchEncrypt(b, 1024)
+}
+
+func BenchmarkLionessEncrypt_4096(b *testing.B) {
+	doBenchEncrypt(b, 4096)
+}
+
+func BenchmarkLionessEncrypt_16384(b *testing.B) {
+	doBenchEncrypt(b, 16384)
+}
+
+func BenchmarkLionessEncrypt_32768(b *testing.B) {
+	doBenchEncrypt(b, 32768)
+}
+
+func BenchmarkLionessEncrypt_65536(b *testing.B) {
+	doBenchEncrypt(b, 65536)
+}
+
+func BenchmarkLionessEncrypt_1024768(b *testing.B) {
+	doBenchEncrypt(b, 1024768)
+}

+ 29 - 0
xorbytes_amd64.go

@@ -0,0 +1,29 @@
+// xorbytes_amd64.go - AMD64 optimized xorBytes32.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to lioness, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+// +build amd64,!gccgo,!appengine
+
+package lioness
+
+//go:noescape
+func xorBytes32Amd64SSE2(dest, a, b *byte)
+
+func xorBytes32Amd64(dst, a, b []byte) {
+	// This is basically a pointless microoptimization, and only
+	// helps the 33 byte by 0.8 MB/s on my craptop.  Using AVX2
+	// is essentially totally pointless, so I didn't do it.
+
+	if len(dst) != 32 {
+		panic("lioness: xorBytes32Amd64() len != 32")
+	}
+
+	xorBytes32Amd64SSE2(&dst[0], &a[0], &b[0])
+}
+
+func init() {
+	xorBytes32 = xorBytes32Amd64
+}

+ 20 - 0
xorbytes_amd64.s

@@ -0,0 +1,20 @@
+// xorbytes_amd64.s - AMD64 SSE2 32 byte XOR.
+//
+// To the extent possible under law, Yawning Angel has waived all copyright
+// and related or neighboring rights to lioness, using the Creative
+// Commons "CC0" public domain dedication. See LICENSE or
+// <http://creativecommons.org/publicdomain/zero/1.0/> for full details.
+
+TEXT ·xorBytes32Amd64SSE2(SB),4,$0-24
+	MOVQ dst+0(FP), AX
+	MOVQ a+8(FP), BX
+	MOVQ b+16(FP), CX
+	MOVOU 0(BX), X0
+	MOVOU 0(CX), X1
+	MOVOU 16(BX), X2
+	MOVOU 16(CX), X3
+	PXOR X1, X0
+	PXOR X3, X2
+	MOVOU X0, 0(AX)
+	MOVOU X2, 16(AX)
+	RET