Module: Ruby::Rego::Builtins::Crypto
- Extended by:
- RegistryHelpers
- Defined in:
- lib/ruby/rego/builtins/crypto.rb,
lib/ruby/rego/builtins/crypto/keys.rb,
lib/ruby/rego/builtins/crypto/verify.rb,
lib/ruby/rego/builtins/crypto/keypair.rb,
lib/ruby/rego/builtins/crypto/certificates.rb,
lib/ruby/rego/builtins/crypto/certificate_uri.rb,
lib/ruby/rego/builtins/crypto/certificate_name.rb,
lib/ruby/rego/builtins/crypto/certificate_struct.rb,
lib/ruby/rego/builtins/crypto/certificate_request.rb,
lib/ruby/rego/builtins/crypto/certificate_extensions.rb,
lib/ruby/rego/builtins/crypto/certificate_public_key.rb,
lib/ruby/rego/builtins/crypto/certificate_request_struct.rb
Overview
crypto.x509.parse_certificate_request — parse one PEM / base64-DER PKCS#10 certificate request into the JSON shape OPA emits: json.Marshal of Go’s x509.CertificateRequest (a single object, not an array). Reuses the certificate machinery’s atoms (Name, public key, signature algorithm, the ASN.1 depth/type guards, the SAN validators, scrub) since a CSR is a strict subset of a certificate’s fields — no validity, issuer, or the dozen parsed extensions a certificate carries.
Defined Under Namespace
Modules: CertificateStruct, Name Classes: MalformedCertificate
Constant Summary collapse
- HMAC_ALGORITHMS =
Maps each crypto.hmac.* digest builtin to its OpenSSL digest name.
{ "crypto.hmac.md5" => "MD5", "crypto.hmac.sha1" => "SHA1", "crypto.hmac.sha256" => "SHA256", "crypto.hmac.sha512" => "SHA512" }.freeze
- CRYPTO_FUNCTIONS =
{ "crypto.md5" => { arity: 1, handler: :md5 }, "crypto.sha1" => { arity: 1, handler: :sha1 }, "crypto.sha256" => { arity: 1, handler: :sha256 }, "crypto.hmac.md5" => { arity: 2, handler: :hmac_md5 }, "crypto.hmac.sha1" => { arity: 2, handler: :hmac_sha1 }, "crypto.hmac.sha256" => { arity: 2, handler: :hmac_sha256 }, "crypto.hmac.sha512" => { arity: 2, handler: :hmac_sha512 }, "crypto.hmac.equal" => { arity: 2, handler: :hmac_equal }, "crypto.x509.parse_rsa_private_key" => { arity: 1, handler: :parse_rsa_private_key }, "crypto.parse_private_keys" => { arity: 1, handler: :parse_private_keys } }.freeze
- RECOGNIZED_KEY_BLOCKS =
PEM block types getPrivateKeysFromPEMData parses; every other type is skipped, not failed.
["RSA PRIVATE KEY", "PRIVATE KEY", "EC PRIVATE KEY"].freeze
- PEM_START =
The pem.Decode marker sentinels (Go’s pemStart / pemEnd / pemEndOfLine), with and without the leading newline that anchors a marker to the start of a line.
"\n-----BEGIN "- PEM_START_NOLF =
"-----BEGIN "- PEM_END =
"\n-----END "- PEM_END_NOLF =
"-----END "- PEM_EOL =
"-----"- BASE64_SKIP =
The bytes Go’s base64 decoder treats as insignificant (removeSpacesAndTabs strips “ \t”; the decoder ignores “\r\n”), and the complement of the std-base64 alphabet (plus padding and those skip bytes) — a byte matching it inside a block body dooms the decode.
" \t\r\n"- NON_BASE64_BYTE =
%r{[^A-Za-z0-9+/= \t\r\n]}- EC_CURVE_NAMES =
JWK “crv” names (RFC 7518) for the curves the JWK encoding covers (P-224 is excluded).
{ "prime256v1" => "P-256", "secp384r1" => "P-384", "secp521r1" => "P-521" }.freeze
- EC_STRUCT_CURVES =
The EC curves Go’s crypto/elliptic supports (so x509 parses them): the JWK curves plus P-224, which the Go-struct output handles even though the JWK encoding does not.
(EC_CURVE_NAMES.keys + ["secp224r1"]).freeze
- BLOCK_FORMAT_ELEMENT =
Go dispatches each PEM block type to ONE x509 parser (PKCS#1 / PKCS#8 / SEC1), so a DER in the wrong format for its label is rejected even though OpenSSL’s format-agnostic reader would accept it. The three formats are distinguished by their second ASN.1 element: PKCS#1’s is the modulus INTEGER, PKCS#8’s is the algorithm SEQUENCE, SEC1’s is the private-key OCTET STRING.
{ "RSA PRIVATE KEY" => OpenSSL::ASN1::Integer, "PRIVATE KEY" => OpenSSL::ASN1::Sequence, "EC PRIVATE KEY" => OpenSSL::ASN1::OctetString }.freeze
- MAX_KEY_DER_BYTES =
Upper bound on the leading ASN.1 element a block may parse to before OpenSSL::PKey.read is attempted (DoS guard — see read_key). 64 KiB is far above any real private key (an RSA-16384 DER is ~9 KB), so only an absurd >130K-bit key would exceed it.
64 * 1024
- VERIFY_FUNCTIONS =
{ "crypto.x509.parse_and_verify_certificates" => { arity: 1, handler: :parse_and_verify_certificates }, "crypto.x509.parse_and_verify_certificates_with_options" => { arity: 2, handler: :parse_and_verify_certificates_with_options } }.freeze
- INSECURE_SIGNATURE =
Signature digests Go’s x509 rejects as insecure (InsecureAlgorithmError) on non-root certs. The token appears mid-word in OpenSSL’s algorithm names (sha1WithRSAEncryption, ecdsa-with-SHA1, md5WithRSAEncryption), and none of the safe names (sha256/384/512, …) contain it, so no anchors.
/md2|md5|sha1/i- KEY_USAGE_ENUM =
OPA’s KeyUsages option enum -> Go’s ExtKeyUsage integer (the value CertificateStruct emits in a cert’s ExtKeyUsage field). KeyUsageAny (0) drops the EKU constraint entirely. The integers MUST match CertificateStruct::EXT_KEY_USAGES (the OID->enum map the chain structs are built with).
{ "KeyUsageAny" => 0, "KeyUsageServerAuth" => 1, "KeyUsageClientAuth" => 2, "KeyUsageCodeSigning" => 3, "KeyUsageEmailProtection" => 4, "KeyUsageIPSECEndSystem" => 5, "KeyUsageIPSECTunnel" => 6, "KeyUsageIPSECUser" => 7, "KeyUsageTimeStamping" => 8, "KeyUsageOCSPSigning" => 9, "KeyUsageMicrosoftServerGatedCrypto" => 10, "KeyUsageNetscapeServerGatedCrypto" => 11, "KeyUsageMicrosoftCommercialCodeSigning" => 12, "KeyUsageMicrosoftKernelCodeSigning" => 13 }.freeze
- ANY_EXT_KEY_USAGE =
Go’s ExtKeyUsageAny
0- SERVER_AUTH_EXT_KEY_USAGE =
Go’s empty-KeyUsages default
1- VERIFY_OPTION_KEYS =
The option keys OPA 1.17 accepts; any other key (including the doc-mentioned MaxPathLen, which this OPA version does not recognize) makes the call undefined.
%w[DNSName CurrentTime KeyUsages].freeze
- KEYPAIR_FUNCTIONS =
{ "crypto.x509.parse_keypair" => { arity: 2, handler: :parse_keypair } }.freeze
- CERTIFICATE_FUNCTIONS =
{ "crypto.x509.parse_certificates" => { arity: 1, handler: :parse_certificates } }.freeze
- REQUEST_FUNCTIONS =
{ "crypto.x509.parse_certificate_request" => { arity: 1, handler: :parse_certificate_request } }.freeze
Class Method Summary collapse
-
.hmac_equal(mac1_value, mac2_value) ⇒ Ruby::Rego::BooleanValue
Constant-time comparison of two MAC strings (OPA’s crypto.hmac.equal).
-
.hmac_md5(message_value, key_value) ⇒ Ruby::Rego::StringValue
-
.hmac_sha1(message_value, key_value) ⇒ Ruby::Rego::StringValue
-
.hmac_sha256(message_value, key_value) ⇒ Ruby::Rego::StringValue
-
.hmac_sha512(message_value, key_value) ⇒ Ruby::Rego::StringValue
-
.md5(value) ⇒ Ruby::Rego::StringValue
-
.parse_and_verify_certificates(value) ⇒ Ruby::Rego::Value
-
.parse_and_verify_certificates_with_options(value, options_value) ⇒ Ruby::Rego::Value
:reek:NilCheck – :invalid is the bad-options sentinel mapped to OPA’s undefined.
-
.parse_certificate_request(value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is request_from’s parse-failure sentinel (-> OPA undefined).
-
.parse_certificates(value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is certificates_from’s “a block failed to parse” sentinel (-> undefined).
-
.parse_keypair(cert_value, key_value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is the parse-failure sentinel from keypair_cert_ders / matching_key.
-
.parse_private_keys(value) ⇒ Ruby::Rego::Value
:reek:TooManyStatements – a faithful port of OPA’s builtinCryptoParsePrivateKeys flow.
-
.parse_rsa_private_key(value) ⇒ Ruby::Rego::Value
:reek:TooManyStatements – a faithful port of OPA’s builtinCryptoJWKFromPrivateKey flow.
-
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.register_certificates! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.register_keypairs! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.register_requests! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.register_verifications! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
-
.sha1(value) ⇒ Ruby::Rego::StringValue
-
.sha256(value) ⇒ Ruby::Rego::StringValue
Methods included from RegistryHelpers
Class Method Details
.hmac_equal(mac1_value, mac2_value) ⇒ Ruby::Rego::BooleanValue
Constant-time comparison of two MAC strings (OPA’s crypto.hmac.equal). Uses OpenSSL.secure_compare, which is timing-safe and returns false (rather than raising) for unequal-length inputs, matching Go’s hmac.Equal.
120 121 122 123 124 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 120 def self.hmac_equal(mac1_value, mac2_value) left = string_value(mac1_value, "crypto.hmac.equal") right = string_value(mac2_value, "crypto.hmac.equal") BooleanValue.new(OpenSSL.secure_compare(left, right)) end |
.hmac_md5(message_value, key_value) ⇒ Ruby::Rego::StringValue
73 74 75 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 73 def self.hmac_md5(, key_value) hmac(, key_value, "crypto.hmac.md5") end |
.hmac_sha1(message_value, key_value) ⇒ Ruby::Rego::StringValue
80 81 82 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 80 def self.hmac_sha1(, key_value) hmac(, key_value, "crypto.hmac.sha1") end |
.hmac_sha256(message_value, key_value) ⇒ Ruby::Rego::StringValue
87 88 89 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 87 def self.hmac_sha256(, key_value) hmac(, key_value, "crypto.hmac.sha256") end |
.hmac_sha512(message_value, key_value) ⇒ Ruby::Rego::StringValue
94 95 96 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 94 def self.hmac_sha512(, key_value) hmac(, key_value, "crypto.hmac.sha512") end |
.md5(value) ⇒ Ruby::Rego::StringValue
54 55 56 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 54 def self.md5(value) StringValue.new(Digest::MD5.hexdigest(string_value(value, "crypto.md5"))) end |
.parse_and_verify_certificates(value) ⇒ Ruby::Rego::Value
69 70 71 72 73 |
# File 'lib/ruby/rego/builtins/crypto/verify.rb', line 69 def self.parse_and_verify_certificates(value) string = string_value(value, "crypto.x509.parse_and_verify_certificates") # No-options: default usage ServerAuth, and each chain cert keeps the injected URIStrings field. verify_result(string, { key_usages: [SERVER_AUTH_EXT_KEY_USAGE] }, uri_strings: true) end |
.parse_and_verify_certificates_with_options(value, options_value) ⇒ Ruby::Rego::Value
:reek:NilCheck – :invalid is the bad-options sentinel mapped to OPA’s undefined.
79 80 81 82 83 84 85 86 87 |
# File 'lib/ruby/rego/builtins/crypto/verify.rb', line 79 def self.(value, ) string = string_value(value, "crypto.x509.parse_and_verify_certificates_with_options") opts = () return UndefinedValue.new unless opts.is_a?(Hash) # ...with_options marshals each chain cert as Go's raw x509.Certificate — WITHOUT the URIStrings # field parse_certificates injects (an OPA quirk: the two builtins differ here). verify_result(string, opts, uri_strings: false) end |
.parse_certificate_request(value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is request_from’s parse-failure sentinel (-> OPA undefined).
29 30 31 32 33 34 35 36 37 |
# File 'lib/ruby/rego/builtins/crypto/certificate_request.rb', line 29 def self.parse_certificate_request(value) string = string_value(value, "crypto.x509.parse_certificate_request") return UndefinedValue.new unless scannable?(string) request = request_from(string) return UndefinedValue.new if request.nil? build_request_struct(request) end |
.parse_certificates(value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is certificates_from’s “a block failed to parse” sentinel (-> undefined).
39 40 41 42 43 44 45 46 47 |
# File 'lib/ruby/rego/builtins/crypto/certificates.rb', line 39 def self.parse_certificates(value) string = string_value(value, "crypto.x509.parse_certificates") return UndefinedValue.new unless scannable?(string) certs = certificates_from(string) return UndefinedValue.new if certs.nil? build_structs(certs) end |
.parse_keypair(cert_value, key_value) ⇒ Ruby::Rego::Value
:reek:NilCheck – nil is the parse-failure sentinel from keypair_cert_ders / matching_key. :reek:TooManyStatements – a faithful port of OPA’s keypair parse + validation flow. rubocop:disable Metrics/AbcSize
38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 |
# File 'lib/ruby/rego/builtins/crypto/keypair.rb', line 38 def self.parse_keypair(cert_value, key_value) cert_string = string_value(cert_value, "crypto.x509.parse_keypair") key_string = string_value(key_value, "crypto.x509.parse_keypair") return UndefinedValue.new unless scannable?(cert_string) && scannable?(key_string) cert_ders = keypair_cert_ders(cert_string) return UndefinedValue.new if cert_ders.nil? leaf = leaf_certificate(cert_ders.first) return UndefinedValue.new if leaf.nil? key = matching_key(leaf, key_string) return UndefinedValue.new if key.nil? build_keypair_struct(cert_ders, leaf, key) end |
.parse_private_keys(value) ⇒ Ruby::Rego::Value
:reek:TooManyStatements – a faithful port of OPA’s builtinCryptoParsePrivateKeys flow. :reek:NilCheck – nil is supported_keys’s “a recognized block failed or is unsupported” sentinel.
81 82 83 84 85 86 87 88 89 90 |
# File 'lib/ruby/rego/builtins/crypto/keys.rb', line 81 def self.parse_private_keys(value) string = string_value(value, "crypto.parse_private_keys") return UndefinedValue.new unless scannable?(string) return NullValue.new if string.empty? keys = supported_keys(string) return UndefinedValue.new if keys.nil? Value.from_ruby(keys.map { |key| go_struct_for(key) }) end |
.parse_rsa_private_key(value) ⇒ Ruby::Rego::Value
:reek:TooManyStatements – a faithful port of OPA’s builtinCryptoJWKFromPrivateKey flow. :reek:NilCheck – nil sentinels: bad base64/failed block (undefined), JWK-unsupported key (undefined). rubocop:disable Metrics/AbcSize
97 98 99 100 101 102 103 104 105 106 107 108 109 110 |
# File 'lib/ruby/rego/builtins/crypto/keys.rb', line 97 def self.parse_rsa_private_key(value) string = string_value(value, "crypto.x509.parse_rsa_private_key") return UndefinedValue.new if !scannable?(string) || string.empty? pem = pem_or_base64(string) return UndefinedValue.new if pem.nil? keys = supported_keys(pem) return UndefinedValue.new if keys.nil? return NullValue.new if keys.empty? jwk = jwk_for(keys.first) jwk.nil? ? UndefinedValue.new : Value.from_ruby(jwk) end |
.register! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
44 45 46 47 48 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 44 def self.register! registry = BuiltinRegistry.instance register_configured_functions(registry, CRYPTO_FUNCTIONS) registry end |
.register_certificates! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
30 31 32 33 34 |
# File 'lib/ruby/rego/builtins/crypto/certificates.rb', line 30 def self.register_certificates! registry = BuiltinRegistry.instance register_configured_functions(registry, CERTIFICATE_FUNCTIONS) registry end |
.register_keypairs! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
26 27 28 29 30 |
# File 'lib/ruby/rego/builtins/crypto/keypair.rb', line 26 def self.register_keypairs! registry = BuiltinRegistry.instance register_configured_functions(registry, KEYPAIR_FUNCTIONS) registry end |
.register_requests! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
20 21 22 23 24 |
# File 'lib/ruby/rego/builtins/crypto/certificate_request.rb', line 20 def self.register_requests! registry = BuiltinRegistry.instance register_configured_functions(registry, REQUEST_FUNCTIONS) registry end |
.register_verifications! ⇒ Ruby::Rego::Builtins::BuiltinRegistry
61 62 63 64 65 |
# File 'lib/ruby/rego/builtins/crypto/verify.rb', line 61 def self.register_verifications! registry = BuiltinRegistry.instance register_configured_functions(registry, VERIFY_FUNCTIONS) registry end |
.sha1(value) ⇒ Ruby::Rego::StringValue
60 61 62 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 60 def self.sha1(value) StringValue.new(Digest::SHA1.hexdigest(string_value(value, "crypto.sha1"))) end |
.sha256(value) ⇒ Ruby::Rego::StringValue
66 67 68 |
# File 'lib/ruby/rego/builtins/crypto.rb', line 66 def self.sha256(value) StringValue.new(Digest::SHA256.hexdigest(string_value(value, "crypto.sha256"))) end |