cryptlib
 
A powerful security toolkit which allows even inexperienced crypto programmers to easily add encryption and authentication services to their software.

Website: https://cryptlib.com/
Platforms supported: Windows, Linux (a matching native cryptlib library is required)
Headers to include: cryptlib.bi
Header version: 3.4.5; the hashing example uses APIs also present in cryptlib 3.4.9.5
Examples: in examples/math/cryptlib/

The example calculates SHA-256 and SHA-1 over the exact file bytes, including embedded nulls and partial final blocks. SHA-256 is the primary digest; SHA-1 is shown for comparison with older data. These are unkeyed digests, not message authentication codes.

cryptlib 3.4.9.5 keeps MD5 internal to older TLS mechanisms and rejects public MD5 contexts with CRYPT_ERROR_NOTAVAIL. Enabling USE_DEPRECATED_ALGORITHMS does not remove that public API restriction. The example therefore uses the public SHA-256 selector CRYPT_ALGO_SHA2.

Run hash filename for the original key-wait behavior, or hash filename --no-pause for an unattended run. Native initialization, context operations, file reads and cleanup are checked. On failure the example exits with status 1 and prints an error instead of an empty or incomplete digest.

Native Linux amd64 qualification uses the real cryptlib 3.4.9.5 source, independent C structure/constant checks and both FreeBASIC backends. It checks incremental hashing, guard bytes, empty and binary files, partial final blocks and failing input statuses. The SHA-256 and SHA-1 bytes are compared with independently calculated digests. The library is built in a private prefix; these example checks do not qualify a cryptographic service deployment.

Example

'' examples/manual/libraries/cryptlib.bas
'' Purpose: Hash a binary file with the native cryptlib API.
'' Responsibilities: Exact reads, checked native status and owned cleanup.
'' This example contains no network protocol or library configuration.
''
'' Example extracted from the FreeBASIC Manual
'' from topic 'cryptlib'
''
'' See Also: https://www.freebasic.net/wiki/wikka.php?wakka=ExtLibcryptlib
'' --------

#include Once "cryptlib.bi"

Function calc_hash( ByVal filename As String, ByVal algo As CRYPT_ALGO_TYPE, ByRef status As Long ) As String
    Const BUFFER_SIZE = 8192
    Dim As Byte buffer( 0 To BUFFER_SIZE-1 )
    Dim As String result

    '' The context owns the algorithm state. Every path after creation releases it.
    Dim As CRYPT_CONTEXT ctx
    status = cryptCreateContext( @ctx, CRYPT_UNUSED, algo )
    If cryptStatusError( status ) Then Return ""

    Dim As Integer f = FreeFile()
    If Open( filename For Binary Access Read As #f ) <> 0 Then
        status = CRYPT_ERROR_READ
        cryptDestroyContext( ctx )
        Return ""
    End If

    Do Until EOF( f )
        '' GET succeeds on a partial final block. Hash only the reported bytes,
        '' never the unused tail of the fixed buffer or a saved error value.
        Dim As UInteger readlength
        Dim As Long read_error = Get( #f, , buffer(), , readlength )
        If read_error <> 0 OrElse readlength > BUFFER_SIZE Then
            status = CRYPT_ERROR_READ
            Exit Do
        End If
        If readlength = 0 Then
            If EOF( f ) = 0 Then status = CRYPT_ERROR_READ
            Exit Do
        End If
        status = cryptEncrypt( ctx, @buffer(0), CLng( readlength ) )
        If cryptStatusError( status ) Then Exit Do
    Loop

    If Close( f ) <> 0 AndAlso cryptStatusOK( status ) Then status = CRYPT_ERROR_READ
    If cryptStatusOK( status ) Then
        '' A zero-length update completes the hash. Keep a valid buffer address
        '' because the native API declares this pointer nonnull, even at length zero.
        status = cryptEncrypt( ctx, @buffer(0), 0 )
    End If
    If cryptStatusOK( status ) Then
        Dim As Long buffersize
        status = cryptGetAttributeString( ctx, CRYPT_CTXINFO_HASHVALUE, @buffer(0), @buffersize )
        If cryptStatusOK( status ) Then
            If buffersize <= 0 OrElse buffersize > CRYPT_MAX_HASHSIZE Then
                status = CRYPT_ERROR_OVERFLOW
            Else
                result = String( buffersize * 2, " " )
                For i As Long = 0 To buffersize - 1
                    '' result has exactly two writable characters for every digest byte.
                    '' FB-LINTER: DISABLE-NEXT-LINE FBL514
                    Mid( result, i * 2 + 1, 2 ) = Right( "0" & Hex( CUByte( buffer(i) ) ), 2 )
                Next
            End If
        End If
    End If

    Dim As Long release_error = cryptDestroyContext( ctx )
    If cryptStatusOK( status ) Then status = release_error
    Return result
End Function

Dim As String filename = Command(1)
Dim As String mode = Command(2)
If filename = "" OrElse (mode <> "" AndAlso mode <> "--no-pause") OrElse Command(3) <> "" Then
    Print "Usage: hash filename [--no-pause]"
    End 1
End If

Dim As Long status = cryptInit()
If cryptStatusError( status ) Then
    Print "cryptInit failed: "; status
    End 1
End If
'' SHA-256 is the primary file digest. SHA-1 remains for legacy comparison;
'' neither output is a keyed authentication code. Current cryptlib keeps MD5
'' internal and rejects attempts to create it through this public API.
Dim As String sha256_digest = calc_hash( filename, CRYPT_ALGO_SHA2, status )
If cryptStatusError( status ) Then
    cryptEnd()
    Print "SHA-256 failed: "; status
    End 1
End If
Dim As String sha1_digest = calc_hash( filename, CRYPT_ALGO_SHA1, status )
If cryptStatusError( status ) Then
    cryptEnd()
    Print "SHA-1 failed: "; status
    End 1
End If
status = cryptEnd()
If cryptStatusError( status ) Then
    Print "cryptEnd failed: "; status
    End 1
End If

'' Calculate both results before printing, so a failed API call cannot leave
'' an apparently successful empty digest.
Print "sha256: "; sha256_digest
Print "sha1:   "; sha1_digest
If mode <> "--no-pause" Then Sleep

'' end of cryptlib.bas