839 lines
17 KiB
Go
839 lines
17 KiB
Go
package cryptoutil
|
|
|
|
import (
|
|
"fmt"
|
|
"math"
|
|
"strings"
|
|
)
|
|
|
|
// RSA Constants
|
|
const (
|
|
biRadixBits = 16
|
|
bitsPerDigit = biRadixBits
|
|
biRadix = 1 << 16 // 65536
|
|
biHalfRadix = biRadix >> 1
|
|
biRadixSquared = biRadix * biRadix
|
|
maxDigitVal = biRadix - 1
|
|
maxInteger = 9999999999999998
|
|
dpl10 = 15
|
|
)
|
|
|
|
var (
|
|
maxDigits int
|
|
zeroArray []int
|
|
bigZero *BigInt
|
|
bigOne *BigInt
|
|
lr10 *BigInt
|
|
highBitMasks = []int{0x0000, 0x8000, 0xC000, 0xE000, 0xF000, 0xF800,
|
|
0xFC00, 0xFE00, 0xFF00, 0xFF80, 0xFFC0, 0xFFE0,
|
|
0xFFF0, 0xFFF8, 0xFFFC, 0xFFFE, 0xFFFF}
|
|
lowBitMasks = []int{0x0000, 0x0001, 0x0003, 0x0007, 0x000F, 0x001F,
|
|
0x003F, 0x007F, 0x00FF, 0x01FF, 0x03FF, 0x07FF,
|
|
0x0FFF, 0x1FFF, 0x3FFF, 0x7FFF, 0xFFFF}
|
|
hexToChar = []byte{'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'a', 'b', 'c', 'd', 'e', 'f'}
|
|
hexatrigesimalToChar = []byte{
|
|
'0', '1', '2', '3', '4', '5', '6', '7', '8', '9',
|
|
'a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j',
|
|
'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't',
|
|
'u', 'v', 'w', 'x', 'y', 'z'}
|
|
)
|
|
|
|
// BigInt represents a big integer
|
|
type BigInt struct {
|
|
digits []int
|
|
isNeg bool
|
|
}
|
|
|
|
// NewBigInt creates a new BigInt
|
|
func NewBigInt() *BigInt {
|
|
digits := make([]int, len(zeroArray))
|
|
copy(digits, zeroArray)
|
|
return &BigInt{
|
|
digits: digits,
|
|
isNeg: false,
|
|
}
|
|
}
|
|
|
|
// NewBigIntEmpty creates an empty BigInt
|
|
func NewBigIntEmpty() *BigInt {
|
|
return &BigInt{
|
|
digits: nil,
|
|
isNeg: false,
|
|
}
|
|
}
|
|
|
|
// SetMaxDigits sets the maximum number of digits
|
|
func SetMaxDigits(value int) {
|
|
maxDigits = value
|
|
zeroArray = make([]int, maxDigits)
|
|
bigZero = NewBigInt()
|
|
bigOne = NewBigInt()
|
|
bigOne.digits[0] = 1
|
|
lr10 = BiFromNumber(1000000000000000)
|
|
}
|
|
|
|
// BiFromNumber creates a BigInt from a number
|
|
func BiFromNumber(i int64) *BigInt {
|
|
result := NewBigInt()
|
|
result.isNeg = i < 0
|
|
i = int64(math.Abs(float64(i)))
|
|
j := 0
|
|
for i > 0 {
|
|
result.digits[j] = int(i & maxDigitVal)
|
|
j++
|
|
i = i / biRadix
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BiFromDecimal creates a BigInt from a decimal string
|
|
func BiFromDecimal(s string) *BigInt {
|
|
isNeg := len(s) > 0 && s[0] == '-'
|
|
i := 0
|
|
if isNeg {
|
|
i = 1
|
|
}
|
|
|
|
// Skip leading zeros
|
|
for i < len(s) && s[i] == '0' {
|
|
i++
|
|
}
|
|
|
|
if i == len(s) {
|
|
return NewBigInt()
|
|
}
|
|
|
|
digitCount := len(s) - i
|
|
fgl := digitCount % dpl10
|
|
if fgl == 0 {
|
|
fgl = dpl10
|
|
}
|
|
|
|
num := parseNumber(s[i : i+fgl])
|
|
result := BiFromNumber(num)
|
|
i += fgl
|
|
|
|
for i < len(s) {
|
|
result = BiAdd(BiMultiply(result, lr10), BiFromNumber(parseNumber(s[i:min(i+dpl10, len(s))])))
|
|
i += dpl10
|
|
}
|
|
|
|
result.isNeg = isNeg
|
|
return result
|
|
}
|
|
|
|
// BiCopy copies a BigInt
|
|
func BiCopy(bi *BigInt) *BigInt {
|
|
result := NewBigIntEmpty()
|
|
result.digits = make([]int, len(bi.digits))
|
|
copy(result.digits, bi.digits)
|
|
result.isNeg = bi.isNeg
|
|
return result
|
|
}
|
|
|
|
// ReverseStr reverses a string
|
|
func ReverseStr(s string) string {
|
|
runes := []rune(s)
|
|
for i, j := 0, len(runes)-1; i < j; i, j = i+1, j-1 {
|
|
runes[i], runes[j] = runes[j], runes[i]
|
|
}
|
|
return string(runes)
|
|
}
|
|
|
|
// BiToString converts BigInt to string with given radix
|
|
func BiToString(x *BigInt, radix int) string {
|
|
b := NewBigInt()
|
|
b.digits[0] = radix
|
|
qr := BiDivideModulo(x, b)
|
|
result := string(hexatrigesimalToChar[qr[1].digits[0]])
|
|
|
|
for BiCompare(qr[0], bigZero) == 1 {
|
|
qr = BiDivideModulo(qr[0], b)
|
|
result += string(hexatrigesimalToChar[qr[1].digits[0]])
|
|
}
|
|
|
|
prefix := ""
|
|
if x.isNeg {
|
|
prefix = "-"
|
|
}
|
|
return prefix + ReverseStr(result)
|
|
}
|
|
|
|
// BiToDecimal converts BigInt to decimal string
|
|
func BiToDecimal(x *BigInt) string {
|
|
b := NewBigInt()
|
|
b.digits[0] = 10
|
|
qr := BiDivideModulo(x, b)
|
|
result := fmt.Sprintf("%d", qr[1].digits[0])
|
|
|
|
for BiCompare(qr[0], bigZero) == 1 {
|
|
qr = BiDivideModulo(qr[0], b)
|
|
result += fmt.Sprintf("%d", qr[1].digits[0])
|
|
}
|
|
|
|
prefix := ""
|
|
if x.isNeg {
|
|
prefix = "-"
|
|
}
|
|
return prefix + ReverseStr(result)
|
|
}
|
|
|
|
// DigitToHex converts a digit to hex string
|
|
func DigitToHex(n int) string {
|
|
mask := 0xf
|
|
result := ""
|
|
for i := 0; i < 4; i++ {
|
|
result += string(hexToChar[n&mask])
|
|
n >>= 4
|
|
}
|
|
return ReverseStr(result)
|
|
}
|
|
|
|
// BiToHex converts BigInt to hex string
|
|
func BiToHex(x *BigInt) string {
|
|
result := ""
|
|
for i := BiHighIndex(x); i > -1; i-- {
|
|
result += DigitToHex(x.digits[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// CharToHex converts a character code to hex value
|
|
func CharToHex(c byte) int {
|
|
const (
|
|
ZERO = 48
|
|
NINE = ZERO + 9
|
|
littleA = 97
|
|
littleZ = littleA + 25
|
|
bigA = 65
|
|
bigZ = 65 + 25
|
|
)
|
|
|
|
if c >= ZERO && c <= NINE {
|
|
return int(c - ZERO)
|
|
} else if c >= bigA && c <= bigZ {
|
|
return 10 + int(c-bigA)
|
|
} else if c >= littleA && c <= littleZ {
|
|
return 10 + int(c-littleA)
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// HexToDigit converts hex string to digit
|
|
func HexToDigit(s string) int {
|
|
result := 0
|
|
sl := min(len(s), 4)
|
|
for i := 0; i < sl; i++ {
|
|
result <<= 4
|
|
result |= CharToHex(s[i])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BiFromHex creates BigInt from hex string
|
|
func BiFromHex(s string) *BigInt {
|
|
result := NewBigInt()
|
|
sl := len(s)
|
|
for i, j := sl, 0; i > 0; i, j = i-4, j+1 {
|
|
start := max(i-4, 0)
|
|
length := min(i, 4)
|
|
if start+length > sl {
|
|
length = sl - start
|
|
}
|
|
result.digits[j] = HexToDigit(s[start : start+length])
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BiFromString creates BigInt from string with given radix
|
|
func BiFromString(s string, radix int) *BigInt {
|
|
isNeg := len(s) > 0 && s[0] == '-'
|
|
istop := 0
|
|
if isNeg {
|
|
istop = 1
|
|
}
|
|
|
|
result := NewBigInt()
|
|
place := NewBigInt()
|
|
place.digits[0] = 1
|
|
|
|
for i := len(s) - 1; i >= istop; i-- {
|
|
c := s[i]
|
|
digit := CharToHex(c)
|
|
biDigit := BiMultiplyDigit(place, digit)
|
|
result = BiAdd(result, biDigit)
|
|
place = BiMultiplyDigit(place, radix)
|
|
}
|
|
|
|
result.isNeg = isNeg
|
|
return result
|
|
}
|
|
|
|
// BiAdd adds two BigInts
|
|
func BiAdd(x, y *BigInt) *BigInt {
|
|
var result *BigInt
|
|
|
|
if x.isNeg != y.isNeg {
|
|
y.isNeg = !y.isNeg
|
|
result = BiSubtract(x, y)
|
|
y.isNeg = !y.isNeg
|
|
} else {
|
|
result = NewBigInt()
|
|
c := 0
|
|
for i := 0; i < len(x.digits); i++ {
|
|
n := x.digits[i] + y.digits[i] + c
|
|
result.digits[i] = n % biRadix
|
|
if n >= biRadix {
|
|
c = 1
|
|
} else {
|
|
c = 0
|
|
}
|
|
}
|
|
result.isNeg = x.isNeg
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// BiSubtract subtracts two BigInts
|
|
func BiSubtract(x, y *BigInt) *BigInt {
|
|
var result *BigInt
|
|
|
|
if x.isNeg != y.isNeg {
|
|
y.isNeg = !y.isNeg
|
|
result = BiAdd(x, y)
|
|
y.isNeg = !y.isNeg
|
|
} else {
|
|
result = NewBigInt()
|
|
c := 0
|
|
|
|
for i := 0; i < len(x.digits); i++ {
|
|
n := x.digits[i] - y.digits[i] + c
|
|
result.digits[i] = n % biRadix
|
|
if result.digits[i] < 0 {
|
|
result.digits[i] += biRadix
|
|
}
|
|
if n < 0 {
|
|
c = -1
|
|
} else {
|
|
c = 0
|
|
}
|
|
}
|
|
|
|
if c == -1 {
|
|
c = 0
|
|
for i := 0; i < len(x.digits); i++ {
|
|
n := 0 - result.digits[i] + c
|
|
result.digits[i] = n % biRadix
|
|
if result.digits[i] < 0 {
|
|
result.digits[i] += biRadix
|
|
}
|
|
if n < 0 {
|
|
c = -1
|
|
} else {
|
|
c = 0
|
|
}
|
|
}
|
|
result.isNeg = !x.isNeg
|
|
} else {
|
|
result.isNeg = x.isNeg
|
|
}
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// BiHighIndex returns the highest non-zero digit index
|
|
func BiHighIndex(x *BigInt) int {
|
|
result := len(x.digits) - 1
|
|
for result > 0 && x.digits[result] == 0 {
|
|
result--
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BiNumBits returns the number of bits
|
|
func BiNumBits(x *BigInt) int {
|
|
n := BiHighIndex(x)
|
|
d := x.digits[n]
|
|
m := (n + 1) * bitsPerDigit
|
|
result := m
|
|
for result > m-bitsPerDigit {
|
|
if (d & 0x8000) != 0 {
|
|
break
|
|
}
|
|
d <<= 1
|
|
result--
|
|
}
|
|
return result
|
|
}
|
|
|
|
// BiMultiply multiplies two BigInts
|
|
func BiMultiply(x, y *BigInt) *BigInt {
|
|
result := NewBigInt()
|
|
n := BiHighIndex(x)
|
|
t := BiHighIndex(y)
|
|
|
|
for i := 0; i <= t; i++ {
|
|
c := 0
|
|
k := i
|
|
for j := 0; j <= n; j, k = j+1, k+1 {
|
|
uv := result.digits[k] + x.digits[j]*y.digits[i] + c
|
|
result.digits[k] = uv & maxDigitVal
|
|
c = uv >> biRadixBits
|
|
}
|
|
result.digits[i+n+1] = c
|
|
}
|
|
|
|
result.isNeg = x.isNeg != y.isNeg
|
|
return result
|
|
}
|
|
|
|
// BiMultiplyDigit multiplies BigInt by a digit
|
|
func BiMultiplyDigit(x *BigInt, y int) *BigInt {
|
|
result := NewBigInt()
|
|
n := BiHighIndex(x)
|
|
c := 0
|
|
|
|
for j := 0; j <= n; j++ {
|
|
uv := result.digits[j] + x.digits[j]*y + c
|
|
result.digits[j] = uv & maxDigitVal
|
|
c = uv >> biRadixBits
|
|
}
|
|
result.digits[1+n] = c
|
|
return result
|
|
}
|
|
|
|
// ArrayCopy copies array elements
|
|
func ArrayCopy(src []int, srcStart int, dest []int, destStart int, n int) {
|
|
m := min(srcStart+n, len(src))
|
|
for i, j := srcStart, destStart; i < m; i, j = i+1, j+1 {
|
|
dest[j] = src[i]
|
|
}
|
|
}
|
|
|
|
// BiShiftLeft shifts BigInt left by n bits
|
|
func BiShiftLeft(x *BigInt, n int) *BigInt {
|
|
digitCount := n / bitsPerDigit
|
|
result := NewBigInt()
|
|
ArrayCopy(x.digits, 0, result.digits, digitCount, len(result.digits)-digitCount)
|
|
|
|
bits := n % bitsPerDigit
|
|
rightBits := bitsPerDigit - bits
|
|
|
|
for i, i1 := len(result.digits)-1, len(result.digits)-2; i > 0; i, i1 = i-1, i1-1 {
|
|
result.digits[i] = ((result.digits[i] << bits) & maxDigitVal) |
|
|
((result.digits[i1] & highBitMasks[bits]) >> rightBits)
|
|
}
|
|
result.digits[0] = ((result.digits[0] << bits) & maxDigitVal)
|
|
result.isNeg = x.isNeg
|
|
return result
|
|
}
|
|
|
|
// BiShiftRight shifts BigInt right by n bits
|
|
func BiShiftRight(x *BigInt, n int) *BigInt {
|
|
digitCount := n / bitsPerDigit
|
|
result := NewBigInt()
|
|
ArrayCopy(x.digits, digitCount, result.digits, 0, len(x.digits)-digitCount)
|
|
|
|
bits := n % bitsPerDigit
|
|
leftBits := bitsPerDigit - bits
|
|
|
|
for i, i1 := 0, 1; i < len(result.digits)-1; i, i1 = i+1, i1+1 {
|
|
result.digits[i] = (result.digits[i] >> bits) |
|
|
((result.digits[i1] & lowBitMasks[bits]) << leftBits)
|
|
}
|
|
result.digits[len(result.digits)-1] >>= bits
|
|
result.isNeg = x.isNeg
|
|
return result
|
|
}
|
|
|
|
// BiMultiplyByRadixPower multiplies BigInt by radix^n
|
|
func BiMultiplyByRadixPower(x *BigInt, n int) *BigInt {
|
|
result := NewBigInt()
|
|
ArrayCopy(x.digits, 0, result.digits, n, len(result.digits)-n)
|
|
return result
|
|
}
|
|
|
|
// BiDivideByRadixPower divides BigInt by radix^n
|
|
func BiDivideByRadixPower(x *BigInt, n int) *BigInt {
|
|
result := NewBigInt()
|
|
ArrayCopy(x.digits, n, result.digits, 0, len(result.digits)-n)
|
|
return result
|
|
}
|
|
|
|
// BiModuloByRadixPower computes BigInt mod radix^n
|
|
func BiModuloByRadixPower(x *BigInt, n int) *BigInt {
|
|
result := NewBigInt()
|
|
ArrayCopy(x.digits, 0, result.digits, 0, n)
|
|
return result
|
|
}
|
|
|
|
// BiCompare compares two BigInts
|
|
func BiCompare(x, y *BigInt) int {
|
|
if x.isNeg != y.isNeg {
|
|
if x.isNeg {
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
|
|
for i := len(x.digits) - 1; i >= 0; i-- {
|
|
if x.digits[i] != y.digits[i] {
|
|
if x.isNeg {
|
|
if x.digits[i] > y.digits[i] {
|
|
return -1
|
|
}
|
|
return 1
|
|
} else {
|
|
if x.digits[i] < y.digits[i] {
|
|
return -1
|
|
}
|
|
return 1
|
|
}
|
|
}
|
|
}
|
|
return 0
|
|
}
|
|
|
|
// BiDivideModulo divides two BigInts and returns quotient and remainder
|
|
func BiDivideModulo(x, y *BigInt) []*BigInt {
|
|
nb := BiNumBits(x)
|
|
tb := BiNumBits(y)
|
|
origYIsNeg := y.isNeg
|
|
var q, r *BigInt
|
|
|
|
if nb < tb {
|
|
if x.isNeg {
|
|
q = BiCopy(bigOne)
|
|
q.isNeg = !y.isNeg
|
|
x.isNeg = false
|
|
y.isNeg = false
|
|
r = BiSubtract(y, x)
|
|
x.isNeg = true
|
|
y.isNeg = origYIsNeg
|
|
} else {
|
|
q = NewBigInt()
|
|
r = BiCopy(x)
|
|
}
|
|
return []*BigInt{q, r}
|
|
}
|
|
|
|
q = NewBigInt()
|
|
r = x
|
|
|
|
t := int(math.Ceil(float64(tb)/float64(bitsPerDigit))) - 1
|
|
lambda := 0
|
|
for y.digits[t] < biHalfRadix {
|
|
y = BiShiftLeft(y, 1)
|
|
lambda++
|
|
tb++
|
|
t = int(math.Ceil(float64(tb)/float64(bitsPerDigit))) - 1
|
|
}
|
|
|
|
r = BiShiftLeft(r, lambda)
|
|
nb += lambda
|
|
n := int(math.Ceil(float64(nb)/float64(bitsPerDigit))) - 1
|
|
|
|
b := BiMultiplyByRadixPower(y, n-t)
|
|
for BiCompare(r, b) != -1 {
|
|
q.digits[n-t]++
|
|
r = BiSubtract(r, b)
|
|
}
|
|
|
|
for i := n; i > t; i-- {
|
|
ri := 0
|
|
if i < len(r.digits) {
|
|
ri = r.digits[i]
|
|
}
|
|
ri1 := 0
|
|
if i-1 < len(r.digits) {
|
|
ri1 = r.digits[i-1]
|
|
}
|
|
ri2 := 0
|
|
if i-2 < len(r.digits) {
|
|
ri2 = r.digits[i-2]
|
|
}
|
|
yt := 0
|
|
if t < len(y.digits) {
|
|
yt = y.digits[t]
|
|
}
|
|
yt1 := 0
|
|
if t-1 < len(y.digits) {
|
|
yt1 = y.digits[t-1]
|
|
}
|
|
|
|
if ri == yt {
|
|
q.digits[i-t-1] = maxDigitVal
|
|
} else {
|
|
q.digits[i-t-1] = (ri*biRadix + ri1) / yt
|
|
}
|
|
|
|
c1 := q.digits[i-t-1] * ((yt * biRadix) + yt1)
|
|
c2 := (ri * biRadixSquared) + ((ri1 * biRadix) + ri2)
|
|
|
|
for c1 > c2 {
|
|
q.digits[i-t-1]--
|
|
c1 = q.digits[i-t-1] * ((yt * biRadix) | yt1)
|
|
c2 = (ri * biRadix * biRadix) + ((ri1 * biRadix) + ri2)
|
|
}
|
|
|
|
b = BiMultiplyByRadixPower(y, i-t-1)
|
|
r = BiSubtract(r, BiMultiplyDigit(b, q.digits[i-t-1]))
|
|
if r.isNeg {
|
|
r = BiAdd(r, b)
|
|
q.digits[i-t-1]--
|
|
}
|
|
}
|
|
|
|
r = BiShiftRight(r, lambda)
|
|
q.isNeg = x.isNeg != origYIsNeg
|
|
|
|
if x.isNeg {
|
|
if origYIsNeg {
|
|
q = BiAdd(q, bigOne)
|
|
} else {
|
|
q = BiSubtract(q, bigOne)
|
|
}
|
|
y = BiShiftRight(y, lambda)
|
|
r = BiSubtract(y, r)
|
|
}
|
|
|
|
if r.digits[0] == 0 && BiHighIndex(r) == 0 {
|
|
r.isNeg = false
|
|
}
|
|
|
|
return []*BigInt{q, r}
|
|
}
|
|
|
|
// BiDivide divides two BigInts
|
|
func BiDivide(x, y *BigInt) *BigInt {
|
|
return BiDivideModulo(x, y)[0]
|
|
}
|
|
|
|
// BiModulo computes x mod y
|
|
func BiModulo(x, y *BigInt) *BigInt {
|
|
return BiDivideModulo(x, y)[1]
|
|
}
|
|
|
|
// BiMultiplyMod computes (x * y) mod m
|
|
func BiMultiplyMod(x, y, m *BigInt) *BigInt {
|
|
return BiModulo(BiMultiply(x, y), m)
|
|
}
|
|
|
|
// BiPowMod computes x^y mod m
|
|
func BiPowMod(x, y, m *BigInt) *BigInt {
|
|
result := BiCopy(bigOne)
|
|
a := x
|
|
k := y
|
|
|
|
for {
|
|
if (k.digits[0] & 1) != 0 {
|
|
result = BiMultiplyMod(result, a, m)
|
|
}
|
|
k = BiShiftRight(k, 1)
|
|
if k.digits[0] == 0 && BiHighIndex(k) == 0 {
|
|
break
|
|
}
|
|
a = BiMultiplyMod(a, a, m)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// BarrettMu represents Barrett reduction
|
|
type BarrettMu struct {
|
|
modulus *BigInt
|
|
k int
|
|
mu *BigInt
|
|
bkplus1 *BigInt
|
|
}
|
|
|
|
// NewBarrettMu creates a new BarrettMu
|
|
func NewBarrettMu(m *BigInt) *BarrettMu {
|
|
bm := &BarrettMu{
|
|
modulus: BiCopy(m),
|
|
k: BiHighIndex(m) + 1,
|
|
}
|
|
|
|
b2k := NewBigInt()
|
|
if 2*bm.k < len(b2k.digits) {
|
|
b2k.digits[2*bm.k] = 1
|
|
}
|
|
bm.mu = BiDivide(b2k, bm.modulus)
|
|
|
|
bm.bkplus1 = NewBigInt()
|
|
if bm.k+1 < len(bm.bkplus1.digits) {
|
|
bm.bkplus1.digits[bm.k+1] = 1
|
|
}
|
|
|
|
return bm
|
|
}
|
|
|
|
// Modulo performs Barrett modulo
|
|
func (bm *BarrettMu) Modulo(x *BigInt) *BigInt {
|
|
q1 := BiDivideByRadixPower(x, bm.k-1)
|
|
q2 := BiMultiply(q1, bm.mu)
|
|
q3 := BiDivideByRadixPower(q2, bm.k+1)
|
|
r1 := BiModuloByRadixPower(x, bm.k+1)
|
|
r2term := BiMultiply(q3, bm.modulus)
|
|
r2 := BiModuloByRadixPower(r2term, bm.k+1)
|
|
r := BiSubtract(r1, r2)
|
|
|
|
if r.isNeg {
|
|
r = BiAdd(r, bm.bkplus1)
|
|
}
|
|
|
|
for BiCompare(r, bm.modulus) >= 0 {
|
|
r = BiSubtract(r, bm.modulus)
|
|
}
|
|
|
|
return r
|
|
}
|
|
|
|
// MultiplyMod performs modular multiplication
|
|
func (bm *BarrettMu) MultiplyMod(x, y *BigInt) *BigInt {
|
|
xy := BiMultiply(x, y)
|
|
return bm.Modulo(xy)
|
|
}
|
|
|
|
// PowMod performs modular exponentiation
|
|
func (bm *BarrettMu) PowMod(x, y *BigInt) *BigInt {
|
|
result := NewBigInt()
|
|
result.digits[0] = 1
|
|
a := x
|
|
k := y
|
|
|
|
for {
|
|
if (k.digits[0] & 1) != 0 {
|
|
result = bm.MultiplyMod(result, a)
|
|
}
|
|
k = BiShiftRight(k, 1)
|
|
if k.digits[0] == 0 && BiHighIndex(k) == 0 {
|
|
break
|
|
}
|
|
a = bm.MultiplyMod(a, a)
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// NutRSAKeyPair represents an RSA key pair
|
|
type NutRSAKeyPair struct {
|
|
e *BigInt
|
|
d *BigInt
|
|
m *BigInt
|
|
chunkSize int
|
|
radix int
|
|
barrett *BarrettMu
|
|
}
|
|
|
|
// NewNutRSAKeyPair creates a new RSA key pair
|
|
func NewNutRSAKeyPair(encryptionExponent, decryptionExponent, modulus string) *NutRSAKeyPair {
|
|
kp := &NutRSAKeyPair{
|
|
e: BiFromHex(encryptionExponent),
|
|
d: BiFromHex(decryptionExponent),
|
|
m: BiFromHex(modulus),
|
|
radix: 16,
|
|
}
|
|
kp.chunkSize = 2 * BiHighIndex(kp.m)
|
|
kp.barrett = NewBarrettMu(kp.m)
|
|
return kp
|
|
}
|
|
|
|
// EncryptedString encrypts a string
|
|
func (kp *NutRSAKeyPair) EncryptedString(s string) string {
|
|
a := make([]int, 0)
|
|
for i := 0; i < len(s); i++ {
|
|
a = append(a, int(s[i]))
|
|
}
|
|
|
|
// Padding
|
|
for len(a)%kp.chunkSize != 0 {
|
|
a = append(a, 0)
|
|
}
|
|
|
|
result := ""
|
|
for i := 0; i < len(a); i += kp.chunkSize {
|
|
block := NewBigInt()
|
|
j := 0
|
|
for k := i; k < i+kp.chunkSize; j++ {
|
|
if k < len(a) {
|
|
block.digits[j] = a[k]
|
|
k++
|
|
}
|
|
if k < len(a) {
|
|
block.digits[j] += a[k] << 8
|
|
k++
|
|
}
|
|
}
|
|
crypt := kp.barrett.PowMod(block, kp.e)
|
|
var text string
|
|
if kp.radix == 16 {
|
|
text = BiToHex(crypt)
|
|
} else {
|
|
text = BiToString(crypt, kp.radix)
|
|
}
|
|
result += text + " "
|
|
}
|
|
|
|
if len(result) > 0 {
|
|
return result[:len(result)-1]
|
|
}
|
|
return result
|
|
}
|
|
|
|
// DecryptedString decrypts a string
|
|
func (kp *NutRSAKeyPair) DecryptedString(s string) string {
|
|
blocks := strings.Split(s, " ")
|
|
result := ""
|
|
|
|
for i := 0; i < len(blocks); i++ {
|
|
var bi *BigInt
|
|
if kp.radix == 16 {
|
|
bi = BiFromHex(blocks[i])
|
|
} else {
|
|
bi = BiFromString(blocks[i], kp.radix)
|
|
}
|
|
block := kp.barrett.PowMod(bi, kp.d)
|
|
|
|
for j := 0; j <= BiHighIndex(block); j++ {
|
|
result += string(rune(block.digits[j] & 255))
|
|
result += string(rune(block.digits[j] >> 8))
|
|
}
|
|
}
|
|
|
|
// Remove trailing null
|
|
if len(result) > 0 && result[len(result)-1] == 0 {
|
|
result = result[:len(result)-1]
|
|
}
|
|
|
|
return result
|
|
}
|
|
|
|
// Helper functions
|
|
func min(a, b int) int {
|
|
if a < b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func max(a, b int) int {
|
|
if a > b {
|
|
return a
|
|
}
|
|
return b
|
|
}
|
|
|
|
func parseNumber(s string) int64 {
|
|
var result int64
|
|
fmt.Sscanf(s, "%d", &result)
|
|
return result
|
|
}
|
|
|
|
func init() {
|
|
SetMaxDigits(130)
|
|
}
|