/
EnclaveSessionCache.cs
75 lines (64 loc) · 3.29 KB
/
EnclaveSessionCache.cs
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
// See the LICENSE file in the project root for more information.
using System;
using System.Runtime.Caching;
using System.Threading;
namespace Microsoft.Data.SqlClient
{
// Maintains a cache of SqlEnclaveSession instances
internal class EnclaveSessionCache
{
private readonly MemoryCache enclaveMemoryCache = new MemoryCache("EnclaveMemoryCache");
private readonly Object enclaveCacheLock = new Object();
// Nonce for each message sent by the client to the server to prevent replay attacks by the server,
// given that for Always Encrypted scenarios, the server is considered an "untrusted" man-in-the-middle.
private long _counter;
// Cache timeout of 8 hours to be consistent with jwt validity.
private static int enclaveCacheTimeOutInHours = 8;
// Retrieves a SqlEnclaveSession from the cache
internal SqlEnclaveSession GetEnclaveSession(string servername, string attestationUrl, out long counter)
{
string cacheKey = GenerateCacheKey(servername, attestationUrl);
SqlEnclaveSession enclaveSession = enclaveMemoryCache[cacheKey] as SqlEnclaveSession;
counter = Interlocked.Increment(ref _counter);
return enclaveSession;
}
// Invalidates a SqlEnclaveSession entry in the cache
internal void InvalidateSession(string serverName, string enclaveAttestationUrl, SqlEnclaveSession enclaveSessionToInvalidate)
{
string cacheKey = GenerateCacheKey(serverName, enclaveAttestationUrl);
lock (enclaveCacheLock)
{
long counter;
SqlEnclaveSession enclaveSession = GetEnclaveSession(serverName, enclaveAttestationUrl, out counter);
if (enclaveSession != null && enclaveSession.SessionId == enclaveSessionToInvalidate.SessionId)
{
SqlEnclaveSession enclaveSessionRemoved = enclaveMemoryCache.Remove(cacheKey) as SqlEnclaveSession;
if (enclaveSessionRemoved == null)
{
throw new InvalidOperationException(SR.EnclaveSessionInvalidationFailed);
}
}
}
}
// Creates a new SqlEnclaveSession and adds it to the cache
internal SqlEnclaveSession CreateSession(string attestationUrl, string serverName, byte[] sharedSecret, long sessionId, out long counter)
{
string cacheKey = GenerateCacheKey(serverName, attestationUrl);
SqlEnclaveSession enclaveSession = null;
lock (enclaveCacheLock)
{
enclaveSession = new SqlEnclaveSession(sharedSecret, sessionId);
enclaveMemoryCache.Add(cacheKey, enclaveSession, DateTime.UtcNow.AddHours(enclaveCacheTimeOutInHours));
counter = Interlocked.Increment(ref _counter);
}
return enclaveSession;
}
// Generates the cache key for the enclave session cache
private string GenerateCacheKey(string serverName, string attestationUrl)
{
return (serverName + attestationUrl).ToLowerInvariant();
}
}
}