1个稳定版本

1.0.0 2021年2月18日

#9 in #session-keys

Apache-2.0

45KB
869 代码行

LiteSession

创建抵抗滥用和劫持的会话令牌


lib.rs:

LiteSession是一个安全令牌生成器,可用于HTTP认证头、cookie、Json Web Tokens的替代品、物联网以及其他需要客户端和服务器之间通信安全令牌的任何地方。它提供带有关联客户端数据的密钥哈希消息认证码,以加密(默认设置)或未加密的形式。

算法的一般形式是

identifier | issued | expiry | (data)k | nonce | ConfidentialityMode | Blake3HMAC( username | issued | expiration | data | session key, k)
            where `k = Blake3HMAC(user | issued | expiry | ConfidentialityMode, sk)`

涉及步骤包括

  1. 生成一个 随机标识符

  2. 生成一个 发行时间过期时间,精确到纳秒

  3. 生成用于加密令牌数据部分的 加密密钥。使用算法 k = Blake3HMAC(identifier | issued | expiry | ConfidentialityMode, sk)

    • 创建一个空的字符串 encryption_key
    • identifier 添加到 encryption_key
    • issued 添加到 encryption_key
    • expiry 添加到 encryption_key
    • ConfidentialityMode 添加到 encryption_key
    • 使用键控模式的Blake3和 server_key 作为密钥对 encryption_key 执行HMAC函数
    • hex 或作为 string 返回上述Blake3操作的结果
  4. 使用上述Blake3Hash作为加密密钥,使用 ChaCha8 加密加密数据

  5. 返回加密数据和 nonce

  6. identifier | issued | expiry | (data)k | nonce | ConfidentialityMode 执行Blake3Hmac

  7. 生成令牌

    • 创建一个名为 token 的空字符串
    • identifier 添加到 token
    • issued 添加到 token
    • expiry 添加到 token
    • 将加密数据 encrypted data 添加到 token
    • nonce 添加到 token
    • ConfidentialityMode 添加到 token
    • Blake3Hmac 添加到 token
    • 以字符串或十六进制形式返回token。生成的token格式为 identifier⊕issued⊕expiry⊕ciphertext⊕nonce⊕confidentiality⊕hmac

验证token的步骤如下

  1. 检查token结构是否有效
  2. 将token解构为其组件字段
  3. expiry 与服务器的 当前时间 进行比较,并返回 SessionExpired 作为 TokenOutcome
  4. 计算加密密钥如下: k=HMAC(identifier | issued | expiry | ConfidentialityMode, sk)
  5. 使用 k 解密加密数据。
  6. 计算 Blake3HMAC(identifier | issued | expiry | ciphertext | nonce | ConfidentialityMode | session key, k)
  7. 如果token匹配,则返回 TokenOutcome::TokenAuthetic;如果token不匹配,则返回 TokenOutcome::TokenRejected

keyed 模式下使用 Blake3 算法,密钥长度为 32byte/256bitChaCha8 算法需要一个 32byte/256bit 的密钥和一个 12byte/96bit 的nonce。使用 International Atomic Time(TAI) 来保证纳秒级精度,且无需处理闰秒和时区问题。使用 session key 可以防止 volumeDenning-Sacco 攻击

用法

创建token

use lite_session::{LiteSessionToken, LiteSessionError, ConfidentialityMode, LiteSessionData, Role, LiteSessionMode};
use core::time::Duration;

fn main() -> Result<(), LiteSessionError> {
    let mut token = LiteSessionToken::default();

    let expiry = 60*60_u64;
    token.expiry(expiry);

    let mut data = LiteSessionData::default();
    data.username("foo_user");
    data.role(Role::SuperUser);
    data.tag("Foo-Tag");
    data.add_acl("Network-TCP");
    data.add_acl("Network-UDP");
    token.hmac_data(data);
    token.confidential(true);
    token.mode(LiteSessionMode::SessionID("foobarbaz".into()));

    let server_key = [0_u8; 32];
    let session_token = token.build_secure(&server_key)?;

    Ok(())
}

验证token

use lite_session::{LiteSessionToken, LiteSessionError, ConfidentialityMode, LiteSessionData, LiteSessionMode};
fn main() -> Result<(), LiteSessionError> {
    let server_key = [0_u8; 32];

    let mut destructured = LiteSessionToken::default();
    let session_token = "5tl726krvgmhoe1pyc4jadqs3fw09bi8⊕40000000602e51ab3a8e2d17⊕40000000603013ab3a8e2d17⊕3cf157bed212d5b34122a713ea860ec373800e5004bff1a195d603305bd5b7921d1017e70ef599bc1f7ed949bd3c66c696d74a16487f95a3f6fd⊕jrzapflsi618⊕ConfidentialityMode::High⊕4faab373d7247dfb2d50e213e5cb66e415afc22066f71c2b966fdeabb11cac64";
    let outcome = destructured.from_string(&server_key, &session_token)?;
    
    Ok(())
}

依赖项

~2.5MB
~59K SLoC