summaryrefslogtreecommitdiffstats
path: root/internal/cryptokit/cryptokit_test.go
diff options
context:
space:
mode:
authorGab Virebent <gabriel1@virebent.art>2026-08-22 20:36:56 +0200
committerGab Virebent <gabriel1@virebent.art>2026-08-22 20:36:56 +0200
commitc1decadb590c4d79d92bcc4df7772119a5c91244 (patch)
tree468f9fa37535dbb90cc5d4061eb20de83912131e /internal/cryptokit/cryptokit_test.go
downloadaegis-c1decadb590c4d79d92bcc4df7772119a5c91244.tar.gz
aegis-c1decadb590c4d79d92bcc4df7772119a5c91244.tar.xz
aegis-c1decadb590c4d79d92bcc4df7772119a5c91244.zip
Initial Aegis Usenet client release
Diffstat (limited to 'internal/cryptokit/cryptokit_test.go')
-rw-r--r--internal/cryptokit/cryptokit_test.go156
1 files changed, 156 insertions, 0 deletions
diff --git a/internal/cryptokit/cryptokit_test.go b/internal/cryptokit/cryptokit_test.go
new file mode 100644
index 0000000..928120e
--- /dev/null
+++ b/internal/cryptokit/cryptokit_test.go
@@ -0,0 +1,156 @@
+package cryptokit
+
+import (
+ "bytes"
+ "crypto/ed25519"
+ "crypto/rand"
+ "crypto/rsa"
+ "encoding/base64"
+ "encoding/pem"
+ "os"
+ "os/exec"
+ "testing"
+
+ "filippo.io/age"
+ "golang.org/x/crypto/ssh"
+)
+
+func TestEd25519SignVerifyAcceptsSeedEncoding(t *testing.T) {
+ public, private, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ message := []byte("Aegis cryptographic test")
+ signature, err := SignEd25519(message, base64.StdEncoding.EncodeToString(private.Seed()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := VerifyEd25519(message, signature, base64.StdEncoding.EncodeToString(public)); err != nil {
+ t.Fatal(err)
+ }
+ if err := VerifyEd25519([]byte("tampered"), signature, base64.StdEncoding.EncodeToString(public)); err == nil {
+ t.Fatal("tampered message verified")
+ }
+}
+
+func TestEd25519PublicKeyAndFingerprint(t *testing.T) {
+ public, private, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ derived, err := Ed25519PublicKey(base64.StdEncoding.EncodeToString(private.Seed()))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if derived != base64.StdEncoding.EncodeToString(public) {
+ t.Fatalf("derived public key = %q, want %q", derived, base64.StdEncoding.EncodeToString(public))
+ }
+ fingerprint, err := Ed25519PublicKeyFingerprint(derived)
+ if err != nil {
+ t.Fatal(err)
+ }
+ if len(fingerprint) != len("sha256:")+64 || fingerprint[:len("sha256:")] != "sha256:" {
+ t.Fatalf("unexpected fingerprint %q", fingerprint)
+ }
+}
+
+func TestAgeRoundTrip(t *testing.T) {
+ identity, err := age.GenerateX25519Identity()
+ if err != nil {
+ t.Fatal(err)
+ }
+ ciphertext, err := EncryptAge([]byte("age test"), identity.Recipient().String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ plaintext, err := DecryptAge(ciphertext, identity.String())
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(plaintext) != "age test" {
+ t.Fatalf("unexpected plaintext %q", plaintext)
+ }
+}
+
+func TestAgeSSHCompatibility(t *testing.T) {
+ testKey := func(private any) {
+ signer, err := ssh.NewSignerFromKey(private)
+ if err != nil {
+ t.Fatal(err)
+ }
+ privatePEM, err := ssh.MarshalPrivateKey(private, "")
+ if err != nil {
+ t.Fatal(err)
+ }
+ ciphertext, err := EncryptAge([]byte("age SSH test"), string(ssh.MarshalAuthorizedKey(signer.PublicKey())))
+ if err != nil {
+ t.Fatal(err)
+ }
+ plaintext, err := DecryptAge(ciphertext, string(pem.EncodeToMemory(privatePEM)))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(plaintext) != "age SSH test" {
+ t.Fatalf("unexpected plaintext %q", plaintext)
+ }
+ }
+ _, edPrivate, err := ed25519.GenerateKey(rand.Reader)
+ if err != nil {
+ t.Fatal(err)
+ }
+ testKey(edPrivate)
+ rsaPrivate, err := rsa.GenerateKey(rand.Reader, 2048)
+ if err != nil {
+ t.Fatal(err)
+ }
+ testKey(rsaPrivate)
+}
+
+func TestOpenPGPRoundTripWithUserKeyMaterial(t *testing.T) {
+ gpg, err := exec.LookPath("gpg")
+ if err != nil {
+ t.Skip("gpg is not installed")
+ }
+ home := t.TempDir()
+ if err := os.Chmod(home, 0o700); err != nil {
+ t.Fatal(err)
+ }
+ run := func(args []string, input []byte) ([]byte, error) {
+ cmd := exec.Command(gpg, append([]string{"--batch", "--no-tty", "--no-options", "--homedir", home}, args...)...)
+ cmd.Stdin = bytes.NewReader(input)
+ return cmd.Output()
+ }
+ if _, err := run([]string{"--pinentry-mode", "loopback", "--passphrase", "", "--quick-generate-key", "Aegis Test <aegis@example.invalid>", "rsa2048", "sign", "1d"}, nil); err != nil {
+ t.Skipf("gpg cannot create an ephemeral test key: %v", err)
+ }
+ if _, err := run([]string{"--pinentry-mode", "loopback", "--passphrase", "", "--quick-add-key", "aegis@example.invalid", "rsa2048", "encrypt", "1d"}, nil); err != nil {
+ t.Skipf("gpg cannot create an ephemeral encryption subkey: %v", err)
+ }
+ publicKey, err := run([]string{"--armor", "--export", "aegis@example.invalid"}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ privateKey, err := run([]string{"--armor", "--export-secret-keys", "aegis@example.invalid"}, nil)
+ if err != nil {
+ t.Fatal(err)
+ }
+ message := []byte("OpenPGP compatibility test")
+ signature, err := SignOpenPGPDetached(message, string(privateKey))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if err := VerifyOpenPGPDetached(message, signature, string(publicKey)); err != nil {
+ t.Fatal(err)
+ }
+ ciphertext, err := EncryptOpenPGP(message, string(publicKey), string(privateKey))
+ if err != nil {
+ t.Fatal(err)
+ }
+ plaintext, err := DecryptOpenPGP([]byte(ciphertext), string(privateKey))
+ if err != nil {
+ t.Fatal(err)
+ }
+ if string(plaintext) != string(message) {
+ t.Fatalf("unexpected plaintext %q", plaintext)
+ }
+}