package/agec: new package

Agec is a simple file encryption tool that implements the
age format in C with minimal dependencies. The tool supports
asymmetric encryption based on X25519, and a passphrase
encryption based on scrypt.

https://git.sr.ht/~min/agec
https://age-encryption.org

Encryption is silently broken for files <35 bytes, so add a patch submitted
upstream to fix that.

Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
This commit is contained in:
Peter Korsgaard
2026-06-19 11:20:03 +02:00
parent 4bebe90835
commit 3bd4b9f434
7 changed files with 136 additions and 0 deletions

View File

@@ -2649,6 +2649,7 @@ F: configs/orangepi_pc_defconfig
F: configs/orangepi_r1_defconfig
F: configs/sheevaplug_defconfig
F: configs/visionfive_defconfig
F: package/agec/
F: package/bats-core/
F: package/dfu-programmer/
F: package/docker-compose/
@@ -2683,6 +2684,7 @@ F: package/triggerhappy/
F: package/ugetty/
F: package/wireguard-linux-compat/
F: package/wireguard-tools/
F: support/testing/tests/package/test_agec.py
F: support/testing/tests/package/test_docker_compose.py
F: support/testing/tests/package/test_python_hid.py

View File

@@ -2729,6 +2729,7 @@ comment "Shells"
source "package/nushell/Config.in"
source "package/zsh/Config.in"
comment "Utilities"
source "package/agec/Config.in"
source "package/apg/Config.in"
source "package/at/Config.in"
source "package/bash-completion/Config.in"

View File

@@ -0,0 +1,35 @@
From eb8ccfe5bb32273226d80236caab7a9386d71071 Mon Sep 17 00:00:00 2001
From: Peter Korsgaard <peter@korsgaard.com>
Date: Thu, 18 Jun 2026 15:12:38 +0200
Subject: [PATCH] io.c: isarmor(): do not set eof for <35 byte files
Encryption is silently broken for <35 byte files since commit b374d8de5a
("stop reading after first EOF"), as readall() sets the eof flag when it was
unable to read the entire 35 bytes in isarmor(), causing bread() to return
EOF and ignore the <35 bytes already read.
Fix it by only setting the eof flag in isarmor() if nothing could be read.
Upstream: mailed to amin@firemail.cc
Signed-off-by: Peter Korsgaard <peter@korsgaard.com>
---
io.c | 3 +--
1 file changed, 1 insertion(+), 2 deletions(-)
diff --git a/io.c b/io.c
index 15ed4f7..2773022 100644
--- a/io.c
+++ b/io.c
@@ -128,8 +128,7 @@ isarmor(Ibuf *b)
nr = readall(b->fd, b->buf, sizeof(armorfirst) - 1, &b->eof);
if(nr == -1)
return -1;
- if(nr == 0)
- b->eof = 1;
+ b->eof = (nr == 0);
b->size = nr;
if((usize)nr < sizeof(armorfirst) - 1)
return 0;
--
2.47.3

10
package/agec/Config.in Normal file
View File

@@ -0,0 +1,10 @@
config BR2_PACKAGE_AGEC
bool "agec"
select BR2_PACKAGE_OPENSSL
help
Agec is a simple file encryption tool that implements the
age format in C with minimal dependencies. The tool supports
asymmetric encryption based on X25519, and a passphrase
encryption based on scrypt.
https://git.sr.ht/~min/agec

3
package/agec/agec.hash Normal file
View File

@@ -0,0 +1,3 @@
# Locally calculated
sha256 97958ff82eaa6aa89328f4319d585e362130168c478cf6a85ba3f4d05e453669 0.1.0.tar.gz
sha256 f7f37a8bb7d993825b10f5ce2838c1c452d902eda63cd180fdabc7c3a5dd0341 LICENSE

24
package/agec/agec.mk Normal file
View File

@@ -0,0 +1,24 @@
################################################################################
#
# agec
#
################################################################################
AGEC_VERSION = 0.1.0
AGEC_SOURCE = $(AGEC_VERSION).tar.gz
AGEC_SITE = https://git.sr.ht/~min/agec/archive
AGEC_LICENSE = BSD-0-Clause
AGEC_LICENSE_FILES = LICENSE
AGEC_DEPENDENCIES = host-pkgconf openssl
define AGEC_BUILD_CMDS
$(MAKE) -C $(@D) $(TARGET_CONFIGURE_OPTS) \
LIBS="`$(PKG_CONFIG_HOST_BINARY) --libs openssl`"
endef
define AGEC_INSTALL_TARGET_CMDS
$(MAKE) -C $(@D) $(TARGET_CONFIGURE_OPTS) \
PREFIX=$(TARGET_DIR)/usr install
endef
$(eval $(generic-package))

View File

@@ -0,0 +1,61 @@
import os
import infra.basetest
class TestAgec(infra.basetest.BRTest):
config = infra.basetest.BASIC_TOOLCHAIN_CONFIG + """
BR2_PACKAGE_AGEC=y
BR2_TARGET_ROOTFS_CPIO=y
"""
# generate keypair in file and return pubkey
def generate_keypair(self, filename):
self.assertRunOk(f"agec-keygen > {filename}")
output, exit_code = self.emulator.run(f"agec-keygen -y < {filename}")
self.assertEqual(exit_code, 0)
pubkey = output[0].strip()
self.assertNotEqual(pubkey, "")
return pubkey
def test_run(self):
cpio_file = os.path.join(self.builddir, "images", "rootfs.cpio")
self.emulator.boot(arch="armv5",
kernel="builtin",
options=["-initrd", cpio_file])
self.emulator.login()
# We define two keypairs
key1 = "/tmp/key1.txt"
key2 = "/tmp/key2.txt"
# And files to work on
orig_file = "/bin/busybox"
decrypted_file = "/tmp/busybox"
encrypted_file = decrypted_file + ".age"
# should output a valid looking keypair to stdout
output, exit_code = self.emulator.run("agec-keygen")
self.assertEqual(exit_code, 0)
self.assertIn("public key:", output[0])
self.assertIn("AGE-SECRET-KEY-", output[1])
# generate keypairs and extract pubkeys
pubkey1 = self.generate_keypair(key1)
_ = self.generate_keypair(key2)
# encrypt file
self.assertRunOk(f"agec -r {pubkey1} {orig_file} > {encrypted_file}")
# should be encrypted
self.assertRunNotOk(f"cmp {orig_file} {encrypted_file}")
# should be decryptable with key1
self.assertRunOk(f"agec -d -i {key1} {encrypted_file} > {decrypted_file}")
# and equal to original
self.assertRunOk(f"cmp {orig_file} {decrypted_file} ")
# should NOT be decryptable with key2
self.assertRunNotOk(f"agec -d -i {key2} {encrypted_file} > {decrypted_file}")