diff --git a/pkg/api/stream_key.go b/pkg/api/stream_key.go index f48cc083d..4b0e45aed 100644 --- a/pkg/api/stream_key.go +++ b/pkg/api/stream_key.go @@ -83,11 +83,7 @@ func (a *StreamplaceAPI) MakeMediaSigner(ctx context.Context, keyStr string) (me } var mediaSigner media.MediaSigner - if a.CLI.ExternalSigning { - mediaSigner, err = media.MakeMediaSignerExt(ctx, a.CLI, did, addrBytes) - } else { - mediaSigner, err = media.MakeMediaSigner(ctx, a.CLI, did, signer) - } + mediaSigner, err = media.MakeMediaSigner(ctx, a.CLI, did, signer) if err != nil { return nil, fmt.Errorf("invalid authorization key (not valid secp256k1): %w", err) } diff --git a/pkg/c2patypes/callback_signer.go b/pkg/c2patypes/callback_signer.go new file mode 100644 index 000000000..93a6fc715 --- /dev/null +++ b/pkg/c2patypes/callback_signer.go @@ -0,0 +1,17 @@ +package c2patypes + +import ( + "crypto" + "crypto/rand" + "crypto/sha256" +) + +type CallbackSigner struct { + signer crypto.Signer +} + +func (c *CallbackSigner) Sign(data []byte) ([]byte, error) { + digest := sha256.New().Sum(data) + + return c.signer.Sign(rand.Reader, digest, nil) +} diff --git a/pkg/cmd/streamplace.go b/pkg/cmd/streamplace.go index dde41e71c..d3ad3589c 100644 --- a/pkg/cmd/streamplace.go +++ b/pkg/cmd/streamplace.go @@ -3,7 +3,6 @@ package cmd import ( "context" "crypto" - "encoding/json" "errors" "flag" "fmt" @@ -25,7 +24,6 @@ import ( "stream.place/streamplace/pkg/aqhttp" "stream.place/streamplace/pkg/atproto" "stream.place/streamplace/pkg/bus" - c2patypes "stream.place/streamplace/pkg/c2patypes" "stream.place/streamplace/pkg/crypto/signers" "stream.place/streamplace/pkg/crypto/signers/eip712" "stream.place/streamplace/pkg/director" @@ -45,8 +43,6 @@ import ( "stream.place/streamplace/pkg/api" "stream.place/streamplace/pkg/config" "stream.place/streamplace/pkg/model" - - "stream.place/streamplace/pkg/iroh/generated/iroh_streamplace" ) // Additional jobs that can be injected by platforms @@ -54,23 +50,24 @@ type jobFunc func(ctx context.Context, cli *config.CLI) error // parse the CLI and fire up an streamplace node! func start(build *config.BuildFlags, platformJobs []jobFunc) error { - bs, err := os.ReadFile("/home/iameli/.streamplace/segments/did-plc-dkh4rwafdcda4ko7lewe43ml/2025/09/18/20/54/2025-09-18T20-54-31-693Z.mp4") - if err != nil { - return err - } - manifest, rustErr := iroh_streamplace.GetManifest(bs) - if rustErr.AsError() != nil { - return rustErr.AsError() - } - var mani c2patypes.Manifest - err = json.Unmarshal([]byte(manifest), &mani) - if err != nil { - return err - } - fmt.Println(mani) - os.Exit(0) + // builder := c2patypes.Builder{} + // bs, err := os.ReadFile("/home/iameli/.streamplace/segments/did-plc-dkh4rwafdcda4ko7lewe43ml/2025/09/18/20/54/2025-09-18T20-54-31-693Z.mp4") + // if err != nil { + // return err + // } + // manifest, rustErr := iroh_streamplace.GetManifest(bs) + // if rustErr.AsError() != nil { + // return rustErr.AsError() + // } + // var mani c2patypes.Manifest + // err = json.Unmarshal([]byte(manifest), &mani) + // if err != nil { + // return err + // } + // fmt.Println(mani) + // os.Exit(0) selfTest := len(os.Args) > 1 && os.Args[1] == "self-test" - err = media.RunSelfTest(context.Background()) + err := media.RunSelfTest(context.Background()) if err != nil { if selfTest { fmt.Println(err.Error()) diff --git a/pkg/crypto/signers/signers.go b/pkg/crypto/signers/signers.go index 44efc274b..7722324eb 100644 --- a/pkg/crypto/signers/signers.go +++ b/pkg/crypto/signers/signers.go @@ -42,14 +42,18 @@ func GenerateES256KCert(signer gocrypto.Signer) ([]byte, error) { // publicKeyBytes := elliptic.Marshal(elliptic.P256(), pub.X, pub.Y) pub := signer.Public().(*ecdsa.PublicKey) publicKeyBytes := elliptic.Marshal(crypto.S256(), pub.X, pub.Y) //nolint:all + atpub, err := atcrypto.ParsePublicUncompressedBytesK256(publicKeyBytes) + if err != nil { + return nil, fmt.Errorf("failed to parse public key: %w", err) + } idhash := sha1.Sum(publicKeyBytes) subjectKeyID := idhash[:] - hex := HexAddr(pub) template := x509.Certificate{ SerialNumber: serialNumber, Subject: pkix.Name{ - CommonName: hex, + CommonName: atpub.DIDKey(), + Organization: []string{"Streamplace"}, }, NotBefore: notBefore, NotAfter: notAfter, diff --git a/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.go b/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.go index 2d636ae9c..d3296b4fc 100644 --- a/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.go +++ b/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.go @@ -335,6 +335,7 @@ func readFloat64(reader io.Reader) float64 { func init() { FfiConverterDataHandlerINSTANCE.register() + FfiConverterGoSignerINSTANCE.register() uniffiCheckChecksums() } @@ -349,22 +350,31 @@ func uniffiCheckChecksums() { // If this happens try cleaning and rebuilding your project panic("iroh_streamplace: UniFFI contract version mismatch") } + { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iroh_streamplace_checksum_func_get_cert() + }) + if checksum != 59172 { + // If this happens try cleaning and rebuilding your project + panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_func_get_cert: UniFFI API checksum mismatch") + } + } { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { return C.uniffi_iroh_streamplace_checksum_func_get_manifest() }) - if checksum != 65147 { + if checksum != 23005 { // If this happens try cleaning and rebuilding your project panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_func_get_manifest: UniFFI API checksum mismatch") } } { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { - return C.uniffi_iroh_streamplace_checksum_func_print_cert() + return C.uniffi_iroh_streamplace_checksum_func_sign() }) - if checksum != 39427 { + if checksum != 23786 { // If this happens try cleaning and rebuilding your project - panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_func_print_cert: UniFFI API checksum mismatch") + panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_func_sign: UniFFI API checksum mismatch") } } { @@ -385,6 +395,15 @@ func uniffiCheckChecksums() { panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_method_endpoint_node_addr: UniFFI API checksum mismatch") } } + { + checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { + return C.uniffi_iroh_streamplace_checksum_method_gosigner_sign() + }) + if checksum != 50597 { + // If this happens try cleaning and rebuilding your project + panic("iroh_streamplace: uniffi_iroh_streamplace_checksum_method_gosigner_sign: UniFFI API checksum mismatch") + } + } { checksum := rustCall(func(_uniffiStatus *C.RustCallStatus) C.uint16_t { return C.uniffi_iroh_streamplace_checksum_method_nodeaddr_direct_addresses() @@ -1041,6 +1060,135 @@ func (_ FfiDestroyerEndpoint) Destroy(value *Endpoint) { value.Destroy() } +type GoSigner interface { + Sign(data []byte) ([]byte, *SpError) +} +type GoSignerImpl struct { + ffiObject FfiObject +} + +func (_self *GoSignerImpl) Sign(data []byte) ([]byte, *SpError) { + _pointer := _self.ffiObject.incrementPointer("GoSigner") + defer _self.ffiObject.decrementPointer() + _uniffiRV, _uniffiErr := rustCallWithError[SpError](FfiConverterSpError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return GoRustBuffer{ + inner: C.uniffi_iroh_streamplace_fn_method_gosigner_sign( + _pointer, FfiConverterBytesINSTANCE.Lower(data), _uniffiStatus), + } + }) + if _uniffiErr != nil { + var _uniffiDefaultValue []byte + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterBytesINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} +func (object *GoSignerImpl) Destroy() { + runtime.SetFinalizer(object, nil) + object.ffiObject.destroy() +} + +type FfiConverterGoSigner struct { + handleMap *concurrentHandleMap[GoSigner] +} + +var FfiConverterGoSignerINSTANCE = FfiConverterGoSigner{ + handleMap: newConcurrentHandleMap[GoSigner](), +} + +func (c FfiConverterGoSigner) Lift(pointer unsafe.Pointer) GoSigner { + result := &GoSignerImpl{ + newFfiObject( + pointer, + func(pointer unsafe.Pointer, status *C.RustCallStatus) unsafe.Pointer { + return C.uniffi_iroh_streamplace_fn_clone_gosigner(pointer, status) + }, + func(pointer unsafe.Pointer, status *C.RustCallStatus) { + C.uniffi_iroh_streamplace_fn_free_gosigner(pointer, status) + }, + ), + } + runtime.SetFinalizer(result, (*GoSignerImpl).Destroy) + return result +} + +func (c FfiConverterGoSigner) Read(reader io.Reader) GoSigner { + return c.Lift(unsafe.Pointer(uintptr(readUint64(reader)))) +} + +func (c FfiConverterGoSigner) Lower(value GoSigner) unsafe.Pointer { + // TODO: this is bad - all synchronization from ObjectRuntime.go is discarded here, + // because the pointer will be decremented immediately after this function returns, + // and someone will be left holding onto a non-locked pointer. + pointer := unsafe.Pointer(uintptr(c.handleMap.insert(value))) + return pointer + +} + +func (c FfiConverterGoSigner) Write(writer io.Writer, value GoSigner) { + writeUint64(writer, uint64(uintptr(c.Lower(value)))) +} + +type FfiDestroyerGoSigner struct{} + +func (_ FfiDestroyerGoSigner) Destroy(value GoSigner) { + if val, ok := value.(*GoSignerImpl); ok { + val.Destroy() + } else { + panic("Expected *GoSignerImpl") + } +} + +//export iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerMethod0 +func iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerMethod0(uniffiHandle C.uint64_t, data C.RustBuffer, uniffiOutReturn *C.RustBuffer, callStatus *C.RustCallStatus) { + handle := uint64(uniffiHandle) + uniffiObj, ok := FfiConverterGoSignerINSTANCE.handleMap.tryGet(handle) + if !ok { + panic(fmt.Errorf("no callback in handle map: %d", handle)) + } + + res, err := + uniffiObj.Sign( + FfiConverterBytesINSTANCE.Lift(GoRustBuffer{ + inner: data, + }), + ) + + if err != nil { + // The only way to bypass an unexpected error is to bypass pointer to an empty + // instance of the error + if err.err == nil { + *callStatus = C.RustCallStatus{ + code: C.int8_t(uniffiCallbackUnexpectedResultError), + } + return + } + + *callStatus = C.RustCallStatus{ + code: C.int8_t(uniffiCallbackResultError), + errorBuf: FfiConverterSpErrorINSTANCE.Lower(err), + } + return + } + + *uniffiOutReturn = FfiConverterBytesINSTANCE.Lower(res) +} + +var UniffiVTableCallbackInterfaceGoSignerINSTANCE = C.UniffiVTableCallbackInterfaceGoSigner{ + sign: (C.UniffiCallbackInterfaceGoSignerMethod0)(C.iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerMethod0), + + uniffiFree: (C.UniffiCallbackInterfaceFree)(C.iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerFree), +} + +//export iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerFree +func iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerFree(handle C.uint64_t) { + FfiConverterGoSignerINSTANCE.handleMap.remove(uint64(handle)) +} + +func (c FfiConverterGoSigner) register() { + C.uniffi_iroh_streamplace_fn_init_callback_vtable_gosigner(&UniffiVTableCallbackInterfaceGoSignerINSTANCE) +} + // A peer and it's addressing information. type NodeAddrInterface interface { // Get the direct addresses of this peer. @@ -1610,124 +1758,6 @@ func (_ FfiDestroyerSender) Destroy(value *Sender) { value.Destroy() } -type CertError struct { - err error -} - -// Convience method to turn *CertError into error -// Avoiding treating nil pointer as non nil error interface -func (err *CertError) AsError() error { - if err == nil { - return nil - } else { - return err - } -} - -func (err CertError) Error() string { - return fmt.Sprintf("CertError: %s", err.err.Error()) -} - -func (err CertError) Unwrap() error { - return err.err -} - -// Err* are used for checking error type with `errors.Is` -var ErrCertErrorNoCertificateChainFound = fmt.Errorf("CertErrorNoCertificateChainFound") -var ErrCertErrorC2paError = fmt.Errorf("CertErrorC2paError") - -// Variant structs -type CertErrorNoCertificateChainFound struct { - message string -} - -func NewCertErrorNoCertificateChainFound() *CertError { - return &CertError{err: &CertErrorNoCertificateChainFound{}} -} - -func (e CertErrorNoCertificateChainFound) destroy() { -} - -func (err CertErrorNoCertificateChainFound) Error() string { - return fmt.Sprintf("NoCertificateChainFound: %s", err.message) -} - -func (self CertErrorNoCertificateChainFound) Is(target error) bool { - return target == ErrCertErrorNoCertificateChainFound -} - -type CertErrorC2paError struct { - message string -} - -func NewCertErrorC2paError() *CertError { - return &CertError{err: &CertErrorC2paError{}} -} - -func (e CertErrorC2paError) destroy() { -} - -func (err CertErrorC2paError) Error() string { - return fmt.Sprintf("C2paError: %s", err.message) -} - -func (self CertErrorC2paError) Is(target error) bool { - return target == ErrCertErrorC2paError -} - -type FfiConverterCertError struct{} - -var FfiConverterCertErrorINSTANCE = FfiConverterCertError{} - -func (c FfiConverterCertError) Lift(eb RustBufferI) *CertError { - return LiftFromRustBuffer[*CertError](c, eb) -} - -func (c FfiConverterCertError) Lower(value *CertError) C.RustBuffer { - return LowerIntoRustBuffer[*CertError](c, value) -} - -func (c FfiConverterCertError) Read(reader io.Reader) *CertError { - errorID := readUint32(reader) - - message := FfiConverterStringINSTANCE.Read(reader) - switch errorID { - case 1: - return &CertError{&CertErrorNoCertificateChainFound{message}} - case 2: - return &CertError{&CertErrorC2paError{message}} - default: - panic(fmt.Sprintf("Unknown error code %d in FfiConverterCertError.Read()", errorID)) - } - -} - -func (c FfiConverterCertError) Write(writer io.Writer, value *CertError) { - switch variantValue := value.err.(type) { - case *CertErrorNoCertificateChainFound: - writeInt32(writer, 1) - case *CertErrorC2paError: - writeInt32(writer, 2) - default: - _ = variantValue - panic(fmt.Sprintf("invalid error value `%v` in FfiConverterCertError.Write", value)) - } -} - -type FfiDestroyerCertError struct{} - -func (_ FfiDestroyerCertError) Destroy(value *CertError) { - switch variantValue := value.err.(type) { - case CertErrorNoCertificateChainFound: - variantValue.destroy() - case CertErrorC2paError: - variantValue.destroy() - default: - _ = variantValue - panic(fmt.Sprintf("invalid error value `%v` in FfiDestroyerCertError.Destroy", value)) - } -} - // An Error. type Error struct { err error @@ -1977,6 +2007,124 @@ func (_ FfiDestroyerError) Destroy(value *Error) { } } +type SpError struct { + err error +} + +// Convience method to turn *SpError into error +// Avoiding treating nil pointer as non nil error interface +func (err *SpError) AsError() error { + if err == nil { + return nil + } else { + return err + } +} + +func (err SpError) Error() string { + return fmt.Sprintf("SpError: %s", err.err.Error()) +} + +func (err SpError) Unwrap() error { + return err.err +} + +// Err* are used for checking error type with `errors.Is` +var ErrSpErrorNoCertificateChainFound = fmt.Errorf("SpErrorNoCertificateChainFound") +var ErrSpErrorC2paError = fmt.Errorf("SpErrorC2paError") + +// Variant structs +type SpErrorNoCertificateChainFound struct { + message string +} + +func NewSpErrorNoCertificateChainFound() *SpError { + return &SpError{err: &SpErrorNoCertificateChainFound{}} +} + +func (e SpErrorNoCertificateChainFound) destroy() { +} + +func (err SpErrorNoCertificateChainFound) Error() string { + return fmt.Sprintf("NoCertificateChainFound: %s", err.message) +} + +func (self SpErrorNoCertificateChainFound) Is(target error) bool { + return target == ErrSpErrorNoCertificateChainFound +} + +type SpErrorC2paError struct { + message string +} + +func NewSpErrorC2paError() *SpError { + return &SpError{err: &SpErrorC2paError{}} +} + +func (e SpErrorC2paError) destroy() { +} + +func (err SpErrorC2paError) Error() string { + return fmt.Sprintf("C2paError: %s", err.message) +} + +func (self SpErrorC2paError) Is(target error) bool { + return target == ErrSpErrorC2paError +} + +type FfiConverterSpError struct{} + +var FfiConverterSpErrorINSTANCE = FfiConverterSpError{} + +func (c FfiConverterSpError) Lift(eb RustBufferI) *SpError { + return LiftFromRustBuffer[*SpError](c, eb) +} + +func (c FfiConverterSpError) Lower(value *SpError) C.RustBuffer { + return LowerIntoRustBuffer[*SpError](c, value) +} + +func (c FfiConverterSpError) Read(reader io.Reader) *SpError { + errorID := readUint32(reader) + + message := FfiConverterStringINSTANCE.Read(reader) + switch errorID { + case 1: + return &SpError{&SpErrorNoCertificateChainFound{message}} + case 2: + return &SpError{&SpErrorC2paError{message}} + default: + panic(fmt.Sprintf("Unknown error code %d in FfiConverterSpError.Read()", errorID)) + } + +} + +func (c FfiConverterSpError) Write(writer io.Writer, value *SpError) { + switch variantValue := value.err.(type) { + case *SpErrorNoCertificateChainFound: + writeInt32(writer, 1) + case *SpErrorC2paError: + writeInt32(writer, 2) + default: + _ = variantValue + panic(fmt.Sprintf("invalid error value `%v` in FfiConverterSpError.Write", value)) + } +} + +type FfiDestroyerSpError struct{} + +func (_ FfiDestroyerSpError) Destroy(value *SpError) { + switch variantValue := value.err.(type) { + case SpErrorNoCertificateChainFound: + variantValue.destroy() + case SpErrorC2paError: + variantValue.destroy() + default: + _ = variantValue + panic(fmt.Sprintf("invalid error value `%v` in FfiDestroyerSpError.Destroy", value)) + } +} + type FfiConverterOptionalString struct{} var FfiConverterOptionalStringINSTANCE = FfiConverterOptionalString{} @@ -2120,10 +2268,10 @@ func iroh_streamplace_uniffiFreeGorutine(data C.uint64_t) { guard <- struct{}{} } -func GetManifest(data []byte) (string, *CertError) { - _uniffiRV, _uniffiErr := rustCallWithError[CertError](FfiConverterCertError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { +func GetCert(data []byte) (string, *SpError) { + _uniffiRV, _uniffiErr := rustCallWithError[SpError](FfiConverterSpError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { return GoRustBuffer{ - inner: C.uniffi_iroh_streamplace_fn_func_get_manifest(FfiConverterBytesINSTANCE.Lower(data), _uniffiStatus), + inner: C.uniffi_iroh_streamplace_fn_func_get_cert(FfiConverterBytesINSTANCE.Lower(data), _uniffiStatus), } }) if _uniffiErr != nil { @@ -2134,10 +2282,10 @@ func GetManifest(data []byte) (string, *CertError) { } } -func PrintCert(data []byte) (string, *CertError) { - _uniffiRV, _uniffiErr := rustCallWithError[CertError](FfiConverterCertError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { +func GetManifest(data []byte) (string, *SpError) { + _uniffiRV, _uniffiErr := rustCallWithError[SpError](FfiConverterSpError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { return GoRustBuffer{ - inner: C.uniffi_iroh_streamplace_fn_func_print_cert(FfiConverterBytesINSTANCE.Lower(data), _uniffiStatus), + inner: C.uniffi_iroh_streamplace_fn_func_get_manifest(FfiConverterBytesINSTANCE.Lower(data), _uniffiStatus), } }) if _uniffiErr != nil { @@ -2147,3 +2295,17 @@ func PrintCert(data []byte) (string, *CertError) { return FfiConverterStringINSTANCE.Lift(_uniffiRV), _uniffiErr } } + +func Sign(manifest string, data []byte, certs []byte, gosigner GoSigner) ([]byte, *SpError) { + _uniffiRV, _uniffiErr := rustCallWithError[SpError](FfiConverterSpError{}, func(_uniffiStatus *C.RustCallStatus) RustBufferI { + return GoRustBuffer{ + inner: C.uniffi_iroh_streamplace_fn_func_sign(FfiConverterStringINSTANCE.Lower(manifest), FfiConverterBytesINSTANCE.Lower(data), FfiConverterBytesINSTANCE.Lower(certs), FfiConverterGoSignerINSTANCE.Lower(gosigner), _uniffiStatus), + } + }) + if _uniffiErr != nil { + var _uniffiDefaultValue []byte + return _uniffiDefaultValue, _uniffiErr + } else { + return FfiConverterBytesINSTANCE.Lift(_uniffiRV), _uniffiErr + } +} diff --git a/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.h b/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.h index a8bee0e8f..d089312b5 100644 --- a/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.h +++ b/pkg/iroh/generated/iroh_streamplace/iroh_streamplace.h @@ -391,6 +391,20 @@ static void call_UniffiCallbackInterfaceDataHandlerMethod0( } +#endif +#ifndef UNIFFI_FFIDEF_CALLBACK_INTERFACE_GO_SIGNER_METHOD0 +#define UNIFFI_FFIDEF_CALLBACK_INTERFACE_GO_SIGNER_METHOD0 +typedef void (*UniffiCallbackInterfaceGoSignerMethod0)(uint64_t uniffi_handle, RustBuffer data, RustBuffer* uniffi_out_return, RustCallStatus* callStatus ); + +// Making function static works arround: +// https://github.com/golang/go/issues/11263 +static void call_UniffiCallbackInterfaceGoSignerMethod0( + UniffiCallbackInterfaceGoSignerMethod0 cb, uint64_t uniffi_handle, RustBuffer data, RustBuffer* uniffi_out_return, RustCallStatus* callStatus ) +{ + return cb(uniffi_handle, data, uniffi_out_return, callStatus ); +} + + #endif #ifndef UNIFFI_FFIDEF_V_TABLE_CALLBACK_INTERFACE_DATA_HANDLER #define UNIFFI_FFIDEF_V_TABLE_CALLBACK_INTERFACE_DATA_HANDLER @@ -399,6 +413,14 @@ typedef struct UniffiVTableCallbackInterfaceDataHandler { UniffiCallbackInterfaceFree uniffiFree; } UniffiVTableCallbackInterfaceDataHandler; +#endif +#ifndef UNIFFI_FFIDEF_V_TABLE_CALLBACK_INTERFACE_GO_SIGNER +#define UNIFFI_FFIDEF_V_TABLE_CALLBACK_INTERFACE_GO_SIGNER +typedef struct UniffiVTableCallbackInterfaceGoSigner { + UniffiCallbackInterfaceGoSignerMethod0 sign; + UniffiCallbackInterfaceFree uniffiFree; +} UniffiVTableCallbackInterfaceGoSigner; + #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_DATAHANDLER #define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_DATAHANDLER @@ -441,6 +463,26 @@ uint64_t uniffi_iroh_streamplace_fn_constructor_endpoint_new(void uint64_t uniffi_iroh_streamplace_fn_method_endpoint_node_addr(void* ptr ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_GOSIGNER +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_GOSIGNER +void* uniffi_iroh_streamplace_fn_clone_gosigner(void* ptr, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FREE_GOSIGNER +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FREE_GOSIGNER +void uniffi_iroh_streamplace_fn_free_gosigner(void* ptr, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_INIT_CALLBACK_VTABLE_GOSIGNER +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_INIT_CALLBACK_VTABLE_GOSIGNER +void uniffi_iroh_streamplace_fn_init_callback_vtable_gosigner(UniffiVTableCallbackInterfaceGoSigner* vtable +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_METHOD_GOSIGNER_SIGN +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_METHOD_GOSIGNER_SIGN +RustBuffer uniffi_iroh_streamplace_fn_method_gosigner_sign(void* ptr, RustBuffer data, RustCallStatus *out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_NODEADDR #define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_CLONE_NODEADDR void* uniffi_iroh_streamplace_fn_clone_nodeaddr(void* ptr, RustCallStatus *out_status @@ -571,14 +613,19 @@ uint64_t uniffi_iroh_streamplace_fn_method_sender_node_addr(void* ptr uint64_t uniffi_iroh_streamplace_fn_method_sender_send(void* ptr, RustBuffer key, RustBuffer data ); #endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_GET_CERT +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_GET_CERT +RustBuffer uniffi_iroh_streamplace_fn_func_get_cert(RustBuffer data, RustCallStatus *out_status +); +#endif #ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_GET_MANIFEST #define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_GET_MANIFEST RustBuffer uniffi_iroh_streamplace_fn_func_get_manifest(RustBuffer data, RustCallStatus *out_status ); #endif -#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_PRINT_CERT -#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_PRINT_CERT -RustBuffer uniffi_iroh_streamplace_fn_func_print_cert(RustBuffer data, RustCallStatus *out_status +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_SIGN +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_FN_FUNC_SIGN +RustBuffer uniffi_iroh_streamplace_fn_func_sign(RustBuffer manifest, RustBuffer data, RustBuffer certs, void* gosigner, RustCallStatus *out_status ); #endif #ifndef UNIFFI_FFIDEF_FFI_IROH_STREAMPLACE_RUSTBUFFER_ALLOC @@ -859,6 +906,12 @@ void ffi_iroh_streamplace_rust_future_free_void(uint64_t handle #ifndef UNIFFI_FFIDEF_FFI_IROH_STREAMPLACE_RUST_FUTURE_COMPLETE_VOID #define UNIFFI_FFIDEF_FFI_IROH_STREAMPLACE_RUST_FUTURE_COMPLETE_VOID void ffi_iroh_streamplace_rust_future_complete_void(uint64_t handle, RustCallStatus *out_status +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_GET_CERT +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_GET_CERT +uint16_t uniffi_iroh_streamplace_checksum_func_get_cert(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_GET_MANIFEST @@ -867,9 +920,9 @@ uint16_t uniffi_iroh_streamplace_checksum_func_get_manifest(void ); #endif -#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_PRINT_CERT -#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_PRINT_CERT -uint16_t uniffi_iroh_streamplace_checksum_func_print_cert(void +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_SIGN +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_FUNC_SIGN +uint16_t uniffi_iroh_streamplace_checksum_func_sign(void ); #endif @@ -883,6 +936,12 @@ uint16_t uniffi_iroh_streamplace_checksum_method_datahandler_handle_data(void #define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_METHOD_ENDPOINT_NODE_ADDR uint16_t uniffi_iroh_streamplace_checksum_method_endpoint_node_addr(void +); +#endif +#ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_METHOD_GOSIGNER_SIGN +#define UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_METHOD_GOSIGNER_SIGN +uint16_t uniffi_iroh_streamplace_checksum_method_gosigner_sign(void + ); #endif #ifndef UNIFFI_FFIDEF_UNIFFI_IROH_STREAMPLACE_CHECKSUM_METHOD_NODEADDR_DIRECT_ADDRESSES @@ -1002,6 +1061,8 @@ uint32_t ffi_iroh_streamplace_uniffi_contract_version(void void iroh_streamplace_cgo_dispatchCallbackInterfaceDataHandlerMethod0(uint64_t uniffi_handle, RustBuffer topic, RustBuffer data, UniffiForeignFutureCompleteVoid uniffi_future_callback, uint64_t uniffi_callback_data, UniffiForeignFuture* uniffi_out_return); void iroh_streamplace_cgo_dispatchCallbackInterfaceDataHandlerFree(uint64_t handle); + void iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerMethod0(uint64_t uniffi_handle, RustBuffer data, RustBuffer* uniffi_out_return, RustCallStatus* callStatus ); + void iroh_streamplace_cgo_dispatchCallbackInterfaceGoSignerFree(uint64_t handle); void iroh_streamplace_uniffiFutureContinuationCallback(uint64_t, int8_t); void iroh_streamplace_uniffiFreeGorutine(uint64_t); diff --git a/pkg/media/media.go b/pkg/media/media.go index bc8795b3f..a42409278 100644 --- a/pkg/media/media.go +++ b/pkg/media/media.go @@ -18,6 +18,7 @@ import ( "stream.place/streamplace/pkg/aqtime" "stream.place/streamplace/pkg/atproto" "stream.place/streamplace/pkg/bus" + c2patypes "stream.place/streamplace/pkg/c2patypes" "stream.place/streamplace/pkg/config" "stream.place/streamplace/pkg/gstinit" "stream.place/streamplace/pkg/model" @@ -25,7 +26,6 @@ import ( "stream.place/streamplace/pkg/log" "stream.place/streamplace/pkg/replication" - "git.stream.place/streamplace/c2pa-go/pkg/c2pa/generated/manifeststore" "github.com/piprate/json-gold/ld" irohStreamplace "stream.place/streamplace/pkg/iroh/generated/iroh_streamplace" @@ -195,10 +195,10 @@ type SegmentMetadata struct { var ErrInvalidMetadata = errors.New("invalid segment metadata") -func ParseSegmentAssertions(ctx context.Context, mani *manifeststore.Manifest) (*SegmentMetadata, error) { +func ParseSegmentAssertions(ctx context.Context, mani *c2patypes.Manifest) (*SegmentMetadata, error) { _, span := otel.Tracer("signer").Start(ctx, "ParseSegmentAssertions") defer span.End() - var ass *manifeststore.ManifestAssertion + var ass *c2patypes.ManifestAssertion for _, a := range mani.Assertions { if a.Label == StreamplaceMetadata { ass = &a diff --git a/pkg/media/media_signer.go b/pkg/media/media_signer.go index 05f26a4eb..c55ecd6f6 100644 --- a/pkg/media/media_signer.go +++ b/pkg/media/media_signer.go @@ -1,25 +1,24 @@ package media import ( - "bytes" "context" "crypto" "crypto/ecdsa" + "crypto/rand" + "crypto/sha256" "encoding/json" "fmt" "io" - "path/filepath" "time" - "git.stream.place/streamplace/c2pa-go/pkg/c2pa" "go.opentelemetry.io/otel" - "stream.place/streamplace/pkg/aqio" "stream.place/streamplace/pkg/aqtime" "stream.place/streamplace/pkg/atproto" + c2patypes "stream.place/streamplace/pkg/c2patypes" "stream.place/streamplace/pkg/config" "stream.place/streamplace/pkg/crypto/aqpub" "stream.place/streamplace/pkg/crypto/signers" - "stream.place/streamplace/pkg/log" + "stream.place/streamplace/pkg/iroh/generated/iroh_streamplace" "stream.place/streamplace/pkg/spmetrics" ) @@ -28,6 +27,7 @@ type MediaSigner interface { Pub() aqpub.Pub Streamer() string DID() string + Sign(data []byte) ([]byte, *iroh_streamplace.SpError) } type MediaSignerLocal struct { @@ -39,40 +39,18 @@ type MediaSignerLocal struct { did string } -func prepareCert(ctx context.Context, cli *config.CLI, signer crypto.Signer) ([]byte, string, error) { - pub, err := aqpub.FromPublicKey(signer.Public().(*ecdsa.PublicKey)) - if err != nil { - return nil, "", err - } - fSlice := []string{pub.String(), CertFile} - exists, err := cli.DataFileExists(fSlice) +func prepareCert(ctx context.Context, cli *config.CLI, signer crypto.Signer) ([]byte, error) { + + cert, err := signers.GenerateES256KCert(signer) if err != nil { - return nil, "", err - } - if !exists { - cert, err := signers.GenerateES256KCert(signer) - if err != nil { - return nil, "", err - } - r := bytes.NewReader(cert) - err = cli.DataFileWrite(fSlice, r, false) - if err != nil { - return nil, "", err - } - log.Log(ctx, "wrote new media signing certificate", "file", filepath.Join(pub.String(), CertFile)) - } - buf := bytes.Buffer{} - if err := cli.DataFileRead(fSlice, &buf); err != nil { - return nil, "", err + return nil, err } - fPath := cli.DataFilePath(fSlice) - cert := buf.Bytes() - return cert, fPath, nil + return cert, nil } func MakeMediaSigner(ctx context.Context, cli *config.CLI, streamer string, signer crypto.Signer) (MediaSigner, error) { - cert, _, err := prepareCert(ctx, cli, signer) + cert, err := prepareCert(ctx, cli, signer) if err != nil { return nil, err } @@ -133,43 +111,26 @@ func (ms *MediaSignerLocal) SignMP4(ctx context.Context, input io.ReadSeeker, st if err != nil { return nil, fmt.Errorf("failed to marshal manifest: %w", err) } - var manifest c2pa.ManifestDefinition + var manifest c2patypes.ManifestDefinition err = json.Unmarshal(manifestBs, &manifest) if err != nil { return nil, fmt.Errorf("failed to unmarshal manifest: %w", err) } span.End() - ctx, span = otel.Tracer("signer").Start(ctx, "SignMP4_GetSigningAlgorithm") - alg, err := c2pa.GetSigningAlgorithm(string(c2pa.ES256K)) - if err != nil { - return nil, fmt.Errorf("failed to get signing algorithm: %w", err) - } - span.End() - - ctx, span = otel.Tracer("signer").Start(ctx, "SignMP4_NewBuilder") - b, err := c2pa.NewBuilder(&manifest, &c2pa.BuilderParams{ - Cert: ms.Cert, - Signer: ms.Signer, - Algorithm: alg, - TAURL: ms.TAURL, - }) + bs, err := io.ReadAll(input) if err != nil { - return nil, fmt.Errorf("failed to create C2PA builder: %w", err) + return nil, fmt.Errorf("failed to read input: %w", err) } - span.End() - ctx, span = otel.Tracer("signer").Start(ctx, "SignMP4_Sign") - output := &aqio.ReadWriteSeeker{} - err = b.Sign(input, output, "video/mp4") - if err != nil { - return nil, fmt.Errorf("failed to sign MP4: %w", err) + bs, rustErr := iroh_streamplace.Sign(string(manifestBs), bs, ms.Cert, ms) + if rustErr.AsError() != nil { + return nil, rustErr.AsError() } span.End() ctx, span = otel.Tracer("signer").Start(ctx, "SignMP4_OutputBytes") defer ctx.Done() - bs, err := output.Bytes() if err != nil { return nil, fmt.Errorf("failed to get output bytes: %w", err) } @@ -178,6 +139,15 @@ func (ms *MediaSignerLocal) SignMP4(ctx context.Context, input io.ReadSeeker, st return bs, nil } +func (ms *MediaSignerLocal) Sign(data []byte) ([]byte, *iroh_streamplace.SpError) { + digest := sha256.Sum256(data) + sig, err := ms.Signer.Sign(rand.Reader, digest[:], nil) + if err != nil { + return nil, iroh_streamplace.NewSpErrorNoCertificateChainFound() + } + return sig, nil +} + func (ms *MediaSignerLocal) Pub() aqpub.Pub { return ms.AQPub } diff --git a/pkg/media/media_signer_ext.go b/pkg/media/media_signer_ext.go index 66fd7aa63..64b9d25b3 100644 --- a/pkg/media/media_signer_ext.go +++ b/pkg/media/media_signer_ext.go @@ -1,128 +1,108 @@ package media -import ( - "bytes" - "context" - "crypto" - "crypto/ecdsa" - "fmt" - "io" - "os" - "os/exec" - "time" - - "github.com/decred/dcrd/dcrec/secp256k1" - "github.com/mr-tron/base58" - "go.opentelemetry.io/otel" - "stream.place/streamplace/pkg/atproto" - "stream.place/streamplace/pkg/config" - "stream.place/streamplace/pkg/crypto/aqpub" - "stream.place/streamplace/pkg/spmetrics" -) - -type MediaSignerExt struct { - cli *config.CLI - signer crypto.Signer - pub aqpub.Pub - certPath string - streamer string - keyBs []byte - taURL string - did string -} - -func MakeMediaSignerExt(ctx context.Context, cli *config.CLI, streamer string, keyBs []byte) (MediaSigner, error) { - key, _ := secp256k1.PrivKeyFromBytes(keyBs) - if key == nil { - return nil, fmt.Errorf("invalid authorization key (not valid secp256k1)") - } - var signer crypto.Signer = key.ToECDSA() - _, certPath, err := prepareCert(ctx, cli, signer) - if err != nil { - return nil, err - } - pub, err := aqpub.FromPublicKey(signer.Public().(*ecdsa.PublicKey)) - if err != nil { - return nil, err - } - did, err := atproto.ParsePubKey(signer.Public().(*ecdsa.PublicKey)) - if err != nil { - return nil, err - } - return &MediaSignerExt{ - // cli: cli, - signer: signer, - certPath: certPath, - streamer: streamer, - pub: pub, - keyBs: keyBs, - taURL: cli.TAURL, - did: did.DIDKey(), - }, nil -} - -func (ms *MediaSignerExt) SignMP4(ctx context.Context, input io.ReadSeeker, start int64) ([]byte, error) { - startTime := time.Now() - _, span := otel.Tracer("signer").Start(ctx, "SignMP4_Ext") - defer span.End() - // Get the path to the current executable - execPath, err := os.Executable() - if err != nil { - return nil, fmt.Errorf("failed to get executable path: %w", err) - } - - enc := base58.Encode(ms.keyBs) - - // Prepare command - cmd := exec.Command(execPath, "sign", - "--key", enc, - "--cert", ms.certPath, - "--ta-url", ms.taURL, - "--streamer", ms.streamer, - "--start-time", fmt.Sprintf("%d", start)) - - // overwrite so that our subprocesses don't do their own leak checking - cmd.Env = append(os.Environ(), "LD_PRELOAD=") - - // Set up pipes for stdin and stdout - stdin, err := cmd.StdinPipe() - if err != nil { - return nil, fmt.Errorf("failed to create stdin pipe: %w", err) - } - - stdout := &bytes.Buffer{} - cmd.Stdout = stdout - stderr := &bytes.Buffer{} - cmd.Stderr = stderr - - // Start the command - if err := cmd.Start(); err != nil { - return nil, fmt.Errorf("failed to start command: %w", err) - } - - // Copy input to stdin - _, err = io.Copy(stdin, input) - if err != nil { - return nil, fmt.Errorf("failed to write to stdin: %w stderr=%s", err, stderr.String()) - } - stdin.Close() - - // Wait for the command to complete - if err := cmd.Wait(); err != nil { - return nil, fmt.Errorf("command failed: %w, stderr: %s", err, stderr.String()) - } - spmetrics.SigningDuration.WithLabelValues(ms.streamer).Observe(float64(time.Since(startTime).Milliseconds())) - return stdout.Bytes(), nil -} - -func (ms *MediaSignerExt) Pub() aqpub.Pub { - return ms.pub -} - -func (ms *MediaSignerExt) Streamer() string { - return ms.streamer -} - -func (ms *MediaSignerExt) DID() string { - return ms.did -} +// type MediaSignerExt struct { +// cli *config.CLI +// signer crypto.Signer +// pub aqpub.Pub +// certPath string +// streamer string +// keyBs []byte +// taURL string +// did string +// } + +// func MakeMediaSignerExt(ctx context.Context, cli *config.CLI, streamer string, keyBs []byte) (MediaSigner, error) { +// key, _ := secp256k1.PrivKeyFromBytes(keyBs) +// if key == nil { +// return nil, fmt.Errorf("invalid authorization key (not valid secp256k1)") +// } +// var signer crypto.Signer = key.ToECDSA() +// _, certPath, err := prepareCert(ctx, cli, signer) +// if err != nil { +// return nil, err +// } +// pub, err := aqpub.FromPublicKey(signer.Public().(*ecdsa.PublicKey)) +// if err != nil { +// return nil, err +// } +// did, err := atproto.ParsePubKey(signer.Public().(*ecdsa.PublicKey)) +// if err != nil { +// return nil, err +// } +// return &MediaSignerExt{ +// // cli: cli, +// signer: signer, +// certPath: certPath, +// streamer: streamer, +// pub: pub, +// keyBs: keyBs, +// taURL: cli.TAURL, +// did: did.DIDKey(), +// }, nil +// } + +// func (ms *MediaSignerExt) SignMP4(ctx context.Context, input io.ReadSeeker, start int64) ([]byte, error) { +// startTime := time.Now() +// _, span := otel.Tracer("signer").Start(ctx, "SignMP4_Ext") +// defer span.End() +// // Get the path to the current executable +// execPath, err := os.Executable() +// if err != nil { +// return nil, fmt.Errorf("failed to get executable path: %w", err) +// } + +// enc := base58.Encode(ms.keyBs) + +// // Prepare command +// cmd := exec.Command(execPath, "sign", +// "--key", enc, +// "--cert", ms.certPath, +// "--ta-url", ms.taURL, +// "--streamer", ms.streamer, +// "--start-time", fmt.Sprintf("%d", start)) + +// // overwrite so that our subprocesses don't do their own leak checking +// cmd.Env = append(os.Environ(), "LD_PRELOAD=") + +// // Set up pipes for stdin and stdout +// stdin, err := cmd.StdinPipe() +// if err != nil { +// return nil, fmt.Errorf("failed to create stdin pipe: %w", err) +// } + +// stdout := &bytes.Buffer{} +// cmd.Stdout = stdout +// stderr := &bytes.Buffer{} +// cmd.Stderr = stderr + +// // Start the command +// if err := cmd.Start(); err != nil { +// return nil, fmt.Errorf("failed to start command: %w", err) +// } + +// // Copy input to stdin +// _, err = io.Copy(stdin, input) +// if err != nil { +// return nil, fmt.Errorf("failed to write to stdin: %w stderr=%s", err, stderr.String()) +// } +// stdin.Close() + +// // Wait for the command to complete +// if err := cmd.Wait(); err != nil { +// return nil, fmt.Errorf("command failed: %w, stderr: %s", err, stderr.String()) +// } +// spmetrics.SigningDuration.WithLabelValues(ms.streamer).Observe(float64(time.Since(startTime).Milliseconds())) +// return stdout.Bytes(), nil +// } + +// func (ms *MediaSignerExt) Pub() aqpub.Pub { +// return ms.pub +// } + +// func (ms *MediaSignerExt) Streamer() string { +// return ms.streamer +// } + +// func (ms *MediaSignerExt) DID() string { +// return ms.did +// } diff --git a/pkg/media/validate.go b/pkg/media/validate.go index e6f0e2b96..d832d731f 100644 --- a/pkg/media/validate.go +++ b/pkg/media/validate.go @@ -3,18 +3,19 @@ package media import ( "bytes" "context" + "encoding/json" "fmt" "io" "strings" "go.opentelemetry.io/otel" "stream.place/streamplace/pkg/aqtime" + c2patypes "stream.place/streamplace/pkg/c2patypes" "stream.place/streamplace/pkg/constants" "stream.place/streamplace/pkg/crypto/signers" + "stream.place/streamplace/pkg/iroh/generated/iroh_streamplace" "stream.place/streamplace/pkg/log" "stream.place/streamplace/pkg/model" - - "git.stream.place/streamplace/c2pa-go/pkg/c2pa" ) func (mm *MediaManager) ValidateMP4(ctx context.Context, input io.Reader) error { @@ -24,18 +25,24 @@ func (mm *MediaManager) ValidateMP4(ctx context.Context, input io.Reader) error if err != nil { return err } - r := bytes.NewReader(buf) - reader, err := c2pa.FromStream(r, "video/mp4") + maniStr, rustErr := iroh_streamplace.GetManifest(buf) + if rustErr.AsError() != nil { + return rustErr.AsError() + } + var mani c2patypes.Manifest + err = json.Unmarshal([]byte(maniStr), &mani) if err != nil { return err } - mani := reader.GetActiveManifest() - certs := reader.GetProvenanceCertChain() - pub, err := signers.ParseES256KCert([]byte(certs)) + certStr, rustErr := iroh_streamplace.GetCert(buf) + if rustErr.AsError() != nil { + return rustErr.AsError() + } + pub, err := signers.ParseES256KCert([]byte(certStr)) if err != nil { return err } - meta, err := ParseSegmentAssertions(ctx, mani) + meta, err := ParseSegmentAssertions(ctx, &mani) if err != nil { return err } @@ -75,7 +82,7 @@ func (mm *MediaManager) ValidateMP4(ctx context.Context, input io.Reader) error } defer fd.Close() go mm.replicator.NewSegment(ctx, buf) - r = bytes.NewReader(buf) + r := bytes.NewReader(buf) if _, err := io.Copy(fd, r); err != nil { return err } diff --git a/rust/iroh-streamplace/src/c2pa.rs b/rust/iroh-streamplace/src/c2pa.rs index 3207d7439..be954fa91 100644 --- a/rust/iroh-streamplace/src/c2pa.rs +++ b/rust/iroh-streamplace/src/c2pa.rs @@ -1,9 +1,13 @@ +use c2pa::Builder; +use c2pa::CallbackSigner; use c2pa::Reader; +use c2pa::settings::Settings; use std::io::Cursor; +use std::sync::Arc; #[derive(Debug, thiserror::Error, uniffi::Error)] #[uniffi(flat_error)] -pub enum CertError { +pub enum SPError { #[error("No certificate chain found")] NoCertificateChainFound, #[error("C2PA error: {0}")] @@ -11,27 +15,123 @@ pub enum CertError { } #[uniffi::export] -pub fn print_cert(data: Vec) -> Result { +pub fn get_cert(data: Vec) -> Result { let reader = Reader::from_stream("video/mp4", Cursor::new(data)) - .map_err(|e| CertError::C2paError(e.to_string()))?; - // todo: add cawg certs here?? + .map_err(|e| SPError::C2paError(e.to_string()))?; if let Some(manifest) = reader.active_manifest() { if let Some(si) = manifest.signature_info() { - println!("{}", si.cert_chain()); - // todo: add ocsp validation info return Ok(si.cert_chain().to_string()); } } - Err(CertError::NoCertificateChainFound) + Err(SPError::NoCertificateChainFound) } #[uniffi::export] -pub fn get_manifest(data: Vec) -> Result { +pub fn get_manifest(data: Vec) -> Result { let reader = Reader::from_stream("video/mp4", Cursor::new(data)) - .map_err(|e| CertError::C2paError(e.to_string()))?; - // todo: add cawg certs here?? + .map_err(|e| SPError::C2paError(e.to_string()))?; if let Some(manifest) = reader.active_manifest() { return Ok(manifest.to_string()); } - Err(CertError::NoCertificateChainFound) + Err(SPError::NoCertificateChainFound) +} + +#[uniffi::export(with_foreign)] +pub trait GoSigner: Send + Sync { + fn sign(&self, data: Vec) -> Result, SPError>; +} + +// #[derive(uniffi::Object)] +// struct Authenticator { +// gosigner: Arc, +// } + +// impl Authenticator { +// pub fn new(gosigner: Arc) -> Self { +// Self { gosigner } +// } + +// pub fn login(&self) { +// let username = self.gosigner.get("username".into()); +// let password = self.gosigner.get("password".into()); +// } +// } + +const TOML_SETTINGS: &str = r#" +version_major = 1 +version_minor = 0 + +[trust] + +[core] +debug = true +hash_alg = "sha256" +salt_jumbf_boxes = true +prefer_box_hash = false +merkle_tree_max_proofs = 5 +compress_manifests = true + +[verify] +verify_after_reading = false +verify_after_sign = false +verify_trust = false +verify_timestamp_trust = false +ocsp_fetch = false +remote_manifest_fetch = false +check_ingredient_trust = false +skip_ingredient_conflict_resolution = false +strict_v1_validation = false + +[builder.thumbnail] +enabled = false +ignore_errors = true +long_edge = 1024 +prefer_smallest_format = true +quality = "medium" + +[builder.actions] +all_actions_included = false + +[builder.actions.auto_created_action] +enabled = true +source_type = "http://c2pa.org/digitalsourcetype/empty" + +[builder.actions.auto_opened_action] +enabled = true + +[builder.actions.auto_placed_action] +enabled = true +"#; + +#[uniffi::export] +pub fn sign( + manifest: String, + data: Vec, + certs: Vec, + gosigner: Arc, +) -> Result, SPError> { + Settings::from_toml(TOML_SETTINGS).map_err(|e| SPError::C2paError(e.to_string()))?; + let callback_signer = CallbackSigner::new( + move |_context: *const (), data: &[u8]| { + gosigner + .sign(data.to_vec()) + .map_err(|e| c2pa::Error::BadParam(e.to_string())) + }, + c2pa::SigningAlg::Es256K, + certs, + ); + let mut builder = + Builder::from_json(&manifest).map_err(|e| SPError::C2paError(e.to_string()))?; + let mut output = Vec::new(); + let mut input_cursor = Cursor::new(data); + let mut output_cursor = Cursor::new(&mut output); + builder + .sign( + &callback_signer, + "video/mp4", + &mut input_cursor, + &mut output_cursor, + ) + .map_err(|e| SPError::C2paError(e.to_string()))?; + Ok(output) }