Slide 60 de 74

Criptografia

Calculando hash
proc main(): void {
	echo hive.crypto.sha256("hive")
	echo hive.crypto.sha512("hive")
	echo hive.crypto.hmacSha256("hive", bypass("secret"))
}
Cifrando e decifrando
proc main(): void {
	password := bypass("password")
	sealed := hive.crypto.encrypt(bypass("um segredo"), password)
	echo sealed
	if hive.crypto.decrypt(sealed, password) is Result.Ok(plain) {
		echo reveal(plain)
	}
}
Codificação e bytes aleatórios
proc main(): void {
	encoded := hive.crypto.base64Encode("hive")
	if hive.crypto.base64Decode(encoded) is Result.Ok(decoded) {
		echo decoded
	}
	echo hive.crypto.randomHex(8)
	if hive.crypto.randomSecret(8) is Result.Ok(key) {
		echo len(reveal(key))   // 16
	}
}
JWTs
type Claims { user: Str }

proc main(): void {
	token := encode(Claims("ada"), hive.crypto.jwtCodec(bypass("secret")))

	// Confere a assinatura e os prazos, e então lê as claims.
	if Claims.decode(token, hive.crypto.jwtCodec(bypass("secret"))) is Result.Ok(claims) {
		echo claims.user
	}
	echo hive.crypto.jwtHeader(token)
}

hive.crypto é pura, então funciona tanto em um func quanto em um proc.

  • sha256(input) e sha512(input) calculam hashes, e hmacSha256(input, key) autentica com uma chave Secret — cada um respondendo em hexadecimal minúsculo.
  • encrypt(plaintext, password) sela um Secret sob uma senha Secret com AES-256-GCM, a chave esticada com 600.000 rodadas de PBKDF2 e um salt e um nonce novos a cada chamada, então o mesmo texto nunca é cifrado igual duas vezes. O texto cifrado é uma Str, segura para imprimir e guardar, e decrypt(ciphertext, password) o abre num Secret sem que o texto claro jamais seja uma Str.
  • base64Encode(input) e base64Decode(input) usam base64 padrão com preenchimento; randomHex(bytes) sorteia essa quantidade de bytes aleatórios, como o dobro de dígitos hexadecimais, e randomSecret(bytes) faz o mesmo direto num Secret, respondendo com um Result<Secret, SecretError>.

JSON Web Tokens são um codec, jwtCodec(secret) com um Secret, nos mesmos dois lugares do de JSON: encode(claims, hive.crypto.jwtCodec(secret)) assina um valor de um tipo seu, e Claims.decode(token, hive.crypto.jwtCodec(secret)) confere a assinatura antes de ler as claims de volta. Só HS256 é aceito, o que fecha de vez o clássico ataque de confusão de algoritmo. Um token só expira se o tipo das suas claims declarar exp: Int — ou nbf, não antes de — em segundos Unix, que então são conferidos; encode não acrescenta nenhum dos dois. Uma falha é um hive.crypto.JwtError, com path $ para um token que não confere e o caminho de um campo para claims com a forma errada. jwtDecode(token) lê as claims como JSON sem conferir nada, só para inspeção, e jwtHeader(token) lê alg, typ e kid — o kid sendo vazio nos tokens que o Hive assina, que têm um cabeçalho fixo.

O que pode falhar — decrypt, base64Decode, jwtDecode, jwtHeader — responde com um Result cujo hive.crypto.CryptoError tem um reason, como "Malformed", "BadSignature" (uma senha errada, ou uma mensagem que alguém alterou), "Expired", "NotYetValid", "AlgorithmMismatch" ou, vindo do decrypt, o "LimitExceeded" de um SecretError, e uma message.