| /* |
| Licensed to the Apache Software Foundation (ASF) under one |
| or more contributor license agreements. See the NOTICE file |
| distributed with this work for additional information |
| regarding copyright ownership. The ASF licenses this file |
| to you under the Apache License, Version 2.0 (the |
| "License"); you may not use this file except in compliance |
| with the License. You may obtain a copy of the License at |
| |
| http://www.apache.org/licenses/LICENSE-2.0 |
| |
| Unless required by applicable law or agreed to in writing, |
| software distributed under the License is distributed on an |
| "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY |
| KIND, either express or implied. See the License for the |
| specific language governing permissions and limitations |
| under the License. |
| */ |
| |
| /* Elliptic Curve API high-level functions */ |
| |
| public sealed class ECDH |
| { |
| public const int INVALID_PUBLIC_KEY = -2; |
| public const int ERROR = -3; |
| public const int INVALID = -4; |
| public static readonly int EFS = ROM.MODBYTES; |
| public static readonly int EGS = ROM.MODBYTES; |
| public static readonly int EAS = AES.KS; |
| public static readonly int EBS = AES.BS; |
| |
| /* Convert Integer to n-byte array */ |
| private static sbyte[] inttoBytes(int n, int len) |
| { |
| int i; |
| sbyte[] b = new sbyte[len]; |
| |
| for (i = 0;i < len;i++) |
| { |
| b[i] = 0; |
| } |
| i = len; |
| while (n > 0 && i > 0) |
| { |
| i--; |
| b[i] = unchecked((sbyte)(n & 0xff)); |
| n /= 256; |
| } |
| return b; |
| } |
| |
| /* Key Derivation Functions */ |
| /* Input octet Z */ |
| /* Output key of length olen */ |
| public static sbyte[] KDF1(sbyte[] Z, int olen) |
| { |
| /* NOTE: the parameter olen is the length of the output K in bytes */ |
| HASH H = new HASH(); |
| int hlen = HASH.len; |
| sbyte[] K = new sbyte[olen]; |
| |
| sbyte[] B; |
| int counter , cthreshold , k = 0; |
| |
| for (int i = 0;i < K.Length;i++) |
| { |
| K[i] = 0; |
| } |
| |
| cthreshold = olen / hlen; |
| if (olen % hlen != 0) |
| { |
| cthreshold++; |
| } |
| |
| for (counter = 0;counter < cthreshold;counter++) |
| { |
| H.process_array(Z); |
| if (counter > 0) |
| { |
| H.process_num(counter); |
| } |
| B = H.hash(); |
| if (k + hlen > olen) |
| { |
| for (int i = 0;i < olen % hlen;i++) |
| { |
| K[k++] = B[i]; |
| } |
| } |
| else |
| { |
| for (int i = 0;i < hlen;i++) |
| { |
| K[k++] = B[i]; |
| } |
| } |
| } |
| return K; |
| } |
| |
| public static sbyte[] KDF2(sbyte[] Z, sbyte[] P, int olen) |
| { |
| /* NOTE: the parameter olen is the length of the output k in bytes */ |
| HASH H = new HASH(); |
| int hlen = HASH.len; |
| sbyte[] K = new sbyte[olen]; |
| |
| sbyte[] B = new sbyte[hlen]; |
| int counter , cthreshold , k = 0; |
| |
| for (int i = 0;i < K.Length;i++) |
| { |
| K[i] = 0; |
| } |
| |
| cthreshold = olen / hlen; |
| if (olen % hlen != 0) |
| { |
| cthreshold++; |
| } |
| |
| for (counter = 1;counter <= cthreshold;counter++) |
| { |
| H.process_array(Z); |
| H.process_num(counter); |
| H.process_array(P); |
| B = H.hash(); |
| if (k + hlen > olen) |
| { |
| for (int i = 0;i < olen % hlen;i++) |
| { |
| K[k++] = B[i]; |
| } |
| } |
| else |
| { |
| for (int i = 0;i < hlen;i++) |
| { |
| K[k++] = B[i]; |
| } |
| } |
| } |
| return K; |
| } |
| |
| /* Password based Key Derivation Function */ |
| /* Input password p, salt s, and repeat count */ |
| /* Output key of length olen */ |
| public static sbyte[] PBKDF2(sbyte[] Pass, sbyte[] Salt, int rep, int olen) |
| { |
| int i, j, k, d, opt; |
| d = olen / 32; |
| if (olen % 32 != 0) |
| { |
| d++; |
| } |
| sbyte[] F = new sbyte[EFS]; |
| sbyte[] U = new sbyte[EFS]; |
| sbyte[] S = new sbyte[Salt.Length + 4]; |
| |
| sbyte[] K = new sbyte[d * EFS]; |
| opt = 0; |
| |
| for (i = 1;i <= d;i++) |
| { |
| for (j = 0;j < Salt.Length;j++) |
| { |
| S[j] = Salt[j]; |
| } |
| sbyte[] N = inttoBytes(i,4); |
| for (j = 0;j < 4;j++) |
| { |
| S[Salt.Length + j] = N[j]; |
| } |
| |
| HMAC(S,Pass,F); |
| |
| for (j = 0;j < EFS;j++) |
| { |
| U[j] = F[j]; |
| } |
| for (j = 2;j <= rep;j++) |
| { |
| HMAC(U,Pass,U); |
| for (k = 0;k < EFS;k++) |
| { |
| F[k] ^= U[k]; |
| } |
| } |
| for (j = 0;j < EFS;j++) |
| { |
| K[opt++] = F[j]; |
| } |
| } |
| sbyte[] key = new sbyte[olen]; |
| for (i = 0;i < olen;i++) |
| { |
| key[i] = K[i]; |
| } |
| return key; |
| } |
| |
| /* Calculate HMAC of m using key k. HMAC is tag of length olen */ |
| public static int HMAC(sbyte[] M, sbyte[] K, sbyte[] tag) |
| { |
| /* Input is from an octet m * |
| * olen is requested output length in bytes. k is the key * |
| * The output is the calculated tag */ |
| int b; |
| sbyte[] B; |
| sbyte[] K0 = new sbyte[64]; |
| int olen = tag.Length; |
| |
| b = K0.Length; |
| if (olen < 4 || olen> HASH.len) |
| { |
| return 0; |
| } |
| |
| for (int i = 0;i < b;i++) |
| { |
| K0[i] = 0; |
| } |
| |
| HASH H = new HASH(); |
| |
| if (K.Length > b) |
| { |
| H.process_array(K); |
| B = H.hash(); |
| for (int i = 0;i < 32;i++) |
| { |
| K0[i] = B[i]; |
| } |
| } |
| else |
| { |
| for (int i = 0;i < K.Length;i++) |
| { |
| K0[i] = K[i]; |
| } |
| } |
| |
| for (int i = 0;i < b;i++) |
| { |
| K0[i] ^= 0x36; |
| } |
| H.process_array(K0); |
| H.process_array(M); |
| B = H.hash(); |
| |
| for (int i = 0;i < b;i++) |
| { |
| K0[i] ^= 0x6a; |
| } |
| H.process_array(K0); |
| H.process_array(B); |
| B = H.hash(); |
| |
| for (int i = 0;i < olen;i++) |
| { |
| tag[i] = B[i]; |
| } |
| |
| return 1; |
| } |
| |
| /* AES encryption/decryption. Encrypt byte array M using key K and returns ciphertext */ |
| public static sbyte[] AES_CBC_IV0_ENCRYPT(sbyte[] K, sbyte[] M) |
| { // AES CBC encryption, with Null IV and key K |
| /* Input is from an octet string M, output is to an octet string C */ |
| /* Input is padded as necessary to make up a full final block */ |
| AES a = new AES(); |
| bool fin; |
| int i, j, ipt, opt; |
| sbyte[] buff = new sbyte[16]; |
| int clen = 16 + (M.Length / 16) * 16; |
| |
| sbyte[] C = new sbyte[clen]; |
| int padlen; |
| |
| a.init(AES.CBC,K,null); |
| |
| ipt = opt = 0; |
| fin = false; |
| for (;;) |
| { |
| for (i = 0;i < 16;i++) |
| { |
| if (ipt < M.Length) |
| { |
| buff[i] = M[ipt++]; |
| } |
| else |
| { |
| fin = true; |
| break; |
| } |
| } |
| if (fin) |
| { |
| break; |
| } |
| a.encrypt(buff); |
| for (i = 0;i < 16;i++) |
| { |
| C[opt++] = buff[i]; |
| } |
| } |
| |
| /* last block, filled up to i-th index */ |
| |
| padlen = 16 - i; |
| for (j = i;j < 16;j++) |
| { |
| buff[j] = (sbyte)padlen; |
| } |
| |
| a.encrypt(buff); |
| |
| for (i = 0;i < 16;i++) |
| { |
| C[opt++] = buff[i]; |
| } |
| a.end(); |
| return C; |
| } |
| |
| /* returns plaintext if all consistent, else returns null string */ |
| public static sbyte[] AES_CBC_IV0_DECRYPT(sbyte[] K, sbyte[] C) |
| { // padding is removed |
| AES a = new AES(); |
| int i, ipt, opt, ch; |
| sbyte[] buff = new sbyte[16]; |
| sbyte[] MM = new sbyte[C.Length]; |
| bool fin, bad; |
| int padlen; |
| ipt = opt = 0; |
| |
| a.init(AES.CBC,K,null); |
| |
| if (C.Length == 0) |
| { |
| return new sbyte[0]; |
| } |
| ch = C[ipt++]; |
| |
| fin = false; |
| |
| for (;;) |
| { |
| for (i = 0;i < 16;i++) |
| { |
| buff[i] = (sbyte)ch; |
| if (ipt >= C.Length) |
| { |
| fin = true; |
| break; |
| } |
| else |
| { |
| ch = C[ipt++]; |
| } |
| } |
| a.decrypt(buff); |
| if (fin) |
| { |
| break; |
| } |
| for (i = 0;i < 16;i++) |
| { |
| MM[opt++] = buff[i]; |
| } |
| } |
| |
| a.end(); |
| bad = false; |
| padlen = buff[15]; |
| if (i != 15 || padlen < 1 || padlen>16) |
| { |
| bad = true; |
| } |
| if (padlen >= 2 && padlen <= 16) |
| { |
| for (i = 16 - padlen;i < 16;i++) |
| { |
| if (buff[i] != padlen) |
| { |
| bad = true; |
| } |
| } |
| } |
| |
| if (!bad) |
| { |
| for (i = 0;i < 16 - padlen;i++) |
| { |
| MM[opt++] = buff[i]; |
| } |
| } |
| |
| if (bad) |
| { |
| return new sbyte[0]; |
| } |
| |
| sbyte[] M = new sbyte[opt]; |
| for (i = 0;i < opt;i++) |
| { |
| M[i] = MM[i]; |
| } |
| |
| return M; |
| } |
| |
| /* Calculate a public/private EC GF(p) key pair W,S where W=S.G mod EC(p), |
| * where S is the secret key and W is the public key |
| * and G is fixed generator. |
| * If RNG is NULL then the private key is provided externally in S |
| * otherwise it is generated randomly internally */ |
| public static int KEY_PAIR_GENERATE(RAND RNG, sbyte[] S, sbyte[] W) |
| { |
| BIG r, gx, gy, s; |
| ECP G, WP; |
| int res = 0; |
| sbyte[] T = new sbyte[EFS]; |
| |
| gx = new BIG(ROM.CURVE_Gx); |
| if (ROM.CURVETYPE != ROM.MONTGOMERY) |
| { |
| gy = new BIG(ROM.CURVE_Gy); |
| G = new ECP(gx,gy); |
| } |
| else |
| { |
| G = new ECP(gx); |
| } |
| |
| r = new BIG(ROM.CURVE_Order); |
| |
| if (RNG == null) |
| { |
| s = BIG.fromBytes(S); |
| } |
| else |
| { |
| s = BIG.randomnum(r,RNG); |
| |
| s.toBytes(T); |
| for (int i = 0;i < EGS;i++) |
| { |
| S[i] = T[i]; |
| } |
| } |
| |
| WP = G.mul(s); |
| WP.toBytes(W); |
| |
| return res; |
| } |
| |
| /* validate public key. Set full=true for fuller check */ |
| public static int PUBLIC_KEY_VALIDATE(bool full, sbyte[] W) |
| { |
| BIG r; |
| ECP WP = ECP.fromBytes(W); |
| int res = 0; |
| |
| r = new BIG(ROM.CURVE_Order); |
| |
| if (WP.is_infinity()) |
| { |
| res = INVALID_PUBLIC_KEY; |
| } |
| if (res == 0 && full) |
| { |
| WP = WP.mul(r); |
| if (!WP.is_infinity()) |
| { |
| res = INVALID_PUBLIC_KEY; |
| } |
| } |
| return res; |
| } |
| |
| /* IEEE-1363 Diffie-Hellman online calculation Z=S.WD */ |
| public static int ECPSVDP_DH(sbyte[] S, sbyte[] WD, sbyte[] Z) |
| { |
| BIG r, s; |
| ECP W; |
| int res = 0; |
| sbyte[] T = new sbyte[EFS]; |
| |
| s = BIG.fromBytes(S); |
| |
| W = ECP.fromBytes(WD); |
| if (W.is_infinity()) |
| { |
| res = ERROR; |
| } |
| |
| if (res == 0) |
| { |
| r = new BIG(ROM.CURVE_Order); |
| s.mod(r); |
| W = W.mul(s); |
| if (W.is_infinity()) |
| { |
| res = ERROR; |
| } |
| else |
| { |
| W.X.toBytes(T); |
| for (int i = 0;i < EFS;i++) |
| { |
| Z[i] = T[i]; |
| } |
| } |
| } |
| return res; |
| } |
| |
| /* IEEE ECDSA Signature, C and D are signature on F using private key S */ |
| public static int ECPSP_DSA(RAND RNG, sbyte[] S, sbyte[] F, sbyte[] C, sbyte[] D) |
| { |
| sbyte[] T = new sbyte[EFS]; |
| BIG gx, gy, r, s, f, c, d, u, vx; |
| ECP G, V; |
| |
| HASH H = new HASH(); |
| H.process_array(F); |
| sbyte[] B = H.hash(); |
| |
| gx = new BIG(ROM.CURVE_Gx); |
| gy = new BIG(ROM.CURVE_Gy); |
| |
| G = new ECP(gx,gy); |
| r = new BIG(ROM.CURVE_Order); |
| |
| s = BIG.fromBytes(S); |
| f = BIG.fromBytes(B); |
| |
| c = new BIG(0); |
| d = new BIG(0); |
| V = new ECP(); |
| |
| do |
| { |
| u = BIG.randomnum(r,RNG); |
| |
| V.copy(G); |
| V = V.mul(u); |
| vx = V.X; |
| c.copy(vx); |
| c.mod(r); |
| if (c.iszilch()) |
| { |
| continue; |
| } |
| u.invmodp(r); |
| d.copy(BIG.modmul(s,c,r)); |
| d.add(f); |
| d.copy(BIG.modmul(u,d,r)); |
| } while (d.iszilch()); |
| |
| c.toBytes(T); |
| for (int i = 0;i < EFS;i++) |
| { |
| C[i] = T[i]; |
| } |
| d.toBytes(T); |
| for (int i = 0;i < EFS;i++) |
| { |
| D[i] = T[i]; |
| } |
| return 0; |
| } |
| |
| /* IEEE1363 ECDSA Signature Verification. Signature C and D on F is verified using public key W */ |
| public static int ECPVP_DSA(sbyte[] W, sbyte[] F, sbyte[] C, sbyte[] D) |
| { |
| BIG r, gx, gy, f, c, d, h2; |
| int res = 0; |
| ECP G, WP, P; |
| |
| HASH H = new HASH(); |
| H.process_array(F); |
| sbyte[] B = H.hash(); |
| |
| gx = new BIG(ROM.CURVE_Gx); |
| gy = new BIG(ROM.CURVE_Gy); |
| |
| G = new ECP(gx,gy); |
| r = new BIG(ROM.CURVE_Order); |
| |
| c = BIG.fromBytes(C); |
| d = BIG.fromBytes(D); |
| f = BIG.fromBytes(B); |
| |
| if (c.iszilch() || BIG.comp(c,r) >= 0 || d.iszilch() || BIG.comp(d,r) >= 0) |
| { |
| res = INVALID; |
| } |
| |
| if (res == 0) |
| { |
| d.invmodp(r); |
| f.copy(BIG.modmul(f,d,r)); |
| h2 = BIG.modmul(c,d,r); |
| |
| WP = ECP.fromBytes(W); |
| if (WP.is_infinity()) |
| { |
| res = ERROR; |
| } |
| else |
| { |
| P = new ECP(); |
| P.copy(WP); |
| P = P.mul2(h2,G,f); |
| if (P.is_infinity()) |
| { |
| res = INVALID; |
| } |
| else |
| { |
| d = P.X; |
| d.mod(r); |
| if (BIG.comp(d,c) != 0) |
| { |
| res = INVALID; |
| } |
| } |
| } |
| } |
| |
| return res; |
| } |
| |
| /* IEEE1363 ECIES encryption. Encryption of plaintext M uses public key W and produces ciphertext V,C,T */ |
| public static sbyte[] ECIES_ENCRYPT(sbyte[] P1, sbyte[] P2, RAND RNG, sbyte[] W, sbyte[] M, sbyte[] V, sbyte[] T) |
| { |
| int i; |
| |
| sbyte[] Z = new sbyte[EFS]; |
| sbyte[] VZ = new sbyte[3 * EFS + 1]; |
| sbyte[] K1 = new sbyte[EAS]; |
| sbyte[] K2 = new sbyte[EAS]; |
| sbyte[] U = new sbyte[EGS]; |
| |
| if (KEY_PAIR_GENERATE(RNG,U,V) != 0) |
| { |
| return new sbyte[0]; |
| } |
| if (ECPSVDP_DH(U,W,Z) != 0) |
| { |
| return new sbyte[0]; |
| } |
| |
| for (i = 0;i < 2 * EFS + 1;i++) |
| { |
| VZ[i] = V[i]; |
| } |
| for (i = 0;i < EFS;i++) |
| { |
| VZ[2 * EFS + 1 + i] = Z[i]; |
| } |
| |
| |
| sbyte[] K = KDF2(VZ,P1,EFS); |
| |
| for (i = 0;i < EAS;i++) |
| { |
| K1[i] = K[i]; |
| K2[i] = K[EAS + i]; |
| } |
| |
| sbyte[] C = AES_CBC_IV0_ENCRYPT(K1,M); |
| |
| sbyte[] L2 = inttoBytes(P2.Length,8); |
| |
| sbyte[] AC = new sbyte[C.Length + P2.Length + 8]; |
| for (i = 0;i < C.Length;i++) |
| { |
| AC[i] = C[i]; |
| } |
| for (i = 0;i < P2.Length;i++) |
| { |
| AC[C.Length + i] = P2[i]; |
| } |
| for (i = 0;i < 8;i++) |
| { |
| AC[C.Length + P2.Length + i] = L2[i]; |
| } |
| |
| HMAC(AC,K2,T); |
| |
| return C; |
| } |
| |
| /* IEEE1363 ECIES decryption. Decryption of ciphertext V,C,T using private key U outputs plaintext M */ |
| public static sbyte[] ECIES_DECRYPT(sbyte[] P1, sbyte[] P2, sbyte[] V, sbyte[] C, sbyte[] T, sbyte[] U) |
| { |
| |
| int i; |
| |
| sbyte[] Z = new sbyte[EFS]; |
| sbyte[] VZ = new sbyte[3 * EFS + 1]; |
| sbyte[] K1 = new sbyte[EAS]; |
| sbyte[] K2 = new sbyte[EAS]; |
| sbyte[] TAG = new sbyte[T.Length]; |
| |
| if (ECPSVDP_DH(U,V,Z) != 0) |
| { |
| return new sbyte[0]; |
| } |
| |
| for (i = 0;i < 2 * EFS + 1;i++) |
| { |
| VZ[i] = V[i]; |
| } |
| for (i = 0;i < EFS;i++) |
| { |
| VZ[2 * EFS + 1 + i] = Z[i]; |
| } |
| |
| sbyte[] K = KDF2(VZ,P1,EFS); |
| |
| for (i = 0;i < EAS;i++) |
| { |
| K1[i] = K[i]; |
| K2[i] = K[EAS + i]; |
| } |
| |
| sbyte[] M = AES_CBC_IV0_DECRYPT(K1,C); |
| |
| if (M.Length == 0) |
| { |
| return M; |
| } |
| |
| sbyte[] L2 = inttoBytes(P2.Length,8); |
| |
| sbyte[] AC = new sbyte[C.Length + P2.Length + 8]; |
| |
| for (i = 0;i < C.Length;i++) |
| { |
| AC[i] = C[i]; |
| } |
| for (i = 0;i < P2.Length;i++) |
| { |
| AC[C.Length + i] = P2[i]; |
| } |
| for (i = 0;i < 8;i++) |
| { |
| AC[C.Length + P2.Length + i] = L2[i]; |
| } |
| |
| HMAC(AC,K2,TAG); |
| |
| bool same = true; |
| for (i = 0;i < T.Length;i++) |
| { |
| if (T[i] != TAG[i]) |
| { |
| same = false; |
| } |
| } |
| if (!same) |
| { |
| return new sbyte[0]; |
| } |
| |
| return M; |
| |
| } |
| } |