From 1a1479791f29cb5bc280afe82ac09e4b55d001b6 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 1 Sep 2024 15:52:29 +1000 Subject: [PATCH 1/2] BuildToolsSigning --- .../AzureSignTool/AlgorithmTranslator.cs | 36 + .../AuthenticodeKeyVaultSigner.cs | 294 +++ .../KeyVaultConfigurationDiscoverer.cs | 1658 +++++++++++++++++ tools/CustomBuildTool/AzureSignTool/LICENSE | 21 + .../AzureSignTool/MemoryCertificateStore.cs | 82 + .../AzureSignTool/NativeMethods.cs | 120 ++ .../RSAKeyVaultProvider/ECDsaKeyVault.cs | 156 ++ .../ECDsaKeyVaultExtensions.cs | 53 + .../EncryptionPaddingTranslator.cs | 23 + .../RSAKeyVaultProvider/KeyVaultContext.cs | 128 ++ .../AzureSignTool/RSAKeyVaultProvider/LICENSE | 21 + .../RSAKeyVaultProvider/RSAKeyVault.cs | 151 ++ .../RSAKeyVaultExtensions.cs | 65 + .../RSAKeyVaultProvider/Sha1Helper.cs | 19 + .../RSAKeyVaultProvider/SignatureAlgorithm.cs | 8 + .../SignatureAlgorithmTranslator.cs | 38 + .../AzureSignTool/SignCommand.cs | 57 + .../AzureSignTool/SipExtensionFactory.cs | 58 + .../AzureSignTool/TimeStampConfiguration.cs | 65 + tools/CustomBuildTool/Build.cs | 222 ++- tools/CustomBuildTool/CustomBuildTool.csproj | 23 +- tools/CustomBuildTool/CustomBuildTool.slnx | 15 + tools/CustomBuildTool/DynData.cs | 77 +- tools/CustomBuildTool/Github.cs | 355 ++-- tools/CustomBuildTool/HeaderGen.cs | 11 +- tools/CustomBuildTool/Http.cs | 114 ++ tools/CustomBuildTool/NativeMethods.cs | 117 +- tools/CustomBuildTool/NativeMethods.txt | 35 + tools/CustomBuildTool/Utils.cs | 81 +- tools/CustomBuildTool/Vault.cs | 109 ++ tools/CustomBuildTool/Verify.cs | 10 +- tools/CustomBuildTool/VisualStudio.cs | 174 +- tools/CustomBuildTool/Win32.cs | 133 +- tools/CustomBuildTool/Zip.cs | 249 ++- 34 files changed, 4103 insertions(+), 675 deletions(-) create mode 100644 tools/CustomBuildTool/AzureSignTool/AlgorithmTranslator.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/KeyVaultConfigurationDiscoverer.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/LICENSE create mode 100644 tools/CustomBuildTool/AzureSignTool/MemoryCertificateStore.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/NativeMethods.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVault.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVaultExtensions.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/EncryptionPaddingTranslator.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/KeyVaultContext.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/LICENSE create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVault.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVaultExtensions.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/Sha1Helper.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithm.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithmTranslator.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/SignCommand.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/SipExtensionFactory.cs create mode 100644 tools/CustomBuildTool/AzureSignTool/TimeStampConfiguration.cs create mode 100644 tools/CustomBuildTool/CustomBuildTool.slnx create mode 100644 tools/CustomBuildTool/Http.cs create mode 100644 tools/CustomBuildTool/NativeMethods.txt create mode 100644 tools/CustomBuildTool/Vault.cs diff --git a/tools/CustomBuildTool/AzureSignTool/AlgorithmTranslator.cs b/tools/CustomBuildTool/AzureSignTool/AlgorithmTranslator.cs new file mode 100644 index 000000000000..8bb6ecb8b4ac --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/AlgorithmTranslator.cs @@ -0,0 +1,36 @@ +namespace CustomBuildTool +{ + internal static class AlgorithmTranslator + { + public static Windows.Win32.Security.Cryptography.ALG_ID HashAlgorithmToAlgId(HashAlgorithmName hashAlgorithmName) + { + if (hashAlgorithmName.Name == HashAlgorithmName.SHA1.Name) + return Windows.Win32.Security.Cryptography.ALG_ID.CALG_SHA1; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA256.Name) + return Windows.Win32.Security.Cryptography.ALG_ID.CALG_SHA_256; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA384.Name) + return Windows.Win32.Security.Cryptography.ALG_ID.CALG_SHA_384; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA512.Name) + return Windows.Win32.Security.Cryptography.ALG_ID.CALG_SHA_512; + + throw new NotSupportedException("The algorithm specified is not supported."); + } + + public static ReadOnlySpan HashAlgorithmToOidAsciiTerminated(HashAlgorithmName hashAlgorithmName) + { + if (hashAlgorithmName.Name == HashAlgorithmName.SHA1.Name) + //1.3.14.3.2.26 + return new byte[] { 0x31, 0x2e, 0x33, 0x2e, 0x31, 0x34, 0x2e, 0x33, 0x2e, 0x32, 0x2e, 0x32, 0x36, 0x00 }; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA256.Name) + //2.16.840.1.101.3.4.2.1 + return new byte[] { 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x31, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x2e, 0x31, 0x00 }; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA384.Name) + //2.16.840.1.101.3.4.2.2 + return new byte[] { 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x31, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x2e, 0x32, 0x00 }; + if (hashAlgorithmName.Name == HashAlgorithmName.SHA512.Name) + //2.16.840.1.101.3.4.2.3 + return new byte[] { 0x32, 0x2e, 0x31, 0x36, 0x2e, 0x38, 0x34, 0x30, 0x2e, 0x31, 0x2e, 0x31, 0x30, 0x31, 0x2e, 0x33, 0x2e, 0x34, 0x2e, 0x32, 0x2e, 0x33, 0x00 }; + throw new NotSupportedException("The algorithm specified is not supported."); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs b/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs new file mode 100644 index 000000000000..8b63385bf222 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs @@ -0,0 +1,294 @@ +using AzureSign.Core.Interop; +using Windows.Win32.Security.Cryptography; + +namespace CustomBuildTool +{ + /// + /// Signs a file with an Authenticode signature. + /// + public unsafe class AuthenticodeKeyVaultSigner : IDisposable + { + private readonly AsymmetricAlgorithm SigningAlgorithm; + private readonly X509Certificate2 SigningCertificate; + private readonly HashAlgorithmName FileDigestAlgorithm; + private readonly TimeStampConfiguration TimeStampConfiguration; + private readonly MemoryCertificateStore CertificateStore; + private readonly X509Chain CertificateChain; + private readonly PFN_AUTHENTICODE_DIGEST_SIGN SigningCallbaack; + + /// + /// The PFN_AUTHENTICODE_DIGEST_SIGN user supplied callback function implements digest signing. + /// This function is currently called by SignerSignEx3 for digest signing. + /// + /// A pointer to a CERT_CONTEXT structure that specifies the certificate used to create the digital signature. + /// Pointer to a CRYPT_DATA_BLOB structure that contains metadata for digest signing. + /// Specifies the digest algorithm to be used for digest signing. + /// Pointer to a buffer which contains the digest to be signed. + /// The size, in bytes, of the pbToBeSignedDigest buffer. + /// Pointer to CRYPT_DATA_BLOB which receives the signed digest. + /// If the function succeeds, the function returns S_OK. If the function fails, it returns an HRESULT value that indicates the error. + [UnmanagedFunctionPointer(CallingConvention.Winapi)] + internal unsafe delegate HRESULT PFN_AUTHENTICODE_DIGEST_SIGN( + [In] CERT_CONTEXT* pSigningCert, + [In, Optional] CRYPT_DATA_BLOB* pMetadataBlob, + [In] ALG_ID digestAlgId, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 4)] byte[] pbToBeSignedDigest, // byte* + [In] uint cbToBeSignedDigest, + [In, Out] CRYPT_INTEGER_BLOB* pSignedDigest + ); + + /// + /// Creates a new instance of . + /// + /// + /// An instance of an asymmetric algorithm that will be used to sign. It must support signing with + /// a private key. + /// + /// The X509 public certificate for the . + /// The digest algorithm to sign the file. + /// The timestamp configuration for timestamping the file. To omit timestamping, + /// use . + /// Any additional certificates to assist in building a certificate chain. + public unsafe AuthenticodeKeyVaultSigner( + AsymmetricAlgorithm signingAlgorithm, + X509Certificate2 signingCertificate, + HashAlgorithmName fileDigestAlgorithm, + TimeStampConfiguration timeStampConfiguration, + X509Certificate2Collection additionalCertificates = null) + { + this.FileDigestAlgorithm = fileDigestAlgorithm; + this.SigningCertificate = signingCertificate; + this.TimeStampConfiguration = timeStampConfiguration; + this.SigningAlgorithm = signingAlgorithm; + this.CertificateStore = MemoryCertificateStore.Create(); + this.CertificateChain = new X509Chain(); + + if (additionalCertificates is not null) + { + this.CertificateChain.ChainPolicy.ExtraStore.AddRange(additionalCertificates); + } + + this.CertificateChain.ChainPolicy.VerificationFlags = X509VerificationFlags.AllFlags; + + if (!this.CertificateChain.Build(signingCertificate)) + { + throw new InvalidOperationException("Failed to build chain for certificate."); + } + + for (var i = 0; i < this.CertificateChain.ChainElements.Count; i++) + { + this.CertificateStore.Add(this.CertificateChain.ChainElements[i].Certificate); + } + + this.SigningCallbaack = this.SignDigestCallback; + } + + /// Authenticode signs a file. + /// True if the signing process should try to include page hashing, otherwise false. + /// Use null to use the operating system default. Note that page hashing still may be disabled if the + /// Subject Interface Package does not support page hashing. + /// A URL describing the signature or the signer. + /// The description to apply to the signature. + /// The path to the file to signed. + /// An optional logger to capture signing operations. + /// A HRESULT indicating the result of the signing operation. S_OK, or zero, is returned if the signing + /// operation completed successfully. + internal unsafe HRESULT SignFile(ReadOnlySpan path, ReadOnlySpan description, ReadOnlySpan descriptionUrl, bool pageHashing = false) + { + HRESULT result; + SIGNER_CONTEXT* context = null; + SIGNER_TIMESTAMP_FLAGS timeStampFlags = 0; + ReadOnlySpan timestampAlgorithmOid = null; + APPX_SIP_CLIENT_DATA *clientData = null; + string timestampString = null; + + var flags = SIGNER_SIGN_FLAGS.SPC_DIGEST_SIGN_FLAG; + + if (pageHashing) + flags |= SIGNER_SIGN_FLAGS.SPC_INC_PE_PAGE_HASHES_FLAG; + else + flags |= SIGNER_SIGN_FLAGS.SPC_EXC_PE_PAGE_HASHES_FLAG; + + if (this.TimeStampConfiguration.Type == TimeStampType.Authenticode) + { + timeStampFlags = SIGNER_TIMESTAMP_FLAGS.SIGNER_TIMESTAMP_AUTHENTICODE; + timestampString = this.TimeStampConfiguration.Url; + } + else if (this.TimeStampConfiguration.Type == TimeStampType.RFC3161) + { + timeStampFlags = SIGNER_TIMESTAMP_FLAGS.SIGNER_TIMESTAMP_RFC3161; + timestampAlgorithmOid = AlgorithmTranslator.HashAlgorithmToOidAsciiTerminated(this.TimeStampConfiguration.DigestAlgorithm); + timestampString = this.TimeStampConfiguration.Url; + } + + fixed (byte* pTimestampAlg = ×tampAlgorithmOid.GetPinnableReference()) + fixed (char* timestampUrl = timestampString) + fixed (char* pPath = NullTerminate(path)) + fixed (char* pDescription = NullTerminate(description)) + fixed (char* pDescriptionUrl = NullTerminate(descriptionUrl)) + { + var timestampAlg = new PCSTR(pTimestampAlg); + + var fileInfo = new SIGNER_FILE_INFO(); + fileInfo.cbSize = (uint)sizeof(SIGNER_FILE_INFO); + fileInfo.pwszFileName = new PCWSTR(pPath); + + var subjectInfo = new SIGNER_SUBJECT_INFO(); + subjectInfo.cbSize = (uint)sizeof(SIGNER_SUBJECT_INFO); + subjectInfo.pdwIndex = (uint*)NativeMemory.AllocZeroed((nuint)sizeof(IntPtr)); + subjectInfo.dwSubjectChoice = SIGNER_SUBJECT_CHOICE.SIGNER_SUBJECT_FILE; + subjectInfo.Anonymous.pSignerFileInfo = &fileInfo; + + var storeInfo = new SIGNER_CERT_STORE_INFO(); + storeInfo.cbSize = (uint)sizeof(SIGNER_CERT_STORE_INFO); + storeInfo.dwCertPolicy = SIGNER_CERT_POLICY.SIGNER_CERT_POLICY_CHAIN; + storeInfo.hCertStore = new HCERTSTORE((void*)this.CertificateStore.Handle); + storeInfo.pSigningCert = (CERT_CONTEXT*)this.SigningCertificate.Handle; + + var signerCert = new SIGNER_CERT(); + signerCert.cbSize = (uint)sizeof(SIGNER_CERT); + signerCert.dwCertChoice = SIGNER_CERT_CHOICE.SIGNER_CERT_STORE; + signerCert.Anonymous.pCertStoreInfo = &storeInfo; + + var signatureAuthcode = new SIGNER_ATTR_AUTHCODE(); + signatureAuthcode.cbSize = (uint)sizeof(SIGNER_ATTR_AUTHCODE); + signatureAuthcode.pwszName = pDescription; + signatureAuthcode.pwszInfo = pDescriptionUrl; + + var signatureInfo = new SIGNER_SIGNATURE_INFO(); + signatureInfo.cbSize = (uint)sizeof(SIGNER_SIGNATURE_INFO); + signatureInfo.dwAttrChoice = SIGNER_SIGNATURE_ATTRIBUTE_CHOICE.SIGNER_AUTHCODE_ATTR; + signatureInfo.algidHash = AlgorithmTranslator.HashAlgorithmToAlgId(this.FileDigestAlgorithm); + signatureInfo.Anonymous.pAttrAuthcode = &signatureAuthcode; + + var signCallbackInfo = new SIGNER_DIGEST_SIGN_INFO(); + signCallbackInfo.cbSize = (uint)Marshal.SizeOf(); + signCallbackInfo.dwDigestSignChoice = 1; // DIGEST_SIGN + signCallbackInfo.Anonymous.pfnAuthenticodeDigestSign = (delegate* unmanaged[Stdcall])Marshal.GetFunctionPointerForDelegate(this.SigningCallbaack); + + if (SipExtensionFactory.GetSipKind(path) == SipKind.Appx) + { + APPX_SIP_CLIENT_DATA sipclientdata; + SIGNER_SIGN_EX3_PARAMS parameters; + SIGNER_DIGEST_SIGN_INFO_V1 digestCallback; + + digestCallback.Size = (uint)sizeof(SIGNER_DIGEST_SIGN_INFO_V1); + digestCallback.AuthenticodeDigestSign = Marshal.GetFunctionPointerForDelegate(this.SigningCallbaack); + + clientData = &sipclientdata; + clientData->pSignerParams = ¶meters; + clientData->pSignerParams->dwFlags = SIGNER_SIGN_FLAGS.SPC_DIGEST_SIGN_FLAG | SIGNER_SIGN_FLAGS.SPC_EXC_PE_PAGE_HASHES_FLAG; + clientData->pSignerParams->dwTimestampFlags = timeStampFlags; + clientData->pSignerParams->pSubjectInfo = &subjectInfo; + clientData->pSignerParams->pSignerCert = &signerCert; + clientData->pSignerParams->pSignatureInfo = &signatureInfo; + clientData->pSignerParams->ppSignerContext = &context; + clientData->pSignerParams->pwszHttpTimeStamp = timestampUrl; + clientData->pSignerParams->pszTimestampAlgorithmOid = timestampAlg; + clientData->pSignerParams->pSignCallBack = &digestCallback; + } + + result = PInvoke.SignerSignEx3( + flags, + &subjectInfo, + &signerCert, + &signatureInfo, + null, + timeStampFlags, + timestampAlg, + timestampUrl, + null, + clientData, + &context, + null, + signCallbackInfo, + null + ); + + if (result == HRESULT.S_OK) + { + if (context != null) + { + PInvoke.SignerFreeSignerContext(context); + } + + if (clientData != null && clientData->pAppxSipState != IntPtr.Zero) + { + Marshal.Release(clientData->pAppxSipState); + } + } + + NativeMemory.Free(subjectInfo.pdwIndex); + + return result; + } + } + + /// + /// Frees all resources used by the . + /// + public void Dispose() + { + this.CertificateChain.Dispose(); + this.CertificateStore.Close(); + GC.SuppressFinalize(this); + } + + private unsafe HRESULT SignDigestCallback( + [In] CERT_CONTEXT* pSigningCert, + [In, Optional] CRYPT_DATA_BLOB* pMetadataBlob, + [In] ALG_ID digestAlgId, + [In, MarshalAs(UnmanagedType.LPArray, ArraySubType = UnmanagedType.U1, SizeParamIndex = 4)] byte[] pbToBeSignedDigest, // byte* + [In] uint cbToBeSignedDigest, + [In, Out] CRYPT_INTEGER_BLOB* SignedDigest + ) + { + byte[] digest; + //X509Certificate2 cert = new X509Certificate2((IntPtr)pSigningCert); + //ECDsa dsa = ECDsaCertificateExtensions.GetECDsaPublicKey(cert); + // + //{ + // byte[] buffer = new byte[cbToBeSignedDigest]; + // + // fixed (void* ptr = &buffer[0]) + // { + // Unsafe.CopyBlock(ptr, pbToBeSignedDigest, (uint)cbToBeSignedDigest); + // } + //} + + { + switch (this.SigningAlgorithm) + { + case RSA rsa: + digest = rsa.SignHash(pbToBeSignedDigest, this.FileDigestAlgorithm, RSASignaturePadding.Pkcs1); + break; + case ECDsa ecdsa: + digest = ecdsa.SignHash(pbToBeSignedDigest); + break; + default: + return HRESULT.E_INVALIDARG; + } + } + + { + SignedDigest->pbData = (byte*)NativeMemory.Alloc((nuint)digest.Length); + SignedDigest->cbData = (uint)digest.Length; + + fixed (void* ptr = &digest[0]) + { + Unsafe.CopyBlock(SignedDigest->pbData, ptr, (uint)digest.Length); + } + } + + return HRESULT.S_OK; + } + + private static char[] NullTerminate(ReadOnlySpan str) + { + char[] result = new char[str.Length + 1]; + str.CopyTo(result); + result[result.Length - 1] = '\0'; + return result; + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/KeyVaultConfigurationDiscoverer.cs b/tools/CustomBuildTool/AzureSignTool/KeyVaultConfigurationDiscoverer.cs new file mode 100644 index 000000000000..3216ffc60582 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/KeyVaultConfigurationDiscoverer.cs @@ -0,0 +1,1658 @@ +using Azure.Core; + +namespace CustomBuildTool +{ + // https://learn.microsoft.com/en-us/rest/api/keyvault/certificates/get-certificate/get-certificate?tabs=HTTP + + //public async Task> Materialize(AzureKeyVaultSignConfigurationSet configuration) + //{ + // TokenCredential credential; + // + // if (configuration.ManagedIdentity) + // { + // credential = new DefaultAzureCredential(); + // } + // else if(!string.IsNullOrWhiteSpace(configuration.AzureAccessToken)) + // { + // credential = new AccessTokenCredential(configuration.AzureAccessToken); + // } + // else + // { + // credential = new ClientSecretCredential(configuration.AzureTenantId, configuration.AzureClientId, configuration.AzureClientSecret); + // } + // + // X509Certificate2 certificate; + // KeyVaultCertificateWithPolicy azureCertificate; + // try + // { + // var certClient = new CertificateClient(configuration.AzureKeyVaultUrl, credential); + // + // //_logger.LogTrace($"Retrieving certificate {configuration.AzureKeyVaultCertificateName}."); + // azureCertificate = (await certClient.GetCertificateAsync(configuration.AzureKeyVaultCertificateName).ConfigureAwait(false)).Value; + // //_logger.LogTrace($"Retrieved certificate {configuration.AzureKeyVaultCertificateName}."); + // + // certificate = new X509Certificate2(azureCertificate.Cer); + // } + // catch (Exception e) + // { + // //_logger.LogError($"Failed to retrieve certificate {configuration.AzureKeyVaultCertificateName} from Azure Key Vault. Please verify the name of the certificate and the permissions to the certificate. Error message: {e.Message}."); + // //_logger.LogTrace(e.ToString()); + // + // return e; + // } + // var keyId = azureCertificate.KeyId; + // return new AzureKeyVaultMaterializedConfiguration(credential, certificate, keyId); + //} + + public static class Base64Url + { + public static byte[] Decode(string str) + { + str = new StringBuilder(str).Replace('-', '+').Replace('_', '/').Append('=', (str.Length % 4 != 0) ? (4 - str.Length % 4) : 0).ToString(); + return Convert.FromBase64String(str); + } + + public static string Encode(byte[] bytes) + { + return new StringBuilder(Convert.ToBase64String(bytes)).Replace('+', '-').Replace('/', '_').Replace("=", "").ToString(); + } + } + + public class AzureKeyVaultMaterializedConfiguration + { + public AzureKeyVaultMaterializedConfiguration(TokenCredential credential, X509Certificate2 publicCertificate, Uri keyId) + { + TokenCredential = credential; + KeyId = keyId; + PublicCertificate = publicCertificate; + } + + public X509Certificate2 PublicCertificate { get; } + public TokenCredential TokenCredential { get; } + public Uri KeyId { get; } + } + + public class AccessTokenCredential : TokenCredential + { + private readonly AccessToken accessToken; + + public AccessTokenCredential(string token, DateTimeOffset expiresOn) + { + if (string.IsNullOrWhiteSpace(token)) + { + throw new ArgumentException("Access Token cannot be null or empty", nameof(token)); + } + + accessToken = new AccessToken(token, expiresOn); + } + + public AccessTokenCredential(string token) : this(token, DateTimeOffset.UtcNow.AddHours(1)) + { + + } + + public override AccessToken GetToken(TokenRequestContext requestContext, CancellationToken cancellationToken) + { + return accessToken; + } + + public override ValueTask GetTokenAsync(TokenRequestContext requestContext, CancellationToken cancellationToken) + { + return new ValueTask(accessToken); + } + } + + // + // Summary: + // An operation that can be performed with the key. + public readonly struct KeyOperation : IEquatable + { + private readonly string _value; + + // + // Summary: + // Gets a value that indicates the key can be used to encrypt with the Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.EncryptAsync(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm,System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Encrypt(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm,System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation Encrypt { get; } = new KeyOperation("encrypt"); + + + // + // Summary: + // Gets a value that indicates the key can be used to decrypt with the Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.DecryptAsync(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm,System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Decrypt(Azure.Security.KeyVault.Keys.Cryptography.EncryptionAlgorithm,System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation Decrypt { get; } = new KeyOperation("decrypt"); + + + // + // Summary: + // Gets a value that indicates the key can be used to sign with the Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.SignAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm,System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Sign(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm,System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation Sign { get; } = new KeyOperation("sign"); + + + // + // Summary: + // Gets a value that indicates the key can be used to verify with the Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.VerifyAsync(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm,System.Byte[],System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.Verify(Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm,System.Byte[],System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation Verify { get; } = new KeyOperation("verify"); + + + // + // Summary: + // Gets a value that indicates the key can be used to wrap another key with the + // Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.WrapKeyAsync(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm,System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.WrapKey(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm,System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation WrapKey { get; } = new KeyOperation("wrapKey"); + + + // + // Summary: + // Gets a value that indicates the key can be used to unwrap another key with the + // Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.UnwrapKeyAsync(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm,System.Byte[],System.Threading.CancellationToken) + // or Azure.Security.KeyVault.Keys.Cryptography.CryptographyClient.UnwrapKey(Azure.Security.KeyVault.Keys.Cryptography.KeyWrapAlgorithm,System.Byte[],System.Threading.CancellationToken) + // methods. + public static KeyOperation UnwrapKey { get; } = new KeyOperation("unwrapKey"); + + + // + // Summary: + // Initializes a new instance of the Azure.Security.KeyVault.Keys.KeyOperation structure. + // + // Parameters: + // value: + // The string value of the instance. + public KeyOperation(string value) + { + _value = value ?? throw new ArgumentNullException("value"); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyOperation values are the same. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyOperation to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyOperation to compare. + // + // Returns: + // True if left and right are the same; otherwise, false. + public static bool operator ==(KeyOperation left, KeyOperation right) + { + return left.Equals(right); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyOperation values are different. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyOperation to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyOperation to compare. + // + // Returns: + // True if left and right are different; otherwise, false. + public static bool operator !=(KeyOperation left, KeyOperation right) + { + return !left.Equals(right); + } + + // + // Summary: + // Converts a string to a Azure.Security.KeyVault.Keys.KeyOperation. + // + // Parameters: + // value: + // The string value to convert. + public static implicit operator KeyOperation(string value) + { + return new KeyOperation(value); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + if (obj is KeyOperation) + { + KeyOperation other = (KeyOperation)obj; + return Equals(other); + } + + return false; + } + + public bool Equals(KeyOperation other) + { + return string.Equals(_value, other._value, StringComparison.Ordinal); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return _value?.GetHashCode() ?? 0; + } + + public override string ToString() + { + return _value; + } + } + + // + // Summary: + // Azure.Security.KeyVault.Keys.JsonWebKey key types. + public readonly struct KeyType : IEquatable + { + private readonly string _value; + + // + // Summary: + // An Elliptic Curve Cryptographic (ECC) algorithm. + public static KeyType Ec { get; } = new KeyType("EC"); + + + // + // Summary: + // An Elliptic Curve Cryptographic (ECC) algorithm backed by HSM. + public static KeyType EcHsm { get; } = new KeyType("EC-HSM"); + + + // + // Summary: + // An RSA cryptographic algorithm. + public static KeyType Rsa { get; } = new KeyType("RSA"); + + + // + // Summary: + // An RSA cryptographic algorithm backed by HSM. + public static KeyType RsaHsm { get; } = new KeyType("RSA-HSM"); + + + // + // Summary: + // An AES cryptographic algorithm. + public static KeyType Oct { get; } = new KeyType("oct"); + + + // + // Summary: + // Initializes a new instance of the Azure.Security.KeyVault.Keys.KeyType structure. + // + // Parameters: + // value: + // The string value of the instance. + public KeyType(string value) + { + _value = value ?? throw new ArgumentNullException("value"); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyType values are the same. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyType to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyType to compare. + // + // Returns: + // True if left and right are the same; otherwise, false. + public static bool operator ==(KeyType left, KeyType right) + { + return left.Equals(right); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyType values are different. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyType to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyType to compare. + // + // Returns: + // True if left and right are different; otherwise, false. + public static bool operator !=(KeyType left, KeyType right) + { + return !left.Equals(right); + } + + // + // Summary: + // Converts a string to a Azure.Security.KeyVault.Keys.KeyType. + // + // Parameters: + // value: + // The string value to convert. + public static implicit operator KeyType(string value) + { + return new KeyType(value); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + //public override bool Equals(object obj) + //{ + // if (obj is KeyType) + // { + // KeyType other = (KeyType)obj; + // return Equals(other); + // } + + // return false; + //} + + public bool Equals(KeyType other) + { + return string.Equals(_value, other._value, StringComparison.OrdinalIgnoreCase); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return _value?.GetHashCode() ?? 0; + } + + public override string ToString() + { + return _value; + } + + public override bool Equals(object obj) + { + return obj is KeyType && Equals((KeyType)obj); + } + } + + public interface IJsonSerializable where TSelf : IJsonSerializable + { + static abstract TSelf Read(ref Utf8JsonReader reader, Type typeToConvert, JsonSerializerOptions options); + } + + public interface IJsonDeserializable where TSelf : IJsonDeserializable + { + static abstract void Write(Utf8JsonWriter writer, TSelf value, JsonSerializerOptions options); + } + + /// + /// A key which is used to encrypt, or wrap, another key. + /// + public interface IKeyEncryptionKey + { + /// + /// The Id of the key used to perform cryptographic operations for the client. + /// + string KeyId { get; } + + /// + /// Encrypts the specified key using the specified algorithm. + /// + /// The key wrap algorithm used to encrypt the specified key. + /// The key to be encrypted. + /// A controlling the request lifetime. + /// The encrypted key bytes. + byte[] WrapKey(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken = default); + + /// + /// Encrypts the specified key using the specified algorithm. + /// + /// The key wrap algorithm used to encrypt the specified key. + /// The key to be encrypted. + /// A controlling the request lifetime. + /// The encrypted key bytes. + Task WrapKeyAsync(string algorithm, ReadOnlyMemory key, CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified encrypted key using the specified algorithm. + /// + /// The key wrap algorithm which was used to encrypt the specified encrypted key. + /// The encrypted key to be decrypted. + /// A controlling the request lifetime. + /// The decrypted key bytes. + byte[] UnwrapKey(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken = default); + + /// + /// Decrypts the specified encrypted key using the specified algorithm. + /// + /// The key wrap algorithm which was used to encrypt the specified encrypted key. + /// The encrypted key to be decrypted. + /// A controlling the request lifetime. + /// The decrypted key bytes. + Task UnwrapKeyAsync(string algorithm, ReadOnlyMemory encryptedKey, CancellationToken cancellationToken = default); + } + + // + // Summary: + // A JSON Web Key (JWK) is a JavaScript Object Notation (JSON) data structure that + // represents a cryptographic key. For more information, see http://tools.ietf.org/html/draft-ietf-jose-json-web-key-18. + //public class JsonWebKey //: IJsonDeserializable, IJsonSerializable + //{ + // private const string KeyIdPropertyName = "kid"; + // private const string KeyTypePropertyName = "kty"; + // private const string KeyOpsPropertyName = "key_ops"; + // private const string CurveNamePropertyName = "crv"; + // private const string NPropertyName = "n"; + // private const string EPropertyName = "e"; + // private const string DPPropertyName = "dp"; + // private const string DQPropertyName = "dq"; + // private const string QIPropertyName = "qi"; + // private const string PPropertyName = "p"; + // private const string QPropertyName = "q"; + // private const string XPropertyName = "x"; + // private const string YPropertyName = "y"; + // private const string DPropertyName = "d"; + // private const string KPropertyName = "k"; + // private const string TPropertyName = "key_hsm"; + + // private static readonly JsonEncodedText s_keyTypePropertyNameBytes = JsonEncodedText.Encode("kty"); + // private static readonly JsonEncodedText s_keyOpsPropertyNameBytes = JsonEncodedText.Encode("key_ops"); + // private static readonly JsonEncodedText s_curveNamePropertyNameBytes = JsonEncodedText.Encode("crv"); + // private static readonly JsonEncodedText s_nPropertyNameBytes = JsonEncodedText.Encode("n"); + // private static readonly JsonEncodedText s_ePropertyNameBytes = JsonEncodedText.Encode("e"); + // private static readonly JsonEncodedText s_dPPropertyNameBytes = JsonEncodedText.Encode("dp"); + // private static readonly JsonEncodedText s_dQPropertyNameBytes = JsonEncodedText.Encode("dq"); + // private static readonly JsonEncodedText s_qIPropertyNameBytes = JsonEncodedText.Encode("qi"); + // private static readonly JsonEncodedText s_pPropertyNameBytes = JsonEncodedText.Encode("p"); + // private static readonly JsonEncodedText s_qPropertyNameBytes = JsonEncodedText.Encode("q"); + // private static readonly JsonEncodedText s_xPropertyNameBytes = JsonEncodedText.Encode("x"); + // private static readonly JsonEncodedText s_yPropertyNameBytes = JsonEncodedText.Encode("y"); + // private static readonly JsonEncodedText s_dPropertyNameBytes = JsonEncodedText.Encode("d"); + + // private static readonly JsonEncodedText s_kPropertyNameBytes = JsonEncodedText.Encode("k"); + + // private static readonly JsonEncodedText s_tPropertyNameBytes = JsonEncodedText.Encode("key_hsm"); + + // private static readonly KeyOperation[] s_aesKeyOperation = new KeyOperation[4] + // { + // KeyOperation.Encrypt, + // KeyOperation.Decrypt, + // KeyOperation.WrapKey, + // KeyOperation.UnwrapKey + // }; + + // private static readonly KeyOperation[] s_rSAPublicKeyOperation = new KeyOperation[3] + // { + // KeyOperation.Encrypt, + // KeyOperation.Verify, + // KeyOperation.WrapKey + // }; + + // private static readonly KeyOperation[] s_rSAPrivateKeyOperation = new KeyOperation[6] + // { + // KeyOperation.Encrypt, + // KeyOperation.Decrypt, + // KeyOperation.Sign, + // KeyOperation.Verify, + // KeyOperation.WrapKey, + // KeyOperation.UnwrapKey + // }; + + // private static readonly KeyOperation[] s_eCPublicKeyOperation = new KeyOperation[1] { KeyOperation.Sign }; + + // private static readonly KeyOperation[] s_eCPrivateKeyOperation = new KeyOperation[2] + // { + // KeyOperation.Sign, + // KeyOperation.Verify + // }; + + // private readonly IList _keyOps; + + // private static readonly byte[] s_zeroBuffer = new byte[1]; + + // // + // // Summary: + // // Gets the identifier of the key. This is not limited to a System.Uri. + // public string Id { get; internal set; } + + // // + // // Summary: + // // Gets the Azure.Security.KeyVault.Keys.JsonWebKey.KeyType for this Azure.Security.KeyVault.Keys.JsonWebKey. + // public KeyType KeyType { get; internal set; } + + // // + // // Summary: + // // Gets a list of Azure.Security.KeyVault.Keys.KeyOperation values supported by + // // this key. + // public IReadOnlyCollection KeyOps { get; } + + // // + // // Summary: + // // Gets the RSA modulus. + // public byte[] N { get; internal set; } + + // // + // // Summary: + // // Gets RSA public exponent. + // public byte[] E { get; internal set; } + + // // + // // Summary: + // // Gets the RSA private key parameter. + // public byte[] DP { get; internal set; } + + // // + // // Summary: + // // Gets the RSA private key parameter. + // public byte[] DQ { get; internal set; } + + // // + // // Summary: + // // Gets the RSA private key parameter. + // public byte[] QI { get; internal set; } + + // // + // // Summary: + // // Gets the RSA secret prime. + // public byte[] P { get; internal set; } + + // // + // // Summary: + // // Gets the RSA secret prime. + // public byte[] Q { get; internal set; } + + // // + // // Summary: + // // Gets the name of the elliptical curve. + // public KeyCurveName? CurveName { get; internal set; } + + // // + // // Summary: + // // Gets the X coordinate of the elliptic curve point. + // public byte[] X { get; internal set; } + + // // + // // Summary: + // // Gets the Y coordinate for the elliptic curve point. + // public byte[] Y { get; internal set; } + + // // + // // Summary: + // // Gets the RSA private exponent or EC private key. + // public byte[] D { get; internal set; } + + // // + // // Summary: + // // Gets the symmetric key. + // public byte[] K { get; internal set; } + + // // + // // Summary: + // // Gets the HSM token used with "Bring Your Own Key". + // public byte[] T { get; internal set; } + + // internal bool HasPrivateKey + // { + // get + // { + // if (KeyType == KeyType.Rsa || KeyType == KeyType.Ec || KeyType == KeyType.RsaHsm || KeyType == KeyType.EcHsm) + // { + // return D != null; + // } + + // if (KeyType == KeyType.Oct) + // { + // return K != null; + // } + + // return false; + // } + // } + + // internal JsonWebKey() + // : this((IEnumerable)null) + // { + // } + + // internal JsonWebKey(IEnumerable keyOps) + // { + // _keyOps = ((keyOps == null) ? new List() : new List(keyOps)); + // KeyOps = new ReadOnlyCollection(_keyOps); + // } + + // // + // // Summary: + // // Initializes a new instance of the Azure.Security.KeyVault.Keys.JsonWebKey class + // // using type Azure.Security.KeyVault.Keys.KeyType.Oct. + // // + // // Parameters: + // // aesProvider: + // // An System.Security.Cryptography.Aes provider. + // // + // // keyOps: + // // Optional list of supported Azure.Security.KeyVault.Keys.KeyOperation values. + // // If null, the default for the key type is used, including: Azure.Security.KeyVault.Keys.KeyOperation.Encrypt, + // // Azure.Security.KeyVault.Keys.KeyOperation.Decrypt, Azure.Security.KeyVault.Keys.KeyOperation.WrapKey, + // // and Azure.Security.KeyVault.Keys.KeyOperation.UnwrapKey. + // // + // // Exceptions: + // // T:System.ArgumentNullException: + // // aesProvider is null. + // public JsonWebKey(Aes aesProvider, IEnumerable keyOps = null) + // { + // //Azure.Core.Argument.AssertNotNull(aesProvider, "aesProvider"); + // _keyOps = new List(keyOps ?? s_aesKeyOperation); + // KeyOps = new ReadOnlyCollection(_keyOps); + // KeyType = KeyType.Oct; + // K = aesProvider.Key; + // } + + // // + // // Summary: + // // Initializes a new instance of the Azure.Security.KeyVault.Keys.JsonWebKey class + // // using type Azure.Security.KeyVault.Keys.KeyType.Ec. + // // + // // Parameters: + // // ecdsa: + // // An System.Security.Cryptography.ECDsa provider. + // // + // // includePrivateParameters: + // // Whether to include the private key. + // // + // // keyOps: + // // Optional list of supported Azure.Security.KeyVault.Keys.KeyOperation values. + // // If null, the default for the key type is used, including: Azure.Security.KeyVault.Keys.KeyOperation.Sign, + // // and Azure.Security.KeyVault.Keys.KeyOperation.Decrypt if includePrivateParameters + // // is true. + // // + // // Exceptions: + // // T:System.ArgumentNullException: + // // ecdsa is null. + // // + // // T:System.InvalidOperationException: + // // The elliptic curve name is invalid. + // public JsonWebKey(ECDsa ecdsa, bool includePrivateParameters = false, IEnumerable keyOps = null) + // { + // //Azure.Core.Argument.AssertNotNull(ecdsa, "ecdsa"); + // _keyOps = new List(keyOps ?? (includePrivateParameters ? s_eCPrivateKeyOperation : s_eCPublicKeyOperation)); + // KeyOps = new ReadOnlyCollection(_keyOps); + // Initialize(ecdsa, includePrivateParameters); + // } + + // // + // // Summary: + // // Initializes a new instance of the Azure.Security.KeyVault.Keys.JsonWebKey class + // // using type Azure.Security.KeyVault.Keys.KeyType.Rsa. + // // + // // Parameters: + // // rsaProvider: + // // An System.Security.Cryptography.RSA provider. + // // + // // includePrivateParameters: + // // Whether to include the private key. + // // + // // keyOps: + // // Optional list of supported Azure.Security.KeyVault.Keys.KeyOperation values. + // // If null, the default for the key type is used, including: Azure.Security.KeyVault.Keys.KeyOperation.Encrypt, + // // Azure.Security.KeyVault.Keys.KeyOperation.Verify, and Azure.Security.KeyVault.Keys.KeyOperation.WrapKey; + // // and Azure.Security.KeyVault.Keys.KeyOperation.Decrypt, Azure.Security.KeyVault.Keys.KeyOperation.Sign, + // // and Azure.Security.KeyVault.Keys.KeyOperation.UnwrapKey if includePrivateParameters + // // is true. + // // + // // Exceptions: + // // T:System.ArgumentNullException: + // // rsaProvider is null. + // public JsonWebKey(RSA rsaProvider, bool includePrivateParameters = false, IEnumerable keyOps = null) + // { + // //Azure.Core.Argument.AssertNotNull(rsaProvider, "rsaProvider"); + // _keyOps = new List(keyOps ?? (includePrivateParameters ? s_rSAPrivateKeyOperation : s_rSAPublicKeyOperation)); + // KeyOps = new ReadOnlyCollection(_keyOps); + // KeyType = KeyType.Rsa; + // RSAParameters rSAParameters = rsaProvider.ExportParameters(includePrivateParameters); + // E = rSAParameters.Exponent; + // N = rSAParameters.Modulus; + // D = rSAParameters.D; + // DP = rSAParameters.DP; + // DQ = rSAParameters.DQ; + // P = rSAParameters.P; + // Q = rSAParameters.Q; + // QI = rSAParameters.InverseQ; + // } + + // // + // // Summary: + // // Converts this Azure.Security.KeyVault.Keys.JsonWebKey of type Azure.Security.KeyVault.Keys.KeyType.Oct + // // to an System.Security.Cryptography.Aes object. + // // + // // Returns: + // // An System.Security.Cryptography.Aes object. + // // + // // Exceptions: + // // T:System.InvalidOperationException: + // // This key is not of type Azure.Security.KeyVault.Keys.KeyType.Oct or Azure.Security.KeyVault.Keys.JsonWebKey.K + // // is null. + // public Aes ToAes() + // { + // if (KeyType != KeyType.Oct) + // { + // throw new InvalidOperationException("key is not an Oct key"); + // } + + // if (K == null) + // { + // throw new InvalidOperationException("key does not contain a value"); + // } + + // Aes aes = Aes.Create(); + // aes.Key = K; + // return aes; + // } + + // // + // // Summary: + // // Converts this Azure.Security.KeyVault.Keys.JsonWebKey of type Azure.Security.KeyVault.Keys.KeyType.Ec + // // or Azure.Security.KeyVault.Keys.KeyType.EcHsm to an System.Security.Cryptography.ECDsa + // // object. + // // + // // Parameters: + // // includePrivateParameters: + // // Whether to include private parameters. + // // + // // Returns: + // // An System.Security.Cryptography.ECDsa object. + // // + // // Exceptions: + // // T:System.InvalidOperationException: + // // This key is not of type Azure.Security.KeyVault.Keys.KeyType.Ec or Azure.Security.KeyVault.Keys.KeyType.EcHsm, + // // or one or more key parameters are invalid. + // public ECDsa ToECDsa(bool includePrivateParameters = false) + // { + // return ToECDsa(includePrivateParameters, throwIfNotSupported: true); + // } + + // internal ECDsa ToECDsa(bool includePrivateParameters, bool throwIfNotSupported) + // { + // if (KeyType != KeyType.Ec && KeyType != KeyType.EcHsm) + // { + // throw new InvalidOperationException("key is not an Ec or EcHsm type"); + // } + + // ValidateKeyParameter("X", X); + // ValidateKeyParameter("Y", Y); + // return Convert(includePrivateParameters, throwIfNotSupported); + // } + + // // + // // Summary: + // // Converts this Azure.Security.KeyVault.Keys.JsonWebKey of type Azure.Security.KeyVault.Keys.KeyType.Rsa + // // or Azure.Security.KeyVault.Keys.KeyType.RsaHsm to an System.Security.Cryptography.RSA + // // object. + // // + // // Parameters: + // // includePrivateParameters: + // // Whether to include private parameters. + // // + // // Returns: + // // An System.Security.Cryptography.RSA object. + // // + // // Exceptions: + // // T:System.InvalidOperationException: + // // This key is not of type Azure.Security.KeyVault.Keys.KeyType.Rsa or Azure.Security.KeyVault.Keys.KeyType.RsaHsm, + // // or one or more key parameters are invalid. + // public RSA ToRSA(bool includePrivateParameters = false) + // { + // if (KeyType != KeyType.Rsa && KeyType != KeyType.RsaHsm) + // { + // throw new InvalidOperationException("key is not an Rsa or RsaHsm type"); + // } + + // ValidateKeyParameter("E", E); + // ValidateKeyParameter("N", N); + // RSAParameters rSAParameters = default(RSAParameters); + // rSAParameters.Exponent = E; + // rSAParameters.Modulus = TrimBuffer(N); + // RSAParameters parameters = rSAParameters; + // if (includePrivateParameters && HasPrivateKey) + // { + // int num = parameters.Modulus!.Length; + // parameters.D = ForceBufferLength("D", D, num); + // num >>= 1; + // parameters.DP = ForceBufferLength("DP", DP, num); + // parameters.DQ = ForceBufferLength("DQ", DQ, num); + // parameters.P = ForceBufferLength("P", P, num); + // parameters.Q = ForceBufferLength("Q", Q, num); + // parameters.InverseQ = ForceBufferLength("QI", QI, num); + // } + + // RSA rSA = RSA.Create(); + // rSA.ImportParameters(parameters); + // return rSA; + // } + + // internal void ReadProperties(JsonElement json) + // { + // foreach (JsonProperty item in json.EnumerateObject()) + // { + // switch (item.Name) + // { + // case "kid": + // Id = item.Value.GetString(); + // break; + // case "kty": + // KeyType = item.Value.GetString(); + // break; + // case "key_ops": + // foreach (JsonElement item2 in item.Value.EnumerateArray()) + // { + // _keyOps.Add(item2.ToString()); + // } + + // break; + // case "crv": + // CurveName = item.Value.GetString(); + // break; + // case "n": + // N = Base64Url.Decode(item.Value.GetString()); + // break; + // case "e": + // E = Base64Url.Decode(item.Value.GetString()); + // break; + // case "dp": + // DP = Base64Url.Decode(item.Value.GetString()); + // break; + // case "dq": + // DQ = Base64Url.Decode(item.Value.GetString()); + // break; + // case "qi": + // QI = Base64Url.Decode(item.Value.GetString()); + // break; + // case "p": + // P = Base64Url.Decode(item.Value.GetString()); + // break; + // case "q": + // Q = Base64Url.Decode(item.Value.GetString()); + // break; + // case "x": + // X = Base64Url.Decode(item.Value.GetString()); + // break; + // case "y": + // Y = Base64Url.Decode(item.Value.GetString()); + // break; + // case "d": + // D = Base64Url.Decode(item.Value.GetString()); + // break; + // case "k": + // K = Base64Url.Decode(item.Value.GetString()); + // break; + // case "key_hsm": + // T = Base64Url.Decode(item.Value.GetString()); + // break; + // } + // } + // } + + // internal void WriteProperties(Utf8JsonWriter json) + // { + // if (KeyType != default(KeyType)) + // { + // json.WriteString(s_keyTypePropertyNameBytes, KeyType.ToString()); + // } + + // if (KeyOps != null) + // { + // json.WriteStartArray(s_keyOpsPropertyNameBytes); + // foreach (KeyOperation keyOp in KeyOps) + // { + // json.WriteStringValue(keyOp.ToString()); + // } + + // json.WriteEndArray(); + // } + + // if (CurveName.HasValue) + // { + // json.WriteString(s_curveNamePropertyNameBytes, CurveName.Value.ToString()); + // } + + // if (N != null) + // { + // json.WriteString(s_nPropertyNameBytes, Base64Url.Encode(N)); + // } + + // if (E != null) + // { + // json.WriteString(s_ePropertyNameBytes, Base64Url.Encode(E)); + // } + + // if (DP != null) + // { + // json.WriteString(s_dPPropertyNameBytes, Base64Url.Encode(DP)); + // } + + // if (DQ != null) + // { + // json.WriteString(s_dQPropertyNameBytes, Base64Url.Encode(DQ)); + // } + + // if (QI != null) + // { + // json.WriteString(s_qIPropertyNameBytes, Base64Url.Encode(QI)); + // } + + // if (P != null) + // { + // json.WriteString(s_pPropertyNameBytes, Base64Url.Encode(P)); + // } + + // if (Q != null) + // { + // json.WriteString(s_qPropertyNameBytes, Base64Url.Encode(Q)); + // } + + // if (X != null) + // { + // json.WriteString(s_xPropertyNameBytes, Base64Url.Encode(X)); + // } + + // if (Y != null) + // { + // json.WriteString(s_yPropertyNameBytes, Base64Url.Encode(Y)); + // } + // if (D != null) + // { + // json.WriteString(s_dPropertyNameBytes, Base64Url.Encode(D)); + // } + // if (K != null) + // { + // json.WriteString(s_kPropertyNameBytes, Base64Url.Encode(K)); + // } + // if (T != null) + // { + // json.WriteString(s_tPropertyNameBytes, Base64Url.Encode(T)); + // } + // } + // //void ReadProperties(JsonElement json) // IJsonDeserializable + // //{ + // // ReadProperties(json); + // //} + // //void WriteProperties(Utf8JsonWriter json) // IJsonSerializable + // //{ + // // WriteProperties(json); + // //} + // private static byte[] ForceBufferLength(string name, byte[] value, int requiredLengthInBytes) + // { + // if (value == null || value.Length == 0) + // { + // throw new InvalidOperationException("key parameter " + name + " is null or empty"); + // } + // if (value.Length == requiredLengthInBytes) + // { + // return value; + // } + // if (value.Length < requiredLengthInBytes) + // { + // byte[] array = new byte[requiredLengthInBytes]; + // Array.Copy(value, 0, array, requiredLengthInBytes - value.Length, value.Length); + // return array; + // } + // int num = value.Length - requiredLengthInBytes; + // for (int i = 0; i < num; i++) + // { + // if (value[i] != 0) + // { + // throw new InvalidOperationException($"key parameter {name} is too long: expected at most {requiredLengthInBytes} bytes, but found {value.Length - i} bytes"); + // } + // } + // byte[] array2 = new byte[requiredLengthInBytes]; + // Array.Copy(value, value.Length - requiredLengthInBytes, array2, 0, requiredLengthInBytes); + // return array2; + // } + // private static byte[] TrimBuffer(byte[] value) + // { + // if (value == null || value.Length <= 1 || value[0] != 0) + // { + // return value; + // } + // for (int i = 1; i < value.Length; i++) + // { + // if (value[i] != 0) + // { + // byte[] array = new byte[value.Length - i]; + // Array.Copy(value, i, array, 0, array.Length); + // return array; + // } + // } + // return s_zeroBuffer; + // } + // private static void ValidateKeyParameter(string name, byte[] value) + // { + // if (value != null) + // { + // for (int i = 0; i < value.Length; i++) + // { + // if (value[i] != 0) + // { + // return; + // } + // } + // } + // throw new InvalidOperationException("key parameter " + name + " is null or zeros"); + // } + // [MethodImpl(MethodImplOptions.NoInlining)] + // private void Initialize(ECDsa ecdsa, bool includePrivateParameters) + // { + // KeyType = KeyType.Ec; + // ECParameters eCParameters = ecdsa.ExportParameters(includePrivateParameters); + // CurveName = KeyCurveName.FromOid(eCParameters.Curve.Oid, ecdsa.KeySize).ToString() ?? throw new InvalidOperationException("elliptic curve name is invalid"); + // D = eCParameters.D; + // X = eCParameters.Q.X; + // Y = eCParameters.Q.Y; + // } + // [MethodImpl(MethodImplOptions.NoInlining)] + // private ECDsa Convert(bool includePrivateParameters, bool throwIfNotSupported) + // { + // if (!CurveName.HasValue) + // { + // if (throwIfNotSupported) + // { + // throw new InvalidOperationException("missing required curve name"); + // } + // return null; + // } + // KeyCurveName value = CurveName.Value; + // int keyParameterSize = value.KeyParameterSize; + // if (keyParameterSize <= 0) + // { + // if (throwIfNotSupported) + // { + // throw new InvalidOperationException("invalid curve name: " + CurveName.ToString()); + // } + // return null; + // } + // ECParameters eCParameters = default(ECParameters); + // eCParameters.Curve = ECCurve.CreateFromOid(value.Oid); + // eCParameters.Q = new ECPoint + // { + // X = ForceBufferLength("X", X, keyParameterSize), + // Y = ForceBufferLength("Y", Y, keyParameterSize) + // }; + // ECParameters parameters = eCParameters; + // if (includePrivateParameters && HasPrivateKey) + // { + // parameters.D = ForceBufferLength("D", D, keyParameterSize); + // } + // ECDsa eCDsa = ECDsa.Create(); + // try + // { + // eCDsa.ImportParameters(parameters); + // return eCDsa; + // } + // catch when (!throwIfNotSupported) + // { + // eCDsa.Dispose(); + // return null; + // } + // } + //} + + + public enum KeyVaultSignatureAlgorithm + { + RSAPkcs15, + ECDsa + } + + public static class SignatureAlgorithmTranslator + { + public static SignatureAlgorithm SignatureAlgorithmToJwsAlgId(KeyVaultSignatureAlgorithm signatureAlgorithm, HashAlgorithmName hashAlgorithmName) + { + switch (signatureAlgorithm) + { + case KeyVaultSignatureAlgorithm.RSAPkcs15: + if (hashAlgorithmName == HashAlgorithmName.SHA1) + { + return new SignatureAlgorithm("RSNULL"); + } + + if (hashAlgorithmName == HashAlgorithmName.SHA256) + { + return SignatureAlgorithm.RS256; + } + + if (hashAlgorithmName == HashAlgorithmName.SHA384) + { + return SignatureAlgorithm.RS384; + } + + if (hashAlgorithmName == HashAlgorithmName.SHA512) + { + return SignatureAlgorithm.RS512; + } + + break; + case KeyVaultSignatureAlgorithm.ECDsa: + if (hashAlgorithmName == HashAlgorithmName.SHA256) + { + return SignatureAlgorithm.ES256; + } + + if (hashAlgorithmName == HashAlgorithmName.SHA384) + { + return SignatureAlgorithm.ES384; + } + + if (hashAlgorithmName == HashAlgorithmName.SHA512) + { + return SignatureAlgorithm.ES512; + } + + break; + } + + throw new NotSupportedException("The algorithm specified is not supported."); + } + } + + // + // Summary: + // An algorithm used for signing and verification. + public readonly struct SignatureAlgorithm : IEquatable + { + internal const string RS256Value = "RS256"; + + internal const string RS384Value = "RS384"; + + internal const string RS512Value = "RS512"; + + internal const string PS256Value = "PS256"; + + internal const string PS384Value = "PS384"; + + internal const string PS512Value = "PS512"; + + internal const string ES256Value = "ES256"; + + internal const string ES384Value = "ES384"; + + internal const string ES512Value = "ES512"; + + internal const string ES256KValue = "ES256K"; + + private readonly string _value; + + // + // Summary: + // Gets an RSA SHA-256 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm RS256 { get; } = new SignatureAlgorithm("RS256"); + + + // + // Summary: + // Gets an RSA SHA-384 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm RS384 { get; } = new SignatureAlgorithm("RS384"); + + + // + // Summary: + // Gets an RSA SHA-512 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm RS512 { get; } = new SignatureAlgorithm("RS512"); + + + // + // Summary: + // Gets an RSASSA-PSS using SHA-256 and MGF1 with SHA-256 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm PS256 { get; } = new SignatureAlgorithm("PS256"); + + + // + // Summary: + // Gets an RSASSA-PSS using SHA-384 and MGF1 with SHA-384 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm PS384 { get; } = new SignatureAlgorithm("PS384"); + + + // + // Summary: + // Gets an RSASSA-PSS using SHA-512 and MGF1 with SHA-512 Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm PS512 { get; } = new SignatureAlgorithm("PS512"); + + + // + // Summary: + // Gets an ECDSA with a P-256 curve Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm ES256 { get; } = new SignatureAlgorithm("ES256"); + + + // + // Summary: + // Gets an ECDSA with a P-384 curve Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm ES384 { get; } = new SignatureAlgorithm("ES384"); + + + // + // Summary: + // Gets an ECDSA with a P-521 curve Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm ES512 { get; } = new SignatureAlgorithm("ES512"); + + + // + // Summary: + // Gets an ECDSA with a secp256k1 curve Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + public static SignatureAlgorithm ES256K { get; } = new SignatureAlgorithm("ES256K"); + + + // + // Summary: + // Initializes a new instance of the Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm + // structure. + // + // Parameters: + // value: + // The string value of the instance. + public SignatureAlgorithm(string value) + { + _value = value ?? throw new ArgumentNullException("value"); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm + // values are the same. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm to compare. + // + // Returns: + // True if left and right are the same; otherwise, false. + public static bool operator ==(SignatureAlgorithm left, SignatureAlgorithm right) + { + return left.Equals(right); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm + // values are different. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm to compare. + // + // Returns: + // True if left and right are different; otherwise, false. + public static bool operator !=(SignatureAlgorithm left, SignatureAlgorithm right) + { + return !left.Equals(right); + } + + // + // Summary: + // Converts a string to a Azure.Security.KeyVault.Keys.Cryptography.SignatureAlgorithm. + // + // Parameters: + // value: + // The string value to convert. + public static implicit operator SignatureAlgorithm(string value) + { + return new SignatureAlgorithm(value); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + if (obj is SignatureAlgorithm) + { + SignatureAlgorithm other = (SignatureAlgorithm)obj; + return Equals(other); + } + + return false; + } + + public bool Equals(SignatureAlgorithm other) + { + return string.Equals(_value, other._value, StringComparison.Ordinal); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return _value?.GetHashCode() ?? 0; + } + + public override string ToString() + { + return _value; + } + + internal HashAlgorithm GetHashAlgorithm() + { + switch (_value) + { + case "RS256": + case "PS256": + case "ES256": + case "ES256K": + return SHA256.Create(); + case "RS384": + case "PS384": + case "ES384": + return SHA384.Create(); + case "RS512": + case "PS512": + case "ES512": + return SHA512.Create(); + default: + throw new InvalidOperationException("Invalid Algorithm"); + } + } + + internal HashAlgorithmName GetHashAlgorithmName() + { + switch (_value) + { + case "RS256": + case "PS256": + case "ES256": + case "ES256K": + return HashAlgorithmName.SHA256; + case "RS384": + case "PS384": + case "ES384": + return HashAlgorithmName.SHA384; + case "RS512": + case "PS512": + case "ES512": + return HashAlgorithmName.SHA512; + default: + return default(HashAlgorithmName); + } + } + + internal KeyCurveName GetEcKeyCurveName() + { + return _value switch + { + "ES256" => KeyCurveName.P256, + "ES256K" => KeyCurveName.P256K, + "ES384" => KeyCurveName.P384, + "ES512" => KeyCurveName.P521, + _ => KeyCurveName.s_default, + }; + } + + internal RSASignaturePadding GetRsaSignaturePadding() + { + switch (_value) + { + case "RS256": + case "RS384": + case "RS512": + return RSASignaturePadding.Pkcs1; + case "PS256": + case "PS384": + case "PS512": + return RSASignaturePadding.Pss; + default: + return null; + } + } + } + + // + // Summary: + // Elliptic Curve Cryptography (ECC) curve names. + public readonly struct KeyCurveName : IEquatable + { + private const string P256Value = "P-256"; + + private const string P256KValue = "P-256K"; + + private const string P384Value = "P-384"; + + private const string P521Value = "P-521"; + + private const string P256OidValue = "1.2.840.10045.3.1.7"; + + private const string P256KOidValue = "1.3.132.0.10"; + + private const string P384OidValue = "1.3.132.0.34"; + + private const string P521OidValue = "1.3.132.0.35"; + + private readonly string _value; + + internal static readonly KeyCurveName s_default = default(KeyCurveName); + + // + // Summary: + // Gets the NIST P-256 elliptic curve, AKA SECG curve SECP256R1 For more information, + // see https://docs.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates#curve-types. + public static KeyCurveName P256 { get; } = new KeyCurveName("P-256"); + + + // + // Summary: + // Gets the SECG SECP256K1 elliptic curve. For more information, see https://docs.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates#curve-types. + public static KeyCurveName P256K { get; } = new KeyCurveName("P-256K"); + + + // + // Summary: + // Gets the NIST P-384 elliptic curve, AKA SECG curve SECP384R1. For more information, + // see https://docs.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates#curve-types. + public static KeyCurveName P384 { get; } = new KeyCurveName("P-384"); + + + // + // Summary: + // Gets the NIST P-521 elliptic curve, AKA SECG curve SECP521R1. For more information, + // see https://docs.microsoft.com/en-us/azure/key-vault/about-keys-secrets-and-certificates#curve-types. + public static KeyCurveName P521 { get; } = new KeyCurveName("P-521"); + + + internal bool IsSupported + { + get + { + switch (_value) + { + case "P-256": + case "P-256K": + case "P-384": + case "P-521": + return true; + default: + return false; + } + } + } + + internal int KeyParameterSize => _value switch + { + "P-256" => 32, + "P-256K" => 32, + "P-384" => 48, + "P-521" => 66, + _ => 0, + }; + + internal int KeySize => _value switch + { + "P-256" => 256, + "P-256K" => 256, + "P-384" => 384, + "P-521" => 521, + _ => 0, + }; + + internal Oid Oid => _value switch + { + "P-256" => new Oid("1.2.840.10045.3.1.7"), + "P-256K" => new Oid("1.3.132.0.10"), + "P-384" => new Oid("1.3.132.0.34"), + "P-521" => new Oid("1.3.132.0.35"), + _ => null, + }; + + // + // Summary: + // Initializes a new instance of the Azure.Security.KeyVault.Keys.KeyCurveName structure. + // + // Parameters: + // value: + // The string value of the instance. + public KeyCurveName(string value) + { + _value = value ?? throw new ArgumentNullException("value"); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyCurveName values are the same. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyCurveName to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyCurveName to compare. + // + // Returns: + // True if left and right are the same; otherwise, false. + public static bool operator ==(KeyCurveName left, KeyCurveName right) + { + return left.Equals(right); + } + + // + // Summary: + // Determines if two Azure.Security.KeyVault.Keys.KeyCurveName values are different. + // + // Parameters: + // left: + // The first Azure.Security.KeyVault.Keys.KeyCurveName to compare. + // + // right: + // The second Azure.Security.KeyVault.Keys.KeyCurveName to compare. + // + // Returns: + // True if left and right are different; otherwise, false. + public static bool operator !=(KeyCurveName left, KeyCurveName right) + { + return !left.Equals(right); + } + + // + // Summary: + // Converts a string to a Azure.Security.KeyVault.Keys.KeyCurveName. + // + // Parameters: + // value: + // The string value to convert. + public static implicit operator KeyCurveName(string value) + { + return new KeyCurveName(value); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override bool Equals(object obj) + { + if (obj is KeyCurveName) + { + KeyCurveName other = (KeyCurveName)obj; + return Equals(other); + } + + return false; + } + + public bool Equals(KeyCurveName other) + { + return string.Equals(_value, other._value, StringComparison.Ordinal); + } + + //[EditorBrowsable(EditorBrowsableState.Never)] + public override int GetHashCode() + { + return _value?.GetHashCode() ?? 0; + } + + public override string ToString() + { + return _value; + } + + internal static KeyCurveName FromOid(Oid oid, int keySize = 0) + { + if (!string.IsNullOrEmpty(oid?.Value)) + { + if (string.Equals(oid.Value, "1.3.132.0.35", StringComparison.Ordinal)) + { + return P521; + } + + if (string.Equals(oid.Value, "1.3.132.0.34", StringComparison.Ordinal)) + { + return P384; + } + + if (string.Equals(oid.Value, "1.3.132.0.10", StringComparison.Ordinal)) + { + return P256K; + } + + if (string.Equals(oid.Value, "1.2.840.10045.3.1.7", StringComparison.Ordinal)) + { + return P256; + } + } + + if (!string.IsNullOrEmpty(oid?.FriendlyName)) + { + switch (keySize) + { + case 521: + if (string.Equals(oid.FriendlyName, "nistP521", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "secp521r1", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "ECDSA_P521", StringComparison.OrdinalIgnoreCase)) + { + return P521; + } + + break; + case 384: + if (string.Equals(oid.FriendlyName, "nistP384", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "secp384r1", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "ECDSA_P384", StringComparison.OrdinalIgnoreCase)) + { + return P384; + } + + break; + case 256: + if (string.Equals(oid.FriendlyName, "secp256k1", StringComparison.OrdinalIgnoreCase)) + { + return P256K; + } + + if (string.Equals(oid.FriendlyName, "nistP256", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "secp256r1", StringComparison.OrdinalIgnoreCase) || string.Equals(oid.FriendlyName, "ECDSA_P256", StringComparison.OrdinalIgnoreCase)) + { + return P256; + } + + break; + } + } + + return s_default; + } + } + +} diff --git a/tools/CustomBuildTool/AzureSignTool/LICENSE b/tools/CustomBuildTool/AzureSignTool/LICENSE new file mode 100644 index 000000000000..a5cce15d00fe --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/LICENSE @@ -0,0 +1,21 @@ +MIT License + +Copyright (c) 2017 Kevin Jones and Oren Novotny + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in all +copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE +SOFTWARE. diff --git a/tools/CustomBuildTool/AzureSignTool/MemoryCertificateStore.cs b/tools/CustomBuildTool/AzureSignTool/MemoryCertificateStore.cs new file mode 100644 index 000000000000..1cc9847770a4 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/MemoryCertificateStore.cs @@ -0,0 +1,82 @@ +using Windows.Win32.Security.Cryptography; + +namespace CustomBuildTool +{ + internal unsafe sealed class MemoryCertificateStore : IDisposable + { + private HCERTSTORE _handle; + private readonly X509Store _store; + + private MemoryCertificateStore(HCERTSTORE handle) + { + _handle = handle; + try + { + _store = new X509Store((nint)_handle.Value); + } + catch + { + //We need to manually clean up the handle here. If we throw here for whatever reason, + //we'll leak the handle because we'll have a partially constructed object that won't get + //a finalizer called on or anything to dispose of. + FreeHandle(); + throw; + } + } + + public unsafe static MemoryCertificateStore Create() + { + byte[] buffer = Encoding.UTF8.GetBytes("Memory"); + GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned); + IntPtr address = handle.AddrOfPinnedObject(); + + try + { + var MemoryCertStore = PInvoke.CertOpenStore( + new PCSTR((byte*)address.ToPointer()), + 0, + (HCRYPTPROV_LEGACY)0, + 0, + null + ); + + return new MemoryCertificateStore(MemoryCertStore); + } + finally + { + handle.Free(); + } + } + + public void Close() => Dispose(true); + void IDisposable.Dispose() => Dispose(true); + ~MemoryCertificateStore() => Dispose(false); + + + public IntPtr Handle => _store.StoreHandle; + public void Add(X509Certificate2 certificate) => _store.Add(certificate); + public void Add(X509Certificate2Collection collection) => _store.AddRange(collection); + public X509Certificate2Collection Certificates => _store.Certificates; + + private void Dispose(bool disposing) + { + GC.SuppressFinalize(this); + + if (disposing) + { + _store.Dispose(); + } + + FreeHandle(); + } + + private unsafe void FreeHandle() + { + if (this._handle.Value != null) + { + PInvoke.CertCloseStore(this._handle, 0); + this._handle = default; + } + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/NativeMethods.cs b/tools/CustomBuildTool/AzureSignTool/NativeMethods.cs new file mode 100644 index 000000000000..f2933b438d66 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/NativeMethods.cs @@ -0,0 +1,120 @@ +using Windows.Win32.Security.Cryptography; + +namespace AzureSign.Core.Interop +{ + [Flags] + public enum SignerSignEx3Flags : uint + { + NONE = 0x0, + /// + /// Exclude page hashes when creating SIP indirect data for the PE file. This flag takes precedence over the SPC_INC_PE_PAGE_HASHES_FLAG flag. + /// + SPC_EXC_PE_PAGE_HASHES_FLAG = 0x10, + /// + /// This value is not supported. + /// + SPC_INC_PE_IMPORT_ADDR_TABLE_FLAG = 0x20, + /// + /// This value is not supported. + /// + SPC_INC_PE_DEBUG_INFO_FLAG = 0x40, + /// + /// This value is not supported. + /// + SPC_INC_PE_RESOURCES_FLAG = 0x80, + /// + /// Include page hashes when creating SIP indirect data for the PE file. + /// + SPC_INC_PE_PAGE_HASHES_FLAG = 0x100, + /// + /// + /// + SPC_DIGEST_GENERATE_FLAG = 0x200, + /// + /// Digest signing will be done. + /// + SPC_DIGEST_SIGN_FLAG = 0x400, // 0x800 + /// + /// The signature will be nested. + /// If you set this flag before any signature has been added, the generated signature will be added as the outer signature. + /// If you do not set this flag, the generated signature replaces the outer signature, deleting all inner signatures. + /// + SIG_APPEND = 0x1000, + /// + /// + /// + SIG_SEAL = 0x2000, + /// + /// Digest signing will be done. The caller’s digest signing function will select and return the code signing certificate which performed the signing operation. + /// + SPC_DIGEST_SIGN_EX_FLAG = 0x4000 + } + + [StructLayout(LayoutKind.Sequential)] + public struct CRYPTOAPI_BLOB + { + public uint cbData; + public IntPtr pbData; + } + + [StructLayout(LayoutKind.Sequential)] + internal unsafe struct SIGNER_SIGN_EX3_PARAMS + { + public SIGNER_SIGN_FLAGS dwFlags; + public SIGNER_SUBJECT_INFO* pSubjectInfo; + public SIGNER_CERT* pSignerCert; + public SIGNER_SIGNATURE_INFO* pSignatureInfo; + public IntPtr pProviderInfo; + public SIGNER_TIMESTAMP_FLAGS dwTimestampFlags; + public byte* pszTimestampAlgorithmOid; + public char* pwszHttpTimeStamp; + public IntPtr psRequest; + public SIGNER_DIGEST_SIGN_INFO_V1* pSignCallBack; + public SIGNER_CONTEXT** ppSignerContext; + public IntPtr pCryptoPolicy; + public IntPtr pReserved; + } + + [StructLayout(LayoutKind.Sequential)] + internal struct APPX_SIP_CLIENT_DATA + { + public unsafe SIGNER_SIGN_EX3_PARAMS* pSignerParams; // SIGNER_SIGN_EX2_PARAMS + public IntPtr pAppxSipState; // IUnknown* + } + + [StructLayout(LayoutKind.Sequential)] + public unsafe struct CRYPT_DATA_BLOB + { + public uint cbData; + public void* pbData; + } + + /// + /// The SIGNER_DIGEST_SIGN_INFO_V1 structure contains information about digest signing. + /// It contains a pointer to a digest signing function implemented within the provided dll. + /// + [StructLayout(LayoutKind.Sequential)] + public unsafe struct SIGNER_DIGEST_SIGN_INFO_V1 + { + public static readonly uint SizeOf = (uint)Marshal.SizeOf(); + + /// + /// The size, in bytes, of the structure. + /// + public uint Size; + /// + /// The pointer to a PFN_AUTHENTICODE_DIGEST_SIGN function. + /// + public IntPtr AuthenticodeDigestSign; + /// + /// Optional pointer to CRYPT_DATA_BLOB specifying metadata (opaque to SignerSignEx3). + /// + public CRYPT_DATA_BLOB* pMetadataBlob; + + public SIGNER_DIGEST_SIGN_INFO_V1(IntPtr Callback) + { + this.Size = (uint)Marshal.SizeOf(this); + this.AuthenticodeDigestSign = Callback; + } + } +} \ No newline at end of file diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVault.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVault.cs new file mode 100644 index 000000000000..4d3b3b24871d --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVault.cs @@ -0,0 +1,156 @@ +namespace RSAKeyVaultProvider +{ + /// + /// ECDsa implementation that uses Azure Key Vault + /// + public sealed class ECDsaKeyVault : ECDsa + { + readonly KeyVaultContext context; + ECDsa publicKey; + + /// + /// Creates a new ECDsaKeyVault instance + /// + /// Context with parameters + public ECDsaKeyVault(KeyVaultContext context) + { + if (!context.IsValid) + throw new ArgumentException("Must not be the default", nameof(context)); + + this.context = context; + publicKey = context.Key.ToECDsa(); + KeySizeValue = publicKey.KeySize; + LegalKeySizesValue = new[] { new KeySizes(publicKey.KeySize, publicKey.KeySize, 0) }; + } + + void CheckDisposed() + { + if (publicKey is null) + throw new ObjectDisposedException($"{nameof(ECDsaKeyVault)} is disposed."); + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + publicKey?.Dispose(); + publicKey = null; + } + + base.Dispose(disposing); + } + + public override byte[] SignHash(byte[] hash) + { + CheckDisposed(); + ValidateKeyDigestCombination(KeySize, hash.Length); + + // We know from ValidateKeyDigestCombination that the key size and hash size are matched up + // according to RFC 7518 Sect. 3.1. + if (KeySize == 256) + return context.SignDigest(hash, HashAlgorithmName.SHA256, KeyVaultSignatureAlgorithm.ECDsa); + if (KeySize == 384) + return context.SignDigest(hash, HashAlgorithmName.SHA384, KeyVaultSignatureAlgorithm.ECDsa); + if (KeySize == 521) //ES512 uses nistP521 + return context.SignDigest(hash, HashAlgorithmName.SHA512, KeyVaultSignatureAlgorithm.ECDsa); + + throw new ArgumentException("Digest length is not valid for the key size.", nameof(hash)); + } + + protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) + { + ValidateKeyDigestCombination(KeySize, hashAlgorithm); + + using (IncrementalHash hash = IncrementalHash.CreateHash(hashAlgorithm)) + { + hash.AppendData(data, offset, count); + return hash.GetHashAndReset(); + } + } + + /// + public override bool VerifyHash(byte[] hash, byte[] signature) + { + CheckDisposed(); + ValidateKeyDigestCombination(KeySize, hash.Length); + + return publicKey.VerifyHash(hash, signature); + } + + /// + public override ECParameters ExportParameters(bool includePrivateParameters) + { + if (includePrivateParameters) + throw new Exception("Private keys cannot be exported by this provider"); + + return publicKey.ExportParameters(false); + } + + /// + public override ECParameters ExportExplicitParameters(bool includePrivateParameters) + { + if (includePrivateParameters) + throw new Exception("Private keys cannot be exported by this provider"); + + return publicKey.ExportExplicitParameters(false); + } + + /// + /// Importing parameters is not supported. + /// + public override void ImportParameters(ECParameters parameters) => + throw new NotSupportedException(); + + /// + /// Key generation is not supported. + /// + public override void GenerateKey(ECCurve curve) => + throw new NotSupportedException(); + + /// + public override string ToXmlString(bool includePrivateParameters) + { + if (includePrivateParameters) + throw new Exception("Private keys cannot be exported by this provider"); + + return publicKey.ToXmlString(false); + } + + /// + /// Importing parameters from XML is not supported. + public override void FromXmlString(string xmlString) => + throw new NotSupportedException(); + + private static void ValidateKeyDigestCombination(int keySizeBits, int digestSizeBytes) + { + if (keySizeBits == 256 && digestSizeBytes == 32 || + keySizeBits == 384 && digestSizeBytes == 48 || + keySizeBits == 521 && digestSizeBytes == 64) + { + return; + } + + throw new NotSupportedException($"The key size '{keySizeBits}' is not valid for digest of size '{digestSizeBytes}' bytes."); + } + + private static void ValidateKeyDigestCombination(int keySizeBits, HashAlgorithmName hashAlgorithmName) + { + if (hashAlgorithmName != HashAlgorithmName.SHA256 && + hashAlgorithmName != HashAlgorithmName.SHA384 && + hashAlgorithmName != HashAlgorithmName.SHA512) + { + throw new NotSupportedException("The specified algorithm is not supported."); + } + + if (keySizeBits == 256 && hashAlgorithmName == HashAlgorithmName.SHA256 || + keySizeBits == 384 && hashAlgorithmName == HashAlgorithmName.SHA384 || + keySizeBits == 521 && hashAlgorithmName == HashAlgorithmName.SHA512) + { + return; + } + + throw new NotSupportedException($"The key size '{keySizeBits}' is not valid for digest algorithm '{hashAlgorithmName}'."); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVaultExtensions.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVaultExtensions.cs new file mode 100644 index 000000000000..f1d1e7bdde0c --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/ECDsaKeyVaultExtensions.cs @@ -0,0 +1,53 @@ +using Azure.Core; +using Azure.Security.KeyVault.Keys; + +namespace RSAKeyVaultProvider +{ + /// + /// Extensions for creating ECDsa from a Key Vault client. + /// + public static class ECDsaFactory + { + /// + /// Creates an ECDsa object + /// + /// + /// + /// + /// + public static ECDsa Create(TokenCredential credential, Uri keyId, JsonWebKey key) + { + if (credential is null) + throw new ArgumentNullException(nameof(credential)); + + if (keyId is null) + throw new ArgumentNullException(nameof(keyId)); + + if (key is null) + throw new ArgumentNullException(nameof(key)); + + return new ECDsaKeyVault(new KeyVaultContext(credential, keyId, key)); + } + + /// + /// Creates an ECDsa object + /// + /// + /// + /// + /// + public static ECDsa Create(TokenCredential credential, Uri keyId, X509Certificate2 publicCertificate) + { + if (credential is null) + throw new ArgumentNullException(nameof(credential)); + + if (keyId is null) + throw new ArgumentNullException(nameof(keyId)); + + if (publicCertificate is null) + throw new ArgumentNullException(nameof(publicCertificate)); + + return new ECDsaKeyVault(new KeyVaultContext(credential, keyId, publicCertificate)); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/EncryptionPaddingTranslator.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/EncryptionPaddingTranslator.cs new file mode 100644 index 000000000000..51c74a896938 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/EncryptionPaddingTranslator.cs @@ -0,0 +1,23 @@ +using Azure.Security.KeyVault.Keys.Cryptography; + +namespace RSAKeyVaultProvider +{ + static class EncryptionPaddingTranslator + { + public static EncryptionAlgorithm EncryptionPaddingToJwsAlgId(RSAEncryptionPadding padding) + { + switch (padding.Mode) + { + case RSAEncryptionPaddingMode.Pkcs1: + return EncryptionAlgorithm.Rsa15; + case RSAEncryptionPaddingMode.Oaep when padding.OaepHashAlgorithm == HashAlgorithmName.SHA1: + return EncryptionAlgorithm.RsaOaep; + case RSAEncryptionPaddingMode.Oaep when padding.OaepHashAlgorithm == HashAlgorithmName.SHA256: + return EncryptionAlgorithm.RsaOaep256; + default: + throw new NotSupportedException("The padding specified is not supported."); + + } + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/KeyVaultContext.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/KeyVaultContext.cs new file mode 100644 index 000000000000..9838353fd922 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/KeyVaultContext.cs @@ -0,0 +1,128 @@ +using Azure.Security.KeyVault.Keys; +using Azure.Security.KeyVault.Keys.Cryptography; +using Azure.Core; + +namespace RSAKeyVaultProvider +{ + /// + /// A signing context used for signing packages with Azure Key Vault Keys. + /// + public readonly struct KeyVaultContext + { + readonly CryptographyClient cryptographyClient; + + /// + /// Creates a new Key Vault context. + /// + public KeyVaultContext(TokenCredential credential, Uri keyId, JsonWebKey key) + { + if (credential is null) + { + throw new ArgumentNullException(nameof(credential)); + } + if (keyId is null) + { + throw new ArgumentNullException(nameof(keyId)); + } + if (key is null) + { + throw new ArgumentNullException(nameof(key)); + } + + this.Certificate = null; + this.KeyIdentifier = keyId; + this.Key = key; + this.cryptographyClient = new CryptographyClient(keyId, credential); + } + + /// + /// Creates a new Key Vault context. + /// + public KeyVaultContext(TokenCredential credential, Uri keyId, X509Certificate2 publicCertificate) + { + if (credential is null) + { + throw new ArgumentNullException(nameof(credential)); + } + if (keyId is null) + { + throw new ArgumentNullException(nameof(keyId)); + } + if (publicCertificate is null) + { + throw new ArgumentNullException(nameof(publicCertificate)); + } + + this.Certificate = publicCertificate; + this.KeyIdentifier = keyId; + this.cryptographyClient = new CryptographyClient(keyId, credential); + + string algorithm = publicCertificate.GetKeyAlgorithm(); + + if (algorithm.Equals("1.2.840.113549.1.1.1", StringComparison.OrdinalIgnoreCase)) // RSA + { + using (var rsa = publicCertificate.GetRSAPublicKey()) + { + Key = new JsonWebKey(rsa, includePrivateParameters: false); + } + } + else if (algorithm.Equals("1.2.840.10045.2.1", StringComparison.OrdinalIgnoreCase)) // ECDsa + { + using (var ecdsa = publicCertificate.GetECDsaPublicKey()) + { + Key = new JsonWebKey(ecdsa, includePrivateParameters: false); + } + } + else + { + throw new NotSupportedException($"Certificate algorithm '{algorithm}' is not supported."); + } + } + + /// + /// Gets the certificate and public key used to validate the signature. May be null if + /// Key isn't part of a certificate + /// + public X509Certificate2 Certificate { get; } + + /// + /// Identifyer of current key + /// + public Uri KeyIdentifier { get; } + + /// + /// Public key + /// + public JsonWebKey Key { get; } + + internal byte[] SignDigest(byte[] digest, HashAlgorithmName hashAlgorithm, KeyVaultSignatureAlgorithm signatureAlgorithm) + { + var algorithm = SignatureAlgorithmTranslator.SignatureAlgorithmToJwsAlgId(signatureAlgorithm, hashAlgorithm); + + if (hashAlgorithm == HashAlgorithmName.SHA1) + { + if (signatureAlgorithm != KeyVaultSignatureAlgorithm.RSAPkcs15) + throw new InvalidOperationException("SHA1 algorithm is not supported for this signature algorithm."); + + digest = Sha1Helper.CreateDigest(digest); + } + + var sigResult = cryptographyClient.Sign(algorithm, digest); + + return sigResult.Signature; + } + + internal byte[] DecryptData(byte[] cipherText, RSAEncryptionPadding padding) + { + var algorithm = EncryptionPaddingTranslator.EncryptionPaddingToJwsAlgId(padding); + + var dataResult = cryptographyClient.Decrypt(algorithm, cipherText); + return dataResult.Plaintext; + } + + /// + /// Returns true if properly constructed. If default, then false. + /// + public bool IsValid => cryptographyClient != null; + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/LICENSE b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/LICENSE new file mode 100644 index 000000000000..a5a7bea5c793 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/LICENSE @@ -0,0 +1,21 @@ +The MIT License (MIT) + +Copyright (c) 2017-2020 Claire Novotny + +Permission is hereby granted, free of charge, to any person obtaining a copy +of this software and associated documentation files (the "Software"), to deal +in the Software without restriction, including without limitation the rights +to use, copy, modify, merge, publish, distribute, sublicense, and/or sell +copies of the Software, and to permit persons to whom the Software is +furnished to do so, subject to the following conditions: + +The above copyright notice and this permission notice shall be included in +all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR +IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, +FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE +AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER +LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, +OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN +THE SOFTWARE. \ No newline at end of file diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVault.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVault.cs new file mode 100644 index 000000000000..84073b043cb3 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVault.cs @@ -0,0 +1,151 @@ +namespace RSAKeyVaultProvider +{ + /// + /// RSA implementation that uses Azure Key Vault + /// + public sealed class RSAKeyVault : RSA + { + readonly KeyVaultContext context; + RSA publicKey; + + /// + /// Creates a new RSAKeyVault instance + /// + /// Context with parameters + public RSAKeyVault(KeyVaultContext context) + { + if (!context.IsValid) + throw new ArgumentException("Must not be the default", nameof(context)); + + this.context = context; + publicKey = context.Key.ToRSA(); + KeySizeValue = publicKey.KeySize; + LegalKeySizesValue = new[] { new KeySizes(publicKey.KeySize, publicKey.KeySize, 0) }; + } + + /// + public override byte[] SignHash(byte[] hash, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) + { + CheckDisposed(); + + // Key Vault only supports PKCSv1 padding + if (padding.Mode != RSASignaturePaddingMode.Pkcs1) + throw new Exception("Unsupported padding mode"); + + try + { + return context.SignDigest(hash, hashAlgorithm, KeyVaultSignatureAlgorithm.RSAPkcs15); + } + catch (Exception e) + { + throw new Exception("Error calling Key Vault", e); + } + } + + /// + public override bool VerifyHash(byte[] hash, byte[] signature, HashAlgorithmName hashAlgorithm, RSASignaturePadding padding) + { + CheckDisposed(); + + // Verify can be done locally using the public key + return publicKey.VerifyHash(hash, signature, hashAlgorithm, padding); + } + + /// + protected override byte[] HashData(byte[] data, int offset, int count, HashAlgorithmName hashAlgorithm) + { + CheckDisposed(); + + using (var digestAlgorithm = Create(hashAlgorithm)) + { + return digestAlgorithm.ComputeHash(data, offset, count); + } + } + + /// + protected override byte[] HashData(Stream data, HashAlgorithmName hashAlgorithm) + { + CheckDisposed(); + + using (var digestAlgorithm = Create(hashAlgorithm)) + { + return digestAlgorithm.ComputeHash(data); + } + } + + /// + public override byte[] Decrypt(byte[] data, RSAEncryptionPadding padding) + { + CheckDisposed(); + + try + { + return context.DecryptData(data, padding); + } + catch (Exception e) + { + throw new Exception("Error calling Key Vault", e); + } + } + + /// + public override byte[] Encrypt(byte[] data, RSAEncryptionPadding padding) + { + CheckDisposed(); + + return publicKey.Encrypt(data, padding); + } + + /// + public override RSAParameters ExportParameters(bool includePrivateParameters) + { + CheckDisposed(); + + if (includePrivateParameters) + throw new Exception("Private keys cannot be exported by this provider"); + + return publicKey.ExportParameters(includePrivateParameters); + } + + /// + public override void ImportParameters(RSAParameters parameters) + { + throw new NotSupportedException(); + } + + void CheckDisposed() + { + if (publicKey == null) + throw new ObjectDisposedException($"{nameof(RSAKeyVault)} is disposed"); + } + + /// + protected override void Dispose(bool disposing) + { + if (disposing) + { + publicKey?.Dispose(); + publicKey = null; + } + + base.Dispose(disposing); + } + + private static HashAlgorithm Create(HashAlgorithmName algorithm) + { + if (algorithm == HashAlgorithmName.SHA1) + return SHA1.Create(); + + if (algorithm == HashAlgorithmName.SHA256) + return SHA256.Create(); + + if (algorithm == HashAlgorithmName.SHA384) + return SHA384.Create(); + + if (algorithm == HashAlgorithmName.SHA512) + return SHA512.Create(); + + throw new NotSupportedException("The specified algorithm is not supported."); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVaultExtensions.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVaultExtensions.cs new file mode 100644 index 000000000000..c292c9075c94 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/RSAKeyVaultExtensions.cs @@ -0,0 +1,65 @@ +using Azure.Core; +using Azure.Security.KeyVault.Keys; + +namespace RSAKeyVaultProvider +{ + /// + /// Extensions for creating RSA objects from a Key Vault client. + /// + public static class RSAFactory + { + /// + /// Creates an RSA object + /// + /// + /// + /// + /// + public static RSA Create(TokenCredential credential, Uri keyId, JsonWebKey key) + { + if (credential == null) + { + throw new ArgumentNullException(nameof(credential)); + } + + if (keyId == null) + { + throw new ArgumentNullException(nameof(keyId)); + } + + if (key == null) + { + throw new ArgumentNullException(nameof(key)); + } + + return new RSAKeyVault(new KeyVaultContext(credential, keyId, key)); + } + + /// + /// Creates an RSA object + /// + /// + /// + /// + /// + public static RSA Create(TokenCredential credential, Uri keyId, X509Certificate2 publicCertificate) + { + if (credential == null) + { + throw new ArgumentNullException(nameof(credential)); + } + + if (keyId == null) + { + throw new ArgumentNullException(nameof(keyId)); + } + + if (publicCertificate == null) + { + throw new ArgumentNullException(nameof(publicCertificate)); + } + + return new RSAKeyVault(new KeyVaultContext(credential, keyId, publicCertificate)); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/Sha1Helper.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/Sha1Helper.cs new file mode 100644 index 000000000000..a2db584d0756 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/Sha1Helper.cs @@ -0,0 +1,19 @@ +namespace RSAKeyVaultProvider +{ + static class Sha1Helper + { + const int SHA1_SIZE = 20; + static readonly byte[] sha1Digest = new byte[] { 0x30, 0x21, 0x30, 0x09, 0x06, 0x05, 0x2B, 0x0E, 0x03, 0x02, 0x1A, 0x05, 0x00, 0x04, 0x14, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00 }; + + public static byte[] CreateDigest(byte[] hash) + { + if (hash.Length != SHA1_SIZE) + throw new ArgumentException("Invalid hash value"); + + byte[] pkcs1Digest = (byte[])sha1Digest.Clone(); + Array.Copy(hash, 0, pkcs1Digest, pkcs1Digest.Length - hash.Length, hash.Length); + + return pkcs1Digest; + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithm.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithm.cs new file mode 100644 index 000000000000..601ecf842426 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithm.cs @@ -0,0 +1,8 @@ +namespace RSAKeyVaultProvider +{ + internal enum KeyVaultSignatureAlgorithm + { + RSAPkcs15, + ECDsa + } +} \ No newline at end of file diff --git a/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithmTranslator.cs b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithmTranslator.cs new file mode 100644 index 000000000000..7a04df34ea39 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/RSAKeyVaultProvider/SignatureAlgorithmTranslator.cs @@ -0,0 +1,38 @@ +using Azure.Security.KeyVault.Keys.Cryptography; + +namespace RSAKeyVaultProvider +{ + static class SignatureAlgorithmTranslator + { + public static SignatureAlgorithm SignatureAlgorithmToJwsAlgId(KeyVaultSignatureAlgorithm signatureAlgorithm, HashAlgorithmName hashAlgorithmName) + { + if (signatureAlgorithm == KeyVaultSignatureAlgorithm.RSAPkcs15) + { + if (hashAlgorithmName == HashAlgorithmName.SHA1) + return new SignatureAlgorithm("RSNULL"); + + if (hashAlgorithmName == HashAlgorithmName.SHA256) + return SignatureAlgorithm.RS256; + + if (hashAlgorithmName == HashAlgorithmName.SHA384) + return SignatureAlgorithm.RS384; + + if (hashAlgorithmName == HashAlgorithmName.SHA512) + return SignatureAlgorithm.RS512; + } + else if (signatureAlgorithm == KeyVaultSignatureAlgorithm.ECDsa) + { + if (hashAlgorithmName == HashAlgorithmName.SHA256) + return SignatureAlgorithm.ES256; + + if (hashAlgorithmName == HashAlgorithmName.SHA384) + return SignatureAlgorithm.ES384; + + if (hashAlgorithmName == HashAlgorithmName.SHA512) + return SignatureAlgorithm.ES512; + } + + throw new NotSupportedException("The algorithm specified is not supported."); + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/SignCommand.cs b/tools/CustomBuildTool/AzureSignTool/SignCommand.cs new file mode 100644 index 000000000000..e2ed4613cd95 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/SignCommand.cs @@ -0,0 +1,57 @@ +namespace CustomBuildTool +{ + internal sealed class SignCommand + { + internal static readonly string CodeSigningOid = "1.3.6.1.5.5.7.3.3"; + + internal static bool IsSigned(string filePath) + { + try + { + var certificate = new X509Certificate2(X509Certificate.CreateFromSignedFile(filePath)); + + // check if file contains a code signing cert. + // Note that this does not check validity of the signature + return certificate.Extensions + .Select(extension => extension as X509EnhancedKeyUsageExtension) + .Select(enhancedExtension => enhancedExtension?.EnhancedKeyUsages) + .Any(oids => oids?[CodeSigningOid] != null); + } + catch (Exception) + { + return false; + } + } + + private static X509Certificate2Collection GetAdditionalCertificates(IEnumerable paths) + { + var collection = new X509Certificate2Collection(); + try + { + foreach (var path in paths) + { + + var type = X509Certificate2.GetCertContentType(path); + switch (type) + { + case X509ContentType.Cert: + case X509ContentType.Authenticode: + case X509ContentType.SerializedCert: + var certificate = new X509Certificate2(path); + //logger.LogTrace($"Including additional certificate {certificate.Thumbprint}."); + collection.Add(certificate); + break; + default: + throw new Exception($"Specified file {path} is not a public valid certificate."); + } + } + } + catch (Exception e) + { + Program.PrintColorMessage("[ERROR] An exception occurred while including an additional certificate.", ConsoleColor.Red); + } + + return collection; + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/SipExtensionFactory.cs b/tools/CustomBuildTool/AzureSignTool/SipExtensionFactory.cs new file mode 100644 index 000000000000..75f7a35c2bc9 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/SipExtensionFactory.cs @@ -0,0 +1,58 @@ +namespace CustomBuildTool +{ + public enum SipKind + { + None, + Appx + } + + public static class SipExtensionFactory + { + public static SipKind GetSipKind(ReadOnlySpan filePath) + { + var extension = Path.GetExtension(filePath.ToString()); + + if (extension.Equals(".appx", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".eappx", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".appxbundle", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".eappxbundle", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".msix", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".emsix", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".msixbundle", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + if (extension.Equals(".emsixbundle", StringComparison.OrdinalIgnoreCase)) + { + return SipKind.Appx; + } + + return SipKind.None; + } + } +} diff --git a/tools/CustomBuildTool/AzureSignTool/TimeStampConfiguration.cs b/tools/CustomBuildTool/AzureSignTool/TimeStampConfiguration.cs new file mode 100644 index 000000000000..c0ad5b871e21 --- /dev/null +++ b/tools/CustomBuildTool/AzureSignTool/TimeStampConfiguration.cs @@ -0,0 +1,65 @@ +namespace CustomBuildTool +{ + /// + /// Contains configuration for timestamping authenticode signatures. + /// + public class TimeStampConfiguration + { + /// + /// The URL to the timestamp authority. + /// + public string Url { get; } + + /// + /// The digest algorithm the timestamp service authority should use on timestamp signatures. + /// + public HashAlgorithmName DigestAlgorithm { get; } + + /// + /// The type of timestamp to use. See for details, or null if + /// no timestamping should be performed. + /// + public TimeStampType? Type { get; } + + /// + /// A default timestamp configuration indicating no timestamp should be generated. + /// + public static TimeStampConfiguration None { get; } = new TimeStampConfiguration(); + + /// + /// Creates a new instance of a . + /// + /// The URL to the timestamp authority. + /// The digest algorithm the timestamp service authority should use on timestamp signatures. + /// The type of timestamp to use. See for details. + public TimeStampConfiguration(string url, HashAlgorithmName digestAlgorithm, TimeStampType type) + { + Url = url; + DigestAlgorithm = digestAlgorithm; + Type = type; + } + + private TimeStampConfiguration() + { + Type = null; + } + } + + /// + /// An enumeration of possible timestamp kinds. + /// + public enum TimeStampType + { + /// + /// Indicates that a timestamp authority should use the legacy Authenticode style of timestamps. + /// This option should only be used for backward compatibility with Windows XP and only supports + /// timestamp signatures. + /// + Authenticode, + + /// + /// Indicates that a timestamp authority should use an RFC3161 timestamp signatures. + /// + RFC3161 + } +} diff --git a/tools/CustomBuildTool/Build.cs b/tools/CustomBuildTool/Build.cs index 2b3f4d059e19..8a3042a7d8de 100644 --- a/tools/CustomBuildTool/Build.cs +++ b/tools/CustomBuildTool/Build.cs @@ -171,6 +171,11 @@ public static string BuildUpdated get { return new DateTime(TimeStart.Year, TimeStart.Month, TimeStart.Day, TimeStart.Hour, 0, 0).ToString("o"); } } + public static DateTime BuildDateTime + { + get { return new DateTime(TimeStart.Year, TimeStart.Month, TimeStart.Day, TimeStart.Hour, 0, 0); } + } + public static void ShowBuildStats() { TimeSpan buildTime = (DateTime.UtcNow - Build.TimeStart); @@ -370,10 +375,10 @@ public static bool BuildDynamicData(string OutDir) public static bool CopyKernelDriver(BuildFlags Flags) { string[] Build_Driver_Files = - { + [ "SystemInformer.sys", "ksi.dll", - }; + ]; try { @@ -441,8 +446,10 @@ public static bool ResignFiles() return true; } - public static bool BuildSdk(BuildFlags Flags) + public static unsafe bool BuildSdk(BuildFlags Flags) { + bool verbose = Flags.HasFlag(BuildFlags.BuildVerbose); + foreach (string folder in BuildConfig.Build_Sdk_Directories) { Win32.CreateDirectory(folder); @@ -473,39 +480,51 @@ public static bool BuildSdk(BuildFlags Flags) if (Flags.HasFlag(BuildFlags.BuildDebug)) { if (Flags.HasFlag(BuildFlags.Build32bit)) - Win32.CopyIfNewer("bin\\Debug32\\SystemInformer.lib", "sdk\\lib\\i386\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\Debug32\\SystemInformer.lib", "sdk\\lib\\i386\\SystemInformer.lib", verbose); if (Flags.HasFlag(BuildFlags.Build64bit)) - Win32.CopyIfNewer("bin\\Debug64\\SystemInformer.lib", "sdk\\lib\\amd64\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\Debug64\\SystemInformer.lib", "sdk\\lib\\amd64\\SystemInformer.lib", verbose); if (Flags.HasFlag(BuildFlags.BuildArm64bit)) - Win32.CopyIfNewer("bin\\DebugARM64\\SystemInformer.lib", "sdk\\lib\\arm64\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\DebugARM64\\SystemInformer.lib", "sdk\\lib\\arm64\\SystemInformer.lib", verbose); } if (Flags.HasFlag(BuildFlags.BuildRelease)) { if (Flags.HasFlag(BuildFlags.Build32bit)) - Win32.CopyIfNewer("bin\\Release32\\SystemInformer.lib", "sdk\\lib\\i386\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\Release32\\SystemInformer.lib", "sdk\\lib\\i386\\SystemInformer.lib", verbose); if (Flags.HasFlag(BuildFlags.Build64bit)) - Win32.CopyIfNewer("bin\\Release64\\SystemInformer.lib", "sdk\\lib\\amd64\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\Release64\\SystemInformer.lib", "sdk\\lib\\amd64\\SystemInformer.lib", verbose); if (Flags.HasFlag(BuildFlags.BuildArm64bit)) - Win32.CopyIfNewer("bin\\ReleaseARM64\\SystemInformer.lib", "sdk\\lib\\arm64\\SystemInformer.lib"); + Win32.CopyIfNewer("bin\\ReleaseARM64\\SystemInformer.lib", "sdk\\lib\\arm64\\SystemInformer.lib", verbose); } // Build the SDK HeaderGen.Execute(); // Copy the SDK headers - Win32.CopyIfNewer("SystemInformer\\sdk\\phapppub.h", "sdk\\include\\phapppub.h"); - Win32.CopyIfNewer("SystemInformer\\sdk\\phdk.h", "sdk\\include\\phdk.h"); + Win32.CopyIfNewer("SystemInformer\\include\\phappres.h", "sdk\\include\\phappres.h", verbose); + Win32.CopyIfNewer("SystemInformer\\sdk\\phapppub.h", "sdk\\include\\phapppub.h", verbose); + Win32.CopyIfNewer("SystemInformer\\sdk\\phdk.h", "sdk\\include\\phdk.h", verbose); //Win32.CopyIfNewer("SystemInformer\\resource.h", "sdk\\include\\phappresource.h"); // Copy the resource header and prefix types with PHAPP { - NativeMethods.GetFileAttributesEx("SystemInformer\\resource.h", 0, out var sourceFile); - NativeMethods.GetFileAttributesEx("sdk\\include\\phappresource.h", 0, out var destinationFile); + WIN32_FILE_ATTRIBUTE_DATA sourceFile; + WIN32_FILE_ATTRIBUTE_DATA destinationFile; + + PInvoke.GetFileAttributesEx( + "SystemInformer\\resource.h", + GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, + &sourceFile); + + PInvoke.GetFileAttributesEx( + "sdk\\include\\phappresource.h", + GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, + &destinationFile + ); if ( - sourceFile.CreationTime.FileTime != destinationFile.CreationTime.FileTime || - sourceFile.LastWriteTime.FileTime != destinationFile.LastWriteTime.FileTime + sourceFile.ftCreationTime.FileTimeToDateTime() != destinationFile.ftCreationTime.FileTimeToDateTime() || + sourceFile.ftLastWriteTime.FileTimeToDateTime() != destinationFile.ftLastWriteTime.FileTimeToDateTime() ) { string resourceContent = Utils.ReadAllText("SystemInformer\\resource.h"); @@ -520,11 +539,13 @@ public static bool BuildSdk(BuildFlags Flags) sdkContent ); - Win32.SetFileTime( - "sdk\\include\\phappresource.h", - sourceFile.CreationTime.FileTime, - sourceFile.LastWriteTime.FileTime - ); + using var fs = File.OpenWrite("sdk\\include\\phappresource.h"); + { + PInvoke.SetFileTime( + (HANDLE)fs.SafeFileHandle.DangerousGetHandle(), + &sourceFile.ftCreationTime, + &sourceFile.ftLastWriteTime); + } } } } @@ -673,7 +694,7 @@ public static bool BuildBinZip(string Channel) $"{BuildOutputFolder}\\systeminformer-build-{Channel}-bin.zip" ); - Program.PrintColorMessage(Win32.GetFileSize($"{BuildOutputFolder}\\systeminformer-build-{Channel}-bin.zip").ToPrettySize(), ConsoleColor.Green); + Program.PrintColorMessage(Win32.GetFileSize($"{BuildOutputFolder}\\systeminformer-build-{Channel}-bin.zip").ToPrettySize(), ConsoleColor.Green, false); } catch (Exception ex) { @@ -782,23 +803,23 @@ private static string MsbuildCommandString(string Solution, string Platform, Bui StringBuilder linkerOptions = new StringBuilder(0x100); if (Flags.HasFlag(BuildFlags.BuildApi)) - compilerOptions.Append("PH_BUILD_API;"); + compilerOptions.Append("PH_BUILD_API; "); if (Flags.HasFlag(BuildFlags.BuildMsix)) - compilerOptions.Append("PH_BUILD_MSIX;"); + compilerOptions.Append("PH_BUILD_MSIX; "); if (!string.IsNullOrWhiteSpace(Channel)) - compilerOptions.Append($"PH_RELEASE_CHANNEL_ID={BuildConfig.Build_Channels[Channel]};"); + compilerOptions.Append($"PH_RELEASE_CHANNEL_ID=\"{BuildConfig.Build_Channels[Channel]}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildCommitHash)) - compilerOptions.Append($"PHAPP_VERSION_COMMITHASH=\"{Build.BuildCommitHash.AsSpan(0, 8)}\";"); + compilerOptions.Append($"PHAPP_VERSION_COMMITHASH=\"{Build.BuildCommitHash.AsSpan(0, 8)}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildVersionMajor)) - compilerOptions.Append($"PHAPP_VERSION_MAJOR=\"{Build.BuildVersionMajor}\";"); + compilerOptions.Append($"PHAPP_VERSION_MAJOR=\"{Build.BuildVersionMajor}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildVersionMinor)) - compilerOptions.Append($"PHAPP_VERSION_MINOR=\"{Build.BuildVersionMinor}\";"); + compilerOptions.Append($"PHAPP_VERSION_MINOR=\"{Build.BuildVersionMinor}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildVersionBuild)) - compilerOptions.Append($"PHAPP_VERSION_BUILD=\"{Build.BuildVersionBuild}\";"); + compilerOptions.Append($"PHAPP_VERSION_BUILD=\"{Build.BuildVersionBuild}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildVersionRevision)) - compilerOptions.Append($"PHAPP_VERSION_REVISION=\"{Build.BuildVersionRevision}\";"); + compilerOptions.Append($"PHAPP_VERSION_REVISION=\"{Build.BuildVersionRevision}\"; "); if (!string.IsNullOrWhiteSpace(Build.BuildSourceLink)) - linkerOptions.Append($"/SOURCELINK:\"{Build.BuildSourceLink}\""); + linkerOptions.Append($"/SOURCELINK:\"{Build.BuildSourceLink}\" "); commandLine.Append($"/m /nologo /nodereuse:false /verbosity:{(Build.BuildToolsDebug ? "diagnostic" : "quiet")} "); commandLine.Append($"/p:Platform={Platform} /p:Configuration={(Flags.HasFlag(BuildFlags.BuildDebug) ? "Debug" : "Release")} "); @@ -996,37 +1017,24 @@ public static bool BuildDeployUpdateConfig() if (buildPostString.LongLength == 0) return false; - using HttpClientHandler httpClientHandler = new HttpClientHandler(); - httpClientHandler.AutomaticDecompression = DecompressionMethods.All; - httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - httpClientHandler.ServerCertificateCustomValidationCallback = (sender, cert, chain, sslPolicyErrors) => + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, buildPostSfUrl)) { - if ( - sslPolicyErrors == SslPolicyErrors.None && - cert.Subject.Equals("CN=sourceforge.io, O=\"Cloudflare, Inc.\", L=San Francisco, S=California, C=US", StringComparison.OrdinalIgnoreCase) - ) - { - return true; - } + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Add("X-ApiKey", buildPostSfApiKey); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version30; - return false; - }; - - using HttpClient httpClient = new HttpClient(httpClientHandler); - httpClient.DefaultRequestHeaders.Add("X-ApiKey", buildPostSfApiKey); - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - httpClient.DefaultRequestVersion = HttpVersion.Version20; + requestMessage.Content = new ByteArrayContent(buildPostString); + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - using ByteArrayContent httpContent = new ByteArrayContent(buildPostString); - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + var httpResult = Http.SendMessage(requestMessage); - var httpTask = httpClient.PostAsync(buildPostSfUrl, httpContent); - httpTask.Wait(); - - if (!httpTask.Result.IsSuccessStatusCode) - { - Program.PrintColorMessage($"[UpdateBuildWebService-SF] {httpTask.Result}", ConsoleColor.Red); - return false; + if (string.IsNullOrWhiteSpace(httpResult)) + { + Program.PrintColorMessage("[UpdateBuildWebService-SF]", ConsoleColor.Red); + return false; + } } } catch (Exception ex) @@ -1446,5 +1454,105 @@ public static void CleanupBuildEnvironment() Program.PrintColorMessage($"[Cleanup] {ex}", ConsoleColor.Red); } } + + public static void ReflowExportDefinitions(bool ReleaseBuild) + { +//const string Header = +//@";/* +//; * Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved. +//; * +//; * This file is part of System Informer. +//; * +//; * Authors: +//; * +//; * wj32 2008-2016 +//; * dmex 2017-2024 +//; * +//; */ +// +//; +//; This file was automatically generated. +//; +//; Do not link exports at runtime. +//; +//"; + StringBuilder content = new StringBuilder(); + string[] exports = File.ReadAllLines("SystemInformer\\SystemInformer.def"); + int total = 0; + + using (var reader = File.OpenText("SystemInformer\\SystemInformer.def")) + { + while (reader.ReadLine() is { } line) + { + if (line.Contains('@', StringComparison.OrdinalIgnoreCase)) + { + total++; + } + } + } + + List ordinals = new List(); + + if (ReleaseBuild) + { + while (ordinals.Count < total) + { + var value = Random.Shared.Next(1000, 1000 + total); // ushort.MaxValue + + if (!ordinals.Contains(value)) + { + ordinals.Add(value); + } + } + } + else + { + while (ordinals.Count < total) + { + ordinals.Add(1000 + ordinals.Count + 1); + } + } + + foreach (string line in exports) + { + var span = line.AsSpan(); + + if (span.IsEmpty || span.IsWhiteSpace()) + { + content.Append(span); + content.Append(Environment.NewLine); + } + else + { + int length = span.IndexOf("@", StringComparison.OrdinalIgnoreCase); + + if (length != -1) + { + var ordinal = ordinals[0]; ordinals.RemoveAt(0); + + content.Append(span.Slice(0, length)); + content.Append('@'); + content.Append(ordinal); + content.Append(" NONAME"); + + if (span.EndsWith("DATA", StringComparison.OrdinalIgnoreCase)) + { + content.Append(" DATA"); + } + + content.Append(Environment.NewLine); + } + else + { + content.Append(span); + content.Append(Environment.NewLine); + } + } + } + + string export_content = content.ToString().TrimEnd(); + + Utils.WriteAllText("SystemInformer\\SystemInformer.def", export_content); + } } } diff --git a/tools/CustomBuildTool/CustomBuildTool.csproj b/tools/CustomBuildTool/CustomBuildTool.csproj index 09561b390e49..76842546d1d2 100644 --- a/tools/CustomBuildTool/CustomBuildTool.csproj +++ b/tools/CustomBuildTool/CustomBuildTool.csproj @@ -27,6 +27,7 @@ true True false + true x86 @@ -79,11 +80,17 @@ false false + + + + + + @@ -103,8 +110,22 @@ + + + + - + + + + + + all + runtime; build; native; contentfiles; analyzers; buildtransitive + + + + \ No newline at end of file diff --git a/tools/CustomBuildTool/CustomBuildTool.slnx b/tools/CustomBuildTool/CustomBuildTool.slnx new file mode 100644 index 000000000000..98369429e13c --- /dev/null +++ b/tools/CustomBuildTool/CustomBuildTool.slnx @@ -0,0 +1,15 @@ + + + + + + + + + + + + + + + diff --git a/tools/CustomBuildTool/DynData.cs b/tools/CustomBuildTool/DynData.cs index f95b920c3654..8c6194ffd1b4 100644 --- a/tools/CustomBuildTool/DynData.cs +++ b/tools/CustomBuildTool/DynData.cs @@ -259,12 +259,32 @@ public static bool Execute(string OutDir, bool StrictChecks) string headerFile = $"{Build.BuildWorkingFolder}\\kphlib\\include\\kphdyn.h"; string sourceFile = $"{Build.BuildWorkingFolder}\\kphlib\\kphdyn.c"; - GenerateConfig(manifestFile, out byte[] config); + // Check for new or modified content. We don't want to touch the file if it's not needed. + { + string headerUpdateText = GenerateHeader(); + string headerCurrentText = Utils.ReadAllText(headerFile); + + if (!string.Equals(headerUpdateText, headerCurrentText, StringComparison.OrdinalIgnoreCase)) + { + Utils.WriteAllText(headerFile, headerUpdateText); + } + + Program.PrintColorMessage($"Dynamic header -> {headerFile}", ConsoleColor.Cyan); + } + + byte[] config = GenerateConfig(manifestFile); - Utils.WriteAllText(headerFile, GenerateHeader()); - Program.PrintColorMessage($"Dynamic header -> {headerFile}", ConsoleColor.Cyan); - Utils.WriteAllText(sourceFile, GenerateSource(BytesToString(config))); - Program.PrintColorMessage($"Dynamic source -> {sourceFile}", ConsoleColor.Cyan); + { + var headerUpdateText = GenerateSource(BytesToString(config)); + var headerCurrentText = Utils.ReadAllText(sourceFile); + + if (!string.Equals(headerUpdateText, headerCurrentText, StringComparison.OrdinalIgnoreCase)) + { + Utils.WriteAllText(sourceFile, headerUpdateText); + } + + Program.PrintColorMessage($"Dynamic source -> {sourceFile}", ConsoleColor.Cyan); + } if (string.IsNullOrWhiteSpace(OutDir)) return true; @@ -272,11 +292,37 @@ public static bool Execute(string OutDir, bool StrictChecks) string configFile = $"{OutDir}\\ksidyn.bin"; if (File.Exists(configFile)) + { + var configUpdateBytes = Utils.ReadAllBytes(configFile).AsSpan(); + var configSigFileName = Path.ChangeExtension(configFile, ".sig"); + + if (configUpdateBytes.SequenceEqual(config) && File.Exists(configSigFileName)) + { + Program.PrintColorMessage($"Dynamic config -> {configFile}", ConsoleColor.Cyan); + return true; + } + File.Delete(configFile); - Directory.CreateDirectory(OutDir); - Utils.WriteAllBytes(configFile, config); - Program.PrintColorMessage($"Dynamic config -> {configFile}", ConsoleColor.Cyan); - return Verify.CreateSigFile("kph", configFile, StrictChecks); + File.Delete(configSigFileName); + Utils.WriteAllBytes(configFile, config); + + bool configFileSig = Verify.CreateSigFile("kph", configFile, StrictChecks); + + Program.PrintColorMessage($"Dynamic config -> {configFile}", ConsoleColor.Cyan); + + return configFileSig; + } + else + { + Directory.CreateDirectory(OutDir); + Utils.WriteAllBytes(configFile, config); + + bool configFileSig = Verify.CreateSigFile("kph", configFile, StrictChecks); + + Program.PrintColorMessage($"Dynamic config -> {configFile}", ConsoleColor.Cyan); + + return configFileSig; + } } private static string GenerateHeader() @@ -321,10 +367,7 @@ string Config return sb.ToString(); } - private static void GenerateConfig( - string ManifestFile, - out byte[] ConfigBytes - ) + private static byte[] GenerateConfig(string ManifestFile) { var xml = new XmlDocument(); var configs = new List(10); @@ -390,16 +433,16 @@ out byte[] ConfigBytes var member = typeof(DynConfigArch).GetField(name); - if (arch == null) + if (string.IsNullOrEmpty(arch)) { member.SetValueDirect(__makeref(config.ArchAMD64), Convert.ChangeType(value, member.FieldType)); member.SetValueDirect(__makeref(config.ArchARM64), Convert.ChangeType(value, member.FieldType)); } - else if (arch == "amd64") + else if (string.Equals(arch, "amd64", StringComparison.OrdinalIgnoreCase)) { member.SetValueDirect(__makeref(config.ArchAMD64), Convert.ChangeType(value, member.FieldType)); } - else if (arch == "arm64") + else if (string.Equals(arch, "arm64", StringComparison.OrdinalIgnoreCase)) { member.SetValueDirect(__makeref(config.ArchARM64), Convert.ChangeType(value, member.FieldType)); } @@ -430,7 +473,7 @@ out byte[] ConfigBytes writer.Write((uint)configs.Count); writer.Write(MemoryMarshal.AsBytes(CollectionsMarshal.AsSpan(configs))); - ConfigBytes = stream.ToArray(); + return stream.ToArray(); } } diff --git a/tools/CustomBuildTool/Github.cs b/tools/CustomBuildTool/Github.cs index 587c3a9da3a4..8794c7d4c51f 100644 --- a/tools/CustomBuildTool/Github.cs +++ b/tools/CustomBuildTool/Github.cs @@ -45,60 +45,33 @@ public static GithubReleasesResponse CreateRelease(string Version) if (buildPostString.LongLength == 0) return null; - using (HttpClientHandler httpClientHandler = new HttpClientHandler()) + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/releases")) { - httpClientHandler.AutomaticDecompression = DecompressionMethods.All; - httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - httpClientHandler.ServerCertificateCustomValidationCallback = CertValidationCallback; + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; - using (HttpClient httpClient = new HttpClient(httpClientHandler)) - { - httpClient.DefaultRequestVersion = HttpVersion.Version20; - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", BaseToken); - - using (ByteArrayContent httpContent = new ByteArrayContent(buildPostString)) - { - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - - var httpTask = httpClient.PostAsync($"{BaseUrl}/releases", httpContent); - httpTask.Wait(); - - if (!httpTask.Result.IsSuccessStatusCode) - { - Program.PrintColorMessage($"[CreateRelease-Post] {httpTask.Result}", ConsoleColor.Red); - return null; - } - - var httpResult = httpTask.Result.Content.ReadAsStringAsync(); - httpResult.Wait(); + requestMessage.Content = new ByteArrayContent(buildPostString); + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - if (!httpResult.IsCompletedSuccessfully || string.IsNullOrWhiteSpace(httpResult.Result)) - { - Program.PrintColorMessage("[CreateRelease-ReadAsStringAsync]", ConsoleColor.Red); - return null; - } + var httpResult = Http.SendMessage(requestMessage, GithubReleasesResponseContext.Default.GithubReleasesResponse); - var response = JsonSerializer.Deserialize(httpResult.Result, GithubReleasesResponseContext.Default.GithubReleasesResponse); - - if (response == null) - { - Program.PrintColorMessage("[CreateRelease-GithubReleasesResponse]", ConsoleColor.Red); - return null; - } - - if (string.IsNullOrWhiteSpace(response.UploadUrl)) - { - Program.PrintColorMessage("[CreateRelease-upload_url]", ConsoleColor.Red); - return null; - } + if (httpResult == null) + { + Program.PrintColorMessage("[CreateRelease-GithubReleasesResponse]", ConsoleColor.Red); + return null; + } - return response; - } + if (string.IsNullOrWhiteSpace(httpResult.UploadUrl)) + { + Program.PrintColorMessage("[CreateRelease-upload_url]", ConsoleColor.Red); + return null; } - } + + return httpResult; + } } catch (Exception ex) { @@ -117,83 +90,100 @@ public static bool DeleteRelease(string Version) try { - using (HttpClientHandler httpClientHandler = new HttpClientHandler()) - { - httpClientHandler.AutomaticDecompression = DecompressionMethods.All; - httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - httpClientHandler.ServerCertificateCustomValidationCallback = CertValidationCallback; + HttpResponseMessage responseMessage = null; + GithubReleasesResponse githubResponseMessage = null; - using (HttpClient httpClient = new HttpClient(httpClientHandler)) - { - httpClient.DefaultRequestVersion = HttpVersion.Version20; - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Get, $"{BaseUrl}/releases/tags/{Version}")) + { + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; - var httpTask = httpClient.GetAsync($"{BaseUrl}/releases/tags/{Version}"); - httpTask.Wait(); + responseMessage = Http.SendMessageResponse(requestMessage); - if (!httpTask.Result.IsSuccessStatusCode) + if (!responseMessage.IsSuccessStatusCode) + { + if (responseMessage.StatusCode == HttpStatusCode.NotFound) { - if (httpTask.Result.StatusCode == HttpStatusCode.NotFound) - { - return true; // tag doesn't exist - } - else - { - Program.PrintColorMessage("[DeleteRelease-GetAsync] " + httpTask.Result, ConsoleColor.Red); - return false; - } + return true; // tag doesn't exist } - - var httpResult = httpTask.Result.Content.ReadAsStringAsync(); - httpResult.Wait(); - - if (!httpResult.IsCompletedSuccessfully || string.IsNullOrWhiteSpace(httpResult.Result)) + else { - Program.PrintColorMessage("[DeleteRelease-ReadAsStringAsync]", ConsoleColor.Red); + Program.PrintColorMessage("[DeleteRelease-GetAsync] " + responseMessage, ConsoleColor.Red); return false; } + } + } - var response = JsonSerializer.Deserialize(httpResult.Result, GithubReleasesResponseContext.Default.GithubReleasesResponse); + { + var result = Task.Run(responseMessage.Content.ReadAsStringAsync).GetAwaiter().GetResult(); - if (response == null) - { - Program.PrintColorMessage("[DeleteRelease-GithubReleasesResponse]", ConsoleColor.Red); - return false; - } + if (string.IsNullOrWhiteSpace(result)) + { + Program.PrintColorMessage("[DeleteRelease-ReadAsStringAsync]", ConsoleColor.Red); + return false; + } - if (string.IsNullOrWhiteSpace(response.ReleaseId)) - { - Program.PrintColorMessage("[DeleteRelease-ReleaseId]", ConsoleColor.Red); - return false; - } + githubResponseMessage = JsonSerializer.Deserialize(result, GithubReleasesResponseContext.Default.GithubReleasesResponse); - // Delete the release (required) - var httpDeleteReleaseTask = httpClient.DeleteAsync($"{BaseUrl}/releases/{response.ReleaseId}"); - httpDeleteReleaseTask.Wait(); + if (githubResponseMessage == null) + { + Program.PrintColorMessage("[DeleteRelease-GithubReleasesResponse]", ConsoleColor.Red); + return false; + } + } - if (!httpDeleteReleaseTask.IsCompletedSuccessfully) - { - Program.PrintColorMessage("[DeleteRelease-DeleteAsync]", ConsoleColor.Red); - return false; - } + if (string.IsNullOrWhiteSpace(githubResponseMessage.ReleaseId)) + { + Program.PrintColorMessage("[DeleteRelease-ReleaseId]", ConsoleColor.Red); + return false; + } + + // Delete the release (required) + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Delete, $"{BaseUrl}/releases/{githubResponseMessage.ReleaseId}")) + { + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; - // Delete the release tag (optional) - var httpDeleteTagTask = httpClient.DeleteAsync($"{BaseUrl}/git/refs/tags/{Version}"); - httpDeleteTagTask.Wait(); + var response = Http.SendMessage(requestMessage); - return true; + if (string.IsNullOrWhiteSpace(response)) + { + Program.PrintColorMessage("[DeleteRelease-DeleteAsync]", ConsoleColor.Red); + return false; + } + } + + // Delete the release tag (optional) + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Delete, $"{BaseUrl}/git/refs/tags/{Version}")) + { + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; + + var response = Http.SendMessage(requestMessage); + + if (string.IsNullOrWhiteSpace(response)) + { + Program.PrintColorMessage("[DeleteRelease-DeleteAsync]", ConsoleColor.Red); + return false; } } } catch (Exception ex) { Program.PrintColorMessage("[DeleteRelease] " + ex, ConsoleColor.Red); + return false; } - return false; + return true; } public static GithubReleasesResponse UpdateRelease(string ReleaseId) @@ -216,59 +206,32 @@ public static GithubReleasesResponse UpdateRelease(string ReleaseId) if (buildPostString.LongLength == 0) return null; - using (HttpClientHandler httpClientHandler = new HttpClientHandler()) + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, $"{BaseUrl}/releases/{ReleaseId}")) { - httpClientHandler.AutomaticDecompression = DecompressionMethods.All; - httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - httpClientHandler.ServerCertificateCustomValidationCallback = CertValidationCallback; + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; - using (HttpClient httpClient = new HttpClient(httpClientHandler)) - { - httpClient.DefaultRequestVersion = HttpVersion.Version20; - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.Content = new ByteArrayContent(buildPostString); + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/json"); - using (ByteArrayContent httpContent = new ByteArrayContent(buildPostString)) - { - httpContent.Headers.ContentType = new MediaTypeHeaderValue("application/json"); + var response = Http.SendMessage(requestMessage, GithubReleasesResponseContext.Default.GithubReleasesResponse); - var httpTask = httpClient.PostAsync($"{BaseUrl}/releases/{ReleaseId}", httpContent); - httpTask.Wait(); - - if (!httpTask.Result.IsSuccessStatusCode) - { - Program.PrintColorMessage("[UpdateRelease-Post] " + httpTask.Result, ConsoleColor.Red); - return null; - } - - var httpResult = httpTask.Result.Content.ReadAsStringAsync(); - httpResult.Wait(); - - if (!httpResult.IsCompletedSuccessfully || string.IsNullOrWhiteSpace(httpResult.Result)) - { - Program.PrintColorMessage("[UpdateRelease-ReadAsStringAsync]", ConsoleColor.Red); - return null; - } - - var response = JsonSerializer.Deserialize(httpResult.Result, GithubReleasesResponseContext.Default.GithubReleasesResponse); - - if (response == null) - { - Program.PrintColorMessage("[UpdateRelease-GithubReleasesResponse]", ConsoleColor.Red); - return null; - } - - if (string.IsNullOrWhiteSpace(response.UploadUrl)) - { - Program.PrintColorMessage("[UpdateRelease-upload_url]", ConsoleColor.Red); - return null; - } + if (response == null) + { + Program.PrintColorMessage("[UpdateRelease-GithubReleasesResponse]", ConsoleColor.Red); + return null; + } - return response; - } + if (string.IsNullOrWhiteSpace(response.UploadUrl)) + { + Program.PrintColorMessage("[UpdateRelease-upload_url]", ConsoleColor.Red); + return null; } + + return response; } } catch (Exception ex) @@ -298,61 +261,34 @@ public static GithubAssetsResponse UploadAssets(string Version, string FileName, try { - using (HttpClientHandler httpClientHandler = new HttpClientHandler()) + using (FileStream fileStream = File.OpenRead(FileName)) + using (BufferedStream bufferedStream = new BufferedStream(fileStream)) + using (HttpRequestMessage requestMessage = new HttpRequestMessage(HttpMethod.Post, upload_url)) { - httpClientHandler.AutomaticDecompression = DecompressionMethods.All; - httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; - httpClientHandler.ServerCertificateCustomValidationCallback = CertValidationCallback; - - using (HttpClient httpClient = new HttpClient(httpClientHandler)) - { - httpClient.DefaultRequestVersion = HttpVersion.Version20; - httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; - httpClient.DefaultRequestHeaders.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); - httpClient.DefaultRequestHeaders.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); - httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Token", BaseToken); - - using (FileStream fileStream = File.OpenRead(FileName)) - using (BufferedStream bufferedStream = new BufferedStream(fileStream)) - using (StreamContent streamContent = new StreamContent(bufferedStream)) - { - streamContent.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); - - var httpUpload = httpClient.PostAsync(upload_url, streamContent); - httpUpload.Wait(); + requestMessage.Headers.UserAgent.Add(new ProductInfoHeaderValue("CustomBuildTool", "1.0")); + requestMessage.Headers.Accept.Add(new MediaTypeWithQualityHeaderValue("application/json")); + requestMessage.Headers.Authorization = new AuthenticationHeaderValue("Token", BaseToken); + requestMessage.VersionPolicy = HttpVersionPolicy.RequestVersionExact; + requestMessage.Version = HttpVersion.Version20; - if (!httpUpload.Result.IsSuccessStatusCode) - { - Program.PrintColorMessage("[UploadAssets-Post] " + httpUpload.Result, ConsoleColor.Red); - return null; - } + requestMessage.Content = new StreamContent(bufferedStream); + requestMessage.Content.Headers.ContentType = new MediaTypeHeaderValue("application/octet-stream"); - var httpUploadResult = httpUpload.Result.Content.ReadAsStringAsync(); - httpUploadResult.Wait(); + var response = Http.SendMessage(requestMessage, GithubAssetsResponseContext.Default.GithubAssetsResponse); - if (!httpUploadResult.IsCompletedSuccessfully || string.IsNullOrWhiteSpace(httpUploadResult.Result)) - { - Program.PrintColorMessage("[UploadAssets-ReadAsStringAsync]", ConsoleColor.Red); - return null; - } - - var response = JsonSerializer.Deserialize(httpUploadResult.Result, GithubAssetsResponseContext.Default.GithubAssetsResponse); - - if (response == null) - { - Program.PrintColorMessage("[UploadAssets-GithubAssetsResponse]", ConsoleColor.Red); - return null; - } - - if (!response.Uploaded || string.IsNullOrWhiteSpace(response.DownloadUrl)) - { - Program.PrintColorMessage("[UploadAssets-download_url]", ConsoleColor.Red); - return null; - } + if (response == null) + { + Program.PrintColorMessage("[UploadAssets-GithubAssetsResponse]", ConsoleColor.Red); + return null; + } - return response; - } + if (!response.Uploaded || string.IsNullOrWhiteSpace(response.DownloadUrl)) + { + Program.PrintColorMessage("[UploadAssets-download_url]", ConsoleColor.Red); + return null; } + + return response; } } catch (Exception ex) @@ -362,29 +298,6 @@ public static GithubAssetsResponse UploadAssets(string Version, string FileName, return null; } - - private static readonly string[] TrustedSubjects = - { - "CN=*.github.com", - "CN=*.github.com, O=\"GitHub, Inc.\", L=San Francisco, S=California, C=US", - }; - - private static bool CertValidationCallback( - HttpRequestMessage Rquest, - X509Certificate Cert, - X509Chain Chain, - SslPolicyErrors Errors - ) - { - if (Errors != SslPolicyErrors.None) - return false; - - if (TrustedSubjects.Contains(Cert.Subject)) - return true; - - Program.PrintColorMessage($"[CertValidationCallback] {Cert.Subject}", ConsoleColor.Red); - return false; - } } public class GithubRelease : IComparable, IComparable, IEquatable diff --git a/tools/CustomBuildTool/HeaderGen.cs b/tools/CustomBuildTool/HeaderGen.cs index f6b295c10a6d..2e399d8d1070 100644 --- a/tools/CustomBuildTool/HeaderGen.cs +++ b/tools/CustomBuildTool/HeaderGen.cs @@ -14,6 +14,7 @@ namespace CustomBuildTool { public static class HeaderGen { + private static readonly string Notice = "/*\r\n * Copyright (c) Winsider Seminars & Solutions, Inc. All rights reserved.\r\n *\r\n * This file is part of System Informer.\r\n *\r\n */\r\n\r\n"; private static readonly string Header = "#ifndef _PH_PHAPPPUB_H\r\n#define _PH_PHAPPPUB_H\r\n\r\n// This file was automatically generated. Do not edit.\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n"; private const string Footer = "\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"; @@ -148,11 +149,11 @@ public static void Execute() foreach (string line in h.Lines) { - string trimmed = line.Trim(); + var trimmed = line.AsSpan().Trim(); if (trimmed.StartsWith("#include <", StringComparison.OrdinalIgnoreCase) && trimmed.EndsWith(">", StringComparison.OrdinalIgnoreCase)) { - string dependencyName = trimmed.AsSpan().Slice("#include <".Length, trimmed.Length - 1 - "#include <".Length).ToString(); + string dependencyName = trimmed.Slice("#include <".Length, trimmed.Length - 1 - "#include <".Length).ToString(); if (headerFiles.TryGetValue(dependencyName, out HeaderFile dependency)) { @@ -199,6 +200,9 @@ public static void Execute() // Write out the result. using (StreamWriter sw = new StreamWriter(Path.Join([BaseDirectory, OutputFile]), false, Utils.UTF8NoBOM)) { + // Copyright + sw.Write(Notice); + // Header sw.Write(Header); @@ -248,9 +252,6 @@ public override int GetHashCode() public override bool Equals(object obj) { - if (obj == null) - return false; - if (obj is not HeaderFile file) return false; diff --git a/tools/CustomBuildTool/Http.cs b/tools/CustomBuildTool/Http.cs new file mode 100644 index 000000000000..8f3ba4ed5a25 --- /dev/null +++ b/tools/CustomBuildTool/Http.cs @@ -0,0 +1,114 @@ +/* + * Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved. + * + * This file is part of System Informer. + * + * Authors: + * + * dmex + * + */ + +namespace CustomBuildTool +{ + public static class Http + { + private static readonly string[] TrustedSubjects = + { + "CN=*.github.com", + "CN=*.github.com, O=\"GitHub, Inc.\", L=San Francisco, S=California, C=US", + "CN=sourceforge.io, O=\"Cloudflare, Inc.\", L=San Francisco, S=California, C=US" + }; + + private static bool CertValidationCallback( + HttpRequestMessage Rquest, + X509Certificate Cert, + X509Chain Chain, + SslPolicyErrors Errors + ) + { + if ( + Errors == SslPolicyErrors.None && + TrustedSubjects.Contains(Cert.Subject, StringComparer.OrdinalIgnoreCase) + ) + { + return true; + } + + Program.PrintColorMessage($"[CertValidationCallback] {Cert.Subject}", ConsoleColor.Red); + return false; + } + + public static HttpClient CreateHttpClient() + { + HttpClientHandler httpClientHandler = new HttpClientHandler(); + httpClientHandler.AutomaticDecompression = DecompressionMethods.All; + httpClientHandler.SslProtocols = SslProtocols.Tls12 | SslProtocols.Tls13; + httpClientHandler.ServerCertificateCustomValidationCallback = CertValidationCallback; + + HttpClient httpClient = new HttpClient(httpClientHandler); + httpClient.DefaultRequestVersion = HttpVersion.Version20; + httpClient.DefaultVersionPolicy = HttpVersionPolicy.RequestVersionOrHigher; + + return httpClient; + } + + public static HttpResponseMessage SendMessageResponse(HttpRequestMessage HttpMessage) + { + HttpResponseMessage response = null; + + try + { + response = Task.Run(async () => await CreateHttpClient().SendAsync(HttpMessage)).GetAwaiter().GetResult(); + } + catch (Exception) + { + return null; + } + + return response; + } + + public static string SendMessage(HttpRequestMessage HttpMessage) + { + string response = null; + + try + { + var httpTask = Task.Run(async () => await CreateHttpClient().SendAsync(HttpMessage)).GetAwaiter().GetResult(); + + if (httpTask.IsSuccessStatusCode) + { + response = Task.Run(httpTask.Content.ReadAsStringAsync).GetAwaiter().GetResult(); + } + } + catch (Exception) + { + return null; + } + + return response; + } + + public static TValue SendMessage(HttpRequestMessage HttpMessage, JsonTypeInfo jsonTypeInfo) where TValue : class + { + TValue result = default; + + try + { + string response = SendMessage(HttpMessage); + + if (string.IsNullOrEmpty(response)) + return null; + + result = JsonSerializer.Deserialize(response, jsonTypeInfo); + } + catch (Exception) + { + return null; + } + + return result; + } + } +} diff --git a/tools/CustomBuildTool/NativeMethods.cs b/tools/CustomBuildTool/NativeMethods.cs index 40e9891e3f2d..12696506b4b3 100644 --- a/tools/CustomBuildTool/NativeMethods.cs +++ b/tools/CustomBuildTool/NativeMethods.cs @@ -11,123 +11,20 @@ namespace CustomBuildTool { - public static unsafe partial class NativeMethods + public static class NativeMethods { - public const uint HRESULT_S_OK = 0u; - public const uint HRESULT_S_FALSE = 1u; - - public static readonly UIntPtr CURRENT_PROCESS = new UIntPtr(0xffffffffffffffff); - public static readonly IntPtr CURRENT_TOKEN = new IntPtr(-5); - public static readonly IntPtr HKEY_LOCAL_MACHINE = new IntPtr(unchecked((int)0x80000002)); - public static readonly IntPtr HKEY_CURRENT_USER = new IntPtr(unchecked((int)0x80000001)); - public static readonly uint KEY_READ = 0x20019u; - public static readonly uint HIGH_PRIORITY_CLASS = 0x00000080; - - public static readonly uint REG_NONE = 0; // No value type - public static readonly uint REG_SZ = 1; // Unicode nul terminated string - public static readonly uint REG_EXPAND_SZ = 2; // Unicode nul terminated string - public static readonly uint REG_BINARY = 3; // Free form binary - public static readonly uint REG_DWORD = 4; // 32-bit number - public static readonly uint REG_MULTI_SZ = 7; // Multiple Unicode strings - public static readonly uint REG_QWORD = 11; // 64-bit number - - [LibraryImport("userenv.dll", EntryPoint = "CreateEnvironmentBlock", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool CreateEnvironmentBlock(out IntPtr lpEnvironment, IntPtr hToken, [MarshalAs(UnmanagedType.Bool)] bool bInherit); - - [LibraryImport("userenv.dll", EntryPoint = "DestroyEnvironmentBlock", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool DestroyEnvironmentBlock(IntPtr lpEnvironment); - - [LibraryImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static unsafe partial bool GetFileInformationByHandleEx(SafeFileHandle hFile, int FileInformationClass, void* lpFileInformation, uint dwBufferSize); - [LibraryImport("kernel32.dll")] - [return: MarshalAs(UnmanagedType.Bool)] - public static unsafe partial bool SetFileInformationByHandle(SafeFileHandle hFile, int FileInformationClass, void* lpFileInformation, uint dwBufferSize); - - [LibraryImport("advapi32.dll", EntryPoint = "RegOpenKeyExW", StringMarshalling = StringMarshalling.Utf16)] - public static partial uint RegOpenKeyEx(IntPtr RootKeyHandle, string KeyName, uint Options, uint AccessMask, IntPtr* KeyHandle); - - [LibraryImport("advapi32.dll", EntryPoint = "RegQueryValueExW", StringMarshalling = StringMarshalling.Utf16)] - public static partial uint RegQueryValueEx(IntPtr KeyHandle, string ValueName, IntPtr Reserved, int* DataType, void* DataBuffer, int* DataLength); - - [LibraryImport("advapi32.dll", EntryPoint = "RegSetKeyValueW", StringMarshalling = StringMarshalling.Utf16)] - public static partial uint RegSetKeyValue(IntPtr KeyHandle, string SubKeyName, string ValueName, uint DataType, IntPtr DataBuffer, int DataLength); - - [LibraryImport("advapi32.dll", EntryPoint = "RegCloseKey")] - public static partial uint RegCloseKey(IntPtr KeyHandle); - - [LibraryImport("oleaut32.dll", EntryPoint = "SafeArrayGetLBound")] - public static partial uint SafeArrayGetLBound(IntPtr psa, uint nDim, out uint lLBound); - - [LibraryImport("oleaut32.dll", EntryPoint = "SafeArrayGetUBound")] - public static partial uint SafeArrayGetUBound(IntPtr psa, uint nDim, out uint lUBound); - - [LibraryImport("oleaut32.dll", EntryPoint = "SafeArrayGetElement")] - public static partial uint SafeArrayGetElement(IntPtr psa, uint* rgIndices, IntPtr* pv); - - [StructLayout(LayoutKind.Sequential)] - public struct FILETIME + public static DateTime FileTimeToDateTime(this System.Runtime.InteropServices.ComTypes.FILETIME FileTime) { - public int dwLowDateTime; - public int dwHighDateTime; - - public DateTime DateTime - { - get - { - long fileTime = ((long)dwHighDateTime << 32) + dwLowDateTime; - - return DateTime.FromFileTime(fileTime); - } - } + long fileTime = ((long)FileTime.dwHighDateTime << 32) + FileTime.dwLowDateTime; - public long FileTime - { - get { return ((long)dwHighDateTime << 32) + dwLowDateTime; } - } + return DateTime.FromFileTime(fileTime); } - [StructLayout(LayoutKind.Sequential)] - public struct WIN32_FILE_ATTRIBUTE_DATA + public static long FileTimeFromFileTime(this System.Runtime.InteropServices.ComTypes.FILETIME FileTime) { - public uint FileAttributes; - public FILETIME CreationTime; - public FILETIME LastAccessTime; - public FILETIME LastWriteTime; - public uint FileSizeHigh; - public uint FileSizeLow; - } + long fileTime = ((long)FileTime.dwHighDateTime << 32) + FileTime.dwLowDateTime; - [StructLayout(LayoutKind.Sequential)] - public ref struct FILE_BASIC_INFO - { - public long CreationTime; - public long LastAccessTime; - public long LastWriteTime; - public long ChangeTime; - public uint FileAttributes; + return fileTime; } - - [LibraryImport("kernel32.dll", EntryPoint = "GetFileAttributesExW", StringMarshalling = StringMarshalling.Utf16)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool GetFileAttributesEx(string FileName, uint InfoLevelId, out WIN32_FILE_ATTRIBUTE_DATA FileInformation); - - [LibraryImport("kernel32.dll", EntryPoint = "GetErrorMode")] - public static partial uint GetErrorMode(); - - public const uint SEM_NOGPFAULTERRORBOX = 0x0002; - public const uint SEM_NOOPENFILEERRORBOX = 0x8000; - - [LibraryImport("kernel32.dll", EntryPoint = "SetErrorMode")] - public static partial uint SetErrorMode(uint uMode); - - [LibraryImport("kernel32.dll", EntryPoint = "ExitProcess")] - public static partial void ExitProcess(int exitCode); - - [LibraryImport("kernel32.dll", SetLastError = true)] - [return: MarshalAs(UnmanagedType.Bool)] - public static partial bool SetPriorityClass(UIntPtr handle, uint priorityClass); } } diff --git a/tools/CustomBuildTool/NativeMethods.txt b/tools/CustomBuildTool/NativeMethods.txt new file mode 100644 index 000000000000..0add209df5db --- /dev/null +++ b/tools/CustomBuildTool/NativeMethods.txt @@ -0,0 +1,35 @@ +CertCloseStore +CertOpenStore +CreateEnvironmentBlock +DestroyEnvironmentBlock +ExitProcess +GetFileInformationByHandleEx +SetFileInformationByHandle +RegOpenKeyEx +RegQueryValueEx +RegSetKeyValue +RegCloseKey +SafeArrayGetLBound +SafeArrayGetUBound +SafeArrayGetElement +GetFileAttributesEx +GetCurrentProcess +GetErrorMode +SetErrorMode +SignerSignEx3 +SignerFreeSignerContext +SetPriorityClass +SetFileTime +CreateFile +WIN32_FILE_ATTRIBUTE_DATA +FILE_BASIC_INFO +HKEY_LOCAL_MACHINE +HKEY_CURRENT_USER +GENERIC_ACCESS_RIGHTS +INVALID_HANDLE_VALUE +HRESULT +S_OK +TRUST_E_SUBJECT_FORM_UNKNOWN +COR_E_BADIMAGEFORMAT +E_INVALIDARG +ERROR_BAD_FORMAT \ No newline at end of file diff --git a/tools/CustomBuildTool/Utils.cs b/tools/CustomBuildTool/Utils.cs index a19328d36771..939819d4b84f 100644 --- a/tools/CustomBuildTool/Utils.cs +++ b/tools/CustomBuildTool/Utils.cs @@ -16,6 +16,7 @@ public static class Utils private static Dictionary EnvironmentBlock = new Dictionary(StringComparer.OrdinalIgnoreCase); public static readonly Encoding UTF8NoBOM = new UTF8Encoding(encoderShouldEmitUTF8Identifier: false, throwOnInvalidBytes: true); private static string GitFilePath; + private static string MsBuildFilePath; private static string VsWhereFilePath; /// @@ -180,47 +181,27 @@ public static string GetVswhereFilePath() private static string GetMsbuildFilePath(BuildFlags Flags) { - string MsBuildFilePath = null; - List MsBuildPath = - [ - "\\MSBuild\\Current\\Bin\\amd64\\MSBuild.exe", - "\\MSBuild\\Current\\Bin\\MSBuild.exe", - "\\MSBuild\\15.0\\Bin\\MSBuild.exe" - ]; - - if (Flags.HasFlag(BuildFlags.BuildArm64bit) && RuntimeInformation.OSArchitecture == Architecture.Arm64) + if (string.IsNullOrWhiteSpace(MsBuildFilePath)) { - MsBuildPath.Insert(0, "\\MSBuild\\Current\\Bin\\arm64\\MSBuild.exe"); - } - - VisualStudioInstance instance = VisualStudio.GetVisualStudioInstance(); + List MsBuildPath = + [ + "\\MSBuild\\Current\\Bin\\amd64\\MSBuild.exe", + "\\MSBuild\\Current\\Bin\\MSBuild.exe", + "\\MSBuild\\15.0\\Bin\\MSBuild.exe" + ]; - if (instance != null) - { - foreach (string path in MsBuildPath) + if (Flags.HasFlag(BuildFlags.BuildArm64bit) && RuntimeInformation.OSArchitecture == Architecture.Arm64) { - string file = Path.Join([instance.Path, path]); - - if (File.Exists(file)) - { - MsBuildFilePath = file; - break; - } + MsBuildPath.Insert(0, "\\MSBuild\\Current\\Bin\\arm64\\MSBuild.exe"); } - } - if (string.IsNullOrWhiteSpace(MsBuildFilePath)) - { - // -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" - string vswhereResult = ExecuteVsWhereCommand( - "-latest -prerelease -products * -requiresAny -requires Microsoft.Component.MSBuild -property installationPath" - ); + VisualStudioInstance instance = VisualStudio.GetVisualStudioInstance(); - if (!string.IsNullOrWhiteSpace(vswhereResult)) + if (instance != null) { foreach (string path in MsBuildPath) { - string file = Path.Join([vswhereResult, path]); + string file = Path.Join([instance.Path, path]); if (File.Exists(file)) { @@ -229,6 +210,28 @@ private static string GetMsbuildFilePath(BuildFlags Flags) } } } + + if (string.IsNullOrWhiteSpace(MsBuildFilePath)) + { + // -latest -requires Microsoft.Component.MSBuild -find "MSBuild\**\Bin\MSBuild.exe" + string vswhereResult = ExecuteVsWhereCommand( + "-latest -prerelease -products * -requiresAny -requires Microsoft.Component.MSBuild -property installationPath" + ); + + if (!string.IsNullOrWhiteSpace(vswhereResult)) + { + foreach (string path in MsBuildPath) + { + string file = Path.Join([vswhereResult, path]); + + if (File.Exists(file)) + { + MsBuildFilePath = file; + break; + } + } + } + } } return MsBuildFilePath; @@ -659,17 +662,17 @@ public static string ExpandFullPath(string Name) return value; } - public static Dictionary GetSystemEnvironmentBlock() + public static unsafe Dictionary GetSystemEnvironmentBlock() { if (EnvironmentBlock.Count == 0) { - if (NativeMethods.CreateEnvironmentBlock(out IntPtr block, IntPtr.Zero, false)) + if (PInvoke.CreateEnvironmentBlock(out var block, null, false)) { - IntPtr offset = block; + char* offset = (char*)block; - while (offset != IntPtr.Zero) + while (offset != null) { - string variable = Marshal.PtrToStringUni(offset); + string variable = new string(offset); if (string.IsNullOrEmpty(variable)) break; @@ -677,10 +680,10 @@ public static Dictionary GetSystemEnvironmentBlock() string[] parts = variable.Split('=', StringSplitOptions.RemoveEmptyEntries | StringSplitOptions.TrimEntries); EnvironmentBlock.Add(parts[0], parts.Length <= 1 ? string.Empty : parts[1]); - offset = new IntPtr(offset.ToInt64() + (variable.Length + 1) * sizeof(char)); + offset += variable.Length + 1; } - NativeMethods.DestroyEnvironmentBlock(block); + PInvoke.DestroyEnvironmentBlock(block); } } diff --git a/tools/CustomBuildTool/Vault.cs b/tools/CustomBuildTool/Vault.cs new file mode 100644 index 000000000000..f82300316238 --- /dev/null +++ b/tools/CustomBuildTool/Vault.cs @@ -0,0 +1,109 @@ +/* + * Copyright (c) 2022 Winsider Seminars & Solutions, Inc. All rights reserved. + * + * This file is part of System Informer. + * + * Authors: + * + * dmex + * + */ + +namespace CustomBuildTool +{ + public static class Vault + { + private static string ENTRA_TIMESTAMP_ALGORITHM; + private static string ENTRA_TIMESTAMP_SERVER; + private static string ENTRA_CERIFICATE_NAME; + private static string ENTRA_CERIFICATE_VAULT; + private static string ENTRA_TENANT_GUID; + private static string ENTRA_CLIENT_GUID; + private static string ENTRA_CLIENT_SECRET; + + static Vault() + { + //ENTRA_TIMESTAMP_ALGORITHM = Win32.GetEnvironmentVariable("BUILD_TIMESTAMP_ALGORITHM"); + //ENTRA_TIMESTAMP_SERVER = Win32.GetEnvironmentVariable("BUILD_TIMESTAMP_SERVER"); + ENTRA_CERIFICATE_NAME = Win32.GetEnvironmentVariable("BUILD_ENTRA_CERT_ID"); + ENTRA_CERIFICATE_VAULT = Win32.GetEnvironmentVariable("BUILD_ENTRA_VAULT_ID"); + ENTRA_TENANT_GUID = Win32.GetEnvironmentVariable("BUILD_ENTRA_TENANT_ID"); + ENTRA_CLIENT_GUID = Win32.GetEnvironmentVariable("BUILD_ENTRA_CLIENT_ID"); + ENTRA_CLIENT_SECRET = Win32.GetEnvironmentVariable("BUILD_ENTRA_SECRET_ID"); + } + + public static bool SignFiles(string Path, string SearchPattern) + { + //if (string.IsNullOrWhiteSpace(ENTRA_TIMESTAMP_ALGORITHM)) + // return false; + //if (string.IsNullOrWhiteSpace(ENTRA_TIMESTAMP_SERVER)) + // return false; + if (string.IsNullOrWhiteSpace(ENTRA_CERIFICATE_NAME)) + return false; + if (string.IsNullOrWhiteSpace(ENTRA_CERIFICATE_VAULT)) + return false; + if (string.IsNullOrWhiteSpace(ENTRA_TENANT_GUID)) + return false; + if (string.IsNullOrWhiteSpace(ENTRA_CLIENT_GUID)) + return false; + if (string.IsNullOrWhiteSpace(ENTRA_CLIENT_SECRET)) + return false; + + try + { + var timeStampConfiguration = new TimeStampConfiguration("https://timestamp.digicert.com", HashAlgorithmName.SHA256, TimeStampType.RFC3161); + var clientCredential = new Azure.Identity.ClientSecretCredential(ENTRA_TENANT_GUID, ENTRA_CLIENT_GUID, ENTRA_CLIENT_SECRET); + + if (clientCredential == null) + { + Program.PrintColorMessage($"[ERROR] Identity", ConsoleColor.Red); + return false; + } + + var certificateClient = new Azure.Security.KeyVault.Certificates.CertificateClient(new Uri(ENTRA_CERIFICATE_VAULT), clientCredential); + var azureCertificate = certificateClient.GetCertificateAsync("mynewcert").GetAwaiter().GetResult(); + + if (!azureCertificate.HasValue) + return false; + + var certificateRaw = new X509Certificate2(azureCertificate.Value.Cer); + var azureKeyVault = new AzureKeyVaultMaterializedConfiguration(clientCredential, certificateRaw, azureCertificate.Value.KeyId); + + using (var rsa = RSAKeyVaultProvider.RSAFactory.Create(azureKeyVault.TokenCredential, azureKeyVault.KeyId, azureKeyVault.PublicCertificate)) + using (var sig = new AuthenticodeKeyVaultSigner(rsa, azureKeyVault.PublicCertificate, HashAlgorithmName.SHA256, timeStampConfiguration, null)) + { + var files = Directory.EnumerateFiles(Path, SearchPattern, SearchOption.AllDirectories); + + foreach (var file in files) + { + var result = sig.SignFile(file, null, null, true); + + if (result == HRESULT.COR_E_BADIMAGEFORMAT) + { + Program.PrintColorMessage($"[ERROR] The AppxManifest.xml publisher CN does not match the certificate: {file}", ConsoleColor.Red); + } + else if (result == HRESULT.TRUST_E_SUBJECT_FORM_UNKNOWN) + { + Console.WriteLine($"[ERROR] The file type is not supported or corrupt: {file}", ConsoleColor.Red); + } + else if (result == HRESULT.S_OK) + { + Console.WriteLine($"[Verified] {file}", ConsoleColor.Green); + } + else + { + Console.WriteLine($"[ERROR] Unknown failure: ({result}) {file}", ConsoleColor.Red); + } + } + } + } + catch (Exception ex) + { + Program.PrintColorMessage($"[ERROR] {ex}", ConsoleColor.Red); + return false; + } + + return true; + } + } +} diff --git a/tools/CustomBuildTool/Verify.cs b/tools/CustomBuildTool/Verify.cs index f11fa7d8dfc0..0df4fb799b74 100644 --- a/tools/CustomBuildTool/Verify.cs +++ b/tools/CustomBuildTool/Verify.cs @@ -15,11 +15,11 @@ public static class Verify { public static readonly SortedDictionary KeyName_Vars = new(StringComparer.OrdinalIgnoreCase) { - { "kph", "KPH_BUILD_KEY" }, - { "release", "RELEASE_BUILD_KEY" }, - { "preview", "PREVIEW_BUILD_KEY" }, { "canary", "CANARY_BUILD_KEY" }, { "developer", "DEVELOPER_BUILD_KEY" }, + { "kph", "KPH_BUILD_KEY" }, + { "preview", "PREVIEW_BUILD_KEY" }, + { "release", "RELEASE_BUILD_KEY" }, }; public static bool EncryptFile(string FileName, string OutFileName, string Secret) @@ -188,8 +188,8 @@ private static byte[] Decrypt(byte[] Bytes, string Secret) { using (var rijndael = GetRijndael(Secret)) using (var cryptoDecrypt = rijndael.CreateDecryptor()) - using (MemoryStream blobStream = new MemoryStream(Bytes)) - using (CryptoStream cryptoStream = new CryptoStream(memoryStream, cryptoDecrypt, CryptoStreamMode.Write, true)) + using (var blobStream = new MemoryStream(Bytes)) + using (var cryptoStream = new CryptoStream(memoryStream, cryptoDecrypt, CryptoStreamMode.Write, true)) { blobStream.CopyTo(cryptoStream); } diff --git a/tools/CustomBuildTool/VisualStudio.cs b/tools/CustomBuildTool/VisualStudio.cs index b5f910aa4c33..a29c7ac263e3 100644 --- a/tools/CustomBuildTool/VisualStudio.cs +++ b/tools/CustomBuildTool/VisualStudio.cs @@ -24,7 +24,7 @@ static VisualStudio() { if (NativeLibrary.TryGetExport(baseAddress, "GetSetupConfiguration", out IntPtr ExportAddressPtr)) { - var GetSetupConfiguration_I = (delegate* unmanaged[Stdcall])ExportAddressPtr; + var GetSetupConfiguration_I = (delegate* unmanaged[Stdcall])ExportAddressPtr; IntPtr SetupConfigurationInterfacePtr; IntPtr SetupConfiguration2InterfacePtr; IntPtr EnumSetupInstancesInterfacePtr; @@ -35,7 +35,7 @@ static VisualStudio() if (GetSetupConfiguration_I( &SetupConfigurationInterfacePtr, IntPtr.Zero - ) == NativeMethods.HRESULT_S_OK) + ).Succeeded) { var setupInterface = *(ISetupConfigurationVTable**)SetupConfigurationInterfacePtr; @@ -43,21 +43,21 @@ static VisualStudio() SetupConfigurationInterfacePtr, IID_ISetupConfiguration2, &SetupConfiguration2InterfacePtr - ) == NativeMethods.HRESULT_S_OK) + ).Succeeded) { var setupConfiguration = *(ISetupConfiguration2VTable**)SetupConfiguration2InterfacePtr; if (setupConfiguration->EnumAllInstances( SetupConfiguration2InterfacePtr, &EnumSetupInstancesInterfacePtr - ) == NativeMethods.HRESULT_S_OK) + ).Succeeded) { var enumSetupInstances = *(IEnumSetupInstancesVTable**)EnumSetupInstancesInterfacePtr; while (true) { - if (enumSetupInstances->Next(EnumSetupInstancesInterfacePtr, 1, &SetupInstancePtr, &instancesFetched) != NativeMethods.HRESULT_S_OK) - break; + enumSetupInstances->Next(EnumSetupInstancesInterfacePtr, 1, &SetupInstancePtr, &instancesFetched); + if (instancesFetched == 0) break; @@ -67,7 +67,7 @@ static VisualStudio() SetupInstancePtr, IID_ISetupInstance2, &SetupInstance2Ptr - ) == NativeMethods.HRESULT_S_OK) + ).Succeeded) { var setupInstance2 = *(ISetupInstance2VTable**)SetupInstance2Ptr; VisualStudioInstanceList.Add(new VisualStudioInstance(setupInstance2, SetupInstance2Ptr)); @@ -106,22 +106,16 @@ static VisualStudio() }); } - public static VisualStudioInstance GetVisualStudioInstance() + internal static VisualStudioInstance GetVisualStudioInstance() { if (VisualStudioInstance == null && VisualStudioInstanceList != null) { - foreach (VisualStudioInstance instance in VisualStudioInstanceList) - { - //if (instance.HasRequiredDependency) - { - VisualStudioInstance = instance; - break; - } - //else - //{ - // Program.PrintColorMessage($"[WARN] {instance.DisplayName} does not have required the packages:\n{instance.MissingDependencyList}", ConsoleColor.Yellow); - //} - } + //if (VisualStudioInstanceList.Any(t => t.Name.Contains("preview", StringComparison.OrdinalIgnoreCase))) + //{ + // return VisualStudioInstanceList.Last(); // stable + //} + + VisualStudioInstance = VisualStudioInstanceList.First(); } return VisualStudioInstance; @@ -157,7 +151,7 @@ private static string GetLibraryPath() } } - public unsafe class VisualStudioInstance : IComparable, IComparable + internal unsafe class VisualStudioInstance : IComparable, IComparable { public string DisplayName { get; } public string Name { get; } @@ -179,7 +173,7 @@ public VisualStudioInstance(ISetupInstance2VTable* FromInstance, IntPtr SetupIns IntPtr PathPtr; IntPtr VersionPtr; IntPtr DisplayNamePtr; - IntPtr SetupPackagesArrayPtr; + Windows.Win32.System.Com.SAFEARRAY* SetupPackagesArrayPtr; IntPtr SetupPackagePtr; uint SetupPackageState; @@ -188,47 +182,47 @@ public VisualStudioInstance(ISetupInstance2VTable* FromInstance, IntPtr SetupIns // this.InstanceId = Marshal.PtrToStringBSTR(InstancePtr); //} - if (FromInstance->GetInstallationName(SetupInstancePtr, &NamePtr) == NativeMethods.HRESULT_S_OK && NamePtr != IntPtr.Zero) + if (FromInstance->GetInstallationName(SetupInstancePtr, &NamePtr).Succeeded && NamePtr != IntPtr.Zero) { this.Name = Marshal.PtrToStringBSTR(NamePtr); } - if (FromInstance->GetInstallationPath(SetupInstancePtr, &PathPtr) == NativeMethods.HRESULT_S_OK && PathPtr != IntPtr.Zero) + if (FromInstance->GetInstallationPath(SetupInstancePtr, &PathPtr).Succeeded && PathPtr != IntPtr.Zero) { this.Path = Marshal.PtrToStringBSTR(PathPtr); } - if (FromInstance->GetInstallationVersion(SetupInstancePtr, &VersionPtr) == NativeMethods.HRESULT_S_OK && VersionPtr != IntPtr.Zero) + if (FromInstance->GetInstallationVersion(SetupInstancePtr, &VersionPtr).Succeeded && VersionPtr != IntPtr.Zero) { this.InstallationVersion = Marshal.PtrToStringBSTR(VersionPtr); } - if (FromInstance->GetDisplayName(SetupInstancePtr, 0, &DisplayNamePtr) == NativeMethods.HRESULT_S_OK && DisplayNamePtr != IntPtr.Zero) + if (FromInstance->GetDisplayName(SetupInstancePtr, 0, &DisplayNamePtr).Succeeded && DisplayNamePtr != IntPtr.Zero) { this.DisplayName = Marshal.PtrToStringBSTR(DisplayNamePtr); } - if (FromInstance->GetState(SetupInstancePtr, &SetupPackageState) == NativeMethods.HRESULT_S_OK) + if (FromInstance->GetState(SetupInstancePtr, &SetupPackageState).Succeeded) { this.State = SetupPackageState; } this.Packages = new List(); - if (FromInstance->GetPackages(SetupInstancePtr, &SetupPackagesArrayPtr) == NativeMethods.HRESULT_S_OK) + if (FromInstance->GetPackages(SetupInstancePtr, &SetupPackagesArrayPtr).Succeeded) { - if (NativeMethods.SafeArrayGetLBound(SetupPackagesArrayPtr, 1, out uint lbound) == NativeMethods.HRESULT_S_OK && - NativeMethods.SafeArrayGetUBound(SetupPackagesArrayPtr, 1, out uint ubound) == NativeMethods.HRESULT_S_OK) + if (PInvoke.SafeArrayGetLBound(SetupPackagesArrayPtr, 1, out var lbound).Succeeded && + PInvoke.SafeArrayGetUBound(SetupPackagesArrayPtr, 1, out var ubound).Succeeded) { - uint count = ubound - lbound + 1; + var count = ubound - lbound + 1; - for (uint i = 0; i < count; i++) + for (var i = 0; i < count; i++) { - if (NativeMethods.SafeArrayGetElement( + if (PInvoke.SafeArrayGetElement( SetupPackagesArrayPtr, &i, &SetupPackagePtr - ) == NativeMethods.HRESULT_S_OK) + ).Succeeded) { var package = *(ISetupPackageReferenceVTable**)SetupPackagePtr; @@ -395,7 +389,7 @@ public int CompareTo(VisualStudioInstance obj) } } - public unsafe class VisualStudioPackage : IComparable, IComparable + internal unsafe class VisualStudioPackage : IComparable, IComparable { public string Id { get; } public string Version { get; } @@ -405,12 +399,12 @@ public VisualStudioPackage(ISetupPackageReferenceVTable* FromInstance, IntPtr Se IntPtr IdPtr; IntPtr VersionPtr; - if (FromInstance->GetId(SetupInstancePtr, &IdPtr) == NativeMethods.HRESULT_S_OK && IdPtr != IntPtr.Zero) + if (FromInstance->GetId(SetupInstancePtr, &IdPtr).Succeeded && IdPtr != IntPtr.Zero) { this.Id = Marshal.PtrToStringBSTR(IdPtr); } - if (FromInstance->GetVersion(SetupInstancePtr, &VersionPtr) == NativeMethods.HRESULT_S_OK && VersionPtr != IntPtr.Zero) + if (FromInstance->GetVersion(SetupInstancePtr, &VersionPtr).Succeeded && VersionPtr != IntPtr.Zero) { this.Version = Marshal.PtrToStringBSTR(VersionPtr); } @@ -453,48 +447,48 @@ public enum InstanceState : uint } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupHelperVTable + internal readonly unsafe struct ISetupHelperVTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // ISetupHelper - public readonly delegate* unmanaged[Stdcall] ParseVersion; - public readonly delegate* unmanaged[Stdcall] ParseVersionRange; + public readonly delegate* unmanaged[Stdcall] ParseVersion; + public readonly delegate* unmanaged[Stdcall] ParseVersionRange; } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupConfigurationVTable + internal readonly unsafe struct ISetupConfigurationVTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // STDMETHOD(EnumInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0; - public readonly delegate* unmanaged[Stdcall] EnumInstances; + public readonly delegate* unmanaged[Stdcall] EnumInstances; // STDMETHOD(GetInstanceForCurrentProcess)(_Out_ ISetupInstance ** ppInstance) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstanceForCurrentProcess; + public readonly delegate* unmanaged[Stdcall] GetInstanceForCurrentProcess; // STDMETHOD(GetInstanceForPath)(_In_z_ LPCWSTR wzPath, _Out_ ISetupInstance **ppInstance) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstanceForPath; + public readonly delegate* unmanaged[Stdcall] GetInstanceForPath; } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupConfiguration2VTable + internal readonly unsafe struct ISetupConfiguration2VTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // STDMETHOD(EnumAllInstances)(_Out_ IEnumSetupInstances **ppEnumInstances) = 0; - public readonly delegate* unmanaged[Stdcall] EnumAllInstances; + public readonly delegate* unmanaged[Stdcall] EnumAllInstances; } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct IEnumSetupInstancesVTable + internal readonly unsafe struct IEnumSetupInstancesVTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // STDMETHOD(Next)(_In_ ULONG celt, _Out_writes_to_(celt, *pceltFetched) ISetupInstance** rgelt, _Out_opt_ _Deref_out_range_(0, celt) ULONG* pceltFetched) = 0; - public readonly delegate* unmanaged[Stdcall] Next; + public readonly delegate* unmanaged[Stdcall] Next; // STDMETHOD(Skip)(_In_ ULONG celt) = 0; public readonly delegate* unmanaged[Stdcall] Skip; // STDMETHOD(Reset)(void) = 0; @@ -504,96 +498,96 @@ public readonly unsafe struct IEnumSetupInstancesVTable } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupInstanceVTable + internal readonly unsafe struct ISetupInstanceVTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // ISetupInstance // STDMETHOD(GetInstanceId)(_Out_ BSTR *pbstrInstanceId) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstanceId; + public readonly delegate* unmanaged[Stdcall] GetInstanceId; // STDMETHOD(GetInstallDate)(_Out_ LPFILETIME pInstallDate) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallDate; + public readonly delegate* unmanaged[Stdcall] GetInstallDate; // STDMETHOD(GetInstallationName)(_Out_ BSTR *pbstrInstallationName) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationName; + public readonly delegate* unmanaged[Stdcall] GetInstallationName; // STDMETHOD(GetInstallationPath)(_Out_ BSTR *pbstrInstallationPath) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationPath; + public readonly delegate* unmanaged[Stdcall] GetInstallationPath; // STDMETHOD(GetInstallationVersion)(_Out_ BSTR *pbstrInstallationVersion) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationVersion; + public readonly delegate* unmanaged[Stdcall] GetInstallationVersion; // STDMETHOD(GetDisplayName)(_In_ LCID lcid, _Out_ BSTR *pbstrDisplayName) = 0; - public readonly delegate* unmanaged[Stdcall] GetDisplayName; + public readonly delegate* unmanaged[Stdcall] GetDisplayName; // STDMETHOD(GetDescription)(_In_ LCID lcid, _Out_ BSTR *pbstrDescription) = 0; - public readonly delegate* unmanaged[Stdcall] GetDescription; + public readonly delegate* unmanaged[Stdcall] GetDescription; // STDMETHOD(ResolvePath)(_In_opt_z_ LPCOLESTR pwszRelativePath, _Out_ BSTR* pbstrAbsolutePath) = 0; - public readonly delegate* unmanaged[Stdcall] ResolvePath; + public readonly delegate* unmanaged[Stdcall] ResolvePath; } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupInstance2VTable + internal readonly unsafe struct ISetupInstance2VTable { public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // ISetupInstance // STDMETHOD(GetInstanceId)(_Out_ BSTR *pbstrInstanceId) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstanceId; + public readonly delegate* unmanaged[Stdcall] GetInstanceId; // STDMETHOD(GetInstallDate)(_Out_ LPFILETIME pInstallDate) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallDate; + public readonly delegate* unmanaged[Stdcall] GetInstallDate; // STDMETHOD(GetInstallationName)(_Out_ BSTR *pbstrInstallationName) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationName; + public readonly delegate* unmanaged[Stdcall] GetInstallationName; // STDMETHOD(GetInstallationPath)(_Out_ BSTR *pbstrInstallationPath) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationPath; + public readonly delegate* unmanaged[Stdcall] GetInstallationPath; // STDMETHOD(GetInstallationVersion)(_Out_ BSTR *pbstrInstallationVersion) = 0; - public readonly delegate* unmanaged[Stdcall] GetInstallationVersion; + public readonly delegate* unmanaged[Stdcall] GetInstallationVersion; // STDMETHOD(GetDisplayName)(_In_ LCID lcid, _Out_ BSTR *pbstrDisplayName) = 0; - public readonly delegate* unmanaged[Stdcall] GetDisplayName; + public readonly delegate* unmanaged[Stdcall] GetDisplayName; // STDMETHOD(GetDescription)(_In_ LCID lcid, _Out_ BSTR *pbstrDescription) = 0; - public readonly delegate* unmanaged[Stdcall] GetDescription; + public readonly delegate* unmanaged[Stdcall] GetDescription; // STDMETHOD(ResolvePath)(_In_opt_z_ LPCOLESTR pwszRelativePath, _Out_ BSTR* pbstrAbsolutePath) = 0; - public readonly delegate* unmanaged[Stdcall] ResolvePath; + public readonly delegate* unmanaged[Stdcall] ResolvePath; // ISetupInstance2 // STDMETHOD(GetState)(_Out_ InstanceState *pState) = 0; - public readonly delegate* unmanaged[Stdcall] GetState; + public readonly delegate* unmanaged[Stdcall] GetState; // STDMETHOD(GetPackages)(_Out_ LPSAFEARRAY *ppsaPackages) = 0; - public readonly delegate* unmanaged[Stdcall] GetPackages; + public readonly delegate* unmanaged[Stdcall] GetPackages; // STDMETHOD(GetProduct)(_Outptr_result_maybenull_ ISetupPackageReference **ppPackage) = 0; - public readonly delegate* unmanaged[Stdcall] GetProduct; + public readonly delegate* unmanaged[Stdcall] GetProduct; // STDMETHOD(GetProductPath)(_Outptr_result_maybenull_ BSTR *pbstrProductPath) = 0; - public readonly delegate* unmanaged[Stdcall] GetProductPath; + public readonly delegate* unmanaged[Stdcall] GetProductPath; // STDMETHOD(GetErrors)(_Outptr_result_maybenull_ ISetupErrorState** ppErrorState) = 0; - public readonly delegate* unmanaged[Stdcall] GetErrors; + public readonly delegate* unmanaged[Stdcall] GetErrors; // STDMETHOD(IsLaunchable)(_Out_ VARIANT_BOOL* pfIsLaunchable) = 0; - public readonly delegate* unmanaged[Stdcall] IsLaunchable; + public readonly delegate* unmanaged[Stdcall] IsLaunchable; // STDMETHOD(IsComplete)(_Out_ VARIANT_BOOL* pfIsComplete) = 0; - public readonly delegate* unmanaged[Stdcall] IsComplete; + public readonly delegate* unmanaged[Stdcall] IsComplete; // STDMETHOD(GetProperties)(_Outptr_result_maybenull_ ISetupPropertyStore** ppProperties) = 0; - public readonly delegate* unmanaged[Stdcall] GetProperties; + public readonly delegate* unmanaged[Stdcall] GetProperties; // STDMETHOD(GetEnginePath)(_Outptr_result_maybenull_ BSTR* pbstrEnginePath) = 0; - public readonly delegate* unmanaged[Stdcall] GetEnginePath; + public readonly delegate* unmanaged[Stdcall] GetEnginePath; } [StructLayout(LayoutKind.Sequential)] - public readonly unsafe struct ISetupPackageReferenceVTable + internal readonly unsafe struct ISetupPackageReferenceVTable { - public readonly delegate* unmanaged[Stdcall] QueryInterface; + public readonly delegate* unmanaged[Stdcall] QueryInterface; public readonly delegate* unmanaged[Stdcall] AddRef; public readonly delegate* unmanaged[Stdcall] Release; // ISetupPackageReference // STDMETHOD(GetId)(_Out_ BSTR* pbstrId) = 0; - public readonly delegate* unmanaged[Stdcall] GetId; + public readonly delegate* unmanaged[Stdcall] GetId; // STDMETHOD(GetVersion)(_Out_ BSTR* pbstrVersion) = 0; - public readonly delegate* unmanaged[Stdcall] GetVersion; + public readonly delegate* unmanaged[Stdcall] GetVersion; // STDMETHOD(GetChip)(_Out_ BSTR* pbstrChip) = 0; - public readonly delegate* unmanaged[Stdcall] GetChip; + public readonly delegate* unmanaged[Stdcall] GetChip; // STDMETHOD(GetLanguage)(_Out_ BSTR* pbstrLanguage) = 0; - public readonly delegate* unmanaged[Stdcall] GetLanguage; + public readonly delegate* unmanaged[Stdcall] GetLanguage; // STDMETHOD(GetBranch)(_Out_ BSTR* pbstrBranch) = 0; - public readonly delegate* unmanaged[Stdcall] GetBranch; + public readonly delegate* unmanaged[Stdcall] GetBranch; // STDMETHOD(GetType)(_Out_ BSTR* pbstrType) = 0; - public readonly delegate* unmanaged[Stdcall] GetPackageType; + public readonly delegate* unmanaged[Stdcall] GetPackageType; // STDMETHOD(GetUniqueId)(_Out_ BSTR* pbstrUniqueId) = 0; - public readonly delegate* unmanaged[Stdcall] GetUniqueId; + public readonly delegate* unmanaged[Stdcall] GetUniqueId; // STDMETHOD(GetIsExtension)(_Out_ BSTR* pfIsExtension) = 0; - public readonly delegate* unmanaged[Stdcall] GetIsExtension; + public readonly delegate* unmanaged[Stdcall] GetIsExtension; } } diff --git a/tools/CustomBuildTool/Win32.cs b/tools/CustomBuildTool/Win32.cs index 39a810b37986..b865aad900f9 100644 --- a/tools/CustomBuildTool/Win32.cs +++ b/tools/CustomBuildTool/Win32.cs @@ -168,7 +168,7 @@ public static string SearchPath(string FileName) return null; } - public static void CopyIfNewer(string SourceFile, string DestinationFile) + public static void CopyIfNewer(string SourceFile, string DestinationFile, bool Verbose = false) { if (!File.Exists(SourceFile)) { @@ -186,9 +186,23 @@ public static void CopyIfNewer(string SourceFile, string DestinationFile) string directory = Path.GetDirectoryName(DestinationFile); if (string.IsNullOrWhiteSpace(directory)) + { + if (Verbose) + { + Program.PrintColorMessage("[Verbose] GetDirectoryName IsNullOrWhiteSpace", ConsoleColor.Yellow); + } return; + } - Win32.CreateDirectory(directory); + if (!Directory.Exists(directory)) + { + Win32.CreateDirectory(directory); + + if (Verbose) + { + Program.PrintColorMessage($"[Verbose] CreateDirectory: {directory}", ConsoleColor.Yellow); + } + } } if (File.Exists(DestinationFile)) @@ -202,11 +216,28 @@ public static void CopyIfNewer(string SourceFile, string DestinationFile) ) { File.Copy(SourceFile, DestinationFile, true); + + if (Verbose) + { + Program.PrintColorMessage($"[Verbose] Copied new file: {SourceFile} --> {DestinationFile}", ConsoleColor.Yellow); + } + } + else + { + if (Verbose) + { + Program.PrintColorMessage($"[Verbose] Skipped older file: {SourceFile} --> {DestinationFile}", ConsoleColor.Yellow); + } } } else { File.Copy(SourceFile, DestinationFile, true); + + if (Verbose) + { + Program.PrintColorMessage($"[Verbose] Copied new file: {SourceFile} --> {DestinationFile}", ConsoleColor.Yellow); + } } } @@ -326,60 +357,69 @@ public static string GetKernelVersion() public static unsafe string GetKeyValue(bool LocalMachine, string KeyName, string ValueName, string DefaultValue) { + Windows.Win32.System.Registry.HKEY keyHandle; string value = string.Empty; - void* valueBuffer; - IntPtr keyHandle; - - if (NativeMethods.RegOpenKeyEx( - LocalMachine ? NativeMethods.HKEY_LOCAL_MACHINE : NativeMethods.HKEY_CURRENT_USER, - KeyName, - 0, - NativeMethods.KEY_READ, - &keyHandle - ) == 0) + byte* valueBuffer; + + fixed (char* p = KeyName) { - int valueLength = 0; - int valueType = 0; - - NativeMethods.RegQueryValueEx( - keyHandle, - ValueName, - IntPtr.Zero, - &valueType, - null, - &valueLength - ); - - if (valueType == 1 || valueLength > 4) + if (PInvoke.RegOpenKeyEx( + LocalMachine ? Windows.Win32.System.Registry.HKEY.HKEY_LOCAL_MACHINE : Windows.Win32.System.Registry.HKEY.HKEY_CURRENT_USER, + p, + 0, + Windows.Win32.System.Registry.REG_SAM_FLAGS.KEY_READ, + &keyHandle + ) == 0) { - valueBuffer = NativeMemory.Alloc((nuint)valueLength); + uint valueLength = 0; + Windows.Win32.System.Registry.REG_VALUE_TYPE valueType = 0; - if (NativeMethods.RegQueryValueEx( + PInvoke.RegQueryValueEx( keyHandle, - ValueName, - IntPtr.Zero, + p, + null, + &valueType, null, - valueBuffer, &valueLength - ) == 0) + ); + + if (valueType == Windows.Win32.System.Registry.REG_VALUE_TYPE.REG_SZ || valueLength > 4) { - value = Marshal.PtrToStringUni((nint)valueBuffer, valueLength / 2 - 1); + valueBuffer = (byte*)NativeMemory.Alloc(valueLength); + + if (PInvoke.RegQueryValueEx( + keyHandle, + p, + null, + null, + valueBuffer, + &valueLength + ) == 0) + { + value = Marshal.PtrToStringUni((nint)valueBuffer, (int)valueLength / 2 - 1); + } + + NativeMemory.Free(valueBuffer); } - NativeMemory.Free(valueBuffer); + _ = PInvoke.RegCloseKey(keyHandle); } - - _ = NativeMethods.RegCloseKey(keyHandle); } return string.IsNullOrWhiteSpace(value) ? DefaultValue : value; } - public static long GetFileSize(string FileName) + public static unsafe long GetFileSize(string FileName) { - if (NativeMethods.GetFileAttributesEx(FileName, 0, out var fileAttributeData)) + WIN32_FILE_ATTRIBUTE_DATA sourceFile = new WIN32_FILE_ATTRIBUTE_DATA(); + + if (PInvoke.GetFileAttributesEx( + FileName, + GET_FILEEX_INFO_LEVELS.GetFileExInfoStandard, + &sourceFile + )) { - return ((long)fileAttributeData.FileSizeHigh) << 32 | fileAttributeData.FileSizeLow & 0xFFFFFFFFL; + return ((long)sourceFile.nFileSizeHigh) << 32 | sourceFile.nFileSizeLow & 0xFFFFFFFFL; } return 0; @@ -399,13 +439,19 @@ public static Version GetFileVersion(string FileName) public static void SetErrorMode() { - NativeMethods.SetErrorMode(NativeMethods.SEM_NOGPFAULTERRORBOX | NativeMethods.SEM_NOOPENFILEERRORBOX); + PInvoke.SetErrorMode( + THREAD_ERROR_MODE.SEM_NOGPFAULTERRORBOX | + THREAD_ERROR_MODE.SEM_NOOPENFILEERRORBOX + ); } public static void SetBasePriority() { //Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High; - NativeMethods.SetPriorityClass(NativeMethods.CURRENT_PROCESS, NativeMethods.HIGH_PRIORITY_CLASS); + PInvoke.SetPriorityClass( + PInvoke.GetCurrentProcess(), + Windows.Win32.System.Threading.PROCESS_CREATION_FLAGS.HIGH_PRIORITY_CLASS + ); } public static unsafe void SetFileTime( @@ -414,12 +460,15 @@ public static unsafe void SetFileTime( long LastWriteDateTime ) { - NativeMethods.FILE_BASIC_INFO basicInfo; + FILE_BASIC_INFO basicInfo; + basicInfo.CreationTime = CreationDateTime == 0 ? DateTime.UtcNow.ToFileTimeUtc() : CreationDateTime; basicInfo.LastWriteTime = LastWriteDateTime == 0 ? DateTime.UtcNow.ToFileTimeUtc() : LastWriteDateTime; using var fs = File.OpenWrite(FileName); - NativeMethods.SetFileInformationByHandle(fs.SafeFileHandle, 0, &basicInfo, (uint)sizeof(NativeMethods.FILE_BASIC_INFO)); + { + PInvoke.SetFileInformationByHandle(fs.SafeFileHandle, 0, &basicInfo, (uint)sizeof(FILE_BASIC_INFO)); + } } } } diff --git a/tools/CustomBuildTool/Zip.cs b/tools/CustomBuildTool/Zip.cs index c265e6977723..9f3a98e2e992 100644 --- a/tools/CustomBuildTool/Zip.cs +++ b/tools/CustomBuildTool/Zip.cs @@ -13,107 +13,133 @@ namespace CustomBuildTool { public static class Zip { - // https://github.com/phofman/zip/blob/master/src/ZipFile.cs - private static string[] GetEntryNames(string[] names, string sourceFolder, bool includeBaseName) + // Modified based on GetEntryNames https://github.com/phofman/zip/blob/master/src/ZipFile.cs + private static Dictionary GetEntryNames(string SourceDirectory, bool IncludeBaseName, Dictionary KeyValuePairs) { - if (names == null || names.Length == 0) - return Array.Empty(); + var files = Directory.GetFiles(SourceDirectory, "*", SearchOption.AllDirectories); - if (includeBaseName) - sourceFolder = Path.GetDirectoryName(sourceFolder); + if (files.Length == 0) + return null; - int length = string.IsNullOrWhiteSpace(sourceFolder) ? 0 : sourceFolder.Length; - if (length > 0 && sourceFolder != null && sourceFolder[length - 1] != Path.DirectorySeparatorChar && sourceFolder[length - 1] != Path.AltDirectorySeparatorChar) + if (IncludeBaseName) + SourceDirectory = Path.GetDirectoryName(SourceDirectory); + + int length = string.IsNullOrWhiteSpace(SourceDirectory) ? 0 : SourceDirectory.Length; + if (length > 0 && SourceDirectory != null && SourceDirectory[length - 1] != Path.DirectorySeparatorChar && SourceDirectory[length - 1] != Path.AltDirectorySeparatorChar) length++; - var result = new string[names.Length]; - for (int i = 0; i < names.Length; i++) + var result = new Dictionary(files.Length, StringComparer.OrdinalIgnoreCase); + for (int i = 0; i < files.Length; i++) { - result[i] = names[i].Substring(length); + var file = files[i]; + var name = file.Substring(length); + + foreach (var item in KeyValuePairs) + { + if (name.StartsWith(item.Key, StringComparison.OrdinalIgnoreCase)) + { + name = name.Replace(item.Key, item.Value, StringComparison.OrdinalIgnoreCase); + } + } + + result.Add(file, name); } return result; } + public static void CreateInboxZip(string File, string Name, string ArchiveFileName) + { + using (FileStream zipFileStream = new FileStream(ArchiveFileName, FileMode.Create)) + using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + { + archive.CreateEntryFromFile(File, Name, CompressionLevel.Optimal); + } + } + public static void CreateCompressedFolder(string sourceDirectoryName, string destinationArchiveFileName) { - string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); - string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + Dictionary archiveDirectoryMappingPairs = new Dictionary(3, StringComparer.OrdinalIgnoreCase) + { + { "Release32\\", "i386\\" }, + { "Release64\\", "amd64\\" }, + { "ReleaseARM64\\", "arm64\\" } + }; - using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + var entryNames = GetEntryNames(sourceDirectoryName, false, archiveDirectoryMappingPairs); + + foreach (var entry in entryNames) { - for (int i = 0; i < filesToAdd.Length; i++) + if ( + entry.Key.StartsWith("bin\\Debug", StringComparison.OrdinalIgnoreCase) || // Remove junk files + entry.Key.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".lib", StringComparison.OrdinalIgnoreCase) + ) { - string file = filesToAdd[i]; - string name = entryNames[i]; - - if ( - file.StartsWith("bin\\Debug", StringComparison.OrdinalIgnoreCase) || // Ignore junk files - file.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || - file.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || - file.EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || - file.EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || - file.EndsWith(".lib", StringComparison.OrdinalIgnoreCase) - ) - { - continue; - } + entryNames.Remove(entry.Key); + } + } - if (name.StartsWith("Release32\\", StringComparison.OrdinalIgnoreCase)) - name = name.Replace("Release32\\", "i386\\", StringComparison.OrdinalIgnoreCase); - if (name.StartsWith("Release64\\", StringComparison.OrdinalIgnoreCase)) - name = name.Replace("Release64\\", "amd64\\", StringComparison.OrdinalIgnoreCase); - if (name.StartsWith("ReleaseARM64\\", StringComparison.OrdinalIgnoreCase)) - name = name.Replace("ReleaseARM64\\", "arm64\\", StringComparison.OrdinalIgnoreCase); + using (var fileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + { + var options = new ZipWriterOptions(CompressionType.LZMA) + { + ArchiveEncoding = new ArchiveEncoding() { Default = Utils.UTF8NoBOM, }, + DeflateCompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression, + CompressionType = CompressionType.LZMA + }; - archive.CreateEntryFromFile(file, name, CompressionLevel.Optimal); + using (var writer = new ZipWriter(fileStream, options)) + { + foreach (var entry in entryNames) + { + bool binary = entry.Key.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".exe", StringComparison.OrdinalIgnoreCase); + + using (var input = File.OpenRead(entry.Key)) + { + writer.Write(entry.Value, input, new ZipWriterEntryOptions + { + ModificationDateTime = Build.BuildDateTime, + CompressionType = binary ? CompressionType.BCJ2 : CompressionType.LZMA + }); + } + } } } - - //using (var filestream = File.Create(destinationArchiveFileName)) - //using (var archive = new SevenZipArchive(filestream, FileAccess.Write)) - //using (var compressor = archive.Compressor()) - //{ - // compressor.CompressHeader = true; - // compressor.PreserveDirectoryStructure = true; - // compressor.Solid = true; - // - // for (int i = 0; i < filesToAdd.Length; i++) - // { - // string file = filesToAdd[i]; - // - // if ( - // file.StartsWith("Debug", StringComparison.OrdinalIgnoreCase) ||// Ignore directories (Debug32/Debug64) - // file.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase) || // Ignore files - // file.EndsWith(".iobj", StringComparison.OrdinalIgnoreCase) || - // file.EndsWith(".ipdb", StringComparison.OrdinalIgnoreCase) || - // file.EndsWith(".exp", StringComparison.OrdinalIgnoreCase) || - // file.EndsWith(".lib", StringComparison.OrdinalIgnoreCase) - // ) - // { - // continue; - // } - // - // var name = file.Replace(sourceDirectoryName, string.Empty); - // compressor.AddFile(file, name); - // } - // - // compressor.Finalize(); - //} } public static void CreateCompressedSdkFromFolder(string sourceDirectoryName, string destinationArchiveFileName) { - string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); - string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + var entryNames = GetEntryNames(sourceDirectoryName, false, null); - using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + using (var fileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) { - for (int i = 0; i < filesToAdd.Length; i++) + var options = new ZipWriterOptions(CompressionType.LZMA) + { + ArchiveEncoding = new ArchiveEncoding() { Default = Utils.UTF8NoBOM, }, + DeflateCompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression, + }; + + using (var writer = new ZipWriter(fileStream, options)) { - archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], CompressionLevel.Optimal); + foreach (var entry in entryNames) + { + bool binary = entry.Key.EndsWith(".dll", StringComparison.OrdinalIgnoreCase) || + entry.Key.EndsWith(".exe", StringComparison.OrdinalIgnoreCase); + + using (var input = File.OpenRead(entry.Key)) + { + writer.Write(entry.Value, input, new ZipWriterEntryOptions + { + ModificationDateTime = Build.BuildDateTime, + CompressionType = binary ? CompressionType.BCJ2 : CompressionType.LZMA + }); + } + } } } @@ -143,27 +169,74 @@ public static void CreateCompressedSdkFromFolder(string sourceDirectoryName, str public static void CreateCompressedPdbFromFolder(string sourceDirectoryName, string destinationArchiveFileName) { - string[] filesToAdd = Directory.GetFiles(sourceDirectoryName, "*", SearchOption.AllDirectories); - string[] entryNames = GetEntryNames(filesToAdd, sourceDirectoryName, false); + var entryNames = GetEntryNames(sourceDirectoryName, false, null); - using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) - using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + foreach (var entry in entryNames) { - for (int i = 0; i < filesToAdd.Length; i++) + string file = entry.Key; + string name = entry.Value; + + // Ignore junk files + if (!file.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) + { + entryNames.Remove(file); + continue; + } + + // Ignore junk directories + if (file.Contains("bin\\Debug", StringComparison.OrdinalIgnoreCase) || + file.Contains("obj\\", StringComparison.OrdinalIgnoreCase) || + file.Contains("tests\\", StringComparison.OrdinalIgnoreCase)) { - // Ignore junk files - if (!filesToAdd[i].EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) - continue; + entryNames.Remove(file); + continue; + } + } - // Ignore junk directories - if (filesToAdd[i].Contains("bin\\Debug", StringComparison.OrdinalIgnoreCase) || - filesToAdd[i].Contains("obj\\", StringComparison.OrdinalIgnoreCase) || - filesToAdd[i].Contains("tests\\", StringComparison.OrdinalIgnoreCase)) - continue; + using (var fileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + { + var options = new ZipWriterOptions(CompressionType.LZMA) + { + ArchiveEncoding = new ArchiveEncoding() { Default = Utils.UTF8NoBOM, }, + DeflateCompressionLevel = SharpCompress.Compressors.Deflate.CompressionLevel.BestCompression, + }; - archive.CreateEntryFromFile(filesToAdd[i], entryNames[i], CompressionLevel.Optimal); + using (var writer = new ZipWriter(fileStream, options)) + { + foreach (var entry in entryNames) + { + using (var input = File.OpenRead(entry.Key)) + { + writer.Write(entry.Value, input, new ZipWriterEntryOptions + { + ModificationDateTime = Build.BuildDateTime + }); + } + } } } + + //using (FileStream zipFileStream = new FileStream(destinationArchiveFileName, FileMode.Create)) + //using (ZipArchive archive = new ZipArchive(zipFileStream, ZipArchiveMode.Create)) + //{ + // foreach (var entry in entryNames) + // { + // string file = entry.Key; + // string name = entry.Value; + // + // // Ignore junk files + // if (!file.EndsWith(".pdb", StringComparison.OrdinalIgnoreCase)) + // continue; + // + // // Ignore junk directories + // if (file.Contains("bin\\Debug", StringComparison.OrdinalIgnoreCase) || + // file.Contains("obj\\", StringComparison.OrdinalIgnoreCase) || + // file.Contains("tests\\", StringComparison.OrdinalIgnoreCase)) + // continue; + // + // archive.CreateEntryFromFile(file, name, CompressionLevel.Optimal); + // } + //} } } } From 9df06b06b55a293ba8dad12bbe7ae711e2d4bad2 Mon Sep 17 00:00:00 2001 From: dmex Date: Sun, 1 Sep 2024 16:47:59 +1000 Subject: [PATCH 2/2] Fix json formatting and timestamps --- .../AzureSignTool/AuthenticodeKeyVaultSigner.cs | 11 +++-------- tools/CustomBuildTool/Build.cs | 8 ++++---- 2 files changed, 7 insertions(+), 12 deletions(-) diff --git a/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs b/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs index 8b63385bf222..ff424e0657b3 100644 --- a/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs +++ b/tools/CustomBuildTool/AzureSignTool/AuthenticodeKeyVaultSigner.cs @@ -103,23 +103,18 @@ internal unsafe HRESULT SignFile(ReadOnlySpan path, ReadOnlySpan des string timestampString = null; var flags = SIGNER_SIGN_FLAGS.SPC_DIGEST_SIGN_FLAG; - if (pageHashing) flags |= SIGNER_SIGN_FLAGS.SPC_INC_PE_PAGE_HASHES_FLAG; else flags |= SIGNER_SIGN_FLAGS.SPC_EXC_PE_PAGE_HASHES_FLAG; if (this.TimeStampConfiguration.Type == TimeStampType.Authenticode) - { timeStampFlags = SIGNER_TIMESTAMP_FLAGS.SIGNER_TIMESTAMP_AUTHENTICODE; - timestampString = this.TimeStampConfiguration.Url; - } else if (this.TimeStampConfiguration.Type == TimeStampType.RFC3161) - { timeStampFlags = SIGNER_TIMESTAMP_FLAGS.SIGNER_TIMESTAMP_RFC3161; - timestampAlgorithmOid = AlgorithmTranslator.HashAlgorithmToOidAsciiTerminated(this.TimeStampConfiguration.DigestAlgorithm); - timestampString = this.TimeStampConfiguration.Url; - } + + timestampAlgorithmOid = AlgorithmTranslator.HashAlgorithmToOidAsciiTerminated(this.TimeStampConfiguration.DigestAlgorithm); + timestampString = this.TimeStampConfiguration.Url; fixed (byte* pTimestampAlg = ×tampAlgorithmOid.GetPinnableReference()) fixed (char* timestampUrl = timestampString) diff --git a/tools/CustomBuildTool/Build.cs b/tools/CustomBuildTool/Build.cs index 8a3042a7d8de..c95c8e360af2 100644 --- a/tools/CustomBuildTool/Build.cs +++ b/tools/CustomBuildTool/Build.cs @@ -1335,10 +1335,10 @@ public static void CopySourceLink(bool CreateSourceLink) string directory = Build.BuildWorkingFolder.Replace("\\", "\\\\", StringComparison.OrdinalIgnoreCase); StringBuilder sb = new StringBuilder(260); - sb.Append("{{ \"documents\": {{ "); - sb.Append($"\"\\\\*\": \"https://raw.githubusercontent.com/winsiderss/systeminformer/{Build.BuildCommitHash}/*\", "); - sb.Append($"\"{directory}\\\\*\": \"https://raw.githubusercontent.com/winsiderss/systeminformer/{Build.BuildCommitHash}/*\", "); - sb.Append("}} }}"); + sb.Append("{\"documents\":{"); + sb.Append($"\"{directory}\\\\*\":\"https://raw.githubusercontent.com/winsiderss/systeminformer/{Build.BuildCommitHash}/*\","); + sb.Append($"\"\\\\*\":\"https://raw.githubusercontent.com/winsiderss/systeminformer/{Build.BuildCommitHash}/*\" "); + sb.Append("}}"); Utils.WriteAllText(Build.BuildSourceLink, sb.ToString()); }