How to resolve the algorithm MD5/Implementation step by step in the Swift programming language

Published on 12 May 2024 09:40 PM

How to resolve the algorithm MD5/Implementation step by step in the Swift programming language

Table of Contents

Problem Statement

The purpose of this task to code and validate an implementation of the MD5 Message Digest Algorithm by coding the algorithm directly (not using a call to a built-in or external hashing library). For details of the algorithm refer to MD5 on Wikipedia or the MD5 definition in IETF RFC (1321).

The solutions shown here will provide practical illustrations of bit manipulation, unsigned integers, working with little-endian data. Additionally, the task requires an attention to details such as boundary conditions since being out by even 1 bit will produce dramatically different results. Subtle implementation bugs can result in some hashes being correct while others are wrong. Not only is it critical to get the individual sub functions working correctly, even small errors in padding, endianness, or data layout will result in failure. In addition, intermediate outputs to aid in developing an implementation can be found here. The MD5 Message-Digest Algorithm was developed by RSA Data Security, Inc. in 1991.

Let's start with the solution:

Step by Step solution about How to resolve the algorithm MD5/Implementation step by step in the Swift programming language

Source code in the swift programming language

    import Foundation
    public class MD5 {
        /** specifies the per-round shift amounts */
        private let s: [UInt32] = [7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,  7, 12, 17, 22,
                           5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,  5,  9, 14, 20,
                           4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,  4, 11, 16, 23,
                           6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21,  6, 10, 15, 21]
        
        /** binary integer part of the sines of integers (Radians) */
        private let K: [UInt32] = (0 ..< 64).map { UInt32(0x100000000 * abs(sin(Double($0 + 1)))) }
        
        let a0: UInt32 = 0x67452301
        let b0: UInt32 = 0xefcdab89
        let c0: UInt32 = 0x98badcfe
        let d0: UInt32 = 0x10325476
        
        private var message: NSData
        
        //MARK: Public
        
        public init(_ message: NSData) {
            self.message = message
        }
        
        public func calculate() -> NSData? {
            var tmpMessage: NSMutableData = NSMutableData(data: message)
            let wordSize = sizeof(UInt32)
            
            var aa = a0
            var bb = b0
            var cc = c0
            var dd = d0
            
            // Step 1. Append Padding Bits
            tmpMessage.appendBytes([0x80]) // append one bit (Byte with one bit) to message
            
            // append "0" bit until message length in bits ≡ 448 (mod 512)
            while tmpMessage.length % 64 != 56 {
                tmpMessage.appendBytes([0x00])
            }
            
            // Step 2. Append Length a 64-bit representation of lengthInBits
            var lengthInBits = (message.length * 8)
            var lengthBytes = lengthInBits.bytes(64 / 8)
            tmpMessage.appendBytes(reverse(lengthBytes));
            
            // Process the message in successive 512-bit chunks:
            let chunkSizeBytes = 512 / 8
            var leftMessageBytes = tmpMessage.length
            for var i = 0; i < tmpMessage.length; i = i + chunkSizeBytes, leftMessageBytes -= chunkSizeBytes {
                let chunk = tmpMessage.subdataWithRange(NSRange(location: i, length: min(chunkSizeBytes,leftMessageBytes)))
                
                // break chunk into sixteen 32-bit words M[j], 0 ≤ j ≤ 15
                // println("wordSize \(wordSize)");
                var M:[UInt32] = [UInt32](count: 16, repeatedValue: 0)
                for x in 0..<M.count {
                    var range = NSRange(location:x * wordSize, length: wordSize)
                    chunk.getBytes(&M[x], range:range);
                }
                
                // Initialize hash value for this chunk:
                var A:UInt32 = a0
                var B:UInt32 = b0
                var C:UInt32 = c0
                var D:UInt32 = d0
                
                var dTemp:UInt32 = 0
                
                // Main loop
                for j in 0...63 {
                    var g = 0
                    var F:UInt32 = 0
                    
                    switch (j) {
                    case 0...15:
                        F = (B & C) | ((~B) & D)
                        g = j
                        break
                    case 16...31:
                        F = (D & B) | (~D & C)
                        g = (5 * j + 1) % 16
                        break
                    case 32...47:
                        F = B ^ C ^ D
                        g = (3 * j + 5) % 16
                        break
                    case 48...63:
                        F = C ^ (B | (~D))
                        g = (7 * j) % 16
                        break
                    default:
                        break
                    }
                    dTemp = D
                    D = C
                    C = B
                    B = B &+ rotateLeft((A &+ F &+ K[j] &+ M[g]), s[j])
                    A = dTemp    
                }
                
                aa = aa &+ A
                bb = bb &+ B
                cc = cc &+ C
                dd = dd &+ D
            }

            var buf: NSMutableData = NSMutableData();
            buf.appendBytes(&aa, length: wordSize)
            buf.appendBytes(&bb, length: wordSize)
            buf.appendBytes(&cc, length: wordSize)
            buf.appendBytes(&dd, length: wordSize)
            
            return buf.copy() as? NSData;
        }

        //MARK: Class
        class func calculate(message: NSData) -> NSData?
        {
            return MD5(message).calculate();
        }
        
        //MARK: Private
        private func rotateLeft(x:UInt32, _ n:UInt32) -> UInt32 {
            return (x &<< n) | (x &>> (32 - n))
        }
    }


import Foundation

let shift : [UInt32] = [7, 12, 17, 22, 5, 9, 14, 20, 4, 11, 16, 23, 6, 10, 15, 21]
let table: [UInt32] = (0 ..< 64).map { UInt32(0x100000000 * abs(sin(Double($0 + 1)))) }

func md5(var message: [UInt8]) -> [UInt8] {
  var messageLenBits = UInt64(message.count) * 8
  message.append(0x80)
  while message.count % 64 != 56 {
    message.append(0)
  }
  
  var lengthBytes = [UInt8](count: 8, repeatedValue: 0)
  UnsafeMutablePointer<UInt64>(lengthBytes).memory = messageLenBits.littleEndian
  message += lengthBytes
  
  var a : UInt32 = 0x67452301
  var b : UInt32 = 0xEFCDAB89
  var c : UInt32 = 0x98BADCFE
  var d : UInt32 = 0x10325476
  for chunkOffset in stride(from: 0, to: message.count, by: 64) {
    let chunk = UnsafePointer<UInt32>(UnsafePointer<UInt8>(message) + chunkOffset)
    let originalA = a
    let originalB = b
    let originalC = c
    let originalD = d
    for j in 0 ..< 64 {
      var f : UInt32 = 0
      var bufferIndex = j
      let round = j >> 4
      switch round {
      case 0:
        f = (b & c) | (~b & d)
      case 1:
        f = (b & d) | (c & ~d)
        bufferIndex = (bufferIndex*5 + 1) & 0x0F
      case 2:
        f = b ^ c ^ d
        bufferIndex = (bufferIndex*3 + 5) & 0x0F
      case 3:
        f = c ^ (b | ~d)
        bufferIndex = (bufferIndex * 7) & 0x0F
      default:
        assert(false)
      }
      let sa = shift[(round<<2)|(j&3)]
      let tmp = a &+ f &+ UInt32(littleEndian: chunk[bufferIndex]) &+ table[j]
      a = d
      d = c
      c = b
      b = b &+ (tmp << sa | tmp >> (32-sa))
    }
    a = a &+ originalA
    b = b &+ originalB
    c = c &+ originalC
    d = d &+ originalD
  }
  
  var result = [UInt8](count: 16, repeatedValue: 0)
  for (i, n) in enumerate([a, b, c, d]) {
    UnsafeMutablePointer<UInt32>(result)[i] = n.littleEndian
  }
  return result
}

func toHexString(bytes: [UInt8]) -> String {
  return "".join(bytes.map { String(format:"%02x", $0) })
}

for (hashCode, string) in [
  ("d41d8cd98f00b204e9800998ecf8427e", ""),
  ("0cc175b9c0f1b6a831c399e269772661", "a"),
  ("900150983cd24fb0d6963f7d28e17f72", "abc"),
  ("f96b697d7cb7938d525a2f31aaf161d0", "message digest"),
  ("c3fcd3d76192e4007dfb496cca67e13b", "abcdefghijklmnopqrstuvwxyz"),
  ("d174ab98d277d9f5a5611c2c9f419d9f",
    "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789"),
  ("57edf4a22be3c955ac49da2e2107b67a", "12345678901234567890" +
    "123456789012345678901234567890123456789012345678901234567890")] {
      println(hashCode)
      println(toHexString(md5(Array(string.utf8))))
      println()
}


  

You may also check:How to resolve the algorithm Pseudo-random numbers/Splitmix64 step by step in the Jasmin programming language
You may also check:How to resolve the algorithm Chinese zodiac step by step in the Quackery programming language
You may also check:How to resolve the algorithm Terminal control/Clear the screen step by step in the Delphi programming language
You may also check:How to resolve the algorithm Perlin noise step by step in the REXX programming language
You may also check:How to resolve the algorithm Topswops step by step in the Ruby programming language