digest

package module
v0.0.0-...-3bb001e Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Jul 17, 2026 License: BSD-3-Clause Imports: 12 Imported by: 0

README

go-ruby-digest/digest

digest — go-ruby-digest

Docs License Go Coverage

A pure-Go (no cgo) reimplementation of Ruby's Digest standard library — the deterministic, interpreter-independent message-digest core of MRI 4.0.5's digest, digest/md5, digest/sha1, digest/sha2, digest/rmd160 and digest/bubblebabble. It computes Digest::MD5 / SHA1 / SHA256 / SHA384 / SHA512 / RMD160 and the Digest.bubblebabble encoding byte-for-byte identical to MRI, with no Ruby runtime and no cgo.

It is the digest backend for go-embedded-ruby, but is a standalone, reusable module — a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-erb (the ERB compiler) and go-ruby-yaml (the Psych emitter/loader).

What it is — and isn't. A message digest is pure computation over bytes, so it lives here as pure Go over crypto/* and golang.org/x/crypto/ripemd160. Binding the algorithms into Ruby classes (Digest::SHA256, the #update / #hexdigest protocol, == against a hex string) is the host's job; this library hands back a small, idiomatic Go Digest interface plus one-shot helpers the host maps onto MRI's surface.

Features

Faithful port of MRI's Digest, validated against the ruby binary on every supported platform:

  • Six algorithmsMD5, SHA1, SHA256, SHA384, SHA512, RMD160 (RIPEMD-160), each with its correct block and digest lengths.
  • Incremental protocolUpdate / Reset / Finish / HexFinish / Base64Finish mirror Digest::Instance#update / #<< / #reset / #digest / #hexdigest / #base64digest.
  • Class one-shotsSum / HexSum / Base64Sum are Digest::ALGO.digest / .hexdigest / .base64digest; SumFile / HexSumFile / Base64SumFile are Digest::ALGO.file(path).
  • Bubble BabbleBubbleBabble is Digest.bubblebabble (the Antti Huima pronounceable encoding); SumBubbleBabble is Digest::ALGO.bubblebabble (bubble-babble of the algorithm's digest), matching MRI's digest/bubblebabble.
  • Name factoryNew("SHA256") is the Digest(name) factory, case- and dash-insensitive ("sha-256"), with RIPEMD160 aliased to RMD160.

CGO-free, 100% test coverage, gofmt + go vet clean, and green across the six 64-bit Go targets (amd64, arm64, riscv64, loong64, ppc64le, s390x).

Install

go get github.com/go-ruby-digest/digest

Usage

package main

import (
	"fmt"

	"github.com/go-ruby-digest/digest"
)

func main() {
	// One-shot: Digest::SHA256.hexdigest("abc")
	hx, _ := digest.HexSum("SHA256", []byte("abc"))
	fmt.Println(hx) // ba7816bf...20015ad

	// Incremental: Digest::SHA1.new; d.update("a"); d << "bc"; d.hexdigest
	d := digest.SHA1()
	d.Update([]byte("a"))
	d.Update([]byte("bc"))
	fmt.Println(d.HexFinish()) // a9993e36...7850c26c9cd0d89d

	// Digest.bubblebabble("Pineapple")
	fmt.Println(digest.BubbleBabble([]byte("Pineapple"))) // xigak-nyryk-humil-bosek-sonax
}

API

// Digest is the incremental Digest::Instance protocol.
type Digest interface {
	Update(data []byte)    // #update / #<<
	Reset()                // #reset
	Finish() []byte        // #digest
	HexFinish() string     // #hexdigest
	Base64Finish() string  // #base64digest
	BlockLength() int      // #block_length
	DigestLength() int     // #digest_length / #length / #size
}

// New is the Digest(name) factory; the direct constructors are the named classes.
func New(name string) (Digest, error)
func MD5() Digest
func SHA1() Digest
func SHA256() Digest
func SHA384() Digest
func SHA512() Digest
func RMD160() Digest

// Class one-shots: Digest::ALGO.digest / .hexdigest / .base64digest.
func Sum(name string, data []byte) ([]byte, error)
func HexSum(name string, data []byte) (string, error)
func Base64Sum(name string, data []byte) (string, error)

// File one-shots: Digest::ALGO.file(path).{digest,hexdigest,base64digest}.
func SumFile(name, path string) ([]byte, error)
func HexSumFile(name, path string) (string, error)
func Base64SumFile(name, path string) (string, error)

// Bubble Babble: Digest.bubblebabble and Digest::ALGO.bubblebabble.
func BubbleBabble(data []byte) string
func SumBubbleBabble(name string, data []byte) (string, error)

Algorithm table

MRI class name block digest backend
Digest::MD5 "MD5" 64 16 crypto/md5
Digest::SHA1 "SHA1" 64 20 crypto/sha1
Digest::SHA256 "SHA256" 64 32 crypto/sha256
Digest::SHA384 "SHA384" 128 48 crypto/sha512
Digest::SHA512 "SHA512" 128 64 crypto/sha512
Digest::RMD160 "RMD160" 64 20 golang.org/x/crypto/ripemd160

Tests & coverage

The suite pairs deterministic, ruby-free tests (which alone hold coverage at 100%, so the qemu cross-arch and Windows lanes pass the gate) with a differential MRI oracle: a corpus is hashed here and by the system ruby (Digest::ALGO.hexdigest / .base64digest / .digest / .bubblebabble / .file, the incremental update/<< path, and SHA2.new(bitlen)), and the outputs must match byte-for-byte. The oracle scripts $stdout.binmode / $stdin.binmode so Windows text-mode never pollutes the bytes, gate on MRI >= 4.0, and skip themselves where ruby is absent; the .file test uses t.TempDir() + filepath.ToSlash so no OS path is embedded in Ruby source.

COVERPKG=$(go list ./... | paste -sd, -)
go test -race -coverpkg="$COVERPKG" -coverprofile=cover.out ./...
go tool cover -func=cover.out | tail -1   # 100.0%

License

BSD-3-Clause — see LICENSE. Copyright the go-ruby-digest/digest authors.

WebAssembly

Being pure Go (CGO=0), this library also compiles to WebAssembly — both GOOS=js GOARCH=wasm (browser / Node.js) and GOOS=wasip1 GOARCH=wasm (WASI). CI builds both targets on every push, alongside the six 64-bit native/qemu arches.

GOOS=js     GOARCH=wasm go build ./...   # browser / Node
GOOS=wasip1 GOARCH=wasm go build ./...   # WASI (wasmtime, wasmer, wasmedge, …)

Documentation

Overview

Package digest is a pure-Go (no cgo) reimplementation of Ruby's Digest standard library — the deterministic, interpreter-independent message-digest core of MRI 4.0.5's digest, digest/md5, digest/sha1, digest/sha2, digest/rmd160 and digest/bubblebabble.

Each algorithm (MD5, SHA1, SHA256, SHA384, SHA512, RMD160) is exposed as an incremental Digest — an accumulating hasher fed through Update and read with Finish / HexFinish / Base64Finish — mirroring Ruby's Digest::Instance protocol (#update / #<< / #digest / #hexdigest / #base64digest / #reset). The class-level one-shots Digest::SHA256.digest(s) / .hexdigest(s) / .base64digest(s) map to Sum / HexSum / Base64Sum, and the digest/bubblebabble extension maps to BubbleBabble.

The computation is backed by Go's crypto/* (MD5/SHA1/SHA2) and golang.org/x/crypto/ripemd160 (RMD160) — both pure Go — so every digest is byte-identical to MRI's, with no Ruby runtime and no cgo. The one-shot and finish paths sum and hex/Base64-encode through stack buffers so a full hexdigest allocates only its result string (see fastSum / hexEncode). It is the digest backend for go-embedded-ruby, but is a standalone, reusable module, a sibling of go-ruby-regexp (the Onigmo engine), go-ruby-erb (the ERB compiler) and go-ruby-yaml (the Psych emitter/loader).

Index

Constants

This section is empty.

Variables

This section is empty.

Functions

func Base64Sum

func Base64Sum(name string, data []byte) (string, error)

Base64Sum is the class one-shot Digest::ALGO.base64digest(data), with the same stack-buffered single-allocation strategy as HexSum.

func Base64SumFile

func Base64SumFile(name, path string) (string, error)

Base64SumFile is Digest::ALGO.file(path).base64digest.

func BubbleBabble

func BubbleBabble(data []byte) string

BubbleBabble encodes data with the Bubble Babble algorithm, the deterministic pronounceable digest format used by Digest.bubblebabble. The output begins and ends with 'x' and reads as a string of CV/CVC tuples separated by hyphens — the same bytes MRI emits.

func HexSum

func HexSum(name string, data []byte) (string, error)

HexSum is the class one-shot Digest::ALGO.hexdigest(data). This is the exact Go analogue of Ruby's Digest::SHA256.hexdigest(s) — the hot path the parity benchmark exercises. For the crypto algorithms it sums into a stack array and hex-encodes (inline, so the array never escapes) into a second stack buffer, leaving the returned string as the sole allocation.

func HexSumFile

func HexSumFile(name, path string) (string, error)

HexSumFile is Digest::ALGO.file(path).hexdigest.

func Sum

func Sum(name string, data []byte) ([]byte, error)

Sum is the binary class one-shot Digest::ALGO.digest(data): the raw digest of data under the named algorithm. For the built-in crypto algorithms it uses the allocation-free stack hasher (fastSum), copying out only the exact-length result slice it must return; RMD160 falls back to the streaming hasher.

func SumBubbleBabble

func SumBubbleBabble(name string, data []byte) (string, error)

SumBubbleBabble is the class/instance one-shot Digest::ALGO.bubblebabble(data) and #bubblebabble: the Bubble Babble encoding of the algorithm's binary digest of data (not of data itself).

func SumFile

func SumFile(name, path string) ([]byte, error)

SumFile is the class one-shot Digest::ALGO.file(path): the binary digest of a file's contents. The returned bytes match Digest::ALGO.file(path).digest.

Types

type Digest

type Digest interface {
	// Update appends data to the message being digested.
	Update(data []byte)
	// Reset discards the accumulated state, as if freshly constructed.
	Reset()
	// Finish returns the binary digest of the bytes fed so far.
	Finish() []byte
	// HexFinish returns the lowercase hexadecimal digest.
	HexFinish() string
	// Base64Finish returns the standard-Base64 (padded) digest.
	Base64Finish() string
	// BlockLength is the algorithm's internal block size in bytes
	// (Digest::Instance#block_length).
	BlockLength() int
	// DigestLength is the size of the produced digest in bytes
	// (Digest::Instance#digest_length / #length / #size).
	DigestLength() int
}

Digest is an incremental message-digest hasher, the Go analogue of Ruby's Digest::Instance protocol. Update feeds bytes (Digest::Instance#update / #<<), Reset returns the hasher to its initial state (#reset), and the three Finish methods read the running digest in binary, hex and Base64 form (#digest, #hexdigest, #base64digest). Reading the digest does not reset it, matching MRI's no-argument finalizers.

func MD5

func MD5() Digest

MD5 returns a fresh Digest::MD5 hasher.

func New

func New(name string) (Digest, error)

New returns a fresh incremental Digest for an MRI algorithm name ("MD5", "SHA1", "SHA256", "SHA384", "SHA512", "RMD160"). It is the Digest(name) factory; an unknown name returns an error.

func RMD160

func RMD160() Digest

RMD160 returns a fresh Digest::RMD160 (RIPEMD-160) hasher.

func SHA1

func SHA1() Digest

SHA1 returns a fresh Digest::SHA1 hasher.

func SHA256

func SHA256() Digest

SHA256 returns a fresh Digest::SHA256 hasher.

func SHA384

func SHA384() Digest

SHA384 returns a fresh Digest::SHA384 hasher.

func SHA512

func SHA512() Digest

SHA512 returns a fresh Digest::SHA512 hasher.

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL