github.com/ericwq/aprilsh@v0.0.0-20240517091432-958bc568daa0/encrypt/encrypt.go (about)

     1  // Copyright 2022 wangqi. All rights reserved.
     2  // Use of this source code is governed by a MIT-style
     3  // license that can be found in the LICENSE file.
     4  
     5  package encrypt
     6  
     7  import (
     8  	"crypto/aes"
     9  	"crypto/cipher"
    10  	"crypto/rand"
    11  	"encoding/base64"
    12  	"encoding/binary"
    13  	"fmt"
    14  	"io"
    15  	"sync/atomic"
    16  	"syscall"
    17  
    18  	"github.com/ericwq/aprilsh/util"
    19  )
    20  
    21  const (
    22  	NONCE_LEN = 12
    23  
    24  	RECEIVE_MTU = 2048
    25  	ADDED_BYTES = 16 /* final OCB block */
    26  )
    27  
    28  // var logW = log.New(os.Stderr, "WARN: ", log.Ldate|log.Ltime|log.Lshortfile)
    29  
    30  // use sys call to generate random number
    31  func PrngFill(size int) (dst []byte) {
    32  	dst = make([]byte, size)
    33  	if size == 0 {
    34  		return dst
    35  	}
    36  
    37  	rand.Read(dst)
    38  	// _, err := rand.Read(dst)
    39  	// if err != nil {
    40  	// 	panic(fmt.Sprintf("Could not read random number. %s", err))
    41  	// }
    42  	return
    43  }
    44  
    45  func PrngUint8() uint8 {
    46  	var u8 uint8
    47  	for u8 == 0 {
    48  		dst := PrngFill(1)
    49  		u8 = dst[0]
    50  	}
    51  	return u8
    52  }
    53  
    54  func randomNonce() ([]byte, error) {
    55  	return _randomNonce()
    56  }
    57  
    58  // don't use this function directly, it's for internal purpose only
    59  type _randFunc func(io.Reader, []byte) (int, error)
    60  
    61  // don't use this function directly, it's for internal purpose only
    62  func _randomNonce(r ..._randFunc) ([]byte, error) {
    63  	// Never use more than 2^32 random nonces with a given key because of the risk of a repeat.
    64  	var f _randFunc
    65  	if len(r) > 0 {
    66  		f = r[0]
    67  	} else {
    68  		f = io.ReadFull
    69  	}
    70  
    71  	nonce := make([]byte, NONCE_LEN)
    72  	if _, err := f(rand.Reader, nonce); err != nil {
    73  		// logW.Printf("#randomNonce. %s\n", err)
    74  		util.Logger.Warn("#randomNonce", "error", err)
    75  		return nil, err
    76  	}
    77  
    78  	return nonce, nil
    79  }
    80  
    81  type Base64Key struct {
    82  	key []uint8
    83  }
    84  
    85  // random key 128bit
    86  func NewBase64Key() *Base64Key {
    87  	b := &Base64Key{}
    88  	b.key = PrngFill(16)
    89  	return b
    90  }
    91  
    92  func NewBase64Key2(printableKey string) *Base64Key {
    93  	key, err := base64.StdEncoding.DecodeString(printableKey)
    94  	if err != nil {
    95  		// logW.Printf("#Base64Key Key must be well-formed base64. %s\n", err)
    96  		util.Logger.Warn("key must be well-formed base64", "error", err)
    97  		return nil
    98  	}
    99  
   100  	if len(key) != 16 {
   101  		// logW.Println("#Base64Key Key must represent 16 octets.")
   102  		util.Logger.Warn("key must represent 16 octets.", "key", key)
   103  		return nil
   104  	}
   105  
   106  	b := &Base64Key{}
   107  	b.key = key
   108  	// // to catch changes after the first 128 bits
   109  	// if printableKey != b.printableKey() {
   110  	// 	panic("Base64 key was not encoded 128-bit key.")
   111  	// }
   112  
   113  	return b
   114  }
   115  
   116  func (b *Base64Key) printableKey() string {
   117  	return base64.StdEncoding.EncodeToString(b.key)
   118  }
   119  
   120  func (b *Base64Key) data() []uint8 {
   121  	return b.key
   122  }
   123  
   124  func (b *Base64Key) String() string {
   125  	return b.printableKey()
   126  }
   127  
   128  var counter uint64
   129  
   130  func Unique() uint64 {
   131  	atomic.AddUint64(&counter, 1)
   132  	return atomic.LoadUint64(&counter)
   133  }
   134  
   135  type Message struct {
   136  	nonce []byte
   137  	text  []byte
   138  }
   139  
   140  func NewMessage(seqNonce uint64, payload []byte) (m *Message) {
   141  	m = &Message{}
   142  	// fmt.Printf("#Message seqNonce=% x\n", seqNonce)
   143  
   144  	b := make([]byte, NONCE_LEN)
   145  	binary.BigEndian.PutUint32(b[0:], 0)
   146  	binary.BigEndian.PutUint64(b[4:], seqNonce)
   147  
   148  	m.nonce = b
   149  	m.text = payload
   150  
   151  	// fmt.Printf("#Message head=% x, nonce=% x\n", m.nonce[:4], m.nonce[4:])
   152  	// fmt.Printf("#Message text=% x\n", m.text)
   153  	return m
   154  }
   155  
   156  func (m *Message) NonceVal() uint64 {
   157  	seqNonce := binary.BigEndian.Uint64(m.nonce[4:])
   158  
   159  	// fmt.Printf("#NonceVal seqNonce=%x\n", seqNonce)
   160  	return seqNonce
   161  }
   162  
   163  // the first two bytes is timestamp in text field
   164  func (m *Message) GetTimestamp() uint16 {
   165  	// var ts uint16
   166  	// buf := bytes.NewReader(m.text[:2])
   167  	// err := binary.Read(buf, hostEndian, &ts)
   168  	// if err != nil {
   169  	// 	fmt.Printf("#GetTimestamp failed. %s\n", err)
   170  	// }
   171  	//
   172  	// return ts
   173  	return binary.BigEndian.Uint16(m.text[:2])
   174  }
   175  
   176  // the [2:4] bytes is timestampReply in text field
   177  func (m *Message) GetTimestampReply() uint16 {
   178  	// var tsr uint16
   179  	// buf := bytes.NewReader(m.text[2:4])
   180  	// err := binary.Read(buf, hostEndian, &tsr)
   181  	// if err != nil {
   182  	// 	fmt.Printf("#GetTimestampReply failed. %s\n", err)
   183  	// }
   184  	//
   185  	// return tsr
   186  	return binary.BigEndian.Uint16(m.text[2:4])
   187  }
   188  
   189  func (m *Message) GetPayload() (payload []byte) {
   190  	return m.text[4:]
   191  }
   192  
   193  type Session struct {
   194  	base64Key Base64Key
   195  	aead      cipher.AEAD
   196  	// sync.Mutex
   197  }
   198  
   199  func NewSession(key Base64Key) (*Session, error) {
   200  	s := &Session{base64Key: key}
   201  	block, err := aes.NewCipher([]byte(s.base64Key.key))
   202  	if err != nil {
   203  		// logW.Printf("#session %s\n", err)
   204  		util.Logger.Warn("create session from key", "error", err)
   205  		return nil, err
   206  	}
   207  
   208  	aesgcm, _ := cipher.NewGCM(block)
   209  	// aesgcm, err := cipher.NewGCM(block)
   210  	// if err != nil {
   211  	// 	return nil, err
   212  	// }
   213  
   214  	s.aead = aesgcm
   215  	return s, nil
   216  }
   217  
   218  // https://stackoverflow.com/questions/1220751/how-to-choose-an-aes-encryption-mode-cbc-ecb-ctr-ocb-cfb
   219  // https://installmd.com/c/276/go/encrypt-a-string-using-aes-gcm
   220  
   221  // Encrypt with AES-128 GCM
   222  func (s *Session) Encrypt(plainText *Message) []byte {
   223  	// s.Lock()
   224  	// defer s.Unlock()
   225  	nonce := plainText.nonce
   226  
   227  	cipherText := s.aead.Seal(nonce, nonce, plainText.text, nil)
   228  	return cipherText
   229  }
   230  
   231  // Decrypt with AES-128 GCM
   232  func (s *Session) Decrypt(text []byte) (*Message, error) {
   233  	// s.Lock()
   234  	// defer s.Unlock()
   235  	ns := s.aead.NonceSize()
   236  	nonce, cipherText := text[:ns], text[ns:]
   237  	// fmt.Printf("#decrypt ciphertext=% x, %p\n", cipherText, cipherText)
   238  
   239  	plainText, err := s.aead.Open(nil, nonce, cipherText, nil)
   240  	if err != nil {
   241  		// logW.Printf("#decrypt %s\n", err)
   242  		return nil, err
   243  	}
   244  
   245  	m := Message{}
   246  	m.nonce = nonce
   247  	m.text = plainText
   248  	// fmt.Printf("#decrypt nonce=% x, plaintext=% x\n", nonce, plainText)
   249  
   250  	return &m, nil
   251  }
   252  
   253  var savedCoreLimit uint64
   254  
   255  // Disable dumping core, as a precaution to avoid saving sensitive data to disk.
   256  func DisableDumpingCore() error {
   257  	// the value argument is provided by last parameter of accessRlimit
   258  	f := func(rlim *syscall.Rlimit, value uint64) {
   259  		savedCoreLimit = rlim.Cur
   260  		rlim.Cur = value
   261  	}
   262  	return accessRlimit(syscall.RLIMIT_CORE, f, 0)
   263  }
   264  
   265  // restore the dumping core to saved value
   266  func ReenableDumpingCore() error {
   267  	f := func(rlim *syscall.Rlimit, _ uint64) {
   268  		rlim.Cur = savedCoreLimit
   269  	}
   270  
   271  	// we don't use value parameter, so it's not important the specific value
   272  	return accessRlimit(syscall.RLIMIT_CORE, f, 0)
   273  }
   274  
   275  // get specified resource, then do some action defined by f, finally set the specififed resource
   276  func accessRlimit(resource int, f func(rlim *syscall.Rlimit, value uint64), value uint64) error {
   277  	var rlim syscall.Rlimit
   278  	if err := syscall.Getrlimit(resource, &rlim); err != nil {
   279  		return fmt.Errorf("Getrlimit() reports %s", err.Error())
   280  	}
   281  
   282  	f(&rlim, value)
   283  
   284  	if err := syscall.Setrlimit(resource, &rlim); err != nil {
   285  		return fmt.Errorf("Setrlimit() reports %s", err.Error())
   286  	}
   287  	return nil
   288  }
   289  
   290  // https://commandcenter.blogspot.com/2012/04/byte-order-fallacy.html
   291  //
   292  // var hostEndian binary.ByteOrder
   293  //
   294  // func init() {
   295  // 	buf := [2]byte{}
   296  // 	*(*uint16)(unsafe.Pointer(&buf[0])) = uint16(0xABCD)
   297  //
   298  // 	switch buf {
   299  // 	case [2]byte{0xCD, 0xAB}:
   300  // 		hostEndian = binary.LittleEndian
   301  // 	case [2]byte{0xAB, 0xCD}:
   302  // 		hostEndian = binary.BigEndian
   303  // 	default:
   304  // 		panic("Could not determine native endianness.")
   305  // 	}
   306  // }