| line | stmt | bran | cond | sub | pod | time | code | 
| 1 |  |  |  |  |  |  | MODULE = CryptX         PACKAGE = Crypt::PK::RSA | 
| 2 |  |  |  |  |  |  |  | 
| 3 |  |  |  |  |  |  | PROTOTYPES: DISABLE | 
| 4 |  |  |  |  |  |  |  | 
| 5 |  |  |  |  |  |  | Crypt::PK::RSA | 
| 6 |  |  |  |  |  |  | _new(Class) | 
| 7 |  |  |  |  |  |  | CODE: | 
| 8 |  |  |  |  |  |  | { | 
| 9 |  |  |  |  |  |  | int rv; | 
| 10 | 107 |  |  |  |  |  | Newz(0, RETVAL, 1, struct rsa_struct); | 
| 11 | 107 | 50 |  |  |  |  | if (!RETVAL) croak("FATAL: Newz failed"); | 
| 12 | 107 |  |  |  |  |  | RETVAL->key.type = -1; | 
| 13 | 107 |  |  |  |  |  | RETVAL->pindex = find_prng("chacha20"); | 
| 14 | 107 | 50 |  |  |  |  | if (RETVAL->pindex == -1) { | 
| 15 | 0 |  |  |  |  |  | Safefree(RETVAL); | 
| 16 | 0 |  |  |  |  |  | croak("FATAL: find_prng('chacha20') failed"); | 
| 17 |  |  |  |  |  |  | } | 
| 18 | 107 |  |  |  |  |  | rv = rng_make_prng(320, RETVAL->pindex, &RETVAL->pstate, NULL); /* 320bits = 40bytes */ | 
| 19 | 107 | 50 |  |  |  |  | if (rv != CRYPT_OK) { | 
| 20 | 0 |  |  |  |  |  | Safefree(RETVAL); | 
| 21 | 0 |  |  |  |  |  | croak("FATAL: rng_make_prng failed: %s", error_to_string(rv)); | 
| 22 |  |  |  |  |  |  | } | 
| 23 |  |  |  |  |  |  | } | 
| 24 |  |  |  |  |  |  | OUTPUT: | 
| 25 |  |  |  |  |  |  | RETVAL | 
| 26 |  |  |  |  |  |  |  | 
| 27 |  |  |  |  |  |  | void | 
| 28 |  |  |  |  |  |  | generate_key(Crypt::PK::RSA self, int key_size=256, long key_e=65537) | 
| 29 |  |  |  |  |  |  | PPCODE: | 
| 30 |  |  |  |  |  |  | { | 
| 31 |  |  |  |  |  |  | /* key_size is in octets */ | 
| 32 |  |  |  |  |  |  | int rv; | 
| 33 |  |  |  |  |  |  | /* gen the key */ | 
| 34 | 1 |  |  |  |  |  | rv = rsa_make_key(&self->pstate, self->pindex, key_size, key_e, &self->key); | 
| 35 | 1 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_make_key failed: %s", error_to_string(rv)); | 
| 36 | 1 | 50 |  |  |  |  | XPUSHs(ST(0)); /* return self */ | 
| 37 |  |  |  |  |  |  | } | 
| 38 |  |  |  |  |  |  |  | 
| 39 |  |  |  |  |  |  | void | 
| 40 |  |  |  |  |  |  | _import(Crypt::PK::RSA self, SV * key_data) | 
| 41 |  |  |  |  |  |  | PPCODE: | 
| 42 |  |  |  |  |  |  | { | 
| 43 |  |  |  |  |  |  | int rv; | 
| 44 | 128 |  |  |  |  |  | unsigned char *data=NULL; | 
| 45 | 128 |  |  |  |  |  | STRLEN data_len=0; | 
| 46 |  |  |  |  |  |  |  | 
| 47 | 128 | 50 |  |  |  |  | data = (unsigned char *)SvPVbyte(key_data, data_len); | 
| 48 | 128 | 100 |  |  |  |  | if (self->key.type != -1) { rsa_free(&self->key); self->key.type = -1; } | 
| 49 | 128 |  |  |  |  |  | rv = rsa_import(data, (unsigned long)data_len, &self->key); | 
| 50 | 128 | 100 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_import failed: %s", error_to_string(rv)); | 
| 51 | 125 | 50 |  |  |  |  | XPUSHs(ST(0)); /* return self */ | 
| 52 |  |  |  |  |  |  | } | 
| 53 |  |  |  |  |  |  |  | 
| 54 |  |  |  |  |  |  | void | 
| 55 |  |  |  |  |  |  | _import_pkcs8(Crypt::PK::RSA self, SV * key_data, SV * passwd) | 
| 56 |  |  |  |  |  |  | PPCODE: | 
| 57 |  |  |  |  |  |  | { | 
| 58 |  |  |  |  |  |  | int rv; | 
| 59 | 5 |  |  |  |  |  | unsigned char *data=NULL, *pwd=NULL; | 
| 60 | 5 |  |  |  |  |  | STRLEN data_len=0, pwd_len=0; | 
| 61 |  |  |  |  |  |  |  | 
| 62 | 5 | 50 |  |  |  |  | data = (unsigned char *)SvPVbyte(key_data, data_len); | 
| 63 | 5 | 100 |  |  |  |  | if (SvOK(passwd)) { | 
|  |  | 50 |  |  |  |  |  | 
|  |  | 50 |  |  |  |  |  | 
| 64 | 2 | 50 |  |  |  |  | pwd = (unsigned char *)SvPVbyte(passwd, pwd_len); | 
| 65 |  |  |  |  |  |  | } | 
| 66 | 5 | 100 |  |  |  |  | if (self->key.type != -1) { rsa_free(&self->key); self->key.type = -1; } | 
| 67 | 5 |  |  |  |  |  | rv = rsa_import_pkcs8(data, (unsigned long)data_len, pwd, (unsigned long)pwd_len, &self->key); | 
| 68 | 5 | 100 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_import_pkcs8 failed: %s", error_to_string(rv)); | 
| 69 | 4 | 50 |  |  |  |  | XPUSHs(ST(0)); /* return self */ | 
| 70 |  |  |  |  |  |  | } | 
| 71 |  |  |  |  |  |  |  | 
| 72 |  |  |  |  |  |  | void | 
| 73 |  |  |  |  |  |  | _import_x509(Crypt::PK::RSA self, SV * key_data) | 
| 74 |  |  |  |  |  |  | PPCODE: | 
| 75 |  |  |  |  |  |  | { | 
| 76 |  |  |  |  |  |  | int rv; | 
| 77 | 2 |  |  |  |  |  | unsigned char *data=NULL; | 
| 78 | 2 |  |  |  |  |  | STRLEN data_len=0; | 
| 79 |  |  |  |  |  |  |  | 
| 80 | 2 | 50 |  |  |  |  | data = (unsigned char *)SvPVbyte(key_data, data_len); | 
| 81 | 2 | 50 |  |  |  |  | if (self->key.type != -1) { rsa_free(&self->key); self->key.type = -1; } | 
| 82 | 2 |  |  |  |  |  | rv = rsa_import_x509(data, (unsigned long)data_len, &self->key); | 
| 83 | 2 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_import_x509 failed: %s", error_to_string(rv)); | 
| 84 | 2 | 50 |  |  |  |  | XPUSHs(ST(0)); /* return self */ | 
| 85 |  |  |  |  |  |  | } | 
| 86 |  |  |  |  |  |  |  | 
| 87 |  |  |  |  |  |  | void | 
| 88 |  |  |  |  |  |  | _import_hex(Crypt::PK::RSA self, char *N, char *e, char *d=NULL, char *p=NULL, char *q=NULL, char *dP=NULL, char *dQ=NULL, char *qP=NULL) | 
| 89 |  |  |  |  |  |  | PPCODE: | 
| 90 |  |  |  |  |  |  | { | 
| 91 |  |  |  |  |  |  | int rv; | 
| 92 |  |  |  |  |  |  | unsigned char Nbin[1024], ebin[128], dbin[1024], pbin[512], qbin[512], dPbin[512], dQbin[512], qPbin[512]; | 
| 93 | 13 |  |  |  |  |  | unsigned long Nlen=sizeof(Nbin), elen=sizeof(ebin), dlen=sizeof(dbin), plen=sizeof(pbin), | 
| 94 | 13 |  |  |  |  |  | qlen=sizeof(qbin), dPlen=sizeof(dPbin), dQlen=sizeof(dQbin), qPlen=sizeof(qPbin); | 
| 95 |  |  |  |  |  |  |  | 
| 96 | 13 |  |  |  |  |  | rv = radix_to_bin(N,  16,  Nbin,  &Nlen); | 
| 97 | 13 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(N) failed: %s", error_to_string(rv)); | 
| 98 | 13 |  |  |  |  |  | rv = radix_to_bin(e,  16,  ebin,  &elen); | 
| 99 | 13 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(e) failed: %s", error_to_string(rv)); | 
| 100 |  |  |  |  |  |  |  | 
| 101 | 13 | 50 |  |  |  |  | if (d && strlen(d) > 0) { | 
|  |  | 0 |  |  |  |  |  | 
| 102 |  |  |  |  |  |  | /* private */ | 
| 103 | 0 |  |  |  |  |  | rv = radix_to_bin(d,  16,  dbin,  &dlen); | 
| 104 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(d) failed: %s", error_to_string(rv)); | 
| 105 | 0 |  |  |  |  |  | rv = rsa_set_key(Nbin, Nlen, ebin, elen, dbin, dlen, &self->key); | 
| 106 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_set_key failed: %s", error_to_string(rv)); | 
| 107 |  |  |  |  |  |  | } | 
| 108 |  |  |  |  |  |  | else { | 
| 109 |  |  |  |  |  |  | /* public */ | 
| 110 | 13 |  |  |  |  |  | rv = rsa_set_key(Nbin, Nlen, ebin, elen, NULL, 0, &self->key); | 
| 111 | 13 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_set_key failed: %s", error_to_string(rv)); | 
| 112 |  |  |  |  |  |  | } | 
| 113 |  |  |  |  |  |  |  | 
| 114 | 13 | 50 |  |  |  |  | if (p && strlen(p) > 0 && q && strlen(q) > 0) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 115 |  |  |  |  |  |  | /* private only */ | 
| 116 | 0 |  |  |  |  |  | rv = radix_to_bin(p,  16,  pbin,  &plen); | 
| 117 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(p) failed: %s", error_to_string(rv)); | 
| 118 | 0 |  |  |  |  |  | rv = radix_to_bin(q,  16,  qbin,  &qlen); | 
| 119 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(q) failed: %s", error_to_string(rv)); | 
| 120 | 0 |  |  |  |  |  | rv = rsa_set_factors(pbin, plen, qbin, qlen, &self->key); | 
| 121 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_set_factors failed: %s", error_to_string(rv)); | 
| 122 |  |  |  |  |  |  | } | 
| 123 |  |  |  |  |  |  |  | 
| 124 | 13 | 50 |  |  |  |  | if (dP && strlen(dP) > 0 && dQ && strlen(dQ) > 0 && qP && strlen(qP) > 0) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 125 |  |  |  |  |  |  | /* private only */ | 
| 126 | 0 |  |  |  |  |  | rv = radix_to_bin(dP, 16, dPbin, &dPlen); | 
| 127 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(dP) failed: %s", error_to_string(rv)); | 
| 128 | 0 |  |  |  |  |  | rv = radix_to_bin(dQ, 16, dQbin, &dQlen); | 
| 129 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(dQ) failed: %s", error_to_string(rv)); | 
| 130 | 0 |  |  |  |  |  | rv = radix_to_bin(qP, 16, qPbin, &qPlen); | 
| 131 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: radix_to_bin(qP) failed: %s", error_to_string(rv)); | 
| 132 | 0 |  |  |  |  |  | rv = rsa_set_crt_params(dPbin, dPlen, dQbin, dQlen, qPbin, qPlen, &self->key); | 
| 133 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_set_crt_params failed: %s", error_to_string(rv)); | 
| 134 |  |  |  |  |  |  | } | 
| 135 |  |  |  |  |  |  |  | 
| 136 | 13 | 50 |  |  |  |  | XPUSHs(ST(0)); /* return self */ | 
| 137 |  |  |  |  |  |  | } | 
| 138 |  |  |  |  |  |  |  | 
| 139 |  |  |  |  |  |  | int | 
| 140 |  |  |  |  |  |  | is_private(Crypt::PK::RSA self) | 
| 141 |  |  |  |  |  |  | CODE: | 
| 142 | 70 | 50 |  |  |  |  | if (self->key.type == -1 || self->key.N == NULL) XSRETURN_UNDEF; | 
|  |  | 50 |  |  |  |  |  | 
| 143 | 70 |  |  |  |  |  | RETVAL = (self->key.type == PK_PRIVATE) ? 1 : 0; | 
| 144 |  |  |  |  |  |  | OUTPUT: | 
| 145 |  |  |  |  |  |  | RETVAL | 
| 146 |  |  |  |  |  |  |  | 
| 147 |  |  |  |  |  |  | int | 
| 148 |  |  |  |  |  |  | size(Crypt::PK::RSA self) | 
| 149 |  |  |  |  |  |  | CODE: | 
| 150 | 1 | 50 |  |  |  |  | if (self->key.type == -1 || self->key.N == NULL) XSRETURN_UNDEF; | 
|  |  | 50 |  |  |  |  |  | 
| 151 | 1 |  |  |  |  |  | RETVAL = mp_unsigned_bin_size(self->key.N); | 
| 152 |  |  |  |  |  |  | OUTPUT: | 
| 153 |  |  |  |  |  |  | RETVAL | 
| 154 |  |  |  |  |  |  |  | 
| 155 |  |  |  |  |  |  | SV* | 
| 156 |  |  |  |  |  |  | key2hash(Crypt::PK::RSA self) | 
| 157 |  |  |  |  |  |  | PREINIT: | 
| 158 |  |  |  |  |  |  | HV *rv_hash; | 
| 159 |  |  |  |  |  |  | long siz, nsize; | 
| 160 |  |  |  |  |  |  | char buf[20001]; | 
| 161 |  |  |  |  |  |  | SV **not_used; | 
| 162 |  |  |  |  |  |  | CODE: | 
| 163 | 109 | 50 |  |  |  |  | if (self->key.type == -1 || self->key.N == NULL) XSRETURN_UNDEF; | 
|  |  | 50 |  |  |  |  |  | 
| 164 | 109 |  |  |  |  |  | nsize = mp_unsigned_bin_size(self->key.N); | 
| 165 | 109 |  |  |  |  |  | rv_hash = newHV(); | 
| 166 |  |  |  |  |  |  | /* e */ | 
| 167 | 109 | 50 |  |  |  |  | siz = (self->key.e) ? mp_unsigned_bin_size(self->key.e) : 0; | 
| 168 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 169 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'e' too big number"); | 
| 170 |  |  |  |  |  |  | } | 
| 171 | 109 | 50 |  |  |  |  | if (siz>0) { | 
| 172 | 109 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.e, buf, 20000, 0); | 
| 173 | 109 |  |  |  |  |  | not_used = hv_store(rv_hash, "e", 1, newSVpv(buf, strlen(buf)), 0); | 
| 174 |  |  |  |  |  |  | } | 
| 175 |  |  |  |  |  |  | else{ | 
| 176 | 0 |  |  |  |  |  | not_used = hv_store(rv_hash, "e", 1, newSVpv("", 0), 0); | 
| 177 |  |  |  |  |  |  | } | 
| 178 |  |  |  |  |  |  | /* d */ | 
| 179 | 109 | 50 |  |  |  |  | siz = (self->key.d) ? mp_unsigned_bin_size(self->key.d) : 0; | 
| 180 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 181 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'd' too big number"); | 
| 182 |  |  |  |  |  |  | } | 
| 183 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 184 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.d, buf, 20000, 0); | 
| 185 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "d", 1, newSVpv(buf, strlen(buf)), 0); | 
| 186 |  |  |  |  |  |  | } | 
| 187 |  |  |  |  |  |  | else{ | 
| 188 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "d", 1, newSVpv("", 0), 0); | 
| 189 |  |  |  |  |  |  | } | 
| 190 |  |  |  |  |  |  | /* N */ | 
| 191 | 109 | 50 |  |  |  |  | siz = (self->key.N) ? nsize : 0; | 
| 192 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 193 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'N' too big number"); | 
| 194 |  |  |  |  |  |  | } | 
| 195 | 109 | 50 |  |  |  |  | if (siz>0) { | 
| 196 | 109 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.N, buf, 20000, 0); | 
| 197 | 109 |  |  |  |  |  | not_used = hv_store(rv_hash, "N", 1, newSVpv(buf, strlen(buf)), 0); | 
| 198 |  |  |  |  |  |  | } | 
| 199 |  |  |  |  |  |  | else{ | 
| 200 | 0 |  |  |  |  |  | not_used = hv_store(rv_hash, "N", 1, newSVpv("", 0), 0); | 
| 201 |  |  |  |  |  |  | } | 
| 202 |  |  |  |  |  |  | /* q */ | 
| 203 | 109 | 50 |  |  |  |  | siz = (self->key.q) ? mp_unsigned_bin_size(self->key.q) : 0; | 
| 204 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 205 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'q' too big number"); | 
| 206 |  |  |  |  |  |  | } | 
| 207 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 208 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.q, buf, 20000, 0); | 
| 209 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "q", 1, newSVpv(buf, strlen(buf)), 0); | 
| 210 |  |  |  |  |  |  | } | 
| 211 |  |  |  |  |  |  | else{ | 
| 212 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "q", 1, newSVpv("", 0), 0); | 
| 213 |  |  |  |  |  |  | } | 
| 214 |  |  |  |  |  |  | /* p */ | 
| 215 | 109 | 50 |  |  |  |  | siz = (self->key.p) ? mp_unsigned_bin_size(self->key.p) : 0; | 
| 216 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 217 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'p' too big number"); | 
| 218 |  |  |  |  |  |  | } | 
| 219 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 220 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.p, buf, 20000, 0); | 
| 221 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "p", 1, newSVpv(buf, strlen(buf)), 0); | 
| 222 |  |  |  |  |  |  | } | 
| 223 |  |  |  |  |  |  | else{ | 
| 224 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "p", 1, newSVpv("", 0), 0); | 
| 225 |  |  |  |  |  |  | } | 
| 226 |  |  |  |  |  |  | /* qP */ | 
| 227 | 109 | 50 |  |  |  |  | siz = (self->key.qP) ? mp_unsigned_bin_size(self->key.qP) : 0; | 
| 228 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 229 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'qP' too big number"); | 
| 230 |  |  |  |  |  |  | } | 
| 231 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 232 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.qP, buf, 20000, 0); | 
| 233 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "qP", 2, newSVpv(buf, strlen(buf)), 0); | 
| 234 |  |  |  |  |  |  | } | 
| 235 |  |  |  |  |  |  | else{ | 
| 236 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "qP", 2, newSVpv("", 0), 0); | 
| 237 |  |  |  |  |  |  | } | 
| 238 |  |  |  |  |  |  | /* dP */ | 
| 239 | 109 | 50 |  |  |  |  | siz = (self->key.dP) ? mp_unsigned_bin_size(self->key.dP) : 0; | 
| 240 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 241 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'dP' too big number"); | 
| 242 |  |  |  |  |  |  | } | 
| 243 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 244 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.dP, buf, 20000, 0); | 
| 245 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "dP", 2, newSVpv(buf, strlen(buf)), 0); | 
| 246 |  |  |  |  |  |  | } | 
| 247 |  |  |  |  |  |  | else{ | 
| 248 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "dP", 2, newSVpv("", 0), 0); | 
| 249 |  |  |  |  |  |  | } | 
| 250 |  |  |  |  |  |  | /* dQ */ | 
| 251 | 109 | 50 |  |  |  |  | siz = (self->key.dQ) ? mp_unsigned_bin_size(self->key.dQ) : 0; | 
| 252 | 109 | 50 |  |  |  |  | if (siz>10000) { | 
| 253 | 0 |  |  |  |  |  | croak("FATAL: key2hash failed - 'dQ' too big number"); | 
| 254 |  |  |  |  |  |  | } | 
| 255 | 109 | 100 |  |  |  |  | if (siz>0) { | 
| 256 | 49 |  |  |  |  |  | cryptx_internal_mp2hex_with_leading_zero(self->key.dQ, buf, 20000, 0); | 
| 257 | 49 |  |  |  |  |  | not_used = hv_store(rv_hash, "dQ", 2, newSVpv(buf, strlen(buf)), 0); | 
| 258 |  |  |  |  |  |  | } | 
| 259 |  |  |  |  |  |  | else{ | 
| 260 | 60 |  |  |  |  |  | not_used = hv_store(rv_hash, "dQ", 2, newSVpv("", 0), 0); | 
| 261 |  |  |  |  |  |  | } | 
| 262 |  |  |  |  |  |  | /* size */ | 
| 263 | 109 |  |  |  |  |  | not_used = hv_store(rv_hash, "size", 4, newSViv(nsize), 0); | 
| 264 |  |  |  |  |  |  | /* type */ | 
| 265 | 109 |  |  |  |  |  | not_used = hv_store(rv_hash, "type", 4, newSViv(self->key.type), 0); | 
| 266 |  |  |  |  |  |  | LTC_UNUSED_PARAM(not_used); | 
| 267 | 109 |  |  |  |  |  | RETVAL = newRV_noinc((SV*)rv_hash); | 
| 268 |  |  |  |  |  |  | OUTPUT: | 
| 269 |  |  |  |  |  |  | RETVAL | 
| 270 |  |  |  |  |  |  |  | 
| 271 |  |  |  |  |  |  | SV* | 
| 272 |  |  |  |  |  |  | export_key_der(Crypt::PK::RSA self, char * type) | 
| 273 |  |  |  |  |  |  | CODE: | 
| 274 |  |  |  |  |  |  | { | 
| 275 |  |  |  |  |  |  | int rv; | 
| 276 |  |  |  |  |  |  | unsigned char out[4096]; | 
| 277 | 5 |  |  |  |  |  | unsigned long out_len = 4096; | 
| 278 |  |  |  |  |  |  |  | 
| 279 | 5 |  |  |  |  |  | RETVAL = newSVpvn(NULL, 0); /* undef */ | 
| 280 | 5 | 100 |  |  |  |  | if (strnEQ(type, "private", 7)) { | 
| 281 | 2 |  |  |  |  |  | rv = rsa_export(out, &out_len, PK_PRIVATE, &self->key); | 
| 282 | 2 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_export(PK_PRIVATE) failed: %s", error_to_string(rv)); | 
| 283 | 2 |  |  |  |  |  | RETVAL = newSVpvn((char*)out, out_len); | 
| 284 |  |  |  |  |  |  | } | 
| 285 | 3 | 50 |  |  |  |  | else if (strnEQ(type, "public", 6)) { | 
| 286 | 3 |  |  |  |  |  | rv = rsa_export(out, &out_len, PK_PUBLIC|PK_STD, &self->key); | 
| 287 | 3 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_export(PK_PUBLIC|PK_STD) failed: %s", error_to_string(rv)); | 
| 288 | 3 |  |  |  |  |  | RETVAL = newSVpvn((char*)out, out_len); | 
| 289 |  |  |  |  |  |  | } | 
| 290 |  |  |  |  |  |  | else { | 
| 291 | 0 |  |  |  |  |  | croak("FATAL: export_key_der invalid type '%s'", type); | 
| 292 |  |  |  |  |  |  | } | 
| 293 |  |  |  |  |  |  | } | 
| 294 |  |  |  |  |  |  | OUTPUT: | 
| 295 |  |  |  |  |  |  | RETVAL | 
| 296 |  |  |  |  |  |  |  | 
| 297 |  |  |  |  |  |  | SV * | 
| 298 |  |  |  |  |  |  | encrypt(Crypt::PK::RSA self, SV * data, const char * padding = "oaep", const char * oaep_hash = "SHA1", SV * oaep_lparam = NULL) | 
| 299 |  |  |  |  |  |  | CODE: | 
| 300 |  |  |  |  |  |  | { | 
| 301 |  |  |  |  |  |  | int rv, hash_id; | 
| 302 | 2 |  |  |  |  |  | unsigned char *lparam_ptr=NULL; | 
| 303 | 2 |  |  |  |  |  | STRLEN lparam_len=0; | 
| 304 | 2 |  |  |  |  |  | unsigned char *data_ptr=NULL; | 
| 305 | 2 |  |  |  |  |  | STRLEN data_len=0; | 
| 306 |  |  |  |  |  |  | unsigned char buffer[1024]; | 
| 307 | 2 |  |  |  |  |  | unsigned long buffer_len = 1024; | 
| 308 |  |  |  |  |  |  |  | 
| 309 | 2 | 50 |  |  |  |  | data_ptr = (unsigned char *)SvPVbyte(data, data_len); | 
| 310 |  |  |  |  |  |  |  | 
| 311 | 2 |  |  |  |  |  | RETVAL = newSVpvn(NULL, 0); /* undef */ | 
| 312 | 2 | 100 |  |  |  |  | if (strnEQ(padding, "oaep", 4)) { | 
| 313 | 1 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(oaep_hash); | 
| 314 | 1 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", oaep_hash); | 
| 315 | 1 | 50 |  |  |  |  | if (oaep_lparam) lparam_ptr = (unsigned char *)SvPVbyte(oaep_lparam, lparam_len); | 
|  |  | 0 |  |  |  |  |  | 
| 316 | 1 |  |  |  |  |  | rv = rsa_encrypt_key_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, lparam_ptr, (unsigned long)lparam_len, | 
| 317 |  |  |  |  |  |  | &self->pstate, self->pindex, | 
| 318 | 1 |  |  |  |  |  | hash_id, LTC_PKCS_1_OAEP, &self->key); | 
| 319 | 1 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_encrypt_key_ex failed: %s", error_to_string(rv)); | 
| 320 | 1 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 321 |  |  |  |  |  |  | } | 
| 322 | 1 | 50 |  |  |  |  | else if (strnEQ(padding, "v1.5", 4)) { | 
| 323 | 0 |  |  |  |  |  | rv = rsa_encrypt_key_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, NULL, 0, | 
| 324 |  |  |  |  |  |  | &self->pstate, self->pindex, | 
| 325 | 0 |  |  |  |  |  | 0, LTC_PKCS_1_V1_5, &self->key); | 
| 326 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_encrypt_key_ex failed: %s", error_to_string(rv)); | 
| 327 | 0 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 328 |  |  |  |  |  |  | } | 
| 329 | 1 | 50 |  |  |  |  | else if (strnEQ(padding, "none", 4)) { | 
| 330 |  |  |  |  |  |  | /* raw RSA */ | 
| 331 | 1 |  |  |  |  |  | rv = ltc_mp.rsa_me(data_ptr, (unsigned long)data_len, buffer, &buffer_len, PK_PUBLIC, &self->key); | 
| 332 | 1 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_me failed: %s", error_to_string(rv)); | 
| 333 | 1 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 334 |  |  |  |  |  |  | } | 
| 335 |  |  |  |  |  |  | else { | 
| 336 | 0 |  |  |  |  |  | croak("FATAL: rsa_encrypt invalid padding '%s'", padding); | 
| 337 |  |  |  |  |  |  | } | 
| 338 |  |  |  |  |  |  | } | 
| 339 |  |  |  |  |  |  | OUTPUT: | 
| 340 |  |  |  |  |  |  | RETVAL | 
| 341 |  |  |  |  |  |  |  | 
| 342 |  |  |  |  |  |  | SV * | 
| 343 |  |  |  |  |  |  | decrypt(Crypt::PK::RSA self, SV * data, const char * padding = "oaep", const char * oaep_hash = "SHA1", SV * oaep_lparam = NULL) | 
| 344 |  |  |  |  |  |  | CODE: | 
| 345 |  |  |  |  |  |  | { | 
| 346 |  |  |  |  |  |  | int rv, hash_id, stat; | 
| 347 | 38 |  |  |  |  |  | unsigned char *lparam_ptr=NULL; | 
| 348 | 38 |  |  |  |  |  | STRLEN lparam_len=0; | 
| 349 | 38 |  |  |  |  |  | unsigned char *data_ptr=NULL; | 
| 350 | 38 |  |  |  |  |  | STRLEN data_len=0; | 
| 351 |  |  |  |  |  |  | unsigned char buffer[1024]; | 
| 352 | 38 |  |  |  |  |  | unsigned long buffer_len = 1024; | 
| 353 |  |  |  |  |  |  |  | 
| 354 | 38 | 50 |  |  |  |  | data_ptr = (unsigned char *)SvPVbyte(data, data_len); | 
| 355 |  |  |  |  |  |  |  | 
| 356 | 38 |  |  |  |  |  | RETVAL = newSVpvn(NULL, 0); /* undef */ | 
| 357 | 38 | 100 |  |  |  |  | if (strnEQ(padding, "oaep", 4)) { | 
| 358 | 1 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(oaep_hash); | 
| 359 | 1 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", oaep_hash); | 
| 360 | 1 | 50 |  |  |  |  | if (oaep_lparam) lparam_ptr = (unsigned char *)SvPVbyte(oaep_lparam, lparam_len); | 
|  |  | 0 |  |  |  |  |  | 
| 361 | 1 |  |  |  |  |  | rv = rsa_decrypt_key_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, lparam_ptr, (unsigned long)lparam_len, | 
| 362 | 1 |  |  |  |  |  | hash_id, LTC_PKCS_1_OAEP, &stat, &self->key); | 
| 363 | 1 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_decrypt_key_ex failed: %s", error_to_string(rv)); | 
| 364 | 1 | 50 |  |  |  |  | if (stat != 1) croak("FATAL: rsa_decrypt - not valid OAEP packet"); | 
| 365 | 1 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 366 |  |  |  |  |  |  | } | 
| 367 | 37 | 100 |  |  |  |  | else if (strnEQ(padding, "v1.5", 4)) { | 
| 368 | 36 |  |  |  |  |  | rv = rsa_decrypt_key_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, NULL, 0, | 
| 369 | 36 |  |  |  |  |  | 0, LTC_PKCS_1_V1_5, &stat, &self->key); | 
| 370 | 36 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_decrypt_key_ex failed: %s", error_to_string(rv)); | 
| 371 | 36 | 50 |  |  |  |  | if (stat != 1) croak("FATAL: rsa_decrypt - invalid"); | 
| 372 | 36 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 373 |  |  |  |  |  |  | } | 
| 374 | 1 | 50 |  |  |  |  | else if (strnEQ(padding, "none", 4)) { | 
| 375 |  |  |  |  |  |  | /* raw RSA */ | 
| 376 | 1 |  |  |  |  |  | rv = ltc_mp.rsa_me(data_ptr, (unsigned long)data_len, buffer, &buffer_len, PK_PRIVATE, &self->key); | 
| 377 | 1 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_me failed: %s", error_to_string(rv)); | 
| 378 | 1 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 379 |  |  |  |  |  |  | } | 
| 380 |  |  |  |  |  |  | else { | 
| 381 | 0 |  |  |  |  |  | croak("FATAL: rsa_encrypt invalid padding '%s'", padding); | 
| 382 |  |  |  |  |  |  | } | 
| 383 |  |  |  |  |  |  | } | 
| 384 |  |  |  |  |  |  | OUTPUT: | 
| 385 |  |  |  |  |  |  | RETVAL | 
| 386 |  |  |  |  |  |  |  | 
| 387 |  |  |  |  |  |  | SV * | 
| 388 |  |  |  |  |  |  | sign_hash(Crypt::PK::RSA self, SV * data, const char * hash_name = "SHA1", const char * padding = "pss", unsigned long saltlen=12) | 
| 389 |  |  |  |  |  |  | ALIAS: | 
| 390 |  |  |  |  |  |  | sign_message = 1 | 
| 391 |  |  |  |  |  |  | CODE: | 
| 392 |  |  |  |  |  |  | { | 
| 393 |  |  |  |  |  |  | int rv, hash_id; | 
| 394 | 4 |  |  |  |  |  | unsigned char buffer[1024], tmp[MAXBLOCKSIZE], *data_ptr = NULL; | 
| 395 | 4 |  |  |  |  |  | unsigned long tmp_len = MAXBLOCKSIZE, buffer_len = 1024; | 
| 396 | 4 |  |  |  |  |  | STRLEN data_len = 0; | 
| 397 |  |  |  |  |  |  |  | 
| 398 | 4 | 50 |  |  |  |  | data_ptr = (unsigned char *)SvPVbyte(data, data_len); | 
| 399 | 4 | 100 |  |  |  |  | if (ix == 1) { | 
| 400 | 2 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 401 | 2 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 402 | 2 |  |  |  |  |  | rv = hash_memory(hash_id, data_ptr, (unsigned long)data_len, tmp, &tmp_len); | 
| 403 | 2 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: hash_memory failed: %s", error_to_string(rv)); | 
| 404 | 2 |  |  |  |  |  | data_ptr = tmp; | 
| 405 | 2 |  |  |  |  |  | data_len = tmp_len; | 
| 406 |  |  |  |  |  |  | } | 
| 407 | 4 | 50 |  |  |  |  | if (strnEQ(padding, "pss", 3)) { | 
| 408 | 4 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 409 | 4 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 410 | 4 |  |  |  |  |  | rv = rsa_sign_hash_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, LTC_PKCS_1_PSS, | 
| 411 |  |  |  |  |  |  | &self->pstate, self->pindex, | 
| 412 | 4 |  |  |  |  |  | hash_id, saltlen, &self->key); | 
| 413 | 4 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_sign_hash_ex failed: %s", error_to_string(rv)); | 
| 414 | 4 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 415 |  |  |  |  |  |  | } | 
| 416 | 0 | 0 |  |  |  |  | else if (strnEQ(padding, "v1.5", 4)) { | 
| 417 | 0 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 418 | 0 | 0 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 419 | 0 |  |  |  |  |  | rv = rsa_sign_hash_ex(data_ptr, (unsigned long)data_len, buffer, &buffer_len, LTC_PKCS_1_V1_5, | 
| 420 |  |  |  |  |  |  | &self->pstate, self->pindex, | 
| 421 | 0 |  |  |  |  |  | hash_id, 0, &self->key); | 
| 422 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_sign_hash_ex failed: %s", error_to_string(rv)); | 
| 423 | 0 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 424 |  |  |  |  |  |  | } | 
| 425 | 0 | 0 |  |  |  |  | else if (strnEQ(padding, "none", 4)) { | 
| 426 |  |  |  |  |  |  | /* raw RSA */ | 
| 427 | 0 |  |  |  |  |  | rv = ltc_mp.rsa_me(data_ptr, (unsigned long)data_len, buffer, &buffer_len, PK_PRIVATE, &self->key); | 
| 428 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_me failed: %s", error_to_string(rv)); | 
| 429 | 0 |  |  |  |  |  | RETVAL = newSVpvn((char*)buffer, buffer_len); | 
| 430 |  |  |  |  |  |  | } | 
| 431 |  |  |  |  |  |  | else { | 
| 432 | 0 |  |  |  |  |  | croak("FATAL: rsa_sign invalid padding '%s'", padding); | 
| 433 |  |  |  |  |  |  | } | 
| 434 |  |  |  |  |  |  | } | 
| 435 |  |  |  |  |  |  | OUTPUT: | 
| 436 |  |  |  |  |  |  | RETVAL | 
| 437 |  |  |  |  |  |  |  | 
| 438 |  |  |  |  |  |  | int | 
| 439 |  |  |  |  |  |  | verify_hash(Crypt::PK::RSA self, SV * sig, SV * data, const char * hash_name = "SHA1", const char * padding = "pss", unsigned long saltlen = 12) | 
| 440 |  |  |  |  |  |  | ALIAS: | 
| 441 |  |  |  |  |  |  | verify_message = 1 | 
| 442 |  |  |  |  |  |  | CODE: | 
| 443 |  |  |  |  |  |  | { | 
| 444 |  |  |  |  |  |  | int rv, hash_id, stat; | 
| 445 | 109 |  |  |  |  |  | unsigned char tmp[MAXBLOCKSIZE], buffer[1024], *data_ptr = NULL, *sig_ptr = NULL; | 
| 446 | 109 |  |  |  |  |  | unsigned long i, tmp_len = MAXBLOCKSIZE, buffer_len = 1024; | 
| 447 | 109 |  |  |  |  |  | STRLEN data_len = 0, sig_len = 0; | 
| 448 |  |  |  |  |  |  |  | 
| 449 | 109 | 50 |  |  |  |  | data_ptr = (unsigned char *)SvPVbyte(data, data_len); | 
| 450 | 109 | 50 |  |  |  |  | sig_ptr = (unsigned char *)SvPVbyte(sig, sig_len); | 
| 451 | 109 | 100 |  |  |  |  | if (ix == 1) { | 
| 452 | 107 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 453 | 107 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 454 | 107 |  |  |  |  |  | rv = hash_memory(hash_id, data_ptr, (unsigned long)data_len, tmp, &tmp_len); | 
| 455 | 107 | 50 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: hash_memory failed: %s", error_to_string(rv)); | 
| 456 | 107 |  |  |  |  |  | data_ptr = tmp; | 
| 457 | 107 |  |  |  |  |  | data_len = tmp_len; | 
| 458 |  |  |  |  |  |  | } | 
| 459 | 109 |  |  |  |  |  | RETVAL = 1; | 
| 460 | 109 |  |  |  |  |  | stat = 0; | 
| 461 | 109 | 100 |  |  |  |  | if (strnEQ(padding, "pss", 3)) { | 
| 462 | 4 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 463 | 4 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 464 | 4 |  |  |  |  |  | rv = rsa_verify_hash_ex(sig_ptr, (unsigned long)sig_len, data_ptr, (unsigned long)data_len, LTC_PKCS_1_PSS, | 
| 465 | 4 |  |  |  |  |  | hash_id, saltlen, &stat, &self->key); | 
| 466 | 4 | 50 |  |  |  |  | if (rv != CRYPT_OK || stat != 1) RETVAL = 0; | 
|  |  | 50 |  |  |  |  |  | 
| 467 |  |  |  |  |  |  | } | 
| 468 | 105 | 50 |  |  |  |  | else if (strnEQ(padding, "v1.5", 4)) { | 
| 469 | 105 |  |  |  |  |  | hash_id = cryptx_internal_find_hash(hash_name); | 
| 470 | 105 | 50 |  |  |  |  | if (hash_id == -1) croak("FATAL: find_hash failed for '%s'", hash_name); | 
| 471 | 105 |  |  |  |  |  | rv = rsa_verify_hash_ex(sig_ptr, (unsigned long)sig_len, data_ptr, (unsigned long)data_len, LTC_PKCS_1_V1_5, | 
| 472 | 105 |  |  |  |  |  | hash_id, 0, &stat, &self->key); | 
| 473 | 105 | 100 |  |  |  |  | if (rv != CRYPT_OK || stat != 1) RETVAL = 0; | 
|  |  | 50 |  |  |  |  |  | 
| 474 |  |  |  |  |  |  | } | 
| 475 | 0 | 0 |  |  |  |  | else if (strnEQ(padding, "none", 4)) { | 
| 476 |  |  |  |  |  |  | /* raw RSA */ | 
| 477 | 0 |  |  |  |  |  | Zero(buffer, buffer_len, unsigned char); | 
| 478 | 0 |  |  |  |  |  | rv = ltc_mp.rsa_me(sig_ptr, (unsigned long)sig_len, buffer, &buffer_len, PK_PUBLIC, &self->key); | 
| 479 | 0 | 0 |  |  |  |  | if (rv != CRYPT_OK) croak("FATAL: rsa_me failed: %s", error_to_string(rv)); | 
| 480 | 0 | 0 |  |  |  |  | if (data_len <= buffer_len && buffer_len > 0 && data_len > 0) { | 
|  |  | 0 |  |  |  |  |  | 
|  |  | 0 |  |  |  |  |  | 
| 481 | 0 | 0 |  |  |  |  | for (i = 0; i < buffer_len - data_len; i++) if (buffer[i] != 0) RETVAL = 0; | 
|  |  | 0 |  |  |  |  |  | 
| 482 | 0 | 0 |  |  |  |  | if (memNE(data_ptr, buffer + buffer_len - data_len, data_len)) RETVAL = 0; | 
| 483 |  |  |  |  |  |  | } | 
| 484 |  |  |  |  |  |  | else { | 
| 485 | 0 |  |  |  |  |  | RETVAL = 0; | 
| 486 |  |  |  |  |  |  | } | 
| 487 |  |  |  |  |  |  | } | 
| 488 |  |  |  |  |  |  | else { | 
| 489 | 0 |  |  |  |  |  | croak("FATAL: rsa_verify invalid padding '%s'", padding); | 
| 490 |  |  |  |  |  |  | } | 
| 491 |  |  |  |  |  |  | } | 
| 492 |  |  |  |  |  |  | OUTPUT: | 
| 493 |  |  |  |  |  |  | RETVAL | 
| 494 |  |  |  |  |  |  |  | 
| 495 |  |  |  |  |  |  | void | 
| 496 |  |  |  |  |  |  | DESTROY(Crypt::PK::RSA self) | 
| 497 |  |  |  |  |  |  | CODE: | 
| 498 | 107 | 50 |  |  |  |  | if (self->key.type != -1) { rsa_free(&self->key); self->key.type = -1; } | 
| 499 | 107 |  |  |  |  |  | Safefree(self); |