Skip to content

Commit

Permalink
Add Parsec authentication plugin implementation.
Browse files Browse the repository at this point in the history
Create a new ServerFeature for parsec.

Drop netstandard2.0 support. PBKDF2-SHA512 support was only added in .NET Framework 4.7.2 and .NET Standard 2.1.

Signed-off-by: Bradley Grainger <[email protected]>
  • Loading branch information
bgrainger committed Jan 27, 2025
1 parent 11937af commit 208f2ec
Show file tree
Hide file tree
Showing 23 changed files with 547 additions and 109 deletions.
2 changes: 1 addition & 1 deletion .ci/config/config.compression.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
}
Expand Down
2 changes: 1 addition & 1 deletion .ci/config/config.json
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@
"SocketPath": "./../../../../.ci/run/mysql/mysqld.sock",
"PasswordlessUser": "no_password",
"SecondaryDatabase": "testdb2",
"UnsupportedFeatures": "Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"UnsupportedFeatures": "Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket,ZeroDateTime",
"MySqlBulkLoaderLocalCsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.CSV",
"MySqlBulkLoaderLocalTsvFile": "../../../../tests/TestData/LoadData_UTF8_BOM_Unix.TSV"
}
Expand Down
6 changes: 6 additions & 0 deletions .ci/docker-run.sh
Original file line number Diff line number Diff line change
Expand Up @@ -79,6 +79,12 @@ for i in `seq 1 120`; do
if [ $? -ne 0 ]; then exit $?; fi
fi

if [[ $OMIT_FEATURES != *"ParsecAuthentication"* ]]; then
echo "Installing auth_parsec component"
docker exec mysql bash -c "$MYSQL -uroot -ptest < /etc/mysql/conf.d/init_parsec.sql"
if [ $? -ne 0 ]; then exit $?; fi
fi

if [[ $OMIT_FEATURES != *"QueryAttributes"* ]]; then
echo "Installing query_attributes component"
docker exec mysql $MYSQL -uroot -ptest -e "INSTALL COMPONENT 'file://component_query_attributes';"
Expand Down
3 changes: 3 additions & 0 deletions .ci/server/init_parsec.sql
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
INSTALL SONAME 'auth_parsec';
CREATE USER 'parsec-user'@'%' IDENTIFIED via parsec using PASSWORD('P@rs3c-Pa55');
GRANT ALL PRIVILEGES ON *.* TO 'parsec-user'@'%';
18 changes: 9 additions & 9 deletions azure-pipelines.yml
Original file line number Diff line number Diff line change
Expand Up @@ -51,7 +51,7 @@ jobs:
arguments: 'tests\IntegrationTests\IntegrationTests.csproj -c MySqlData'
testRunTitle: 'MySql.Data integration tests'
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,StreamingResults,TlsFingerprintValidation,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=root;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600'
DATA__CERTIFICATESPATH: '$(Build.Repository.LocalPath)\.ci\server\certs\'
DATA__MYSQLBULKLOADERLOCALCSVFILE: '$(Build.Repository.LocalPath)\tests\TestData\LoadData_UTF8_BOM_Unix.CSV'
Expand Down Expand Up @@ -120,7 +120,7 @@ jobs:
arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net481/net9.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True;UseCompression=True'

- job: windows_integration_tests_2
Expand Down Expand Up @@ -158,7 +158,7 @@ jobs:
arguments: '-c Release --no-restore -p:TestTfmsInParallel=false'
testRunTitle: ${{ format('{0}, $(Agent.OS), {1}, {2}', 'mysql:8.0', 'net8.0', 'No SSL') }}
env:
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__UNSUPPORTEDFEATURES: 'Ed25519,QueryAttributes,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,UnixDomainSocket'
DATA__CONNECTIONSTRING: 'server=localhost;port=3306;user id=mysqltest;password=test;database=mysqltest;ssl mode=none;DefaultCommandTimeout=3600;AllowPublicKeyRetrieval=True'

- job: linux_integration_tests
Expand All @@ -171,27 +171,27 @@ jobs:
'MySQL 8.0':
image: 'mysql:8.0'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MySQL 8.4':
image: 'mysql:8.4'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MySQL 9.2':
image: 'mysql:9.2'
connectionStringExtra: 'AllowPublicKeyRetrieval=True'
unsupportedFeatures: 'Ed25519,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
unsupportedFeatures: 'Ed25519,ParsecAuthentication,Redirection,StreamingResults,Tls11,TlsFingerprintValidation,ZeroDateTime'
'MariaDB 10.6':
image: 'mariadb:10.6'
connectionStringExtra: ''
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
'MariaDB 10.11':
image: 'mariadb:10.11'
connectionStringExtra: ''
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Redirection,Sha256Password,Tls11,TlsFingerprintValidation,UuidToBin'
'MariaDB 11.4':
image: 'mariadb:11.4'
connectionStringExtra: ''
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,Sha256Password,Tls11,UuidToBin,Redirection'
unsupportedFeatures: 'CachingSha2Password,CancelSleepSuccessfully,Json,RoundDateTime,QueryAttributes,ParsecAuthentication,Sha256Password,Tls11,UuidToBin,Redirection'
'MariaDB 11.6':
image: 'mariadb:11.6'
connectionStringExtra: ''
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
using System;
using System.Runtime.CompilerServices;

namespace Chaos.NaCl
{
internal static class CryptoBytes
{
public static void Wipe(byte[] data)
{
if (data == null)
throw new ArgumentNullException("data");
InternalWipe(data, 0, data.Length);
}

// Secure wiping is hard
// * the GC can move around and copy memory
// Perhaps this can be avoided by using unmanaged memory or by fixing the position of the array in memory
// * Swap files and error dumps can contain secret information
// It seems possible to lock memory in RAM, no idea about error dumps
// * Compiler could optimize out the wiping if it knows that data won't be read back
// I hope this is enough, suppressing inlining
// but perhaps `RtlSecureZeroMemory` is needed
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void InternalWipe(byte[] data, int offset, int count)
{
Array.Clear(data, offset, count);
}

// shallow wipe of structs
[MethodImpl(MethodImplOptions.NoInlining)]
internal static void InternalWipe<T>(ref T data)
where T : struct
{
data = default(T);
}
}
}
63 changes: 63 additions & 0 deletions src/MySqlConnector.Authentication.Ed25519/Chaos.NaCl/Ed25519.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
using System;
using System.Security.Cryptography;
using Chaos.NaCl.Internal.Ed25519Ref10;

namespace Chaos.NaCl
{
internal static class Ed25519
{
public static readonly int PublicKeySizeInBytes = 32;
public static readonly int SignatureSizeInBytes = 64;
public static readonly int ExpandedPrivateKeySizeInBytes = 32 * 2;
public static readonly int PrivateKeySeedSizeInBytes = 32;
public static readonly int SharedKeySizeInBytes = 32;

public static void Sign(ArraySegment<byte> signature, ArraySegment<byte> message, ArraySegment<byte> expandedPrivateKey)
{
if (signature.Array == null)
throw new ArgumentNullException("signature.Array");
if (signature.Count != SignatureSizeInBytes)
throw new ArgumentException("signature.Count");
if (expandedPrivateKey.Array == null)
throw new ArgumentNullException("expandedPrivateKey.Array");
if (expandedPrivateKey.Count != ExpandedPrivateKeySizeInBytes)
throw new ArgumentException("expandedPrivateKey.Count");
if (message.Array == null)
throw new ArgumentNullException("message.Array");
Ed25519Operations.crypto_sign2(signature.Array, signature.Offset, message.Array, message.Offset, message.Count, expandedPrivateKey.Array, expandedPrivateKey.Offset);
}

public static byte[] Sign(byte[] message, byte[] expandedPrivateKey)
{
var signature = new byte[SignatureSizeInBytes];
Sign(new ArraySegment<byte>(signature), new ArraySegment<byte>(message), new ArraySegment<byte>(expandedPrivateKey));
return signature;
}

public static byte[] ExpandedPrivateKeyFromSeed(byte[] privateKeySeed)
{
byte[] privateKey;
byte[] publicKey;
KeyPairFromSeed(out publicKey, out privateKey, privateKeySeed);
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
CryptographicOperations.ZeroMemory(publicKey);
#else
CryptoBytes.Wipe(publicKey);
#endif
return privateKey;
}

public static void KeyPairFromSeed(out byte[] publicKey, out byte[] expandedPrivateKey, byte[] privateKeySeed)
{
if (privateKeySeed == null)
throw new ArgumentNullException("privateKeySeed");
if (privateKeySeed.Length != PrivateKeySeedSizeInBytes)
throw new ArgumentException("privateKeySeed");
var pk = new byte[PublicKeySizeInBytes];
var sk = new byte[ExpandedPrivateKeySizeInBytes];
Ed25519Operations.crypto_sign_keypair(pk, 0, sk, 0, privateKeySeed, 0);
publicKey = pk;
expandedPrivateKey = sk;
}
}
}
Original file line number Diff line number Diff line change
@@ -1,52 +1,63 @@
namespace Chaos.NaCl.Internal.Ed25519Ref10;
using System;

/*
ge means group element.
Here the group is the set of pairs (x,y) of field elements (see fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2
where d = -121665/121666.
namespace Chaos.NaCl.Internal.Ed25519Ref10
{
/*
ge means group element.
Representations:
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
ge_precomp (Duif): (y+x,y-x,2dxy)
*/
Here the group is the set of pairs (x,y) of field elements (see fe.h)
satisfying -x^2 + y^2 = 1 + d x^2y^2
where d = -121665/121666.
internal struct GroupElementP2
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
} ;
Representations:
ge_p2 (projective): (X:Y:Z) satisfying x=X/Z, y=Y/Z
ge_p3 (extended): (X:Y:Z:T) satisfying x=X/Z, y=Y/Z, XY=ZT
ge_p1p1 (completed): ((X:Z),(Y:T)) satisfying x=X/Z, y=Y/T
ge_precomp (Duif): (y+x,y-x,2dxy)
*/

internal struct GroupElementP3
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
public FieldElement T;
} ;
internal struct GroupElementP2
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
} ;

internal struct GroupElementP1P1
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
public FieldElement T;
} ;
internal struct GroupElementP3
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
public FieldElement T;
} ;

internal struct GroupElementPreComp
{
public FieldElement yplusx;
public FieldElement yminusx;
public FieldElement xy2d;
internal struct GroupElementP1P1
{
public FieldElement X;
public FieldElement Y;
public FieldElement Z;
public FieldElement T;
} ;

public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d)
internal struct GroupElementPreComp
{
public FieldElement yplusx;
public FieldElement yminusx;
public FieldElement xy2d;

public GroupElementPreComp(FieldElement yplusx, FieldElement yminusx, FieldElement xy2d)
{
this.yplusx = yplusx;
this.yminusx = yminusx;
this.xy2d = xy2d;
}
} ;

internal struct GroupElementCached
{
this.yplusx = yplusx;
this.yminusx = yminusx;
this.xy2d = xy2d;
}
} ;
public FieldElement YplusX;
public FieldElement YminusX;
public FieldElement Z;
public FieldElement T2d;
} ;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
using System;

namespace Chaos.NaCl.Internal.Ed25519Ref10
{
internal static partial class LookupTables
{
internal static readonly GroupElementPreComp[] Base2 = new GroupElementPreComp[]{
new GroupElementPreComp(
new FieldElement( 25967493,-14356035,29566456,3660896,-12694345,4014787,27544626,-11754271,-6079156,2047605 ),
new FieldElement( -12545711,934262,-2722910,3049990,-727428,9406986,12720692,5043384,19500929,-15469378 ),
new FieldElement( -8738181,4489570,9688441,-14785194,10184609,-12363380,29287919,11864899,-24514362,-4438546 )
),
new GroupElementPreComp(
new FieldElement( 15636291,-9688557,24204773,-7912398,616977,-16685262,27787600,-14772189,28944400,-1550024 ),
new FieldElement( 16568933,4717097,-11556148,-1102322,15682896,-11807043,16354577,-11775962,7689662,11199574 ),
new FieldElement( 30464156,-5976125,-11779434,-15670865,23220365,15915852,7512774,10017326,-17749093,-9920357 )
),
new GroupElementPreComp(
new FieldElement( 10861363,11473154,27284546,1981175,-30064349,12577861,32867885,14515107,-15438304,10819380 ),
new FieldElement( 4708026,6336745,20377586,9066809,-11272109,6594696,-25653668,12483688,-12668491,5581306 ),
new FieldElement( 19563160,16186464,-29386857,4097519,10237984,-4348115,28542350,13850243,-23678021,-15815942 )
),
new GroupElementPreComp(
new FieldElement( 5153746,9909285,1723747,-2777874,30523605,5516873,19480852,5230134,-23952439,-15175766 ),
new FieldElement( -30269007,-3463509,7665486,10083793,28475525,1649722,20654025,16520125,30598449,7715701 ),
new FieldElement( 28881845,14381568,9657904,3680757,-20181635,7843316,-31400660,1370708,29794553,-1409300 )
),
new GroupElementPreComp(
new FieldElement( -22518993,-6692182,14201702,-8745502,-23510406,8844726,18474211,-1361450,-13062696,13821877 ),
new FieldElement( -6455177,-7839871,3374702,-4740862,-27098617,-10571707,31655028,-7212327,18853322,-14220951 ),
new FieldElement( 4566830,-12963868,-28974889,-12240689,-7602672,-2830569,-8514358,-10431137,2207753,-3209784 )
),
new GroupElementPreComp(
new FieldElement( -25154831,-4185821,29681144,7868801,-6854661,-9423865,-12437364,-663000,-31111463,-16132436 ),
new FieldElement( 25576264,-2703214,7349804,-11814844,16472782,9300885,3844789,15725684,171356,6466918 ),
new FieldElement( 23103977,13316479,9739013,-16149481,817875,-15038942,8965339,-14088058,-30714912,16193877 )
),
new GroupElementPreComp(
new FieldElement( -33521811,3180713,-2394130,14003687,-16903474,-16270840,17238398,4729455,-18074513,9256800 ),
new FieldElement( -25182317,-4174131,32336398,5036987,-21236817,11360617,22616405,9761698,-19827198,630305 ),
new FieldElement( -13720693,2639453,-24237460,-7406481,9494427,-5774029,-6554551,-15960994,-2449256,-14291300 )
),
new GroupElementPreComp(
new FieldElement( -3151181,-5046075,9282714,6866145,-31907062,-863023,-18940575,15033784,25105118,-7894876 ),
new FieldElement( -24326370,15950226,-31801215,-14592823,-11662737,-5090925,1573892,-2625887,2198790,-15804619 ),
new FieldElement( -3099351,10324967,-2241613,7453183,-5446979,-2735503,-13812022,-16236442,-32461234,-12290683 )
)
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
using System;
using System.Security.Cryptography;

namespace Chaos.NaCl.Internal.Ed25519Ref10
{
internal static partial class Ed25519Operations
{
public static void crypto_sign_keypair(byte[] pk, int pkoffset, byte[] sk, int skoffset, byte[] seed, int seedoffset)
{
GroupElementP3 A;
int i;

Array.Copy(seed, seedoffset, sk, skoffset, 32);
#if NET5_0_OR_GREATER
byte[] h = SHA512.HashData(sk.AsSpan(skoffset, 32));
#else
using var hash = SHA512.Create();
byte[] h = hash.ComputeHash(sk, skoffset, 32);
#endif
ScalarOperations.sc_clamp(h, 0);

GroupOperations.ge_scalarmult_base(out A, h, 0);
GroupOperations.ge_p3_tobytes(pk, pkoffset, ref A);

for (i = 0; i < 32; ++i) sk[skoffset + 32 + i] = pk[pkoffset + i];
#if NETSTANDARD2_1_OR_GREATER || NETCOREAPP2_1_OR_GREATER
CryptographicOperations.ZeroMemory(h);
#else
CryptoBytes.Wipe(h);
#endif
}
}
}
Loading

0 comments on commit 208f2ec

Please sign in to comment.