Slide 60 of 74

Cryptography

Hashing
proc main(): void {
	echo hive.crypto.sha256("hive")
	echo hive.crypto.sha512("hive")
	echo hive.crypto.hmacSha256("hive", bypass("secret"))
}
Encryption
proc main(): void {
	password := bypass("password")
	sealed := hive.crypto.encrypt(bypass("a secret"), password)
	echo sealed
	if hive.crypto.decrypt(sealed, password) is Result.Ok(plain) {
		echo reveal(plain)
	}
}
Encoding and random bytes
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")))

	// Checks the signature and the times, then reads the claims.
	if Claims.decode(token, hive.crypto.jwtCodec(bypass("secret"))) is Result.Ok(claims) {
		echo claims.user
	}
	echo hive.crypto.jwtHeader(token)
}

hive.crypto is pure, so it works in a func as well as a proc.

  • sha256(input) and sha512(input) hash, and hmacSha256(input, key) authenticates with a Secret key — each answering in lowercase hex.
  • encrypt(plaintext, password) seals a Secret under a Secret password with AES-256-GCM, the key stretched with 600,000 rounds of PBKDF2 and a fresh salt and nonce every call, so the same text never encrypts alike twice. The ciphertext is a Str, safe to print and store, and decrypt(ciphertext, password) opens it into a Secret without the plaintext ever being a Str.
  • base64Encode(input) and base64Decode(input) use standard base64 with padding; randomHex(bytes) draws that many random bytes, as twice as many hex digits, and randomSecret(bytes) does the same straight into a Secret, answering a Result<Secret, SecretError>.

JSON Web Tokens are a codec, jwtCodec(secret) with a Secret, in the same two places as JSON's: encode(claims, hive.crypto.jwtCodec(secret)) signs a value of your own type, and Claims.decode(token, hive.crypto.jwtCodec(secret)) checks the signature before reading the claims back. Only HS256 is accepted, which closes the classic algorithm-confusion attack outright. A token expires only if its claims type declares exp: Int — or nbf, not before — in Unix seconds, which are then checked; encode adds neither. A failure is a hive.crypto.JwtError, its path $ for a token that does not check out and a field's path for claims of the wrong shape. jwtDecode(token) reads the claims as JSON without verifying anything, for inspection only, and jwtHeader(token) reads alg, typ and kid — kid being empty on the tokens Hive signs, which carry a fixed header.

What can fail — decrypt, base64Decode, jwtDecode, jwtHeader — answers with a Result whose hive.crypto.CryptoError has a reason, such as "Malformed", "BadSignature" (a wrong password, or a message somebody changed), "Expired", "NotYetValid", "AlgorithmMismatch" or, from decrypt, a SecretError's "LimitExceeded", and a message.