Skip to content

Commit 7b6bcfd

Browse files
committed
zkgroup: Derive profile keys directly using AES
1 parent 7da883b commit 7b6bcfd

File tree

3 files changed

+17
-6
lines changed

3 files changed

+17
-6
lines changed

Cargo.lock

Lines changed: 2 additions & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

rust/zkgroup/Cargo.toml

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -19,12 +19,12 @@ workspace = true
1919
libsignal-account-keys = { workspace = true, features = ["serde"] }
2020
libsignal-core = { workspace = true }
2121
poksho = { workspace = true }
22-
signal-crypto = { workspace = true }
2322
zkcredential = { workspace = true, features = ["rayon"] }
2423

2524
# Use our fork of curve25519-dalek for zkgroup support.
2625
curve25519-dalek-signal = { workspace = true, features = ["serde"] }
2726

27+
aes = { workspace = true }
2828
aes-gcm-siv = { workspace = true }
2929
bincode = { workspace = true }
3030
const-str = { workspace = true }
@@ -38,6 +38,7 @@ rand = { workspace = true }
3838
rayon = { workspace = true }
3939
serde = { workspace = true, features = ["derive"] }
4040
sha2 = { workspace = true }
41+
static_assertions = { workspace = true }
4142
subtle = { workspace = true }
4243
thiserror = { workspace = true }
4344
uuid = { workspace = true }

rust/zkgroup/src/api/profiles/profile_key.rs

Lines changed: 13 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -3,9 +3,9 @@
33
// SPDX-License-Identifier: AGPL-3.0-only
44
//
55

6+
use aes::cipher::{BlockEncrypt as _, KeyInit as _};
67
use partial_default::PartialDefault;
78
use serde::{Deserialize, Serialize};
8-
use signal_crypto::Aes256GcmEncryption;
99
use subtle::ConstantTimeEq;
1010

1111
use crate::common::constants::*;
@@ -65,10 +65,19 @@ impl ProfileKey {
6565
}
6666

6767
pub fn derive_access_key(&self) -> [u8; ACCESS_KEY_LEN] {
68-
let nonce = &[0u8; AESGCM_NONCE_LEN];
69-
let mut cipher = Aes256GcmEncryption::new(&self.bytes, nonce, &[]).unwrap();
68+
// Uses AES to implement a seeded PRNG, taking the first block of output as the result.
69+
// Originally defined as AES-GCM(&mut [0; ACCESS_KEY_LEN], [0; NONCE_LEN], init_ctr=1).
70+
// AES-GCM uses the first block to initialize its tag hash, which we discard in this case,
71+
// so we can simplify to AES-CTR(&mut [0; ACCESS_KEY_LEN], [0; NONCE_LEN], init_ctr=2)
72+
// and then since our "plaintext" is zeros, this becomes simply raw AES([0, 0, ..., 2]).
73+
static_assertions::const_assert_eq!(ACCESS_KEY_LEN, {
74+
type BlockSize = <::aes::Aes256Enc as ::aes::cipher::BlockSizeUser>::BlockSize;
75+
<BlockSize as ::aes::cipher::Unsigned>::USIZE
76+
});
77+
let aes = ::aes::Aes256Enc::new((&self.bytes).into());
7078
let mut buf = [0u8; ACCESS_KEY_LEN];
71-
cipher.encrypt(&mut buf[..]);
79+
buf[ACCESS_KEY_LEN - 1] = 2;
80+
aes.encrypt_block((&mut buf).into());
7281
buf
7382
}
7483
}

0 commit comments

Comments
 (0)