From 2a8c4d196c0c02a3fe557fd3284f10c2942a9df8 Mon Sep 17 00:00:00 2001 From: "Namhyeon, Go" Date: Sun, 20 Jul 2025 19:49:52 +0900 Subject: [PATCH] Add HIGHT block cipher implementation #293 Implemented the HIGHT block cipher with ECB mode support, including HightAlgorithm, HightCore, and HightEcbTransform classes. Updated the test program to include HIGHT encryption/decryption tests and added support for PaddingMode.None in ARIA and SEED ECB transforms. --- .../WelsonJS.Cryptography.Test/Program.vb | 16 +- .../WelsonJS.Cryptography/AriaEcbTransform.vb | 6 + .../WelsonJS.Cryptography/HightAlgorithm.vb | 51 ++++++ .../WelsonJS.Cryptography/HightCore.vb | 139 ++++++++++++++++ .../HightEcbTransform.vb | 156 ++++++++++++++++++ .../WelsonJS.Cryptography/SeedEcbTransform.vb | 6 + 6 files changed, 371 insertions(+), 3 deletions(-) create mode 100644 WelsonJS.Toolkit/WelsonJS.Cryptography/HightAlgorithm.vb create mode 100644 WelsonJS.Toolkit/WelsonJS.Cryptography/HightCore.vb create mode 100644 WelsonJS.Toolkit/WelsonJS.Cryptography/HightEcbTransform.vb diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography.Test/Program.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography.Test/Program.vb index a908d53..c712760 100644 --- a/WelsonJS.Toolkit/WelsonJS.Cryptography.Test/Program.vb +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography.Test/Program.vb @@ -30,12 +30,22 @@ Module Program RunTest(ariaCipher) Console.WriteLine() + ' HIGHT algorithm + Console.WriteLine("Start HIGHT encryption and decryption test") + Dim hightCipher As New WelsonJS.Cryptography.HightAlgorithm() + hightCipher.Key = {&H0, &H11, &H22, &H33, &H44, &H55, &H66, &H77, &H88, &H99, &HAA, &HBB, &HCC, &HDD, &HEE, &HFF} + ' hightCipher.IV = {&H0F, &H1E, &H2D, &H3C, &H4B, &H5A, &H69, &H78, &H87, &H96, &HA5, &HB4, &HC3, &HD2, &HE1, &HF0} + hightCipher.Mode = CipherMode.ECB + hightCipher.Padding = PaddingMode.PKCS7 + RunTest(hightCipher) + Console.WriteLine() + End Sub Public Sub RunTest(cipher As SymmetricAlgorithm) - ' Dim inputBytes As Byte() = {&H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &HFE} - Dim inputBytes As Byte() = {&H11, &H11, &H11, &H11, &HAA, &HAA, &HAA, &HAA, &H11, &H11, &H11, &H11, &HBB, &HBB, &HBB, &HBB} - + ' Dim inputBytes As Byte() = {&H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0, &HFE} ' SEED test vector + ' Dim inputBytes As Byte() = {&H11, &H11, &H11, &H11, &HAA, &HAA, &HAA, &HAA, &H11, &H11, &H11, &H11, &HBB, &HBB, &HBB, &HBB} ' ARIA test vector + Dim inputBytes As Byte() = {&H0, &H0, &H0, &H0, &H0, &H0, &H0, &H0} ' HIGHT test vector Console.WriteLine("Key (HEX):") PrintHex(cipher.Key) diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography/AriaEcbTransform.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography/AriaEcbTransform.vb index 20009d8..51c3e6a 100644 --- a/WelsonJS.Toolkit/WelsonJS.Cryptography/AriaEcbTransform.vb +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography/AriaEcbTransform.vb @@ -70,6 +70,12 @@ Public Class AriaEcbTransform Dim blockSize = InputBlockSize Dim buffer() As Byte + If paddingMode = PaddingMode.None Then + buffer = New Byte(inputCount - 1) {} + TransformBlock(input, inputOffset, inputCount, buffer, 0) + Return buffer + End If + If encrypt Then Dim paddedLength As Integer Select Case paddingMode diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography/HightAlgorithm.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightAlgorithm.vb new file mode 100644 index 0000000..a664032 --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightAlgorithm.vb @@ -0,0 +1,51 @@ +Imports System.Security.Cryptography + +Public Class HightAlgorithm + Inherits SymmetricAlgorithm + + Public Sub New() + LegalBlockSizesValue = New KeySizes() {New KeySizes(64, 64, 0)} + LegalKeySizesValue = New KeySizes() {New KeySizes(128, 128, 0)} + + Me.BlockSize = 64 + Me.KeySize = 128 + Me.FeedbackSize = 64 + + Me.Mode = CipherMode.ECB + Me.Padding = PaddingMode.PKCS7 + + Me.Key = New Byte(15) {} + Me.IV = New Byte(7) {} + End Sub + + Public Overrides Sub GenerateKey() + Using rng As New RNGCryptoServiceProvider() + rng.GetBytes(Me.Key) + End Using + End Sub + + Public Overrides Sub GenerateIV() + Using rng As New RNGCryptoServiceProvider() + rng.GetBytes(Me.IV) + End Using + End Sub + + Public Overrides Function CreateEncryptor(rgbKey As Byte(), rgbIV As Byte()) As ICryptoTransform + Return CreateTransform(rgbKey, rgbIV, True) + End Function + + Public Overrides Function CreateDecryptor(rgbKey As Byte(), rgbIV As Byte()) As ICryptoTransform + Return CreateTransform(rgbKey, rgbIV, False) + End Function + + Private Function CreateTransform(key As Byte(), iv As Byte(), encrypt As Boolean) As ICryptoTransform + Select Case Me.Mode + Case CipherMode.ECB + Return New HightEcbTransform(key, encrypt, Me.Padding) + Case Else + Throw New NotSupportedException("This mode not supported yet") + End Select + End Function + + ' TODO: CCM, GCM, CMAC +End Class \ No newline at end of file diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography/HightCore.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightCore.vb new file mode 100644 index 0000000..5bb666c --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightCore.vb @@ -0,0 +1,139 @@ +Public Class HightCore + Private ReadOnly roundKey(135) As Byte + Private Shared ReadOnly Delta As Byte() = { + &H5A, &H6D, &H36, &H1B, &HD, &H6, &H3, &H41, + &H60, &H30, &H18, &H4C, &H66, &H33, &H59, &H2C, + &H56, &H2B, &H15, &H4A, &H65, &H72, &H39, &H1C, + &H4E, &H67, &H73, &H79, &H3C, &H5E, &H6F, &H37, + &H5B, &H2D, &H16, &HB, &H5, &H42, &H21, &H50, + &H28, &H54, &H2A, &H55, &H6A, &H75, &H7A, &H7D, + &H3E, &H5F, &H2F, &H17, &H4B, &H25, &H52, &H29, + &H14, &HA, &H45, &H62, &H31, &H58, &H6C, &H76, + &H3B, &H1D, &HE, &H47, &H63, &H71, &H78, &H7C, + &H7E, &H7F, &H3F, &H1F, &HF, &H7, &H43, &H61, + &H70, &H38, &H5C, &H6E, &H77, &H7B, &H3D, &H1E, + &H4F, &H27, &H53, &H69, &H34, &H1A, &H4D, &H26, + &H13, &H49, &H24, &H12, &H9, &H4, &H2, &H1, + &H40, &H20, &H10, &H8, &H44, &H22, &H11, &H48, + &H64, &H32, &H19, &HC, &H46, &H23, &H51, &H68, + &H74, &H3A, &H5D, &H2E, &H57, &H6B, &H35, &H5A + } + + Public Sub New(userKey As Byte()) + If userKey.Length <> 16 Then Throw New ArgumentException("Key must be 16 bytes") + KeySchedule(userKey) + End Sub + + Private Sub KeySchedule(userKey As Byte()) + ' Whitening keys + For i As Integer = 0 To 3 + roundKey(i) = userKey(i + 12) + roundKey(i + 4) = userKey(i) + Next + + ' Round keys (SK008 to SK135) + For i As Integer = 0 To 7 + For j As Integer = 0 To 7 + Dim idx = 16 * i + j + roundKey(8 + idx) = CByte((userKey((j - i) And 7) + Delta(idx)) And &HFF) + roundKey(8 + idx + 8) = CByte((userKey(8 + ((j - i) And 7)) + Delta(idx + 8)) And &HFF) + Next + Next + End Sub + + Private Function RotL(x As Byte, n As Integer) As Byte + Return CByte(((x << n) Or (x >> (8 - n))) And &HFF) + End Function + + Private Function F0(x As Byte) As Byte + Return CByte(RotL(x, 1) Xor RotL(x, 2) Xor RotL(x, 7)) + End Function + + Private Function F1(x As Byte) As Byte + Return CByte(RotL(x, 3) Xor RotL(x, 4) Xor RotL(x, 6)) + End Function + + Public Sub EncryptBlock(input() As Byte, inOffset As Integer, output() As Byte, outOffset As Integer) + Dim X(7) As Byte + ' Initial whitening + X(0) = CByte((input(inOffset + 0) + roundKey(0)) And &HFF) + X(1) = input(inOffset + 1) + X(2) = CByte(input(inOffset + 2) Xor roundKey(1)) + X(3) = input(inOffset + 3) + X(4) = CByte((input(inOffset + 4) + roundKey(2)) And &HFF) + X(5) = input(inOffset + 5) + X(6) = CByte(input(inOffset + 6) Xor roundKey(3)) + X(7) = input(inOffset + 7) + + For r As Integer = 0 To 31 + Dim T0 = CByte(F0(X(1)) Xor roundKey(8 + 4 * r + 0)) + Dim T1 = CByte(F1(X(3)) Xor roundKey(8 + 4 * r + 1)) + Dim T2 = CByte(F0(X(5)) Xor roundKey(8 + 4 * r + 2)) + Dim T3 = CByte(F1(X(7)) Xor roundKey(8 + 4 * r + 3)) + + Dim tmp(7) As Byte + tmp(0) = X(1) + tmp(1) = CByte((X(2) + T0) And &HFF) + tmp(2) = X(3) + tmp(3) = CByte((X(0) + T1) And &HFF) + tmp(4) = X(5) + tmp(5) = CByte((X(6) + T2) And &HFF) + tmp(6) = X(7) + tmp(7) = CByte((X(4) + T3) And &HFF) + + Array.Copy(tmp, X, 8) + Next + + ' Final whitening + output(outOffset + 0) = CByte((X(0) + roundKey(4)) And &HFF) + output(outOffset + 1) = X(1) + output(outOffset + 2) = CByte(X(2) Xor roundKey(5)) + output(outOffset + 3) = X(3) + output(outOffset + 4) = CByte((X(4) + roundKey(6)) And &HFF) + output(outOffset + 5) = X(5) + output(outOffset + 6) = CByte(X(6) Xor roundKey(7)) + output(outOffset + 7) = X(7) + End Sub + + Public Sub DecryptBlock(input() As Byte, inOffset As Integer, output() As Byte, outOffset As Integer) + Dim X(7) As Byte + ' Final whitening undo + X(0) = CByte((input(inOffset + 0) - roundKey(4)) And &HFF) + X(1) = input(inOffset + 1) + X(2) = CByte(input(inOffset + 2) Xor roundKey(5)) + X(3) = input(inOffset + 3) + X(4) = CByte((input(inOffset + 4) - roundKey(6)) And &HFF) + X(5) = input(inOffset + 5) + X(6) = CByte(input(inOffset + 6) Xor roundKey(7)) + X(7) = input(inOffset + 7) + + For r As Integer = 31 To 0 Step -1 + Dim T3 = CByte(F1(X(6)) Xor roundKey(8 + 4 * r + 3)) + Dim T2 = CByte(F0(X(4)) Xor roundKey(8 + 4 * r + 2)) + Dim T1 = CByte(F1(X(2)) Xor roundKey(8 + 4 * r + 1)) + Dim T0 = CByte(F0(X(0)) Xor roundKey(8 + 4 * r + 0)) + + Dim tmp(7) As Byte + tmp(0) = CByte((X(3) - T1) And &HFF) + tmp(1) = X(0) + tmp(2) = CByte((X(1) - T0) And &HFF) + tmp(3) = X(2) + tmp(4) = CByte((X(7) - T3) And &HFF) + tmp(5) = X(4) + tmp(6) = CByte((X(5) - T2) And &HFF) + tmp(7) = X(6) + + Array.Copy(tmp, X, 8) + Next + + ' Initial whitening undo + output(outOffset + 0) = CByte((X(0) - roundKey(0)) And &HFF) + output(outOffset + 1) = X(1) + output(outOffset + 2) = CByte(X(2) Xor roundKey(1)) + output(outOffset + 3) = X(3) + output(outOffset + 4) = CByte((X(4) - roundKey(2)) And &HFF) + output(outOffset + 5) = X(5) + output(outOffset + 6) = CByte(X(6) Xor roundKey(3)) + output(outOffset + 7) = X(7) + End Sub +End Class diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography/HightEcbTransform.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightEcbTransform.vb new file mode 100644 index 0000000..40a215a --- /dev/null +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography/HightEcbTransform.vb @@ -0,0 +1,156 @@ +Imports System.Security.Cryptography + +Public Class HightEcbTransform + Implements ICryptoTransform + + Private ReadOnly rnd As New Random() + Private ReadOnly core As HightCore + Private ReadOnly encrypt As Boolean + Private ReadOnly paddingMode As PaddingMode + + Public Sub New(key As Byte(), encryptMode As Boolean, Optional mode As PaddingMode = PaddingMode.PKCS7) + core = New HightCore(key) + encrypt = encryptMode + paddingMode = mode + End Sub + + Public ReadOnly Property InputBlockSize As Integer Implements ICryptoTransform.InputBlockSize + Get + Return 8 + End Get + End Property + + Public ReadOnly Property OutputBlockSize As Integer Implements ICryptoTransform.OutputBlockSize + Get + Return 8 + End Get + End Property + + Public ReadOnly Property CanTransformMultipleBlocks As Boolean Implements ICryptoTransform.CanTransformMultipleBlocks + Get + Return True + End Get + End Property + + Public ReadOnly Property CanReuseTransform As Boolean Implements ICryptoTransform.CanReuseTransform + Get + Return True + End Get + End Property + + Public Function TransformBlock(input() As Byte, inputOffset As Integer, inputCount As Integer, + output() As Byte, outputOffset As Integer) As Integer Implements ICryptoTransform.TransformBlock + If inputCount <= 0 Then Return 0 + + Dim blockSize = InputBlockSize + Dim remaining = inputCount + Dim inPtr = inputOffset + Dim outPtr = outputOffset + + While remaining >= blockSize + If encrypt Then + core.EncryptBlock(input, inPtr, output, outPtr) + Else + core.DecryptBlock(input, inPtr, output, outPtr) + End If + inPtr += blockSize + outPtr += blockSize + remaining -= blockSize + End While + + Return inputCount - remaining + End Function + + Public Function TransformFinalBlock(input() As Byte, inputOffset As Integer, inputCount As Integer) As Byte() Implements ICryptoTransform.TransformFinalBlock + Dim blockSize = InputBlockSize + Dim buffer() As Byte + + If paddingMode = PaddingMode.None Then + buffer = New Byte(inputCount - 1) {} + TransformBlock(input, inputOffset, inputCount, buffer, 0) + Return buffer + End If + + If encrypt Then + Dim paddedLength As Integer + Select Case paddingMode + Case PaddingMode.Zeros + paddedLength = ((inputCount + blockSize - 1) \ blockSize) * blockSize + + Case PaddingMode.PKCS7, PaddingMode.ANSIX923, PaddingMode.ISO10126 + Dim padLen = blockSize - (inputCount Mod blockSize) + If padLen = 0 Then padLen = blockSize + paddedLength = inputCount + padLen + + Case Else + Throw New NotSupportedException("Unsupported padding mode: " & paddingMode.ToString()) + End Select + + buffer = New Byte(paddedLength - 1) {} + Array.Copy(input, inputOffset, buffer, 0, inputCount) + + Dim padVal As Byte = CByte(paddedLength - inputCount) + Select Case paddingMode + Case PaddingMode.PKCS7 + For i = inputCount To paddedLength - 1 + buffer(i) = padVal + Next + Case PaddingMode.ANSIX923 + For i = inputCount To paddedLength - 2 + buffer(i) = 0 + Next + buffer(paddedLength - 1) = padVal + Case PaddingMode.ISO10126 + For i = inputCount To paddedLength - 2 + buffer(i) = CByte(rnd.Next(0, 256)) + Next + buffer(paddedLength - 1) = padVal + End Select + + For i = 0 To buffer.Length - 1 Step blockSize + core.EncryptBlock(buffer, i, buffer, i) + Next + Return buffer + + Else + If (inputCount Mod blockSize) <> 0 Then + Throw New CryptographicException("Encrypted data is not a multiple of block size.") + End If + + buffer = New Byte(inputCount - 1) {} + TransformBlock(input, inputOffset, inputCount, buffer, 0) + + Dim padVal As Integer = buffer(buffer.Length - 1) + If padVal <= 0 OrElse padVal > blockSize Then + Throw New CryptographicException("Invalid padding.") + End If + + Select Case paddingMode + Case PaddingMode.PKCS7 + For i = buffer.Length - padVal To buffer.Length - 1 + If buffer(i) <> padVal Then + Throw New CryptographicException("Invalid PKCS7 padding value.") + End If + Next + Case PaddingMode.ANSIX923 + For i = buffer.Length - padVal To buffer.Length - 2 + If buffer(i) <> 0 Then + Throw New CryptographicException("Invalid ANSIX923 padding value.") + End If + Next + Case PaddingMode.ISO10126 + ' no need to check random bytes, only length byte matters + Case Else + Throw New NotSupportedException("Unsupported padding mode: " & paddingMode.ToString()) + End Select + + Dim result(buffer.Length - padVal - 1) As Byte + Array.Copy(buffer, 0, result, 0, result.Length) + Return result + End If + End Function + + Public Sub Dispose() Implements IDisposable.Dispose + ' No resources to dispose + End Sub +End Class diff --git a/WelsonJS.Toolkit/WelsonJS.Cryptography/SeedEcbTransform.vb b/WelsonJS.Toolkit/WelsonJS.Cryptography/SeedEcbTransform.vb index c7535a7..8d6640b 100644 --- a/WelsonJS.Toolkit/WelsonJS.Cryptography/SeedEcbTransform.vb +++ b/WelsonJS.Toolkit/WelsonJS.Cryptography/SeedEcbTransform.vb @@ -70,6 +70,12 @@ Public Class SeedEcbTransform Dim blockSize = InputBlockSize Dim buffer() As Byte + If paddingMode = PaddingMode.None Then + buffer = New Byte(inputCount - 1) {} + TransformBlock(input, inputOffset, inputCount, buffer, 0) + Return buffer + End If + If encrypt Then Dim paddedLength As Integer Select Case paddingMode