BrightSide Workbench Full Report + Source Code
org.turro.push.service.HttpEce Class Reference

Public Member Functions

 HttpEce ()
 
 HttpEce (Map< String, KeyPair > keys, Map< String, String > labels)
 
byte[] encrypt (byte[] plaintext, byte[] salt, byte[] privateKey, String keyid, ECPublicKey dh, byte[] authSecret, Encoding version) throws GeneralSecurityException
 
byte[] decrypt (byte[] payload, byte[] salt, byte[] key, String keyid, Encoding version) throws InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException, NoSuchProviderException, NoSuchPaddingException
 
byte[][] parseHeader (byte[] payload)
 
byte[] decryptRecord (byte[] ciphertext, byte[] key, byte[] nonce, Encoding version) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException
 
byte[][] extractSecretAndContext (byte[] key, String keyId, ECPublicKey dh, byte[] authSecret) throws InvalidKeyException, NoSuchAlgorithmException
 
byte[][] deriveKeyAndNonce (byte[] salt, byte[] key, String keyId, ECPublicKey dh, byte[] authSecret, Encoding version, int mode) throws NoSuchAlgorithmException, InvalidKeyException
 
byte[] webpushSecret (String keyId, ECPublicKey dh, byte[] authSecret, int mode) throws NoSuchAlgorithmException, InvalidKeyException
 

Static Public Member Functions

static byte[] concat (byte[]... arrays)
 
static int combinedLength (byte[]... arrays)
 
static byte[] toByteArray (int integer, int size)
 

Static Public Attributes

static final int KEY_LENGTH = 16
 
static final int SHA_256_LENGTH = 32
 
static final int TAG_SIZE = 16
 
static final int TWO_BYTE_MAX = 65_536
 
static final String WEB_PUSH_INFO = "WebPush: info\0"
 

Static Protected Member Functions

static byte[] buildInfo (String type, byte[] context)
 
static byte[] hkdfExpand (byte[] ikm, byte[] salt, byte[] info, int length)
 

Detailed Description

Author
Lluis TurrĂ³ Cutiller lluis.nosp@m.@tur.nosp@m.ro.or.nosp@m.g

Definition at line 52 of file HttpEce.java.

Constructor & Destructor Documentation

◆ HttpEce() [1/2]

org.turro.push.service.HttpEce.HttpEce ( )

Definition at line 63 of file HttpEce.java.

63  {
64  this(new HashMap<String, KeyPair>(), new HashMap<String, String>());
65  }

◆ HttpEce() [2/2]

org.turro.push.service.HttpEce.HttpEce ( Map< String, KeyPair >  keys,
Map< String, String >  labels 
)

Definition at line 67 of file HttpEce.java.

67  {
68  this.keys = keys;
69  this.labels = labels;
70  }

Member Function Documentation

◆ buildInfo()

static byte [] org.turro.push.service.HttpEce.buildInfo ( String  type,
byte[]  context 
)
staticprotected

Future versions might require a null-terminated info string?

Parameters
type
Returns

Definition at line 212 of file HttpEce.java.

212  {
213  ByteBuffer buffer = ByteBuffer.allocate(19 + type.length() + context.length);
214 
215  buffer.put("Content-Encoding: ".getBytes(UTF_8), 0, 18);
216  buffer.put(type.getBytes(UTF_8), 0, type.length());
217  buffer.put(new byte[1], 0, 1);
218  buffer.put(context, 0, context.length);
219 
220  return buffer.array();
221  }
Here is the caller graph for this function:

◆ combinedLength()

static int org.turro.push.service.HttpEce.combinedLength ( byte...[]  arrays)
static

Definition at line 488 of file HttpEce.java.

488  {
489  int combinedLength = 0;
490 
491  for (byte[] array : arrays) {
492  if (array == null) {
493  continue;
494  }
495 
496  combinedLength += array.length;
497  }
498 
499  return combinedLength;
500  }
static int combinedLength(byte[]... arrays)
Definition: HttpEce.java:488
Here is the caller graph for this function:

◆ concat()

static byte [] org.turro.push.service.HttpEce.concat ( byte...[]  arrays)
static

Definition at line 470 of file HttpEce.java.

470  {
471  int lastPos = 0;
472 
473  byte[] combined = new byte[combinedLength(arrays)];
474 
475  for (byte[] array : arrays) {
476  if (array == null) {
477  continue;
478  }
479 
480  System.arraycopy(array, 0, combined, lastPos, array.length);
481 
482  lastPos += array.length;
483  }
484 
485  return combined;
486  }
Here is the call graph for this function:
Here is the caller graph for this function:

◆ decrypt()

byte [] org.turro.push.service.HttpEce.decrypt ( byte[]  payload,
byte[]  salt,
byte[]  key,
String  keyid,
Encoding  version 
) throws InvalidKeyException, NoSuchAlgorithmException, IllegalBlockSizeException, InvalidAlgorithmParameterException, BadPaddingException, NoSuchProviderException, NoSuchPaddingException

Decrypt the payload.

Parameters
payloadHeader and body (ciphertext)
saltMay be null when version is AES128GCM; the salt is extracted from the header.
versionAES128GCM or AESGCM.
Returns

Definition at line 126 of file HttpEce.java.

126  {
127  byte[] body;
128 
129  // Parse and strip the header
130  if (version == Encoding.AES128GCM) {
131  byte[][] header = parseHeader(payload);
132 
133  salt = header[0];
134  keyid = new String(header[2]);
135  body = header[3];
136  } else {
137  body = payload;
138  }
139 
140  // Derive key and nonce.
141  byte[][] keyAndNonce = deriveKeyAndNonce(salt, key, keyid, null, null, version, DECRYPT_MODE);
142 
143  return decryptRecord(body, keyAndNonce[0], keyAndNonce[1], version);
144  }
byte[][] parseHeader(byte[] payload)
Definition: HttpEce.java:146
byte[] decryptRecord(byte[] ciphertext, byte[] key, byte[] nonce, Encoding version)
Definition: HttpEce.java:161
byte[][] deriveKeyAndNonce(byte[] salt, byte[] key, String keyId, ECPublicKey dh, byte[] authSecret, Encoding version, int mode)
Definition: HttpEce.java:274
Here is the call graph for this function:

◆ decryptRecord()

byte [] org.turro.push.service.HttpEce.decryptRecord ( byte[]  ciphertext,
byte[]  key,
byte[]  nonce,
Encoding  version 
) throws NoSuchPaddingException, NoSuchAlgorithmException, NoSuchProviderException, InvalidAlgorithmParameterException, InvalidKeyException, BadPaddingException, IllegalBlockSizeException

Definition at line 161 of file HttpEce.java.

161  {
162  Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
163  GCMParameterSpec params = new GCMParameterSpec(TAG_SIZE * 8, nonce);
164  cipher.init(DECRYPT_MODE, new SecretKeySpec(key, "AES"), params);
165 
166  byte[] plaintext = cipher.doFinal(ciphertext);
167 
168  if (version == Encoding.AES128GCM) {
169  // Remove one byte of padding at the end
170  return Arrays.copyOfRange(plaintext, 0, plaintext.length - 1);
171  } else {
172  // Remove two bytes of padding at the start
173  return Arrays.copyOfRange(plaintext, 2, plaintext.length);
174  }
175  }
static final int TAG_SIZE
Definition: HttpEce.java:56
Here is the caller graph for this function:

◆ deriveKeyAndNonce()

byte [][] org.turro.push.service.HttpEce.deriveKeyAndNonce ( byte[]  salt,
byte[]  key,
String  keyId,
ECPublicKey  dh,
byte[]  authSecret,
Encoding  version,
int  mode 
) throws NoSuchAlgorithmException, InvalidKeyException

Definition at line 274 of file HttpEce.java.

274  {
275  byte[] secret;
276  byte[] keyInfo;
277  byte[] nonceInfo;
278 
279  if (version == Encoding.AESGCM) {
280  byte[][] secretAndContext = extractSecretAndContext(key, keyId, dh, authSecret);
281  secret = secretAndContext[0];
282 
283  keyInfo = buildInfo("aesgcm", secretAndContext[1]);
284  nonceInfo = buildInfo("nonce", secretAndContext[1]);
285  } else if (version == Encoding.AES128GCM) {
286  keyInfo = "Content-Encoding: aes128gcm\0".getBytes();
287  nonceInfo = "Content-Encoding: nonce\0".getBytes();
288 
289  secret = extractSecret(key, keyId, dh, authSecret, mode);
290  } else {
291  throw new IllegalStateException("Unknown version: " + version);
292  }
293 
294  byte[] hkdf_key = hkdfExpand(secret, salt, keyInfo, 16);
295  byte[] hkdf_nonce = hkdfExpand(secret, salt, nonceInfo, 12);
296 
297  log("key", hkdf_key);
298  log("nonce", hkdf_nonce);
299 
300  return new byte[][]{
301  hkdf_key,
302  hkdf_nonce
303  };
304  }
byte[][] extractSecretAndContext(byte[] key, String keyId, ECPublicKey dh, byte[] authSecret)
Definition: HttpEce.java:243
static byte[] hkdfExpand(byte[] ikm, byte[] salt, byte[] info, int length)
Definition: HttpEce.java:227
static byte[] buildInfo(String type, byte[] context)
Definition: HttpEce.java:212
Here is the call graph for this function:
Here is the caller graph for this function:

◆ encrypt()

byte [] org.turro.push.service.HttpEce.encrypt ( byte[]  plaintext,
byte[]  salt,
byte[]  privateKey,
String  keyid,
ECPublicKey  dh,
byte[]  authSecret,
Encoding  version 
) throws GeneralSecurityException

Encrypt the given plaintext.

Parameters
plaintextPayload to encrypt.
saltA random 16-byte buffer
privateKeyA private key to encrypt this message with (Web Push: the local private key)
keyidAn identifier for the local key. Only applies to AESGCM. For AES128GCM, the header contains the keyid.
dhAn Elliptic curve Diffie-Hellman public privateKey on the P-256 curve (Web Push: the user's keys.p256dh)
authSecretAn authentication secret (Web Push: the user's keys.auth)
version
Returns
Exceptions
GeneralSecurityException

Definition at line 88 of file HttpEce.java.

88  {
89  log("encrypt", plaintext);
90 
91  byte[][] keyAndNonce = deriveKeyAndNonce(salt, privateKey, keyid, dh, authSecret, version, ENCRYPT_MODE);
92  byte[] key = keyAndNonce[0];
93  byte[] nonce = keyAndNonce[1];
94 
95  // Note: Cipher adds the tag to the end of the ciphertext
96  Cipher cipher = Cipher.getInstance("AES/GCM/NoPadding", "BC");
97  GCMParameterSpec params = new GCMParameterSpec(TAG_SIZE * 8, nonce);
98  cipher.init(ENCRYPT_MODE, new SecretKeySpec(key, "AES"), params);
99 
100  // For AES128GCM suffix {0x02}, for AESGCM prefix {0x00, 0x00}.
101  if (version == Encoding.AES128GCM) {
102  byte[] header = buildHeader(salt, keyid);
103  log("header", header);
104 
105  byte[] padding = new byte[]{2};
106  log("padding", padding);
107 
108  byte[][] encrypted = {cipher.update(plaintext), cipher.update(padding), cipher.doFinal()};
109  log("encrypted", concat(encrypted));
110 
111  return log("ciphertext", concat(header, concat(encrypted)));
112  } else {
113  return concat(cipher.update(new byte[2]), cipher.doFinal(plaintext));
114  }
115  }
static byte[] concat(byte[]... arrays)
Definition: HttpEce.java:470

◆ extractSecretAndContext()

byte [][] org.turro.push.service.HttpEce.extractSecretAndContext ( byte[]  key,
String  keyId,
ECPublicKey  dh,
byte[]  authSecret 
) throws InvalidKeyException, NoSuchAlgorithmException

Definition at line 243 of file HttpEce.java.

243  {
244  byte[] secret = null;
245  byte[] context = null;
246 
247  if (key != null) {
248  secret = key;
249  if (secret.length != KEY_LENGTH) {
250  throw new IllegalStateException("An explicit key must be " + KEY_LENGTH + " bytes.");
251  }
252  } else if (dh != null) {
253  byte[][] bytes = extractDH(keyId, dh);
254  secret = bytes[0];
255  context = bytes[1];
256  } else if (keyId != null) {
257  secret = keys.get(keyId).getPublic().getEncoded();
258  }
259 
260  if (secret == null) {
261  throw new IllegalStateException("Unable to determine key.");
262  }
263 
264  if (authSecret != null) {
265  secret = hkdfExpand(secret, authSecret, buildInfo("auth", new byte[0]), SHA_256_LENGTH);
266  }
267 
268  return new byte[][]{
269  secret,
270  context
271  };
272  }
static final int SHA_256_LENGTH
Definition: HttpEce.java:55
static final int KEY_LENGTH
Definition: HttpEce.java:54
Here is the call graph for this function:
Here is the caller graph for this function:

◆ hkdfExpand()

static byte [] org.turro.push.service.HttpEce.hkdfExpand ( byte[]  ikm,
byte[]  salt,
byte[]  info,
int  length 
)
staticprotected

Convenience method for computing the HMAC Key Derivation Function. The real work is offloaded to BouncyCastle.

Definition at line 227 of file HttpEce.java.

227  {
228  log("salt", salt);
229  log("ikm", ikm);
230  log("info", info);
231 
232  HKDFBytesGenerator hkdf = new HKDFBytesGenerator(new SHA256Digest());
233  hkdf.init(new HKDFParameters(ikm, salt, info));
234 
235  byte[] okm = new byte[length];
236  hkdf.generateBytes(okm, 0, length);
237 
238  log("expand", okm);
239 
240  return okm;
241  }
Here is the caller graph for this function:

◆ parseHeader()

byte [][] org.turro.push.service.HttpEce.parseHeader ( byte[]  payload)

Definition at line 146 of file HttpEce.java.

146  {
147  byte[] salt = Arrays.copyOfRange(payload, 0, KEY_LENGTH);
148  byte[] recordSize = Arrays.copyOfRange(payload, KEY_LENGTH, 20);
149  int keyIdLength = Arrays.copyOfRange(payload, 20, 21)[0];
150  byte[] keyId = Arrays.copyOfRange(payload, 21, 21 + keyIdLength);
151  byte[] body = Arrays.copyOfRange(payload, 21 + keyIdLength, payload.length);
152 
153  return new byte[][]{
154  salt,
155  recordSize,
156  keyId,
157  body
158  };
159  }
Here is the caller graph for this function:

◆ toByteArray()

static byte [] org.turro.push.service.HttpEce.toByteArray ( int  integer,
int  size 
)
static

Definition at line 502 of file HttpEce.java.

502  {
503  ByteBuffer buffer = ByteBuffer.allocate(size);
504  buffer.putInt(integer);
505 
506  return buffer.array();
507  }

◆ webpushSecret()

byte [] org.turro.push.service.HttpEce.webpushSecret ( String  keyId,
ECPublicKey  dh,
byte[]  authSecret,
int  mode 
) throws NoSuchAlgorithmException, InvalidKeyException

Combine Shared and Authentication Secrets

See https://tools.ietf.org/html/draft-ietf-webpush-encryption-09#section-3.3.

Parameters
keyId
dh
authSecret
mode
Returns
Exceptions
NoSuchAlgorithmException
InvalidKeyException

Definition at line 341 of file HttpEce.java.

341  {
342  ECPublicKey senderPubKey;
343  ECPublicKey remotePubKey;
344  ECPublicKey receiverPubKey;
345 
346  if (mode == ENCRYPT_MODE) {
347  senderPubKey = getPublicKey(keyId);
348  remotePubKey = dh;
349  receiverPubKey = dh;
350  } else if (mode == DECRYPT_MODE) {
351  remotePubKey = getPublicKey(keyId);
352  senderPubKey = remotePubKey;
353  receiverPubKey = dh;
354  } else {
355  throw new IllegalArgumentException("Unsupported mode: " + mode);
356  }
357 
358  log("remote pubkey", ServerKeys.encode(remotePubKey));
359  log("sender pubkey", ServerKeys.encode(senderPubKey));
360  log("receiver pubkey", ServerKeys.encode(receiverPubKey));
361 
362  KeyAgreement keyAgreement = KeyAgreement.getInstance("ECDH");
363  keyAgreement.init(getPrivateKey(keyId));
364  keyAgreement.doPhase(remotePubKey, true);
365  byte[] secret = keyAgreement.generateSecret();
366 
367  byte[] ikm = secret;
368  byte[] salt = authSecret;
369  byte[] info = concat(WEB_PUSH_INFO.getBytes(), ServerKeys.encode(receiverPubKey), ServerKeys.encode(senderPubKey));
370 
371  return hkdfExpand(ikm, salt, info, SHA_256_LENGTH);
372  }
static final String WEB_PUSH_INFO
Definition: HttpEce.java:58
Here is the call graph for this function:

Member Data Documentation

◆ KEY_LENGTH

final int org.turro.push.service.HttpEce.KEY_LENGTH = 16
static

Definition at line 54 of file HttpEce.java.

◆ SHA_256_LENGTH

final int org.turro.push.service.HttpEce.SHA_256_LENGTH = 32
static

Definition at line 55 of file HttpEce.java.

◆ TAG_SIZE

final int org.turro.push.service.HttpEce.TAG_SIZE = 16
static

Definition at line 56 of file HttpEce.java.

◆ TWO_BYTE_MAX

final int org.turro.push.service.HttpEce.TWO_BYTE_MAX = 65_536
static

Definition at line 57 of file HttpEce.java.

◆ WEB_PUSH_INFO

final String org.turro.push.service.HttpEce.WEB_PUSH_INFO = "WebPush: info\0"
static

Definition at line 58 of file HttpEce.java.


The documentation for this class was generated from the following file: