@@ -79,7 +79,39 @@ public static byte[] ConvertX25519PublicKeyToEd25519(byte[] x25519PublicKey)
79
79
80
80
// Clear the sign bit (bit 7 of the last byte) as per Ed25519 specification
81
81
edPublicKey [ 31 ] &= 0x7F ;
82
-
82
+ // Implements the birational map from Montgomery (X25519) u to Edwards (Ed25519) y:
83
+ // y = (u - 1) / (u + 1) mod p
84
+ // See: https://tools.ietf.org/html/rfc7748#section-5
85
+
86
+ // Curve25519 prime: 2^255 - 19
87
+ BigInteger p = Ed25519FieldElement . Q ;
88
+
89
+ // Interpret the X25519 public key as a little-endian integer u
90
+ byte [ ] uBytes = new byte [ 32 ] ;
91
+ Array . Copy ( x25519PublicKey , uBytes , 32 ) ;
92
+ // Ensure top bit is masked (Montgomery u-coordinate is 255 bits)
93
+ uBytes [ 31 ] &= 0x7F ;
94
+ BigInteger u = new BigInteger ( 1 , uBytes . Reverse ( ) . ToArray ( ) ) ;
95
+
96
+ // Compute y = (u - 1) * (u + 1)^-1 mod p
97
+ BigInteger one = BigInteger . One ;
98
+ BigInteger uMinus1 = u . Subtract ( one ) . Mod ( p ) ;
99
+ BigInteger uPlus1 = u . Add ( one ) . Mod ( p ) ;
100
+ BigInteger uPlus1Inv = uPlus1 . ModInverse ( p ) ;
101
+ BigInteger y = uMinus1 . Multiply ( uPlus1Inv ) . Mod ( p ) ;
102
+
103
+ // Encode y as 32-byte little-endian
104
+ byte [ ] yBytes = y . ToByteArrayUnsigned ( ) ;
105
+ byte [ ] edPublicKey = new byte [ 32 ] ;
106
+ // Copy yBytes into edPublicKey (little-endian)
107
+ for ( int i = 0 ; i < yBytes . Length && i < 32 ; i ++ )
108
+ {
109
+ edPublicKey [ i ] = yBytes [ yBytes . Length - 1 - i ] ;
110
+ }
111
+ // If yBytes is shorter than 32 bytes, the rest is already zero
112
+
113
+ // Set the sign bit to 0 (positive x)
114
+ edPublicKey [ 31 ] &= 0x7F ;
83
115
return edPublicKey ;
84
116
}
85
117
0 commit comments