Module: Pdfrb::DigitalSignature::Verification
- Defined in:
- lib/pdfrb/digital_signature/verification.rb
Class Method Summary collapse
-
.der_prefix(bytes) ⇒ Object
The DER structure starting at
bytes(a SEQUENCE), exactly as long as its own declared length - any placeholder padding after it is dropped. - .find_signature(pdf_bytes, start_offset) ⇒ Object
- .verify(pdf_bytes, trusted_certs: []) ⇒ Object
- .verify_pkcs7(der, signed_data, trusted_certs) ⇒ Object
- .verify_signature(pdf_bytes, sig_info, trusted_certs) ⇒ Object
Class Method Details
.der_prefix(bytes) ⇒ Object
The DER structure starting at bytes (a SEQUENCE), exactly
as long as its own declared length - any placeholder padding
after it is dropped. Non-SEQUENCE input passes through.
109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 |
# File 'lib/pdfrb/digital_signature/verification.rb', line 109 def der_prefix(bytes) return bytes if bytes.bytesize < 2 || bytes.getbyte(0) != 0x30 length = bytes.getbyte(1) offset = 2 if length.anybits?(0x80) count = length & 0x7F return bytes if count.zero? || bytes.bytesize < 2 + count length = 0 count.times do |i| length = (length << 8) | bytes.getbyte(2 + i) end offset = 2 + count end bytes.byteslice(0, offset + length) end |
.find_signature(pdf_bytes, start_offset) ⇒ Object
26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 |
# File 'lib/pdfrb/digital_signature/verification.rb', line 26 def find_signature(pdf_bytes, start_offset) contents_marker = "/Contents <" ck_start = pdf_bytes.index(contents_marker, start_offset) return nil unless ck_start hex_start = ck_start + contents_marker.bytesize hex_end = pdf_bytes.index(">", hex_start) return nil unless hex_end br_start = pdf_bytes.rindex("/ByteRange [", ck_start) return nil unless br_start br_str_start = br_start + "/ByteRange [".bytesize br_end = pdf_bytes.index("]", br_str_start) return nil unless br_end br_str = pdf_bytes[br_str_start...br_end] byte_range = br_str.split.map(&:to_i) { byte_range: byte_range, contents_hex_start: hex_start, contents_hex_end: hex_end, contents_end: hex_end, } end |
.verify(pdf_bytes, trusted_certs: []) ⇒ Object
11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/pdfrb/digital_signature/verification.rb', line 11 def verify(pdf_bytes, trusted_certs: []) results = [] offset = 0 loop do sig_info = find_signature(pdf_bytes, offset) break unless sig_info results << verify_signature(pdf_bytes, sig_info, trusted_certs) offset = sig_info[:contents_end] + 1 end results end |
.verify_pkcs7(der, signed_data, trusted_certs) ⇒ Object
81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 |
# File 'lib/pdfrb/digital_signature/verification.rb', line 81 def verify_pkcs7(der, signed_data, trusted_certs) pkcs7 = OpenSSL::PKCS7.new(der) store = OpenSSL::X509::Store.new trusted_certs.each { |c| store.add_cert(c) } valid = pkcs7.verify(nil, store, signed_data, OpenSSL::PKCS7::DETACHED | OpenSSL::PKCS7::BINARY) VerificationResult.new( signer: pkcs7.signers.first&.issuer&.to_s, valid?: valid, byte_range_ok?: true, cert_chain: pkcs7.certificates || [], trusted?: !trusted_certs.empty? && valid, error: valid ? nil : "signature verification failed", ) rescue OpenSSL::PKCS7::PKCS7Error, ArgumentError => e VerificationResult.new(valid?: false, byte_range_ok?: true, cert_chain: [], error: e.) end |
.verify_signature(pdf_bytes, sig_info, trusted_certs) ⇒ Object
53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 |
# File 'lib/pdfrb/digital_signature/verification.rb', line 53 def verify_signature(pdf_bytes, sig_info, trusted_certs) byte_range = sig_info[:byte_range] if byte_range.nil? || byte_range.length != 4 return VerificationResult.new(valid?: false, byte_range_ok?: false, cert_chain: [], error: "invalid ByteRange") end _start1, len1, start2, len2 = byte_range part1 = pdf_bytes.bytes[0, len1] || [] part2 = pdf_bytes.bytes[start2, len2] || [] signed_data = part1.pack("C*") + part2.pack("C*") hex_start = sig_info[:contents_hex_start] hex_end = sig_info[:contents_hex_end] contents_hex = pdf_bytes[hex_start...hex_end] # The /Contents placeholder is zero-padded to its reserved # size; DER is self-delimiting, so der_prefix reads the # declared length instead of guessing where the signature # ends (blindly trimming trailing zeros also eats legitimate # 0x00 final bytes and can split a hex byte). verify_pkcs7(der_prefix([contents_hex].pack("H*")), signed_data, trusted_certs) end |