load "Montgomery.m";

// Useful stuff:
p := 2^255 - 19;
k := FiniteField(p);
Curve25519 := MontgomeryModel(k!486662,1);
x := xMap(Curve25519);
PP1 := Codomain(x);

function Compressed(xP)
    // Compress xP = (x:z) on PP1 down to x*z^(p-2) mod p, and return it.

    /* EXERCISE 3.1: complete here */

end function;

function Decompressed(compressed_pt)
    // Decompress compressed_pt (a 255-bit integer)
    // to a point on PP1, and return the result.

    compressed_pt mod:= 2^255; // truncate if necessary, and mask last bit

    /* EXERCISE 3.1: complete here */

end function;

function Clamped(scalar)
    // "Clamp" the 256-bit scalar, and return the result.

    scalar mod:= 2^256; // truncate to 256 bits if required

    /* EXERCISE 3.2: complete here */

end function;

function X25519(m,u)
    // The X25519 function, as defined in RFC7748.
    // The arguments m and u are non-negative integers
    // to be interpreted as 256-bit values (truncating if necessary).
    
    // Step 1: Clamp m
    m := Clamped(m);

    // Step 2: Decompress the point

    xP := Decompressed(u);

    // Step 3: Apply the ladder
    xmP := Ladder(Curve25519,m,xP);

    // Step 4: Compress the result
    v := Compressed(xmP);

    return v;
end function;
