From f55dc17dfb3d7862146f8ea286d5a66228c43304 Mon Sep 17 00:00:00 2001 From: Matt Palmer Date: Tue, 17 Oct 2017 12:09:31 +1100 Subject: [PATCH] Check for ROCA factorisation weaknesses Based on sample Python code from https://github.com/crocs-muni/roca. --- lib/certlint/certlint.rb | 5 +++ lib/certlint/extensions/rocatest.rb | 48 +++++++++++++++++++++++++++++ 2 files changed, 53 insertions(+) create mode 100644 lib/certlint/extensions/rocatest.rb diff --git a/lib/certlint/certlint.rb b/lib/certlint/certlint.rb index 04869e1..a057349 100755 --- a/lib/certlint/certlint.rb +++ b/lib/certlint/certlint.rb @@ -37,6 +37,7 @@ require_relative 'extensions/policymappings' require_relative 'extensions/privatekeyusageperiod' require_relative 'extensions/qcstatements' +require_relative 'extensions/rocatest' require_relative 'extensions/signedcertificatetimestamplist' require_relative 'extensions/smimecapabilities' require_relative 'extensions/subjectaltname' @@ -351,6 +352,10 @@ def self.lint(der) return messages end + if ROCATest.new(cert).unsafe_key? + messages << 'E: Public key is vulnerable to ROCA factorisation' + end + if cert.version > 2 messages << 'E: Invalid certificate version' elsif cert.version < 2 diff --git a/lib/certlint/extensions/rocatest.rb b/lib/certlint/extensions/rocatest.rb new file mode 100644 index 0000000..835e9d8 --- /dev/null +++ b/lib/certlint/extensions/rocatest.rb @@ -0,0 +1,48 @@ +class ROCATest + PRIMES = [3, 5, 7, 11, 13, 17, 19, 23, 29, 31, 37, 41, 43, 47, 53, 59, 61, + 67, 71, 73, 79, 83, 89, 97, 101, 103, 107, 109, 113, 127, 131, + 137, 139, 149, 151, 157, 163, 167] + + PRINTS = [6, 30, 126, 1026, 5658, 107286, 199410, 8388606, 536870910, + 2147483646, 67109890, 2199023255550, 8796093022206, + 140737488355326, 5310023542746834, 576460752303423486, + 1455791217086302986, 147573952589676412926, 20052041432995567486, + 6041388139249378920330, 207530445072488465666, + 9671406556917033397649406, 618970019642690137449562110, + 79228162521181866724264247298, 2535301200456458802993406410750, + 1760368345969468176824550810518, 50079290986288516948354744811034, + 473022961816146413042658758988474, + 10384593717069655257060992658440190, + 144390480366845522447407333004847678774, + 2722258935367507707706996859454145691646, + 174224571863520493293247799005065324265470, + 696898287454081973172991196020261297061886, + 713623846352979940529142984724747568191373310, + 1800793591454480341970779146165214289059119882, + 126304807362733370595828809000324029340048915994, + 11692013098647223345629478661730264157247460343806, + 187072209578355573530071658587684226515959365500926] + + def initialize(cert) + unless cert.is_a?(OpenSSL::X509::Certificate) + raise ArgumentError, "This class only accepts OpenSSL::X509::Certificate objects" + end + + @public_key = cert.public_key + end + + def unsafe_key? + # Only RSA keys are potentially vulnerable to ROCA factorisation + return false unless @public_key.is_a?(OpenSSL::PKey::RSA) + + modulus = @public_key.n + + PRIMES.length.times do |i| + if (1 << (modulus % PRIMES[i])) & PRINTS[i] == 0 + return false + end + end + + true + end +end