An Argument for Signing Support #230
Replies: 11 comments 5 replies
-
Arguments against signature support:
Ultimately, the issue I see with signatures is that it's not clear what the use case is, and one of the best things about |
Beta Was this translation helpful? Give feedback.
-
I wholeheartedly agree with all of @adamcaudill's reasoning, except the proposed implementation.
The use case is, that secure messaging - transporting files/streams through space and/or time usually requires both encryption and authentication of the data (making sure it's the correct data). |
Beta Was this translation helpful? Give feedback.
-
I prefer to have static binaries compile/run on multiple distributions/OSs easily. That is why I like golang and am trying to void C/C++ libs. The In the end, I am going to use # Encrypt the md5
md5sum file | age -r ... > file.sig
# Decrypt the md5 to compare against "md5sum file"
age -d -i ... file.sig |
Beta Was this translation helpful? Give feedback.
-
I'll add my two grains of salt :P First, what is the threat model here? If I'm encrypting a file to myself and keeping it somewhere where it can't be tampered with then I see no problem. In other use-cases, it sounds like there is no way to verify the origin of the file, or that the file was intended to me. Which is problematic even when I encrypt to myself. I am not too familiar with minisign, but the problem of composing here is that it most likely doesn't work in some of the important use-cases:
|
Beta Was this translation helpful? Give feedback.
-
This issue seems somewhat related to #59. I will add that I fully agree with tqbf who says
|
Beta Was this translation helpful? Give feedback.
-
What people really want is a single key agent to do all their crypto needs, and GPG sucks if only because it doesnt allow the usage of plain encryption keys and cannot do encryption and signatures with a single secret. Wether or not code X or code Y is needed to sign/encrypt is not really a problem, although it doesnt really make sense to split them, but whatever; this is the dark age of sotware and a man shall do with what is available to him. GPG key agent currently supports a lot of systems. What does Signal have to do with any of this? It seems quite off-topic; here's some messaging platform that uses phone numbers to create trust. Lol. How does one get signal data from his messages to prove a certain device sent a certain message at a certain time? Who the fuck knows! So it's not a real tool, just another crypto2000 vertically-integrated corporate thing. If anything, libolm is what should be used as a comparison, and it seems to mostly do data-encryption, not signatures |
Beta Was this translation helpful? Give feedback.
-
Having no signature support pushes the problem onto users, and comes close to forcing users to "roll their own cryptography." They're not being asked to implement PK, but there are any number of ways a user could hack together their signing solution that opens attack vectors. Having a lack of a standard exposes users and hinders adoption. For example, look at @pauldotknopf's solution above: md5sum is vulnerable to intentional corruption and shouldn't be used here. Finally, by taking no position, age limits itself to people with a certain level of technical sophistication, which is exclusive. It consequently provides a disincentive for client software to add support as it forces them into a walled garden where the only (likely) compatible software is itself. A solution might provide a wrapper that uses minisign and age together as an all-in-one tool, easy to use and set up, combining public signing and encryption keys and farming out to the respective libraries... but, then, what about that hypothetical solution different from adding signing to age?
|
Beta Was this translation helpful? Give feedback.
-
Hey all, sorry to resurrect an old thread, but I just wrote up the details of how we think about authenticated encryption, including why we did not include support for signing and a possible way forward. I'd be interested in feedback from the people who had opinions or use cases revolving around this. https://words.filippo.io/dispatches/age-authentication/ |
Beta Was this translation helpful? Give feedback.
-
Nice to see progress with this. Separate encryption and signing has always been major trouble, as it forces one to do either sign then encrypt (violating the doom principle) or encrypt then sign (where it is easy to rip off the signature or replace it with another). Also if no encryption is employed at all, having the message plainly visible leads to signatures not being verified (because people are lazy), or to signature verification failures due to formatting changes in text (e.g. different whitespace or typographic quotation marks being inserted). These of course are just the issues that we are trying to address with Covert encryption. |
Beta Was this translation helpful? Give feedback.
-
@FiloSottile That was a pleasant read; thanks for taking the time to write it. It was written in a general case, and didn't talk explicitly about signing-for-email. You did a good job explaining that there's subtlety about what "signing" means; I think I'm (personally) still a little confused about how your proposed tweak (regardless of the technical implementation) would impact this issue. As you say, there are a couple of signing use cases; for email, I'd venture that most people who are signing are wanting the signature to assert that the email was signed by a specific identity. Ascertaining trust in the signing key itself is a separate issue. Web of trust, central cert authority, blockchain identity public ledger... I think it'd be ideal if the identity aspect be separated, and it'd be necessary with Age (I think?). Would you mind bringing it back to the email signing use case? What would a solution possibly look like using Age authentication? |
Beta Was this translation helpful? Give feedback.
-
@FiloSottile Here's some implementation with JavaScript using the @paulmillr 's audited Noble & Scure packages, using the secp256k1 but can be any really? import { secp256k1 } from '@noble/curves/secp256k1';
import { hmac } from '@noble/hashes/hmac';
import { pbkdf2 } from '@noble/hashes/pbkdf2';
import { scrypt } from '@noble/hashes/scrypt';
import { sha256 } from '@noble/hashes/sha256';
import { hexToBytes } from '@noble/hashes/utils';
import { bech32 } from '@scure/base';
function createAgeAuth(curve, deriveFn = defaultDeriver) {
return {
deriveAuth(agePrivkey) {
const { prefix, bytes: secret } = bech32.decodeToBytes(agePrivkey.toLowerCase());
if (prefix !== 'age-secret-key-') {
throw new Error('invalid age identity/privkey');
}
const authSecret = deriveFn(secret, 'AGE_AUTH_KEY_MSG_V1');
const curvePubkey = curve.getPublicKey(authSecret);
const privkey = bech32
.encode('AGE-AUTH-PRIVKEY-', bech32.toWords(authSecret), 1000)
.toUpperCase();
const pubkey = bech32.encode('age1authkey', bech32.toWords(curvePubkey), 1000);
return { privkey, pubkey };
},
sign(message, ageAuthPrivkey, raw = true) {
const sig = curve.sign(message, bech32.decodeToBytes(ageAuthPrivkey.toLowerCase()).bytes);
return raw ? sig : sig.toCompactHex();
},
verify(message, signature, agePubkey) {
return curve.verify(
typeof signature === 'string' ? hexToBytes(signature) : signature,
message,
bech32.decodeToBytes(agePubkey.toLowerCase()).bytes,
);
},
};
}
function defaultDeriver(skey, msg) {
return hmac(sha256, skey, msg);
}
function scryptDeriver(secret, msg) {
return scrypt(secret, msg, {
N: 2 ** 18,
r: 8,
p: 1,
dkLen: 32,
});
}
function pbkdf2Deriver(secret, msg) {
return pbkdf2(sha256, secret, msg, {
c: 2 ** 18,
dkLen: 32,
});
}
const ageAuth = createAgeAuth(secp256k1, pbkdf2Deriver);
//=> { privkey: 'AGE-SECRET-KEY-1..', pubkey: 'age1...' }
const alice = await getAgeIdentity();
const bob = await getAgeIdentity();
const charlie = await getAgeIdentity();
//=> { privkey: "AGE-AUTH-PRIVKEY-1...', pubkey: 'age1authkey1...' }
const aliceAuth = ageAuth.deriveAuth(alice.privkey);
const bobAuth = ageAuth.deriveAuth(bob.privkey);
const message = new TextEncoder().encode('hello world');
const signature = ageAuth.sign(message, aliceAuth.privkey);
const verified = ageAuth.verify(message, signature, aliceAuth.pubkey);
console.log({ aliceAuth, bobAuth, sig: signature.toCompactHex(), verified });
// {
// aliceAuth: {
// privkey: "AGE-AUTH-PRIVKEY-16X9Y89CC6EXWDY092XVKZJ8MWRL8M08N4W5DNUE9CU7DXYQPTJVSJXHQSS",
// pubkey: "age1authkey1qtjtp9y4mlxepwng8jec7x2v5f6yvcuf55ftjar336wh9vpvy6tts3m25x4",
// },
// bobAuth: {
// privkey: "AGE-AUTH-PRIVKEY-15GWF3WFR5EY47MD2EW2MDZY8GELUL2Z8H4GS594LRECL5CA3HVZQGCRVR6",
// pubkey: "age1authkey1qv2g278maxxw4znx6kwae4q0nc4tqx86mjauzeguzwa8x64yzxz9y0030xk",
// },
// sig: "08f134988c37ae30b01f36e6cf8d7799643146b1689338c25892a6ecfd40891372649fef8ff34d2d2f6479f0dbc44c08f1aeb9fd61d68f498eb71f371476ebcc",
// verified: true,
// } |
Beta Was this translation helpful? Give feedback.
-
According to the specification, signing of any type is out of scope:
This is, in my opinion, a missed opportunity. This project is an opportunity to not only build a new tool that addresses issues that plague other tools (such as GPG), but also to build support in the community around this tool. By placing this restriction on the functionality that age may contain, age is less useful, and will require the use off additional tools (and additional keys to manage) to achieve the same functionality that users will likely expect.
This expectation can be demonstrated by looking at the open issues (such as #38 & #49), and conversation on Twitter about age - the lack of signing support is striking potential users as a surprise, as it would be assumed that a tool of this nature would include signing support. There is, without question, user interest in this feature.
There are, as noted in the quoted section above, issues with signing support, in that it can involve key distribution and trust, which are complex issues - that said, these issues need not be addressed by age, as a solution is not required to allow users that wish to validate that an encrypted file is from the expected sender/system to perform this validation. Adding optional support for signing a file with the sender's key is a trivial matter; adding an additional header to the age file format is a simple matter, adding CLI support for this is likewise, a simple matter.
In use cases where it is desirable to validate the sender, it would not be unreasonable to require users to transmit their public key out-of-band (from a UX perspective, this could use the proposed
aliases.txt
file to display a friendly name for known senders). It is possible that key distribution methods could evolve around age, though these should be allowed to evolve organically, and it is not necessary for age to address this at this point in time.While there are tools such as signify and minisign (which I am personally a fan of), it is not a good user experience to require users to make use of an additional tool, with additional keys, to perform signing of files they produce.
I would propose that an optional header be added, which includes the sender's public key and a signature of the hash of the encrypted data. When a file is decrypted, this signature would be validated, and decryption should fail if this check fails. The user can either manually review the public key for a match to known senders, or age could look up the public key in the proposed
aliases.txt
file and display a friendly name. This could be done with minimal impact, would improve protection of files that have been signed, and require no extra effort from those that don't have a need to perform this validation.I appreciate that this could lead to a push for greater development of a key distribution & trust solution (which could evolve into a interesting side project, though that's another conversation), though it will without doubt lead to a better user experience and lead to further use cases for age.
Beta Was this translation helpful? Give feedback.
All reactions