github.com/jcmturner/gokrb5/v8@v8.4.4/crypto/rfc3961/nfold.go (about) 1 package rfc3961 2 3 // Implementation of the n-fold algorithm as defined in RFC 3961. 4 5 /* Credits 6 This golang implementation of nfold used the following project for help with implementation detail. 7 Although their source is in java it was helpful as a reference implementation of the RFC. 8 You can find the source code of their open source project along with license information below. 9 We acknowledge and are grateful to these developers for their contributions to open source 10 11 Project: Apache Directory (http://http://directory.apache.org/) 12 https://svn.apache.org/repos/asf/directory/apacheds/tags/1.5.1/kerberos-shared/src/main/java/org/apache/directory/server/kerberos/shared/crypto/encryption/NFold.java 13 License: http://www.apache.org/licenses/LICENSE-2.0 14 */ 15 16 // Nfold expands the key to ensure it is not smaller than one cipher block. 17 // Defined in RFC 3961. 18 // 19 // m input bytes that will be "stretched" to the least common multiple of n bits and the bit length of m. 20 func Nfold(m []byte, n int) []byte { 21 k := len(m) * 8 22 23 //Get the lowest common multiple of the two bit sizes 24 lcm := lcm(n, k) 25 relicate := lcm / k 26 var sumBytes []byte 27 28 for i := 0; i < relicate; i++ { 29 rotation := 13 * i 30 sumBytes = append(sumBytes, rotateRight(m, rotation)...) 31 } 32 33 nfold := make([]byte, n/8) 34 sum := make([]byte, n/8) 35 for i := 0; i < lcm/n; i++ { 36 for j := 0; j < n/8; j++ { 37 sum[j] = sumBytes[j+(i*len(sum))] 38 } 39 nfold = onesComplementAddition(nfold, sum) 40 } 41 return nfold 42 } 43 44 func onesComplementAddition(n1, n2 []byte) []byte { 45 numBits := len(n1) * 8 46 out := make([]byte, numBits/8) 47 carry := 0 48 for i := numBits - 1; i > -1; i-- { 49 n1b := getBit(&n1, i) 50 n2b := getBit(&n2, i) 51 s := n1b + n2b + carry 52 53 if s == 0 || s == 1 { 54 setBit(&out, i, s) 55 carry = 0 56 } else if s == 2 { 57 carry = 1 58 } else if s == 3 { 59 setBit(&out, i, 1) 60 carry = 1 61 } 62 } 63 if carry == 1 { 64 carryArray := make([]byte, len(n1)) 65 carryArray[len(carryArray)-1] = 1 66 out = onesComplementAddition(out, carryArray) 67 } 68 return out 69 } 70 71 func rotateRight(b []byte, step int) []byte { 72 out := make([]byte, len(b)) 73 bitLen := len(b) * 8 74 for i := 0; i < bitLen; i++ { 75 v := getBit(&b, i) 76 setBit(&out, (i+step)%bitLen, v) 77 } 78 return out 79 } 80 81 func lcm(x, y int) int { 82 return (x * y) / gcd(x, y) 83 } 84 85 func gcd(x, y int) int { 86 for y != 0 { 87 x, y = y, x%y 88 } 89 return x 90 } 91 92 func getBit(b *[]byte, p int) int { 93 pByte := p / 8 94 pBit := uint(p % 8) 95 vByte := (*b)[pByte] 96 vInt := int(vByte >> (8 - (pBit + 1)) & 0x0001) 97 return vInt 98 } 99 100 func setBit(b *[]byte, p, v int) { 101 pByte := p / 8 102 pBit := uint(p % 8) 103 oldByte := (*b)[pByte] 104 var newByte byte 105 newByte = byte(v<<(8-(pBit+1))) | oldByte 106 (*b)[pByte] = newByte 107 }