Encryption and decryption on streams using AES CBC + HMAC SHA
Clash Royale CLAN TAG#URR8PPP
.everyoneloves__top-leaderboard:empty,.everyoneloves__mid-leaderboard:empty margin-bottom:0;
up vote
7
down vote
favorite
The goal of the code below is to:
- Support authenticated encryption using AES in the CBC mode and using HMAC SHA.
- Support encryption and decryption of data of size larger than memory (potentially).
Please criticize :)
public class AesCbcThenHmacSha : IDisposable
private readonly Func<HMAC> hmacShaTransformFactory;
private readonly AesCryptoServiceProvider aesProvider;
private bool disposed;
public AesCbcThenHmacSha(byte aesKey, byte aesIv, byte macKey)
if (aesKey == null) throw new ArgumentNullException(nameof(aesKey));
if (aesIv == null) throw new ArgumentNullException(nameof(aesIv));
if (macKey == null) throw new ArgumentNullException(nameof(macKey));
// key size
// --------
// the key size property is set based on the provided
// key. the validation of length is also performed
// by the AesCryptoServiceProvider.
// block size
// ----------
// there is only one valid block size for AES and
// it does not need to be specified explicitly. it
// is 128 by default (and cannot be changed to any
// different value).
// initialization vector
// ---------------------
// the iv has to match the block size, so validation
// of the length is also performed beneath.
this.aesProvider = new AesCryptoServiceProvider
Key = aesKey,
IV = aesIv,
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC
;
this.hmacShaTransformFactory = CreateHmacShaTransform(macKey);
private static Func<HMAC> CreateHmacShaTransform(byte key)
var bits = key.Length * 8;
switch (bits)
case 256: return () => new HMACSHA256(key);
case 384: return () => new HMACSHA384(key);
case 512: return () => new HMACSHA512(key);
default:
throw new ArgumentException($"the bits-bit HMAC SHA is not supported");
public void Encrypt(Stream inputStream, Stream outputStream, out byte hmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
var hmacShaTransform = this.hmacShaTransformFactory.Invoke();
var aesTransform = this.aesProvider.CreateEncryptor();
var hmacShaStream = new CryptoStream(outputStream, hmacShaTransform, CryptoStreamMode.Write);
var aesStream = new CryptoStream(hmacShaStream, aesTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
hmacSha = hmacShaTransform.Hash;
hmacShaTransform.Dispose();
public void Decrypt(Stream inputStream, Stream outputStream, byte expectedHmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
if (expectedHmacSha == null) throw new ArgumentNullException(nameof(expectedHmacSha));
using (var hmacShaTransform = this.hmacShaTransformFactory.Invoke())
using (var hmacShaStream = new CryptoStream(Stream.Null, hmacShaTransform, CryptoStreamMode.Write))
inputStream.CopyTo(hmacShaStream);
hmacShaStream.FlushFinalBlock();
var hmacSha = hmacShaTransform.Hash;
if (!hmacSha.SequenceEqual(expectedHmacSha))
throw "message authentication code does not match the expected value".Security();
inputStream.Position = 0;
var aesCryptoTransform = this.aesProvider.CreateDecryptor();
var aesStream = new CryptoStream(outputStream, aesCryptoTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
public void Dispose()
if (!this.disposed)
this.aesProvider.Dispose();
this.disposed = true;
c# .net aes
add a comment |Â
up vote
7
down vote
favorite
The goal of the code below is to:
- Support authenticated encryption using AES in the CBC mode and using HMAC SHA.
- Support encryption and decryption of data of size larger than memory (potentially).
Please criticize :)
public class AesCbcThenHmacSha : IDisposable
private readonly Func<HMAC> hmacShaTransformFactory;
private readonly AesCryptoServiceProvider aesProvider;
private bool disposed;
public AesCbcThenHmacSha(byte aesKey, byte aesIv, byte macKey)
if (aesKey == null) throw new ArgumentNullException(nameof(aesKey));
if (aesIv == null) throw new ArgumentNullException(nameof(aesIv));
if (macKey == null) throw new ArgumentNullException(nameof(macKey));
// key size
// --------
// the key size property is set based on the provided
// key. the validation of length is also performed
// by the AesCryptoServiceProvider.
// block size
// ----------
// there is only one valid block size for AES and
// it does not need to be specified explicitly. it
// is 128 by default (and cannot be changed to any
// different value).
// initialization vector
// ---------------------
// the iv has to match the block size, so validation
// of the length is also performed beneath.
this.aesProvider = new AesCryptoServiceProvider
Key = aesKey,
IV = aesIv,
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC
;
this.hmacShaTransformFactory = CreateHmacShaTransform(macKey);
private static Func<HMAC> CreateHmacShaTransform(byte key)
var bits = key.Length * 8;
switch (bits)
case 256: return () => new HMACSHA256(key);
case 384: return () => new HMACSHA384(key);
case 512: return () => new HMACSHA512(key);
default:
throw new ArgumentException($"the bits-bit HMAC SHA is not supported");
public void Encrypt(Stream inputStream, Stream outputStream, out byte hmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
var hmacShaTransform = this.hmacShaTransformFactory.Invoke();
var aesTransform = this.aesProvider.CreateEncryptor();
var hmacShaStream = new CryptoStream(outputStream, hmacShaTransform, CryptoStreamMode.Write);
var aesStream = new CryptoStream(hmacShaStream, aesTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
hmacSha = hmacShaTransform.Hash;
hmacShaTransform.Dispose();
public void Decrypt(Stream inputStream, Stream outputStream, byte expectedHmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
if (expectedHmacSha == null) throw new ArgumentNullException(nameof(expectedHmacSha));
using (var hmacShaTransform = this.hmacShaTransformFactory.Invoke())
using (var hmacShaStream = new CryptoStream(Stream.Null, hmacShaTransform, CryptoStreamMode.Write))
inputStream.CopyTo(hmacShaStream);
hmacShaStream.FlushFinalBlock();
var hmacSha = hmacShaTransform.Hash;
if (!hmacSha.SequenceEqual(expectedHmacSha))
throw "message authentication code does not match the expected value".Security();
inputStream.Position = 0;
var aesCryptoTransform = this.aesProvider.CreateDecryptor();
var aesStream = new CryptoStream(outputStream, aesCryptoTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
public void Dispose()
if (!this.disposed)
this.aesProvider.Dispose();
this.disposed = true;
c# .net aes
add a comment |Â
up vote
7
down vote
favorite
up vote
7
down vote
favorite
The goal of the code below is to:
- Support authenticated encryption using AES in the CBC mode and using HMAC SHA.
- Support encryption and decryption of data of size larger than memory (potentially).
Please criticize :)
public class AesCbcThenHmacSha : IDisposable
private readonly Func<HMAC> hmacShaTransformFactory;
private readonly AesCryptoServiceProvider aesProvider;
private bool disposed;
public AesCbcThenHmacSha(byte aesKey, byte aesIv, byte macKey)
if (aesKey == null) throw new ArgumentNullException(nameof(aesKey));
if (aesIv == null) throw new ArgumentNullException(nameof(aesIv));
if (macKey == null) throw new ArgumentNullException(nameof(macKey));
// key size
// --------
// the key size property is set based on the provided
// key. the validation of length is also performed
// by the AesCryptoServiceProvider.
// block size
// ----------
// there is only one valid block size for AES and
// it does not need to be specified explicitly. it
// is 128 by default (and cannot be changed to any
// different value).
// initialization vector
// ---------------------
// the iv has to match the block size, so validation
// of the length is also performed beneath.
this.aesProvider = new AesCryptoServiceProvider
Key = aesKey,
IV = aesIv,
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC
;
this.hmacShaTransformFactory = CreateHmacShaTransform(macKey);
private static Func<HMAC> CreateHmacShaTransform(byte key)
var bits = key.Length * 8;
switch (bits)
case 256: return () => new HMACSHA256(key);
case 384: return () => new HMACSHA384(key);
case 512: return () => new HMACSHA512(key);
default:
throw new ArgumentException($"the bits-bit HMAC SHA is not supported");
public void Encrypt(Stream inputStream, Stream outputStream, out byte hmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
var hmacShaTransform = this.hmacShaTransformFactory.Invoke();
var aesTransform = this.aesProvider.CreateEncryptor();
var hmacShaStream = new CryptoStream(outputStream, hmacShaTransform, CryptoStreamMode.Write);
var aesStream = new CryptoStream(hmacShaStream, aesTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
hmacSha = hmacShaTransform.Hash;
hmacShaTransform.Dispose();
public void Decrypt(Stream inputStream, Stream outputStream, byte expectedHmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
if (expectedHmacSha == null) throw new ArgumentNullException(nameof(expectedHmacSha));
using (var hmacShaTransform = this.hmacShaTransformFactory.Invoke())
using (var hmacShaStream = new CryptoStream(Stream.Null, hmacShaTransform, CryptoStreamMode.Write))
inputStream.CopyTo(hmacShaStream);
hmacShaStream.FlushFinalBlock();
var hmacSha = hmacShaTransform.Hash;
if (!hmacSha.SequenceEqual(expectedHmacSha))
throw "message authentication code does not match the expected value".Security();
inputStream.Position = 0;
var aesCryptoTransform = this.aesProvider.CreateDecryptor();
var aesStream = new CryptoStream(outputStream, aesCryptoTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
public void Dispose()
if (!this.disposed)
this.aesProvider.Dispose();
this.disposed = true;
c# .net aes
The goal of the code below is to:
- Support authenticated encryption using AES in the CBC mode and using HMAC SHA.
- Support encryption and decryption of data of size larger than memory (potentially).
Please criticize :)
public class AesCbcThenHmacSha : IDisposable
private readonly Func<HMAC> hmacShaTransformFactory;
private readonly AesCryptoServiceProvider aesProvider;
private bool disposed;
public AesCbcThenHmacSha(byte aesKey, byte aesIv, byte macKey)
if (aesKey == null) throw new ArgumentNullException(nameof(aesKey));
if (aesIv == null) throw new ArgumentNullException(nameof(aesIv));
if (macKey == null) throw new ArgumentNullException(nameof(macKey));
// key size
// --------
// the key size property is set based on the provided
// key. the validation of length is also performed
// by the AesCryptoServiceProvider.
// block size
// ----------
// there is only one valid block size for AES and
// it does not need to be specified explicitly. it
// is 128 by default (and cannot be changed to any
// different value).
// initialization vector
// ---------------------
// the iv has to match the block size, so validation
// of the length is also performed beneath.
this.aesProvider = new AesCryptoServiceProvider
Key = aesKey,
IV = aesIv,
Padding = PaddingMode.PKCS7,
Mode = CipherMode.CBC
;
this.hmacShaTransformFactory = CreateHmacShaTransform(macKey);
private static Func<HMAC> CreateHmacShaTransform(byte key)
var bits = key.Length * 8;
switch (bits)
case 256: return () => new HMACSHA256(key);
case 384: return () => new HMACSHA384(key);
case 512: return () => new HMACSHA512(key);
default:
throw new ArgumentException($"the bits-bit HMAC SHA is not supported");
public void Encrypt(Stream inputStream, Stream outputStream, out byte hmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
var hmacShaTransform = this.hmacShaTransformFactory.Invoke();
var aesTransform = this.aesProvider.CreateEncryptor();
var hmacShaStream = new CryptoStream(outputStream, hmacShaTransform, CryptoStreamMode.Write);
var aesStream = new CryptoStream(hmacShaStream, aesTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
hmacSha = hmacShaTransform.Hash;
hmacShaTransform.Dispose();
public void Decrypt(Stream inputStream, Stream outputStream, byte expectedHmacSha)
if (inputStream == null) throw new ArgumentNullException(nameof(inputStream));
if (outputStream == null) throw new ArgumentNullException(nameof(outputStream));
if (expectedHmacSha == null) throw new ArgumentNullException(nameof(expectedHmacSha));
using (var hmacShaTransform = this.hmacShaTransformFactory.Invoke())
using (var hmacShaStream = new CryptoStream(Stream.Null, hmacShaTransform, CryptoStreamMode.Write))
inputStream.CopyTo(hmacShaStream);
hmacShaStream.FlushFinalBlock();
var hmacSha = hmacShaTransform.Hash;
if (!hmacSha.SequenceEqual(expectedHmacSha))
throw "message authentication code does not match the expected value".Security();
inputStream.Position = 0;
var aesCryptoTransform = this.aesProvider.CreateDecryptor();
var aesStream = new CryptoStream(outputStream, aesCryptoTransform, CryptoStreamMode.Write);
inputStream.CopyTo(aesStream);
aesStream.FlushFinalBlock();
public void Dispose()
if (!this.disposed)
this.aesProvider.Dispose();
this.disposed = true;
c# .net aes
edited Apr 14 at 9:00
asked Apr 13 at 22:18
Patryk Golebiowski
1362
1362
add a comment |Â
add a comment |Â
1 Answer
1
active
oldest
votes
up vote
2
down vote
My main concern with your design is that it encourages IV reuse - which, as far as I understand, pretty much defeats the point of having an IV in the first place. With IV reuse, the same plaintext always results in the same ciphertext - and plaintexts with the same prefix produce ciphertexts where the first block(s) are the same. That is leaking information that could be useful for an attacker.
I would either move the IV parameter from the constructor to Encrypt
, or let Encrypt
generate an IV (in a cryptographically secure way) and prepend it to the ciphertext (Decrypt
should then discard the first decrypted block).
Some more notes:
- Why is the MAC 'returned', instead of appended to the output stream?
- Do you have a specific reason for combining AES-CBC with a hash-based MAC, instead of using an authenticated mode such as CCM or GCM? Cryptography is not my area of expertise, so I can't tell you which of these is 'best', but I'm just pointing out their existence in case you didn't know about them.
- There are several things that are not being disposed, mostly in
Encrypt
but also inDecrypt
. Disposal is also inconsistent, with a fewusing
statements and a few 'loose'Dispose
calls (without anytry-finally
constructs). - Related to the previous point,
CryptoStream.Dispose
takes ownership of the stream that's given to it, and prior to .NET 4.7.2 there's no way to override that behavior, so that may explain why you're not disposing those. If that's the case, I'd recommend documenting that - otherwise you won't be able to tell whether you simply forgot or you had a specific reason for not disposing those streams. - Personally I wouldn't make
Security
an extension method, as I don't consider strings to be 'inherently convertible' to exceptions. - It's good to see documentation, but why not use XML comments (
<summary>...</summary>
)? That allows your IDE to show relevant information whenever you're using this code, without you (or someone else) having to look at the implementation. You may also want to document which key sizes are valid and which padding mode is used, as well as which exception types a caller may expect.
add a comment |Â
1 Answer
1
active
oldest
votes
1 Answer
1
active
oldest
votes
active
oldest
votes
active
oldest
votes
up vote
2
down vote
My main concern with your design is that it encourages IV reuse - which, as far as I understand, pretty much defeats the point of having an IV in the first place. With IV reuse, the same plaintext always results in the same ciphertext - and plaintexts with the same prefix produce ciphertexts where the first block(s) are the same. That is leaking information that could be useful for an attacker.
I would either move the IV parameter from the constructor to Encrypt
, or let Encrypt
generate an IV (in a cryptographically secure way) and prepend it to the ciphertext (Decrypt
should then discard the first decrypted block).
Some more notes:
- Why is the MAC 'returned', instead of appended to the output stream?
- Do you have a specific reason for combining AES-CBC with a hash-based MAC, instead of using an authenticated mode such as CCM or GCM? Cryptography is not my area of expertise, so I can't tell you which of these is 'best', but I'm just pointing out their existence in case you didn't know about them.
- There are several things that are not being disposed, mostly in
Encrypt
but also inDecrypt
. Disposal is also inconsistent, with a fewusing
statements and a few 'loose'Dispose
calls (without anytry-finally
constructs). - Related to the previous point,
CryptoStream.Dispose
takes ownership of the stream that's given to it, and prior to .NET 4.7.2 there's no way to override that behavior, so that may explain why you're not disposing those. If that's the case, I'd recommend documenting that - otherwise you won't be able to tell whether you simply forgot or you had a specific reason for not disposing those streams. - Personally I wouldn't make
Security
an extension method, as I don't consider strings to be 'inherently convertible' to exceptions. - It's good to see documentation, but why not use XML comments (
<summary>...</summary>
)? That allows your IDE to show relevant information whenever you're using this code, without you (or someone else) having to look at the implementation. You may also want to document which key sizes are valid and which padding mode is used, as well as which exception types a caller may expect.
add a comment |Â
up vote
2
down vote
My main concern with your design is that it encourages IV reuse - which, as far as I understand, pretty much defeats the point of having an IV in the first place. With IV reuse, the same plaintext always results in the same ciphertext - and plaintexts with the same prefix produce ciphertexts where the first block(s) are the same. That is leaking information that could be useful for an attacker.
I would either move the IV parameter from the constructor to Encrypt
, or let Encrypt
generate an IV (in a cryptographically secure way) and prepend it to the ciphertext (Decrypt
should then discard the first decrypted block).
Some more notes:
- Why is the MAC 'returned', instead of appended to the output stream?
- Do you have a specific reason for combining AES-CBC with a hash-based MAC, instead of using an authenticated mode such as CCM or GCM? Cryptography is not my area of expertise, so I can't tell you which of these is 'best', but I'm just pointing out their existence in case you didn't know about them.
- There are several things that are not being disposed, mostly in
Encrypt
but also inDecrypt
. Disposal is also inconsistent, with a fewusing
statements and a few 'loose'Dispose
calls (without anytry-finally
constructs). - Related to the previous point,
CryptoStream.Dispose
takes ownership of the stream that's given to it, and prior to .NET 4.7.2 there's no way to override that behavior, so that may explain why you're not disposing those. If that's the case, I'd recommend documenting that - otherwise you won't be able to tell whether you simply forgot or you had a specific reason for not disposing those streams. - Personally I wouldn't make
Security
an extension method, as I don't consider strings to be 'inherently convertible' to exceptions. - It's good to see documentation, but why not use XML comments (
<summary>...</summary>
)? That allows your IDE to show relevant information whenever you're using this code, without you (or someone else) having to look at the implementation. You may also want to document which key sizes are valid and which padding mode is used, as well as which exception types a caller may expect.
add a comment |Â
up vote
2
down vote
up vote
2
down vote
My main concern with your design is that it encourages IV reuse - which, as far as I understand, pretty much defeats the point of having an IV in the first place. With IV reuse, the same plaintext always results in the same ciphertext - and plaintexts with the same prefix produce ciphertexts where the first block(s) are the same. That is leaking information that could be useful for an attacker.
I would either move the IV parameter from the constructor to Encrypt
, or let Encrypt
generate an IV (in a cryptographically secure way) and prepend it to the ciphertext (Decrypt
should then discard the first decrypted block).
Some more notes:
- Why is the MAC 'returned', instead of appended to the output stream?
- Do you have a specific reason for combining AES-CBC with a hash-based MAC, instead of using an authenticated mode such as CCM or GCM? Cryptography is not my area of expertise, so I can't tell you which of these is 'best', but I'm just pointing out their existence in case you didn't know about them.
- There are several things that are not being disposed, mostly in
Encrypt
but also inDecrypt
. Disposal is also inconsistent, with a fewusing
statements and a few 'loose'Dispose
calls (without anytry-finally
constructs). - Related to the previous point,
CryptoStream.Dispose
takes ownership of the stream that's given to it, and prior to .NET 4.7.2 there's no way to override that behavior, so that may explain why you're not disposing those. If that's the case, I'd recommend documenting that - otherwise you won't be able to tell whether you simply forgot or you had a specific reason for not disposing those streams. - Personally I wouldn't make
Security
an extension method, as I don't consider strings to be 'inherently convertible' to exceptions. - It's good to see documentation, but why not use XML comments (
<summary>...</summary>
)? That allows your IDE to show relevant information whenever you're using this code, without you (or someone else) having to look at the implementation. You may also want to document which key sizes are valid and which padding mode is used, as well as which exception types a caller may expect.
My main concern with your design is that it encourages IV reuse - which, as far as I understand, pretty much defeats the point of having an IV in the first place. With IV reuse, the same plaintext always results in the same ciphertext - and plaintexts with the same prefix produce ciphertexts where the first block(s) are the same. That is leaking information that could be useful for an attacker.
I would either move the IV parameter from the constructor to Encrypt
, or let Encrypt
generate an IV (in a cryptographically secure way) and prepend it to the ciphertext (Decrypt
should then discard the first decrypted block).
Some more notes:
- Why is the MAC 'returned', instead of appended to the output stream?
- Do you have a specific reason for combining AES-CBC with a hash-based MAC, instead of using an authenticated mode such as CCM or GCM? Cryptography is not my area of expertise, so I can't tell you which of these is 'best', but I'm just pointing out their existence in case you didn't know about them.
- There are several things that are not being disposed, mostly in
Encrypt
but also inDecrypt
. Disposal is also inconsistent, with a fewusing
statements and a few 'loose'Dispose
calls (without anytry-finally
constructs). - Related to the previous point,
CryptoStream.Dispose
takes ownership of the stream that's given to it, and prior to .NET 4.7.2 there's no way to override that behavior, so that may explain why you're not disposing those. If that's the case, I'd recommend documenting that - otherwise you won't be able to tell whether you simply forgot or you had a specific reason for not disposing those streams. - Personally I wouldn't make
Security
an extension method, as I don't consider strings to be 'inherently convertible' to exceptions. - It's good to see documentation, but why not use XML comments (
<summary>...</summary>
)? That allows your IDE to show relevant information whenever you're using this code, without you (or someone else) having to look at the implementation. You may also want to document which key sizes are valid and which padding mode is used, as well as which exception types a caller may expect.
answered Apr 20 at 10:57
Pieter Witvoet
3,611621
3,611621
add a comment |Â
add a comment |Â
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
StackExchange.ready(
function ()
StackExchange.openid.initPostLogin('.new-post-login', 'https%3a%2f%2fcodereview.stackexchange.com%2fquestions%2f192013%2fencryption-and-decryption-on-streams-using-aes-cbc-hmac-sha%23new-answer', 'question_page');
);
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Post as a guest
Sign up or log in
StackExchange.ready(function ()
StackExchange.helpers.onClickDraftSave('#login-link');
);
Sign up using Google
Sign up using Facebook
Sign up using Email and Password
Sign up using Google
Sign up using Facebook
Sign up using Email and Password