paint-brush
Verschlüsselung: Chiffren, Digests, Salt und IV – Was Sie wissen müssenvon@gliimlang
Neue Geschichte

Verschlüsselung: Chiffren, Digests, Salt und IV – Was Sie wissen müssen

von Gliimly14m2025/01/04
Read on Terminal Reader

Zu lang; Lesen

Verschlüsselung ist eine Methode, Daten in eine unbrauchbare Form zu bringen, die erst durch Entschlüsselung wieder nutzbar gemacht werden kann. Hier erfahren Sie, wie Sie Daten mit einem Passwort verschlüsseln und entschlüsseln.
featured image - Verschlüsselung: Chiffren, Digests, Salt und IV – Was Sie wissen müssen
Gliimly HackerNoon profile picture

Was ist Verschlüsselung?

Verschlüsselung ist eine Methode, um Daten in eine unbrauchbare Form zu bringen, die nur durch Entschlüsselung nutzbar gemacht werden kann. Der Zweck besteht darin, Daten nur denjenigen zugänglich zu machen, die sie entschlüsseln (d. h. nutzbar machen) können. Normalerweise müssen Daten verschlüsselt werden, um sicherzustellen, dass sie im Falle eines unbefugten Zugriffs nicht abgegriffen werden können. Es ist die letzte Verteidigungslinie, nachdem es einem Angreifer gelungen ist, Autorisierungssysteme und Zugriffskontrollen zu durchbrechen.

Dies bedeutet nicht, dass alle Daten verschlüsselt werden müssen, denn oft reichen Autorisierungs- und Zugriffssysteme aus, und außerdem geht die Ver- und Entschlüsselung von Daten mit Leistungseinbußen einher. Ob und wann die Daten verschlüsselt werden, ist eine Frage der Anwendungsplanung und Risikobewertung, und manchmal ist es auch eine gesetzliche Anforderung, wie z. B. bei HIPAA oder GDPR.

Daten können im Ruhezustand, beispielsweise auf der Festplatte, oder während der Übertragung, beispielsweise zwischen zwei Parteien, die über das Internet kommunizieren, verschlüsselt werden.

Hier erfahren Sie, wie Sie Daten mit einem Passwort ver- und entschlüsseln können, auch symmetrische Verschlüsselung genannt. Dieses Passwort muss beiden Parteien, die Informationen austauschen, bekannt sein.

Chiffre, Digest, Salt, Iterationen, IV

Um Verschlüsselung richtig und sicher zu verwenden, müssen einige Begriffe erläutert werden.


Eine Chiffre ist der Algorithmus, der zur Verschlüsselung verwendet wird. Beispielsweise ist AES256 eine Chiffre. Die meisten Menschen denken bei Verschlüsselung an eine Chiffre.


Ein Digest ist im Grunde eine Hash-Funktion, die verwendet wird, um das Passwort (also den Verschlüsselungsschlüssel) zu verschlüsseln und zu verlängern, bevor es von der Chiffre verwendet wird. Warum wird das gemacht? Zum einen wird dadurch ein gut randomisierter Hash eines Schlüssels mit einheitlicher Länge erstellt, der sich besser für die Verschlüsselung eignet. Es eignet sich auch sehr gut zum „Salting“, worüber wir als Nächstes sprechen werden.


Das „Salt“ ist eine Methode, um sogenannte „Rainbow“-Tabellen zu überwinden. Ein Angreifer weiß, dass zwei gehashte Werte immer noch genau gleich aussehen, wenn die Originale gleich aussehen. Wenn Sie jedoch den Salt-Wert zum Hashen hinzufügen, ist das nicht mehr der Fall. Es wird „Salt“ genannt, weil es gewissermaßen mit dem Schlüssel gemischt wird, um etwas anderes zu erzeugen.


Jetzt versucht eine Rainbow-Tabelle, bekannte Hashwerte mit vorkalkulierten Daten abzugleichen, um ein Kennwort zu erraten.


Normalerweise wird Salt für jeden Schlüssel zufällig generiert und mit diesem gespeichert. Um bekannte Hashes abzugleichen, müsste der Angreifer Rainbow-Tabellen für eine große Anzahl zufälliger Werte vorberechnen, was im Allgemeinen nicht machbar ist.

Sie werden bei der Verschlüsselung häufig von „Iterationen“ hören. Eine Iteration ist ein einzelner Zyklus, in dem ein Schlüssel und ein Salt so gemischt werden, dass das Erraten des Schlüssels schwieriger wird. Dies wird viele Male durchgeführt, um es einem Angreifer rechnerisch schwer zu machen, den Schlüssel zurückzuerraten, daher „Iterationen“ (Plural). Normalerweise beträgt die erforderliche Mindestanzahl von Iterationen 1000, aber es kann auch anders sein. Wenn Sie mit einem wirklich starken Passwort beginnen, brauchen Sie im Allgemeinen weniger.

IV (oder „Initialisierungsvektor“) ist normalerweise ein Zufallswert, der für die Verschlüsselung jeder Nachricht verwendet wird. Salt wird verwendet, um einen Schlüssel basierend auf einem Passwort zu erzeugen. Und IV wird verwendet, wenn Sie bereits einen Schlüssel haben und nun Nachrichten verschlüsseln. Der Zweck von IV besteht darin, dieselben Nachrichten beim Verschlüsseln unterschiedlich erscheinen zu lassen. Manchmal hat IV auch eine sequentielle Komponente, besteht also aus einer zufälligen Zeichenfolge plus einer Sequenz, die ständig zunimmt.


Dies erschwert „Replay“-Angriffe, bei denen der Angreifer eine Nachricht nicht entschlüsseln muss; vielmehr wurde eine verschlüsselte Nachricht „abgehört“ (d. h. zwischen Sender und Empfänger abgefangen) und dann erneut abgespielt, in der Hoffnung, die bereits ausgeführte Aktion zu wiederholen. In Wirklichkeit haben die meisten Protokolle auf hoher Ebene jedoch bereits eine Sequenz, bei der jede Nachricht eine steigende Paketnummer enthält, sodass IV dies in den meisten Fällen nicht benötigt.

Voraussetzungen

Dieses Beispiel verwendet das Gliimly- Framework. Installieren Sie es zuerst.

Verschlüsselungsbeispiel

Um die Beispiele hier auszuführen, erstellen Sie eine Anwendung „enc“ in einem eigenen Verzeichnis (weitere Informationen zum Programm-Manager von Gliimly finden Sie unter mgrg ):

 mkdir enc_example cd enc_example gg -k enc

Um Daten zu verschlüsseln, verwenden Sie die Anweisung „encrypt-data “. Die einfachste Form ist die Verschlüsselung einer nullterminierten Zeichenfolge. Erstellen Sie eine Datei „encrypt.gliim“ und kopieren Sie Folgendes:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str p-out dec_str @ end-handler

Sie können die grundlegende Verwendung von encrypt-data und decrypt-data sehen. Sie geben Daten (Original oder verschlüsselt) und das Passwort ein und schon kann es losgehen. Die Daten werden verschlüsselt und dann entschlüsselt, wodurch das Original zurückgegeben wird.

Im Quellcode enthält eine Zeichenfolgenvariable „enc_str“ (die als „char *“ erstellt wird) die verschlüsselte Version von „Dies enthält einen Geheimcode: Sesam, öffne dich!“ und „dec_str“ sind die entschlüsselten Daten, die genau identisch sein müssen.

Um diesen Code über die Befehlszeile auszuführen, erstellen Sie zuerst die Anwendung:

 gg -q

Lassen Sie Gliimly dann den Bash-Code zur Ausführung erstellen – der Anforderungspfad ist „/encrypt“, was in unserem Fall von der Funktion „void encrypt()“ verarbeitet wird, die in der Quelldatei „encrypt.gliim“ definiert ist. In Gliimly stimmen diese Namen immer überein, was das Schreiben, Lesen und Ausführen von Code erleichtert. Verwenden Sie die Option „-r“ in gg, um den Anforderungspfad anzugeben und den Code abzurufen, den Sie zum Ausführen des Programms benötigen:

 gg -r --req="/encrypt" --silent-header --exec


Sie erhalten eine Antwort wie diese:

 72ddd44c10e9693be6ac77caabc64e05f809290a109df7cfc57400948cb888cd23c7e98e15bcf21b25ab1337ddc6d02094232111aa20a2d548c08f230b6d56e9 This contains a secret code, which is Open Sesame!


Was Sie hier haben, sind die verschlüsselten Daten, und dann werden diese verschlüsselten Daten mit demselben Passwort entschlüsselt. Wenig überraschend stimmt das Ergebnis mit der Zeichenfolge überein, die Sie ursprünglich verschlüsselt haben.

Beachten Sie, dass encrypt-data standardmäßig verschlüsselte Werte in einer für Menschen lesbaren hexadezimalen Form erzeugt, d. h. bestehend aus den hexadezimalen Zeichen „0“ bis „9“ und „a“ bis „f“. Auf diese Weise können Sie die verschlüsselten Daten in einer regulären Zeichenfolge speichern. Sie können sie beispielsweise in ein JSON-Dokument, in eine VARCHAR-Spalte in einer Datenbank oder praktisch überall sonst einfügen. Sie können jedoch auch binär verschlüsselte Daten erzeugen. Mehr dazu in Kürze.

Daten in ein binäres Ergebnis verschlüsseln

Im vorherigen Beispiel liegen die resultierenden verschlüsselten Daten in einer für Menschen lesbaren hexadezimalen Form vor. Sie können auch binär verschlüsselte Daten erstellen, die keine für Menschen lesbare Zeichenfolge sind und außerdem kürzer sind. Verwenden Sie dazu die Klausel „binary“. Ersetzen Sie den Code in „encrypt.gliim“ durch:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" binary // Save the encrypted data to a file write-file "encrypted_data" from enc_str get-app directory to app_dir @Encrypted data written to file <<p-out app_dir>>/encrypted_data // Decrypt data decrypt-data enc_str password "my_password" binary to dec_str p-out dec_str @ end-handler

Wenn Sie binär verschlüsselte Daten erhalten möchten, sollten Sie auch deren Länge in Bytes angeben, da Sie sonst nicht wissen, wo sie enden, da sie möglicherweise Nullbytes enthalten. Verwenden Sie zu diesem Zweck die Klausel „output-length“. In diesem Code werden die verschlüsselten Daten in der Variable „enc_str“ in die Datei „encrypted_data“ geschrieben und die geschriebene Länge beträgt „outlen“ Bytes.


Wenn eine Datei ohne Pfad geschrieben wird, geschieht dies immer im Stammverzeichnis der Anwendung (siehe „Verzeichnisse“ ). Daher verwenden Sie „get-app“ , um dieses Verzeichnis abzurufen.

Beachten Sie beim Entschlüsseln von Daten die Verwendung der Klausel „input-length“. Sie gibt an, wie viele Bytes die verschlüsselten Daten haben. Natürlich können Sie dies der Variable „outlen“ entnehmen, in der encrypt-data die Länge der verschlüsselten Daten speichert. Wenn Verschlüsselung und Entschlüsselung entkoppelt sind, d. h. in getrennten Programmen ausgeführt werden, stellen Sie sicher, dass diese Länge verfügbar gemacht wird.

Beachten Sie auch, dass bei „binär“ verschlüsselten Daten (was bedeutet, dass eine binäre Ausgabe erzeugt wird) bei der Entschlüsselung dieselbe Methode verwendet werden muss.

Stellen Sie den Antrag:

 gg -q


Führen Sie es genauso aus wie zuvor:

 gg -r --req="/encrypt" --silent-header --exec


Das Ergebnis ist:

 Encrypted data written to file /var/lib/gg/enc/app/encrypted_data This contains a secret code, which is Open Sesame!


Die entschlüsselten Daten sind exakt dieselben wie das Original.

Sie können die tatsächlich verschlüsselten Daten, die in die Datei geschrieben wurden, mithilfe des Linux-Dienstprogramms „octal dump“ („od“) sehen:

 od -c /var/lib/gg/enc/app/encrypted_data


mit dem Ergebnis wie:

 $ od -c /var/lib/gg/enc/app/encrypted_data 0000000 r 335 324 L 020 351 i ; 346 254 w 312 253 306 N 005 0000020 370 \t ) \n 020 235 367 317 305 t \0 224 214 270 210 315 0000040 # 307 351 216 025 274 362 033 % 253 023 7 335 306 320 0000060 224 # ! 021 252 242 325 H 300 217 # \vm V 351 0000100

Da haben Sie es. Sie werden feststellen, dass die Daten binär sind und tatsächlich Nullbytes enthalten.

Binäre Daten verschlüsseln

Die zu verschlüsselnden Daten in diesen Beispielen sind ein String, also durch Nullen getrennt. Sie können binäre Daten genauso einfach verschlüsseln, indem Sie sie vollständig angeben (da Gliimly die Anzahl der Bytes im Auge behält!) oder indem Sie ihre Länge in der Klausel „input-length“ angeben. Kopieren Sie beispielsweise Folgendes in „encrypt.gliim“:

 begin-handler /encrypt public set-string str = "This c\000ontains a secret code, which is Open Sesame!" // Encrypt encrypt-data str to enc_str password "my_password" input-length 12 p-out enc_str @ // Decrypt decrypt-data enc_str password "my_password" to dec_str // Output binary data; present null byte as octal \000 string-length dec_str to res_len start-loop repeat res_len use i start-with 0 if-true dec_str[i] equal 0 p-out "\\000" else-if pf-out "%c", dec_str[i] end-if end-loop @ end-handler

Dadurch werden 12 Bytes am Speicherort „enc_str“ verschlüsselt, unabhängig von etwaigen Nullbytes. In diesem Fall ist das „This c“, gefolgt von einem Nullbyte, gefolgt von einer Zeichenfolge „ontain“, aber es können auch beliebige Binärdaten sein, beispielsweise der Inhalt einer JPG-Datei.

Auf der Entschlüsselungsseite erhalten Sie die Anzahl der in der Klausel „Ausgabelänge“ entschlüsselten Bytes. Schließlich wird gezeigt, dass die entschlüsselten Daten genau die Originaldaten sind, und das Nullbyte wird in einer typischen Oktaldarstellung dargestellt.

Stellen Sie den Antrag:

 gg -q


Führen Sie es genauso aus wie zuvor:

 gg -r --req="/encrypt" --silent-header --exec


Das Ergebnis ist:

 6bea45c2f901c0913c87fccb9b347d0a This c\000ontai


Der verschlüsselte Wert ist kürzer, da auch in diesem Fall die Daten kürzer sind und das Ergebnis exakt mit dem Original übereinstimmt.

Verwenden Sie eine beliebige Chiffre oder einen beliebigen Digest

Die standardmäßig verwendete Verschlüsselung ist AES256 und SHA256- Hashing aus der Standard -OpenSSL -Bibliothek, die beide in der Kryptographie weit verbreitet sind. Sie können jedoch jede verfügbare Chiffre und jeden Digest (d. h. Hash) verwenden, die von OpenSSL unterstützt werden (auch die benutzerdefinierten, die Sie bereitstellen).

Um zu sehen, welche Algorithmen verfügbar sind, tun Sie Folgendes in der Befehlszeile:

 #get list of cipher providers openssl list -cipher-algorithms #get list of digest providers openssl list -digest-algorithms


Diese beiden bieten eine Liste von Verschlüsselungs- und Digest-Algorithmen (Hash-Algorithmen). Einige davon sind möglicherweise schwächer als die von Gliimly ausgewählten Standardalgorithmen, und andere dienen möglicherweise nur der Abwärtskompatibilität mit älteren Systemen. Andere wiederum sind möglicherweise recht neu und hatten nicht genug Zeit, um in dem von Ihnen gewünschten Umfang validiert zu werden.


Seien Sie also vorsichtig bei der Auswahl dieser Algorithmen und stellen Sie sicher, dass Sie wissen, warum Sie die Standardalgorithmen ändern. Hier ist ein Beispiel für die Verwendung der Camellia-256-Verschlüsselung (d. h. „CAMELLIA-256-CFB1“) mit „SHA3-512“-Digest. Ersetzen Sie den Code in „encrypt.gliim“ durch:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Encrypt data encrypt-data str to enc_str password "my_password" \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out enc_str @ // Decrypt data decrypt-data enc_str password "my_password" to dec_str \ cipher "CAMELLIA-256-CFB1" digest "SHA3-512" p-out dec_str @ end-handler


Stellen Sie den Antrag:

 gg -q


Führen Sie es aus:

 gg -r --req="/encrypt" --silent-header --exec


In diesem Fall ist das Ergebnis:

 f4d64d920756f7220516567727cef2c47443973de03449915d50a1d2e5e8558e7e06914532a0b0bf13842f67f0a268c98da6 This contains a secret code, which is Open Sesame!

Sie erhalten erneut die Originaldaten. Beachten Sie, dass Sie sowohl beim Verschlüsseln als auch beim Entschlüsseln der Daten dieselbe Chiffre und denselben Digest verwenden müssen!

Sie können den verschlüsselten Binärwert natürlich genauso wie zuvor erzeugen, indem Sie die Klauseln „binary“ und „output-length“ verwenden.

Wenn Sie externe Systeme haben, die Daten verschlüsseln, und Sie wissen, welche Chiffre und Digests diese verwenden, können Sie diese abgleichen und Ihren Code interoperabel machen. Gliimly verwendet die Standard-OpenSSL-Bibliothek, daher ist es wahrscheinlich, dass dies auch bei anderer Software der Fall ist.

Mit Salz

Um der Verschlüsselung Salt hinzuzufügen, verwenden Sie die Klausel „salt“. Sie können zufällige Salts generieren, indem Sie die Anweisung random-string (oder random-crypto, falls erforderlich) verwenden. Hier ist der Code für „encrypt.gliim“:

 begin-handler /encrypt public set-string str = "This contains a secret code, which is Open Sesame!" // Get salt random-string to rs length 16 // Encrypt data encrypt-data str to enc_str password "my_password" salt rs @Salt used is <<p-out rs>>, and the encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs to dec_str p-out dec_str @ end-handler


Stellen Sie den Antrag:

 gg -q


Führen Sie es ein paar Mal aus:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


Das Ergebnis:

 Salt used is VA9agPKxL9hf3bMd, and the encrypted string is 3272aa49c9b10cb2edf5d8a5e23803a5aa153c1b124296d318e3b3ad22bc911d1c0889d195d800c2bd92153ef7688e8d1cd368dbca3c5250d456f05c81ce0fdd This contains a secret code, which is Open Sesame! Salt used is FeWcGkBO5hQ1uo1A, and the encrypted string is 48b97314c1bc88952c798dfde7a416180dda6b00361217ea25278791c43b34f9c2e31cab6d9f4f28eea59baa70aadb4e8f1ed0709db81dff19f24cb7677c7371 This contains a secret code, which is Open Sesame! Salt used is nCQClR0NMjdetTEf, and the encrypted string is f19cdd9c1ddec487157ac727b2c8d0cdeb728a4ecaf838ca8585e279447bcdce83f7f95fa53b054775be1bb2de3b95f2e66a8b26b216ea18aa8b47f3d177e917 This contains a secret code, which is Open Sesame!

Wie Sie sehen, wird für jede Verschlüsselung ein zufälliger Salt-Wert (in diesem Fall 16 Byte lang) generiert und der verschlüsselte Wert ist jedes Mal anders, obwohl die verschlüsselten Daten dieselben waren! Dies macht es schwierig, eine solche Verschlüsselung zu knacken.

Zum Entschlüsseln müssen Sie natürlich das Salt aufzeichnen und es genau so verwenden wie beim Verschlüsseln. Im Code hier enthält die Variable „rs“ das Salt. Wenn Sie die verschlüsselten Werte in der Datenbank speichern, würden Sie das Salt wahrscheinlich direkt daneben speichern.

Initialisierungsvektor

In der Praxis würden Sie nicht für jede Nachricht einen anderen Salt-Wert verwenden. Dadurch wird jedes Mal ein neuer Schlüssel erstellt, was die Leistung beeinträchtigen kann. Und das ist auch wirklich nicht nötig: Die Verwendung von Salt dient dazu, jeden Schlüssel (selbst die gleichen) viel schwerer zu erraten. Wenn Sie das einmal getan haben, müssen Sie es möglicherweise nicht oder nicht oft erneut tun.

Stattdessen würden Sie für jede Nachricht einen IV (Initialisierungsvektor) verwenden. Dabei handelt es sich normalerweise um eine zufällige Zeichenfolge, die dieselben Nachrichten unterschiedlich erscheinen lässt und den Rechenaufwand zum Knacken des Passworts erhöht. Hier ist der neue Code für „encrypt.gliim“:

 begin-handler /encrypt public // Get salt random-string to rs length 16 // Encrypt data start-loop repeat 10 use i start-with 0 random-string to iv length 16 encrypt-data "The same message" to enc_str password "my_password" salt rs iterations 2000 init-vector iv cache @The encrypted string is <<p-out enc_str>> // Decrypt data decrypt-data enc_str password "my_password" salt rs iterations 2000 init-vector iv to dec_str cache p-out dec_str @ end-loop end-handler


Stellen Sie den Antrag:

 gg -q


Führen Sie es ein paar Mal aus:

 gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec gg -r --req="/encrypt" --silent-header --exec


Das Ergebnis kann sein:

 The encrypted string is 787909d332fd84ba939c594e24c421b00ba46d9c9a776c47d3d0a9ca6fccb1a2 The same message The encrypted string is 7fae887e3ae469b666cff79a68270ea3d11b771dc58a299971d5b49a1f7db1be The same message The encrypted string is 59f95c3e4457d89f611c4f8bd53dd5fa9f8c3bbe748ed7d5aeb939ad633199d7 The same message The encrypted string is 00f218d0bbe7b618a0c2970da0b09e043a47798004502b76bc4a3f6afc626056 The same message The encrypted string is 6819349496b9f573743f5ef65e27ac26f0d64574d39227cc4e85e517f108a5dd The same message The encrypted string is a2833338cf636602881377a024c974906caa16d1f7c47c78d9efdff128918d58 The same message The encrypted string is 04c914cd9338fcba9acb550a79188bebbbb134c34441dfd540473dd8a1e6be40 The same message The encrypted string is 05f0d51561d59edf05befd9fad243e0737e4a98af357a9764cba84bcc55cf4d5 The same message The encrypted string is ae594c4d6e72c05c186383e63c89d93880c8a8a085bf9367bdfd772e3c163458 The same message The encrypted string is 2b28cdf5a67a5a036139fd410112735aa96bc341a170dafb56818dc78efe2e00 The same message

Sie können sehen, dass dieselbe Nachricht verschlüsselt anders aussieht, entschlüsselt jedoch wieder dieselbe ist. Natürlich müssen Passwort, Salt, Anzahl der Iterationen und Init-Vektor für Verschlüsselung und Entschlüsselung gleich sein.

Beachten Sie die Verwendung der „Cache“-Klausel in encrypt-data und decrypt-data. Sie speichert den berechneten Schlüssel (gegebenes Passwort, Salt, Cipher/Digest-Algorithmen und Anzahl der Iterationen) effektiv im Cache, sodass er nicht bei jedem Durchlauf der Schleife neu berechnet wird. Mit „Cache“ wird der Schlüssel einmal berechnet und dann wird für jede Nachricht ein anderer IV (in der „Init-Vector“-Klausel) verwendet.

Wenn Sie den Schlüssel gelegentlich neu erstellen möchten, verwenden Sie die Klausel „clear-cache“, die einen Booleschen Wert liefert. Wenn dieser Wert zutrifft, wird der Schlüssel neu berechnet, andernfalls bleibt er unverändert. Weitere Informationen hierzu finden Sie unter encrypt-data .

Abschluss

Sie haben gelernt, wie Sie Daten mithilfe verschiedener Chiffren, Digests, Salts und IV-Werte in Gliimly verschlüsseln und entschlüsseln. Sie können auch einen für Menschen lesbaren verschlüsselten Wert und eine binäre Ausgabe erstellen sowie sowohl Zeichenfolgen als auch binäre Werte (wie Dokumente) verschlüsseln.