]> www.wagner.pp.ru Git - openssl-gost/engine.git/blobdiff - gost_gost2015.c
Implementation of the MGM mode for magma/kuznyechik
[openssl-gost/engine.git] / gost_gost2015.c
index 1ffa4281a234bf4fa858468ffd3aa71d78a1ae3d..7d207d02a39e2e7e375acf1e954df04f1423530b 100644 (file)
@@ -6,6 +6,8 @@
  */
 #include "gost_lcl.h"
 #include "gost_gost2015.h"
+#include "gost_grasshopper_defines.h"
+#include "gost_grasshopper_math.h"
 #include "e_gost_err.h"
 #include <string.h>
 #include <openssl/rand.h>
@@ -201,3 +203,288 @@ int init_zero_kdf_seed(unsigned char *kdf_seed)
 
     return is_zero_kdfseed ? RAND_bytes(kdf_seed, 8) : 1;
 }
+
+void gost_mgm128_init(mgm128_context *ctx, void *key, block128_f block, mul128_f mul_gf, int blen)
+{
+    memset(ctx, 0, sizeof(*ctx));
+    ctx->block = block;
+    ctx->mul_gf = mul_gf;
+    ctx->key = key;
+    ctx->blocklen = blen;
+
+    /* some precalculations place here
+     *
+     */
+}
+
+int gost_mgm128_setiv(mgm128_context *ctx, const unsigned char *iv,
+                         size_t len)
+{
+    ctx->len.u[0] = 0;          /* AAD length */
+    ctx->len.u[1] = 0;          /* message length */
+    ctx->ares = 0;
+    ctx->mres = 0;
+
+    ctx->ACi.u[0] = 0;
+    ctx->ACi.u[1] = 0;
+    ctx->sum.u[0] = 0;
+    ctx->sum.u[1] = 0;
+
+    memcpy(ctx->nonce.c, iv, ctx->blocklen);
+    ctx->nonce.c[0] &= 0x7f;    /* IV - random vector, but 1st bit should be 0 */
+    return 1;
+}
+
+int gost_mgm128_aad(mgm128_context *ctx, const unsigned char *aad,
+                      size_t len)
+{
+    size_t i;
+    unsigned int n;
+    uint64_t alen = ctx->len.u[0];
+    block128_f block = ctx->block;
+    mul128_f mul_gf = ctx->mul_gf;
+    void *key = ctx->key;
+    int bl = ctx->blocklen;
+
+    if (ctx->len.u[1]) {
+        GOSTerr(GOST_F_GOST_MGM128_AAD,
+                GOST_R_BAD_ORDER);
+        return -2;
+    }
+
+    if (alen == 0) {
+        ctx->nonce.c[0] |= 0x80;
+        (*block) (ctx->nonce.c, ctx->Zi.c, key);    // Z_1 = E_K(1 || nonce)
+    }
+
+    alen += len;
+    if (alen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) ||      // < 2^(n/2)  (len stores in bytes)
+        (sizeof(len) == 8 && alen < len)) {
+            GOSTerr(GOST_F_GOST_MGM128_AAD,
+                    GOST_R_DATA_TOO_LARGE);
+            return -1;
+        }
+    ctx->len.u[0] = alen;
+
+    n = ctx->ares;
+    if (n) {
+        /* Finalize partial_data */
+        while (n && len) {
+            ctx->ACi.c[n] = *(aad++);
+            --len;
+            n = (n + 1) % bl;
+        }
+        if (n == 0) {
+            (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+            mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) A_i
+            grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+              (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+            inc_counter(ctx->Zi.c, bl / 2);                              // Z_{i+1} = incr_l(Z_i)
+        } else {
+            ctx->ares = n;
+            return 0;
+        }
+    }
+    while (len >= bl) {
+        (*block) (ctx->Zi.c, ctx->Hi.c, key);                       // H_i = E_K(Z_i)
+        mul_gf(ctx->mul.u, ctx->Hi.u, (uint64_t *)aad);             // H_i (x) A_i
+        grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,        // acc XOR
+            (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+        inc_counter(ctx->Zi.c, bl / 2);                                  // Z_{i+1} = incr_l(Z_i)
+        aad += bl;
+        len -= bl;
+    }
+    if (len) {
+        n = (unsigned int)len;
+        for (i = 0; i < len; ++i)
+            ctx->ACi.c[i] = aad[i];
+    }
+
+    ctx->ares = n;
+    return 0;
+}
+
+int gost_mgm128_encrypt(mgm128_context *ctx, const unsigned char *in,
+                          unsigned char *out, size_t len)
+{
+    size_t i;
+    unsigned int n, mres;
+    uint64_t alen = ctx->len.u[0];
+    uint64_t mlen = ctx->len.u[1];
+    block128_f block = ctx->block;
+    mul128_f mul_gf = ctx->mul_gf;
+    void *key = ctx->key;
+    int bl = ctx->blocklen;
+
+    if (mlen == 0) {
+        if (alen == 0) {
+            ctx->nonce.c[0] |= 0x80;
+            (*block) (ctx->nonce.c, ctx->Zi.c, key);    // Z_1 = E_K(1 || nonce)
+        }
+        ctx->nonce.c[0] &= 0x7f;
+        (*block) (ctx->nonce.c, ctx->Yi.c, key);    // Y_1 = E_K(0 || nonce)
+    }
+
+    mlen += len;
+
+    if (mlen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) ||     // < 2^(n/2)  (len stores in bytes)
+        (sizeof(len) == 8 && mlen < len) ||
+        (mlen + alen) > ((ossl_uintmax_t)(1) << (bl * 4 - 3))) {
+            GOSTerr(GOST_F_GOST_MGM128_ENCRYPT,
+                    GOST_R_DATA_TOO_LARGE);
+            return -1;
+        }
+    ctx->len.u[1] = mlen;
+
+    mres = ctx->mres;
+
+    if (ctx->ares) {
+        /* First call to encrypt finalizes AAD */
+        memset(ctx->ACi.c + ctx->ares, 0, bl - ctx->ares);
+        (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+        mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) A_i
+        grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+            (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+        inc_counter(ctx->Zi.c, bl / 2);                         // Z_{i+1} = incr_l(Z_i)
+
+        ctx->ares = 0;
+    }
+
+    n = mres % bl;
+    // TODO: replace with full blocks processing
+    for (i = 0; i < len; ++i) {
+        if (n == 0) {
+            (*block) (ctx->Yi.c, ctx->EKi.c, key);          // E_K(Y_i)
+            inc_counter(ctx->Yi.c + bl / 2, bl / 2);        // Y_i = incr_r(Y_{i-1})
+        }
+        ctx->ACi.c[n] = out[i] = in[i] ^ ctx->EKi.c[n];     // C_i = P_i (xor) E_K(Y_i)
+        mres = n = (n + 1) % bl;
+        if (n == 0) {
+            (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+            mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) C_i
+            grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+                (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+            inc_counter(ctx->Zi.c, bl / 2);                         // Z_{i+1} = incr_l(Z_i)
+        }
+    }
+
+    ctx->mres = mres;
+    return 0;
+}
+
+int gost_mgm128_decrypt(mgm128_context *ctx, const unsigned char *in,
+                          unsigned char *out, size_t len)
+{
+    size_t i;
+    unsigned int n, mres;
+    uint64_t alen = ctx->len.u[0];
+    uint64_t mlen = ctx->len.u[1];
+    block128_f block = ctx->block;
+    mul128_f mul_gf = ctx->mul_gf;
+    void *key = ctx->key;
+    int bl = ctx->blocklen;
+
+    if (mlen == 0) {
+        ctx->nonce.c[0] &= 0x7f;
+        (*block) (ctx->nonce.c, ctx->Yi.c, key);  // Y_1 = E_K(0 || nonce)
+    }
+
+    mlen += len;
+    if (mlen > ((ossl_uintmax_t)(1) << (bl * 4 - 3)) ||     // < 2^(n/2)  (len stores in bytes)
+        (sizeof(len) == 8 && mlen < len) ||
+        (mlen + alen) > ((ossl_uintmax_t)(1) << (bl * 4 - 3))) {
+            GOSTerr(GOST_F_GOST_MGM128_DECRYPT,
+                    GOST_R_DATA_TOO_LARGE);
+            return -1;
+        }
+    ctx->len.u[1] = mlen;
+
+    mres = ctx->mres;
+
+    if (ctx->ares) {
+        /* First call to encrypt finalizes AAD */
+        memset(ctx->ACi.c + ctx->ares, 0, bl - ctx->ares);
+        (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+        mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) A_i
+        grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+            (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+        inc_counter(ctx->Zi.c, bl / 2);                         // Z_{i+1} = incr_l(Z_i)
+
+        ctx->ares = 0;
+    }
+
+    n = mres % bl;
+    // TODO: replace with full blocks processing
+    for (i = 0; i < len; ++i) {
+        uint8_t c;
+        if (n == 0) {
+            (*block) (ctx->Yi.c, ctx->EKi.c, key);      // E_K(Y_i)
+            inc_counter(ctx->Yi.c + bl / 2, bl / 2);    // Y_i = incr_r(Y_{i-1})
+        }
+        ctx->ACi.c[n] = c = in[i];
+        out[i] = c ^ ctx->EKi.c[n];             // P_i = C_i (xor) E_K(Y_i)
+        mres = n = (n + 1) % bl;
+        if (n == 0) {
+            (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+            mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) C_i
+            grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+                (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+            inc_counter(ctx->Zi.c, bl / 2);                         // Z_{i+1} = incr_l(Z_i)
+        }
+    }
+
+    ctx->mres = mres;
+    return 0;
+}
+
+int gost_mgm128_finish(mgm128_context *ctx, const unsigned char *tag,
+                         size_t len)
+{
+    uint64_t alen = ctx->len.u[0] << 3;
+    uint64_t clen = ctx->len.u[1] << 3;
+    block128_f block = ctx->block;
+    mul128_f mul_gf = ctx->mul_gf;
+    void *key = ctx->key;
+    int bl = ctx->blocklen;
+
+    if (ctx->mres || ctx->ares) {
+        /* First call to encrypt finalizes AAD/ENC */
+        memset(ctx->ACi.c + ctx->ares + ctx->mres, 0, bl - (ctx->ares + ctx->mres));
+        (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+        mul_gf(ctx->mul.u, ctx->Hi.u, ctx->ACi.u);              // H_i (x) [A_i or C_i]
+        grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+            (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+        inc_counter(ctx->Zi.c, bl / 2);                         // Z_{i+1} = incr_l(Z_i)
+    }
+
+#ifdef L_ENDIAN
+    alen = BSWAP64(alen);
+    clen = BSWAP64(clen);
+#endif
+    if (bl == 16) {
+        ctx->len.u[0] = alen;
+        ctx->len.u[1] = clen;
+    } else {
+        // TODO: check for big-endian
+        ctx->len.u[0] = (alen >> 32) | clen;
+        ctx->len.u[1] = 0;
+    }
+
+    (*block) (ctx->Zi.c, ctx->Hi.c, key);                   // H_i = E_K(Z_i)
+    mul_gf(ctx->mul.u, ctx->Hi.u, ctx->len.u);              // H_i (x) (len(A) || len(C))
+    grasshopper_plus128((grasshopper_w128_t*)ctx->sum.u,    // acc XOR
+            (grasshopper_w128_t*)ctx->sum.u, (grasshopper_w128_t*)ctx->mul.u);
+    (*block) (ctx->sum.c, ctx->tag.c, key);                 // E_K(sum)
+
+    if (tag && len <= sizeof(ctx->tag))
+        return CRYPTO_memcmp(ctx->tag.c, tag, len);         // MSB_S(E_K(sum))
+    else
+        return -1;
+}
+
+void gost_mgm128_tag(mgm128_context *ctx, unsigned char *tag, size_t len)
+{
+    gost_mgm128_finish(ctx, NULL, 0);
+    memcpy(tag, ctx->tag.c,
+           len <= sizeof(ctx->tag.c) ? len : sizeof(ctx->tag.c));
+}