/* After encryption this function is used to retreive the authentication tag */ void psAesGetGCMTag(psAesGcm_t *ctx, uint8_t tagBytes, unsigned char tag[AES_BLOCKLEN]) { unsigned char *pt, *ct; psGhashFinal(ctx); /* Encrypt authentication tag */ ctx->OutputBufferCount = 0; ct = tag; pt = (unsigned char*)ctx->TagTemp; while (tagBytes) { if (ctx->OutputBufferCount == 0) { ctx->OutputBufferCount = 16; /* Initial IV has been set aside in IV */ psAesEncryptBlock(&ctx->key, ctx->IV, ctx->CtrBlock); /* No need to increment since we know tag bytes will never be larger than 16 */ } *(ct++) = *(pt++) ^ ctx->CtrBlock[16 - ctx->OutputBufferCount]; tagBytes--; ctx->OutputBufferCount--; } }
/** Make the PRNG ready to read from and to reseed when desired @param prng The PRNG to seed */ int32 psYarrowReseed(psYarrow_t *ctx) { const unsigned char *IV; int32 keylen, ctr_mode, x, err; IV = ctx->pool; keylen = AESBLOCKSIZE; /* Can only use 16 bytes for the AES key */ ctr_mode = CTR_COUNTER_LITTLE_ENDIAN; /* little endian counter */ /* ctrlen == counter width */ ctx->ctrlen = (ctr_mode & 255) ? (ctr_mode & 255) : AESBLOCKSIZE; if (ctx->ctrlen > AESBLOCKSIZE) { return PS_ARG_FAIL; } if ((ctr_mode & 0x1000) == CTR_COUNTER_BIG_ENDIAN) { ctx->ctrlen = AESBLOCKSIZE - ctx->ctrlen; } /* setup cipher */ if ((err = psAesInitKey(ctx->pool, keylen, &ctx->key)) != PS_SUCCESS) { return err; } /* copy ctr */ ctx->padlen = 0; ctx->mode = ctr_mode & 0x1000; for (x = 0; x < ctx->blocklen; x++) { ctx->ctr[x] = IV[x]; } /* increment the IV */ if (ctx->mode == CTR_COUNTER_LITTLE_ENDIAN) { /* little-endian */ for (x = 0; x < ctx->ctrlen; x++) { ctx->ctr[x] = (ctx->ctr[x] + (unsigned char)1) & (unsigned char)255; if (ctx->ctr[x] != (unsigned char)0) { break; } } } else { /* big-endian */ for (x = ctx->blocklen-1; x >= ctx->ctrlen; x--) { ctx->ctr[x] = (ctx->ctr[x] + (unsigned char)1) & (unsigned char)255; if (ctx->ctr[x] != (unsigned char)0) { break; } } } psAesEncryptBlock(ctx->ctr, ctx->pad, &ctx->key); return PS_SUCCESS; }
/** Read from the PRNG @param out Destination @param outlen Length of output @param ctx The active PRNG to read from @return Number of octets read */ uint32 psYarrowRead(unsigned char *out, uint32 outlen, psYarrow_t *ctx) { unsigned char *pt, *ct; int32 x; uint32 len; /* put out in predictable state first */ memset(out, 0x0, outlen); len = outlen; pt = ct = out; /* is blocklen/padlen valid? */ if (ctx->blocklen < 1 || ctx->blocklen > (int32)sizeof(ctx->ctr) || ctx->padlen < 0 || ctx->padlen > (int32)sizeof(ctx->pad)) { return 0; } while (outlen) { /* is the pad empty? */ if (ctx->padlen == ctx->blocklen) { /* increment counter */ if (ctx->mode == CTR_COUNTER_LITTLE_ENDIAN) { /* little-endian */ for (x = 0; x < ctx->ctrlen; x++) { ctx->ctr[x] = (ctx->ctr[x] + (unsigned char)1) & (unsigned char)255; if (ctx->ctr[x] != (unsigned char)0) { break; } } } else { /* big-endian */ for (x = ctx->blocklen-1; x >= ctx->ctrlen; x--) { ctx->ctr[x] = (ctx->ctr[x] + (unsigned char)1) & (unsigned char)255; if (ctx->ctr[x] != (unsigned char)0) { break; } } } /* encrypt new pad and reset */ psAesEncryptBlock(ctx->ctr, ctx->pad, &ctx->key); ctx->padlen = 0; } *ct++ = *pt++ ^ ctx->pad[ctx->padlen++]; --outlen; } return len; }
/* Initialize an AES GCM context */ int32_t psAesInitGCM(psAesGcm_t *ctx, const unsigned char key[AES_MAXKEYLEN], uint8_t keylen) { int32_t rc; unsigned char blockIn[16] = { 0 }; memset(ctx, 0x0, sizeof(psAesGcm_t)); /* GCM always uses AES in ENCRYPT block mode, even for decrypt */ rc = psAesInitBlockKey(&ctx->key, key, keylen, PS_AES_ENCRYPT); if (rc < 0) { return rc; } psAesEncryptBlock(&ctx->key, blockIn, ctx->gInit); return PS_SUCCESS; }
/* Internal gcm crypt function that uses direction to determine what gets fed to the GHASH update */ static void psAesEncryptGCMx(psAesGcm_t *ctx, const unsigned char *pt, unsigned char *ct, uint32_t len, int8_t direction) { unsigned char *ctStart; uint32_t outLen; int x; /* Must be signed due to positive check below */ outLen = len; ctStart = ct; if (direction == 0) { psGhashUpdate(ctx, pt, len, GHASH_DATATYPE_CIPHERTEXT); } while (len) { if (ctx->OutputBufferCount == 0) { ctx->OutputBufferCount = 16; psAesEncryptBlock(&ctx->key, ctx->EncCtr, ctx->CtrBlock); /* CTR incr */ for (x = (AES_BLOCKLEN - 1); x >= 0; x--) { ctx->EncCtr[x] = (ctx->EncCtr[x] + (unsigned char)1) & (unsigned char)255; if (ctx->EncCtr[x] != (unsigned char)0) { break; } } } *(ct++) = *(pt++) ^ ctx->CtrBlock[16 - ctx->OutputBufferCount]; len--; ctx->OutputBufferCount--; } if (direction == 1) { psGhashUpdate(ctx, ctStart, outLen, GHASH_DATATYPE_CIPHERTEXT); } }