java.lang.Object
net.i2p.router.crypto.ElGamalAESEngine

public final class ElGamalAESEngineextends Object
Handles the actual ElGamal+AES encryption and decryption scenarios using the supplied keys and data.

No, this does not extend AESEngine or CryptixAESEngine.

  • Field Details

    • _log

      private final Log _log
      Logger for this class.
    • MIN_ENCRYPTED_SIZE

      private static final int MIN_ENCRYPTED_SIZE
      Smallest possible encrypted result, in bytes.
      See Also:
    • _context

      private final I2PAppContext _context
      The context supplying the random.
    • MAX_TAGS_RECEIVED

      public static final int MAX_TAGS_RECEIVED
      Enforced since release 0.6.
      See Also:
    • ELG_CLEARTEXT_LENGTH

      private static final int ELG_CLEARTEXT_LENGTH
      Payload length a single ElGamal block can carry, in bytes.
      See Also:
    • ELG_ENCRYPTED_LENGTH

      private static final int ELG_ENCRYPTED_LENGTH
      Length of one encrypted ElGamal block, in bytes.
      See Also:
  • Constructor Details

    • ElGamalAESEngine

      public ElGamalAESEngine(I2PAppContext ctx)
      Creates engine with given context and initializes crypto frequency stats.
      Parameters:
      ctx - the application context supplying the log and stat managers
  • Method Details

    • decrypt

      public byte[] decrypt(byte[] data, PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException
      Decrypt the message using the given private key and using tags from the specified key manager. This works according to the ElGamal+AES algorithm in the data structure spec.

      Warning - use the correct SessionKeyManager. Clients should instantiate their own. Clients using I2PAppContext.sessionKeyManager() may be correlated with the router, unless you are careful to use different keys.

      Parameters:
      data - the encrypted data to decrypt, at least 80 bytes
      targetPrivateKey - the ElGamal private key matching the target's public key
      keyManager - the session key manager supplying the tags to try
      Returns:
      decrypted data or null on failure
      Throws:
      DataFormatException - if the payload cannot be parsed once decrypted
    • decryptFast

      public byte[] decryptFast(byte[] data, PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException
      Tags only. For MuxedEngine use only.
      Parameters:
      data - the encrypted data to decrypt, at least 80 bytes
      targetPrivateKey - the ElGamal private key matching the target's public key
      keyManager - the session key manager supplying the tag to look up
      Returns:
      decrypted data or null on failure
      Throws:
      DataFormatException - if the payload cannot be parsed once decrypted
    • decryptSlow

      public byte[] decryptSlow(byte[] data, PrivateKey targetPrivateKey, SessionKeyManager keyManager) throws DataFormatException
      Full ElG only. For MuxedEngine use only.
      Parameters:
      data - the encrypted data to decrypt, at least 514 bytes
      targetPrivateKey - the ElGamal private key matching the target's public key
      keyManager - the session key manager recording any tags the payload carries
      Returns:
      decrypted data or null on failure
      Throws:
      DataFormatException - if the payload cannot be parsed once decrypted
    • decryptNewSession

      private byte[] decryptNewSession(byte[] data, PrivateKey targetPrivateKey, Set<SessionTag> foundTags, SessionKey usedKey, SessionKey foundKey)
      Scenario 1: Begin with 222 bytes, ElG encrypted, containing:
      - 32 byte SessionKey
      - 32 byte pre-IV for the AES
      - 158 bytes of random padding
      
      After encryption, the ElG section is 514 bytes long. Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV, using the decryptAESBlock method & structure.
      Parameters:
      data - the encrypted message: the ElGamal-encrypted section first, then the AES-encrypted section
      targetPrivateKey - our private key used to ElGamal-decrypt the session key and pre-IV
      foundTags - set which is filled with any sessionTags found during decryption
      usedKey - out parameter. Data must be unset when called; usedKey.setData() will be called by this method on success.
      foundKey - out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
      Returns:
      null if decryption fails
    • decryptExistingSession

      private byte[] decryptExistingSession(byte[] data, SessionKey key, PrivateKey targetPrivateKey, Set<SessionTag> foundTags, SessionKey usedKey, SessionKey foundKey) throws DataFormatException
      Scenario 2: The data begins with 32 byte session tag, which also serves as the preIV. Then decrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
      - 2 byte integer specifying the # of session tags
      - that many 32 byte session tags
      - 4 byte integer specifying data.length
      - SHA256 of data
      - 1 byte flag that, if == 1, is followed by a new SessionKey
      - data
      - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
      

      If anything doesn't match up in decryption, it falls back to decryptNewSession

      Parameters:
      data - the encrypted message, beginning with the 32-byte session tag that also serves as the pre-IV
      key - the existing session key to decrypt with
      targetPrivateKey - our private key for the decryptNewSession fallback if the existing key fails
      foundTags - set which is filled with any sessionTags found during decryption
      usedKey - out parameter. Data must be unset when called; usedKey.setData() will be called by this method on success.
      foundKey - out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
      Returns:
      decrypted data or null on failure
      Throws:
      DataFormatException - declared on the method; decryption failures are caught and reported as a null return
    • decryptAESBlock

      byte[] decryptAESBlock(byte[] encrypted, int offset, int encryptedLen, SessionKey key, byte[] iv, byte[] sentTag, Set<SessionTag> foundTags, SessionKey foundKey)
      Decrypt the AES data with the session key and IV. The result should be:
      - 2 byte integer specifying the # of session tags
      - that many 32 byte session tags
      - 4 byte integer specifying data.length
      - SHA256 of data
      - 1 byte flag that, if == 1, is followed by a new SessionKey
      - data
      - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
      

      If anything doesn't match up in decryption, return null. Otherwise, return the decrypted data and update the session as necessary. If the sentTag is not null, consume it, but if it is null, record the keys, etc as part of a new session.

      Parameters:
      encrypted - the encrypted block
      offset - the offset of the encrypted data in the buffer
      encryptedLen - the length of the encrypted data
      key - the session key
      iv - the AES initialization vector
      sentTag - the tag sent with this message, or null to create a new session
      foundTags - set which is filled with any sessionTags found during decryption
      foundKey - out parameter. Data must be unset when called; may be filled with a new sessionKey found during decryption
      Returns:
      decrypted data or null on failure
    • encrypt

      public byte[] encrypt(byte[] data, PublicKey target, SessionKey key, Set<SessionTag> tagsForDelivery, SessionTag currentTag, SessionKey newKey, long paddedSize)
      Encrypt the unencrypted data to the target. The total size returned will be no less than the paddedSize parameter, but may be more. This method uses the ElGamal+AES algorithm in the data structure spec.

      Unused externally, only called by below (i.e. newKey is always null).

      Parameters:
      data - the plaintext to encrypt
      target - public key to which the data should be encrypted, must be ELGAMAL_2048. May be null if key and currentTag are non-null.
      key - session key to use during encryption
      tagsForDelivery - session tags to be associated with the key (or newKey if specified), or null; 200 max enforced at receiver
      currentTag - sessionTag to use, or null if it should use ElG (i.e. new session)
      newKey - key to be delivered to the target, with which the tagsForDelivery should be associated, or null
      paddedSize - minimum size in bytes of the body after padding it (if less than the body's real size, no bytes are appended but the body is not truncated)
      Returns:
      the encrypted data, at least paddedSize bytes and up to 514 bytes larger
      Throws:
      IllegalArgumentException - on bad target EncType
    • encrypt

      public byte[] encrypt(byte[] data, PublicKey target, SessionKey key, Set<SessionTag> tagsForDelivery, SessionTag currentTag, long paddedSize)
      Encrypt the data to the target using the given key and deliver the specified tags No new session key This is the one called from GarlicMessageBuilder and is the primary entry point.

      Re: padded size: The AES block adds at least 39 bytes of overhead to the data, and that is included in the minimum size calculation.

      In the router, we always use garlic messages. A garlic message with a single clove and zero data is about 84 bytes, so that's 123 bytes minimum. So any paddingSize <= 128 is a no-op as every message will be at least 128 bytes (Streaming, if used, adds more overhead).

      Outside the router, with a client using its own message format, the minimum size is 48, so any paddingSize <= 48 is a no-op.

      Not included in the minimum is a 32-byte session tag for an existing session, or a 514-byte ElGamal block and several 32-byte session tags for a new session. So the returned encrypted data will be at least 32 bytes larger than paddedSize.

      Parameters:
      data - the plaintext to encrypt
      target - public key to which the data should be encrypted, must be ELGAMAL_2048. May be null if key and currentTag are non-null.
      key - session key to use during encryption
      tagsForDelivery - session tags to be associated with the key or null; 200 max enforced at receiver
      currentTag - sessionTag to use, or null if it should use ElG (i.e. new session)
      paddedSize - minimum size in bytes of the body after padding it (if less than the body's real size, no bytes are appended but the body is not truncated)
      Returns:
      the encrypted data, at least paddedSize bytes and up to 514 bytes larger
      Throws:
      IllegalArgumentException - on bad target EncType
    • encryptNewSession

      private byte[] encryptNewSession(byte[] data, PublicKey target, SessionKey key, Set<SessionTag> tagsForDelivery, SessionKey newKey, long paddedSize)
      Scenario 1: Begin with 222 bytes, ElG encrypted, containing:
      - 32 byte SessionKey
      - 32 byte pre-IV for the AES
      - 158 bytes of random padding
      
      After encryption, the ElG section is 514 bytes long. Then encrypt the following with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
      - 2 byte integer specifying the # of session tags
      - that many 32 byte session tags
      - 4 byte integer specifying data.length
      - SHA256 of data
      - 1 byte flag that, if == 1, is followed by a new SessionKey
      - data
      - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
      
      Parameters:
      data - the plaintext to encrypt into the AES section
      target - public key of the recipient, must be ELGAMAL_2048
      key - the new session key, delivered to the target in the ElGamal section and used for the AES section
      tagsForDelivery - session tags to be associated with the key or null; 200 max enforced at receiver
      newKey - a further session key to deliver to the target, or null for no rekey block
      paddedSize - minimum size in bytes of the AES section after padding it
      Returns:
      the 514-byte ElGamal section followed by the encrypted AES section
    • encryptExistingSession

      private byte[] encryptExistingSession(byte[] data, SessionKey key, Set<SessionTag> tagsForDelivery, SessionTag currentTag, SessionKey newKey, long paddedSize)
      Scenario 2: Begin with 32 byte session tag, which also serves as the preIV. Then encrypt with AES using that session key and the first 16 bytes of the SHA256 of the pre-IV:
      - 2 byte integer specifying the # of session tags
      - that many 32 byte session tags
      - 4 byte integer specifying data.length
      - SHA256 of data
      - 1 byte flag that, if == 1, is followed by a new SessionKey
      - data
      - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
      
      Parameters:
      data - the plaintext to encrypt into the AES section
      key - the existing session key used for the AES section
      tagsForDelivery - session tags to be associated with the key or null; 200 max enforced at receiver
      currentTag - the session tag identifying the existing session, also used as the pre-IV
      newKey - a new session key to deliver to the target, or null for no rekey block
      paddedSize - minimum size in bytes of the AES section after padding it
      Returns:
      the encrypted AES section with the 32-byte session tag copied over the reserved prefix
    • halfHash

      private byte[] halfHash(byte[] preIV)
      Generate the first 16 bytes of the SHA-256 hash of the data.

      Here we are careful to use the SHA256Generator method that does not generate a Hash object or cache the result.

      Parameters:
      preIV - the 32 byte pre-IV to hash, from the byte cache; the caller should call SimpleByteCache.release(byte[]) on it after use
      Returns:
      a 16 byte array from the byte cache; the caller should call SimpleByteCache.release(byte[]) on it after use
    • encryptAESBlock

      final byte[] encryptAESBlock(byte[] data, SessionKey key, byte[] iv, Set<SessionTag> tagsForDelivery, SessionKey newKey, long paddedSize)
      For both scenarios, this method encrypts the AES area using the given key, iv and making sure the resulting data is at least as long as the paddedSize and also mod 16 bytes. The contents of the encrypted data is:
      - 2 byte integer specifying the # of session tags
      - that many 32 byte session tags
      - 4 byte integer specifying data.length
      - SHA256 of data
      - 1 byte flag that, if == 1, is followed by a new SessionKey
      - data
      - random bytes, padding the total size to greater than paddedSize with a mod 16 = 0
      

      Note: package private for ElGamalTest.testAES()

      Parameters:
      data - the plaintext to encrypt into the AES area
      key - the AES session key to encrypt with
      iv - the 16 byte AES initialization vector
      tagsForDelivery - session tags to be associated with the key or null; 200 max enforced at receiver
      newKey - key to deliver to the target, or null for no new key block
      paddedSize - minimum size in bytes of the AES area after padding
      Returns:
      the encrypted AES area, or null on encryption failure
    • encryptAESBlock

      private final byte[] encryptAESBlock(byte[] data, SessionKey key, byte[] iv, Set<SessionTag> tagsForDelivery, SessionKey newKey, long paddedSize, int prefixBytes)
      Encrypt the AES area with the given key and IV, reserving the specified number of bytes at the beginning of the result for the session tag prefix.
      Parameters:
      data - the plaintext to encrypt into the AES area
      key - the AES session key to encrypt with
      iv - the 16 byte AES initialization vector
      tagsForDelivery - session tags to be associated with the key or null; 200 max enforced at receiver
      newKey - key to deliver to the target, or null for no new key block
      paddedSize - minimum size in bytes of the AES area after padding
      prefixBytes - number of bytes reserved unwritten at the start of the result for the caller to prepend the session tag, 0 if none
      Returns:
      the encrypted AES area, preceded by prefixBytes unwritten bytes