aliases: sign and verify

This commit is contained in:
Henry 2021-03-11 17:06:54 +01:00
parent c9eb1aef9d
commit 04104c0f9e
2 changed files with 130 additions and 0 deletions

66
aliases/confirm.go Normal file
View File

@ -0,0 +1,66 @@
// SPDX-License-Identifier: MIT
// Package aliases implements the validation and signing features of https://ssb-ngi-pointer.github.io/rooms2/#alias
package aliases
import (
"bytes"
"golang.org/x/crypto/ed25519"
refs "go.mindeco.de/ssb-refs"
)
// Registration ties an alias to the ID of the user and the RoomID it should be registerd on
type Registration struct {
Alias string
UserID refs.FeedRef
RoomID refs.FeedRef
}
// Sign takes the public key (belonging to UserID) and returns the signed confirmation
func (r Registration) Sign(privKey ed25519.PrivateKey) Confirmation {
var conf Confirmation
conf.Registration = r
msg := r.createRegistrationMessage()
conf.Signature = ed25519.Sign(privKey, msg)
return conf
}
// createRegistrationMessage returns the string of bytes that should be signed
func (r Registration) createRegistrationMessage() []byte {
var message bytes.Buffer
message.WriteString("=room-alias-registration:")
message.WriteString(r.RoomID.Ref())
message.WriteString(":")
message.WriteString(r.UserID.Ref())
message.WriteString(":")
message.WriteString(r.Alias)
return message.Bytes()
}
// Confirmation combines a registration with the corresponding signature
type Confirmation struct {
Registration
Signature []byte
}
// Verify checks that the confirmation is for the expected room and from the expected feed
func (c Confirmation) Verify(room, feed refs.FeedRef) bool {
// not for that room
if !c.RoomID.Equal(&room) {
return false
}
// not for that feed
if !c.UserID.Equal(&feed) {
return false
}
// re-construct the registration
message := c.createRegistrationMessage()
// check the signature matches
return ed25519.Verify(c.UserID.PubKey(), message, c.Signature)
}

64
aliases/confirm_test.go Normal file
View File

@ -0,0 +1,64 @@
// SPDX-License-Identifier: MIT
package aliases
import (
"bytes"
"testing"
"github.com/ssb-ngi-pointer/go-ssb-room/internal/maybemod/keys"
"github.com/stretchr/testify/require"
refs "go.mindeco.de/ssb-refs"
)
func TestConfirmation(t *testing.T) {
r := require.New(t)
// this is our room, it's not a valid feed but thats fine for this test
roomID := refs.FeedRef{
ID: bytes.Repeat([]byte("test"), 8),
Algo: "test",
}
// to make the test deterministic, decided by fair dice roll.
seed := bytes.Repeat([]byte("yeah"), 8)
// our user, who will sign the registration
userKeyPair, err := keys.NewKeyPair(bytes.NewReader(seed))
r.NoError(err)
// create and fill out the registration for an alias (in this case the name of the test)
var valid Registration
valid.RoomID = roomID
valid.UserID = userKeyPair.Feed
valid.Alias = t.Name()
// internal function to create the registration string
msg := valid.createRegistrationMessage()
want := "=room-alias-registration:@dGVzdHRlc3R0ZXN0dGVzdHRlc3R0ZXN0dGVzdHRlc3Q=.test:@Rt2aJrtOqWXhBZ5/vlfzeWQ9Bj/z6iT8CMhlr2WWlG4=.ed25519:TestConfirmation"
r.Equal(want, string(msg))
// create the signed confirmation
confirmation := valid.Sign(userKeyPair.Pair.Secret)
yes := confirmation.Verify(roomID, userKeyPair.Feed)
r.True(yes, "should be valid for this room and feed")
// make up another id for the invalid test(s)
otherID := refs.FeedRef{
ID: bytes.Repeat([]byte("nope"), 8),
Algo: "test",
}
yes = confirmation.Verify(otherID, userKeyPair.Feed)
r.False(yes, "should not be valid for another room")
yes = confirmation.Verify(roomID, otherID)
r.False(yes, "should not be valid for this room but another feed")
// puncture the signature to emulate an invalid one
confirmation.Signature[0] = confirmation.Signature[0] ^ 1
yes = confirmation.Verify(roomID, userKeyPair.Feed)
r.False(yes, "should not be valid anymore")
}