mirror of
https://github.com/FiloSottile/age.git
synced 2026-01-08 04:55:12 +00:00
tests: finish parsing and X25519 tests, distinguish HMAC errors
If the implementation re-encodes the header before checking the HMAC, that would mask malleability issues: the HMAC check would fail because the tests HMAC'd the original header, but an attacker could also produce the right HMAC. Instead of duplicating every parsing tests (with the original and re-encoded HMAC), we make the test framework distinguish HMAC errors, which ensures bad encodings are recognized as such and not bypassable HMAC errors.
This commit is contained in:
@@ -16,6 +16,6 @@ func main() {
|
||||
f.HMAC()
|
||||
f.FileKey(testkit.TestFileKey)
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.ExpectHMACFailure()
|
||||
f.Generate()
|
||||
}
|
||||
|
||||
21
tests/stanza_bad_start.go
Normal file
21
tests/stanza_bad_start.go
Normal 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.TestX25519Recipient)
|
||||
f.TextLine("-- stanza")
|
||||
f.Body([]byte(""))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
26
tests/stanza_base64_padding.go
Normal file
26
tests/stanza_base64_padding.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza")
|
||||
f.Body(bytes.Repeat([]byte("A"), 50))
|
||||
f.TextLine(f.UnreadLine() + "=")
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
21
tests/stanza_empty_argument.go
Normal file
21
tests/stanza_empty_argument.go
Normal 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza", "", "argument")
|
||||
f.Body([]byte(""))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
@@ -17,7 +17,7 @@ func main() {
|
||||
f.VersionLine("v1")
|
||||
f.X25519(testkit.TestX25519Recipient)
|
||||
f.ArgsLine("stanza")
|
||||
f.Body(bytes.Repeat([]byte("A"), 48))
|
||||
f.Body(bytes.Repeat([]byte("A"), 48*2))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.Generate()
|
||||
|
||||
21
tests/stanza_invalid_character.go
Normal file
21
tests/stanza_invalid_character.go
Normal 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza", "è")
|
||||
f.Body([]byte(""))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
27
tests/stanza_long_line.go
Normal file
27
tests/stanza_long_line.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza")
|
||||
f.TextLine(strings.Repeat("A", 68))
|
||||
f.TextLine("")
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Comment("a body line is longer than 64 columns")
|
||||
f.Generate()
|
||||
}
|
||||
21
tests/stanza_no_arguments.go
Normal file
21
tests/stanza_no_arguments.go
Normal 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.TestX25519Recipient)
|
||||
f.ArgsLine()
|
||||
f.Body([]byte(""))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
26
tests/stanza_not_canonical.go
Normal file
26
tests/stanza_not_canonical.go
Normal file
@@ -0,0 +1,26 @@
|
||||
// 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza")
|
||||
f.Body(bytes.Repeat([]byte("A"), 50))
|
||||
f.TextLine(testkit.NotCanonicalBase64(f.UnreadLine()))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
25
tests/stanza_spurious_cr.go
Normal file
25
tests/stanza_spurious_cr.go
Normal file
@@ -0,0 +1,25 @@
|
||||
// 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.TestX25519Recipient)
|
||||
f.ArgsLine("stanza")
|
||||
f.TextLine(strings.Repeat("A", 32) + "\r" + strings.Repeat("A", 31))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
19
tests/version_unsupported.go
Normal file
19
tests/version_unsupported.go
Normal 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("v1234")
|
||||
f.X25519(testkit.TestX25519Recipient)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Generate()
|
||||
}
|
||||
27
tests/x25519_bad_tag.go
Normal file
27
tests/x25519_bad_tag.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
|
||||
"filippo.io/age/internal/testkit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f := testkit.NewTestFile()
|
||||
f.VersionLine("v1")
|
||||
f.X25519(testkit.TestX25519Recipient)
|
||||
body, _ := base64.RawStdEncoding.DecodeString(f.UnreadLine())
|
||||
body[len(body)-1] ^= 0xff
|
||||
f.TextLine(base64.RawStdEncoding.EncodeToString(body))
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Comment("the ChaCha20Poly1305 authentication tag on the body of the X25519 stanza is wrong")
|
||||
f.Generate()
|
||||
}
|
||||
22
tests/x25519_grease.go
Normal file
22
tests/x25519_grease.go
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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.ArgsLine("grease")
|
||||
f.Body(nil)
|
||||
f.X25519(testkit.TestX25519Recipient)
|
||||
f.ArgsLine("grease")
|
||||
f.Body(nil)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.Generate()
|
||||
}
|
||||
@@ -6,26 +6,14 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
|
||||
"filippo.io/age/internal/testkit"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
import "filippo.io/age/internal/testkit"
|
||||
|
||||
func main() {
|
||||
f := testkit.NewTestFile()
|
||||
f.VersionLine("v1")
|
||||
f.X25519RecordIdentity(testkit.TestX25519Identity)
|
||||
share := make([]byte, curve25519.PointSize)
|
||||
f.ArgsLine("X25519", base64.RawStdEncoding.EncodeToString(share))
|
||||
secret := make([]byte, curve25519.PointSize)
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, secret, append(share, testkit.TestX25519Recipient...),
|
||||
[]byte("age-encryption.org/v1/X25519")).Read(key)
|
||||
f.AEADBody(key, testkit.TestFileKey)
|
||||
share := make([]byte, 32)
|
||||
f.X25519Stanza(share, testkit.TestX25519Identity)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
|
||||
30
tests/x25519_long_share.go
Normal file
30
tests/x25519_long_share.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
|
||||
"filippo.io/age/internal/testkit"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f := testkit.NewTestFile()
|
||||
f.VersionLine("v1")
|
||||
share, _ := curve25519.X25519(f.Rand(32), curve25519.Basepoint)
|
||||
f.X25519RecordIdentity(testkit.TestX25519Identity)
|
||||
f.X25519Stanza(share, testkit.TestX25519Identity)
|
||||
body, _ := f.UnreadLine(), f.UnreadLine()
|
||||
f.TextLine("-> X25519 " + base64.RawStdEncoding.EncodeToString(append(share, 0x00)))
|
||||
f.TextLine(body)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Comment("a trailing zero is missing from the X25519 share")
|
||||
f.Generate()
|
||||
}
|
||||
@@ -6,14 +6,7 @@
|
||||
|
||||
package main
|
||||
|
||||
import (
|
||||
"crypto/sha256"
|
||||
"encoding/base64"
|
||||
|
||||
"filippo.io/age/internal/testkit"
|
||||
"golang.org/x/crypto/curve25519"
|
||||
"golang.org/x/crypto/hkdf"
|
||||
)
|
||||
import "filippo.io/age/internal/testkit"
|
||||
|
||||
func main() {
|
||||
f := testkit.NewTestFile()
|
||||
@@ -24,12 +17,7 @@ func main() {
|
||||
share := []byte{0x5f, 0x9c, 0x95, 0xbc, 0xa3, 0x50, 0x8c, 0x24, 0xb1, 0xd0,
|
||||
0xb1, 0x55, 0x9c, 0x83, 0xef, 0x5b, 0x04, 0x44, 0x5c, 0xc4, 0x58, 0x1c,
|
||||
0x8e, 0x86, 0xd8, 0x22, 0x4e, 0xdd, 0xd0, 0x9f, 0x11, 0xd7}
|
||||
f.ArgsLine("X25519", base64.RawStdEncoding.EncodeToString(share))
|
||||
secret := make([]byte, curve25519.PointSize)
|
||||
key := make([]byte, 32)
|
||||
hkdf.New(sha256.New, secret, append(share, testkit.TestX25519Recipient...),
|
||||
[]byte("age-encryption.org/v1/X25519")).Read(key)
|
||||
f.AEADBody(key, testkit.TestFileKey)
|
||||
f.X25519Stanza(share, testkit.TestX25519Identity)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
|
||||
27
tests/x25519_lowercase.go
Normal file
27
tests/x25519_lowercase.go
Normal file
@@ -0,0 +1,27 @@
|
||||
// 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.TestX25519Recipient)
|
||||
body, args := f.UnreadLine(), f.UnreadLine()
|
||||
f.TextLine(strings.Replace(args, "X25519", "x25519", -1))
|
||||
f.TextLine(body)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Comment("the first argument in the X25519 stanza is lowercase")
|
||||
f.Generate()
|
||||
}
|
||||
30
tests/x25519_short_share.go
Normal file
30
tests/x25519_short_share.go
Normal file
@@ -0,0 +1,30 @@
|
||||
// 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 (
|
||||
"encoding/base64"
|
||||
"encoding/hex"
|
||||
|
||||
"filippo.io/age/internal/testkit"
|
||||
)
|
||||
|
||||
func main() {
|
||||
f := testkit.NewTestFile()
|
||||
f.VersionLine("v1")
|
||||
share, _ := hex.DecodeString("97ba38a135fd5f9137fca3836bfec24340ab03d7ca316b26f482636334a52600")
|
||||
f.X25519RecordIdentity(testkit.TestX25519Identity)
|
||||
f.X25519Stanza(share, testkit.TestX25519Identity)
|
||||
body, _ := f.UnreadLine(), f.UnreadLine()
|
||||
f.TextLine("-> X25519 " + base64.RawStdEncoding.EncodeToString(share[:31]))
|
||||
f.TextLine(body)
|
||||
f.HMAC()
|
||||
f.Payload("age")
|
||||
f.ExpectHeaderFailure()
|
||||
f.Comment("a trailing zero is missing from the X25519 share")
|
||||
f.Generate()
|
||||
}
|
||||
Reference in New Issue
Block a user