matrix-pretix/vendor/maunium.net/go/mautrix/crypto/olm/utility.go

161 lines
4.4 KiB
Go
Raw Permalink Normal View History

2022-07-25 02:32:08 +02:00
package olm
// #cgo LDFLAGS: -lolm -lstdc++
// #include <olm/olm.h>
import "C"
import (
"encoding/json"
"fmt"
"strings"
"unsafe"
"github.com/tidwall/gjson"
"github.com/tidwall/sjson"
"maunium.net/go/mautrix/crypto/canonicaljson"
"maunium.net/go/mautrix/id"
)
// Utility stores the necessary state to perform hash and signature
// verification operations.
type Utility struct {
int *C.OlmUtility
mem []byte
}
// utilitySize returns the size of a utility object in bytes.
func utilitySize() uint {
return uint(C.olm_utility_size())
}
// sha256Len returns the length of the buffer needed to hold the SHA-256 hash.
func (u *Utility) sha256Len() uint {
return uint(C.olm_sha256_length((*C.OlmUtility)(u.int)))
}
// lastError returns an error describing the most recent error to happen to a
// utility.
func (u *Utility) lastError() error {
return convertError(C.GoString(C.olm_utility_last_error((*C.OlmUtility)(u.int))))
}
// Clear clears the memory used to back this utility.
func (u *Utility) Clear() error {
r := C.olm_clear_utility((*C.OlmUtility)(u.int))
if r == errorVal() {
return u.lastError()
}
return nil
}
// NewUtility creates a new utility.
func NewUtility() *Utility {
memory := make([]byte, utilitySize())
return &Utility{
int: C.olm_utility(unsafe.Pointer(&memory[0])),
mem: memory,
}
}
// Sha256 calculates the SHA-256 hash of the input and encodes it as base64.
func (u *Utility) Sha256(input string) string {
if len(input) == 0 {
panic(EmptyInput)
}
output := make([]byte, u.sha256Len())
r := C.olm_sha256(
(*C.OlmUtility)(u.int),
unsafe.Pointer(&([]byte(input)[0])),
C.size_t(len(input)),
unsafe.Pointer(&(output[0])),
C.size_t(len(output)))
if r == errorVal() {
panic(u.lastError())
}
return string(output)
}
// VerifySignature verifies an ed25519 signature. Returns true if the verification
// suceeds or false otherwise. Returns error on failure. If the key was too
// small then the error will be "INVALID_BASE64".
func (u *Utility) VerifySignature(message string, key id.Ed25519, signature string) (ok bool, err error) {
if len(message) == 0 || len(key) == 0 || len(signature) == 0 {
return false, EmptyInput
}
r := C.olm_ed25519_verify(
(*C.OlmUtility)(u.int),
unsafe.Pointer(&([]byte(key)[0])),
C.size_t(len(key)),
unsafe.Pointer(&([]byte(message)[0])),
C.size_t(len(message)),
unsafe.Pointer(&([]byte(signature)[0])),
C.size_t(len(signature)))
if r == errorVal() {
err = u.lastError()
if err == BadMessageMAC {
err = nil
}
} else {
ok = true
}
return ok, err
}
var gjsonEscaper = strings.NewReplacer(
`\`, `\\`,
".", `\.`,
"|", `\|`,
"#", `\#`,
"@", `\@`,
"*", `\*`,
"?", `\?`)
func gjsonPath(path ...string) string {
var result strings.Builder
for i, part := range path {
_, _ = gjsonEscaper.WriteString(&result, part)
if i < len(path)-1 {
result.WriteRune('.')
}
}
return result.String()
}
// VerifySignatureJSON verifies the signature in the JSON object _obj following
// the Matrix specification:
// https://matrix.org/speculator/spec/drafts%2Fe2e/appendices.html#signing-json
// If the _obj is a struct, the `json` tags will be honored.
func (u *Utility) VerifySignatureJSON(obj interface{}, userID id.UserID, keyName string, key id.Ed25519) (bool, error) {
objJSON, err := json.Marshal(obj)
if err != nil {
return false, err
}
sig := gjson.GetBytes(objJSON, gjsonPath("signatures", string(userID), fmt.Sprintf("ed25519:%s", keyName)))
if !sig.Exists() || sig.Type != gjson.String {
return false, SignatureNotFound
}
objJSON, err = sjson.DeleteBytes(objJSON, "unsigned")
if err != nil {
return false, err
}
objJSON, err = sjson.DeleteBytes(objJSON, "signatures")
if err != nil {
return false, err
}
objJSONString := string(canonicaljson.CanonicalJSONAssumeValid(objJSON))
return u.VerifySignature(objJSONString, key, sig.Str)
}
// VerifySignatureJSON verifies the signature in the JSON object _obj following
// the Matrix specification:
// https://matrix.org/speculator/spec/drafts%2Fe2e/appendices.html#signing-json
// This function is a wrapper over Utility.VerifySignatureJSON that creates and
// destroys the Utility object transparently.
// If the _obj is a struct, the `json` tags will be honored.
func VerifySignatureJSON(obj interface{}, userID id.UserID, keyName string, key id.Ed25519) (bool, error) {
u := NewUtility()
defer u.Clear()
return u.VerifySignatureJSON(obj, userID, keyName, key)
}