Port crypt package

This commit is contained in:
2026-01-21 19:05:46 -05:00
parent a72be0393c
commit d24202f63b
7 changed files with 144 additions and 34 deletions
+25 -6
View File
@@ -1,12 +1,10 @@
package main package main
/* // #include <string.h>
typedef struct { // #include <stdlib.h>
char* data; // #include "types.h"
int len;
} PascalString;
*/
import "C" import "C"
import "unsafe"
// getPascalString returns a pascal // getPascalString returns a pascal
// string struct for the input Go string. // string struct for the input Go string.
@@ -28,3 +26,24 @@ func getPascalStringFromBytes(goBytes []byte) C.PascalString {
len: C.int(len(goBytes)), len: C.int(len(goBytes)),
} }
} }
// GetPascalStringFromCString is a helper meant to be used from C.
//
//export GetPascalStringFromCString
func GetPascalStringFromCString(cString *C.char) C.PascalString {
return C.PascalString{
data: cString,
len: C.int(C.strlen(cString)),
}
}
// FreeArray frees the memory allocated by a C-allocated array.
//
//export FreeArray
func FreeArray(items *C.PascalString, count C.int) {
linesSlice := (*[1 << 30]C.PascalString)(unsafe.Pointer(items))[:count:count]
for i := 0; i < int(count); i++ {
C.free(unsafe.Pointer(linesSlice[i].data))
}
C.free(unsafe.Pointer(items))
}
+38 -18
View File
@@ -12,23 +12,40 @@ to export to C.
All relevant exported libmutton functions have C functions of the same name. All relevant exported libmutton functions have C functions of the same name.
Functions with multiple return values all have CGO-generated structs to store the return values. Functions with multiple return values all have CGO-generated structs to store the return values.
These structs are named `<FunctionName>_return`, as per CGO. These structs are named `<FunctionName>_return`, as per CGO.
All functions continue to perform the same basic operations, with a couple caveats: All functions perform similar basic operations, with a couple caveats:
- Anything that would normally return a Go error now returns a null-terminated *C.char containing the error string. - Anything that would normally return a Go error now returns a null-terminated *C.char containing the error string.
These are safe to null-terminate because their values are much more predictable than other strings. These are safe to null-terminate because their values are much more predictable than other strings.
Errors are always the *first* return value, so they can always be referenced with `<result>.r0`. Errors are always the *first* return value, so they can always be referenced with `<result>.r0`.
- Anything that would normally return a Go string or byte slice now returns a C.PascalString struct. - Many things that would normally require/return a Go string or byte slice now require/return a C.PascalString struct.
This is to avoid bugs with null-terminated strings. This is to avoid bugs with null-terminated strings.
Additionally, since comments tend to fall out of date, please rely on the [Go documentation for libmutton](https://pkg.go.dev/github.com/rwinkhart/libmutton). Documentation for the CGO bindings present in this repo only specify return values. Additionally, since comments tend to fall out of date, please rely on the [Go documentation for libmutton](https://pkg.go.dev/github.com/rwinkhart/libmutton). Documentation for the CGO bindings present in this repo only specify return values.
### Example (libmutton's global.DirInit) ### Example (decrypting and printing lines of a libmutton entry)
Build the following example with `gcc <filename> ./cmutton.a`. Build the following example with `gcc <filename> ./cmutton.a`.
```c ```c
#include <stdio.h> #include <stdio.h>
#include <string.h>
#include "cmutton.h" // import cmutton #include "cmutton.h" // import cmutton
static void read_input(const char *prompt, char *buffer, size_t size) {
printf("%s", prompt);
fflush(stdout);
if (fgets(buffer, size, stdin) == NULL) {
fprintf(stderr, "Error reading user input\n");
exit(1);
}
// remove trailing newline
buffer[strlen(buffer)-1] = '\0';
}
int main() { int main() {
char vanityPath[256];
char password[256];
read_input("Enter vanity path: ", vanityPath, sizeof(vanityPath));
read_input("Enter password: ", password, sizeof(password));
// use CGO-generated struct to get multiple return values // use CGO-generated struct to get multiple return values
struct DirInit_return result = DirInit(1); struct DecryptFileToSlice_return result = DecryptFileToSlice(GetRealPath(vanityPath), GetPascalStringFromCString(password));
// familiar error handling pattern // familiar error handling pattern
if (result.r0 != NULL) { if (result.r0 != NULL) {
@@ -36,17 +53,19 @@ int main() {
exit(1); // result.r0 not freed since program exits exit(1); // result.r0 not freed since program exits
} }
// print Pascal string using "%.*s" with printf and supplying both length and data for (int i = 0; i < result.r2; i++) {
printf("Old device ID: %.*s\n", result.r1.len, result.r1.data); // print Pascal string using "%.*s" with printf and supplying both length and data
printf("%.*s\n", result.r1[i].len, result.r1[i].data);
}
// be sure to free the data! // be sure to free the data!
free(result.r1.data); FreeArray(result.r1, result.r2);
} }
``` ```
# Progress # Progress
- [ ] age - [ ] age
- [ ] AllPasswordEntries(forceReage bool) error - [ ] AllPasswordEntries(forceReage bool, rcwPassword []byte) error
- [ ] Entry(vanityPath string, timestamp int64) error - [ ] Entry(vanityPath string, timestamp int64) error
- [ ] TranslateAgeTimestamp(timestamp *int64) uint8 - [ ] TranslateAgeTimestamp(timestamp *int64) uint8
- [ ] clip - [ ] clip
@@ -65,25 +84,26 @@ int main() {
- [ ] EntryIsNotEmpty(entryData []string) bool - [ ] EntryIsNotEmpty(entryData []string) bool
- [ ] EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error - [ ] EntryRefresh(oldRCWPassword, newRCWPassword []byte, removeOldDir bool) error
- [ ] GenTOTP(secret string, time time.Time) (string, error) - [ ] GenTOTP(secret string, time time.Time) (string, error)
- [ ] GetOldEntryData(realPath string, field int) ([]string, error) - [ ] GetOldEntryData(realPath string, field int, rcwPassword []byte) ([]string, error)
- [ ] LibmuttonInit(inputCB func(prompt string) string, rcwPassword []byte, ...) error - [ ] LibmuttonInit(inputCB func(prompt string) string, rcwPassword []byte, ...) error
- [ ] RCWSanityCheckGen(password []byte) error - [ ] RCWSanityCheckGen(password []byte) error
- [ ] WriteEntry(realPath string, decSlice []string, passwordIsNew bool) error - [ ] WriteEntry(realPath string, decSlice []string, passwordIsNew bool, rcwPassword []byte) error
- [ ] crypt - [ ] crypt
- [ ] DecryptFileToSlice(realPath string) ([]string, error) - [ ] Address TODOs
- [ ] EncryptBytes(decBytes []byte) []byte - [X] ~~VAR: RetryPassword bool~~ (RCWD not supported)
- [ ] RCWDArgument() - [X] DecryptFileToSlice(realPath string) ([]string, error)
- [ ] VAR: Daemonize bool - [X] EncryptBytes(decBytes []byte) []byte
- [ ] VAR: RetryPassword bool - [X] ~~RCWDArgument()~~ (RCWD not supported)
- [ ] global - [ ] global
- [ ] Address TODOs
- [X] ~~VAR (CB func): GetPassword~~ (RCWD not supported)
- [X] DirInit(preserveOldCfgDir bool) (string, error) - [X] DirInit(preserveOldCfgDir bool) (string, error)
- [ ] ~~GenDeviceIDList() ([]fs.DirEntry, error)~~ (not for use outside of libmutton) - [X] ~~GenDeviceIDList() ([]fs.DirEntry, error)~~ (not for use outside of libmutton)
- [X] GetCurrentDeviceID() (string, error) - [X] GetCurrentDeviceID() (string, error)
- [X] GetRealAgePath(vanityPath string) string - [X] GetRealAgePath(vanityPath string) string
- [X] GetRealPath(vanityPath string) string - [X] GetRealPath(vanityPath string) string
- [ ] ~~GetSysProcAttr() *syscall.SysProcAttr~~ (not for use outside of libmutton) - [X] ~~GetSysProcAttr() *syscall.SysProcAttr~~ (not for use outside of libmutton)
- [X] GetVanityPath(realPath string) string - [X] GetVanityPath(realPath string) string
- [ ] VAR (CB func): GetPassword
- [ ] syncclient - [ ] syncclient
- [ ] AddFolderRemote(vanityPath string) error - [ ] AddFolderRemote(vanityPath string) error
- [ ] GenDeviceID(oldDeviceID, prefix string) (string, string, bool, error) - [ ] GenDeviceID(oldDeviceID, prefix string) (string, string, bool, error)
+53
View File
@@ -0,0 +1,53 @@
package main
// #include <stdlib.h>
// #include "types.h"
import "C"
import (
"unsafe"
"github.com/rwinkhart/libmutton/crypt"
"github.com/rwinkhart/rcw/wrappers"
)
// TODO investigate username+password only entries still having blank lines at the end when decrypted
// DecryptFileToSlice returns:
// r0: err
//
// r1: decLines (pointer to C-allocated array)
//
// r2: length decLines array
//
//export DecryptFileToSlice
func DecryptFileToSlice(realPath, rcwPassword C.PascalString) (*C.char, *C.PascalString, C.int) {
lines, err := crypt.DecryptFileToSlice(C.GoStringN(realPath.data, realPath.len), []byte(C.GoStringN(rcwPassword.data, rcwPassword.len)))
if err != nil {
return C.CString(err.Error()), nil, 0
}
if len(lines) == 0 {
return nil, nil, 0
}
// allocate C array for PascalStrings
cLinesPtr := (*C.PascalString)(C.malloc(C.size_t(len(lines)) * C.size_t(C.sizeof_PascalString)))
// populate the array with the decrypted lines
cLinesSlice := (*[1 << 30]C.PascalString)(unsafe.Pointer(cLinesPtr))[:len(lines):len(lines)]
for i, line := range lines {
cLinesSlice[i] = getPascalString(line)
}
return nil, cLinesPtr, C.int(len(lines))
}
// EncryptBytes returns:
// encBytes
//
//export EncryptBytes
func EncryptBytes(decBytes, rcwPassword C.PascalString) C.PascalString {
// use wrappers.Encrypt directly since C bindings do no support the RCWD daemon
encBytes := wrappers.Encrypt([]byte(C.GoStringN(decBytes.data, decBytes.len)), []byte(C.GoStringN(rcwPassword.data, rcwPassword.len)))
return getPascalStringFromBytes(encBytes)
}
+2 -7
View File
@@ -1,12 +1,7 @@
package main package main
/* // #include <stdlib.h>
#include <stdlib.h> // #include "types.h"
typedef struct {
char* data;
int len;
} PascalString;
*/
import "C" import "C"
import ( import (
"github.com/rwinkhart/libmutton/global" "github.com/rwinkhart/libmutton/global"
+9 -1
View File
@@ -2,9 +2,17 @@ module cmutton
go 1.25.6 go 1.25.6
require github.com/rwinkhart/libmutton v0.4.3-0.20260111043639-f82b35bc4398 require (
github.com/rwinkhart/libmutton v0.4.3-0.20260111043639-f82b35bc4398
github.com/rwinkhart/rcw v0.2.4
)
require ( require (
github.com/Microsoft/go-winio v0.6.2 // indirect
github.com/rwinkhart/go-boilerplate v0.1.1 // indirect github.com/rwinkhart/go-boilerplate v0.1.1 // indirect
github.com/rwinkhart/peercred-mini v0.1.2 // indirect
golang.org/x/crypto v0.46.0 // indirect
golang.org/x/sys v0.40.0 // indirect golang.org/x/sys v0.40.0 // indirect
) )
replace github.com/rwinkhart/libmutton => ../libmutton
+8 -2
View File
@@ -1,6 +1,12 @@
github.com/Microsoft/go-winio v0.6.2 h1:F2VQgta7ecxGYO8k3ZZz3RS8fVIXVxONVUPlNERoyfY=
github.com/Microsoft/go-winio v0.6.2/go.mod h1:yd8OoFMLzJbo9gZq8j5qaps8bJ9aShtEA8Ipt1oGCvU=
github.com/rwinkhart/go-boilerplate v0.1.1 h1:C+yyscGWeqvNpR91iw0i91zqX0LcxnCr1VOyT7iM4JY= github.com/rwinkhart/go-boilerplate v0.1.1 h1:C+yyscGWeqvNpR91iw0i91zqX0LcxnCr1VOyT7iM4JY=
github.com/rwinkhart/go-boilerplate v0.1.1/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek= github.com/rwinkhart/go-boilerplate v0.1.1/go.mod h1:/NVRKGslU20E5xU5YOgXzWxA6aa94BMtv5MtHRTb5Ek=
github.com/rwinkhart/libmutton v0.4.3-0.20260111043639-f82b35bc4398 h1:QufX3nG3H1N986Xh7NZWsVELxmGms1yhNP7/fejBeHY= github.com/rwinkhart/peercred-mini v0.1.2 h1:4cGWDbv0whvLeVvbUdx84V/9p+2fS+DEXgrA1KxlRFo=
github.com/rwinkhart/libmutton v0.4.3-0.20260111043639-f82b35bc4398/go.mod h1:qInUv19lzX1adFNI6Ih/IEVy+pktSTh/yA7cRTdVPH8= github.com/rwinkhart/peercred-mini v0.1.2/go.mod h1:LLHG7YshHEpbpJJP+Il9nx2dnGj5O3VGE32rWmflj0c=
github.com/rwinkhart/rcw v0.2.4 h1:FrRSsl+ui1WLmDwqjLSROtFPKqSeGLEfF2SPENt4qP0=
github.com/rwinkhart/rcw v0.2.4/go.mod h1:nhW4RAHvEjW2QcOlqEdWqRQOFJLjoOGSPkSRZlMjU3s=
golang.org/x/crypto v0.46.0 h1:cKRW/pmt1pKAfetfu+RCEvjvZkA9RimPbh7bhFjGVBU=
golang.org/x/crypto v0.46.0/go.mod h1:Evb/oLKmMraqjZ2iQTwDwvCtJkczlDuTmdJXoZVzqU0=
golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ= golang.org/x/sys v0.40.0 h1:DBZZqJ2Rkml6QMQsZywtnjnnGvHza6BTfYFWY9kjEWQ=
golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks= golang.org/x/sys v0.40.0/go.mod h1:OgkHotnGiDImocRcuBABYBEXf8A9a87e/uXjp9XT3ks=
+9
View File
@@ -0,0 +1,9 @@
#ifndef TYPES_H
#define TYPES_H
typedef struct {
char* data;
int len;
} PascalString;
#endif