Skip to content

Commit 832246f

Browse files
authored
Merge pull request #3 from WeihanLi/sha256-auth
SHA256 auth support
2 parents 05a30aa + a5f11a2 commit 832246f

File tree

11 files changed

+134
-660
lines changed

11 files changed

+134
-660
lines changed

.github/workflows/build.yml

Lines changed: 2 additions & 358 deletions
Large diffs are not rendered by default.

.gitignore

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -19,3 +19,4 @@ artifacts/
1919
*.ide/
2020
.vs/
2121
TestResult.xml
22+
.env

Directory.Packages.props

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,5 +43,8 @@
4343

4444
<!-- NativeAOT -->
4545
<PackageVersion Include="Mono.Cecil" Version="0.11.5" />
46+
47+
<!-- Examples -->
48+
<PackageVersion Include="dotenv.net" Version="3.2.1" />
4649
</ItemGroup>
4750
</Project>

Npgsql.slnx

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,9 @@
77
<File Path="NuGet.config" />
88
<File Path="README.md" />
99
</Folder>
10+
<Folder Name="/example/">
11+
<Project Path="example/GetStarted/GetStarted.csproj" />
12+
</Folder>
1013
<Folder Name="/Github/">
1114
<File Path=".github/dependabot.yml" />
1215
<File Path=".github/workflows/build.yml" />

example/GetStarted/.env-example

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
GaussDBConnString=
Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,25 @@
1+
<Project Sdk="Microsoft.NET.Sdk">
2+
3+
<PropertyGroup>
4+
<OutputType>Exe</OutputType>
5+
<TargetFramework>net8.0</TargetFramework>
6+
<ImplicitUsings>enable</ImplicitUsings>
7+
<Nullable>enable</Nullable>
8+
<NoWarn>$(NoWarn);CS8002</NoWarn>
9+
</PropertyGroup>
10+
11+
<ItemGroup>
12+
<ProjectReference Include="..\..\src\Npgsql\Npgsql.csproj" />
13+
</ItemGroup>
14+
15+
<ItemGroup>
16+
<PackageReference Include="dotenv.net" />
17+
</ItemGroup>
18+
19+
<ItemGroup>
20+
<None Condition="Exists('$(MSBuildProjectDirectory)\.env')" Update=".env">
21+
<CopyToOutputDirectory>Always</CopyToOutputDirectory>
22+
</None>
23+
</ItemGroup>
24+
25+
</Project>

example/GetStarted/Program.cs

Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
using System.Data;
2+
using Npgsql;
3+
4+
dotenv.net.DotEnv.Load();
5+
6+
var connString = Environment.GetEnvironmentVariable("GaussdbConnString");
7+
ArgumentNullException.ThrowIfNull(connString);
8+
9+
await using var conn = new NpgsqlConnection(connString);
10+
if (conn.State is ConnectionState.Closed)
11+
{
12+
await conn.OpenAsync();
13+
}
14+
15+
Console.WriteLine($@"Connection state: {conn.State}");
16+
17+
{
18+
await using var cmd = new NpgsqlCommand("SELECT 1", conn);
19+
var result = await cmd.ExecuteScalarAsync();
20+
Console.WriteLine(result);
21+
}
22+
23+
Console.WriteLine(@"Completed!");
Lines changed: 41 additions & 126 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,4 @@
11
using System;
2-
using System.Collections.Generic;
3-
using Microsoft.Extensions.Logging;
42
using Npgsql.Internal;
53

64
namespace Npgsql.BackendMessages;
@@ -44,23 +42,48 @@ internal static AuthenticationMD5PasswordMessage Load(NpgsqlReadBuffer buf)
4442
=> Salt = salt;
4543
}
4644

45+
#region SHA256Password
46+
4747
sealed class AuthenticationSHA256PasswordMessage : AuthenticationRequestMessage
4848
{
4949
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SHA256Password;
50+
internal PasswordStoreType PasswordStoreType { get; }
51+
internal string RandomCode { get; }
52+
internal string Token { get; set; }
53+
internal int Iteration { get; }
5054

51-
internal byte[] Salt { get; }
52-
53-
internal static AuthenticationSHA256PasswordMessage Load(NpgsqlReadBuffer buf)
55+
public AuthenticationSHA256PasswordMessage
56+
(
57+
PasswordStoreType passwordStoreType,
58+
NpgsqlReadBuffer buf
59+
)
5460
{
55-
var salt = new byte[32]; // SHA-256盐值通常为32字节
56-
buf.ReadBytes(salt, 0, 32);
57-
return new AuthenticationSHA256PasswordMessage(salt);
61+
PasswordStoreType = passwordStoreType;
62+
RandomCode = buf.ReadString(64);
63+
Token = buf.ReadString(8);
64+
Iteration = buf.ReadInt32();
5865
}
5966

60-
AuthenticationSHA256PasswordMessage(byte[] salt)
61-
=> Salt = salt;
67+
internal static AuthenticationRequestMessage Load(NpgsqlReadBuffer buf)
68+
{
69+
var passwordStoreTypeValue = buf.ReadInt32();
70+
var passwordStoreType = (PasswordStoreType)passwordStoreTypeValue;
71+
72+
return passwordStoreType switch
73+
{
74+
PasswordStoreType.PlainText => AuthenticationCleartextPasswordMessage.Instance,
75+
PasswordStoreType.MD5 => AuthenticationMD5PasswordMessage.Load(buf),
76+
PasswordStoreType.SHA256 => new AuthenticationSHA256PasswordMessage(
77+
passwordStoreType, buf
78+
),
79+
_ => throw new InvalidOperationException($"Not supported password store type({passwordStoreTypeValue})")
80+
};
81+
}
6282
}
6383

84+
#endregion SHA256Password
85+
86+
6487
sealed class AuthenticationGSSMessage : AuthenticationRequestMessage
6588
{
6689
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.GSS;
@@ -95,118 +118,6 @@ sealed class AuthenticationSSPIMessage : AuthenticationRequestMessage
95118
AuthenticationSSPIMessage() { }
96119
}
97120

98-
#region SASL
99-
100-
sealed class AuthenticationSASLMessage : AuthenticationRequestMessage
101-
{
102-
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASL;
103-
internal List<string> Mechanisms { get; } = [];
104-
105-
internal AuthenticationSASLMessage(NpgsqlReadBuffer buf)
106-
{
107-
while (buf.Buffer[buf.ReadPosition] != 0)
108-
Mechanisms.Add(buf.ReadNullTerminatedString());
109-
buf.ReadByte();
110-
if (Mechanisms.Count == 0)
111-
throw new NpgsqlException("Received AuthenticationSASL message with 0 mechanisms!");
112-
}
113-
}
114-
115-
sealed class AuthenticationSASLContinueMessage : AuthenticationRequestMessage
116-
{
117-
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASLContinue;
118-
internal byte[] Payload { get; }
119-
120-
internal AuthenticationSASLContinueMessage(NpgsqlReadBuffer buf, int len)
121-
{
122-
Payload = new byte[len];
123-
buf.ReadBytes(Payload, 0, len);
124-
}
125-
}
126-
127-
sealed class AuthenticationSCRAMServerFirstMessage
128-
{
129-
internal string Nonce { get; }
130-
internal string Salt { get; }
131-
internal int Iteration { get; }
132-
133-
internal static AuthenticationSCRAMServerFirstMessage Load(byte[] bytes, ILogger connectionLogger)
134-
{
135-
var data = NpgsqlWriteBuffer.UTF8Encoding.GetString(bytes);
136-
string? nonce = null, salt = null;
137-
var iteration = -1;
138-
139-
foreach (var part in data.Split(','))
140-
{
141-
if (part.StartsWith("r=", StringComparison.Ordinal))
142-
nonce = part.Substring(2);
143-
else if (part.StartsWith("s=", StringComparison.Ordinal))
144-
salt = part.Substring(2);
145-
else if (part.StartsWith("i=", StringComparison.Ordinal))
146-
iteration = int.Parse(part.Substring(2));
147-
else
148-
connectionLogger.LogDebug("Unknown part in SCRAM server-first message:" + part);
149-
}
150-
151-
if (nonce == null)
152-
throw new NpgsqlException("Server nonce not received in SCRAM server-first message");
153-
if (salt == null)
154-
throw new NpgsqlException("Server salt not received in SCRAM server-first message");
155-
if (iteration == -1)
156-
throw new NpgsqlException("Server iterations not received in SCRAM server-first message");
157-
158-
return new AuthenticationSCRAMServerFirstMessage(nonce, salt, iteration);
159-
}
160-
161-
AuthenticationSCRAMServerFirstMessage(string nonce, string salt, int iteration)
162-
{
163-
Nonce = nonce;
164-
Salt = salt;
165-
Iteration = iteration;
166-
}
167-
}
168-
169-
sealed class AuthenticationSASLFinalMessage : AuthenticationRequestMessage
170-
{
171-
internal override AuthenticationRequestType AuthRequestType => AuthenticationRequestType.SASLFinal;
172-
internal byte[] Payload { get; }
173-
174-
internal AuthenticationSASLFinalMessage(NpgsqlReadBuffer buf, int len)
175-
{
176-
Payload = new byte[len];
177-
buf.ReadBytes(Payload, 0, len);
178-
}
179-
}
180-
181-
sealed class AuthenticationSCRAMServerFinalMessage
182-
{
183-
internal string ServerSignature { get; }
184-
185-
internal static AuthenticationSCRAMServerFinalMessage Load(byte[] bytes, ILogger connectionLogger)
186-
{
187-
var data = NpgsqlWriteBuffer.UTF8Encoding.GetString(bytes);
188-
string? serverSignature = null;
189-
190-
foreach (var part in data.Split(','))
191-
{
192-
if (part.StartsWith("v=", StringComparison.Ordinal))
193-
serverSignature = part.Substring(2);
194-
else
195-
connectionLogger.LogDebug("Unknown part in SCRAM server-first message:" + part);
196-
}
197-
198-
if (serverSignature == null)
199-
throw new NpgsqlException("Server signature not received in SCRAM server-final message");
200-
201-
return new AuthenticationSCRAMServerFinalMessage(serverSignature);
202-
}
203-
204-
internal AuthenticationSCRAMServerFinalMessage(string serverSignature)
205-
=> ServerSignature = serverSignature;
206-
}
207-
208-
#endregion SASL
209-
210121
enum AuthenticationRequestType
211122
{
212123
Ok = 0,
@@ -215,8 +126,12 @@ enum AuthenticationRequestType
215126
GSS = 7,
216127
GSSContinue = 8,
217128
SSPI = 9,
218-
SASL = 10,
219-
SASLContinue = 11,
220-
SASLFinal = 12,
221-
SHA256Password = 13
129+
SHA256Password = 10
130+
}
131+
132+
enum PasswordStoreType
133+
{
134+
PlainText = 0,
135+
MD5 = 1,
136+
SHA256 = 2
222137
}

0 commit comments

Comments
 (0)