- Python
- HTML + JS
- C
THIS IS FOR EDUCARIONAL USE ONLY. I WILL NOT BE HELD RESPONSIBLE FOR ANY UNSOLICITED USE OF THIS CODE
#include <stdio.h> | |
#include <string.h> | |
#include <openssl/bn.h> | |
#include <openssl/ec.h> | |
#include <openssl/sha.h> | |
#include <assert.h> | |
#define FIELD_BITS 384 | |
#define FIELD_BYTES 48 | |
uint8_t cset[] = "BCDFGHJKMPQRTVWXY2346789"; | |
static void unpack(uint32_t *pid, uint32_t *hash, uint32_t *sig, uint32_t *raw) | |
{ | |
// pid = Bit 0..30 | |
pid[0] = raw[0] & 0x7fffffff; | |
// hash(s) = Bit 31..58 | |
hash[0] = ((raw[0] >> 31) | (raw[1] << 1)) & 0xfffffff; | |
// sig(e) = bit 58..113 | |
sig[0] = (raw[1] >> 27) | (raw[2] << 5); | |
sig[1] = (raw[2] >> 27) | (raw[3] << 5); | |
} | |
static void pack(uint32_t *raw, uint32_t *pid, uint32_t *hash, uint32_t *sig) | |
{ | |
raw[0] = pid[0] | ((hash[0] & 1) << 31); | |
raw[1] = (hash[0] >> 1) | ((sig[0] & 0x1f) << 27); | |
raw[2] = (sig[0] >> 5) | (sig[1] << 27); | |
raw[3] = sig[1] >> 5; | |
} | |
// Reverse data | |
static void endian(uint8_t *data, int len) | |
{ | |
int i; | |
for (i = 0; i < len/2; i++) { | |
uint8_t temp; | |
temp = data[i]; | |
data[i] = data[len-i-1]; | |
data[len-i-1] = temp; | |
} | |
} | |
void unbase24(uint32_t *x, uint8_t *c) | |
{ | |
memset(x, 0, 16); | |
int i, n; | |
BIGNUM *y = BN_new(); | |
BN_zero(y); | |
for (i = 0; i < 25; i++) | |
{ | |
BN_mul_word(y, 24); | |
BN_add_word(y, c[i]); | |
} | |
n = BN_num_bytes(y); | |
BN_bn2bin(y, (uint8_t *)x); | |
BN_free(y); | |
endian((uint8_t *)x, n); | |
} | |
void base24(uint8_t *c, uint32_t *x) | |
{ | |
uint8_t y[16]; | |
int i; | |
BIGNUM *z; | |
// Convert x to BigNum z | |
memcpy(y, x, sizeof(y)); // Copy X to Y; Y=X | |
for (i = 15; y[i] == 0; i--) {} i++; // skip following nulls | |
endian(y, i); // Reverse y | |
z = BN_bin2bn(y, i, NULL); // Convert y to BigNum z | |
// Divide z by 24 and convert remainder with cset to Base24-CDKEY Char | |
c[25] = 0; | |
for (i = 24; i >= 0; i--) { | |
uint8_t t = BN_div_word(z, 24); | |
c[i] = cset[t]; | |
} | |
BN_free(z); | |
} | |
void print_product_id(uint32_t *pid) | |
{ | |
char raw[12]; | |
char b[6], c[8]; | |
int i, digit = 0; | |
// Cut a away last bit of pid and convert it to an accii-number (=raw) | |
sprintf(raw, "%d", pid[0] >> 1); | |
// Make b-part {640-....} | |
strncpy(b, raw, 3); | |
b[3] = 0; | |
// Make c-part {...-123456X...} | |
strcpy(c, raw + 3); | |
// Make checksum digit-part {...56X-} | |
assert(strlen(c) == 6); | |
for (i = 0; i < 6; i++) | |
digit -= c[i] - '0'; // Sum digits | |
while (digit < 0) | |
digit += 7; | |
c[6] = digit + '0'; | |
c[7] = 0; | |
printf("Product ID: 55274-%s-%s-23xxx\n", b, c); | |
} | |
void print_product_key(uint8_t *pk) | |
{ | |
int i; | |
assert(strlen((const char *)pk) == 25); | |
for (i = 0; i < 25; i++) { | |
putchar(pk[i]); | |
if (i != 24 && i % 5 == 4) putchar('-'); | |
} | |
} | |
void verify(EC_GROUP *ec, EC_POINT *generator, EC_POINT *public_key, char *cdkey) | |
{ | |
uint8_t key[25]; | |
int i, j, k; | |
BN_CTX *ctx = BN_CTX_new(); | |
// remove Dashs from CDKEY | |
for (i = 0, k = 0; i < strlen(cdkey); i++) { | |
for (j = 0; j < 24; j++) { | |
if (cdkey[i] != '-' && cdkey[i] == cset[j]) { | |
key[k++] = j; | |
break; | |
} | |
assert(j < 24); | |
} | |
if (k >= 25) break; | |
} | |
// Base24_CDKEY -> Bin_CDKEY | |
uint32_t bkey[4] = {0}; | |
uint32_t pid[1], hash[1], sig[2]; | |
unbase24(bkey, key); | |
// Output Bin_CDKEY | |
printf("%.8x %.8x %.8x %.8x\n", bkey[3], bkey[2], bkey[1], bkey[0]); | |
// Divide/Extract pid_data, hash, sig from Bin_CDKEY | |
unpack(pid, hash, sig, bkey); | |
print_product_id(pid); | |
printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[0], hash[0], sig[1], sig[0]); | |
BIGNUM *e, *s; | |
/* e = hash, s = sig */ | |
e = BN_new(); | |
BN_set_word(e, hash[0]); | |
endian((uint8_t *)sig, sizeof(sig)); | |
s = BN_bin2bn((uint8_t *)sig, sizeof(sig), NULL); | |
BIGNUM *x = BN_new(); | |
BIGNUM *y = BN_new(); | |
EC_POINT *u = EC_POINT_new(ec); | |
EC_POINT *v = EC_POINT_new(ec); | |
/* v = s*generator + e*(-public_key) */ | |
EC_POINT_mul(ec, u, NULL, generator, s, ctx); | |
EC_POINT_mul(ec, v, NULL, public_key, e, ctx); | |
EC_POINT_add(ec, v, u, v, ctx); | |
EC_POINT_get_affine_coordinates_GFp(ec, v, x, y, ctx); | |
uint8_t buf[FIELD_BYTES], md[20]; | |
uint32_t h; | |
uint8_t t[4]; | |
SHA_CTX h_ctx; | |
/* h = (fist 32 bits of SHA1(pid || v.x, v.y)) >> 4 */ | |
SHA1_Init(&h_ctx); | |
t[0] = pid[0] & 0xff; | |
t[1] = (pid[0] & 0xff00) >> 8; | |
t[2] = (pid[0] & 0xff0000) >> 16; | |
t[3] = (pid[0] & 0xff000000) >> 24; | |
SHA1_Update(&h_ctx, t, sizeof(t)); | |
memset(buf, 0, sizeof(buf)); | |
BN_bn2bin(x, buf); | |
endian((uint8_t *)buf, sizeof(buf)); | |
SHA1_Update(&h_ctx, buf, sizeof(buf)); | |
memset(buf, 0, sizeof(buf)); | |
BN_bn2bin(y, buf); | |
endian((uint8_t *)buf, sizeof(buf)); | |
SHA1_Update(&h_ctx, buf, sizeof(buf)); | |
SHA1_Final(md, &h_ctx); | |
h = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4; | |
h &= 0xfffffff; | |
printf("Calculated hash: %.8x\n", h); | |
if (h == hash[0]) printf("Key valid\n"); | |
else printf("Key invalid\n"); | |
putchar('\n'); | |
BN_free(e); | |
BN_free(s); | |
BN_free(x); | |
BN_free(y); | |
EC_POINT_free(u); | |
EC_POINT_free(v); | |
BN_CTX_free(ctx); | |
} | |
void generate(uint8_t *pkey, EC_GROUP *ec, EC_POINT *generator, BIGNUM *order, BIGNUM *priv, uint32_t *pid) | |
{ | |
BN_CTX *ctx = BN_CTX_new(); | |
BIGNUM *k = BN_new(); | |
BIGNUM *s = BN_new(); | |
BIGNUM *x = BN_new(); | |
BIGNUM *y = BN_new(); | |
EC_POINT *r = EC_POINT_new(ec); | |
uint32_t bkey[4]; | |
// Loop in case signaturepart will make cdkey(base-24 "digits") longer than 25 | |
do { | |
BN_pseudo_rand(k, FIELD_BITS, -1, 0); | |
EC_POINT_mul(ec, r, NULL, generator, k, ctx); | |
EC_POINT_get_affine_coordinates_GFp(ec, r, x, y, ctx); | |
SHA_CTX h_ctx; | |
uint8_t t[4], md[20], buf[FIELD_BYTES]; | |
uint32_t hash[1]; | |
/* h = (fist 32 bits of SHA1(pid || r.x, r.y)) >> 4 */ | |
SHA1_Init(&h_ctx); | |
t[0] = pid[0] & 0xff; | |
t[1] = (pid[0] & 0xff00) >> 8; | |
t[2] = (pid[0] & 0xff0000) >> 16; | |
t[3] = (pid[0] & 0xff000000) >> 24; | |
SHA1_Update(&h_ctx, t, sizeof(t)); | |
memset(buf, 0, sizeof(buf)); | |
BN_bn2bin(x, buf); | |
endian((uint8_t *)buf, sizeof(buf)); | |
SHA1_Update(&h_ctx, buf, sizeof(buf)); | |
memset(buf, 0, sizeof(buf)); | |
BN_bn2bin(y, buf); | |
endian((uint8_t *)buf, sizeof(buf)); | |
SHA1_Update(&h_ctx, buf, sizeof(buf)); | |
SHA1_Final(md, &h_ctx); | |
hash[0] = (md[0] | (md[1] << 8) | (md[2] << 16) | (md[3] << 24)) >> 4; | |
hash[0] &= 0xfffffff; | |
/* s = priv*h + k */ | |
BN_copy(s, priv); | |
BN_mul_word(s, hash[0]); | |
BN_mod_add(s, s, k, order, ctx); | |
uint32_t sig[2] = {0}; | |
BN_bn2bin(s, (uint8_t *)sig); | |
endian((uint8_t *)sig, BN_num_bytes(s)); | |
pack(bkey, pid, hash, sig); | |
printf("PID: %.8x\nHash: %.8x\nSig: %.8x %.8x\n", pid[0], hash[0], sig[1], sig[0]); | |
} while (bkey[3] >= 0x62a32); | |
base24(pkey, bkey); | |
BN_free(k); | |
BN_free(s); | |
BN_free(x); | |
BN_free(y); | |
EC_POINT_free(r); | |
BN_CTX_free(ctx); | |
} | |
int main() | |
{ | |
// Init | |
BIGNUM *a, *b, *p, *gx, *gy, *pubx, *puby, *n, *priv; | |
BN_CTX *ctx = BN_CTX_new(); | |
// make BigNumbers | |
a = BN_new(); | |
b = BN_new(); | |
p = BN_new(); | |
gx = BN_new(); | |
gy = BN_new(); | |
pubx = BN_new(); | |
puby = BN_new(); | |
n = BN_new(); | |
priv = BN_new(); | |
// Data from pidgen-Bink-resources | |
/* Elliptic curve parameters: y^2 = x^3 + ax + b mod p */ | |
BN_hex2bn(&p, "92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9"); | |
BN_set_word(a, 1); | |
BN_set_word(b, 0); | |
/* base point (generator) G */ | |
BN_hex2bn(&gx, "46E3775ECE21B0898D39BEA57050D422A0AF989E497962BAEE2CB17E0A28D5360D5476B8DC966443E37A14F1AEF37742"); | |
BN_hex2bn(&gy, "7C8E741D2C34F4478E325469CD491603D807222C9C4AC09DDB2B31B3CE3F7CC191B3580079932BC6BEF70BE27604F65E"); | |
/* inverse of public key */ | |
BN_hex2bn(&pubx, "5D8DBE75198015EC41C45AAB6143542EB098F6A5CC9CE4178A1B8A1E7ABBB5BC64DF64FAF6177DC1B0988AB00BA94BF8"); | |
BN_hex2bn(&puby, "23A2909A0B4803C89F910C7191758B48746CEA4D5FF07667444ACDB9512080DBCA55E6EBF30433672B894F44ACE92BFA"); | |
// Computed data | |
/* order of G - computed in 18 hours using a P3-450 */ | |
BN_hex2bn(&n, "DB6B4C58EFBAFD"); | |
/* THE private key - computed in 10 hours using a P3-450 */ | |
BN_hex2bn(&priv, "565B0DFF8496C8"); | |
// Calculation | |
EC_GROUP *ec = EC_GROUP_new_curve_GFp(p, a, b, ctx); | |
EC_POINT *g = EC_POINT_new(ec); | |
EC_POINT_set_affine_coordinates_GFp(ec, g, gx, gy, ctx); | |
EC_POINT *pub = EC_POINT_new(ec); | |
EC_POINT_set_affine_coordinates_GFp(ec, pub, pubx, puby, ctx); | |
uint8_t pkey[26]; | |
uint32_t pid[1]; | |
pid[0] = 640000000 << 1; /* <- change */ | |
// generate a key | |
generate(pkey, ec, g, n, priv, pid); | |
print_product_key(pkey); printf("\n\n"); | |
// verify the key | |
verify(ec, g, pub, (char*)pkey); | |
// Cleanup | |
BN_CTX_free(ctx); | |
return 0; | |
} |
<html> | |
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/jsbn.js"> </script> | |
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/jsbn2.js"> </script> | |
<script language="JavaScript" type="text/javascript" src="https://techguy16.github.io/xpkeygen-js/sha1.js"> </script> | |
<script language="JavaScript" type="text/javascript"> | |
function addmod(x, a, b, m) { | |
a.addTo(b, x); | |
while (x.compareTo(m) >= 0) { x.subTo(m, x); } | |
} | |
function dblmod(x, a, m) { | |
a.lShiftTo(1, x); | |
while (x.compareTo(m) >= 0) { x.subTo(m, x); } | |
} | |
function submod(x, a, b, m) { | |
a.subTo(b, x); | |
while (x.compareTo(BigInteger.ZERO) < 0) { x.addTo(m, x); } | |
} | |
function mulmod(x, a, b, barrett) { | |
barrett.mulTo(a, b, x); | |
} | |
function sqrmod(x, a, barrett) { | |
barrett.sqrTo(a, x); | |
} | |
function Point(x, y, inf) { | |
this.x = x; | |
this.y = y; | |
this.inf = inf; | |
} | |
function Ecc(a, b, p) { | |
this.barrett = new Barrett(p); | |
this.p = p; | |
this.a = a; | |
this.b = b; | |
} | |
function pinf() { return new Point(0, 0, true); } | |
Ecc.prototype.add = function(p1, p2) { | |
if (p1.inf) { return p2; } | |
if (p2.inf) { return p1; } | |
var t1 = nbi(); | |
var t2 = nbi(); | |
var l = nbi(); | |
if (p1.x.compareTo(p2.x) != 0) { | |
submod(t1, p1.y, p2.y, this.p); | |
submod(t2, p1.x, p2.x, this.p); | |
} else { | |
if (p1.y.compareTo(p2.y) != 0 || p2.y.compareTo(BigInteger.ZERO) == 0) { return Ecc.INF; } | |
sqrmod(t1, p2.x, this.barrett); | |
dblmod(t2, t1, this.p); | |
addmod(t1, t2, t1, this.p); | |
addmod(t1, t1, this.a, this.p); | |
dblmod(t2, p2.y, this.p); | |
} | |
mulmod(l, t1, t2.modInverse(this.p), this.barrett); | |
var x3 = nbi(); | |
var y3 = nbi(); | |
sqrmod(x3, l, this.barrett); | |
submod(x3, x3, p1.x, this.p); | |
submod(x3, x3, p2.x, this.p); | |
submod(y3, p2.x, x3, this.p); | |
mulmod(t1, y3, l, this.barrett); | |
submod(y3, t1, p2.y, this.p); | |
return new Point(x3, y3, false); | |
} | |
Ecc.prototype.neg = function(p) { | |
var ny = nbi(); | |
this.p.subTo(p.y, ny); | |
return new Point(p.x, ny, false); | |
} | |
Ecc.prototype.mul = function(p1, n) { | |
var p = pinf(); | |
var i; | |
var n3 = nbi(); | |
n.addTo(n, n3); | |
n.addTo(n3, n3); | |
var np1 = this.neg(p1); | |
for (i = n3.bitLength() - 1; i >= 1; i--) { | |
p = this.add(p, p); | |
if (n3.testBit(i) && !n.testBit(i)) { | |
p = this.add(p, p1); | |
} else if (!n3.testBit(i) && n.testBit(i)) { | |
p = this.add(p, np1); | |
} | |
} | |
return p; | |
} | |
var cset = "BCDFGHJKMPQRTVWXY2346789"; | |
var hexc = "0123456789abcdef"; | |
var xpecc = new Ecc( | |
new BigInteger("1", 16), | |
new BigInteger("0", 16), | |
new BigInteger("92ddcf14cb9e71f4489a2e9ba350ae29454d98cb93bdbcc07d62b502ea12238ee904a8b20d017197aae0c103b32713a9", 16) | |
); | |
var g = new Point( | |
new BigInteger("46e3775ece21b0898d39bea57050d422a0af989e497962baee2cb17e0a28d5360d5476b8dc966443e37a14f1aef37742", 16), | |
new BigInteger("7c8e741d2c34f4478e325469cd491603d807222c9c4ac09ddb2b31b3ce3f7cc191b3580079932bc6bef70be27604f65e", 16), | |
false | |
); | |
// order of g | |
var order = new BigInteger("db6b4c58efbafd", 16); | |
// pirvate key | |
var priv = new BigInteger("565b0dff8496c8", 16); | |
function hexToByte(s) { | |
t = ""; | |
var i, j; | |
if (s.length % 2 != 0) { s = "0" + s; } | |
for (i = 0; i < s.length; i += 2) { | |
t += String.fromCharCode(parseInt(s.substr(i, 2), 16)); | |
} | |
return t; | |
} | |
function reverse(s) { | |
t = ""; | |
var i; | |
for (i = s.length - 1; i >= 0; i--) { | |
t += s.charAt(i); | |
} | |
return t; | |
} | |
function random(n) { | |
t = ""; | |
var i; | |
for (i = 0; i < 2*n; i++) { | |
t += hexc.charAt(Math.floor(Math.random() * 16)); | |
} | |
k = nbi(); | |
k.fromString(t, 16); | |
return k; | |
} | |
function generate() { | |
pid = 640000000 << 1; | |
maxkey = new BigInteger("62A32B15517FFFFFFFFFFFFFFFFFF", 16); // 24^25-1 | |
do { | |
// calculate the Schnorr signature of pid (http://en.wikipedia.org/wiki/Schnorr_signature) | |
var k = random(7); | |
//log("k: " + k.toString(16)); | |
var r = xpecc.mul(g, k); | |
var x = reverse(hexToByte(r.x.toString(16))); | |
var y = reverse(hexToByte(r.y.toString(16))); | |
while (x.length < 48) { x = x + '�'; } | |
while (y.length < 48) { y = y + '�'; } | |
var h = calcSHA1( | |
String.fromCharCode(pid & 0xff) + | |
String.fromCharCode((pid >> 8) & 0xff) + | |
String.fromCharCode((pid >> 16) & 0xff) + | |
String.fromCharCode((pid >> 24) & 0xff) + | |
x + y | |
); | |
h = hexToByte(h.substr(0, 8)); | |
h = (h.charCodeAt(0) + (h.charCodeAt(1) << 8) + (h.charCodeAt(2) << 16) + (h.charCodeAt(3) << 24)) >>> 4; | |
h = new BigInteger(h.toString(16), 16); | |
var s = nbi(); | |
priv.multiplyTo(h, s); | |
s = s.mod(order); | |
// private key is inverted, add instead of subtract | |
s.addTo(k, s); | |
while (s.compareTo(order) >= 0) { s.subTo(order, s); } | |
// key = s (56) || h (28) || pid (31) | |
var key = new BigInteger(pid.toString(16), 16); | |
key.addTo(h.shiftLeft(31), key); | |
key.addTo(s.shiftLeft(59), key); | |
} while (key.compareTo(maxkey) > 0); | |
var skey = ""; | |
var t; | |
var i; | |
var base = new BigInteger("24", 10); | |
// skey = base 24 of key | |
for (i = 0; i < 25; i++) { | |
t = key.divideAndRemainder(base); | |
key = t[0]; | |
t = t[1]; | |
skey = cset.charAt(parseInt(t.toString(16), 16)) + skey; | |
} | |
t = ""; | |
for (i = 0; i < 25; i++) { | |
t += skey.charAt(i); | |
if (i != 24 && i % 5 == 4) { t += "-"; } | |
} | |
return t; | |
} | |
</script> | |
<body onload='do_generate()'> | |
<input type="text" size="30" id="key" /> | |
<script type="text/javascript"> | |
function log(m) { | |
document.getElementById("msg").innerHTML += m + "<br>" | |
} | |
function do_generate() { | |
var key = document.getElementById("key"); | |
key.value = generate(); | |
} | |
</script> | |
</body> | |
</html> |
import hashlib | |
import random | |
import secrets | |
# JSON object for BINK data | |
key_data = { | |
"p": 22604814143135632990679956684344311209819952803216271952472204855524756275151440456421260165232069708317717961315241, | |
"a": 1, | |
"b": 0, | |
"g": [ | |
10910744922206512781156913169071750153028386884676208947062808346072531411270489432930252839559606812441712224597826, | |
19170993669917204517491618000619818679152109690172641868349612889930480365274675096509477191800826190959228181870174 | |
], | |
"pub": [ | |
14399230353963643339712940015954061581064239835926823517419716769613937039346822269422480779920783799484349086780408, | |
5484731395987446993229594927733430043632089703338918322171291299699820472711849119800714736923107362018017833200634 | |
], | |
"n": 61760995553426173, | |
"priv": 37454031876727861 | |
} | |
p = key_data["p"] | |
a = key_data["a"] | |
b = key_data["b"] | |
B = tuple(key_data["g"]) | |
K = tuple(key_data["pub"]) | |
order = key_data["n"] | |
private_key = -key_data["priv"] % order | |
# PID of product key | |
pid = 756_696969 | |
# Key alphabet | |
KCHARS = "BCDFGHJKMPQRTVWXY2346789" | |
def int_to_bytes(n, l=None): | |
n = int(n) | |
if not l: | |
l = (n.bit_length() + 7) // 8 | |
return n.to_bytes(l, byteorder="little") | |
def encode_pkey(n): | |
out = "" | |
while n > 0: | |
out = KCHARS[n % 24] + out | |
n //= 24 | |
out = "-".join([out[i:i+5] for i in range(0, len(out), 5)]) | |
return out | |
def inverse_mod(k, p): | |
return pow(k, -1, p) | |
def add_points(P, Q, p, a): | |
if P is None: | |
return Q | |
if Q is None: | |
return P | |
if P[0] == Q[0] and (P[1] + Q[1]) % p == 0: | |
return None | |
if P != Q: | |
lam = ((Q[1] - P[1]) * inverse_mod(Q[0] - P[0], p)) % p | |
else: | |
lam = ((3 * P[0] * P[0] + a) * inverse_mod(2 * P[1], p)) % p | |
x = (lam * lam - P[0] - Q[0]) % p | |
y = (lam * (P[0] - x) - P[1]) % p | |
return (x, y) | |
def scalar_mult(k, P, p, a): | |
R = None | |
for i in range(384): | |
if k & (1 << i): | |
R = add_points(R, P, p, a) | |
P = add_points(P, P, p, a) | |
return R | |
pid <<= 1 | |
while True: | |
k = secrets.randbelow(p - 1) + 1 # Generate a random k in the range [1, p-1] | |
r = scalar_mult(k, B, p, a) | |
x, y = r | |
md = hashlib.sha1(int_to_bytes(pid, 4) + int_to_bytes(x, 48) + int_to_bytes(y, 48)).digest() | |
h = int.from_bytes(md[:4], byteorder="little") >> 4 | |
h &= 0xfffffff | |
s = int(abs((private_key * h + k) % order)) | |
raw_pkey = s << 59 | h << 31 | pid | |
if raw_pkey >> 96 < 0x40000: | |
break | |
print(encode_pkey(raw_pkey)) |