static void Main(string[] args) { Regex sign_matcher = new Regex(@"--sign=.*", RegexOptions.Compiled | RegexOptions.ECMAScript); Regex key_matcher = new Regex(@"--key=.*", RegexOptions.Compiled | RegexOptions.ECMAScript); Regex version_matcher = new Regex(@"--version=.*", RegexOptions.Compiled | RegexOptions.ECMAScript); Regex verify_matcher = new Regex(@"--verify=.*", RegexOptions.Compiled | RegexOptions.ECMAScript); Regex signature_matcher = new Regex(@"--signature=.*", RegexOptions.Compiled | RegexOptions.ECMAScript); if (args.Any(s => s == "--help")) { string executable_name = RuntimeInformation.IsOSPlatform(OSPlatform.Windows) switch { true => "gms.exe", _ => "./gms" }; Console.WriteLine("GmodNetModuleSigner 1.0.0 by Gleb Krasilich. https://github.com/GlebChili/GmodNetModuleSigner."); Console.WriteLine("Usage: " + executable_name + " [FLAG1] [FLAG2] ...\n"); Console.WriteLine("Flags:\n"); Console.WriteLine("--help: usage help\n"); Console.WriteLine("--generate-key: Generate new private and public keys pair and save it as private.modulekey file.\n"); Console.WriteLine("--sign=[module_to_sign_path]: sign given file (relative or absolute path). Must be used wtih --key flag " + "and --version flag. Output: signature.modulesign file.\n"); Console.WriteLine("--key=[path_to_key]: Use following key for sign and verification process (relative or absolute path).\n"); Console.WriteLine("--version=[module_version]: Explicitly specify module version to add to *.modulekey file.\n"); Console.WriteLine("--verify=[module_to_verify_path]: Verify signature of the module. Must be used with --key and --signature flags."); Console.WriteLine("--signature=[path_to_the_signature_file]: Signature for the verification process."); } else if (args.Any(s => s == "--generate-key")) { Console.WriteLine("Generating new key pair"); Task <TextKeyPair> future_key = Task <TextKeyPair> .Factory.StartNew(GenerateKey, TaskCreationOptions.LongRunning); int tick_counter = 0; while (!future_key.IsCompleted) { tick_counter++; tick_counter = tick_counter % 1000000000; if (tick_counter == 0) { Console.Write("."); } } Console.Write("\n"); TextKeyPair result = future_key.Result; byte[] key_json = JsonSerializer.SerializeToUtf8Bytes <TextKeyPair>(result, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllBytes("private.modulekey", key_json); Console.WriteLine("Key pair generated and saved as private.modulekey file. KEEP YOUR SECRET KEY SAFE!"); } else if (args.Any(s => sign_matcher.IsMatch(s))) { Console.WriteLine("Starting sign process..."); string sign_flag = args.First(s => sign_matcher.IsMatch(s)); string sign_path; try { sign_path = sign_flag.Split("=")[1]; } catch { Console.WriteLine("Path for file to sign is empty or invalid. Try again."); return; } string key_flag = args.First(s => key_matcher.IsMatch(s)); string key_path; if (key_flag == null || key_flag == String.Empty) { Console.WriteLine("The --key=[path_to_key] flag is missing. Try again."); return; } try { key_path = key_flag.Split("=")[1]; } catch { Console.WriteLine("Key file path is empty or invalid. Try again."); return; } byte[] key_blob; try { key_blob = File.ReadAllBytes(key_path); } catch (Exception e) { Console.WriteLine("Unable to read key file: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } TextKeyPair key_pair; try { key_pair = JsonSerializer.Deserialize <TextKeyPair>(key_blob); } catch (Exception e) { Console.WriteLine("Unable to parse key file: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } if (key_pair.PrivateKey == null || key_pair.PrivateKey == String.Empty) { Console.WriteLine("The key file contains no secret key. Try again."); return; } string version_flag = args.First(s => version_matcher.IsMatch(s)); if (version_flag == null || version_flag == String.Empty) { Console.WriteLine("--version flag is missing. Try again."); return; } string version_name; try { version_name = version_flag.Split("=")[1]; } catch { Console.WriteLine("The vesrsion is empty or invalid. Try again."); return; } Sha512 sha512 = HashAlgorithm.Sha512; byte[] file_blob; try { file_blob = File.ReadAllBytes(sign_path); } catch (Exception e) { Console.WriteLine("Unable to read file to sign: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } byte[] file_hash = sha512.Hash(file_blob); file_hash = file_hash.Concat(Encoding.UTF8.GetBytes(version_name)).ToArray(); byte[] final_hash = sha512.Hash(file_hash); byte[] raw_private_key = HexToBytes(key_pair.PrivateKey); Ed25519 ed25519 = SignatureAlgorithm.Ed25519; Key private_key; try { private_key = Key.Import(ed25519, raw_private_key, KeyBlobFormat.RawPrivateKey); } catch (Exception e) { Console.WriteLine("Unable to import private key: " + e.GetType() + " " + e.Message + ". Try again."); return; } byte[] final_signature = ed25519.Sign(private_key, final_hash); ModuleSignature sign_struct = new ModuleSignature { Version = version_name, Signature = BitConverter.ToString(final_signature).Replace("-", "") }; byte[] sign_to_write = JsonSerializer.SerializeToUtf8Bytes <ModuleSignature>(sign_struct, new JsonSerializerOptions { WriteIndented = true }); File.WriteAllBytes("signature.modulesign", sign_to_write); Console.WriteLine("File was successfully signed."); } else if (args.Any(s => verify_matcher.IsMatch(s))) { Console.WriteLine("Starting verification process..."); string verify_flag = args.First(s => verify_matcher.IsMatch(s)); string verify_path; try { verify_path = verify_flag.Split("=")[1]; } catch { Console.WriteLine("Path for the file to verify is empty or invalid. Try again."); return; } string key_flag = args.First(s => key_matcher.IsMatch(s)); string key_path; if (key_flag == null || key_flag == String.Empty) { Console.WriteLine("The --key=[path_to_key] flag is missing. Try again."); return; } try { key_path = key_flag.Split("=")[1]; } catch { Console.WriteLine("Key file path is empty or invalid. Try again."); return; } string signature_flag = args.First(s => signature_matcher.IsMatch(s)); string signature_path; if (signature_flag == null || signature_flag == String.Empty) { Console.WriteLine("-signature=[path_to_the_signature] flag is missing. Try again."); return; } try { signature_path = signature_flag.Split("=")[1]; } catch { Console.WriteLine("Signature path is empty or invalid. Try again."); return; } byte[] file_blob; try { file_blob = File.ReadAllBytes(verify_path); } catch (Exception e) { Console.WriteLine("Unable to read the module to verify: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } byte[] key_blob; try { key_blob = File.ReadAllBytes(key_path); } catch (Exception e) { Console.WriteLine("Unable to read key: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } byte[] signature_blob; try { signature_blob = File.ReadAllBytes(signature_path); } catch (Exception e) { Console.WriteLine("Unable to read signature: " + e.GetType() + " " + e.Message + ". Try again."); return; } TextKeyPair key_pair; try { key_pair = JsonSerializer.Deserialize <TextKeyPair>(key_blob); } catch (Exception e) { Console.WriteLine("Unable to parse key file: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } if (key_pair.PublicKey == null || key_pair.PublicKey == String.Empty) { Console.WriteLine("Public key is empty or invalid. Try again."); return; } ModuleSignature signature; try { signature = JsonSerializer.Deserialize <ModuleSignature>(signature_blob); } catch (Exception e) { Console.WriteLine("Unable to parse signature file: " + e.GetType().ToString() + e.Message + ". Try again."); return; } Sha512 sha512 = HashAlgorithm.Sha512; byte[] file_hash = sha512.Hash(file_blob); file_hash = file_hash.Concat(Encoding.UTF8.GetBytes(signature.Version)).ToArray(); byte[] final_hash = sha512.Hash(file_hash); Ed25519 ed25519 = SignatureAlgorithm.Ed25519; PublicKey public_key; try { byte[] raw_public_key = HexToBytes(key_pair.PublicKey); public_key = PublicKey.Import(ed25519, raw_public_key, KeyBlobFormat.RawPublicKey); } catch (Exception e) { Console.WriteLine("Unable to parse public key: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } try { bool is_valid = ed25519.Verify(public_key, final_hash, HexToBytes(signature.Signature)); if (is_valid) { Console.WriteLine("Signature is valid."); } else { Console.WriteLine("Signature is NOT valid."); } } catch (Exception e) { Console.WriteLine("Unable to verify signature: " + e.GetType().ToString() + " " + e.Message + ". Try again."); return; } } else { Console.WriteLine("GmodNetModuleSigner 1.0.0 by Gleb Krasilich. https://github.com/GlebChili/GmodNetModuleSigner."); Console.WriteLine("Use --help flag for usage help."); } }
internal override void PreResolve(ModuleDefinition m) { if (m.RefinementBaseRoot != null) { RefinedSig = m.RefinementBaseRoot.Signature; if (RefinedSig.ModuleDef != null) { if (RefinedSig.ModuleDef.IsProtected) { reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName, "module ({0}) named as a refinement base is marked protected and cannot be refined", m.RefinementBaseName.val); } m.RefinementBase = RefinedSig.ModuleDef; if (m.IsExclusiveRefinement) { if (null == m.RefinementBase.ExclusiveRefinement) { m.RefinementBase.ExclusiveRefinement = m; } else { reporter.Error(MessageSource.RefinementTransformer, m.tok, "no more than one exclusive refinement may exist for a given module."); } } // check that the openess in the imports between refinement and its base matches List<TopLevelDecl> declarations = m.TopLevelDecls; List<TopLevelDecl> baseDeclarations = m.RefinementBase.TopLevelDecls; foreach (var im in declarations) { if (im is ModuleDecl) { ModuleDecl mdecl = (ModuleDecl)im; //find the matching import from the base // TODO: this is a terribly slow algorithm; use the symbol table instead foreach (var bim in baseDeclarations) { if (bim is ModuleDecl && ((ModuleDecl)bim).Name.Equals(mdecl.Name)) { if (mdecl.Opened != ((ModuleDecl)bim).Opened) { string message = mdecl.Opened ? "{0} in {1} cannot be imported with \"opened\" because it does not match the corresponding import in the refinement base {2} " : "{0} in {1} must be imported with \"opened\" to match the corresponding import in its refinement base {2}."; reporter.Error(MessageSource.RefinementTransformer, m.tok, message, im.Name, m.Name, m.RefinementBase.Name); } break; } break; } } } PreResolveWorker(m); } else { reporter.Error(MessageSource.RefinementTransformer, m.RefinementBaseName, "module ({0}) named as refinement base does not exist", m.RefinementBaseName.val); } } }
public bool CheckIsRefinement(ModuleSignature derived, ModuleSignature original) { // Check refinement by construction. var derivedPointer = derived; while (derivedPointer != null) { if (derivedPointer == original) return true; derivedPointer = derivedPointer.Refines; } // Check structural refinement. Note this involves several checks. // First, we need to know if the two modules are signature compatible; // this is determined immediately as it is necessary for determining // whether resolution will succeed. This involves checking classes, datatypes, // type declarations, and nested modules. // Second, we need to determine whether the specifications will be compatible // (i.e. substitutable), by translating to Boogie. var errorCount = reporter.Count(ErrorLevel.Error); foreach (var kv in original.TopLevels) { var d = kv.Value; TopLevelDecl nw; if (derived.TopLevels.TryGetValue(kv.Key, out nw)) { if (d is ModuleDecl) { if (!(nw is ModuleDecl)) { reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else { CheckIsRefinement(((ModuleDecl)nw).Signature, ((ModuleDecl)d).Signature); } } else if (d is OpaqueTypeDecl) { if (nw is ModuleDecl) { reporter.Error(MessageSource.RefinementTransformer, nw, "a module ({0}) must refine another module", nw.Name); } else { bool dDemandsEqualitySupport = ((OpaqueTypeDecl)d).MustSupportEquality; if (nw is OpaqueTypeDecl) { if (dDemandsEqualitySupport != ((OpaqueTypeDecl)nw).MustSupportEquality) { reporter.Error(MessageSource.RefinementTransformer, nw, "type declaration '{0}' is not allowed to change the requirement of supporting equality", nw.Name); } } else if (dDemandsEqualitySupport) { if (nw is ClassDecl) { // fine, as long as "nw" does not take any type parameters if (nw.TypeArgs.Count != 0) { reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a class that takes type parameters", nw.Name); } } else if (nw is CoDatatypeDecl) { reporter.Error(MessageSource.RefinementTransformer, nw, "a type declaration that requires equality support cannot be replaced by a codatatype"); } else { Contract.Assert(nw is IndDatatypeDecl); if (nw.TypeArgs.Count != 0) { reporter.Error(MessageSource.RefinementTransformer, nw, "opaque type '{0}' is not allowed to be replaced by a datatype that takes type parameters", nw.Name); } else { var udt = new UserDefinedType(nw.tok, nw.Name, nw, new List<Type>()); if (!(udt.SupportsEquality)) { reporter.Error(MessageSource.RefinementTransformer, nw.tok, "datatype '{0}' is used to refine an opaque type with equality support, but '{0}' does not support equality", nw.Name); } } } } } } else if (d is DatatypeDecl) { if (nw is DatatypeDecl) { if (d is IndDatatypeDecl && !(nw is IndDatatypeDecl)) { reporter.Error(MessageSource.RefinementTransformer, nw, "a datatype ({0}) must be replaced by a datatype, not a codatatype", d.Name); } else if (d is CoDatatypeDecl && !(nw is CoDatatypeDecl)) { reporter.Error(MessageSource.RefinementTransformer, nw, "a codatatype ({0}) must be replaced by a codatatype, not a datatype", d.Name); } // check constructors, formals, etc. CheckDatatypesAreRefinements((DatatypeDecl)d, (DatatypeDecl)nw); } else { reporter.Error(MessageSource.RefinementTransformer, nw, "a {0} ({1}) must be refined by a {0}", d is IndDatatypeDecl ? "datatype" : "codatatype", d.Name); } } else if (d is ClassDecl) { if (!(nw is ClassDecl)) { reporter.Error(MessageSource.RefinementTransformer, nw, "a class declaration ({0}) must be refined by another class declaration", nw.Name); } else { CheckClassesAreRefinements((ClassDecl)nw, (ClassDecl)d); } } else { Contract.Assert(false); throw new cce.UnreachableException(); // unexpected toplevel } } else { reporter.Error(MessageSource.RefinementTransformer, d, "declaration {0} must have a matching declaration in the refining module", d.Name); } } return errorCount == reporter.Count(ErrorLevel.Error); }
void PreResolveWorker(ModuleDefinition m) { Contract.Requires(m != null); if (m.RefinementBase == null) return; if (moduleUnderConstruction != null) { postTasks.Clear(); } moduleUnderConstruction = m; refinementCloner = new RefinementCloner(moduleUnderConstruction); var prev = m.RefinementBase; //copy the signature, including its opened imports refinedSigOpened = Resolver.MergeSignature(new ModuleSignature(), RefinedSig); Resolver.ResolveOpenedImports(refinedSigOpened, m.RefinementBase, false, null); // Create a simple name-to-decl dictionary. Ignore any duplicates at this time. var declaredNames = new Dictionary<string, int>(); for (int i = 0; i < m.TopLevelDecls.Count; i++) { var d = m.TopLevelDecls[i]; if (!declaredNames.ContainsKey(d.Name)) { declaredNames.Add(d.Name, i); } // TODO: This is more restrictive than is necessary, // it would be possible to disambiguate these, but it seems like // a lot of work for not much gain if (refinedSigOpened.TopLevels.ContainsKey(d.Name) && !RefinedSig.TopLevels.ContainsKey(d.Name)) { var decl = Resolver.AmbiguousTopLevelDecl.Create(m, d, refinedSigOpened.TopLevels[d.Name]); if (decl is Resolver.AmbiguousTopLevelDecl) { reporter.Error(MessageSource.RefinementTransformer, d.tok, "Base module {0} imports {1} from an opened import, so it cannot be overridden. Give this declaration a unique name to disambiguate.", prev.Name, d.Name); } } } // Merge the declarations of prev into the declarations of m List<string> processedDecl = new List<string>(); foreach (var d in prev.TopLevelDecls) { int index; processedDecl.Add(d.Name); if (!declaredNames.TryGetValue(d.Name, out index)) { m.TopLevelDecls.Add(refinementCloner.CloneDeclaration(d, m)); } else { var nw = m.TopLevelDecls[index]; MergeTopLevelDecls(m, nw, d, index); } } // Merge the imports of prev var prevTopLevelDecls = RefinedSig.TopLevels.Values; foreach (var d in prevTopLevelDecls) { int index; if (!processedDecl.Contains(d.Name) && (declaredNames.TryGetValue(d.Name, out index))) { // if it is redefined, we need to merge them. var nw = m.TopLevelDecls[index]; MergeTopLevelDecls(m, nw, d, index); } } m.RefinementBaseSig = RefinedSig; Contract.Assert(moduleUnderConstruction == m); // this should be as it was set earlier in this method }
/// <summary> Finds ILSpy's IModule based on ExprCS ModuleSignature. The IModule has access to the module contents, so it's much stronger than ModuleSignature. </summary> internal IModule GetModule(ModuleSignature module) => moduleMap.TryGetValue(module, out var result) ? result : throw new ArgumentException($"Module {module} is not known.");