diff --git a/library/README.md b/library/README.md index f3a74d90..5a40bb2e 100644 --- a/library/README.md +++ b/library/README.md @@ -446,7 +446,7 @@ For example: } ``` -### `extern char* waku_connect_peer(char* address, int timeoutMs)` +### `extern char* waku_connect(char* address, int timeoutMs)` Dial peer using a multiaddress. @@ -499,7 +499,7 @@ For example: } ``` -### `extern char* waku_disconnect_peer(char* peerId)` +### `extern char* waku_disconnect(char* peerId)` Disconnect a peer using its peerID diff --git a/library/api.go b/library/api.go index 1831d6a4..464865ee 100644 --- a/library/api.go +++ b/library/api.go @@ -29,6 +29,7 @@ func main() {} // - keepAliveInterval: interval in seconds to ping all peers // - relay: Enable WakuRelay. Default `true` // - minPeersToPublish: The minimum number of peers required on a topic to allow broadcasting a message. Default `0` +// - filter: Enable Filter. Default `false` func waku_new(configJSON *C.char) *C.char { response := mobile.NewNode(C.GoString(configJSON)) return C.CString(response) diff --git a/waku/v2/noise/handshake.go b/waku/v2/noise/handshake.go index 438a68e7..cbd3c6d2 100644 --- a/waku/v2/noise/handshake.go +++ b/waku/v2/noise/handshake.go @@ -19,6 +19,8 @@ var ( ChaChaPoly = WakuNoiseProtocolID(30) ) +const NoisePaddingBlockSize = 248 + var ErrorHandshakeComplete = errors.New("handshake complete") // All protocols share same cipher suite @@ -121,8 +123,13 @@ func (hs *Handshake) Step(readPayloadV2 *PayloadV2, transportMessage []byte) (*H // We initialize a payload v2 and we set proper protocol ID (if supported) result.Payload2.ProtocolId = hs.protocolID + payload, err := PKCS7_Pad(transportMessage, NoisePaddingBlockSize) + if err != nil { + return nil, err + } + var noisePubKeys [][]byte - msg, cs1, cs2, err = hs.state.WriteMessageAndGetPK(hs.hsBuff, &noisePubKeys, transportMessage) + msg, cs1, cs2, err = hs.state.WriteMessageAndGetPK(hs.hsBuff, &noisePubKeys, payload) if err != nil { return nil, err } @@ -149,8 +156,14 @@ func (hs *Handshake) Step(readPayloadV2 *PayloadV2, transportMessage []byte) (*H hs.shouldWrite = true - // We retrieve and store the (decrypted) received transport message - result.TransportMessage = msg + // We retrieve, and store the (unpadded decrypted) received transport message + + payload, err := PKCS7_Unpad(msg, NoisePaddingBlockSize) + if err != nil { + return nil, err + } + + result.TransportMessage = payload } if cs1 != nil && cs2 != nil { @@ -186,9 +199,12 @@ func (hs *Handshake) Encrypt(plaintext []byte) (*PayloadV2, error) { return nil, errors.New("tried to encrypt empty plaintext") } - // TODO: add padding (?) + paddedTransportMessage, err := PKCS7_Pad(plaintext, NoisePaddingBlockSize) + if err != nil { + return nil, err + } - cyphertext, err := hs.enc.Encrypt(nil, nil, plaintext) + cyphertext, err := hs.enc.Encrypt(nil, nil, paddedTransportMessage) if err != nil { return nil, err } @@ -215,7 +231,12 @@ func (hs *Handshake) Decrypt(payload *PayloadV2) ([]byte, error) { return nil, errors.New("tried to decrypt empty ciphertext") } - return hs.dec.Decrypt(nil, nil, payload.TransportMessage) + paddedMessage, err := hs.dec.Decrypt(nil, nil, payload.TransportMessage) + if err != nil { + return nil, err + } + + return PKCS7_Unpad(paddedMessage, NoisePaddingBlockSize) } // NewHandshake_XX_25519_ChaChaPoly_SHA256 creates a handshake where the initiator and receiver are not aware of each other static keys diff --git a/waku/v2/noise/noise_test.go b/waku/v2/noise/noise_test.go index 0f3a62bc..a3a3cd1d 100644 --- a/waku/v2/noise/noise_test.go +++ b/waku/v2/noise/noise_test.go @@ -186,3 +186,18 @@ func TestNoiseXK1HandshakeRoundtrip(t *testing.T) { handshakeTest(t, hsAlice, hsBob) } + +func TestPKCSPaddingUnpadding(t *testing.T) { + maxMessageLength := 3 * NoisePaddingBlockSize + for messageLen := 0; messageLen <= maxMessageLength; messageLen++ { + message := generateRandomBytes(t, messageLen) + padded, err := PKCS7_Pad(message, NoisePaddingBlockSize) + require.NoError(t, err) + unpadded, err := PKCS7_Unpad(padded, NoisePaddingBlockSize) + require.NoError(t, err) + + require.Greater(t, len(padded), 0) + require.Equal(t, len(padded)%NoisePaddingBlockSize, 0) + require.Equal(t, message, unpadded) + } +} diff --git a/waku/v2/noise/padding.go b/waku/v2/noise/padding.go new file mode 100644 index 00000000..dddca482 --- /dev/null +++ b/waku/v2/noise/padding.go @@ -0,0 +1,48 @@ +package noise + +import ( + "errors" +) + +// PKCS7_Pad pads a payload according to PKCS#7 as per +// RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3 +func PKCS7_Pad(payload []byte, paddingSize int) ([]byte, error) { + if paddingSize >= 256 { + return nil, errors.New("invalid padding size") + } + + k := paddingSize - (len(payload) % paddingSize) + + var padVal int + if k != 0 { + padVal = k + } else { + padVal = paddingSize + } + + padding := make([]byte, padVal) + for i := range padding { + padding[i] = byte(padVal) + } + + return append(payload, padding...), nil +} + +// PKCS7_Unpad unpads a payload according to PKCS#7 as per +// RFC 5652 https://datatracker.ietf.org/doc/html/rfc5652#section-6.3 +func PKCS7_Unpad(payload []byte, paddingSize int) ([]byte, error) { + if paddingSize >= 256 { + return nil, errors.New("invalid padding size") + } + + if len(payload) == 0 { + return nil, nil // empty payload + } + + high := len(payload) - 1 + k := payload[high] + + unpadded := payload[0:(high + 1 - int(k))] + + return unpadded, nil +} diff --git a/waku/v2/rpc/codec.go b/waku/v2/rpc/codec.go index ebcea8ba..65f852f1 100644 --- a/waku/v2/rpc/codec.go +++ b/waku/v2/rpc/codec.go @@ -190,6 +190,6 @@ func (c *CodecRequest) writeServerResponse(w http.ResponseWriter, status int, re _, _ = w.Write(b) } else { // Not sure in which case will this happen. But seems harmless. - rpc.WriteError(w, 400, err.Error()) + rpc.WriteError(w, status, err.Error()) } }