Eine aufbereitete Darstellung der Quelle

 
     
 
 
Anforderungen  |   Konzepte  |   Entwurf  |   Entwicklung  |   Qualitätssicherung  |   Lebenszyklus  |   Steuerung
 
 
 
 

Benutzer

Quelle  aesgcm.rs

  Sprache: Rust
 

/* This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at http://mozilla.org/MPL/2.0/.
 *
 * This supports the now obsolete HTTP-ECE Draft 02 "aesgcm" content
 * type. There are a number of providers that still use this format,
 * and there's no real mechanism to return the client supported crypto
 * versions.
 *
 * */


//! Web Push encryption structure for the legacy AESGCM encoding scheme
//! ([Web Push Encryption Draft 4](https://tools.ietf.org/html/draft-ietf-webpush-encryption-04))
//!
//! This module is meant for advanced use. For simple encryption/decryption, use the top-level
//! [`encrypt_aesgcm`](crate::legacy::encrypt_aesgcm) and [`decrypt_aesgcm`](crate::legacy::decrypt_aesgcm)
//! functions.

use crate::{
    common::*,
    crypto::{self, Cryptographer, LocalKeyPair, RemotePublicKey},
    error::*,
};
use base64::Engine;
use byteorder::{BigEndian, ByteOrder};

pub(crateconst ECE_AESGCM_PAD_SIZE: usize = 2;

const ECE_WEBPUSH_AESGCM_KEYPAIR_LENGTH: usize = 134// (2 + Raw Key Length) * 2
const ECE_WEBPUSH_AESGCM_AUTHINFO: &str = "Content-Encoding: auth\0";

// a DER prefixed key is "\04" + ECE_WEBPUSH_RAW_KEY_LENGTH
const ECE_WEBPUSH_RAW_KEY_LENGTH: usize = 65;
const ECE_WEBPUSH_IKM_LENGTH: usize = 32;

/// Struct representing the result of encrypting with the "aesgcm" scheme.
///
/// Since the "aesgcm" scheme needs to represent some data in HTTP headers and
/// other data in the encoded body, we need to represent it with a structure
/// rather than just with raw bytes.
///
pub struct AesGcmEncryptedBlock {
    pub(crate) dh: Vec<u8>,
    pub(crate) salt: Vec<u8>,
    pub(crate) rs: u32,
    pub(crate) ciphertext: Vec<u8>,
}

impl AesGcmEncryptedBlock {
    fn aesgcm_rs(rs: u32) -> u32 {
        if rs > u32::max_value() - ECE_TAG_LENGTH as u32 {
            return 0;
        }
        rs + ECE_TAG_LENGTH as u32
    }

    pub fn new(
        dh: &[u8],
        salt: &[u8],
        rs: u32,
        ciphertext: Vec<u8>,
    ) -> Result<AesGcmEncryptedBlock> {
        Ok(AesGcmEncryptedBlock {
            dh: dh.to_owned(),
            salt: salt.to_owned(),
            rs: Self::aesgcm_rs(rs),
            ciphertext,
        })
    }

    /// Return the headers Hash.
    /// If you're using VAPID, provide the `p256ecdsa` public key that signed the Json Web Token
    /// so it can be included in the `Crypto-Key` field.
    ///
    /// Disclaimer : You will need to manually add the Authorization field for VAPID containing the JSON Web Token
    pub fn headers(&self, vapid_public_key: Option<&[u8]>) -> Vec<(&yle='color:blue'>'static str, String)> {
        let mut result = Vec::new();
        let mut rs = "".to_owned();
        let dh = base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&self.dh);
        let crypto_key = match vapid_public_key {
            Some(public_key) => format!(
                "dh={}; p256ecdsa={}",
                dh,
                base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(public_key)
            ),
            None => format!("dh={}", dh),
        };
        result.push(("Crypto-Key", crypto_key));
        if self.rs > 0 {
            rs = format!(";rs={}"self.rs);
        }
        result.push((
            "Encryption",
            format!(
                "salt={}{}",
                base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&self.salt),
                rs
            ),
        ));
        result
    }

    /// Encode the body as a String.
    pub fn body(&self) -> String {
        base64::engine::general_purpose::URL_SAFE_NO_PAD.encode(&self.ciphertext)
    }
}

/// Encrypts a Web Push message using the "aesgcm" scheme, with an explicit sender key.
///
/// It is the caller's responsibility to ensure that this function is used correctly,
/// where "correctly" means important cryptographic details like:
///
///    * use a new ephemeral local keypair for each encryption
///    * use a randomly-generated salt
///
pub(cratefn encrypt(
    local_prv_key: &dyn LocalKeyPair,
    remote_pub_key: &dyn RemotePublicKey,
    auth_secret: &[u8],
    plaintext: &[u8],
    mut params: WebPushParams,
) -> Result<AesGcmEncryptedBlock> {
    // Check parameters, including doing the random salt thing.
    // Probably could move into the WebPushParams struct?
    let cryptographer = crypto::holder::get_cryptographer();

    if plaintext.is_empty() {
        return Err(Error::ZeroPlaintext);
    }

    let salt = params.take_or_generate_salt(cryptographer)?;
    let (key, nonce) = derive_key_and_nonce(
        cryptographer,
        EceMode::Encrypt,
        local_prv_key,
        remote_pub_key,
        auth_secret,
        &salt,
    )?;

    // Each record must contain at least some padding, for recording the padding size.
    let pad_length = std::cmp::max(params.pad_length, ECE_AESGCM_PAD_SIZE);

    // For this legacy scheme, we only support encrypting a single record.
    // The record size in this scheme is the size of the plaintext plus padding,
    // and the scheme requires that the final block be of a size less than `rs`.
    if plaintext.len() + pad_length >= params.rs as usize {
        return Err(Error::PlaintextTooLong);
    }

    // Pad out the plaintext.
    // The first two bytes of padding are big-endian padding length,
    // followed by the rest of the padding as zero bytes,
    // followed by the plaintext.
    let mut padded_plaintext = vec![0; pad_length + plaintext.len()];
    BigEndian::write_u16(
        &mut padded_plaintext,
        (pad_length - ECE_AESGCM_PAD_SIZE) as u16,
    );
    padded_plaintext[pad_length..].copy_from_slice(plaintext);

    // Now we can encrypt it.
    let iv = generate_iv_for_record(&nonce, 0);
    let cryptographer = crypto::holder::get_cryptographer();
    let ciphertext = cryptographer.aes_gcm_128_encrypt(&key, &iv, &padded_plaintext)?;

    // Encapsulate the crypto parameters in headers to return to caller.
    let raw_local_pub_key = local_prv_key.pub_as_raw()?;
    Ok(AesGcmEncryptedBlock {
        salt,
        dh: raw_local_pub_key,
        rs: params.rs,
        ciphertext,
    })
}

/// Decrypts a Web Push message encrypted using the "aesgcm" scheme.
///
pub(cratefn decrypt(
    local_prv_key: &dyn LocalKeyPair,
    auth_secret: &[u8],
    block: &AesGcmEncryptedBlock,
) -> Result<Vec<u8>> {
    let cryptographer = crypto::holder::get_cryptographer();

    let sender_key = cryptographer.import_public_key(&block.dh)?;

    let (key, nonce) = derive_key_and_nonce(
        cryptographer,
        EceMode::Decrypt,
        local_prv_key,
        &*sender_key,
        auth_secret,
        block.salt.as_ref(),
    )?;

    // We only support receipt of a single record for this legacy scheme.
    // Recall that the final block must be strictly less than `rs` in size.
    if block.ciphertext.len() - ECE_TAG_LENGTH >= block.rs as usize {
        return Err(Error::MultipleRecordsNotSupported);
    }
    if block.ciphertext.len() <= ECE_TAG_LENGTH + ECE_AESGCM_PAD_SIZE {
        return Err(Error::BlockTooShort);
    }

    let iv = generate_iv_for_record(&nonce, 0);
    let padded_plaintext = cryptographer.aes_gcm_128_decrypt(&key, &iv, &block.ciphertext)?;

    // The first two bytes are a big-endian u16 padding size,
    // then that many zero bytes,
    // then the plaintext.
    let num_padding_bytes =
        (((padded_plaintext[0as u16) << 8) | padded_plaintext[1as u16) as usize;
    if num_padding_bytes + 2 >= padded_plaintext.len() {
        return Err(Error::DecryptPadding);
    }
    if padded_plaintext[2..(2 + num_padding_bytes)]
        .iter()
        .any(|b| *b != 0u8)
    {
        return Err(Error::DecryptPadding);
    }

    Ok(padded_plaintext[(2 + num_padding_bytes)..].to_owned())
}

/// Derives the "aesgcm" decryption key and nonce given the receiver private
/// key, sender public key, authentication secret, and sender salt.
fn derive_key_and_nonce(
    cryptographer: &dyn Cryptographer,
    ece_mode: EceMode,
    local_prv_key: &dyn LocalKeyPair,
    remote_pub_key: &dyn RemotePublicKey,
    auth_secret: &[u8],
    salt: &[u8],
) -> Result<KeyAndNonce> {
    if auth_secret.len() != ECE_WEBPUSH_AUTH_SECRET_LENGTH {
        return Err(Error::InvalidAuthSecret);
    }
    if salt.len() != ECE_SALT_LENGTH {
        return Err(Error::InvalidSalt);
    }

    let shared_secret = cryptographer.compute_ecdh_secret(remote_pub_key, local_prv_key)?;
    let raw_remote_pub_key = remote_pub_key.as_raw()?;
    let raw_local_pub_key = local_prv_key.pub_as_raw()?;

    let keypair = match ece_mode {
        EceMode::Encrypt => encode_keys(&raw_remote_pub_key, &raw_local_pub_key),
        EceMode::Decrypt => encode_keys(&raw_local_pub_key, &raw_remote_pub_key),
    }?;
    let keyinfo = generate_info("aesgcm", &keypair)?;
    let nonceinfo = generate_info("nonce", &keypair)?;
    let ikm = cryptographer.hkdf_sha256(
        auth_secret,
        &shared_secret,
        ECE_WEBPUSH_AESGCM_AUTHINFO.as_bytes(),
        ECE_WEBPUSH_IKM_LENGTH,
    )?;
    let key = cryptographer.hkdf_sha256(salt, &ikm, &keyinfo, ECE_AES_KEY_LENGTH)?;
    let nonce = cryptographer.hkdf_sha256(salt, &ikm, &nonceinfo, ECE_NONCE_LENGTH)?;
    Ok((key, nonce))
}

// Encode the input keys for inclusion in key-derivation info string.
fn encode_keys(raw_key1: &[u8], raw_key2: &[u8]) -> Result<Vec<u8>> {
    let mut combined = vec![0u8; ECE_WEBPUSH_AESGCM_KEYPAIR_LENGTH];

    if raw_key1.len() > ECE_WEBPUSH_RAW_KEY_LENGTH || raw_key2.len() > ECE_WEBPUSH_RAW_KEY_LENGTH {
        return Err(Error::InvalidKeyLength);
    }
    // length prefix each key
    combined[0] = 0;
    combined[1] = 65;
    combined[2..67].copy_from_slice(raw_key1);
    combined[67] = 0;
    combined[68] = 65;
    combined[69..].copy_from_slice(raw_key2);
    Ok(combined)
}

// The "aesgcm" IKM info string is "WebPush: info", followed by the
// receiver and sender public keys prefixed by their lengths.
fn generate_info(encoding: &str, keypair: &[u8]) -> Result<Vec<u8>> {
    let info_str = format!("Content-Encoding: {}\0P-256\0", encoding);
    let offset = info_str.len();
    let mut info = vec![0u8; offset + keypair.len()];
    info[0..offset].copy_from_slice(info_str.as_bytes());
    info[offset..offset + ECE_WEBPUSH_AESGCM_KEYPAIR_LENGTH].copy_from_slice(keypair);
    Ok(info)
}

Messung V0.5 in Prozent
C=69 H=95 G=82

¤ Dauer der Verarbeitung: 0.6 Sekunden  ¤

*© Formatika GbR, Deutschland






Wurzel

Suchen

PVS Prover

Isabelle Prover

NIST Cobol Testsuite

Cephes Mathematical Library

Vienna Development Method

Haftungshinweis

Die Informationen auf dieser Webseite wurden nach bestem Wissen sorgfältig zusammengestellt. Es wird jedoch weder Vollständigkeit, noch Richtigkeit, noch Qualität der bereit gestellten Informationen zugesichert.

Bemerkung:

Die farbliche Syntaxdarstellung und die Messung sind noch experimentell.






                                                                                                                                                                                                                                                                                                                                                                                                     


Neuigkeiten

     Aktuelles
     Motto des Tages

Open Source Software

     Quellcodebibliothek
     Eigene Quellcodes
     Fremde Quellcodes
     Suchen

Jenseits des Üblichen ....
    

Besucherstatistik

Besucherstatistik

Statistik
#Sources=141584
#Domains=752002