An encryption library for Xojo that implements Blowfish, AES, Bcrypt, Scrypt, SHA-256 digest, and SHA-512 digest.
- Important Changes From Blowfish_MTC Project
- How To Use It
- About ECB, CBC, and the Vector
- About Padding
- Compatibility
- Other
- A Word About Threads
- License
- Comments and Contributions
- Who Did This?!?
- Release Notes
This project was originally released as Blowfish_MTC and included the Blowfish_MTC class and Bcrypt module. These have been rolled into M_Crypto with some important changes that may affect existing projects.
- Padding.PKSC5 has been renamed PKCS and is now the default for Blowfish_MTC.
- Padding.NullPadding has been renamed NullsWithCount and is no longer the default for Blowfish_MTC.
Encrypt
andDecrypt
used to take an optional parameter for vector. Now you must useSetInitialVector
orResetInitialVector
.- Padding.NullsOnly has been added.
Bcrypt_MTC.Bcrypt
has been deprecated in favor ofBcrypt_MTC.Hash
.
Let's start with some examples. These are meant to give you an idea of the ways you can use these classes and modules, not how you must use them. Code that demonstrates one Encrypter can usually be used with the other.
dim bf as new Blowfish_MTC( "password", Blowfish_MTC.Padding.PKCS )
dim data as string = EncodeHex( bf.Encrypt( "some data" ) )
// 774FB372E0636E70BA65D0BD35D78829
data = EncodeHex( bf.EncryptECB( "some data" ) )
// D9B0A79853F139603951BFF96C3D0DD5
bf.SetInitialVector "my vecto" // 8 bytes
data = EncodeHex( bf.EncryptCBC( "some data" ) )
// 9B0BA2B3716E777FD89048BC8738869A
bf.SetKey "another password"
data = EncodeHex( bf.Encrypt( "some data" ) )
// 00884CF6829D2CBA51989BB19AF84945
bf.PaddingMethod = Blowfish_MTC.Padding.NullsWithCount
data = EncodeHex( bf.Encrypt( "some data" ) )
// 00884CF6829D2CBACCE75A5012ED7631
dim aes as new AES_MTC( AES_MTC.EncryptionBits.Bits128 )
aes.SetKey "password"
aes.PaddingMethod = AES_MTC.Padding.NullsOnly
dim data as string = EncodeHex( aes.Encrypt( "some data" ) )
// 9EAC7C8DC34E13B89B89F9F6D08E830F
aes = new AES_MTC( _
"another password", AES_MTC.EncryptionBits.Bits256, AES_MTC.Padding.PKCS )
aes.SetInitialVector "1234567890ABCDEF1234567890ABCDEF" // 16 bytes as hex
data = EncodeHex( aes.EncryptCBC( "some data" ) )
// 6984F179E4F969A79CC8D6AD1F295244
dim e as M_Crypto.Encrypter
e = new AES_MTC( 128 )
e.SetKey "password"
e.SetInitialVector "I need 16 bytes!"
e.UseFunction = M_Crypto.Encrypter.Functions.CBC
dim data as string = EncodeHex( e.Encrypt( "some data" ) )
// DDF9F81FF318E5D0596BE0B24CE801DD
e = new Blowfish_MTC
e.SetKey "password"
e.PaddingMethod = M_Crypto.Encrypter.Padding.PKCS
data = EncodeHex( e.EncryptECB( "some data" ) )
// D9B0A79853F139603951BFF96C3D0DD5
e = M_Crypto.GetEncrypter( "aes-256-cbc" )
e.SetKey "password"
e.PaddingMethod = M_Crypto.Encrypter.Padding.PKCS
e.SetInitialVector "I need 16 bytes!"
data = EncodeHex( e.Encrypt( "some data" ) )
// 90BD2689FC13EDD41063AE6DD18AD1D2
dim hash as string = Bcrypt_MTC.Hash( "somebody's password", 10 )
// $2y$10$ZPkkuUAmRjx7JBDylI6GL.Pe4p.8G5dUBhl9grZg/b08Gh.1G8Ez.
if Bcrypt_MTC.Verify( _
"somebody's password", _
"$2y$10$ZPkkuUAmRjx7JBDylI6GL.Pe4p.8G5dUBhl9grZg/b08Gh.1G8Ez." ) then
MsgBox "That's right"
end if
dim salt as string = Bcrypt_MTC.GenerateSalt( 10 )
// $2y$10$7XjO9J5P1DJJPCy7xbHYHu , for example
hash = Bcrypt_MTC.Hash( "somebody's password", salt )
// $2y$10$7XjO9J5P1DJJPCy7xbHYHuCSdEEaV6gsxhZbogNGFlq5dwAiX2S8K
dim hash as string = EncodeHex( _
Scrypt_MTC.Hash( "somebody's password", "a salt", 4, 16 ) )
// FF8D8638295EAAE9E6069EBA9075A777
hash = EncodeHex( _
Scrypt_MTC.Hash( "password", "salt", 10, 64, 8, 4 ) )
// EE8EFBD416D4492BC95BB5E01CBE0C0B19AD9569F239D55C995ABAAC2F2D3272
// AF7525522F12B36C18B4B712D138B71149CC174762B1108014EE443D1DBBB74D
dim d as new SHA256Digest_MTC // or SHA512Digest_MTC, SHA3_224Digest_MTC, etc.
dim hash as string
d.Process( "abc" )
hash = d.Value // same as Crypto.SHA256( "abc" )
d.Process( "def" )
hash = d.Value // same as Crypto.SHA256( "abcdef" )
d.Reset
d.Process( "123" )
hash = d.Value // same as Crypto.SHA256( "123" )
The encryption objects are based on the superclass M_Crypto.Encrypter
and offer the following common methods and properties:
SetKey( string )
PaddingMethod as M_Crypto.Encrypter.Padding
SetInitialVector( string )
ResetInitialVector()
CurrentVector as String [read-only]
BlockSize as Integer [read-only]
UseFunction as M_Crypto.Encrypter.Functions
Encrypt( data As String, isFinalBlock As Boolean = True )
EncryptCBC( data As String, isFinalBlock As Boolean = True )
EncryptECB( data As String, isFinalBlock As Boolean = True )
Decrypt( data As String, isFinalBlock As Boolean = True )
DecryptCBC( data As String, isFinalBlock As Boolean = True )
DecryptECB( data As String, isFinalBlock As Boolean = True )
UseFunction
will determine what happens when Encrypt
or Decrypt
are called. If left at Default, the default encryption is used, otherwise it will redirect to ECB or CBC as specified.
Create an object for the type of encryption you want and optionally specify the key and padding. Each can be set later if desired with SetKey
and PaddingMethod
respectively.
Each type of encryption defaults to PKCS padding.
Example:
dim bf as Blowfish_MTC
bf = new Blowfish_MTC( "my encryption key" )
bf = new Blowfish_MTC( Blowfish_MTC.Padding.NullsOnly )
bf = new Blowfish_MTC( "my encryption key", Blowfish_MTC.Padding.PKCS )
bf = new Blowfish_MTC // Set the key at least before using
AES requires its encryption bits in its Constructor. Examples:
dim aes as AES_MTC
aes = new AES_MTC( AES_MTC.EncryptionBits.Bits128 )
aes = new AES_MTC( 256 )
aes = new AES_MTC( "my encryption key", AES_MTC.EncryptionBits.Bits192 )
aes = new AES_MTC( _
"my encryption key", _
AES_MTC.EncryptionBits.Bits192, _
AES_MTC.Padding.NullsOnly )
AES.Encrypt
and Decrypt
default to ECB if UseFunction
is set to Default.
With either encryption, you must set the key before attempting to encrypt or decrypt.
You can use M_Crypto.GetEncrypter
to get an Encrypter by code. For example, "bf" will return a Blowfish_MTC, "aes-256" an AES_MTC preset to 256-bit, and "aes-128-cbc" an AES_MTC preset to 256-bit and the default functions set to use CBC. In each case you must set the key, the padding if needed, and the vector if applicable.
Bcrypt uses Blowfish to create a hash. The more rounds you specify, the longer it takes. You can either create your own salt according to the Bcrypt standard or let the module do it for you. See the example above in Examples.
Scrypt is a password hashing algorithm that is generally considered "costlier" than Bcrypt and may be preferred. It takes as parameters cost
(other implementations use n
where n
= 2^cost), outputLength
in bytes, blocks
(r
in other implementation), and parallelization
(p
in other implementations). Increase the cost
, parallelization
, and blocks
parameters until just before performance is no longer acceptable. The higher the values, the harder to crack the hash via brute-force. See the example above in Examples.
The project includes SHA256Digest_MTC
, SHA512Digest_MTC
, and SHA3Digest_MTC
classes. These will let you calculate those respective hashes a chunk at a time. Use the Process
method to add data and check the Value
property to get the hash at that moment. Value
will alway be up to date with whatever data as been processed.
If you want to reuse the object for a new hash, use the Reset
method. See the example above in Examples.
ECB encryption treats each block of data individually. This means that repeating blocks will be encrypted in the same way. For example, using Blowfish to encrypt the repeating data "12345678ABCDEFGH12345678" will lead to this result as hex: "DA6003664651D153 805D00DD8BF2133B DA6003664651D153". Notice the first 8 bytes are identical the last 8 bytes.
The other modes (CBC, CFB, and OFB) will chain encryption so the result of the each block will affect the next block. Using CBC and no vector in the above example will result in: "DA6003664651D153 2066457C3AE99820 5C937C55EE7EDEF0". Notice that the first block is identical to that produced by ECB but the subsequent blocks are different.
When using the other modes, you can affect the result of the first block by specifying an initial vector, a number of bytes equal to the block size of the Encrypter (8 bytes for Blowfish, 16 bytes for AES). The vector is ignored when using ECB.
Block encryption algorithms require that data be given in multiples of known block sizes (8 bytes for Blowfish, 16 bytes for AES), but because the real world is rarely that neat, data must be padded to the required bytes.
M_Crypto offers padding that come with their own rules.
This simply appends nulls to the end of the data to reach the required size. When decrypted, the trailing nulls are trimmed. Data that is already a multiple of the block size will not be padded.
This method should usually be avoided unless you are going to pad the data yourself.
The data is padded with nulls followed by a count of the number of padding bytes. Data is that already a multiple of the block size will not be padded unless the last bytes match the pattern of a pad. In other words, if the data ends with "00 00 00 04", a pad equal to the block size will be added.
A pad is always added to the data even if it's already a multiple of the block size. The bytes added are the number of padding bytes repeated. For example, if 5 pad bytes are needed, the pad will be "05 05 05 05 05".
Because a pad is always expected, the lack of this pad will raise an M_Crypto.InvalidPaddingException.
The is the default method for AES and Blowfish.
While ECB and CBC modes require exact block sizes, CFB and OFB modes do not. (They accomplish this by encrypting the initial and subsequent vectors rather than the data directly.) In these modes only, you may specify no padding and the encrypted output will be exactly the size of your data.
Without padding, decrypting with any password will produce output without raising an exception.
The output of the CBC and ECB functions using PKCS padding will be identical to that of other platforms.
SELECT encrypt_iv('some data', 'password', 'I need 16 bytes!', 'aes-cbc/pad:pkcs')::TEXT; -- AES-128
-- \xddf9f81ff318e5d0596be0b24ce801dd
SELECT encrypt_iv('some data', digest('password', 'SHA256'), 'I need 16 bytes!', 'aes-cbc/pad:pkcs')::TEXT; -- AES-256
-- \x7d0fd83942c4948081213c8526af8af3
Note: The key size will determine whether Postgres uses AES-128, AES-192, or AES-256, i.e., a 128-bit (or less) key will force AES-128 while a 129-bit key will force AES-192. A key of 256 bits or more will force AES-256.
The Crypto module follows these rules as of this writing:
AES: Requires a key the size of the specified bits, i.e., AES-128 needs 16 bytes, AES-256 needs 32 bytes.
Blowfish ECB: Will take any key but it will apply an MD5 hash to it internally.
Blowfish CBC: Will take any key.
openssl encrypts and decrypts data using a wide variety of algorithms. In general, to match the output, you will need to specify the exact key as hex. For example:
# Blowfish
echo -n 'some data' | openssl enc -e -bf-cbc -a -A -nosalt -iv 0001020304050607 -K 000102030405060708090a0b0c0d0e0f
# HiFnMV4So2mpOl3vqzQaYQ==
# AES-128
echo -n 'some data' | openssl enc -e -aes-128-cbc -a -A -nosalt -iv 000102030405060708090a0b0c0d0e0f -K 000102030405060708090a0b0c0d0e0f
# Zzs+Pe2dhMCc1tflDadoaA==
M_Crypto.GenerateUUID
will create a UUI using the OS tools, if possible, or native Xojo code if not. In any case, the output conforms to standards and is cryptographically secure.
You may use the same instance of an M_Crypto.Encrypter
in multiple threads simultaneously as long as you do not change the key or Initial Vector while another thread might be using it. Otherwise, create another instance of that Encrypter
, which is probably safer anyway.
This is an open-source project.
This project is distributed AS-IS and no warranty of fitness for any particular purpose is expressed or implied. You may freely use or modify this project or any part of within as long as this notice or any other legal notice is left undisturbed.
You may distribute a modified version of this project as long as all modifications are clearly documented and accredited.
All contributions to this project will be gratefully considered. Fork this repo to your own, then submit your changes via a Pull Request.
All comments are also welcome.
This project was created by and is maintained by Kem Tekinay (ktekinay at mactechnologies dot com).
2.8 (Nov. 10, 2021)
- Converted from
Xojo.Core.MemoryBlock
toMemoryBlock
. - Started using some API2 calls.
- Improved AES performance slightly.
- Improved Scrypt performance dramatically (5X!).
- Improved Bcrypt performance slightly.
- Improved Blowfish performance slightly.
- General code clean-up.
- Improved SHA code.
- Streaming encryption can be ended with an empty string when
isFinalBlock
is True.
2.7 (Feb. 21, 2021)
- Added
SHA3Digest_MTC
. - Encryption window and CLI allows hashing the key with SHA3.
- Updated XojoUnit.
2.6 (Jul. 27, 2020)
- Added CFB and OFB modes.
- Added
Encrypter.Code
and.PaddingString
computed properties.
2.5.3 (Mar. 22, 2020)
- Optimized AES code (~2 times faster!).
- More unit tests.
2.5.2 (Dec. 11, 2018)
- Digest optimizations.
- Bcrypt optimizations.
- Renamed internal methods in
AES_MTC
andBlowfish_MTC
to be more descriptive.
2.5.1 (Nov. 16, 2018)
- Optimizations to the SHA digest classes making them roughly 10x faster.
2.5 (Nov. 15, 2018)
- Added
SHA256Digest_MTC
andSHA512Digest_MTC
classes. - Added "--debug-args" switch to CLI.
- Added BcryptTimimg unit test.
2.4 (Nov. 6, 2017)
- Added pragmas that dramatically increase the speed of
AES_MTC
decryption. - The same instance of an
M_Crypto.Encrypter
is now safe to use in multiple threads. Just don't change the key or Initial Vector of that instance. - More efficient handling of the current vector in
AES_MTC
. - Better description in CLI help.
- Removed unneeded property in
M_Crypto.Encrypter
.
2.3 (Oct. 16, 2017)
- Harness now allows multiple encryption windows.
- Encryption window hides the encryption key unless that field has focus.
- Fixed bug in AES that could lead to a crash (using a Ptr to overwrite the bounds of a MemoryBlock by one byte).
- Refactored around new framework MemoryBlock.
- Quicker initialization for AES.
- Added clone Constructor.
2.2.1 (Oct. 7, 2017)
- Dramatically sped up Scrypt.
2.2 (Oct. 4, 2017)
- Added
M_Crypto.GenerateUUID
. - Fixed Windows bug in M_ANSI.
- Added
Scrypt_MTC
. - Added Scrypt and Get-bytes to CLI project.
2.1 (Sept. 27, 2017)
- Refactored to streamline code.
- Changed NullsWithCount padding to conform to standard. It will now add padding in all cases, even if the block is already the right size. This means the last padding byte can be 0x01, something that wasn't allowed in the previous version. Depadding will still work on something encrypted like that unless you decrypt in blocks and the last block is <= BlockSize.
- Added the CLI project and reorganized files in general.
2.0 (Sept. 19, 2017)
- Renamed M_Crypto and other classes rolled into that module.
- Introduced AES.
- Renamed padding "PKCS5" to "PKCS".
- Renamed
SetVector
toSetInitialVector
and took it out of theConstructor
. - Introduced the Encrypter superclass and the ability to get an Encrypter via code.
- Padding.NullPadding has been renamed NullsWithCount.
- The default padding for Blowfish_MTC is now PKCS, not NullsWithCount.
Bcrypt_MTC.Bcrypt
has been deprecated in favor ofBcrypt_MTC.Hash
.- Refactored Harness to include an all-purpose encryption/decryption utility and unit tests.
1.0 (some time ago as Blowfish_MTC)
- Initial release of Blowfish and Bcrypt.