File: statement.rb

package info (click to toggle)
ruby-android-key-attestation 0.3.0-2
  • links: PTS, VCS
  • area: main
  • in suites: bookworm, bullseye
  • size: 216 kB
  • sloc: ruby: 352; makefile: 7; sh: 4
file content (56 lines) | stat: -rw-r--r-- 1,956 bytes parent folder | download
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
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
52
53
54
55
56
# frozen_string_literal: true

require "forwardable"
require "openssl"
require_relative "key_description"
require_relative "fixed_length_secure_compare"

module AndroidKeyAttestation
  class Statement
    EXTENSION_DATA_OID = "1.3.6.1.4.1.11129.2.1.17"
    GOOGLE_ROOT_CERTIFICATES = begin
      file = File.read(File.join(GEM_ROOT, "android_key_attestation", "google_hardware_attestation_root.pem"))
      [OpenSSL::X509::Certificate.new(file)]
    end.freeze

    extend Forwardable
    def_delegators :key_description, :attestation_version, :attestation_security_level, :keymaster_version,
                   :keymaster_security_level, :unique_id, :tee_enforced, :software_enforced

    using FixedLengthSecureCompare

    def initialize(*certificates)
      @certificates = certificates
    end

    def attestation_certificate
      @certificates.first
    end

    def verify_challenge(challenge)
      attestation_challenge = key_description.attestation_challenge
      attestation_challenge.bytesize == challenge.bytesize &&
        OpenSSL.fixed_length_secure_compare(attestation_challenge, challenge) ||
        raise(ChallengeMismatchError)
    end

    def verify_certificate_chain(root_certificates: GOOGLE_ROOT_CERTIFICATES, time: Time.now)
      store = OpenSSL::X509::Store.new
      root_certificates.each { |cert| store.add_cert(cert) }
      store.time = time

      store.verify(attestation_certificate, @certificates[1..-1]) ||
        raise(CertificateVerificationError, store.error_string)
    end

    def key_description
      @key_description ||= begin
        extension_data = attestation_certificate.extensions.detect { |ext| ext.oid == EXTENSION_DATA_OID }
        raise AndroidKeyAttestation::ExtensionMissingError unless extension_data

        raw_key_description = OpenSSL::ASN1.decode(extension_data).value.last
        KeyDescription.new(OpenSSL::ASN1.decode(raw_key_description.value).value)
      end
    end
  end
end