aes.go 2.4 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394
  1. package cryptoutil
  2. import (
  3. "crypto/aes"
  4. "crypto/cipher"
  5. "crypto/rand"
  6. "errors"
  7. )
  8. // pad uses the PKCS#7 padding scheme to align the a payload to a specific block size
  9. func pad(plaintext []byte, bsize int) ([]byte, error) {
  10. if bsize >= 256 {
  11. return nil, errors.New("bsize must be < 256")
  12. }
  13. pad := bsize - (len(plaintext) % bsize)
  14. if pad == 0 {
  15. pad = bsize
  16. }
  17. for i := 0; i < pad; i++ {
  18. plaintext = append(plaintext, byte(pad))
  19. }
  20. return plaintext, nil
  21. }
  22. // unpad strips the padding previously added using the PKCS#7 padding scheme
  23. func unpad(paddedtext []byte) ([]byte, error) {
  24. length := len(paddedtext)
  25. paddedtext, lbyte := paddedtext[:length-1], paddedtext[length-1]
  26. pad := int(lbyte)
  27. if pad >= 256 || pad > length {
  28. return nil, errors.New("padding malformed")
  29. }
  30. return paddedtext[:length-(pad)], nil
  31. }
  32. // AESEncrypt encrypts a payload with an AES cipher.
  33. // The returned ciphertext has three notable properties:
  34. // 1. ciphertext is aligned to the standard AES block size
  35. // 2. ciphertext is padded using PKCS#7
  36. // 3. IV is prepended to the ciphertext
  37. func AESEncrypt(plaintext, key []byte) ([]byte, error) {
  38. plaintext, err := pad(plaintext, aes.BlockSize)
  39. if err != nil {
  40. return nil, err
  41. }
  42. block, err := aes.NewCipher(key)
  43. if err != nil {
  44. return nil, err
  45. }
  46. ciphertext := make([]byte, aes.BlockSize+len(plaintext))
  47. iv := ciphertext[:aes.BlockSize]
  48. if _, err := rand.Read(iv); err != nil {
  49. return nil, err
  50. }
  51. mode := cipher.NewCBCEncrypter(block, iv)
  52. mode.CryptBlocks(ciphertext[aes.BlockSize:], plaintext)
  53. return ciphertext, nil
  54. }
  55. // AESDecrypt decrypts an encrypted payload with an AES cipher.
  56. // The decryption algorithm makes three assumptions:
  57. // 1. ciphertext is aligned to the standard AES block size
  58. // 2. ciphertext is padded using PKCS#7
  59. // 3. the IV is prepended to ciphertext
  60. func AESDecrypt(ciphertext, key []byte) ([]byte, error) {
  61. if len(ciphertext) < aes.BlockSize {
  62. return nil, errors.New("ciphertext too short")
  63. }
  64. iv := ciphertext[:aes.BlockSize]
  65. ciphertext = ciphertext[aes.BlockSize:]
  66. if len(ciphertext)%aes.BlockSize != 0 {
  67. return nil, errors.New("ciphertext is not a multiple of the block size")
  68. }
  69. block, err := aes.NewCipher(key)
  70. if err != nil {
  71. return nil, err
  72. }
  73. mode := cipher.NewCBCDecrypter(block, iv)
  74. mode.CryptBlocks(ciphertext, ciphertext)
  75. if len(ciphertext)%aes.BlockSize != 0 {
  76. return nil, errors.New("ciphertext is not a multiple of the block size")
  77. }
  78. return unpad(ciphertext)
  79. }