Module: AesCtr
- Defined in:
- lib/aesctr-ruby.rb
Overview
AES
Class Method Summary collapse
-
.decrypt(ciphertext, password, nBits) ⇒ Object
Decrypt a text encrypted by AES in counter mode of operation.
-
.encrypt(plaintext, password, nBits) ⇒ Object
@returns string Encrypted text.
Class Method Details
.decrypt(ciphertext, password, nBits) ⇒ Object
Decrypt a text encrypted by AES in counter mode of operation
224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/aesctr-ruby.rb', line 224 def self.decrypt(ciphertext, password, nBits) blockSize = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES return '' unless(nBits==128 || nBits==192 || nBits==256) ciphertext = Base64.decode64(ciphertext); nBytes = nBits/8 # no bytes in key (16/24/32) pwBytes = [] 0.upto(nBytes-1){|i| pwBytes[i] = password.bytes.to_a[i] & 0xff || 0} key = Aes::cipher(pwBytes, Aes::keyExpansion(pwBytes)) # gives us 16-byte key key = key.concat(key.slice(0, nBytes-16)) # expand key to 16/24/32 bytes long # recover nonce from 1st 8 bytes of ciphertext counterBlock = [] ctrTxt = ciphertext[0,8] 0.upto(7){|i| counterBlock[i] = ctrTxt.bytes.to_a[i]} #generate key Schedule keySchedule = Aes.keyExpansion(key); # separate ciphertext into blocks (skipping past initial 8 bytes) nBlocks = ((ciphertext.length-8)/blockSize.to_f).ceil ct=[] 0.upto(nBlocks-1){|b|ct[b] = ciphertext[8+b*blockSize, 16]} ciphertext = ct; # ciphertext is now array of block-length strings # plaintext will get generated block-by-block into array of block-length strings plaintxt = []; 0.upto(nBlocks-1) do |b| 0.upto(3){|c| counterBlock[15-c] = self.urs(b,c*8) & 0xff} 0.upto(3){|c| counterBlock[15-c-4] = self.urs((b+1)/(0x100000000-1),c*8) & 0xff} cipherCntr = Aes.cipher(counterBlock, keySchedule) # encrypt counter block plaintxtByte = [] 0.upto(ciphertext[b].length - 1) do |i| # -- xor plaintxt with ciphered counter byte-by-byte -- plaintxtByte[i] = (cipherCntr[i] ^ ciphertext[b].bytes.to_a[i]).chr; end plaintxt[b] = plaintxtByte.join('') end plaintext = plaintxt.join('') end |
.encrypt(plaintext, password, nBits) ⇒ Object
@returns string Encrypted text
166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 |
# File 'lib/aesctr-ruby.rb', line 166 def self.encrypt(plaintext, password, nBits) blockSize = 16 # block size fixed at 16 bytes / 128 bits (Nb=4) for AES return '' unless(nBits==128 || nBits==192 || nBits==256) # use AES itself to encrypt password to get cipher key (using plain password as source for key # expansion) - gives us well encrypted key (though hashed key might be preferred for prod'n use) nBytes = nBits/8 # no bytes in key (16/24/32) pwBytes = [] 0.upto(nBytes-1){|i| pwBytes[i] = password.bytes.to_a[i] & 0xff || 0} # use 1st 16/24/32 chars of password for key #warn key = Aes::cipher(pwBytes, Aes::keyExpansion(pwBytes)) # gives us 16-byte key key = key + key[0, nBytes-16] # expand key to 16/24/32 bytes long # initialise 1st 8 bytes of counter block with nonce (NIST SP800-38A §B.2): [0-1] = millisec, # [2-3] = random, [4-7] = seconds, together giving full sub-millisec uniqueness up to Feb 2106 counterBlock = [] nonce = Time.now.to_i #42200 nonceMs = nonce%1000 nonceSec = (nonce/1000.0).floor nonceRnd = (rand() *0xffff).floor #13 0.upto(1){|i| counterBlock[i] = self.urs(nonceMs, i*8) &0xff } 0.upto(1){|i| counterBlock[i+2] = self.urs(nonceRnd, i*8) &0xff } 0.upto(3){|i| counterBlock[i+4] = self.urs(nonceSec,i*8) & 0xff} # and convert it to a string to go on the front of the ciphertext ctrTxt = '' 0.upto(7){|i| ctrTxt += counterBlock[i].chr} # generate key schedule - an expansion of the key into distinct Key Rounds for each round keySchedule = Aes.keyExpansion(key) #p "rks:",keySchedule blockCount = (plaintext.length/blockSize.to_f).ceil ciphertxt = [] 0.upto(blockCount-1) do |b| # set counter (block #) in last 8 bytes of counter block (leaving nonce in 1st 8 bytes) # done in two stages for 32-bit ops: using two words allows us to go past 2^32 blocks (68GB) 0.upto(3){|c| counterBlock[15-c] = self.urs(b,c*8) & 0xff} 0.upto(3){|c| counterBlock[15-c-4] = self.urs(b/0x100000000,c*8)} cipherCntr = Aes.cipher(counterBlock, keySchedule) # -- encrypt counter block -- # block size is reduced on final block blockLength = b < blockCount-1 ? blockSize : (plaintext.length-1) % blockSize + 1 cipherChar = []; 0.upto(blockLength-1) do |i| cipherChar[i] = (cipherCntr[i] ^ plaintext.bytes.to_a[b*blockSize+i]).chr end ciphertxt[b] = cipherChar.join('') end ciphertext = ctrTxt + ciphertxt.join('') ciphertext = Base64.encode64(ciphertext).gsub(/\n/,'')+"\n"; # encode in base64 end |