Skip to content

Commit 012e214

Browse files
committed
add S2K Argon2 and AEAD s2k usage
1 parent 0d5c2b9 commit 012e214

20 files changed

+686
-84
lines changed

include/repgp/repgp_def.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,11 @@
9191
/* Salt size for hashing */
9292
#define PGP_SALT_SIZE 8
9393

94+
#if defined(ENABLE_CRYPTO_REFRESH)
95+
/* Max salt size for s2k */
96+
#define PGP_MAX_S2K_SALT_SIZE 16
97+
#endif
98+
9499
/* SEIPDv2 salt length */
95100
#ifdef ENABLE_CRYPTO_REFRESH
96101
#define PGP_SEIPDV2_SALT_LEN 32
@@ -326,6 +331,9 @@ typedef enum {
326331
*/
327332
typedef enum {
328333
PGP_S2KU_NONE = 0,
334+
#if defined(ENABLE_CRYPTO_REFRESH)
335+
PGP_S2KU_AEAD = 253,
336+
#endif
329337
PGP_S2KU_ENCRYPTED_AND_HASHED = 254,
330338
PGP_S2KU_ENCRYPTED = 255
331339
} pgp_s2k_usage_t;
@@ -336,6 +344,9 @@ typedef enum : uint8_t {
336344
PGP_S2KS_SIMPLE = 0,
337345
PGP_S2KS_SALTED = 1,
338346
PGP_S2KS_ITERATED_AND_SALTED = 3,
347+
#if defined(ENABLE_CRYPTO_REFRESH)
348+
PGP_S2KS_ARGON2 = 4,
349+
#endif
339350
PGP_S2KS_EXPERIMENTAL = 101
340351
} pgp_s2k_specifier_t;
341352

include/rnp/rnp.h

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2712,6 +2712,9 @@ RNP_API rnp_result_t rnp_key_is_locked(rnp_key_handle_t key, bool *result);
27122712
* protection.
27132713
* - "Encrypted-Hashed" : secret key data is encrypted, using the SHA1 hash as
27142714
* an integrity protection.
2715+
* - "AEAD-Encrypted" : secret key data is encrypted using an AEAD algorithm
2716+
* with built-in integrity protection. (only available in experimental build
2717+
* that enables ENABLE_CRYTPO_REFRESH)
27152718
* - "GPG-None" : secret key data is not available at all (this would happen if
27162719
* secret key is exported from GnuPG via --export-secret-subkeys)
27172720
* - "GPG-Smartcard" : secret key data is stored on smartcard by GnuPG, so is not

src/lib/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -211,7 +211,7 @@ if(CRYPTO_BACKEND_BOTAN)
211211
resolve_feature_state(ENABLE_AEAD "AEAD_EAX;AEAD_OCB")
212212
resolve_feature_state(ENABLE_TWOFISH "TWOFISH")
213213
resolve_feature_state(ENABLE_IDEA "IDEA")
214-
resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF")
214+
resolve_feature_state(ENABLE_CRYPTO_REFRESH "HKDF;ARGON2")
215215
resolve_feature_state(ENABLE_PQC "KMAC;DILITHIUM;KYBER;SPHINCS_PLUS_WITH_SHA2;SPHINCS_PLUS_WITH_SHAKE")
216216
resolve_feature_state(ENABLE_BLOWFISH "BLOWFISH")
217217
resolve_feature_state(ENABLE_CAST5 "CAST_128")

src/lib/crypto/s2k.cpp

Lines changed: 95 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -42,6 +42,11 @@
4242
#include "types.h"
4343
#include "utils.h"
4444
#ifdef CRYPTO_BACKEND_BOTAN
45+
#if defined(ENABLE_CRYPTO_REFRESH)
46+
#include "botan/bigint.h"
47+
#include <botan/pwdhash.h>
48+
#include <cmath>
49+
#endif
4550
#include <botan/ffi.h>
4651
#include "hash_botan.hpp"
4752
#endif
@@ -66,11 +71,30 @@ pgp_s2k_derive_key(pgp_s2k_t *s2k, const char *password, uint8_t *key, int keysi
6671
iterations = s2k->iterations;
6772
}
6873
break;
74+
#if defined(ENABLE_CRYPTO_REFRESH)
75+
case PGP_S2KS_ARGON2:
76+
saltptr = s2k->salt;
77+
break;
78+
#endif
6979
default:
7080
return false;
7181
}
7282

73-
if (pgp_s2k_iterated(s2k->hash_alg, key, keysize, password, saltptr, iterations)) {
83+
#if defined(ENABLE_CRYPTO_REFRESH)
84+
if (s2k->specifier == PGP_S2KS_ARGON2) {
85+
if (pgp_s2k_argon2(key,
86+
keysize,
87+
password,
88+
saltptr,
89+
s2k->argon2_t,
90+
s2k->argon2_p,
91+
s2k->argon2_encoded_m)) {
92+
RNP_LOG("s2k argon2 failed");
93+
return false;
94+
}
95+
} else
96+
#endif
97+
if (pgp_s2k_iterated(s2k->hash_alg, key, keysize, password, saltptr, iterations)) {
7498
RNP_LOG("s2k failed");
7599
return false;
76100
}
@@ -79,6 +103,45 @@ pgp_s2k_derive_key(pgp_s2k_t *s2k, const char *password, uint8_t *key, int keysi
79103
}
80104

81105
#ifdef CRYPTO_BACKEND_BOTAN
106+
#if defined(ENABLE_CRYPTO_REFRESH)
107+
int
108+
pgp_s2k_argon2(uint8_t * out,
109+
size_t output_len,
110+
const char * password,
111+
const uint8_t *salt,
112+
uint8_t t,
113+
uint8_t p,
114+
uint8_t encoded_m)
115+
{
116+
const size_t argon2_salt_size = 16;
117+
118+
/* check constraints on p and t */
119+
if (!p || !t) {
120+
RNP_LOG("Argon2 t and p must be non-zero");
121+
return -1;
122+
}
123+
/* check constraints on m. Floating point calculation is fine due to restricted data range
124+
* (uint8_t) */
125+
if (encoded_m < (3 + (uint8_t) std::ceil(std::log2(p))) || encoded_m > 31) {
126+
RNP_LOG("Argon2 encoded_m must be between 3+ceil(log2(p)) and 31");
127+
return -1;
128+
}
129+
130+
try {
131+
auto pwdhash_fam = Botan::PasswordHashFamily::create_or_throw("Argon2id");
132+
133+
std::unique_ptr<Botan::PasswordHash> pwhash =
134+
pwdhash_fam->from_params(1 << encoded_m, t, p);
135+
pwhash->derive_key(
136+
out, output_len, password, std::strlen(password), salt, argon2_salt_size);
137+
} catch (const std::exception &e) {
138+
RNP_LOG("%s", e.what());
139+
return -1;
140+
}
141+
return 0;
142+
}
143+
#endif
144+
82145
int
83146
pgp_s2k_iterated(pgp_hash_alg_t alg,
84147
uint8_t * out,
@@ -201,3 +264,34 @@ pgp_s2k_compute_iters(pgp_hash_alg_t alg, size_t desired_msec, size_t trial_msec
201264

202265
return pgp_s2k_decode_iterations((iters > MIN_ITERS) ? iters : MIN_ITERS);
203266
}
267+
268+
size_t
269+
pgp_s2k_t::salt_size(pgp_s2k_specifier_t specifier)
270+
{
271+
#if defined(ENABLE_CRYPTO_REFRESH)
272+
return (specifier == PGP_S2KS_ARGON2 ? 16 : 8);
273+
#endif
274+
return 8;
275+
}
276+
277+
uint8_t
278+
pgp_s2k_t::specifier_len(pgp_s2k_specifier_t specifier)
279+
{
280+
switch (specifier) {
281+
case PGP_S2KS_SIMPLE:
282+
return 2;
283+
case PGP_S2KS_SALTED:
284+
return 10;
285+
case PGP_S2KS_ITERATED_AND_SALTED:
286+
return 11;
287+
case PGP_S2KS_EXPERIMENTAL:
288+
return 0; /* not used */
289+
#if defined(ENABLE_CRYPTO_REFRESH)
290+
case PGP_S2KS_ARGON2:
291+
return 20;
292+
#endif
293+
default:
294+
RNP_LOG("invalid specifier");
295+
throw rnp::rnp_exception(RNP_ERROR_BAD_PARAMETERS);
296+
}
297+
}

src/lib/crypto/s2k.h

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,10 +32,21 @@
3232
#define RNP_S2K_H_
3333

3434
#include <cstdint>
35+
#include <rnp/rnp_def.h>
3536
#include "repgp/repgp_def.h"
3637

3738
typedef struct pgp_s2k_t pgp_s2k_t;
3839

40+
#if defined(ENABLE_CRYPTO_REFRESH)
41+
int pgp_s2k_argon2(uint8_t * out,
42+
size_t output_len,
43+
const char * password,
44+
const uint8_t *salt,
45+
uint8_t t,
46+
uint8_t p,
47+
uint8_t encoded_m);
48+
#endif
49+
3950
int pgp_s2k_iterated(pgp_hash_alg_t alg,
4051
uint8_t * out,
4152
size_t output_len,

src/lib/key.cpp

Lines changed: 39 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -922,6 +922,9 @@ Key::has_secret() const noexcept
922922
case PGP_S2KS_SIMPLE:
923923
case PGP_S2KS_SALTED:
924924
case PGP_S2KS_ITERATED_AND_SALTED:
925+
#if defined(ENABLE_CRYPTO_REFRESH)
926+
case PGP_S2KS_ARGON2:
927+
#endif
925928
return true;
926929
default:
927930
return false;
@@ -1300,21 +1303,42 @@ Key::protect(pgp_key_pkt_t & decrypted,
13001303
return false;
13011304
}
13021305

1303-
/* force encrypted-and-hashed and iterated-and-salted as it's the only method we support*/
1304-
pkt_.sec_protection.s2k.usage = PGP_S2KU_ENCRYPTED_AND_HASHED;
1305-
pkt_.sec_protection.s2k.specifier = PGP_S2KS_ITERATED_AND_SALTED;
1306-
/* use default values where needed */
1307-
pkt_.sec_protection.symm_alg =
1308-
protection.symm_alg ? protection.symm_alg : DEFAULT_PGP_SYMM_ALG;
1309-
pkt_.sec_protection.cipher_mode =
1310-
protection.cipher_mode ? protection.cipher_mode : DEFAULT_PGP_CIPHER_MODE;
1311-
pkt_.sec_protection.s2k.hash_alg =
1312-
protection.hash_alg ? protection.hash_alg : DEFAULT_PGP_HASH_ALG;
1313-
auto iter = protection.iterations;
1314-
if (!iter) {
1315-
iter = ctx.s2k_iterations(pkt_.sec_protection.s2k.hash_alg);
1316-
}
1317-
pkt_.sec_protection.s2k.iterations = pgp_s2k_round_iterations(iter);
1306+
#if defined(ENABLE_CRYPTO_REFRESH)
1307+
/* force AEAD-encrypted and Argon2 at least for v6 keys */
1308+
if (decrypted.version == PGP_V6) {
1309+
pkt_.sec_protection.s2k.usage = PGP_S2KU_AEAD;
1310+
pkt_.sec_protection.s2k.specifier = PGP_S2KS_ARGON2;
1311+
/* use default values where needed */
1312+
pkt_.sec_protection.symm_alg = PGP_SA_AES_256;
1313+
pkt_.sec_protection.aead_alg = PGP_AEAD_OCB;
1314+
pkt_.sec_protection.cipher_mode = PGP_CIPHER_MODE_NONE;
1315+
pkt_.sec_protection.s2k.hash_alg = PGP_HASH_UNKNOWN;
1316+
// use reasonable default for argon2
1317+
pkt_.sec_protection.s2k.argon2_encoded_m = 21;
1318+
pkt_.sec_protection.s2k.argon2_p = 4;
1319+
pkt_.sec_protection.s2k.argon2_t = 1;
1320+
auto iter = protection.iterations;
1321+
pkt_.sec_protection.s2k.iterations = 0;
1322+
} else
1323+
#endif
1324+
{
1325+
/* force encrypted-and-hashed and iterated-and-salted as it's the only method we
1326+
* support*/
1327+
pkt_.sec_protection.s2k.usage = PGP_S2KU_ENCRYPTED_AND_HASHED;
1328+
pkt_.sec_protection.s2k.specifier = PGP_S2KS_ITERATED_AND_SALTED;
1329+
/* use default values where needed */
1330+
pkt_.sec_protection.symm_alg =
1331+
protection.symm_alg ? protection.symm_alg : DEFAULT_PGP_SYMM_ALG;
1332+
pkt_.sec_protection.cipher_mode =
1333+
protection.cipher_mode ? protection.cipher_mode : DEFAULT_PGP_CIPHER_MODE;
1334+
pkt_.sec_protection.s2k.hash_alg =
1335+
protection.hash_alg ? protection.hash_alg : DEFAULT_PGP_HASH_ALG;
1336+
auto iter = protection.iterations;
1337+
if (!iter) {
1338+
iter = ctx.s2k_iterations(pkt_.sec_protection.s2k.hash_alg);
1339+
}
1340+
pkt_.sec_protection.s2k.iterations = pgp_s2k_round_iterations(iter);
1341+
}
13181342
if (!ownpkt) {
13191343
/* decrypted is assumed to be temporary variable so we may modify it */
13201344
decrypted.sec_protection = pkt_.sec_protection;

src/lib/rnp.cpp

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -234,6 +234,9 @@ static const id_str_pair s2k_type_map[] = {
234234
{PGP_S2KS_SIMPLE, "Simple"},
235235
{PGP_S2KS_SALTED, "Salted"},
236236
{PGP_S2KS_ITERATED_AND_SALTED, "Iterated and salted"},
237+
#if defined(ENABLE_CRYPTO_REFRESH)
238+
{PGP_S2KS_ARGON2, "Argon2"},
239+
#endif
237240
{0, NULL}};
238241

239242
static const id_str_pair key_usage_map[] = {
@@ -7689,6 +7692,11 @@ try {
76897692
if ((s2k.usage == PGP_S2KU_ENCRYPTED) && (s2k.specifier != PGP_S2KS_EXPERIMENTAL)) {
76907693
res = "Encrypted";
76917694
}
7695+
#if defined(ENABLE_CRYPTO_REFRESH)
7696+
if ((s2k.usage == PGP_S2KU_AEAD) && (s2k.specifier != PGP_S2KS_EXPERIMENTAL)) {
7697+
res = "AEAD-encrypted";
7698+
}
7699+
#endif
76927700
if ((s2k.usage == PGP_S2KU_ENCRYPTED_AND_HASHED) &&
76937701
(s2k.specifier != PGP_S2KS_EXPERIMENTAL)) {
76947702
res = "Encrypted-Hashed";

src/lib/types.h

Lines changed: 21 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -129,19 +129,36 @@ typedef struct pgp_s2k_t {
129129
/* below fields may not all be valid, depending on the usage field above */
130130
pgp_s2k_specifier_t specifier{};
131131
pgp_hash_alg_t hash_alg{};
132-
uint8_t salt[PGP_SALT_SIZE];
133-
unsigned iterations{};
132+
#if defined(ENABLE_CRYPTO_REFRESH)
133+
uint8_t salt[PGP_MAX_S2K_SALT_SIZE];
134+
#else
135+
uint8_t salt[PGP_SALT_SIZE];
136+
#endif
137+
unsigned iterations{};
134138
/* GnuPG custom s2k data */
135139
pgp_s2k_gpg_extension_t gpg_ext_num{};
136140
uint8_t gpg_serial_len{};
137141
uint8_t gpg_serial[16];
138142
/* Experimental s2k data */
139143
std::vector<uint8_t> experimental{};
144+
145+
#if defined(ENABLE_CRYPTO_REFRESH)
146+
/* argon2 */
147+
uint8_t argon2_t;
148+
uint8_t argon2_p;
149+
uint8_t argon2_encoded_m;
150+
#endif
151+
152+
static size_t salt_size(pgp_s2k_specifier_t specifier);
153+
static uint8_t specifier_len(pgp_s2k_specifier_t specifier);
140154
} pgp_s2k_t;
141155

142156
typedef struct pgp_key_protection_t {
143-
pgp_s2k_t s2k{}; /* string-to-key kdf params */
144-
pgp_symm_alg_t symm_alg{}; /* symmetric alg */
157+
pgp_s2k_t s2k{}; /* string-to-key kdf params */
158+
pgp_symm_alg_t symm_alg{}; /* symmetric alg */
159+
#if defined(ENABLE_CRYPTO_REFRESH)
160+
pgp_aead_alg_t aead_alg{}; /* AEAD alg */
161+
#endif
145162
pgp_cipher_mode_t cipher_mode{}; /* block cipher mode */
146163
uint8_t iv[PGP_MAX_BLOCK_SIZE];
147164
} pgp_key_protection_t;

src/librekey/key_store_g10.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1125,7 +1125,7 @@ gnupg_sexp_t::add_protected_seckey(pgp_key_pkt_t & seckey,
11251125

11261126
// randomize IV and salt
11271127
ctx.rng.get(prot.iv, sizeof(prot.iv));
1128-
ctx.rng.get(prot.s2k.salt, sizeof(prot.s2k.salt));
1128+
ctx.rng.get(prot.s2k.salt, prot.s2k.salt_size(prot.s2k.specifier));
11291129

11301130
// write seckey
11311131
gnupg_sexp_t raw_s_exp;

src/librepgp/stream-ctx.cpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -41,7 +41,7 @@ rnp_ctx_t::add_encryption_password(const std::string &password,
4141
info.s2k.usage = PGP_S2KU_ENCRYPTED_AND_HASHED;
4242
info.s2k.specifier = PGP_S2KS_ITERATED_AND_SALTED;
4343
info.s2k.hash_alg = halg;
44-
sec_ctx.rng.get(info.s2k.salt, sizeof(info.s2k.salt));
44+
sec_ctx.rng.get(info.s2k.salt, info.s2k.salt_size(info.s2k.specifier));
4545
if (!iterations) {
4646
iterations = sec_ctx.s2k_iterations(halg);
4747
}

0 commit comments

Comments
 (0)