X25519.lean
Declarations on this page (19)
- def p25519
- def leValue
- def uCoord
- def lowOrderUs
- def isLowOrderKey
- structure SubF
- instance L58
- instance L59
-
def SubF.
length - #combine L73
-
abbrev Variant.
SubF. pack - def normalizeX25519Key
- def x25519TorsionVariant
-
def x25519Variant.
attackerKnowledge -
def normalizeX25519Key.
attackerKnowledge - #combine L103
- def x25519LowOrder
- def isCanonicalKey
- def agree
X25519 Key Decoding, Encoding Variants, and 8-Torsion
Byte-level RFC 7748 u-coordinate decoding, low-order point detection, and the DyLean term algebra for non-canonical 32-byte public-key encodings (Fin 4) and 8-torsion shifts (Fin 8).
namespace DY.X25519def p25519
@[expose] public def p25519 : Nat := 2 ^ 255 - 19def leValue
Little-endian Nat value of a byte list.
@[expose] public def leValue : List Nat → Nat
| [] => 0
| x :: xs => x + 256 * leValue xsdef uCoord
RFC 7748 decodeUCoordinate: clear bit 255 and reduce modulo p25519.
def lowOrderUs
Curve25519 and twist u-coordinates in [0, p25519) on which clamped X25519 scalar multiplication outputs all zeros.
@[expose] public def lowOrderUs : List Nat :=
[0, 1, 325606250916557431795983626356110631294008115727848805560023387167927233504,
39382357235489614581723060781553021112529911719440698176882885853963445705823,
p25519 - 1]def isLowOrderKey
Whether bytes is a 32-byte key whose uCoord lies in lowOrderUs.
@[expose] public def isLowOrderKey (bytes : List Nat) : Bool :=
bytes.length == 32 && lowOrderUs.contains (uCoord bytes)namespace Variantstructure SubF
Public key paired with a non-canonical encoding selector variation and 8-torsion shift torsion.
public
structure SubF (Bytes: Type) where
key: Bytes
variation: Fin 4
torsion: Fin 8 := 0instance
public instance: SubBytesFunctor SubF :=
.unaryData (Fin 32)
(fun {variation, torsion, ..} => ⟨variation.val + 4 * torsion.val, a✝:Typex✝:SubF a✝key✝:a✝variation:Fin 4torsion:Fin 8⊢ ↑variation + 4 * ↑torsion < 32 All goals completed! 🐙⟩)
SubF.key
(fun d key => { key, variation := ⟨d.val % 4, a✝:Typed:Fin 32key:a✝⊢ ↑d % 4 < 4 All goals completed! 🐙⟩, torsion := ⟨d.val / 4, a✝:Typed:Fin 32key:a✝⊢ ↑d / 4 < 8 All goals completed! 🐙⟩ })
(fun {key, variation, torsion} => a✝:Typex✝:SubF a✝key:a✝variation:Fin 4torsion:Fin 8⊢ { key := { key := key, variation := variation, torsion := torsion }.key,
variation :=
⟨↑(match { key := key, variation := variation, torsion := torsion } with
| { key := key, variation := variation, torsion := torsion } => ⟨↑variation + 4 * ↑torsion, ⋯⟩) %
4,
⋯⟩,
torsion :=
⟨↑(match { key := key, variation := variation, torsion := torsion } with
| { key := key, variation := variation, torsion := torsion } => ⟨↑variation + 4 * ↑torsion, ⋯⟩) /
4,
⋯⟩ } =
{ key := key, variation := variation, torsion := torsion } a✝:Typex✝:SubF a✝key:a✝variation:Fin 4torsion:Fin 8⊢ (↑variation + 4 * ↑torsion) / 4 = ↑torsion a✝:Typex✝:SubF a✝key:a✝variation:Fin 4torsion:Fin 8⊢ (↑variation + 4 * ↑torsion) / 4 = ↑torsion All goals completed! 🐙)
(fun d _ => a✝:Typed:Fin 32x✝:a✝⊢ (match { key := x✝, variation := ⟨↑d % 4, ⋯⟩, torsion := ⟨↑d / 4, ⋯⟩ } with
| { key := key, variation := variation, torsion := torsion } => ⟨↑variation + 4 * ↑torsion, ⋯⟩) =
d a✝:Typed:Fin 32x✝:a✝⊢ ↑d % 4 + 4 * (↑d / 4) = ↑d; All goals completed! 🐙)
def SubF.
public
def SubF.length [BytesFunctor]: Bytes.PartialLength SubF :=
fun _ _ => 32end Variant#combine
#combine into BytesFunctor, BytesLength from
Variant,variable [BytesFunctor] [BytesFunctor.Has SubF]
abbrev Variant.
public
abbrev Variant.SubF.pack (x: Variant.SubF Bytes) := BytesView.pack xdef normalizeX25519Key
Strip an outer Variant. wrapper if present.
@[expose] public
def normalizeX25519Key (pk: Bytes): Bytes :=
match pk.view? Variant.SubF with
| some x => x.key
| none => pkdef x25519TorsionVariant
Apply encoding selector v and 8-torsion shift t to normalizeX25519Key key, returning the normalized key unchanged when v = 0 ∧ t = 0.
@[expose] public
def x25519TorsionVariant (key: Bytes) (v: Fin 4) (t: Fin 8): Bytes :=
if v = 0 ∧ t = 0 then normalizeX25519Key key
else ({key := normalizeX25519Key key, variation := v, torsion := t}: Variant.SubF Bytes).pack
def x25519Variant.
@[expose] public
def x25519Variant.attackerKnowledge: SubAttackerKnowledge SubF where
pred p out := ∃ key v t, out = x25519TorsionVariant key v t ∧ p key
def normalizeX25519Key.
@[expose] public
def normalizeX25519Key.attackerKnowledge: SubAttackerKnowledge SubF where
pred p out := ∃ x: Variant.SubF Bytes, p x.pack ∧ out = x.key#combine
#combine [BytesFunctor.Has SubF] into attackerKnowledge' from
x25519Variant,
normalizeX25519Key,def x25519LowOrder
Whether normalizeX25519Key pk is a literal 32-byte low-order key.
@[expose] public
def x25519LowOrder [BytesFunctor.Has Literal.SubF] (pk: Bytes): Bool :=
match Literal.bytesToLiteral (normalizeX25519Key pk) with
| some b => isLowOrderKey (b.data.toList.map (·.toNat))
| none => falsedef isCanonicalKey
Symbolic PublicKey::is_canonical check: rejects Variant. wrappers and literal low-order keys.
@[expose] public
def isCanonicalKey [BytesFunctor.Has Literal.SubF] (pk: Bytes): Bool :=
(pk.view? Variant.SubF).isNone && !x25519LowOrder pkdef agree
Symbolic X25519 Diffie-Hellman agreement on normalizeX25519Key pk and secret key sk.
@[expose] public
def agree [BytesFunctor.Has DiffieHellman.SubF] (pk sk: Bytes): Bytes :=
DiffieHellman.dh (normalizeX25519Key pk) skend DY.X25519