Skip to content

Commit

Permalink
Ascon80pq to cipher\ascon
Browse files Browse the repository at this point in the history
  • Loading branch information
dhcgn authored and armfazh committed Feb 20, 2023
1 parent a2a80ed commit 801c327
Show file tree
Hide file tree
Showing 3 changed files with 8,823 additions and 32 deletions.
106 changes: 80 additions & 26 deletions cipher/ascon/ascon.go
Expand Up @@ -13,9 +13,10 @@ import (
)

const (
KeySize = 16
NonceSize = 16
TagSize = KeySize
KeySize = 16
KeySize80pq = 20
NonceSize = 16
TagSize = KeySize
)

type Mode int
Expand All @@ -26,6 +27,8 @@ func (m Mode) String() string {
return "Ascon128"
case Ascon128a:
return "Ascon128a"
case Ascon80pq:
return "Ascon80pq"
default:
panic(ErrMode)
}
Expand All @@ -34,33 +37,48 @@ func (m Mode) String() string {
const (
Ascon128 Mode = iota + 1
Ascon128a
// Ascon-80pq has an increased key-size to provide more resistance against a quantum
// adversary using Grover’s algorithm for key search. Since Ascon-128 and Ascon-
// 80pq share the same building blocks and same parameters except the size of the key,
// we claim the same security for Ascon-80pq against classical attacks as for Ascon-128.
Ascon80pq
)

const (
permA = 12
permB = 6 // 6 for Ascon128, or 8 for Ascon128a
blockSize = 8 // 8 for Ascon128, or 16 for Ascon128a
permB = 6 // 6 for Ascon128 and Ascon80pq, or 8 for Ascon128a
blockSize = 8 // 8 for Ascon128 and Ascon80pq, or 16 for Ascon128a
ivSize = 8
stateSize = ivSize + KeySize + NonceSize
)

type Cipher struct {
s [5]uint64
key [2]uint64
key [3]uint64
mode Mode
}

// New returns a Cipher struct implementing the cipher.AEAD interface. Mode is
// one of Ascon128 or Ascon128a.
// one of Ascon128, Ascon128a or Ascon80pq.
func New(key []byte, m Mode) (*Cipher, error) {
if len(key) != KeySize {
if (m == Ascon128 || m == Ascon128a) && len(key) != KeySize {
return nil, ErrKeySize
}
if !(m == Ascon128 || m == Ascon128a) {
if m == Ascon80pq && len(key) != KeySize80pq {
return nil, ErrKeySize
}
if !(m == Ascon128 || m == Ascon128a || m == Ascon80pq) {
return nil, ErrMode
}
c := new(Cipher)
c.mode = m
if m == Ascon80pq {
c.key[0] = (uint64)(binary.BigEndian.Uint32(key[0:4]))
c.key[1] = binary.BigEndian.Uint64(key[4:12])
c.key[2] = binary.BigEndian.Uint64(key[12:20])
return c, nil
}

c.key[0] = binary.BigEndian.Uint64(key[0:8])
c.key[1] = binary.BigEndian.Uint64(key[8:16])

Expand Down Expand Up @@ -136,21 +154,42 @@ func (a *Cipher) Open(dst, nonce, ciphertext, additionalData []byte) ([]byte, er
}

func (a *Cipher) initialize(nonce []byte) {
bcs := blockSize * uint64(a.mode)
pB := permB + 2*(uint64(a.mode)-1)
a.s[0] = ((KeySize * 8) << 56) | ((bcs * 8) << 48) | (permA << 40) | (pB << 32)
a.s[1] = a.key[0]
a.s[2] = a.key[1]
if a.mode == Ascon80pq {
bcs := blockSize * uint64(Ascon128)
pB := permB + 2*(uint64(Ascon128)-1)
a.s[0] = ((KeySize80pq * 8) << 56) | ((bcs * 8) << 48) | (permA << 40) | (pB << 32) | a.key[0]
a.s[1] = a.key[1]
a.s[2] = a.key[2]
} else {
bcs := blockSize * uint64(a.mode)
pB := permB + 2*(uint64(a.mode)-1)
a.s[0] = ((KeySize * 8) << 56) | ((bcs * 8) << 48) | (permA << 40) | (pB << 32)
a.s[1] = a.key[0]
a.s[2] = a.key[1]
}

a.s[3] = binary.BigEndian.Uint64(nonce[0:8])
a.s[4] = binary.BigEndian.Uint64(nonce[8:16])
a.perm(permA)
a.s[3] ^= a.key[0]
a.s[4] ^= a.key[1]

if a.mode == Ascon80pq {
a.s[2] ^= a.key[0]
a.s[3] ^= a.key[1]
a.s[4] ^= a.key[2]
} else {
a.s[3] ^= a.key[0]
a.s[4] ^= a.key[1]
}
}

func (a *Cipher) assocData(add []byte) {
bcs := blockSize * int(a.mode)
pB := permB + 2*(int(a.mode)-1)
m := a.mode
if m == Ascon80pq {
m = Ascon128
}

bcs := blockSize * int(m)
pB := permB + 2*(int(m)-1)
if len(add) > 0 {
for ; len(add) >= bcs; add = add[bcs:] {
for i := 0; i < bcs; i += 8 {
Expand All @@ -168,8 +207,13 @@ func (a *Cipher) assocData(add []byte) {
}

func (a *Cipher) procText(in, out []byte, enc bool) {
bcs := blockSize * int(a.mode)
pB := permB + 2*(int(a.mode)-1)
m := a.mode
if m == Ascon80pq {
m = Ascon128
}

bcs := blockSize * int(m)
pB := permB + 2*(int(m)-1)
mask := uint64(0)
if enc {
mask -= 1
Expand Down Expand Up @@ -198,12 +242,22 @@ func (a *Cipher) procText(in, out []byte, enc bool) {
}

func (a *Cipher) finalize(tag []byte) {
bcs := blockSize * int(a.mode)
a.s[bcs/8+0] ^= a.key[0]
a.s[bcs/8+1] ^= a.key[1]
a.perm(permA)
binary.BigEndian.PutUint64(tag[0:8], a.s[3]^a.key[0])
binary.BigEndian.PutUint64(tag[8:16], a.s[4]^a.key[1])
if a.mode == Ascon80pq {
bcs := blockSize * int(Ascon128)
a.s[bcs/8+0] ^= a.key[0]<<32 | a.key[1]>>32
a.s[bcs/8+1] ^= a.key[1]<<32 | a.key[2]>>32
a.s[bcs/8+2] ^= a.key[2] << 32
a.perm(permA)
binary.BigEndian.PutUint64(tag[0:8], a.s[3]^a.key[1])
binary.BigEndian.PutUint64(tag[8:16], a.s[4]^a.key[2])
} else {
bcs := blockSize * int(a.mode)
a.s[bcs/8+0] ^= a.key[0]
a.s[bcs/8+1] ^= a.key[1]
a.perm(permA)
binary.BigEndian.PutUint64(tag[0:8], a.s[3]^a.key[0])
binary.BigEndian.PutUint64(tag[8:16], a.s[4]^a.key[1])
}
}

var roundConst = [12]uint64{0xf0, 0xe1, 0xd2, 0xc3, 0xb4, 0xa5, 0x96, 0x87, 0x78, 0x69, 0x5a, 0x4b}
Expand Down
35 changes: 29 additions & 6 deletions cipher/ascon/ascon_test.go
Expand Up @@ -59,7 +59,7 @@ func readFile(t *testing.T, fileName string) []vector {
func TestAscon(t *testing.T) {
// Test vectors generated with pyascon
// https://github.com/meichlseder/pyascon/
for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a} {
for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a, ascon.Ascon80pq} {
name := mode.String()
t.Run(name, func(t *testing.T) {
vectors := readFile(t, "testdata/"+name+".json")
Expand Down Expand Up @@ -105,6 +105,9 @@ func TestBadInputs(t *testing.T) {
_, err = ascon.New(key[:4], ascon.Ascon128)
test.CheckIsErr(t, err, "should fail due to short key")

_, err = ascon.New(key[:], ascon.Ascon80pq)
test.CheckIsErr(t, err, "should fail due to short key")

a, _ := ascon.New(key[:], ascon.Ascon128)
err = test.CheckPanic(func() { _ = a.Seal(nil, nil, nil, nil) })
test.CheckNoErr(t, err, "should panic due to bad nonce")
Expand All @@ -125,7 +128,7 @@ func TestBadInputs(t *testing.T) {
}

func BenchmarkAscon(b *testing.B) {
for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a} {
for _, mode := range []ascon.Mode{ascon.Ascon128, ascon.Ascon128a, ascon.Ascon80pq} {
for _, length := range []int{64, 1350, 8 * 1024} {
b.Run(mode.String()+"/Open-"+strconv.Itoa(length), func(b *testing.B) { benchmarkOpen(b, make([]byte, length), mode) })
b.Run(mode.String()+"/Seal-"+strconv.Itoa(length), func(b *testing.B) { benchmarkSeal(b, make([]byte, length), mode) })
Expand All @@ -137,10 +140,20 @@ func benchmarkSeal(b *testing.B, buf []byte, mode ascon.Mode) {
b.ReportAllocs()
b.SetBytes(int64(len(buf)))

var key [ascon.KeySize]byte
var key []byte
switch mode {
case ascon.Ascon128, ascon.Ascon128a:
key = make([]byte, ascon.KeySize)
case ascon.Ascon80pq:
key = make([]byte, ascon.KeySize80pq)
}

var nonce [ascon.NonceSize]byte
var ad [13]byte
a, _ := ascon.New(key[:], mode)
a, err := ascon.New(key[:], mode)
if err != nil {
b.Fatal(err)
}
var out []byte

b.ResetTimer()
Expand All @@ -153,10 +166,20 @@ func benchmarkOpen(b *testing.B, buf []byte, mode ascon.Mode) {
b.ReportAllocs()
b.SetBytes(int64(len(buf)))

var key [ascon.KeySize]byte
var key []byte
switch mode {
case ascon.Ascon128, ascon.Ascon128a:
key = make([]byte, ascon.KeySize)
case ascon.Ascon80pq:
key = make([]byte, ascon.KeySize80pq)
}

var nonce [ascon.NonceSize]byte
var ad [13]byte
a, _ := ascon.New(key[:], mode)
a, err := ascon.New(key[:], mode)
if err != nil {
b.Fatal(err)
}
var out []byte

ct := a.Seal(nil, nonce[:], buf, ad[:])
Expand Down

0 comments on commit 801c327

Please sign in to comment.