From 72b0f325959a06d7b9d92aa438df0dc889a73ec3 Mon Sep 17 00:00:00 2001 From: moozzyk Date: Wed, 30 Sep 2015 16:31:02 -0700 Subject: [PATCH] Enabling signing assemblies using public key from snk on Mono and CoreClr If the project.json file contains the `keyFile` on CoreClr and Mono we will extract the public key from the snk file and will OSS sign the assembly with the extracted key. (Fixes #2558). We will do the same on desktop CLR if the `keyFile` option is accompanied by `"useOssSigning": true` As a result of this change the `keyFile` and the `useOssSigning` are no longer mutually exclusive and we will no show cyptic Roslyn compilation errors we would have shown before. (Fixes #2452). --- .../CompilerOptionsExtensions.cs | 52 ++++++++-- .../Properties/AssemblyInfo.cs | 2 + .../SnkUtils.cs | 87 ++++++++++++++++ .../RoslynCompiler.cs | 53 ++++------ .../RoslynDiagnostics.cs | 6 +- .../ICompilerOptions.cs | 2 +- .../Compilation/CompilerOptions.cs | 6 +- src/Microsoft.Dnx.Runtime/ProjectReader.cs | 2 +- ...crosoft.Dnx.Compilation.CSharp.Tests.xproj | 3 + .../SigningFacts.cs | 94 +++++++++++++++++ .../SnkUtilsFacts.cs | 96 ++++++++++++++++++ .../keyfile.snk | Bin 0 -> 596 bytes .../project.json | 1 + .../ProjectFacts.cs | 4 +- 14 files changed, 358 insertions(+), 50 deletions(-) create mode 100644 src/Microsoft.Dnx.Compilation.CSharp.Common/SnkUtils.cs create mode 100644 test/Microsoft.Dnx.Compilation.CSharp.Tests/SigningFacts.cs create mode 100644 test/Microsoft.Dnx.Compilation.CSharp.Tests/SnkUtilsFacts.cs create mode 100644 test/Microsoft.Dnx.Compilation.CSharp.Tests/keyfile.snk diff --git a/src/Microsoft.Dnx.Compilation.CSharp.Common/CompilerOptionsExtensions.cs b/src/Microsoft.Dnx.Compilation.CSharp.Common/CompilerOptionsExtensions.cs index 776aa5544..e4a407bdb 100644 --- a/src/Microsoft.Dnx.Compilation.CSharp.Common/CompilerOptionsExtensions.cs +++ b/src/Microsoft.Dnx.Compilation.CSharp.Common/CompilerOptionsExtensions.cs @@ -4,6 +4,7 @@ using System; using System.Collections.Generic; using System.Collections.Immutable; +using System.IO; using System.Linq; using System.Runtime.Versioning; using Microsoft.CodeAnalysis; @@ -72,7 +73,6 @@ private static CSharpCompilationOptions GetCompilationOptions(ICompilerOptions c bool allowUnsafe = compilerOptions.AllowUnsafe ?? false; bool optimize = compilerOptions.Optimize ?? false; bool warningsAsErrors = compilerOptions.WarningsAsErrors ?? false; - bool strongName = compilerOptions.StrongName ?? false; Platform platform; if (!Enum.TryParse(value: platformValue, ignoreCase: true, result: out platform)) @@ -80,13 +80,49 @@ private static CSharpCompilationOptions GetCompilationOptions(ICompilerOptions c platform = Platform.AnyCpu; } - return options.WithAllowUnsafe(allowUnsafe) - .WithPlatform(platform) - .WithGeneralDiagnosticOption(warningsAsErrors ? ReportDiagnostic.Error : ReportDiagnostic.Default) - .WithOptimizationLevel(optimize ? OptimizationLevel.Release : OptimizationLevel.Debug) - .WithCryptoKeyFile(compilerOptions.KeyFile) - .WithDelaySign(compilerOptions.DelaySign) - .WithCryptoPublicKey(strongName ? StrongNameKey : ImmutableArray.Empty); + options = options + .WithAllowUnsafe(allowUnsafe) + .WithPlatform(platform) + .WithGeneralDiagnosticOption(warningsAsErrors ? ReportDiagnostic.Error : ReportDiagnostic.Default) + .WithOptimizationLevel(optimize ? OptimizationLevel.Release : OptimizationLevel.Debug); + + return AddSigningOptions(options, compilerOptions); + } + + private static CSharpCompilationOptions AddSigningOptions(CSharpCompilationOptions options, ICompilerOptions compilerOptions) + { + var useOssSigning = compilerOptions.UseOssSigning == true; + + var keyFile = + Environment.GetEnvironmentVariable(EnvironmentNames.BuildKeyFile) ?? + compilerOptions.KeyFile; + + if (!string.IsNullOrEmpty(keyFile)) + { +#if DNXCORE50 + return options.WithCryptoPublicKey( + SnkUtils.ExtractPublicKey(File.ReadAllBytes(keyFile))); +#else + if (RuntimeEnvironmentHelper.IsMono || useOssSigning) + { + return options.WithCryptoPublicKey( + SnkUtils.ExtractPublicKey(File.ReadAllBytes(keyFile))); + } + + options = options.WithCryptoKeyFile(keyFile); + + var delaySignString = Environment.GetEnvironmentVariable(EnvironmentNames.BuildDelaySign); + var delaySign = + delaySignString == null + ? compilerOptions.DelaySign + : string.Equals(delaySignString, "true", StringComparison.OrdinalIgnoreCase) || + string.Equals(delaySignString, "1", StringComparison.Ordinal); + + return options.WithDelaySign(delaySign); +#endif + } + + return useOssSigning ? options.WithCryptoPublicKey(StrongNameKey) : options; } private static bool IsDesktop(FrameworkName frameworkName) diff --git a/src/Microsoft.Dnx.Compilation.CSharp.Common/Properties/AssemblyInfo.cs b/src/Microsoft.Dnx.Compilation.CSharp.Common/Properties/AssemblyInfo.cs index 3f4a3b62e..432ce1a63 100644 --- a/src/Microsoft.Dnx.Compilation.CSharp.Common/Properties/AssemblyInfo.cs +++ b/src/Microsoft.Dnx.Compilation.CSharp.Common/Properties/AssemblyInfo.cs @@ -3,6 +3,8 @@ using System.Reflection; using System.Resources; +using System.Runtime.CompilerServices; [assembly: AssemblyMetadata("Serviceable", "True")] [assembly: NeutralResourcesLanguage("en-US")] +[assembly: InternalsVisibleTo("Microsoft.Dnx.Compilation.CSharp.Tests")] \ No newline at end of file diff --git a/src/Microsoft.Dnx.Compilation.CSharp.Common/SnkUtils.cs b/src/Microsoft.Dnx.Compilation.CSharp.Common/SnkUtils.cs new file mode 100644 index 000000000..49a23a4bc --- /dev/null +++ b/src/Microsoft.Dnx.Compilation.CSharp.Common/SnkUtils.cs @@ -0,0 +1,87 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Collections.Immutable; +using System.IO; + +namespace Microsoft.Dnx.Compilation.CSharp +{ + internal static class SnkUtils + { + const byte PUBLICKEYBLOB = 0x06; + const byte PRIVATEKEYBLOB = 0x07; + + private const uint CALG_RSA_SIGN = 0x00002400; + private const uint CALG_SHA = 0x00008004; + + private const uint RSA1 = 0x31415352; //"RSA1" publickeyblob + private const uint RSA2 = 0x32415352; //"RSA2" privatekeyblob + + private const int VersionOffset = 1; + private const int ModulusLengthOffset = 12; + private const int ExponentOffset = 16; + private const int MagicPrivateKeyOffset = 8; + private const int MagicPublicKeyOffset = 20; + + public static ImmutableArray ExtractPublicKey(byte[] snk) + { + ValidateBlob(snk); + + if (snk[0] != PRIVATEKEYBLOB) + { + return ImmutableArray.Create(snk); + } + + var version = snk[VersionOffset]; + int modulusBitLength = ReadInt32(snk, ModulusLengthOffset); + uint exponent = (uint)ReadInt32(snk, ExponentOffset); + var modulus = new byte[modulusBitLength >> 3]; + + Array.Copy(snk, 20, modulus, 0, modulus.Length); + + return CreatePublicKey(version, exponent, modulus); + } + + private static void ValidateBlob(byte[] snk) + { + // 160 - the size of public key + if (snk.Length >= 160) + { + if (snk[0] == PRIVATEKEYBLOB && ReadInt32(snk, MagicPrivateKeyOffset) == RSA2 || // valid private key + snk[12] == PUBLICKEYBLOB && ReadInt32(snk, MagicPublicKeyOffset) == RSA1) // valid public key + { + return; + } + } + + throw new InvalidOperationException("Invalid key file."); + } + + private static int ReadInt32(byte[] array, int index) + { + return array[index] | array[index + 1] << 8 | array[index + 2] << 16 | array[index + 3] << 24; + } + + private static ImmutableArray CreatePublicKey(byte version, uint exponent, byte[] modulus) + { + using (var ms = new MemoryStream(160)) + using (var binaryWriter = new BinaryWriter(ms)) + { + binaryWriter.Write(CALG_RSA_SIGN); + binaryWriter.Write(CALG_SHA); + // total size of the rest of the blob (20 - size of RSAPUBKEY) + binaryWriter.Write(modulus.Length + 20); + binaryWriter.Write(PUBLICKEYBLOB); + binaryWriter.Write(version); + binaryWriter.Write((ushort)0x00000000); // reserved + binaryWriter.Write(CALG_RSA_SIGN); + binaryWriter.Write(RSA1); + binaryWriter.Write(modulus.Length << 3); + binaryWriter.Write(exponent); + binaryWriter.Write(modulus); + return ImmutableArray.Create(ms.ToArray()); + } + } + } +} diff --git a/src/Microsoft.Dnx.Compilation.CSharp/RoslynCompiler.cs b/src/Microsoft.Dnx.Compilation.CSharp/RoslynCompiler.cs index 580461636..9d95ba2a7 100644 --- a/src/Microsoft.Dnx.Compilation.CSharp/RoslynCompiler.cs +++ b/src/Microsoft.Dnx.Compilation.CSharp/RoslynCompiler.cs @@ -135,8 +135,8 @@ public CompilationContext CompileProject( incomingReferences, resourcesResolver); - // Apply strong-name settings - ApplyStrongNameSettings(compilationContext); + ValidateSigningOptions(compilationContext); + AddStrongNameProvider(compilationContext); if (isMainAspect && projectContext.Files.PreprocessSourceFiles.Any()) { @@ -179,43 +179,32 @@ public CompilationContext CompileProject( return compilationContext; } - private void ApplyStrongNameSettings(CompilationContext compilationContext) + private void ValidateSigningOptions(CompilationContext compilationContext) { - var keyFile = - Environment.GetEnvironmentVariable(EnvironmentNames.BuildKeyFile) ?? - compilationContext.Compilation.Options.CryptoKeyFile; - - if (!string.IsNullOrEmpty(keyFile) && !RuntimeEnvironmentHelper.IsMono) + var compilerOptions = compilationContext.Project.CompilerOptions; + if (compilerOptions.UseOssSigning == false) { #if DNX451 - var delaySignString = Environment.GetEnvironmentVariable(EnvironmentNames.BuildDelaySign); - var delaySign = - delaySignString == null - ? compilationContext.Compilation.Options.DelaySign - : string.Equals(delaySignString, "true", StringComparison.OrdinalIgnoreCase) || - string.Equals(delaySignString, "1", StringComparison.OrdinalIgnoreCase); - - var strongNameProvider = new DesktopStrongNameProvider( - ImmutableArray.Create(compilationContext.Project.ProjectDirectory)); - var newOptions = compilationContext.Compilation.Options - .WithStrongNameProvider(strongNameProvider) - .WithCryptoKeyFile(keyFile) - .WithDelaySign(delaySign); - compilationContext.Compilation = compilationContext.Compilation.WithOptions(newOptions); -#else - var diag = Diagnostic.Create( - RoslynDiagnostics.StrongNamingNotSupported, - null); - compilationContext.Diagnostics.Add(diag); + if (!RuntimeEnvironmentHelper.IsMono) + { + return; + } #endif + compilationContext.Diagnostics.Add( + Diagnostic.Create(RoslynDiagnostics.RealSigningSupportedOnlyOnDesktopClr, null)); } + } - // If both CryptoPublicKey and CryptoKeyFile compilation will fail so we don't need a check - if (compilationContext.Compilation.Options.CryptoPublicKey != null) + private void AddStrongNameProvider(CompilationContext compilationContext) + { + if (!string.IsNullOrEmpty(compilationContext.Compilation.Options.CryptoKeyFile)) { - var options = compilationContext.Compilation.Options - .WithCryptoPublicKey(compilationContext.Compilation.Options.CryptoPublicKey); - compilationContext.Compilation = compilationContext.Compilation.WithOptions(options); + var strongNameProvider = + new DesktopStrongNameProvider(ImmutableArray.Create(compilationContext.Project.ProjectDirectory)); + + compilationContext.Compilation = + compilationContext.Compilation.WithOptions( + compilationContext.Compilation.Options.WithStrongNameProvider(strongNameProvider)); } } diff --git a/src/Microsoft.Dnx.Compilation.CSharp/RoslynDiagnostics.cs b/src/Microsoft.Dnx.Compilation.CSharp/RoslynDiagnostics.cs index 167c22b48..26e00d06b 100644 --- a/src/Microsoft.Dnx.Compilation.CSharp/RoslynDiagnostics.cs +++ b/src/Microsoft.Dnx.Compilation.CSharp/RoslynDiagnostics.cs @@ -4,10 +4,10 @@ namespace Microsoft.Dnx.Compilation.CSharp { internal class RoslynDiagnostics { - internal static readonly DiagnosticDescriptor StrongNamingNotSupported = new DiagnosticDescriptor( + internal static readonly DiagnosticDescriptor RealSigningSupportedOnlyOnDesktopClr = new DiagnosticDescriptor( id: "DNX1001", - title: "Strong name generation is not supported on this platform", - messageFormat: "Strong name generation is not supported on CoreCLR. Skipping strong name generation.", + title: "Strong name generation with a private and public key pair is not supported on this platform", + messageFormat: "Strong name generation with a private and public key pair is supported only on desktop CLR. Falling back to OSS signing.", category: "StrongNaming", defaultSeverity: DiagnosticSeverity.Warning, isEnabledByDefault: true); diff --git a/src/Microsoft.Dnx.Runtime.Abstractions/ICompilerOptions.cs b/src/Microsoft.Dnx.Runtime.Abstractions/ICompilerOptions.cs index cb0da170e..513d1e8dd 100644 --- a/src/Microsoft.Dnx.Runtime.Abstractions/ICompilerOptions.cs +++ b/src/Microsoft.Dnx.Runtime.Abstractions/ICompilerOptions.cs @@ -27,7 +27,7 @@ public interface ICompilerOptions bool? DelaySign { get; } - bool? StrongName { get; } + bool? UseOssSigning { get; } bool? EmitEntryPoint { get; } } diff --git a/src/Microsoft.Dnx.Runtime/Compilation/CompilerOptions.cs b/src/Microsoft.Dnx.Runtime/Compilation/CompilerOptions.cs index 566fde725..5b6868e49 100644 --- a/src/Microsoft.Dnx.Runtime/Compilation/CompilerOptions.cs +++ b/src/Microsoft.Dnx.Runtime/Compilation/CompilerOptions.cs @@ -24,7 +24,7 @@ public class CompilerOptions : ICompilerOptions public bool? DelaySign { get; set; } - public bool? StrongName { get; set; } + public bool? UseOssSigning { get; set; } public bool? EmitEntryPoint { get; set; } @@ -81,9 +81,9 @@ public static CompilerOptions Combine(params CompilerOptions[] options) result.DelaySign = option.DelaySign; } - if (option.StrongName != null) + if (option.UseOssSigning != null) { - result.StrongName = option.StrongName; + result.UseOssSigning = option.UseOssSigning; } if (option.EmitEntryPoint != null) diff --git a/src/Microsoft.Dnx.Runtime/ProjectReader.cs b/src/Microsoft.Dnx.Runtime/ProjectReader.cs index bbc6b3113..64339b132 100644 --- a/src/Microsoft.Dnx.Runtime/ProjectReader.cs +++ b/src/Microsoft.Dnx.Runtime/ProjectReader.cs @@ -468,7 +468,7 @@ private static CompilerOptions GetCompilationOptions(JsonObject rawObject) Optimize = rawOptions.ValueAsNullableBoolean("optimize"), KeyFile = rawOptions.ValueAsString("keyFile"), DelaySign = rawOptions.ValueAsNullableBoolean("delaySign"), - StrongName = rawOptions.ValueAsNullableBoolean("strongName"), + UseOssSigning = rawOptions.ValueAsNullableBoolean("useOssSigning"), EmitEntryPoint = rawOptions.ValueAsNullableBoolean("emitEntryPoint") }; } diff --git a/test/Microsoft.Dnx.Compilation.CSharp.Tests/Microsoft.Dnx.Compilation.CSharp.Tests.xproj b/test/Microsoft.Dnx.Compilation.CSharp.Tests/Microsoft.Dnx.Compilation.CSharp.Tests.xproj index da413ea43..ba1a5c0ca 100644 --- a/test/Microsoft.Dnx.Compilation.CSharp.Tests/Microsoft.Dnx.Compilation.CSharp.Tests.xproj +++ b/test/Microsoft.Dnx.Compilation.CSharp.Tests/Microsoft.Dnx.Compilation.CSharp.Tests.xproj @@ -10,6 +10,9 @@ ..\..\artifacts\obj\$(MSBuildProjectName) ..\..\artifacts\bin\$(MSBuildProjectName)\ + + Microsoft.Dnx.Compilation.CSharp.Tests + 2.0 diff --git a/test/Microsoft.Dnx.Compilation.CSharp.Tests/SigningFacts.cs b/test/Microsoft.Dnx.Compilation.CSharp.Tests/SigningFacts.cs new file mode 100644 index 000000000..d6aaacbb4 --- /dev/null +++ b/test/Microsoft.Dnx.Compilation.CSharp.Tests/SigningFacts.cs @@ -0,0 +1,94 @@ +using System.Collections.Generic; +using System.IO; +using System.Linq; +using System.Runtime.Versioning; +using Microsoft.AspNet.Testing.xunit; +using Microsoft.Dnx.Compilation.Caching; +using Microsoft.Dnx.Runtime; +using Xunit; + +namespace Microsoft.Dnx.Compilation.CSharp.Tests +{ + public class SigningFacts + { + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.CLR)] + public void UseOssSigningEqualsFalseReturnsWarningOnCoreClrAndMono() + { + RoslynCompiler compiler; + CompilationProjectContext projectContext; + PrepareCompilation(new FakeCompilerOptions { KeyFile = "keyfile.snk", UseOssSigning = false }, out compiler, out projectContext); + + var compilationContext = compiler.CompileProject(projectContext, Enumerable.Empty(), + Enumerable.Empty(), () => new List()); + + Assert.Equal(1, compilationContext.Diagnostics.Count); + Assert.Equal("DNX1001", compilationContext.Diagnostics[0].Id); + } + + [ConditionalFact] + [FrameworkSkipCondition(RuntimeFrameworks.Mono | RuntimeFrameworks.CoreCLR)] + public void UseOssSigningEqualsFalseReturnsNoWarningOnDesktopClr() + { + RoslynCompiler compiler; + CompilationProjectContext projectContext; + PrepareCompilation(new FakeCompilerOptions { KeyFile = "keyfile.snk", UseOssSigning = false }, out compiler, out projectContext); + + var compilationContext = compiler.CompileProject(projectContext, Enumerable.Empty(), + Enumerable.Empty(), () => new List()); + + Assert.Equal(0, compilationContext.Diagnostics.Count); + } + + private static void PrepareCompilation(ICompilerOptions compilerOptions, out RoslynCompiler compiler, + out CompilationProjectContext projectContext) + { + var cacheContextAccessor = new FakeCacheContextAccessor { Current = new CacheContext(null, (d) => { }) }; + compiler = new RoslynCompiler(null, cacheContextAccessor, new FakeNamedDependencyProvider(), null, null, null); + var compilationTarget = new CompilationTarget("test", new FrameworkName(".NET Framework, Version=4.0"), "Release", null); + projectContext = new CompilationProjectContext( + compilationTarget, Directory.GetCurrentDirectory(), "project.json", "title", "description", "copyright", + "1.0.0", new System.Version(1, 0), false, new CompilationFiles(Enumerable.Empty(), + Enumerable.Empty()), compilerOptions); + } + + private class FakeCacheContextAccessor : ICacheContextAccessor + { + public CacheContext Current { get; set; } + } + + private class FakeCompilerOptions : ICompilerOptions + { + public bool? AllowUnsafe { get; set; } + + public IEnumerable Defines { get; set; } + + public bool? DelaySign { get; set; } + + public bool? EmitEntryPoint { get; set; } + + public string KeyFile { get; set; } + + public string LanguageVersion { get; set; } + + public bool? Optimize { get; set; } + + public string Platform { get; set; } + + public bool? UseOssSigning { get; set; } + + public bool? WarningsAsErrors { get; set; } + } + + private class FakeNamedDependencyProvider : INamedCacheDependencyProvider + { + public ICacheDependency GetNamedDependency(string name) + { + return null; + } + + public void Trigger(string name) + { } + } + } +} diff --git a/test/Microsoft.Dnx.Compilation.CSharp.Tests/SnkUtilsFacts.cs b/test/Microsoft.Dnx.Compilation.CSharp.Tests/SnkUtilsFacts.cs new file mode 100644 index 000000000..b442d1a74 --- /dev/null +++ b/test/Microsoft.Dnx.Compilation.CSharp.Tests/SnkUtilsFacts.cs @@ -0,0 +1,96 @@ +// Copyright (c) .NET Foundation. All rights reserved. +// Licensed under the Apache License, Version 2.0. See License.txt in the project root for license information. + +using System; +using System.Diagnostics; +using System.Globalization; +using Xunit; + +namespace Microsoft.Dnx.Compilation.CSharp +{ + public class SnkUtilsFacts + { + [Fact] + public void ExtractPublicKeyReturnsPublicKeyFromPrivatePublicKeyPair() + { + var keyPair = + "07020000002400005253413200040000010001004B8DA89A5A03625E7BA3C17639EA8EAC91A07CC2" + + "BB2F36857FCA73B5CB52CD781A6EB1E14198AD82F9F713F548385DF70C18EC12BC02181AF5EAACC9" + + "390F790ED6485CD4CAE3684BDDAB6896DF8835E2F19EFE7FF416E8F7AF3A7605605BE48947850383" + + "418B9DC10BD00DECCC45C2F2AB68B83D00F118E4519BB828AE3078C74F3FD9D6B638EC3F6552BDCF" + + "9ACA84B5E65A0EC60857D366D5E3369994F95CB68100695681A541A30D811BDD00B0481B17F8D621" + + "E77D3E9C917DD98F8AE380F64573364098F1426E901E294D482F59F4C79FAF45D4EEC4D2A93DA3D3" + + "36B6FF0553B89602F8FE747367A7207C41021F4433A84926A356CE9420ED30492E6F27CF279D56B2" + + "872EA654092329367D58DB5C98DC4405DBFA7921A68F24D0B05B60FE73317561AFD626452715660A" + + "ECB0F0A81D8EB381A61B137A2B0188B00A82F853B9A01F917D35A0E5476622F736F9B674B9D87E8D" + + "FB2170C803713F6113F21ABF837E2B3D9D2F0C9051A36D8D73F58E64A0FC7C59BED7E79E1077755D" + + "18CCC061AEBAE8B805E30A0DD0D78D6EBBE71ADC1B973D9AAB0E0BB52E784942712A9842E5624350" + + "B526F6CF37F8166373A7F76438D5F8F238889C438465789E946030AEB9160FB83F45E5AA3F974F83" + + "D6BC1E80A603BFE12D494806802DB203A82906A29D846C5AD9B0DF7009C0E4205A5637C335F328DA" + + "726DAE93CE761DA5B279FE9137FB86018B4D5872C546243CAD2D8C5626C58188B26A7B240D2DC733" + + "60588B40E5F79E8BD0D627E1FE9B38483CB579F04EFC550C776DFE0459C384B7F74D711C"; + + var extractedPublicKey = SnkUtils.ExtractPublicKey(HexToBin(keyPair)); + + var b = new byte[extractedPublicKey.Length]; + extractedPublicKey.CopyTo(b); + + var s = BitConverter.ToString(b).Replace("-", ""); + + var expectedKey = HexToBin( + "00240000048000009400000006020000002400005253413100040000010001004B8DA89A5A03625E" + + "7BA3C17639EA8EAC91A07CC2BB2F36857FCA73B5CB52CD781A6EB1E14198AD82F9F713F548385DF7" + + "0C18EC12BC02181AF5EAACC9390F790ED6485CD4CAE3684BDDAB6896DF8835E2F19EFE7FF416E8F7" + + "AF3A7605605BE48947850383418B9DC10BD00DECCC45C2F2AB68B83D00F118E4519BB828AE3078C7"); + + Assert.Equal(expectedKey, extractedPublicKey); + } + + [Fact] + public void ExtractPublicKeyReturnsTheSameKeyIfOnlyPublicKeyProvided() + { + var sourceKey = HexToBin( + "002400000D8000009400000006020000002400005253413100040000010001004B8DA89A5A03625E" + + "7BA3C17639EA8EAC91A07CC2BB2F36857FCA73B5CB52CD781A6EB1E14198AD82F9F713F548385DF7" + + "0C18EC12BC02181AF5EAACC9390F790ED6485CD4CAE3684BDDAB6896DF8835E2F19EFE7FF416E8F7" + + "AF3A7605605BE48947850383418B9DC10BD00DECCC45C2F2AB68B83D00F118E4519BB828AE3078C7"); + + var extractedPublicKey = SnkUtils.ExtractPublicKey(sourceKey); + + Assert.Equal(sourceKey, extractedPublicKey); + } + + [Fact] + public void ExtractPublicKeyThrowsForInvalidKeyBlobs() + { + var invalidKeyBlobs = new[] + { + string.Empty, + new string('0', 160 * 2), // 160 * 2 - the length of a public key, 2 - 2 chars per byte + new string('0', 596 * 2), // 596 * 2 - the length of a key pair, 2 - 2 chars per byte + "0702000000240000DEADBEEF" + new string('0', 584 * 2), // private key blob without magic private key + "002400000D800000940000000602000000240000DEADBEEF" + new string('0', 136 * 2), // public key blob without magic public key + }; + + foreach (var key in invalidKeyBlobs) + { + Assert.Equal("Invalid key file.", + Assert.Throws(() => SnkUtils.ExtractPublicKey(HexToBin(key))).Message); + } + } + + private static byte[] HexToBin(string input) + { + Debug.Assert(input != null && (input.Length & 1) == 0, "invalid input string."); + + var result = new byte[input.Length >> 1]; + + for (var i = 0; i < result.Length; i++) + { + result[i] = byte.Parse(input.Substring(i << 1, 2), NumberStyles.HexNumber); + } + + return result; + } + } +} diff --git a/test/Microsoft.Dnx.Compilation.CSharp.Tests/keyfile.snk b/test/Microsoft.Dnx.Compilation.CSharp.Tests/keyfile.snk new file mode 100644 index 0000000000000000000000000000000000000000..5b750229abd6660145e9706bc673e4103b434a56 GIT binary patch literal 596 zcmV-a0;~N80ssI2Bme+XQ$aES1ONa50096?ji{Ph17co#qrrAL>W-|DpnSr+FE)jL z%5$~LQq6c8Zn5D(n5}~O_Y?IM)80-?f0vHa58*4|xvONL&<*U&MZ)r{Xt+H9@fhS$o46>hFnGsL zKiSr{IP5=VQoYZb%7nG%S`NktSJP(I<2IR;`CPVv0BKf%r9q<&fg9Zbut*yh_|_rk zeLkF#ec6wSb2dPj@j`Bp9w|*oFIn`*pRYyK?!?lmJ)_e$w*LiFxRwI={&aI^ zryzVm0v|*(s7WTHR?d_l?J!9$Zzsa{3wR!MP{8bEhZTrEEZyoah literal 0 HcmV?d00001 diff --git a/test/Microsoft.Dnx.Compilation.CSharp.Tests/project.json b/test/Microsoft.Dnx.Compilation.CSharp.Tests/project.json index 5a9cbd0c0..1c30964f8 100644 --- a/test/Microsoft.Dnx.Compilation.CSharp.Tests/project.json +++ b/test/Microsoft.Dnx.Compilation.CSharp.Tests/project.json @@ -2,6 +2,7 @@ "dependencies": { "Microsoft.Dnx.Runtime.Abstractions": "1.0.0-*", "Microsoft.Dnx.Compilation.CSharp": "1.0.0-*", + "Microsoft.AspNet.Testing": "1.0.0-*", "xunit.runner.aspnet": "2.0.0-aspnet-*" }, "frameworks": { diff --git a/test/Microsoft.Dnx.Runtime.Tests/ProjectFacts.cs b/test/Microsoft.Dnx.Runtime.Tests/ProjectFacts.cs index b2269625b..bd8d80a86 100644 --- a/test/Microsoft.Dnx.Runtime.Tests/ProjectFacts.cs +++ b/test/Microsoft.Dnx.Runtime.Tests/ProjectFacts.cs @@ -382,7 +382,7 @@ public void CompilerOptionsAreSet() { var project = ProjectUtilities.GetProject(@" { - ""compilationOptions"": { ""allowUnsafe"": true, ""define"": [""X"", ""y""], ""platform"": ""x86"", ""warningsAsErrors"": true, ""optimize"": true, ""keyFile"" : ""c:\\keyfile.snk"", ""delaySign"" : true, ""strongName"" : true } + ""compilationOptions"": { ""allowUnsafe"": true, ""define"": [""X"", ""y""], ""platform"": ""x86"", ""warningsAsErrors"": true, ""optimize"": true, ""keyFile"" : ""c:\\keyfile.snk"", ""delaySign"" : true, ""useOssSigning"" : true } }", "foo", @"c:\foo\project.json"); @@ -396,7 +396,7 @@ public void CompilerOptionsAreSet() Assert.True(compilerOptions.Optimize.Value); Assert.Equal(compilerOptions.KeyFile, @"c:\keyfile.snk"); Assert.True(compilerOptions.DelaySign); - Assert.True(compilerOptions.StrongName); + Assert.True(compilerOptions.UseOssSigning); } [Fact]