|
| 1 | +// Licensed to the .NET Foundation under one or more agreements. |
| 2 | +// The .NET Foundation licenses this file to you under the MIT license. |
| 3 | +// See the LICENSE file in the project root for more information. |
| 4 | + |
| 5 | +using System; |
| 6 | +using System.Diagnostics; |
| 7 | +using System.Linq; |
| 8 | +using System.Security.Cryptography; |
| 9 | + |
| 10 | +namespace Microsoft.Data.SqlClient |
| 11 | +{ |
| 12 | + internal class EnclavePublicKey |
| 13 | + { |
| 14 | + public byte[] PublicKey { get; set; } |
| 15 | + |
| 16 | + public EnclavePublicKey(byte[] payload) |
| 17 | + { |
| 18 | + PublicKey = payload; |
| 19 | + } |
| 20 | + } |
| 21 | + |
| 22 | + internal class EnclaveDiffieHellmanInfo |
| 23 | + { |
| 24 | + public int Size { get; private set; } |
| 25 | + |
| 26 | + public byte[] PublicKey { get; private set; } |
| 27 | + |
| 28 | + public byte[] PublicKeySignature { get; private set; } |
| 29 | + |
| 30 | + public EnclaveDiffieHellmanInfo(byte[] payload) |
| 31 | + { |
| 32 | + Size = payload.Length; |
| 33 | + |
| 34 | + int offset = 0; |
| 35 | + int publicKeySize = BitConverter.ToInt32(payload, offset); |
| 36 | + offset += sizeof(int); |
| 37 | + |
| 38 | + int publicKeySignatureSize = BitConverter.ToInt32(payload, offset); |
| 39 | + offset += sizeof(int); |
| 40 | + |
| 41 | + PublicKey = payload.Skip(offset).Take(publicKeySize).ToArray(); |
| 42 | + offset += publicKeySize; |
| 43 | + |
| 44 | + PublicKeySignature = payload.Skip(offset).Take(publicKeySignatureSize).ToArray(); |
| 45 | + offset += publicKeySignatureSize; |
| 46 | + } |
| 47 | + } |
| 48 | + |
| 49 | + internal enum EnclaveType |
| 50 | + { |
| 51 | + None = 0, |
| 52 | + |
| 53 | + Vbs = 1, |
| 54 | + |
| 55 | + Sgx = 2 |
| 56 | + } |
| 57 | + |
| 58 | + // Contains methods to convert cryptography keys between different formats. |
| 59 | + internal sealed class KeyConverter |
| 60 | + { |
| 61 | + // The RSA public key blob is structured as follows: |
| 62 | + // BCRYPT_RSAKEY_BLOB header |
| 63 | + // byte[ExponentSize] publicExponent |
| 64 | + // byte[ModulusSize] modulus |
| 65 | + private readonly struct RSAPublicKeyBlob |
| 66 | + { |
| 67 | + // Size of an RSA public key blob |
| 68 | + internal static readonly int Size = 539; |
| 69 | + // Size of the BCRYPT_RSAKEY_BLOB header |
| 70 | + internal static readonly int HeaderSize = 27; |
| 71 | + // Size of the exponent (final 3 bytes of the header) |
| 72 | + internal static readonly int ExponentSize = 3; |
| 73 | + // Size of the modulus (remaining bytes after the header) |
| 74 | + internal static readonly int ModulusSize = Size - HeaderSize; |
| 75 | + internal static readonly int ExponentOffset = HeaderSize - ExponentSize; |
| 76 | + internal static readonly int ModulusOffset = HeaderSize; |
| 77 | + } |
| 78 | + |
| 79 | + // Extracts the public key's modulus and exponent from an RSA public key blob |
| 80 | + // and returns an RSAParameters object |
| 81 | + internal static RSAParameters RSAPublicKeyBlobToParams(byte[] keyBlob) |
| 82 | + { |
| 83 | + Debug.Assert(keyBlob.Length == RSAPublicKeyBlob.Size, |
| 84 | + $"RSA public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {RSAPublicKeyBlob.Size}"); |
| 85 | + return new RSAParameters() |
| 86 | + { |
| 87 | + Exponent = keyBlob.Skip(RSAPublicKeyBlob.ExponentOffset).Take(RSAPublicKeyBlob.ExponentSize).ToArray(), |
| 88 | + Modulus = keyBlob.Skip(RSAPublicKeyBlob.ModulusOffset).Take(RSAPublicKeyBlob.ModulusSize).ToArray() |
| 89 | + }; |
| 90 | + } |
| 91 | + |
| 92 | + // The ECC public key blob is structured as follows: |
| 93 | + // BCRYPT_ECCKEY_BLOB header |
| 94 | + // byte[KeySize] X |
| 95 | + // byte[KeySize] Y |
| 96 | + private readonly struct ECCPublicKeyBlob |
| 97 | + { |
| 98 | + // Size of an ECC public key blob |
| 99 | + internal static readonly int Size = 104; |
| 100 | + // Size of the BCRYPT_ECCKEY_BLOB header |
| 101 | + internal static readonly int HeaderSize = 8; |
| 102 | + // Size of each coordinate |
| 103 | + internal static readonly int KeySize = (Size - HeaderSize) / 2; |
| 104 | + } |
| 105 | + |
| 106 | + // Magic numbers identifying blob types |
| 107 | + // https://docs.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/cba27df5-4880-4f95-a879-783f8657e53b |
| 108 | + private readonly struct KeyBlobMagicNumber |
| 109 | + { |
| 110 | + internal static readonly byte[] ECDHPublicP384 = new byte[] { 0x45, 0x43, 0x4b, 0x33 }; |
| 111 | + } |
| 112 | + |
| 113 | + // Extracts the public key's X and Y coordinates from an ECC public key blob |
| 114 | + // and returns an ECParameters object |
| 115 | + internal static ECParameters ECCPublicKeyBlobToParams(byte[] keyBlob) |
| 116 | + { |
| 117 | + Debug.Assert(keyBlob.Length == ECCPublicKeyBlob.Size, |
| 118 | + $"ECC public key blob was not the expected length. Actual: {keyBlob.Length}. Expected: {ECCPublicKeyBlob.Size}"); |
| 119 | + return new ECParameters |
| 120 | + { |
| 121 | + Curve = ECCurve.NamedCurves.nistP384, |
| 122 | + Q = new ECPoint |
| 123 | + { |
| 124 | + X = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize).Take(ECCPublicKeyBlob.KeySize).ToArray(), |
| 125 | + Y = keyBlob.Skip(ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize).Take(ECCPublicKeyBlob.KeySize).ToArray() |
| 126 | + }, |
| 127 | + }; |
| 128 | + } |
| 129 | + |
| 130 | + // Serializes an ECDiffieHellmanPublicKey to an ECC public key blob |
| 131 | + // "ECDiffieHellmanPublicKey.ToByteArray() doesn't have a (standards-)defined export |
| 132 | + // format. The version used by ECDiffieHellmanPublicKeyCng is Windows-specific" |
| 133 | + // from https://github.com/dotnet/runtime/issues/27276 |
| 134 | + // => ECDiffieHellmanPublicKey.ToByteArray() is not supported in Unix |
| 135 | + internal static byte[] ECDHPublicKeyToECCKeyBlob(ECDiffieHellmanPublicKey publicKey) |
| 136 | + { |
| 137 | + byte[] keyBlob = new byte[ECCPublicKeyBlob.Size]; |
| 138 | + |
| 139 | + // Set magic number |
| 140 | + Array.Copy(KeyBlobMagicNumber.ECDHPublicP384, 0, keyBlob, 0, 4); |
| 141 | + // Set key size |
| 142 | + keyBlob[4] = (byte)ECCPublicKeyBlob.KeySize; |
| 143 | + |
| 144 | + ECPoint ecPoint = publicKey.ExportParameters().Q; |
| 145 | + Debug.Assert(ecPoint.X.Length == ECCPublicKeyBlob.KeySize && ecPoint.Y.Length == ECCPublicKeyBlob.KeySize, |
| 146 | + $"ECDH public key was not the expected length. Actual (X): {ecPoint.X.Length}. Actual (Y): {ecPoint.Y.Length} Expected: {ECCPublicKeyBlob.Size}"); |
| 147 | + // Copy x and y coordinates to key blob |
| 148 | + Array.Copy(ecPoint.X, 0, keyBlob, ECCPublicKeyBlob.HeaderSize, ECCPublicKeyBlob.KeySize); |
| 149 | + Array.Copy(ecPoint.Y, 0, keyBlob, ECCPublicKeyBlob.HeaderSize + ECCPublicKeyBlob.KeySize, ECCPublicKeyBlob.KeySize); |
| 150 | + return keyBlob; |
| 151 | + } |
| 152 | + } |
| 153 | +} |
0 commit comments