Skip to content

Commit 773b376

Browse files
authored
fix: transform payload to sha256 digest only for public key validator (#1711)
1 parent c3ddc13 commit 773b376

File tree

4 files changed

+59
-36
lines changed

4 files changed

+59
-36
lines changed

agent-control/src/opamp/remote_config/validators/signature/public_key.rs

Lines changed: 28 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,9 @@
11
use crate::opamp::remote_config::signature::SigningAlgorithm;
22
use crate::opamp::remote_config::validators::signature::public_key_fetcher::KeyData;
33
use crate::opamp::remote_config::validators::signature::verifier::Verifier;
4-
use base64::{Engine as _, engine::general_purpose::URL_SAFE_NO_PAD};
4+
use base64::engine::general_purpose::URL_SAFE_NO_PAD;
5+
use base64::{Engine, prelude::BASE64_STANDARD};
6+
use ring::digest;
57
use ring::signature::{ED25519, UnparsedPublicKey};
68
use thiserror::Error;
79

@@ -68,9 +70,18 @@ impl Verifier for PublicKey {
6870
));
6971
}
7072

71-
self.public_key.verify(msg, signature).map_err(|_| {
72-
PubKeyError::ValidatingSignature("signature verification failed".to_string())
73-
})
73+
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
74+
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
75+
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
76+
// the signature against that.
77+
let msg = digest::digest(&digest::SHA256, msg);
78+
let msg = BASE64_STANDARD.encode(msg);
79+
80+
self.public_key
81+
.verify(msg.as_bytes(), signature)
82+
.map_err(|_| {
83+
PubKeyError::ValidatingSignature("signature verification failed".to_string())
84+
})
7485
}
7586

7687
fn key_id(&self) -> &str {
@@ -86,6 +97,7 @@ mod tests {
8697
use crate::opamp::remote_config::validators::signature::verifier::Verifier;
8798
use base64::Engine;
8899
use base64::prelude::BASE64_STANDARD;
100+
use ring::digest;
89101
use ring::rand::SystemRandom;
90102
use ring::signature::{Ed25519KeyPair, KeyPair, UnparsedPublicKey};
91103
use serde_json::json;
@@ -103,8 +115,11 @@ mod tests {
103115
let first_key = payload.keys.first().unwrap();
104116

105117
let pub_key = PublicKey::try_new(first_key).unwrap();
118+
// This is a direct call to the verify function. It isn't concerned with the digest
119+
// and base64 transformation
106120
pub_key
107-
.verify_signature(&ED25519, message.as_bytes(), signature.as_bytes())
121+
.public_key
122+
.verify(message.as_bytes(), signature.as_bytes())
108123
.unwrap();
109124
}
110125

@@ -114,7 +129,14 @@ mod tests {
114129
let key_pair = Ed25519KeyPair::from_pkcs8(pkcs8_bytes.as_ref()).unwrap();
115130

116131
const MESSAGE: &[u8] = b"hello, world";
117-
let signature = key_pair.sign(MESSAGE);
132+
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
133+
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
134+
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
135+
// the signature against that.
136+
let digest = digest::digest(&digest::SHA256, MESSAGE);
137+
let msg = BASE64_STANDARD.encode(digest);
138+
139+
let signature = key_pair.sign(msg.as_bytes());
118140

119141
let pub_key = PublicKey {
120142
key_id: "my-signing-key-test/0".to_string(),

agent-control/src/opamp/remote_config/validators/signature/public_key_fetcher.rs

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ pub mod tests {
7373
use base64::Engine;
7474
use base64::prelude::{BASE64_STANDARD, BASE64_URL_SAFE_NO_PAD};
7575
use httpmock::prelude::*;
76+
use ring::digest;
7677
use ring::rand::SystemRandom;
7778
use ring::signature::{Ed25519KeyPair, KeyPair};
7879
use serde_json::json;
@@ -135,7 +136,7 @@ pub mod tests {
135136
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
136137
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
137138
// the signature against that.
138-
let digest = ring::digest::digest(&ring::digest::SHA256, msg);
139+
let digest = digest::digest(&digest::SHA256, msg);
139140
let msg = BASE64_STANDARD.encode(digest);
140141
BASE64_STANDARD.encode(self.key_pair.sign(msg.as_bytes()).as_ref())
141142
}

agent-control/src/opamp/remote_config/validators/signature/validator.rs

Lines changed: 6 additions & 21 deletions
Original file line numberDiff line numberDiff line change
@@ -11,8 +11,6 @@ use crate::opamp::remote_config::validators::signature::public_key::PublicKey;
1111
use crate::opamp::remote_config::validators::signature::public_key_fetcher::PublicKeyFetcher;
1212
use crate::opamp::remote_config::validators::signature::verifier::VerifierStore;
1313
use crate::sub_agent::identity::AgentIdentity;
14-
use base64::Engine as _;
15-
use base64::prelude::BASE64_STANDARD;
1614
use serde::Deserialize;
1715
use std::path::PathBuf;
1816
use std::time::Duration;
@@ -214,15 +212,8 @@ impl RemoteConfigValidator for CompositeSignatureValidator {
214212

215213
let config_content = opamp_remote_config
216214
.get_unique()
217-
.map_err(|e| SignatureValidatorError::VerifySignature(e.to_string()))?;
218-
219-
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
220-
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
221-
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
222-
// the signature against that.
223-
let digest = ring::digest::digest(&ring::digest::SHA256, config_content.as_ref());
224-
let digest_b64 = BASE64_STANDARD.encode(digest);
225-
let msg = digest_b64.as_bytes();
215+
.map_err(|e| SignatureValidatorError::VerifySignature(e.to_string()))?
216+
.as_bytes();
226217

227218
// Until backend migrates to new signature platform, the validation starts with the public key based,
228219
// and falls back to cert based in case of failure.
@@ -232,7 +223,7 @@ impl RemoteConfigValidator for CompositeSignatureValidator {
232223
match public_key_store.verify_signature(
233224
signature.signature_algorithm(),
234225
signature.key_id(),
235-
msg,
226+
config_content,
236227
signature.signature(),
237228
) {
238229
Ok(()) => return Ok(()),
@@ -251,7 +242,7 @@ impl RemoteConfigValidator for CompositeSignatureValidator {
251242
.verify_signature(
252243
signature.signature_algorithm(),
253244
signature.key_id(),
254-
msg,
245+
config_content,
255246
signature.signature(),
256247
)
257248
.map_err(|e| SignatureValidatorError::VerifySignature(e.to_string()))
@@ -677,14 +668,8 @@ pub mod tests {
677668
);
678669

679670
let config = "value";
680-
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
681-
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
682-
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
683-
// the signature against that.
684-
let digest = ring::digest::digest(&ring::digest::SHA256, config.as_bytes());
685-
let msg = BASE64_STANDARD.encode(digest);
686-
687-
let encoded_signature = test_signer.encoded_signature(&msg);
671+
672+
let encoded_signature = test_signer.encoded_signature(config);
688673
let remote_config = OpampRemoteConfig::new(
689674
AgentID::AgentControl,
690675
Hash::from("test"),

agent-control/tests/common/opamp.rs

Lines changed: 23 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -15,6 +15,7 @@ use opamp_client::opamp::proto::{
1515
use opamp_client::operation::instance_uid::InstanceUid;
1616
use prost::Message;
1717
use rcgen::{CertificateParams, KeyPair, PKCS_ED25519, PublicKeyData};
18+
use ring::digest;
1819
use ring::rand::SystemRandom;
1920
use ring::signature::{Ed25519KeyPair, KeyPair as _};
2021
use serde_json::json;
@@ -277,7 +278,16 @@ async fn opamp_handler(state: web::Data<Arc<Mutex<ServerState>>>, req: web::Byte
277278
(&server_state.key_pair, public_key_fingerprint(&public_key))
278279
};
279280

280-
let server_to_agent = build_response(identifier, remote_config, key_pair, key_id, flags);
281+
let use_legacy_signature = server_state.use_legacy_signatures;
282+
283+
let server_to_agent = build_response(
284+
identifier,
285+
remote_config,
286+
key_pair,
287+
key_id,
288+
flags,
289+
use_legacy_signature,
290+
);
281291
HttpResponse::Ok().body(server_to_agent)
282292
}
283293

@@ -308,6 +318,7 @@ fn build_response(
308318
key_pair: &Ed25519KeyPair,
309319
key_id: String,
310320
flags: u64,
321+
use_legacy_signature: bool,
311322
) -> Vec<u8> {
312323
let mut remote_config = None;
313324
let mut custom_message = None;
@@ -326,19 +337,23 @@ fn build_response(
326337
}),
327338
});
328339

329-
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
330-
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
331-
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
332-
// the signature against that.
333-
let digest = ring::digest::digest(&ring::digest::SHA256, config.raw_body.as_bytes());
334-
let msg = BASE64_STANDARD.encode(digest);
340+
let msg = if use_legacy_signature {
341+
config.raw_body
342+
} else {
343+
// Actual implementation from FC side signs the Base64 representation of the SHA256 digest
344+
// of the message (i.e. the remote configs). Hence, to verify the signature, we need to
345+
// compute the SHA256 digest of the message, then Base64 encode it, and finally verify
346+
// the signature against that.
347+
let digest = digest::digest(&digest::SHA256, config.raw_body.as_bytes());
348+
BASE64_STANDARD.encode(digest)
349+
};
335350

336351
let signature = key_pair.sign(msg.as_bytes());
337352

338353
let custom_message_data = HashMap::from([(
339354
"fakeCRC".to_string(), //AC is not using the CRC.
340355
vec![SignatureFields {
341-
signature: BASE64_STANDARD.encode(signature.as_ref()),
356+
signature: BASE64_STANDARD.encode(signature),
342357
signing_algorithm: ED25519,
343358
key_id,
344359
}],

0 commit comments

Comments
 (0)