153 lines
4.5 KiB
Haxe
153 lines
4.5 KiB
Haxe
package leenkx.network.torrent;
|
|
|
|
import haxe.io.Bytes;
|
|
import leenkx.network.torrent.Base58.Base58Check;
|
|
|
|
class Crypto {
|
|
|
|
public static var SEEDPREFIX(default, null) = "490a";
|
|
public static var ADDRESSPREFIX(default, null) = "55";
|
|
|
|
static var drbg:Aegis128x4 = null;
|
|
static var drbgBuf:Bytes = null;
|
|
static var drbgPos:Int = 0;
|
|
static var drbgNative:Int = -1;
|
|
|
|
public static function randomBytes(length:Int):Bytes {
|
|
var bytes = Bytes.alloc(length);
|
|
#if (js && !kha_krom)
|
|
var webCrypto:Dynamic = js.Syntax.code("(typeof globalThis !== 'undefined' ? globalThis.crypto : null)");
|
|
if (webCrypto != null && webCrypto.getRandomValues != null) {
|
|
var view = new js.lib.Uint8Array(length);
|
|
webCrypto.getRandomValues(view);
|
|
for (i in 0...length) bytes.set(i, view[i]);
|
|
return bytes;
|
|
}
|
|
#end
|
|
#if lnx_datachannel
|
|
if (drbgNative == -1) {
|
|
var seed = osEntropy(32);
|
|
try {
|
|
drbgNative = datachannel.RTC.aegisAvailable()
|
|
&& datachannel.RTC.aegisInit(seed.sub(0, 16), seed.sub(16, 16)) ? 1 : 0;
|
|
} catch (e:Dynamic) {
|
|
drbgNative = 0;
|
|
}
|
|
}
|
|
if (drbgNative == 1) {
|
|
return datachannel.RTC.aegisRandom(length);
|
|
}
|
|
#end
|
|
if (drbg == null) {
|
|
var seed = osEntropy(32);
|
|
drbg = new Aegis128x4(seed.sub(0, 16), seed.sub(16, 16));
|
|
drbgBuf = Bytes.alloc(0);
|
|
drbgPos = 0;
|
|
}
|
|
var off = 0;
|
|
while (off < length) {
|
|
if (drbgPos >= drbgBuf.length) {
|
|
drbgBuf = drbg.nextBlock();
|
|
drbgPos = 0;
|
|
}
|
|
var n = Std.int(Math.min(drbgBuf.length - drbgPos, length - off));
|
|
bytes.blit(off, drbgBuf, drbgPos, n);
|
|
drbgPos += n;
|
|
off += n;
|
|
}
|
|
return bytes;
|
|
}
|
|
|
|
static function osEntropy(length:Int):Bytes {
|
|
var bytes = Bytes.alloc(length);
|
|
#if kha_krom
|
|
var kromBytes = leenkx.network.torrent.transport.RtcNative.randomBytes(length);
|
|
if (kromBytes != null) return kromBytes;
|
|
#end
|
|
#if sys
|
|
if (Sys.systemName() != "Windows") {
|
|
try {
|
|
var f = sys.io.File.read("/dev/urandom", true);
|
|
f.readBytes(bytes, 0, length);
|
|
f.close();
|
|
return bytes;
|
|
} catch(e:Dynamic) {}
|
|
} else {
|
|
try {
|
|
var p = new sys.io.Process("powershell", [
|
|
"-NoProfile", "-Command",
|
|
"$r = New-Object 'byte[]' " + length + "; [Security.Cryptography.RandomNumberGenerator]::Create().GetBytes($r); -join ($r | ForEach-Object { $_.ToString('x2') })"
|
|
]);
|
|
var hex = StringTools.trim(p.stdout.readAll().toString());
|
|
p.close();
|
|
if (hex.length == length * 2) {
|
|
return Bytes.ofHex(hex);
|
|
}
|
|
} catch(e:Dynamic) {
|
|
trace("Windows RNG via PowerShell failed: " + e);
|
|
}
|
|
}
|
|
#end
|
|
throw "Crypto: OS entropy unavailable. Refusing to generate predictable keys.";
|
|
}
|
|
|
|
public static function hash(data:Bytes):Bytes {
|
|
return TweetNaCl.hash(data);
|
|
}
|
|
|
|
public static function signDetached(message:Bytes, secretKey:Bytes):Bytes {
|
|
return TweetNaCl.signDetached(message, secretKey);
|
|
}
|
|
|
|
public static function signDetachedVerify(message:Bytes, signature:Bytes, publicKey:Bytes):Bool {
|
|
return TweetNaCl.signDetachedVerify(message, signature, publicKey);
|
|
}
|
|
|
|
public static function box(message:Bytes, nonce:Bytes, theirPublicKey:Bytes, mySecretKey:Bytes):Bytes {
|
|
return TweetNaCl.box(message, nonce, theirPublicKey, mySecretKey);
|
|
}
|
|
|
|
public static function boxOpen(message:Bytes, nonce:Bytes, theirPublicKey:Bytes, mySecretKey:Bytes):Bytes {
|
|
return TweetNaCl.boxOpen(message, nonce, theirPublicKey, mySecretKey);
|
|
}
|
|
|
|
public static function boxKeyPair():{publicKey:Bytes, secretKey:Bytes} {
|
|
return TweetNaCl.boxKeyPair();
|
|
}
|
|
|
|
public static function signKeyPairFromSeed(seed:Bytes):{publicKey:Bytes, secretKey:Bytes} {
|
|
return TweetNaCl.signKeyPairFromSeed(seed);
|
|
}
|
|
|
|
public static var boxNonceLength(get, null):Int;
|
|
static function get_boxNonceLength():Int {
|
|
return TweetNaCl.crypto_box_NONCEBYTES;
|
|
}
|
|
|
|
public static function ripemd160(data:Bytes):Bytes {
|
|
return Ripemd160.make(data);
|
|
}
|
|
|
|
public static function encodeSeed(material:Bytes):String {
|
|
var prefix = Bytes.ofHex(SEEDPREFIX);
|
|
var payload = Bytes.alloc(prefix.length + material.length);
|
|
payload.blit(0, prefix, 0, prefix.length);
|
|
payload.blit(prefix.length, material, 0, material.length);
|
|
return Base58Check.encode(payload);
|
|
}
|
|
|
|
public static function encodeAddress(publicKey:Bytes):String {
|
|
var prefix = Bytes.ofHex(ADDRESSPREFIX);
|
|
var hash = hash(publicKey);
|
|
var ripemd = ripemd160(hash);
|
|
var payload = Bytes.alloc(prefix.length + ripemd.length);
|
|
payload.blit(0, prefix, 0, prefix.length);
|
|
payload.blit(prefix.length, ripemd, 0, ripemd.length);
|
|
return Base58Check.encode(payload);
|
|
}
|
|
|
|
public static function toHex(bytes:Bytes):String {
|
|
return bytes.toHex();
|
|
}
|
|
}
|