EpicGames/lore · error · anyhow::Error

presigned_url_hmac_key must be at least

Error message

presigned_url_hmac_key must be at least {MIN_HMAC_KEY_BYTES} bytes, got {}

What it means

After successfully hex-decoding presigned_url_hmac_key, build_presign_config checks the decoded byte length against MIN_HMAC_KEY_BYTES and bails if the key is too short. Weak HMAC keys would allow presigned-URL signature forgery, so the server refuses to start.

Solutions

  1. Generate a longer key: `openssl rand -hex 32` and update the config.
  2. Check the decoded length: (hex_string.len() / 2) must be >= MIN_HMAC_KEY_BYTES.
  3. Rotate the short key in the upstream secret store and redeploy with the new value.

Example fix

// before
presigned_url_hmac_key = "00112233445566778899aabbccddeeff" // 16 bytes
// after
presigned_url_hmac_key = "<output of: openssl rand -hex 32>" // 32 bytes
Defensive patterns

Strategy: validation

Validate before calling

const MIN_HMAC_KEY_BYTES: usize = 32;
fn key_is_long_enough(hex_key: &str) -> bool {
    hex::decode(hex_key).map(|b| b.len() >= MIN_HMAC_KEY_BYTES).unwrap_or(false)
}

Prevention

When it happens

Trigger: PresignSettings.hmac_key decodes to fewer than MIN_HMAC_KEY_BYTES bytes, e.g. hex string of a 16-byte key passed where 32 bytes are required.

Common situations: Developers use a short test secret like "deadbeef"; a legacy deployment predates the minimum-length requirement; someone truncated the key during copy-paste.

Understand the failure class

Background: "value must be between 0 and 1" / "out of range" / "must not be negative" errors: fixing range-validation failures across open-source libraries — this error's family across 42 libraries.

Related errors


AI-assisted analysis of EpicGames/lore@074eb0b0d1 (2026-09-13). Data as JSON: /api/errors/0c8a7461cdb2d13f. Report an issue: GitHub.

Appendix: source

Thrown at lore-server/src/http/server.rs:207

/// Renders the resolved allowlist for the startup log.
fn describe_allowed_types(types: &[String]) -> String {
    if types.is_empty() {
        "<none>".to_string()
    } else {
        types.join(", ")
    }
}

fn build_presign_config(settings: &PresignSettings) -> Result<Option<PresignConfig>> {
    let Some(key_hex) = settings.hmac_key.as_deref() else {
        return Ok(None);
    };

    let key_bytes = hex::decode(key_hex)
        .map_err(|e| anyhow::anyhow!("presigned_url_hmac_key is not valid hex: {e}"))?;

    if key_bytes.len() < MIN_HMAC_KEY_BYTES {
        anyhow::bail!(
            "presigned_url_hmac_key must be at least {MIN_HMAC_KEY_BYTES} bytes, got {}",
            key_bytes.len()
        );
    }

    let key_id = blake3::hash(&key_bytes).to_hex()[..16].to_string();
    let hmac_key = hmac::Key::new(hmac::HMAC_SHA256, &key_bytes);

    Ok(Some(PresignConfig {
        hmac_key,
        key_id,
        min_ttl_seconds: settings.min_ttl_seconds,
        default_ttl_seconds: settings.default_ttl_seconds,
        max_ttl_seconds: settings.max_ttl_seconds,
        content_type_allowlist: ContentTypeAllowlist::try_from_policy(
            &settings.content_type_policy,
        )
        .map_err(|err| anyhow!("{} {err}", presign_content_type_field(err.field())))?,

View on GitHub (pinned to 074eb0b0d1)