PBToolboxAI v5 ← Site

crypto — n_pbt_crypto #

← Referência dos componentes · Índice do guia

Hashes, HMAC, cifra por palavra-passe, valores aleatórios e assinaturas RSA — a Web Crypto API do motor WebView2, que o seu PowerBuilder 10 não tem em mais lado nenhum. Sem DLL de terceiros, sem serviço: o que o posto já sabe fazer, oferecido ao PowerScript.

▶ Ver ao vivo — Aplicação de demonstração, mosaico Crypto: os resultados, o código que os produz e esta página, lado a lado.


Em resumo #

Objeto não visualn_pbt_crypto
Serve paraVerificar que um ficheiro não mudou, guardar um segredo num INI, assinar uma encomenda, autenticar uma chamada a uma API
PrincípioUma página oculta faz o cálculo; cada método é uma chamada PowerScript normal que devolve o seu valor
DependênciaO runtime WebView2, já exigido pela biblioteca — mais nada

Início rápido #

// Local variables
n_pbt_crypto lnv_crypto
string ls_hash, ls_cipher

// Create the object once, free it when done
lnv_crypto = create n_pbt_crypto

// The fingerprint of a text, in hexadecimal
ls_hash = lnv_crypto.of_sha256(/*text*/ "Invoice 4152, 690.00 EUR")

// A secret kept with a password : one string to store, the password to keep
ls_cipher = lnv_crypto.of_encrypt(/*password*/ "s3cret", /*text*/ "The safe code is 4152.")
MessageBox("Back", lnv_crypto.of_decrypt(/*password*/ "s3cret", /*cipher*/ ls_cipher))

// Free the object
destroy lnv_crypto

Cada método é uma chamada que devolve o seu valor: sem evento, sem espera a escrever.


Os formatos, legíveis pelas outras ferramentas #

Decifrar um valor de of_encrypt do lado do servidor, em Python (biblioteca cryptography):

import base64, struct
from cryptography.hazmat.primitives import hashes
from cryptography.hazmat.primitives.kdf.pbkdf2 import PBKDF2HMAC
from cryptography.hazmat.primitives.ciphers.aead import AESGCM

raw = base64.b64decode(value)
assert raw[:4] == b"PBK2"
rounds = struct.unpack(">I", raw[4:8])[0]
salt, nonce, data = raw[8:24], raw[24:36], raw[36:]   # data = ciphertext + 16-byte tag
key = PBKDF2HMAC(hashes.SHA256(), 32, salt, rounds).derive(password.encode("utf-8"))
text = AESGCM(key).decrypt(nonce, data, None).decode("utf-8")

Propriedades #

PropriedadeTipoPredefiniçãoFunção
il_timeout_mslong20000Quanto pode durar uma chamada à página oculta: gerar uma chave de 4096 bits, ou derivar uma chave com milhões de rondas PBKDF2, pode levar alguns segundos. Os hashes, os ficheiros e os blobs são nativos: nenhum prazo se aplica a eles
il_pbkdf2_roundslong100000Rondas PBKDF2 de of_encrypt e of_encrypt_file: cerca de 100 ms, pagos em cada leitura do valor. De 1.000 a 10.000.000: qualquer outro valor faz RECUSAR a chamada (of_get_last_error() di-lo, -5 para um ficheiro), nunca substituir Vale também para of_encrypt_blob. Um valor leva as suas rondas: mudá-las nunca torna ilegível um valor mais antigo
il_password_hash_roundslong600000Rondas PBKDF2 de of_password_hash: 600.000, o número da OWASP para PBKDF2-HMAC-SHA-256 (cerca de 0,6 s). Um hash guardado é o que um atacante força offline se a tabela vazar; verifica-se uma só vez por início de sessão. Mesmos limites Aumente-as, e of_password_needs_rehash diz que valores guardados renovar
il_jwt_leeway_secondslong60Tolerância de of_jwt_verify e of_jwt_verify_jwks sobre exp e nbf, em segundos: os relógios de duas máquinas derivam. De 0 a 86 400; qualquer outro valor faz recusar a verificação
ipo_ownerpowerobjectnullO objeto visual para o qual este utilitário trabalha: a licença verifica-se na sua classe. Define-se antes da primeira chamada. Necessário apenas na aplicação de demonstração; uma chave de desenvolvimento ou de execução desbloqueia o utilitário sem ele. Não desbloqueado, está em modo demo — textos de 2048 caracteres e ficheiros de 2048 bytes no máximo, tanto à entrada como à saída Os blobs seguem o limite dos ficheiros

Constantes: HASH_MD5, HASH_SHA1, HASH_SHA256, HASH_SHA384, HASH_SHA512 para o algoritmo; KEY_2048, KEY_3072, KEY_4096 para o tamanho de uma chave RSA, KEY_RSA_2048, KEY_RSA_3072, KEY_RSA_4096, KEY_EC_P256, KEY_EC_P384, KEY_ED25519 para o tipo de par; JWT_HS256, JWT_HS384, JWT_HS512, JWT_RS256, JWT_RS384, JWT_RS512, JWT_PS256, JWT_PS384, JWT_PS512, JWT_ES256, JWT_ES384, JWT_EDDSA; SIGN_PKCS1, SIGN_PSS para o esquema de uma assinatura RSA; CHARSET_ALL, CHARSET_ALPHANUM, CHARSET_LETTERS, CHARSET_DIGITS, CHARSET_HEX; KEYFORMAT_TEXT, KEYFORMAT_HEX, KEYFORMAT_BASE64 para a forma de uma chave HMAC.


Métodos #

MétodoFunção
of_get_last_error ( ) → stringPorque a última chamada devolveu uma cadeia vazia, false ou um código negativo: palavra-passe errada, ficheiro ilegível, algoritmo desconhecido, limite demo Um argumento NULL conta como vazio: o motivo nunca é NULL.
of_open ( ) → longCria a página oculta. Facultativo — cada método fá-lo — mas chamá-lo ao abrir a janela paga o custo uma vez. Devolve o handle (> 0) ou -2 se não foi possível criá-la (of_get_last_error() diz porquê)
of_is_open ( ) → booleanVerdadeiro assim que a página oculta existe
of_hash (string as_algorithm, string as_text) → stringO hash de um texto, em hexadecimal; HASH_* para o algoritmo, lido sem maiúsculas, hífenes nem espaços (SHA-256 vale HASH_SHA256). NATIVO para todos os algoritmos: nenhuma página é aberta para um hash. Vazio em caso de falha
of_sha256 (string as_text) → stringof_hash com HASH_SHA256 — o que mais se usa
of_hmac (string as_algorithm, string as_key, string as_text { , string as_key_format }) → stringO HMAC de um texto sob uma chave secreta, em hexadecimal — o que uma API pede para autenticar uma chamada. A chave é um TEXTO por omissão; KEYFORMAT_HEX ou KEYFORMAT_BASE64 dão os seus BYTES: um segredo entregue codificado, ou o resultado de um HMAC anterior (AWS SigV4 encadeia quatro). Uma chave vazia é recusada, e também uma chave PEM: nunca é um segredo HMAC O base64url também é lido.
of_hmac_verify (string as_algorithm, string as_key, string as_text, string as_expected { , string as_key_format }) → booleanVerdadeiro se as_expected é o HMAC de as_text sob as_key, comparado em TEMPO CONSTANTE — a verificação da assinatura de um webhook (Stripe, GitHub, Shopify) sem a denunciar, nunca if of_hmac(…) = cabeçalho. as_expected em hexadecimal (maiúsculas indiferentes) ou em base64; retire antes um prefixo como sha256=. Falso caso contrário as_expected também pode estar em base64url.
of_hash_file (string as_algorithm, string as_path) → stringO hash de um ficheiro do posto, de qualquer tipo e tamanho (lido por pedaços), sem o carregar no PowerBuilder. Um caminho RELATIVO procura-se na pasta atual, depois na pasta em que a aplicação arrancou, depois ao lado do EXE — a regra de todos os ficheiros deste componente. Vazio em caso de falha; of_get_last_error() distingue o algoritmo desconhecido do ficheiro ilegível
of_base64_encode (string as_text) → stringUm texto em base64 (os seus bytes UTF-8): o que um navegador ou o Python escreveria. Vazio em caso de falha
of_base64_decode (string as_base64) → stringO TEXTO de volta (UTF-8). Um valor que não é base64, ou cujos bytes não são um texto (um PDF, uma imagem), devolve uma cadeia vazia e of_get_last_error() — nunca ruído. Os bytes escrevem-se num ficheiro com of_base64_decode_to_file Ou of_base64_decode_blob, que os devolve num blob.
of_base64_encode_file (string as_path) → stringOs bytes de um ficheiro do posto em base64 — um anexo, uma imagem para uma chamada JSON — lidos pela DLL, nunca carregados no PowerBuilder. Até 32 MB numa aplicação de 32 bits (o IDE PowerBuilder é uma), 512 MB em 64 bits; para lá disso, of_base64_encode_file_to_file. Vazio em caso de falha; of_get_last_error() diz qual: um ficheiro grande demais para este processo, ilegível, ou o limite de demonstração
of_base64_encode_file_to_file (string as_source, string as_target) → longO base64 de um ficheiro escrito noutro ficheiro (ASCII, sem quebra de linha), pela DLL, em memória constante e seja qual for o tamanho: o que um grande envio manda. Caminhos como em of_hash_file. Devolve 0, -5 num caminho vazio ou num destino que depende da pasta atual de uma unidade (\x, C:x), -2 origem ilegível, -4 destino impossível de escrever ou, em demonstração, origem com mais de 2048 bytes
of_base64_decode_to_file (string as_base64, string as_path) → longEscreve os bytes de um valor base64 num ficheiro — o anexo com que uma API respondeu. Um valor VAZIO escreve um ficheiro vazio; um caminho RELATIVO é escrito na pasta em que a aplicação arrancou (a pasta atual ao carregar a biblioteca, a mesma no IDE e compilada), onde of_hash_file o reencontra. Devolve 0 uma vez escrito, -5 num caminho vazio ou que depende da pasta atual de uma unidade (\x, C:x), ou um valor que não é base64, -4 se o ficheiro não puder ser escrito ou, em demo, acima de 2048 bytes
of_encrypt (string as_password, string as_text) → stringCifra um texto com uma palavra-passe: uma única cadeia base64 a guardar, que leva as suas rondas PBKDF2 (ver Os formatos). Uma palavra-passe VAZIA é recusada — qualquer um releria o valor. Vazio em caso de falha
of_decrypt (string as_password, string as_cipher) → stringO texto de volta, com a mesma palavra-passe. Palavra-passe errada ou valor alterado: cadeia vazia e of_get_last_error(), nunca ruído. Um valor cujo conteúdo não é um texto (um ficheiro cifrado por of_encrypt_file) devolve vazio também: of_decrypt_file ou of_decrypt_blob sobre os seus bytes.
of_uuid ( ) → stringUm UUID aleatório (versão 4)
of_random_hex (integer ai_bytes) → stringai_bytes bytes aleatórios (1 a 4096) em hexadecimal: um sal, um token
of_generate_keypair (integer ai_bits, ref string as_public_pem, ref string as_private_pem) → longUm par RSA em PEM (KEY_2048, KEY_3072, KEY_4096). Devolve 0 quando ambas as chaves estão preenchidas, -5 em qualquer outro tamanho (ambas ficam vazias), -4 em caso de falha
of_sign (string as_private_pem, string as_text { , string as_scheme, string as_hash }) → stringA assinatura de um texto pela chave privada, em base64. A chave diz a sua família: RSA (RSASSA-PKCS1-v1_5, SHA-256), ECDSA P-256 (SHA-256, assinatura em DER — o que o openssl dgst e o Java SHA256withECDSA verificam) ou Ed25519 (64 bytes). Vazio em caso de falha as_scheme e as_hash: o que a outra parte espera — SIGN_PSS para uma chave RSA (sal do comprimento do hash, MGF1 com o mesmo hash), HASH_SHA384 ou HASH_SHA512; um texto vazio mantém os da chave; uma chave EC aceita um hash, nunca PSS; uma chave Ed25519 nem um nem outro. Uma chave PÚBLICA dada em vez dela é nomeada
of_verify (string as_public_pem, string as_text, string as_signature { , string as_scheme, string as_hash }) → booleanVerdadeiro se a assinatura foi feita exatamente sobre este texto pela chave privada correspondente; falso — não um erro — se o texto mudou. Uma assinatura ECDSA lê-se em DER (openssl, Java) ou em P1363 (`rs, 64 bytes: WebCrypto, .NET) Com o esquema e o hash do signatário (ver of_sign`).
of_generate_keypair (string as_kind, ref string as_public_pem, ref string as_private_pem) → longUm par por TIPO: KEY_RSA_2048/3072/4096, KEY_EC_P256 ou KEY_EC_P384 (assinaturas curtas) ou KEY_ED25519. Devolve 0, -5 num tipo que não é nenhum destes (nunca um par RSA em vez dele), -4 em caso de falha
of_key_kind (string as_pem) → stringA família de uma chave PEM: rsa, ec-p256, ec-p384 ou ed25519; só se lê o PRIMEIRO bloco do texto. Uma chave PKCS#1 (BEGIN RSA PRIVATE KEY), um certificado ou uma chave cifrada é NOMEADA em of_get_last_error(), com o comando openssl que a converte
of_jwk_to_pem (string as_jwk_json) → stringUma chave publicada como JWK (RFC 7517: kty RSA, EC P-256 / P-384 ou OKP Ed25519) em PEM, o que of_verify, of_jwt_verify e o openssl aceitam: pública em SPKI, privada em PKCS#8 quando o JWK tem d. Os seus membros de uso (alg, use, key_ops, kid) não fazem parte da chave. Vazio em caso de falha
of_pem_to_jwk (string as_pem) → stringUma chave PEM como JWK (JSON): o que um conjunto de chaves publica, ou o que uma API pede para registar. Um PEM privado dá um JWK privado (com d): guarde-o para si. Vazio em caso de falha
of_base64url_encode (string as_text) → stringbase64 seguro para URL (JWT, query string)
of_base64url_decode (string as_base64url) → stringO texto de volta; vazio se não for base64url, ou se os seus bytes não forem um texto
of_hex_encode (string as_text) → stringOs bytes UTF-8 de um texto em hexadecimal
of_hex_decode (string as_hex) → stringO texto de volta; vazio se não for hexadecimal, ou se os seus bytes não forem um texto
of_random_password (integer ai_length, string as_charset) → stringUma palavra-passe aleatória (4 a 256 caracteres) num conjunto CHARSET_* (vazio = CHARSET_ALL), sem viés. Vazia num comprimento errado ou num conjunto desconhecido
of_equals_constant_time (string as_a, string as_b) → booleanIgualdade em tempo constante: comparar um token ou um código sem o trair
of_password_hash (string as_password) → stringO que se GUARDA para uma palavra-passe: PBKDF2 com sal (il_password_hash_rounds, 600.000 rondas), rondas e sal no valor. Nunca o mesmo resultado duas vezes. Uma palavra-passe vazia faz hash (verificar uma palavra-passe vazia é legítimo). O formato é PRÓPRIO do PBToolboxAI (ver Os formatos)
of_password_verify (string as_password, string as_stored) → booleanVerdadeiro se a palavra-passe é a do valor guardado, comparada em tempo constante. Um valor cujas rondas saiam de 1 000 a 10 000 000 é recusado: um valor hostil não faz a verificação correr minutos
of_password_needs_rehash (string as_stored) → booleanVerdadeiro quando um valor guardado por of_password_hash tem MENOS rondas do que il_password_hash_rounds: logo após um of_password_verify bem-sucedido, quando a palavra-passe é conhecida, calcule de novo o hash e guarde o novo valor — o conselho da OWASP quando o custo sobe. Falso caso contrário; falso com of_get_last_error() quando as_stored não é um tal valor. Nenhuma página
of_totp_secret ( ) → stringUm segredo novo para os códigos de dois fatores (RFC 6238), em base32
of_totp_code (string as_secret { , string as_hash, integer ai_digits, integer ai_period }) → stringO código de seis dígitos do momento, o que o autenticador mostra Com definições: HASH_SHA1, HASH_SHA256 ou HASH_SHA512, 6 a 8 dígitos, um período de 1 a 3 600 segundos — as que of_totp_uri escreve
of_totp_verify (string as_secret, string as_code { , ref long al_step }) → booleanVerdadeiro se o código é o do instante, do passo anterior ou do seguinte. Com al_step, o PASSO de 30 s para o qual o código foi aceite (0 caso contrário): um código não deve servir duas vezes (RFC 6238) — guarde o último passo aceite para o utilizador e recuse um passo que não seja maior. Um segredo que contenha algo além de A-Z, 2-7, espaços ou = é recusado (um 0 digitado por um O) Os espaços que o utilizador escreve («864 010», como a aplicação o mostra) são ignorados.
of_totp_verify (string as_secret, string as_code, string as_hash, integer ai_digits, integer ai_period, ref long al_step) → booleanA mesma verificação com as definições de of_totp_code; al_step conta períodos de ai_period segundos
of_totp_uri (string as_secret, string as_account, string as_issuer { , string as_hash, integer ai_digits, integer ai_period }) → stringO URI otpauth:// a colocar num código QR para inscrever o utilizador O segredo escreve-se em base32 maiúsculo, sem espaços nem =; um segredo com outro carácter é recusado. Com as definições de of_totp_code, a aplicação mostra os códigos que ele calcula com elas
of_hotp_code (string as_secret, long al_counter) → stringO código de seis dígitos de um CONTADOR (HOTP, RFC 4226, SHA-1): um token físico, um código enviado por email — o servidor guarda o contador e incrementa-o a cada código aceite. al_counter: 0 ou mais. Vazio em caso de falha
of_generate_key ( ) → stringUma chave AES-256 nova, 32 bytes em hexadecimal
of_encrypt_with_key (string as_key, string as_text { , string as_aad }) → stringAES-256-GCM com uma chave EXPLÍCITA (hex ou base64): base64 de nonce + cifrado + tag, o que o openssl ou o Python decifram as_aad: DADOS ADICIONAIS, autenticados mas não cifrados — um número de encomenda, um identificador: o valor só se decifra com os mesmos. A chave também pode estar em base64url
of_decrypt_with_key (string as_key, string as_cipher { , string as_aad }) → stringO texto de volta com a mesma chave; vazio e of_get_last_error() caso contrário Com os MESMOS dados adicionais quando os havia.
of_rsa_encrypt (string as_public_pem, string as_text { , string as_hash }) → stringUm segredo curto cifrado para uma chave PÚBLICA: só a privada o lê. A configuração EXATA, para o outro lado: RSA-OAEP, SHA-256, MGF1 com SHA-256, sem label — o Java (OAEPWithSHA-256AndMGF1Padding) e o openssl pkeyutl usam MGF1-SHA-1 por omissão, ver Os formatos. 190 bytes no máximo com uma chave de 2048 bits; além disso, of_get_last_error() di-lo — cifre os dados com of_encrypt_with_key e só a sua chave assim. Uma chave PRIVADA no lugar da pública é nomeada as_hash: o hash de OAEP e de MGF1 — HASH_SHA1 é o que usam por omissão o openssl pkeyutl, o .NET (OaepSHA1) e o Java (OAEPPadding)
of_rsa_decrypt (string as_private_pem, string as_cipher { , string as_hash }) → stringO segredo de volta, com a chave privada Com o hash do remetente (HASH_SHA256 por omissão).
of_jwt_sign (string as_algorithm, string as_key, string as_claims_json, long al_expires_seconds { , string as_key_format { , string as_header_json } }) → stringUm JWT assinado: JWT_HS256 (segredo partilhado), JWT_RS256/JWT_ES256/JWT_EDDSA (chave privada); o algoritmo é OBRIGATÓRIO. iat acrescentado, exp se a duração for > 0 — um exp nos claims E uma duração é recusado — e um exp/iat/nbf fornecido tem de ser um número. O cabeçalho nomeia o algoritmo como registado (EdDSA). as_key_format: o segredo HS256 em KEYFORMAT_TEXT, KEYFORMAT_HEX ou KEYFORMAT_BASE64; uma chave PEM nunca é um segredo HS256. as_header_json: membros ACRESCENTADOS ao cabeçalho, por exemplo {"kid":"2026-09"} — a chave que uma API escolhe (Apple, qualquer fornecedor que roda as suas chaves); alg e typ continuam os da biblioteca. Vazio em caso de falha Também JWT_HS384/HS512, JWT_RS384/RS512, JWT_PS256/PS384/PS512 e JWT_ES384 (chave KEY_EC_P384). Uma chave da família errada, ou a sua metade PÚBLICA, é nomeada
of_jwt_verify (string as_algorithm, string as_key, string as_token { , string as_key_format { , string as_expected_claims_json } }) → stringOs claims (JSON) de um token cuja assinatura, algoritmo e datas estão certos (il_jwt_leeway_seconds de tolerância, um minuto por omissão). O algoritmo é OBRIGATÓRIO e comparado EXATAMENTE com o do cabeçalho: "" é recusado, e uma chave PEM nunca é um segredo HS — senão uma chave PÚBLICA servia de segredo e um token forjado passava. Um exp ou nbf que não é um número, e um cabeçalho crit, são recusados. as_key_format como em of_jwt_sign. as_expected_claims_json: os claims que devem lá estar com esse valor — o emissor (iss), a audiência (aud, encontrada também numa lista); um claim ausente ou diferente é recusado e nomeado. Vazio e of_get_last_error() caso contrário; um claim lê-se com n_pbt_json (of_load, depois of_get_string)
of_jwt_verify_jwks (string as_algorithm, string as_jwks_json, string as_token { , string as_expected_claims_json }) → stringOs claims de um token verificado por um CONJUNTO DE CHAVES (JWKS, RFC 7517) — o que Azure AD, Google, Auth0 ou Keycloak publicam no seu jwks_uri (leia-o com o restclient, guarde-o algumas horas): o kid do token escolhe a chave. Um token sem kid, um kid ausente do conjunto ou presente duas vezes, uma chave de outro algoritmo ou de outro uso (use = enc): recusados, of_get_last_error() diz qual. Datas e claims esperados como em of_jwt_verify
of_jwt_claims (string as_token) → stringOs claims SEM verificação, para ler quem o token nomeia antes de escolher a chave
of_jwt_header (string as_token) → stringO cabeçalho (JSON) de um token SEM verificação: ler o seu kid para escolher a chave que o verificará. Nunca confiar só nele
of_protect (string as_text { , boolean ab_machine { , string as_entropy } }) → stringUm segredo protegido pelo Windows para este utilizador (ou esta máquina): DPAPI, nativo. O que se guarda no INI para uma palavra-passe de servidor. NÃO o protege de outro programa lançado pelo mesmo utilizador (nem, com ab_machine, de outra conta do posto): as_entropy, um segredo da sua aplicação, reduz esse círculo — a mesma entropia é exigida para o reler
of_unprotect (string as_protected { , string as_entropy }) → stringO texto de volta, na mesma conta e com a MESMA entropia se houver uma; vazio e of_get_last_error() noutro lado
of_crc32 (string as_text) → stringO CRC32 de um texto, 8 dígitos hexadecimais — um controlo de integridade, não um hash
of_crc32_file (string as_path) → stringO CRC32 de um ficheiro, lido pela DLL até ao fim: uma leitura que falha a meio é um erro, nunca o CRC do início
of_encrypt_file (string as_password, string as_source, string as_target) → longUm ficheiro cifrado por palavra-passe, em nativo e por pedaços (o mesmo contentor que of_encrypt), até 64 GB — o limite de um contentor AES-GCM. Caminhos relativos: a origem procura-se como para of_hash_file, o destino escreve-se na pasta em que a aplicação arrancou. Devolve 0, -5 argumento vazio ou il_pbkdf2_rounds fora dos limites, -2 origem ilegível, -4 destino não gravável ou, em demo, origem acima de 2048 bytes, -7 origem acima de 64 GB Um destino que depende da pasta atual de uma unidade (\x, C:x) devolve -5, e of_get_last_error() di-lo
of_decrypt_file (string as_password, string as_source, string as_target) → longO ficheiro de volta. Devolve 0, -3 palavra-passe errada ou ficheiro alterado (nada é escrito: o texto é verificado num ficheiro temporário oculto que o Windows apaga mesmo que a aplicação morra), -5/-2/-4 como of_encrypt_file
of_base64_encode_blob (blob ab_data) → stringOs bytes de um blob em base64 — uma foto lida por SelectBlob, um PDF recebido — sem ficheiro. Nativo. Até 32 MB em 32 bits, 512 MB em 64 bits; em demonstração, 2048 bytes. Vazio em caso de falha
of_base64_decode_blob (string as_base64, ref blob ab_data) → longOs BYTES de um valor base64 (ou base64url) num blob — um anexo recebido, pronto para UpdateBlob. Nativo. Devolve 0 assim que ab_data estiver preenchido, -5 quando o valor não é base64, -4 em demonstração acima de 2048 bytes; ab_data fica então vazio
of_hash_blob (string as_algorithm, blob ab_data) → stringO hash dos bytes de um blob, com os algoritmos de of_hash — o mesmo valor que of_hash_file sobre os mesmos bytes. Nativo. Vazio em caso de falha
of_hmac_blob (string as_algorithm, string as_key, blob ab_data { , string as_key_format }) → stringO HMAC dos bytes de um blob com uma chave secreta, a chave como em of_hmac: um ficheiro recebido, verificado pela assinatura que o serviço enviou com ele. Nativo. Vazio em caso de falha
of_crc32_blob (blob ab_data) → stringO CRC32 dos bytes de um blob, 8 dígitos hexadecimais. Nativo
of_encrypt_blob (string as_password, blob ab_data, ref blob ab_cipher) → longOs bytes de um blob cifrados por palavra-passe: o contentor de of_encrypt e de of_encrypt_file, em bytes (52 a mais do que os dados) — lido em base64, decifra-se com of_decrypt, e o inverso. Nativo, il_pbkdf2_rounds. Devolve 0 assim que ab_cipher estiver preenchido, -5 palavra-passe vazia ou rondas fora dos limites, -4 em caso de falha ou, em demonstração, acima de 2048 bytes
of_decrypt_blob (string as_password, blob ab_cipher, ref blob ab_data) → longOs bytes de of_encrypt_blob (ou de um valor de of_encrypt lido em bytes), com a mesma palavra-passe. Devolve 0 assim que ab_data estiver preenchido, -3 palavra-passe errada ou bytes alterados (ab_data fica vazio), -5 palavra-passe vazia ou bytes que não são um tal valor, -4 em demonstração acima de 2048 bytes
of_close ( ) → longLiberta a página oculta; feito por si ao destruir o objeto Devolve 0 uma vez feito.
of_reset ( ) → longVolta aos valores por omissão: il_timeout_ms, il_pbkdf2_rounds, il_password_hash_rounds, il_jwt_leeway_seconds, of_get_last_error(); ipo_owner mantém-se Devolve 0 uma vez feito.

Exemplos #

Verificar que um ficheiro não mudou #

// Local variables
string ls_expected, ls_actual

// The fingerprint recorded at deploy time, and the one of the file on this workstation
ls_expected = ProfileString("deploy.ini", "files", "orders.pbd", "")
ls_actual = lnv_crypto.of_hash_file(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*path*/ "C:\MyApp\orders.pbd")
if ls_actual <> ls_expected then MessageBox("Deploy", "orders.pbd is not the file that was tested.")

Um anexo em base64 para uma API #

// Local variables
n_pbt_json lnv_j
n_pbt_restclient lnv_rest

// The REST client of the window
lnv_rest = create n_pbt_restclient

// Sending : the file's bytes in the JSON body
lnv_j.of_set_string(/*path*/ "filename", /*value*/ "invoice_4152.pdf")
lnv_j.of_set_string(/*path*/ "content", /*value*/ lnv_crypto.of_base64_encode_file(/*path*/ "C:\invoices\4152.pdf"))
lnv_rest.of_post(/*url*/ "https://api.example.com/documents", /*body*/ lnv_j.of_text())

// Receiving : the answer's attachment, back on disk
lnv_crypto.of_base64_decode_to_file(/*base64*/ lnv_rest.of_json_value(/*path*/ "content"), /*path*/ "C:\inbox\receipt.pdf")

// Free the client
destroy lnv_rest

Guardar um segredo num INI #

// Local variables
string ls_master, ls_db_password

// At setup : encrypt once, store the string
SetProfileString("app.ini", "db", "password", lnv_crypto.of_encrypt(/*password*/ ls_master, /*text*/ ls_db_password))

// At run time : the master password comes from the user, the INI gives the rest
ls_db_password = lnv_crypto.of_decrypt(/*password*/ ls_master, /*cipher*/ ProfileString("app.ini", "db", "password", ""))
if ls_db_password = "" then MessageBox("Login", lnv_crypto.of_get_last_error())

Assinar uma encomenda, verificá-la noutro lado #

// Local variables
string ls_public, ls_private, ls_signature, ls_order_json

// The order to sign, as the application writes it
ls_order_json = '{"order":4152,"amount":690.00}'

// Sign the order with the private key
lnv_crypto.of_generate_keypair(/*bits*/ n_pbt_crypto.KEY_2048, /*public_pem*/ ls_public, /*private_pem*/ ls_private)   // once ; keep ls_private
ls_signature = lnv_crypto.of_sign(/*private_pem*/ ls_private, /*text*/ ls_order_json)

// The public key and the signature travel with the order ; anyone can check :
if not lnv_crypto.of_verify(/*public_pem*/ ls_public, /*text*/ ls_order_json, /*signature*/ ls_signature) then MessageBox("Order", "This order was altered.")

Um token para uma API, e a sua verificação #

// Local variables
string ls_api_secret, ls_token, ls_claims, ls_role
n_pbt_restclient lnv_rest
n_pbt_json lnv_json

// The REST client of the window, and the secret the API shares with you
lnv_rest = create n_pbt_restclient
ls_api_secret = ProfileString("app.ini", "api", "secret", "")

// The client : a token valid one hour, in the Authorization header
ls_token = lnv_crypto.of_jwt_sign(/*algorithm*/ n_pbt_crypto.JWT_HS256, /*key*/ ls_api_secret, /*claims_json*/ '{"sub":"jdoe","role":"admin"}', /*expires_seconds*/ 3600)
lnv_rest.of_set_bearer(/*token*/ ls_token)

// The server side (or a check of what came back) : the claims, once the signature and the expiry are right
ls_claims = lnv_crypto.of_jwt_verify(/*algorithm*/ n_pbt_crypto.JWT_HS256, /*key*/ ls_api_secret, /*token*/ ls_token)
if ls_claims = "" then MessageBox("API", lnv_crypto.of_get_last_error())

// One claim, read with n_pbt_json
lnv_json.of_load(/*json*/ ls_claims)
ls_role = lnv_json.of_get_string(/*path*/ "role")
destroy lnv_rest

Verificar a assinatura de um webhook #

// Local variables
string ls_header, ls_signature, ls_raw_body, ls_webhook_secret

// ls_raw_body and ls_header : the body and the signature header your web service received ;
// the secret, the one the service shares with you
ls_webhook_secret = ProfileString("app.ini", "webhook", "secret", "")

// The service signs the raw body with the shared secret and sends the HMAC in a header
// (GitHub : "sha256=<hex>", Stripe : hex, Shopify : base64). Compare in CONSTANT time.
ls_signature = ls_header
if Left(ls_signature, 7) = "sha256=" then ls_signature = Mid(ls_signature, 8)
if not lnv_crypto.of_hmac_verify(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*key*/ ls_webhook_secret, /*text*/ ls_raw_body, /*expected*/ ls_signature) then
	MessageBox("Webhook", "This call was not sent by the service.")
	return
end if

Uma palavra-passe de servidor guardada pelo Windows, e um utilizador com dois fatores #

// Local variables
string ls_db_password, ls_secret, ls_login, ls_typed, ls_stored_hash, ls_six_digits
long ll_step, ll_last_step

// ls_login, ls_typed and ls_six_digits come from the login window ; ls_secret,
// ls_stored_hash and ll_last_step from the record of the user. At setup : the INI holds a value Windows protects for this account, never the password ;
// the entropy, a secret of the application, keeps other programs of the same user out
SetProfileString("app.ini", "db", "password", lnv_crypto.of_protect(/*text*/ ls_db_password, /*machine*/ false, /*entropy*/ "MyApp-7f3a"))

// At run time : the same entropy
ls_db_password = lnv_crypto.of_unprotect(/*protected*/ ProfileString("app.ini", "db", "password", ""), /*entropy*/ "MyApp-7f3a")

// Enrolling a user : a secret in the database, the QR on screen
ls_secret = lnv_crypto.of_totp_secret()
uo_qr.is_data = lnv_crypto.of_totp_uri(/*secret*/ ls_secret, /*account*/ ls_login, /*issuer*/ "MyApp")

// Logging in : the stored hash, then the six digits - and never the same code twice (RFC 6238)
if lnv_crypto.of_password_verify(/*password*/ ls_typed, /*stored*/ ls_stored_hash) and lnv_crypto.of_totp_verify(/*secret*/ ls_secret, /*code*/ ls_six_digits, /*step*/ ll_step) then
	if ll_step > ll_last_step then ll_last_step = ll_step   // store it with the user ; accept the login
end if

Verificar um token do Azure AD, da Google ou do Auth0 (JWKS) #

// Local variables
n_pbt_restclient lnv_rest
string ls_jwks, ls_token, ls_claims

// ls_token : the token the user brought (the Authorization header). The key set of the identity provider : fetched once, kept a few hours
lnv_rest = create n_pbt_restclient
if lnv_rest.of_get(/*url*/ "https://login.microsoftonline.com/common/discovery/v2.0/keys") = 200 then ls_jwks = lnv_rest.of_response_text()
destroy lnv_rest

// The kid of the token picks the key ; the issuer and the audience must be yours
ls_claims = lnv_crypto.of_jwt_verify_jwks(/*algorithm*/ n_pbt_crypto.JWT_RS256, /*jwks_json*/ ls_jwks, /*token*/ ls_token, /*expected_claims_json*/ '{"iss":"https://login.microsoftonline.com/my-tenant-id/v2.0","aud":"api://orders"}')
if ls_claims = "" then MessageBox("Login", lnv_crypto.of_get_last_error())

Uma foto da base de dados, em base64 (blob) #

// Local variables
blob lb_photo
string ls_answer
n_pbt_json lnv_j

// ls_answer : the base64 an API answered. The photo of the employee, read from the database
SELECTBLOB photo INTO :lb_photo FROM employee WHERE emp_id = 4152 USING SQLCA;

// Its bytes in base64 in the JSON body of a call, with its fingerprint
lnv_j.of_set_string(/*path*/ "photo", /*value*/ lnv_crypto.of_base64_encode_blob(/*data*/ lb_photo))
lnv_j.of_set_string(/*path*/ "sha256", /*value*/ lnv_crypto.of_hash_blob(/*algorithm*/ n_pbt_crypto.HASH_SHA256, /*data*/ lb_photo))

// The other way : the bytes an API answered, back into the database
if lnv_crypto.of_base64_decode_blob(/*base64*/ ls_answer, /*data*/ lb_photo) = 0 then
	UPDATEBLOB employee SET photo = :lb_photo WHERE emp_id = 4152 USING SQLCA;
end if

Boas práticas #

← Referência dos componentes · Índice do guia