net/JetBrains.SignatureVerifier/src/Crypt/MachOSignatureVerifier.cs (86 lines of code) (raw):

using System; using System.IO; using System.Linq; using System.Threading.Tasks; using JetBrains.Annotations; using JetBrains.FormatRipper.MachO; namespace JetBrains.SignatureVerifier.Crypt; /// <summary> /// Mach-o format signatures verifier /// </summary> public class MachOSignatureVerifier: AppleSignatureVerifier { /// <summary> /// Constructs new verifier /// </summary> /// <param name="logger">Logger</param> public MachOSignatureVerifier([CanBeNull] ILogger logger) : base(logger) { } /// <summary> /// Constructs new verifier /// </summary> /// <param name="signedMessageVerifier">Signed message verifier</param> /// <param name="logger">Logger</param> public MachOSignatureVerifier([NotNull] SignedMessageVerifier signedMessageVerifier, [CanBeNull] ILogger logger): base(signedMessageVerifier, logger) { } /// <summary> /// Verify digital signature and file integrity of a Mach-o file /// </summary> /// <param name="machOFile">Parsed Mach-o file</param> /// <param name="stream">Mach-o file raw stream</param> /// <param name="signatureVerificationParams">Verification params</param> /// <param name="fileIntegrityVerificationParams">File integrity verification params</param> /// <returns>Verification result</returns> public async Task<VerifySignatureResult> VerifyAsync( [NotNull] MachOFile machOFile, [NotNull] Stream stream, SignatureVerificationParams signatureVerificationParams, FileIntegrityVerificationParams fileIntegrityVerificationParams) { if (machOFile == null) throw new ArgumentNullException(nameof(machOFile)); if (stream == null) throw new ArgumentNullException(nameof(stream)); foreach (var section in machOFile.Sections) { var sectionVerificationResult = await VerifyAsync(section, stream, signatureVerificationParams, fileIntegrityVerificationParams); if (!sectionVerificationResult.IsValid) return sectionVerificationResult; } _logger?.Info("Mach-O file signature verification successfully passed"); return VerifySignatureResult.Valid; } /// <summary> /// Verify digital signature and file integrity of a single Mach-o file section /// </summary> /// <param name="section">Mach-o file section</param> /// <param name="stream">Mach-o file raw stream. A stream of the entire file is expected.</param> /// <param name="signatureVerificationParams">Verification params</param> /// <param name="fileIntegrityVerificationParams">File integrity verification params</param> /// <returns>Verification result</returns> public async Task<VerifySignatureResult> VerifyAsync( MachOFile.Section section, Stream stream, SignatureVerificationParams signatureVerificationParams, FileIntegrityVerificationParams fileIntegrityVerificationParams) { if (!section.HashVerificationUnits.Any() || !section.CDHashes.Any()) throw new ArgumentException($"Mach-o file was parsed without {nameof(MachOFile.Mode.ComputeHashInfo)} flag", nameof(section)); if (section.SignatureType == MachOFile.SignatureType.AdHoc && !signatureVerificationParams.AllowAdhocSignatures) { _logger?.Warning($"Mach-O file has adhoc signature which is not allowed. Set {nameof(SignatureVerificationParams.AllowAdhocSignatures)} to true is you want to check adhoc signatures."); return new VerifySignatureResult(VerifySignatureStatus.InvalidSignature); } SignedMessage signedMessage = null; bool skipSignedMessageVerification = section.SignatureType == MachOFile.SignatureType.AdHoc & signatureVerificationParams.AllowAdhocSignatures; if (!skipSignedMessageVerification) { signedMessage = SignedMessage.CreateInstance(section.SignatureData); var signatureVerificationResult = await _signedMessageVerifier.VerifySignatureAsync(signedMessage, signatureVerificationParams); if (!signatureVerificationResult.IsValid) { _logger?.Warning("Mach-O file signature verification failed: certificates or attributes validation failed"); return signatureVerificationResult; } } if (!section.HashVerificationUnits.Any()) { _logger?.Warning("Mach-O file signature verification failed: no hash verification units was found in the file"); return new VerifySignatureResult(VerifySignatureStatus.InvalidFileHash); } // Verify hash slots (regular and special) in all Code Directories var codeDirectoryValidationResult = VerifyHashVerificationUnits(stream, section.HashVerificationUnits); if (!codeDirectoryValidationResult.IsValid) { _logger?.Warning("Mach-O file signature verification failed: at least one hash verification unit is invalid"); return codeDirectoryValidationResult; } if (!section.CDHashes.Any()) { _logger?.Warning("Mach-O file signature verification failed: no code directory hashes (CDHash) was found in the file"); return new VerifySignatureResult(VerifySignatureStatus.InvalidFileHash); } if (section.CDHashes.Count() > 1 && !skipSignedMessageVerification) { var cdHashesVerificationResult = VerifyCDHashes(stream, section.CDHashes, signedMessage); if (!cdHashesVerificationResult.IsValid) { _logger?.Warning("Mach-O file signature verification failed: at leash one CDHash verification failed"); return cdHashesVerificationResult; } } _logger?.Info("Mach-O file signature verification successfully passed"); return VerifySignatureResult.Valid; } }