Path: blob/master/libs/symcrypt/lib/chacha20_poly1305.c
15010 views
//1// ChaCha20_Poly1305.c2//3// Copyright (c) Microsoft Corporation.4//56#include "precomp.h"78#define CHACHA20_POLY1305_MAX_DATA_SIZE (((1ull << 32) - 1) * 64)910// Compile time BOOL statically determines if we need to check cbData > CHACHA20_POLY1305_MAX_DATA_SIZE11// Used to suppress MSVC C4127 and clang Wtautological-constant-out-of-range-compare on 32b platforms12const BOOL fcbDataLteMaxDataSizeStatic = SIZE_T_MAX <= CHACHA20_POLY1305_MAX_DATA_SIZE;1314VOID15SYMCRYPT_CALL16SymCryptChaCha20Poly1305ComputeTag(17_Inout_ PSYMCRYPT_POLY1305_STATE pState,18_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,19SIZE_T cbAuthData,20_In_reads_( cbData ) PCBYTE pbData,21SIZE_T cbData,22_Out_writes_( SYMCRYPT_POLY1305_RESULT_SIZE ) PBYTE pbTag )23{24SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_POLY1305_BLOCK_SIZE];25BYTE partialBlockSize;2627SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );2829// Add additional authentication data if needed.30if ( cbAuthData > 0 )31{32SymCryptPoly1305Append( pState, pbAuthData, cbAuthData );3334// Append zeros to make a complete Poly1305 block.35partialBlockSize = cbAuthData % SYMCRYPT_POLY1305_BLOCK_SIZE;36if ( partialBlockSize > 0 )37{38SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );39}40}4142// Add ciphertext if needed.43if ( cbData > 0 )44{45SymCryptPoly1305Append( pState, pbData, cbData );4647// Append zeros to make a complete Poly1305 block.48partialBlockSize = cbData % SYMCRYPT_POLY1305_BLOCK_SIZE;49if ( partialBlockSize > 0 )50{51SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE - partialBlockSize );52}53}5455// Add length of additional authentication data and ciphertext.56SYMCRYPT_STORE_LSBFIRST64( &buf[0], cbAuthData );57SYMCRYPT_STORE_LSBFIRST64( &buf[8], cbData );58SymCryptPoly1305Append( pState, buf, SYMCRYPT_POLY1305_BLOCK_SIZE );59SymCryptWipeKnownSize( buf, SYMCRYPT_POLY1305_BLOCK_SIZE );6061SymCryptPoly1305Result( pState, pbTag );62}6364SYMCRYPT_NOINLINE65SYMCRYPT_ERROR66SYMCRYPT_CALL67SymCryptChaCha20Poly1305Encrypt(68_In_reads_( cbKey ) PCBYTE pbKey,69SIZE_T cbKey,70_In_reads_( cbNonce ) PCBYTE pbNonce,71SIZE_T cbNonce,72_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,73SIZE_T cbAuthData,74_In_reads_( cbData ) PCBYTE pbSrc,75_Out_writes_( cbData ) PBYTE pbDst,76SIZE_T cbData,77_Out_writes_( cbTag ) PBYTE pbTag,78SIZE_T cbTag )79{80SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;81SYMCRYPT_CHACHA20_STATE ChaCha20State;82SYMCRYPT_POLY1305_STATE Poly1305State;83SYMCRYPT_ALIGN BYTE key[SYMCRYPT_POLY1305_KEY_SIZE];8485if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )86{87status = SYMCRYPT_WRONG_DATA_SIZE;88goto cleanup;89}9091if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )92{93status = SYMCRYPT_WRONG_TAG_SIZE;94goto cleanup;95}9697status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );98if ( status != SYMCRYPT_NO_ERROR )99{100goto cleanup;101}102103// Generate the first 32 bytes of keystream.104SymCryptWipeKnownSize( key, sizeof( key ) );105SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof ( key ) );106107// Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.108SymCryptPoly1305Init( &Poly1305State, key );109SymCryptWipeKnownSize( key, sizeof( key ) );110111// Encrypt data if needed.112if ( cbData > 0 )113{114// Advance the keystream to counter 1 (offset 64) for data encryption.115SymCryptChaCha20SetOffset( &ChaCha20State, 64 );116SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );117}118119// We read the ciphertext back, violating the general rule not to rely on I/O buffers120// as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,121// this read-back of data is not a problem. An attacker with access to the buffer122// will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.123// As Poly1305 is strong even with attacker-controlled data, this is harmless.124SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,125pbDst, cbData, pbTag );126cleanup:127128SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );129SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );130131return status;132}133134SYMCRYPT_NOINLINE135SYMCRYPT_ERROR136SYMCRYPT_CALL137SymCryptChaCha20Poly1305Decrypt(138_In_reads_( cbKey ) PCBYTE pbKey,139SIZE_T cbKey,140_In_reads_( cbNonce ) PCBYTE pbNonce,141SIZE_T cbNonce,142_In_reads_opt_( cbAuthData ) PCBYTE pbAuthData,143SIZE_T cbAuthData,144_In_reads_( cbData ) PCBYTE pbSrc,145_Out_writes_( cbData ) PBYTE pbDst,146SIZE_T cbData,147_In_reads_( cbTag ) PCBYTE pbTag,148SIZE_T cbTag )149{150SYMCRYPT_ERROR status = SYMCRYPT_NO_ERROR;151SYMCRYPT_CHACHA20_STATE ChaCha20State;152SYMCRYPT_POLY1305_STATE Poly1305State;153SYMCRYPT_ALIGN BYTE buf[SYMCRYPT_POLY1305_RESULT_SIZE];154SYMCRYPT_ALIGN BYTE key[SYMCRYPT_POLY1305_KEY_SIZE];155156if ( !fcbDataLteMaxDataSizeStatic && cbData > CHACHA20_POLY1305_MAX_DATA_SIZE )157{158status = SYMCRYPT_WRONG_DATA_SIZE;159goto cleanup;160}161162if ( cbTag != SYMCRYPT_POLY1305_RESULT_SIZE )163{164status = SYMCRYPT_WRONG_TAG_SIZE;165goto cleanup;166}167168status = SymCryptChaCha20Init( &ChaCha20State, pbKey, cbKey, pbNonce, cbNonce, 0 );169if ( status != SYMCRYPT_NO_ERROR )170{171goto cleanup;172}173174// Generate the first 32 bytes of keystream.175SymCryptWipeKnownSize( key, sizeof( key ) );176SymCryptChaCha20Crypt( &ChaCha20State, key, key, sizeof( key ) );177178// Create the Poly1305 key using the first 32 bytes of the ChaCha20 keystream.179SymCryptPoly1305Init( &Poly1305State, key );180SymCryptWipeKnownSize( key, sizeof( key ) );181182// We read the ciphertext back, violating the general rule not to rely on I/O buffers183// as they can reside in a different security domain. For ChaCha20Poly1305, like GCM,184// this read-back of data is not a problem. An attacker with access to the buffer185// will get the ChaCha20 key stream plus the Poly1305 authenticator of a single value.186// As Poly1305 is strong even with attacker-controlled data, this is harmless.187SymCryptChaCha20Poly1305ComputeTag( &Poly1305State, pbAuthData, cbAuthData,188pbSrc, cbData, buf );189190// Validate tag.191if (!SymCryptEqual(pbTag, buf, cbTag))192{193status = SYMCRYPT_AUTHENTICATION_FAILURE;194goto cleanup;195}196197// Decrypt data if needed.198if ( cbData > 0)199{200// Advance the keystream to counter 1 (offset 64) for data decryption.201SymCryptChaCha20SetOffset( &ChaCha20State, 64 );202SymCryptChaCha20Crypt( &ChaCha20State, pbSrc, pbDst, cbData );203}204205cleanup:206207SymCryptWipeKnownSize( &ChaCha20State, sizeof( ChaCha20State ) );208SymCryptWipeKnownSize( &Poly1305State, sizeof( Poly1305State ) );209210return status;211}212213214static const BYTE SymCryptChaCha20Poly1305Result[3 + SYMCRYPT_POLY1305_RESULT_SIZE] =215{2160x5d, 0xba, 0x7b,2170x80, 0x10, 0xd2, 0x05, 0x4a, 0xad, 0x53, 0x1f, 0xa2, 0xce, 0x83, 0xc1, 0x66, 0x12, 0x85, 0x21218};219220VOID221SYMCRYPT_CALL222SymCryptChaCha20Poly1305Selftest(void)223{224BYTE buf[3 + SYMCRYPT_POLY1305_RESULT_SIZE];225SYMCRYPT_ERROR err;226227if ( SymCryptChaCha20Poly1305Encrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),228SymCryptTestMsg16, 12,229SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),230&SymCryptTestMsg3[0], buf, 3,231&buf[3], SYMCRYPT_POLY1305_RESULT_SIZE ) != SYMCRYPT_NO_ERROR )232{233SymCryptFatal( 'ccp0' );234}235236SymCryptInjectError( buf, sizeof( buf ) );237if ( memcmp( buf, SymCryptChaCha20Poly1305Result, sizeof( buf ) ) != 0 )238{239SymCryptFatal( 'ccp1' );240}241242// Inject error into the ciphertext or tag.243SymCryptInjectError( buf, sizeof( buf ) );244245err = SymCryptChaCha20Poly1305Decrypt( SymCryptTestKey32, sizeof( SymCryptTestKey32 ),246SymCryptTestMsg16, 12,247SymCryptTestMsg16, sizeof( SymCryptTestMsg16 ),248buf, buf, 3,249&buf[3], SYMCRYPT_POLY1305_RESULT_SIZE );250SymCryptInjectError( buf, 3 );251252if ( err != SYMCRYPT_NO_ERROR || memcmp( buf, SymCryptTestMsg3, 3 ) != 0 )253{254SymCryptFatal( 'ccp2' );255}256}257258259