// Copyright (c) 2020 Nikos Filippakis // // This Source Code Form is subject to the terms of the Mozilla Public // License, v. 2.0. If a copy of the MPL was not distributed with this // file, You can obtain one at http://mozilla.org/MPL/2.0/. //go:build !nosas // +build !nosas package crypto import ( "fmt" "maunium.net/go/mautrix/crypto/olm" "maunium.net/go/mautrix/event" "maunium.net/go/mautrix/id" ) // SASData contains the data that users need to verify. type SASData interface { Type() event.SASMethod } // VerificationMethod describes a method for generating a SAS. type VerificationMethod interface { // GetVerificationSAS uses the user, device ID and key of the user who initiated the verification transaction, // the user, device ID and key of the user who accepted, the transaction ID and the SAS object to generate a SAS. // The SAS can be any type, such as an array of numbers or emojis. GetVerificationSAS(initUserID id.UserID, initDeviceID id.DeviceID, initKey string, acceptUserID id.UserID, acceptDeviceID id.DeviceID, acceptKey string, transactionID string, sas *olm.SAS) (SASData, error) // Type returns the type of this SAS method Type() event.SASMethod } const sasInfoFormat = "MATRIX_KEY_VERIFICATION_SAS|%s|%s|%s|%s|%s|%s|%s" // VerificationMethodDecimal describes the decimal SAS method. type VerificationMethodDecimal struct{} // DecimalSASData contains the verification numbers for the decimal SAS method. type DecimalSASData [3]uint // Type returns the decimal SAS method type. func (DecimalSASData) Type() event.SASMethod { return event.SASDecimal } // GetVerificationSAS generates the three numbers that need to match with the other device for a verification to be valid. func (VerificationMethodDecimal) GetVerificationSAS(initUserID id.UserID, initDeviceID id.DeviceID, initKey string, acceptUserID id.UserID, acceptDeviceID id.DeviceID, acceptKey string, transactionID string, sas *olm.SAS) (SASData, error) { sasInfo := fmt.Sprintf(sasInfoFormat, initUserID, initDeviceID, initKey, acceptUserID, acceptDeviceID, acceptKey, transactionID) sasBytes, err := sas.GenerateBytes([]byte(sasInfo), 5) if err != nil { return DecimalSASData{0, 0, 0}, err } numbers := DecimalSASData{ (uint(sasBytes[0])<<5 | uint(sasBytes[1])>>3) + 1000, (uint(sasBytes[1]&0x7)<<10 | uint(sasBytes[2])<<2 | uint(sasBytes[3]>>6)) + 1000, (uint(sasBytes[3]&0x3F)<<7 | uint(sasBytes[4])>>1) + 1000, } return numbers, nil } // Type returns the decimal SAS method type. func (VerificationMethodDecimal) Type() event.SASMethod { return event.SASDecimal } var allEmojis = [...]VerificationEmoji{ {'🐶', "Dog"}, {'🐱', "Cat"}, {'🦁', "Lion"}, {'🐎', "Horse"}, {'🦄', "Unicorn"}, {'🐷', "Pig"}, {'🐘', "Elephant"}, {'🐰', "Rabbit"}, {'🐼', "Panda"}, {'🐓', "Rooster"}, {'🐧', "Penguin"}, {'🐢', "Turtle"}, {'🐟', "Fish"}, {'🐙', "Octopus"}, {'🦋', "Butterfly"}, {'🌷', "Flower"}, {'🌳', "Tree"}, {'🌵', "Cactus"}, {'🍄', "Mushroom"}, {'🌏', "Globe"}, {'🌙', "Moon"}, {'☁', "Cloud"}, {'🔥', "Fire"}, {'🍌', "Banana"}, {'🍎', "Apple"}, {'🍓', "Strawberry"}, {'🌽', "Corn"}, {'🍕', "Pizza"}, {'🎂', "Cake"}, {'❤', "Heart"}, {'😀', "Smiley"}, {'🤖', "Robot"}, {'🎩', "Hat"}, {'👓', "Glasses"}, {'🔧', "Spanner"}, {'🎅', "Santa"}, {'👍', "Thumbs Up"}, {'☂', "Umbrella"}, {'⌛', "Hourglass"}, {'⏰', "Clock"}, {'🎁', "Gift"}, {'💡', "Light Bulb"}, {'📕', "Book"}, {'✏', "Pencil"}, {'📎', "Paperclip"}, {'✂', "Scissors"}, {'🔒', "Lock"}, {'🔑', "Key"}, {'🔨', "Hammer"}, {'☎', "Telephone"}, {'🏁', "Flag"}, {'🚂', "Train"}, {'🚲', "Bicycle"}, {'✈', "Aeroplane"}, {'🚀', "Rocket"}, {'🏆', "Trophy"}, {'⚽', "Ball"}, {'🎸', "Guitar"}, {'🎺', "Trumpet"}, {'🔔', "Bell"}, {'⚓', "Anchor"}, {'🎧', "Headphones"}, {'📁', "Folder"}, {'📌', "Pin"}, } // VerificationEmoji describes an emoji that might be sent for verifying devices. type VerificationEmoji struct { Emoji rune Description string } func (vm VerificationEmoji) GetEmoji() rune { return vm.Emoji } func (vm VerificationEmoji) GetDescription() string { return vm.Description } // EmojiSASData contains the verification emojis for the emoji SAS method. type EmojiSASData [7]VerificationEmoji // Type returns the emoji SAS method type. func (EmojiSASData) Type() event.SASMethod { return event.SASEmoji } // VerificationMethodEmoji describes the emoji SAS method. type VerificationMethodEmoji struct{} // GetVerificationSAS generates the three numbers that need to match with the other device for a verification to be valid. func (VerificationMethodEmoji) GetVerificationSAS(initUserID id.UserID, initDeviceID id.DeviceID, initKey string, acceptUserID id.UserID, acceptDeviceID id.DeviceID, acceptKey string, transactionID string, sas *olm.SAS) (SASData, error) { sasInfo := fmt.Sprintf(sasInfoFormat, initUserID, initDeviceID, initKey, acceptUserID, acceptDeviceID, acceptKey, transactionID) var emojis EmojiSASData sasBytes, err := sas.GenerateBytes([]byte(sasInfo), 6) if err != nil { return emojis, err } sasNum := uint64(sasBytes[0])<<40 | uint64(sasBytes[1])<<32 | uint64(sasBytes[2])<<24 | uint64(sasBytes[3])<<16 | uint64(sasBytes[4])<<8 | uint64(sasBytes[5]) for i := 0; i < len(emojis); i++ { // take nth group of 6 bits emojiIdx := (sasNum >> uint(48-(i+1)*6)) & 0x3F emoji := allEmojis[emojiIdx] emojis[i] = emoji } return emojis, nil } // Type returns the emoji SAS method type. func (VerificationMethodEmoji) Type() event.SASMethod { return event.SASEmoji }