tests: add more tests for hmac and X25519 encodings

This commit is contained in:
Filippo Valsorda
2022-06-16 15:55:29 +02:00
parent bb4493a7cd
commit f8a121dd87
27 changed files with 374 additions and 5 deletions

View File

@@ -30,6 +30,16 @@ var _, TestX25519Identity, _ = bech32.Decode(
var TestX25519Recipient, _ = curve25519.X25519(TestX25519Identity, curve25519.Basepoint)
func NotCanonicalBase64(s string) string {
// Assuming there are spare zero bits at the end of the encoded bitstring,
// the character immediately after in the alphabet compared to the last one
// in the encoding will only flip the last bit to one, making the string a
// non-canonical encoding of the same value.
alphabet := "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
idx := strings.IndexByte(alphabet, s[len(s)-1])
return s[:len(s)-1] + string(alphabet[idx+1])
}
type TestFile struct {
Buf bytes.Buffer
Rand func(n int) []byte
@@ -64,6 +74,14 @@ func (f *TestFile) TextLine(s string) {
f.Buf.WriteString("\n")
}
func (f *TestFile) UnreadLine() string {
buf := bytes.TrimSuffix(f.Buf.Bytes(), []byte("\n"))
idx := bytes.LastIndex(buf[:len(buf)-1], []byte("\n")) + 1
f.Buf.Reset()
f.Buf.Write(buf[:idx])
return string(buf[idx:])
}
func (f *TestFile) VersionLine(v string) {
f.TextLine("age-encryption.org/" + v)
}

10
testdata/testkit/crlf vendored Normal file
View File

@@ -0,0 +1,10 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
comment: lines in the header end with CRLF instead of LF
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- 2KIGb7ye32MWtUuEVWkO3MP6qCDLzOvT9wF06lelBSI
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

View File

@@ -5,5 +5,5 @@ identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- UGnodA32FkH3AOl9BHP6biSZTkNvu8B99I++7JpqZGk
[æè. ½Ó#ÈwÏ…=a×Yök×z©66Ú¦<01>âRùÛL
--- 8McE3ix9R34E/vLrQv3yepsHjo/LXhfs22Ab3UyInmg
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_extra_space vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_garbage vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNgAAA
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_missing vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
---
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_no_space vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
---WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

10
testdata/testkit/hmac_not_canonical vendored Normal file
View File

@@ -0,0 +1,10 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
comment: the base64 encoding of the HMAC is not canonical
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNh
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_trailing_space vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- WyJp9F/9FOZh7gJdheq2WIJcwHgYc8NIVh3ddwhrcNg
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

9
testdata/testkit/hmac_truncated vendored Normal file
View File

@@ -0,0 +1,9 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
hjabGXwSLQ9c3S6Lw2i+S2Tu2fiwQHHslbBN6B41FLE
--- WyJp
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

10
testdata/testkit/x25519_extra_argument vendored Normal file
View File

@@ -0,0 +1,10 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: the base64 encoding of the share is not canonical
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc 1234
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
--- hQQySEUXL8pOuIOuw0qXzi66RphDJP9IKMNEChNJIPk
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

View File

@@ -1,7 +1,7 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1EGTZVFFV20835NWYV6270LXYVK2VKNX2MMDKWYKLMGR48UAWX40Q2P2LM0
comment: the X25519 share is a low-order point, so the shared secret is the disallowed all-zero value
comment: the X25519 share is a low-order point, so the shared secretis the disallowed all-zero value
age-encryption.org/v1
-> X25519 X5yVvKNQjCSx0LFVnIPvWwREXMRYHI6G2CJO3dCfEdc

View File

@@ -0,0 +1,10 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: the base64 encoding of the share is not canonical
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCc
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7V
--- eSjjCjQyp30yHDPwCztKS+1txs+aoCa5ERz8jeEp+9A
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

View File

@@ -0,0 +1,10 @@
expect: header failure
file key: 59454c4c4f57205355424d4152494e45
identity: AGE-SECRET-KEY-1XMWWC06LY3EE5RYTXM9MFLAZ2U56JJJ36S0MYPDRWSVLUL66MV4QX3S7F6
comment: the base64 encoding of the share is not canonical
age-encryption.org/v1
-> X25519 TEiF0ypqr+bpvcqXNyCVJpL7OuwPdVwPL7KQEbFDOCd
EmECAEcKN+n/Vs9SbWiV+Hu0r+E8R77DdWYyd83nw7U
--- AO6haEGU6BGJ8Tzeqnr2fSLEo31JrWodGtZuCZmijI8
îÏbÇδ3'NhÔòùL·L[þ÷¾ªRÈð¼™,ƒ1ûf

29
tests/crlf.go Normal file
View File

@@ -0,0 +1,29 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import (
"bytes"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
hdr := f.Buf.Bytes()
f.Buf.Reset()
f.Buf.Write(bytes.Replace(hdr, []byte("\n"), []byte("\r\n"), -1))
f.HMAC()
f.Buf.WriteString(f.UnreadLine())
f.Buf.WriteString("\r\n")
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("lines in the header end with CRLF instead of LF")
f.Generate()
}

View File

@@ -12,7 +12,7 @@ func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.FileKey(f.Rand(16))
f.FileKey(make([]byte, 16))
f.HMAC()
f.FileKey(testkit.TestFileKey)
f.Payload("age")

24
tests/hmac_extra_space.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import (
"strings"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(strings.Replace(f.UnreadLine(), "--- ", "--- ", -1))
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

20
tests/hmac_garbage.go Normal file
View File

@@ -0,0 +1,20 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(f.UnreadLine() + "AAA")
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

19
tests/hmac_missing.go Normal file
View File

@@ -0,0 +1,19 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMACLine(nil)
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

24
tests/hmac_no_space.go Normal file
View File

@@ -0,0 +1,24 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import (
"strings"
"filippo.io/age/internal/testkit"
)
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(strings.Replace(f.UnreadLine(), "--- ", "---", -1))
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

View File

@@ -0,0 +1,21 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(testkit.NotCanonicalBase64(f.UnreadLine()))
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the base64 encoding of the HMAC is not canonical")
f.Generate()
}

View File

@@ -0,0 +1,20 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(f.UnreadLine() + " ")
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

20
tests/hmac_truncated.go Normal file
View File

@@ -0,0 +1,20 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Identity)
f.HMAC()
f.TextLine(f.UnreadLine()[:len("--- 1234")])
f.Payload("age")
f.ExpectHeaderFailure()
f.Generate()
}

View File

@@ -0,0 +1,23 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
body, args := f.UnreadLine(), f.UnreadLine()
f.TextLine(args + " 1234")
f.TextLine(body)
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the base64 encoding of the share is not canonical")
f.Generate()
}

View File

@@ -33,6 +33,7 @@ func main() {
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the X25519 share is a low-order point, so the shared secret is the disallowed all-zero value")
f.Comment("the X25519 share is a low-order point, so the shared secret" +
"is the disallowed all-zero value")
f.Generate()
}

View File

@@ -0,0 +1,23 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
body, args := f.UnreadLine(), f.UnreadLine()
f.TextLine(args)
f.TextLine(testkit.NotCanonicalBase64(body))
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the base64 encoding of the share is not canonical")
f.Generate()
}

View File

@@ -0,0 +1,23 @@
// Copyright 2022 The age Authors. All rights reserved.
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file.
//go:build ignore
package main
import "filippo.io/age/internal/testkit"
func main() {
f := testkit.NewTestFile()
f.VersionLine("v1")
f.X25519(testkit.TestX25519Recipient)
body, args := f.UnreadLine(), f.UnreadLine()
f.TextLine(testkit.NotCanonicalBase64(args))
f.TextLine(body)
f.HMAC()
f.Payload("age")
f.ExpectHeaderFailure()
f.Comment("the base64 encoding of the share is not canonical")
f.Generate()
}